> ## Documentation Index
> Fetch the complete documentation index at: https://docs.parmanasystems.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Verification Log

> Dated entries recording live, end-to-end verification sessions run against a real deployment, with database contents cross-checked against API responses at every step.

<Info>This page accumulates real verification sessions over time, each as its own dated
entry. Nothing here is projected forward from one session to the next: a session that only
exercised one connector says so, and a later session covering a different connector gets
its own entry rather than silently extending an earlier claim. The real HubSpot test deal id
used in the 2026-08-11 entry below is redacted to `[redacted-deal-id]` in this public copy;
every other detail (requests, responses, database cross-checks) is reported unchanged.</Info>

## 2026-08-11 — Live end-to-end verification, HubSpot connector, `parmana-api.fly.dev`

### Scope

This session verified the **HubSpot connector only**, against the live `parmana-api`
deployment on Fly.io (`https://parmana-api.fly.dev`) and a real HubSpot test deal
(id `[redacted-deal-id]`, "Parmana Test Deal").

`RAZORPAY_KEY_ID`/`RAZORPAY_KEY_SECRET`/`RAZORPAY_WEBHOOK_SECRET` are configured as
**live-mode** credentials on this same deployment, confirmed via a prior secrets audit in
this session. Razorpay was deliberately **not exercised** in this session to avoid any real
money movement. Nothing in this entry should be read as re-verifying the Razorpay path;
see [Independent TRL 7 Verification](/trust-and-claims/trl7-verification) for what was
checked there.

### What was verified

A full live chain — authorize, policy-check, execute, persist, present — run with real HTTP
requests against the deployed instance, not a local or mocked process. At every step where
the API returned a claim (a trust record, a receipt, a refusal record, a hash, a signature),
the corresponding row was independently queried directly from the production Postgres
database (read-only `SELECT`s over `DATABASE_URL`) and compared field by field, not just
checked for a matching status code.

### Steps and results

| Step                                            | Request                                                                                                                          | Result                                                                                                                                                                                    | Cross-check                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Unauthenticated execute                         | `POST /execute`, no `Authorization` header                                                                                       | `401` `{"error":"authentication required"}`                                                                                                                                               | `caller_audit_events` gained a new row: `type: "caller.rejected"`, `route: "/execute"`, `reason: "missing credential"`, signed — timestamp matched the response to the millisecond                                                                                                                                                                                                                                                                        |
| Policy-approved dealstage change                | `POST /execute`, signed envelope proposing `appointmentscheduled` → `qualifiedtobuy`                                             | `200`, `decision.outcome: "APPROVED"`, real HubSpot write executed (deal `[redacted-deal-id]` actually moved stage)                                                                       | `execution_trust_records`, `executions`, `verifications`, and `receipts` rows queried directly — `trustRecordId`, `trustRecordHash`, `signature.value`, `verificationId`, `receiptId`, `receiptHash`, and every timestamp matched the API response byte-for-byte                                                                                                                                                                                          |
| Policy-denied request                           | `POST /execute`, proposed amount $1,000 → $15,000 ($14,000 delta, over the $10,000 threshold, no `preAuthorizedForAmountChange`) | `403` `{"error":"Execution rejected: Deal update rejected because the requested amount change exceeds the configured threshold and has not been pre-authorized.","code":"POLICY_DENIED"}` | `refusal_records` gained exactly one new, signed row matching the reason and signals; **no** `execution_trust_records` row was created for this transaction (correct — a `REJECT` never reaches the trust pipeline)                                                                                                                                                                                                                                       |
| Zero HubSpot calls on denial                    | —                                                                                                                                | Confirmed two ways                                                                                                                                                                        | (1) Source trace of `RuntimeEngine.execute()`: the signal-state verifier — the only code path that calls HubSpot's live API — runs only `if provisionalDecision.outcome === APPROVE`; this request's provisional decision was `REJECT` directly from `PolicyEngine.evaluate`, so it never ran. (2) Empirically: a follow-up read showed the deal's `amount` still `1000` and no HubSpot-side modification between the approved write and the denial check |
| Trust record / verification presentation        | `GET /trust-records/:id`, `GET /verification/:id` for the approved transaction                                                   | Both `200`, full record returned                                                                                                                                                          | Matched the raw `execution_trust_records`/`verifications` rows exactly (already confirmed above)                                                                                                                                                                                                                                                                                                                                                          |
| Trust record / verification, denied transaction | Same two routes, denied transaction id                                                                                           | Both `404` `{"error":"Execution Trust Record not found."}`                                                                                                                                | Expected and correct — no trust record exists for a rejected transaction                                                                                                                                                                                                                                                                                                                                                                                  |
| Refusal record presentation                     | `GET /refusal/:id`, denied transaction id                                                                                        | `200`, full record returned                                                                                                                                                               | Matched the raw `refusal_records` row exactly — `refusalRecordId`, `refusalRecordHash`, `signature.value`/`keyId`/`algorithm`                                                                                                                                                                                                                                                                                                                             |
| Test deal reverted                              | Direct HubSpot API `PATCH` (see note below)                                                                                      | `dealstage` set back to `appointmentscheduled`                                                                                                                                            | Independently confirmed via a fresh `GET`: `dealstage: "appointmentscheduled"`, `amount: "1000"`, `dealname: "Parmana Test Deal"`, `pipeline: "default"` — exact original state                                                                                                                                                                                                                                                                           |

**No discrepancy was found anywhere in this session** between what the API claimed and what
was actually persisted or actually happened on HubSpot.

### One structural note, stated plainly

The revert in the last row was done with a **direct HubSpot API call**, not another signed
Parmana request. This is not a workaround for a bug: the `hubspot-deal-update` policy
enforces forward-only stage transitions (`HUBSPOT_DEFAULT_STAGE_ORDER`), so Parmana itself
has no policy-approved path to move a deal backward. Reverting the test deal necessarily
went around Parmana rather than through it.

### Operational side effect

This session generated a fresh `PARMANA_API_KEYS` credential (caller id `verification-agent`)
and set it on the live deployment via `flyctl secrets set`, replacing whatever value was
configured before (the prior value's raw key was not available to append to, only its
digest, via `flyctl secrets list`). Any caller credential in use before this session no
longer authenticates.
