Skip to content

fix(ui-kit,dashboard,spec): one canonical money-status map - no refund/allocated/unknown as settled-green - #313

Open
LamaSu wants to merge 13 commits into
masterfrom
fix/genui-statusmap
Open

LamaSu wants to merge 13 commits into
masterfrom
fix/genui-statusmap

Conversation

@LamaSu

@LamaSu LamaSu commented Sep 2, 2026 •

Copy link
Copy Markdown
Owner

Problem

Money/settlement status was rendered by inference, so a REFUND rendered as a PAYMENT (read-route contract rule 1, the forbidden-asserter CRITICAL):

  • shipped kit pcc-ui.js statusClass(): the greedy substring regex /(settl|releas|complet|done|paid|funded|success|approved|active)/ greened refunded (contains "funded"), UNRELEASED, INCOMPLETE, UNDERFUNDED, and every *_ALLOCATED (decided, not final);
  • EscrowPage.tsx: the escrow badge defaulted to green;
  • root cause: master has five competing escrow-status vocabularies (EscrowStatus, Escrow.status, the dashboard DTO, the V-next UnitState, the context-pack summary), and every surface re-infers them. GlowBadge also defaults to green.

Fix (scope updated 2026-09-24 for the PCC reconciliation, STATUS-BOARD PX-1)

  1. One canonical map in @pcc/spec (packages/spec/src/money/money-status.ts: MONEY_STATUS_MAP, classifyMoneyStatus, normalizeMoneyStatus). It maps each exact normalized key to a semantic tone plus an honest, direction-explicit label, and it covers every documented vocabulary on master.
    • Green is reserved for the 3 documented FINAL releases to the operator: SETTLED_RELEASED, COMPLETED, RELEASED.
    • Refunds are refunded (final; the operator is NOT paid). Allocated-but-not-final states are waiting. Unknown values fail closed.
    • Bare SETTLED is deliberately unmapped. It means operator-paid in SettlementResultDTO, but in the V-next phase vocabulary it covers BOTH released and refunded.
    • The map is frozen at every level, and compile-time coverage records fail tsc if a spec vocabulary gains an unmapped value.
  2. The shipped kit mirrors it (pcc-ui.js <status-map v2>). This vanilla asset cannot import from the spec, so it keeps a verbatim copy. It also adds moneyStatusClass for money surfaces, so the receipt window no longer greens an off-schema "success"/"done"/"ok". fix(ui-kit,dashboard,spec): one canonical money-status map - no refund/allocated/unknown as settled-green #313's original releasedCount fix (contract rule 12) is kept.
  3. The dashboard escrow badge uses the canonical map through moneyBadgeColor(), with compile-time coverage of the dashboard's EscrowStatus type.

Tests (all of them now run in CI)

  • packages/spec/src/__tests__/money-status.test.ts: 8 tests (the sec-A table; the green set is exactly the 3 final releases; a never-green battery; unknown fails closed; normalization; full vocabulary coverage; immutability).

  • packages/spec/src/__tests__/money-status.conformance.test.ts (jsdom): 19 tests. It proves the shipped kit's table equals the spec map key for key and agrees with it across an adversarial battery. It also boots the whole kit and checks the rendered receipt pill: a refund, an allocated state, an off-schema success, or a missing status never renders green.

  • apps/dashboard/src/lib/__tests__/money-badge.test.ts: 3 tests.

  • The original node --test file lived under apps/dashboard/public/, so it was served publicly, and no CI job ran it. Its 7 cases are now in the vitest suite and the file is removed.

  • Results (DGX Spark, branch @ 506f437e): @pcc/spec 824/824, @pcc/dashboard 223/223, dashboard tsc --noEmit clean, spec tsc build clean.

  • Mutation proof. Each mutation must turn the suite red:

    Mutation Tests failing
    receipt reverted to the generic classifier 2
    greedy regex restored 11
    kit label drifts from the spec 3
    kit tone drifts from the spec 3
    spec REFUNDED changed to settled 6
    dashboard refunded badge changed to green 1

    With every mutation restored, the suite is green again.

Scope notes

  • SWFDashboardPage's green active badge renders a mock epoch table, not escrow state. Retiring mocks is the product-shell lane's job, so it is deliberately untouched.
  • The EscrowPage milestone badge uses a work-completion vocabulary (fulfilled) with an honest gray default, so it is also untouched.
  • One residual is left for Wave 1: a generic list window bound to money objects still uses the generic classifier. The structural fix is the closed IR (feat(genui-b): closed-IR read-only /mcp/apps render surface (additive, sol-GO) #272), server-owned schema cards, and the state-provenance envelope.

Review ask

Cross-family review requested. gateway / readmodels: please check the vocabulary semantics, especially RELEASED rendering green and bare SETTLED rendering unknown.

Follow-ups (not in this PR)

  • SettlementResultDTO.status should carry an unambiguous state (its settled collides with the V-next phase word).
  • Unify the duplicate dashboard EscrowStatus type with @pcc/spec (readmodels).
  • #307 toPlanCard should consume classifyMoneyStatus.
  • After deploy, verify that the kit asset actually served changes. Prod runs 75e013bf.

Supersedes feat/agent-console (a41e6f46). Ledger row 37 / STATUS-BOARD PX-1.

🤖 Generated with Claude Code

https://claude.ai/code/session_01P7daTDp5o3mAwxYNYyRCVy

LamaSu and others added 6 commits September 2, 2026 13:12
…o refund-as-payment)

statusClass() used a greedy substring regex that rendered any status containing
settl/releas/complet/done/paid/funded/success/approved/active as a green "settled"
pill. So a REFUND ("refunded" contains "funded"), an UNRELEASED/INCOMPLETE/UNSUCCESSFUL/
NOT_APPROVED/INACTIVE/UNDERFUNDED, and every *_ALLOCATED (decided, not final) rendered as
a completed PAYMENT. A refund shown as a payment is the read-route contract's forbidden-
asserter CRITICAL, live in the shipped on-ramp kit.

Replace it with an EXACT normalized-status map keyed off the sec-A conformance table
(V-next finalState/unitState, the 10-state machine) plus the legacy EscrowSummaryDTO enum:
- SETTLED_RELEASED / COMPLETED -> st-settled (operator distribution discharged)
- SETTLED_REFUNDED / REFUNDED  -> new non-green st-refunded ("payer refunded - operator NOT paid")
- RELEASE_ALLOCATED / REFUND_ALLOCATED / in-flight (0-5) / FUNDED / CREATED -> st-waiting
- unmapped / unknown -> new neutral st-unknown, NEVER green (fail closed)
No substring inference for money state. Add honest sec-A direction labels on the settlement
rail, and drop the dishonest rail fallback that inferred 'settled' from releasedCount
(contract rule 12: never key settlement off count/receipt existence).

Adversarial conformance test extracts the shipped <status-map v1> region verbatim (tests the
real bytes, not a copy) and asserts the sec-A table, the never-green invariant, normalization
(casing/whitespace/separators), generic-state tones, and that the old regex is gone. 7/7 green.

Refs: genui-read-route-contract sec-A + rules 1/12; genui conformance matrix v1.4.
Cross-family (sol/GPT-5.6) verdict: the one-line /refund/ guard is insufficient; this is the
exact-map it prescribed. gen-UI owns the fix (defect is in gen-UI's shipped kit + contract).
…d/dispute as green)

EscrowPage.tsx rendered the escrow status GlowBadge with `... : "green"` as the ternary
DEFAULT, so any status other than active/funded -- refunded, disputed, created, unknown --
rendered GREEN: a refunded or disputed escrow shown as a completed payment. Same false-green
money-display bug as the ui-kit statusClass fix, in a parallel formatter (the blast radius sol
predicted: "fixing one helper doesn't help if dashboards independently infer status").

Replace with an exact map defaulting to gray: completed -> green, disputed -> red,
active -> gold, everything else (funded/created/refunded/unknown) -> gray. Matches the
already-honest MilestoneTimeline statusColors + read-route contract rule 1.

Type-correct by inspection (all branches return valid GlowBadge colors: green|gold|red|gray);
a full dashboard typecheck/build is Spark-offload territory and was NOT run here.
Bring PR #313 current with master before extending it (dry-run merge-tree was
clean; no conflicts). No force-push: the PR history is preserved.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7daTDp5o3mAwxYNYyRCVy
…state table

Master had five competing escrow-status vocabularies (EscrowStatus,
Escrow.status, the dashboard DTO, the V-next UnitState, the context-pack
summary) and every surface re-inferred them; the shipped kit's substring regex
rendered a REFUND as a green completed payment (read-route contract rule 1).

MONEY_STATUS_MAP is the ONE exact table: normalized key -> semantic tone +
honest, direction-explicit label. Green ('settled') is reserved for the three
documented FINAL releases to the operator (SETTLED_RELEASED, COMPLETED,
RELEASED). Refunds are 'refunded' (final, operator NOT paid); allocated-not-
final states are 'waiting'; unknown fails closed. Bare SETTLED is deliberately
unmapped: SettlementResultDTO uses it for operator-paid but the V-next phase
vocabulary uses it for BOTH released and refunded.

Frozen at every level; compile-time coverage records make tsc fail if
EscrowStatus / Escrow.status gains a value with no entry. Browser-safe.
8 tests (sec-A table, never-green invariant, unknown fail-closed,
normalization, full vocabulary coverage, immutability).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7daTDp5o3mAwxYNYyRCVy
…ifier

- <status-map v2> mirrors @pcc/spec MONEY_STATUS_MAP verbatim (the vanilla
  kit cannot import it) and now covers every documented escrow vocabulary, so
  canonical states like released/slashed/releasing/expired no longer render
  'unknown'.
- New moneyStatusClass for MONEY surfaces (the receipt window): money table
  only. An off-schema 'success'/'done'/'ok' on a money response is NOT a
  settlement state and never renders paid (it still tones a generic run/list
  surface via statusClass).
- Conformance test moved into CI: #313's node --test file lived under
  apps/dashboard/public/ (served publicly) and no CI job ran it. The new vitest
  file (a) extracts the shipped region verbatim and proves it equals the spec
  map key for key and agrees with classifyMoneyStatus over an adversarial
  battery, and (b) boots the WHOLE kit in jsdom and asserts the rendered receipt
  pill: refund / allocation / off-schema success / missing status (rule 12) are
  never settled-green. 19 tests.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7daTDp5o3mAwxYNYyRCVy
EscrowPage's escrow badge had its own inline ternary (a sixth formatter). It
now calls moneyBadgeColor(), which takes the semantic tone from @pcc/spec
classifyMoneyStatus and only maps tone -> GlowBadge color: green is reserved
for a documented FINAL release; refunded / allocated / unknown are never green.
GlowBadge itself defaults to green, so money badges must pass an explicit color.

A compile-time record lists every value of the dashboard's EscrowStatus type
(tsc fails if the type gains one) and the test asserts each is a KNOWN state in
the canonical map, so no real escrow status renders 'unknown'. 3 tests;
dashboard tsc --noEmit clean; full dashboard suite 223/223.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7daTDp5o3mAwxYNYyRCVy
LamaSu and others added 3 commits September 24, 2026 13:57
…id; route data surfaces by data

First part of the #313 review fixes (astra via coord-watch #2465, product-qa #2594, reviewer-bravo):
- Normalization rejects rather than erases: only a plain status string ([A-Za-z0-9 _-]) is
  classified. "RELEASED?", "❌RELEASED", "releaſed", ["RELEASED"] and objects whose toString
  returns a green word are now unknown. Spec and kit mirror are changed together.
- COMPLETED is no longer green. It ends many non-money DTOs (jobs, A2A, steps, batch claims
  where paid != completed), so it now reads "completed - settlement not confirmed".
- List rows and run status pick their table from the DATA: money unless the binding is a known
  non-money read and the row carries no money field. So an escrow row's "success" or "done" is
  never green, while a completed job still is. Generic surfaces check generic states first.
- The EscrowPage milestone badge now comes from the canonical map (no dead "fulfilled" green).

Tests: money-status 35/35 (+8); full spec 832/832; dashboard 223/223; the 7 gateway kit suites
95/95; tsc (spec, dashboard) clean. More #313 fixes follow (source-schema receipt adapter).

agent: pcc-genui (4df1e691)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7daTDp5o3mAwxYNYyRCVy
…el; no bare-word green

Second part of the #313 review fixes: steward rulings #2490/#2688, astra (coord-watch #2465),
escrow #2580, product-qa #2594.

- Classify by SOURCE SCHEMA: classifySettlementRecord (spec) / settlementRecordClass (kit mirror):
  - V-next /lifecycle: unitState is an ordinal 1..9, pinned to VNextSettlementLib.sol's
    `enum UnitState`; 0 is a read error. finalState, isAllocated and isTerminal must agree, and
    a final state needs all three present.
  - V-next /receipt: finalState counts only when terminal with isAllocated true.
    `null` + allocated reads "decided, not yet paid out".
  - A legacy escrow record goes through the flat table on its status.
  - Anything else (a job, an A2A task) is "not a settlement record".
- The flat word table has NO green entry: bare SETTLED_RELEASED, RELEASED and COMPLETED are not
  settlement reads. The only green is a consistent V-next state 8. AWAITING_FUNDING is unknown.
  Labels are fixed per escrow F4-F6: "not final", "payer not yet refunded", "payout distribution
  discharged", "payees NOT paid".
- Receipt: state by schema; nothing invented (no default "USDC", "payer"/"payee" or
  "escrow-milestone"; missing values read "not reported").
- Run window: a read model by its schema; a full snapshot with no status reads unknown (an
  earlier green is never kept). List/run money rows that are read models use the schema.
- The kit tables are frozen. Compile-time coverage now constrains the real map (`satisfies`).
- EscrowPage: the "Total Locked" and selected-escrow panels no longer glow green.

Tests: money-status 18 + conformance 37 = 55. They cover wire fixtures for states 0-9, every
cross-check disagreement, missing corroboration, receipt shapes, job/A2A records, the kit==spec
adapter over the battery, the ordinal table re-read from the Solidity enum, frozen tables,
no-invention, run null snapshot, and money rows. Full spec 852/852; dashboard 223/223 (after a
spec build); gateway kit suites 95/95; tsc spec + dashboard clean.

agent: pcc-genui (4df1e691)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7daTDp5o3mAwxYNYyRCVy
…rrency

Pins the no-invention rule for the currency line (the earlier test used a receipt with no
amount, so the currency branch never ran; mutation M8 survived). M8 is now red.

agent: pcc-genui (4df1e691)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7daTDp5o3mAwxYNYyRCVy
LamaSu added a commit that referenced this pull request Sep 24, 2026
…els-job-execution

Brings PX-1's fixed money-status map into PX-6 (steward #2688, genui #2743):
bare status words are never green, 'completed' is never paid, and settlement
tone comes only from classifySettlementRecord.

The merge applies cleanly. Under the new map a milestone record's 'released' is
no longer a settled tone, so the next commit adapts the payout axis (5
settlement-axis tests fail at this merge commit alone).

agent: pcc-readmodels (c255d7dc)
LamaSu added a commit that referenced this pull request Sep 24, 2026
…_released, never paid

#313's fixed map (merged in the previous commit) makes every bare status word a
non-green tone, following steward ruling #2490: settlement tone comes only from
authoritative money state. A milestone record's "released" is now a waiting
tone, so reconciling by tone would have reported a recorded release as
"not_paid": a false statement.

The payout now reads the gateway's escrow words exactly (their source schema is
the escrow_milestones and escrows tables):
- a released milestone (released, settled_released) that the escrow record does
  not contradict is the new payout state reported_released, with
  payoutConfirmation record_only. It is gray and says it is not confirmed. On a
  V-next escrow "released" can mean the outcome was allocated, not paid out.
- "paid" is reserved for an authoritative settlement read model (a V-next
  lifecycle or receipt that classifySettlementRecord shows as released). The
  gateway's escrow records never produce it; a test sweeps every word pair of
  the money map to prove that.
- a milestone saying "completed" is ambiguous (completion is not release):
  unknown.
- conflicts are unchanged: released vs a refunded, disputed or slashed escrow,
  and refunded or unreleased vs an escrow saying everything was released, are
  unknown.

This also answers coord-watch's open question on #353 (can payout be "paid" when
this job's money was not released?): no gateway record can make it "paid" now.

Gateway 3042 passed / 6 skipped / 0 failed; spec 861; dashboard 249, tsc -b
clean. Mutation check on the new rule: 5/5 killed.

agent: pcc-readmodels (c255d7dc)
LamaSu added a commit that referenced this pull request Sep 24, 2026
…sign #3013)

A job row can literally say "settled" with nothing paid (#353's notice
job_row_reports_settled), so the Run card could read "Status: settled", and a
job list badge or a status metric likewise. Colour was already neutral; the
text was not.

Any value read from a field named `status` (status, job.status, kernel.status)
whose word is a money state now carries PCC's fixed qualifier "- reported by
the record, not confirmed by a settlement read" (the #313 wording), in every
sink: metric (bindScalar), run card (bindSchemaCard), list badge and list meta
(bindListRows). The money-state word list is narrower than the prose CLAIM_RE
("verified"/"approved" are not money states); lookalikes, zero-width, fullwidth
and camel/snake/kebab joins are folded first. The kit is rebuilt (check:ir-kit).

Tests: 4 new, including the shipped kit end to end (review344 19/19).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7daTDp5o3mAwxYNYyRCVy
…ocated/phase semantics

#313's classifier assumed isAllocated was true for every allocated-or-final state (6-9).
The read routes say otherwise: gateway unit-state-mapper isAllocatedState is true for 6/7
ONLY ("outcome decided, money NOT fully moved"), so a settled /lifecycle or /receipt body
carries isAllocated:false. A genuinely settled unit therefore rendered "settlement fields
disagree" and was never shown as settled -- fail-closed, but wrong, and the hand-written
test fixtures encoded the same assumption (one even pinned the real settled receipt as
unknown).

Now, in the spec classifier and the kit mirror alike:
- lifecycle: allocated = state 6 or 7; `phase` must match VNEXT_PHASE (the mapper's
  PHASE_BY_STATE) when present, and a final state needs finalState, isAllocated,
  isTerminal AND phase;
- receipt (finalState, phase, isAllocated): final only with isAllocated:false and
  phase:"settled" (both present); isTerminal, if present, must agree; "allocated" and
  "in flight" cross-check phase too.

A new gateway suite takes its fixtures from the routes themselves (Fastify inject over a
fake reader) for states 1-9 and classifies every body with the spec AND the shipped kit:
only state 8 is green, from either route, and each tampered field is unknown.

Tests: money-status 18 + conformance 39; spec 854/854; dashboard 223/223; new gateway
suite 3/3.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7daTDp5o3mAwxYNYyRCVy
LamaSu added a commit that referenced this pull request Sep 24, 2026
…the v1.5 design contract

The preserved v1.5 contract describes the design DTO, but master's routes were built to it
through escrow's DTO mapping (#667), which differs: finalState is null (never *_ALLOCATED)
for states 1-7, isAllocated is true for 6/7 only (a settled body says false), phase is
emitted, receipts answer 200 for states 1-5, state 0 is a 503, and several design fields
are not emitted yet. Nothing said so, and #313's classifier was built on the wrong
assumption (settled units never went green; fixed in #313 @7061730a).

Adds an "Implemented on master" section (verified against settlement-read.ts and
unit-state-mapper.ts at ac86a40) with the consumer rule for settled-green, and a pointer
on the golden matrix. The design text is unchanged; the golden-vs-implementation choice is
left to the route owners.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7daTDp5o3mAwxYNYyRCVy
…lay sum

The receipt window fell back to economics.amount and formatted it with fmtUsd. On the
real /receipt route that field is a raw integer in the token's BASE units (read-surface
contract rule 14) and the route sends no tokenDecimals, so a 1 USDC settlement
("1000000") would have rendered as 1,000,000.00.

Now economics.amount becomes a display amount only with the record's own tokenDecimals
(exact string arithmetic, no float); otherwise it is shown as "N base units (decimals
not reported)", with no invented currency. A malformed value is "amount not reported".
Top-level amount/totalAmount (legacy escrow records, already display units) are unchanged.

Tests: 3 new (conformance 42/42) using the route's exact EconomicsRecord shape.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7daTDp5o3mAwxYNYyRCVy
LamaSu added a commit that referenced this pull request Sep 24, 2026
Genui #3113 / #3151. 7061730 classifies settlement reads by the routes'
own isAllocated / phase semantics (isAllocated is true for states 6/7
only, so a settled unit no longer reads as "fields disagree"), and
de3a973 stops the ui-kit receipt window formatting economics.amount
(base units) as a sum. No conflicts. JobExecutionDTO shows no
economics.amount: its milestone.amount and escrowTotal.amount are the
escrow record's decimal strings, labelled as recorded.

agent: pcc-readmodels (c255d7dc)
… it (escrow ruling #3163)

Escrow ruled that master's settlement routes are the target and asked gateway to ADD the
staticcall-authoritative unitState to /receipt (additive), so a receipt consumer can tell
6 (release decided) from 7 (refund decided); genui keys that direction off unitState,
never off finalState.

A receipt with unitState enters the unitState branch, which required isTerminal for a
final state, and /receipt carries no isTerminal, so settled receipts would have
classified "incomplete" the day the field lands. A final state now needs unitState,
finalState, isAllocated and phase (every present field still cross-checked, isTerminal
included when present). Spec and kit alike.

Tests: the route-derived suite classifies each state's real /receipt plus the lifecycle's
unitState (states 1-9: same tones as /lifecycle, green only at 8, 6 vs 7 named) and four
tampers of it (4/4); money-status 60/60. Requiring isTerminal again turns it red.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7daTDp5o3mAwxYNYyRCVy
LamaSu added a commit that referenced this pull request Sep 24, 2026
… ruling #3163)

Escrow, the money-semantics owner for /receipt and /lifecycle, ruled that the target is
master's routes. §A of the conformance matrix now says so, row by row:
- 200 for states 1-9; 503 for state 0; 404 only for an unknown (or foreign) unit;
- finalState only for 8/9; isAllocated true for 6/7 only;
- #313's presentation labels, with 8 the only green.
The superseded #667 rows (404 for 1-5, finalState *_ALLOCATED for 6/7) are named as
such. The contract's open question is marked resolved, with the one additive gateway
change (unitState on /receipt) and the consumer rule (6 vs 7 from unitState, never
finalState).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7daTDp5o3mAwxYNYyRCVy
LamaSu added a commit that referenced this pull request Sep 24, 2026
Genui #3217: per escrow ruling #3163 the gateway will add unitState to
/receipt; classifySettlementRecord accepts that shape and names state 6
vs 7 from unitState. Today's bodies classify as before. No conflicts.

agent: pcc-readmodels (c255d7dc)
LamaSu added a commit that referenced this pull request Sep 24, 2026
#353 now carries #313 @8f946499 (settlement-read classification by the
routes' isAllocated/phase semantics; base-unit receipts; unitState on
/receipt) and scopes JobExecutionDTO's evidence through the job instead
of the never-written evidence_bundles.tenant_id. The one adaptation:
loadLegacySettlement stops passing the removed `tenant` load option (it
still refuses a job outside the caller's tenant before any read).

agent: pcc-readmodels (c255d7dc)
LamaSu added a commit that referenced this pull request Sep 24, 2026
Brings #313 @8f946499 and #353's evidence fix (read through the job, not
the never-written evidence_bundles.tenant_id). No conflicts; the gated
execution route drops its now-unused tenant variable, since gateJobRead
already refuses a job outside the caller's tenant.

agent: pcc-readmodels (c255d7dc)
LamaSu added a commit that referenced this pull request Sep 25, 2026
Brings #313 @8f946499 and #353's evidence fix (JobExecutionDTO reads a
job's evidence through the job, not the never-written
evidence_bundles.tenant_id). No conflicts.

agent: pcc-readmodels (c255d7dc)
LamaSu added a commit that referenced this pull request Sep 25, 2026
Brings #313 @8f946499 and #353's evidence fix through #389. No conflicts.

agent: pcc-readmodels (c255d7dc)
LamaSu added a commit that referenced this pull request Sep 29, 2026
product-steward asked for this (#3378): #352 and #313 conflicted in
EscrowPage.tsx, and #408 and #380 inherited the conflict. Resolved with
the recipe from #3124:
- The one conflicting hunk is the stats grid. It takes #352's side: the
  Total Locked panel stays removed, since it was a TVL sum with no read
  model. #313's only edit there was dropping that panel's glow.
- Everything else auto-merges, including #313's moneyBadgeColor on both
  escrow badges.

tsc is clean. The dashboard passes 284/284, @pcc/ui 17/17.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
LamaSu added a commit that referenced this pull request Sep 29, 2026
…shell-no-prod-mock

Brings #352's round-3 honesty fixes (astra pack 18) and #313's money-status
map (merged into #352 per product-steward #3378). Clean merge.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
LamaSu added a commit that referenced this pull request Sep 29, 2026
…shell-route-model

#354 now stacks on #352 (and so on #313), which answers astra's pack-19
finding 2. At #354's own head, App.tsx still passed kernelsOnline={2},
activeJobs={3} and networkStatus="connected". With #352, the dashboard shell
renders <LiveStatusBar /> instead. The RC2 merge order already puts #352
before #354. Clean merge.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
LamaSu added a commit that referenced this pull request Sep 29, 2026
…hat-held-actions

This brings #354's round-3 fixes (astra pack 19) into #413. The spatial and
agent workspaces now own only their own address, and every sign-in and
sign-out empties the query cache. It also brings #352's round-3 honesty
fixes and #313, which #354 now stacks on.

Conflict resolved: #354's new user-switch test in routing.test.tsx set the
key with useAuthStore.setState({ apiKey }). #368's store, which #413
carries, keeps the key out of state, so the test uses adoptApiKey(). That
still flips isAuthenticated, which is what App.tsx's cache-clearing
subscription watches.

tsc is clean and the dashboard passes 387/387.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
LamaSu added a commit that referenced this pull request Sep 29, 2026
…/shell-product-home

#419 lands after #352, so this brings in #352's round-3 honesty fixes
(astra pack 18) and #313. It carries #352's count-freshness rule over to
#419's ProductHome-based StatusBar.

Conflicts resolved:
- LiveStatusBar: keeps #419's ProductHomeDTO read. It adds #352's re-read on
  recovery, so ProductHome is re-read at once when the gateway answers again
  after a failed health check.
- lib/live-status.ts: the freshness rule is now one exported isCurrent(),
  used by both deriveLiveStatus and deriveHomeStatus. A read counts as
  current only if the gateway is reachable, the read succeeded, it came
  after the last failed health check, and it is at most COUNT_MAX_AGE_MS
  old. Before this, deriveHomeStatus showed any successful ProductHome read,
  so a read cached before an outage came back as current on recovery.
- live-status.test.ts: #419's deriveHomeStatus fixtures carry read
  timestamps now. Two new cases: ProductHome read before an outage stays
  hidden until it is re-read, and a read older than COUNT_MAX_AGE_MS is not
  current. Both fail without isCurrent.
- live-pages-honesty.test.tsx: in #419 the Command Center's Active Jobs KPI
  is ProductHome's exact count, a separate read. So the malformed-/api/jobs
  case now asserts that the job list is unavailable and not shown in part,
  instead of asserting that the KPI is missing.

tsc is clean and the dashboard passes 328/328.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
LamaSu added a commit that referenced this pull request Sep 29, 2026
…rdening

product-steward #3377: so the operator can merge #313 and then #342 without a conflict.
The one conflict is adjacent pill CSS in pcc-ui.js: #313 adds .pcc-pill.st-refunded and
.pcc-pill.st-unknown after st-running, #342 adds .pcc-pill.st-ack there; all three kept.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
LamaSu added a commit that referenced this pull request Sep 29, 2026
…-truth

Shell #3746: #352 now carries astra's round-3 honesty fixes and #313 @8f946499,
with the EscrowPage conflict resolved. No conflicts with #380, and no product
code of #380 changes. Verified on the merged tree: typecheck clean, dashboard
vitest 344/344 (15 files). No force-push (board rule 3).

agent: pcc-operator-ux (f0734fab)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
LamaSu added a commit that referenced this pull request Sep 29, 2026
…st (PX-3)

Product review of #425 (product steward #4050); both notes were reproduced
with failing tests first.

1. flushOutcome answered any successful flush with "Settled epoch N". A flush
   hands the epoch's operations to the bundler as UserOperations, and its
   answer carries their hashes, not an on-chain receipt (#313: accepted is not
   settled). The page now says "The gateway reports epoch N flushed: X
   operations in Y batch(es)." The epoch history is the same record, so its
   labels now read "Epochs Flushed" and "No epoch has been flushed since the
   gateway last started."
2. Manual Flush, an operational money action, ran on one click. It now asks
   first. The confirmation names the pending count, what a flush does (batched
   ERC-4337 UserOperations that act on escrow), and that it cannot be recalled.

agent: pcc-readmodels (c255d7dc)
… route (astra r2 on #313)

astra (gpt-5.6-sol) round 2 at 8f94649: DO-NOT-SHIP. Each finding was reproduced FIRST with a
failing test against 8f94649's kit (verify-before-fix), then fixed:

- F1 HIGH, shape is not provenance: a settled-SHAPED body bound to /api/jobs/j1 rendered green,
  and so did a baked (unsigned) snapshot. New classifySettlementRead(record, {path, live}) in the
  spec and settlementReadClass in the kit: a FINAL V-next state (settled 8, refunded 9) is shown
  only for a LIVE read of an exact per-unit settlement route
  (SETTLEMENT_READ_ROUTE = /api/settlement/units/0x<64 hex>/(receipt|lifecycle), the route's own
  UNIT_ID_RE). Snapshots, fallbacks, stream events and other routes read "final state not shown -
  not a live read of a settlement route". Receipt, list and run windows pass their liveness.
- F2 HIGH: the generic table painted success/done/completed/ok green, so money data the routing
  heuristic missed (nested economics) turned green. Green means money finally reached the payee
  and nothing else is ever green: generic success words are now a NEUTRAL st-ack.
- F3 MEDIUM: a failed poll kept an earlier green. It now reads "unknown · read failed".
- F4 LOW: classifySettlementRecord's doc comment described the old /receipt rule; rewritten to the
  implemented semantics, and it now says display must go through classifySettlementRead.

Eight earlier expectations encoded exactly what F1/F2 flagged (green from snapshots and from
generic "completed"); they now assert the fixed behavior. New: live-mode checks (exact route,
?asOf, invalid unit id, other leaves, the direction label), kit == spec parity for the gate over
records x sources, and the gateway suite runs the gate on the routes' real bodies.

Tests: spec 864/864; gateway settlement suites 32/32; dashboard 223/223.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
LamaSu added a commit that referenced this pull request Sep 30, 2026
…erdict

Verify-before-fix pass against 29b-doc-350-r2-runner-ed229845.astra.verdict.md.
All 4 MEDIUM findings reproduced and fixed in the two docs only (no route/mapper/
sol files touched):

- D1: Route 3 text + rule 17 table + design-DTO finalState still contradicted the
  doc's own "Implemented on master" section (states 1-5 return 200/null, never
  invisible/no-receipt; finalState is terminal-only). Rewrote both, marked
  superseded-by-#3163, fixed matrix §B fixture 1's refundReason NONE -> null
  (RefundReason has no NONE member).
- D2: finalizedBlock definition only covered the deferred (dischargeClaim) path.
  Confirmed via VNextSettlementEscrow.sol (_allocateRelease/_allocateRefund set
  SETTLED_* directly when remainingClaimCount hits 0 inline, no ClaimDischarged
  emitted) and a live route trace (fake reader, state 8, complete index, no
  zeroing-discharge block -> /receipt returned finalizedBlock: null). Documented
  both inline/deferred shapes, added inline+deferred matrix fixtures (1a/1b), and
  an explicit implementation-gap note (gateway-owned, SettlementUnitReader needs
  an allocation-block-aware read).
- D3: doc presented classifySettlementRecord and settlement-read-money-status.test.ts
  as implemented/shipped; neither exists at ed22984 (git cat-file -e fails;
  zero grep hits) or anywhere on this branch -- both live only on unmerged PR #313.
  Reworded to explicit pending/unmerged language in both docs.
- D4: doc specified TENANT_FORBIDDEN -> 403 while requiring it be indistinguishable
  from unknown-unit's 404 -- self-contradictory, and master always maps both to an
  identical 404 UNKNOWN_UNIT (settlement-read.ts:163-176,275-288). Removed the
  external 403 from the error union; TENANT_FORBIDDEN is internal-only.

Evidence, per-finding confirmation questions, and the temp-test trace log are in
/mnt/sparkbulk/pcc-reconciliation/returns/pcc-genui-work/triage-350-r2.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
LamaSu added a commit that referenced this pull request Sep 30, 2026
…he consumer rule

Review of docwriter-alpha's 14fd55a (D1-D4, accepted as is). Two factual syncs:
- the pending-code notes named #313 @8f946499, which has since moved; they now name
  the PR (still unmerged) without a SHA;
- the consumer rule now states #313's round-3 rule: a final presentation needs a
  LIVE read of the exact per-unit settlement route (never a snapshot or another route).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
LamaSu added a commit that referenced this pull request Oct 1, 2026
Finding: any 2xx flush body lacking the expected fields returned
{ ok: true, message: "The gateway accepted the flush." }, asserting an
acceptance the gateway's answer never established. Repro at 58a888b:
flushOutcome(200, {}) returned ok:true (2 tests failing, see below).

Fix: flushOutcome now requires the full flush contract on a 2xx — epoch,
totalIntents and batches as safe non-negative integers, and (when present)
a batchDetails array whose length matches `batches` and whose entries each
have a real UserOp hash (0x + 64 hex), a safe operationCount and a trigger
in {manual,size,age,value}. Anything else is ok:false with the same shape
message a malformed status/epochs read already uses. isCount now uses
Number.isSafeInteger (not isInteger) throughout the module. The success
wording still never says "settled" (#313 kept green).

Updated the old test that had permitted {} and null success bodies to
assert ok:false instead, and fixed an unrealistic "0xab" placeholder hash
in the #313 fixture to a real 64-hex value.

agent: implementer-charlie for pcc-readmodels (c255d7dc)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant