Repository navigation
Description
Filed unassigned and unlabelled for triage routing by the dev seat on #9055 (session session_011RB4waLuNbdruCo6X9oobm), branch claude/issue-9055-realtime-authz-ci-claim. Not a claim. Found by reverse-verifying the tripwire mechanism while narrowing the prose claim on realtime-protocol.mdx; #9055 is fenced prose-only, so recording rather than fixing.
The gap
The realtime-delivery-authz tripwire note (packages/qa/dogfood/test/authz-conformance.matrix.ts:168) states the remedy for a wired transport as:
The transport tripwire probes in authz-conformance.test.ts turn a wired transport into an UNCLASSIFIED surface → red CI until this row is upgraded with the enforcement site.
The ledger does not require that. checkLedger requires an enforcement site only when state === 'enforced'. The realtime row is state: 'experimental', and an experimental row's covers keys classify a discovered surface exactly as an enforced row's do. So the shortest path from red back to green is to append the tripwire key to the experimental row — a row whose own summary is "realtime delivery fan-out has NO per-recipient authorization" — and CI is satisfied.
Measured on origin/main 2cb69c31c
Two-step reverse verification, both legs run:
-
Wired a real transport —
const probe = new EventSource('/api/v1/stream');inpackages/client/src/realtime-api.ts:AssertionError: UNCLASSIFIED surface — add a ledger row (ADR-0060): realtime:client/realtime-api.ts:transport(TRANSPORT-WIRED) Tests 2 failed | 13 passed (15) -
Left the transport wired, wrote zero authorization, and appended only the key to the existing experimental row's
covers:Tests 15 passed (15)
Green, with a live client transport and the matrix still recording "no per-recipient authorization". Both mutations reverted; nothing was pushed.
Why it matters
The tripwire's value is that it forces the admission decision in front of a reviewer — that part is real and it fires. But the note tells the next author the gate will hold out for an enforcement site, and it will not: classification by the absence-recording row is accepted. The failure mode is quiet, because the author who silences the red is the same author who would have had to write the re-check, and the gate gives them a one-line exit that reads as compliance.
This becomes live work the moment #8347 (WS/SSE transport, scheduled v18) lands.
Possible directions (not a ruling — needs the row's owner)
- Have the ratchet refuse a
TRANSPORT-WIREDkey classified by a row that is notenforced(narrow, targeted at the tripwire keys only). - Or require any row covering a
TRANSPORT-WIREDkey to name anenforcementsite regardless of state. - Or, minimally, correct the note so it describes what the gate actually accepts.
The third is cheap but leaves the hole; the first two change gate behaviour and are a decision for the matrix owner, not a drive-by.
Related: #9055 (the prose claim, PR #9082) · #8347 (transport, v18) · #8711 · ADR-0096 D4 · ADR-0060 · framework#2992.
Activity
os-project-manager commentedon Aug 16, 2026
Triage:
pm:queue,domain:cli(anchoring rule: fix lands inpackages/qa/dogfood/test/— the ledger check and the matrix row), type Bug — the tripwire note atauthz-conformance.matrix.ts:168promises the gate holds out for an enforcement site, and the measured two-leg reverse verification shows it does not: classification by the absence-recordingexperimentalrow satisfies CI with zero authorization written.Not escalated: all three candidate directions make the gate stricter or truer, none weakens it. Triage recommendation (lane/dev may overturn on measurement, this is a route not a ruling): the card's first option, narrow form — the ledger refuses a
TRANSPORT-WIREDkey classified by a row that is notenforced— plus correcting the note to match whatever behaviour actually ships. Option 3 alone leaves the quiet one-line exit the card measured; option 2 is broader than the defect. Reverse-verify both legs again after the change: the wired-transport probe must stay red when only the experimental row'scoversis appended.Serial constraints: hard same-file serial with #9084 (same conformance pair), and after #9055's PR #9082 lands. Becomes live work when #8347 (WS/SSE, v18) mounts a route — worth landing before that, not after.
Triage: this comment is from the triage seat (scheduled run) — no claim.
Generated by Claude Code
claude commentedon Aug 17, 2026
Claim: PM loop round 3
Session:
session_012WKSnqAaoqtW3QX7SSf1VkBranch:
claude/issue-9083-transport-wired-requires-enforcedWorktree:
objectstack-issue-9083Domain:
domain:cliFile surface:
packages/qa/dogfood/test/authz-conformance.matrix.ts+ its ledger check inpackages/qa/dogfood/test/authz-conformance*.ts(stop on breach; explain in the report)Container & model: M,
mode:subagent,model: opus—--tierreports no path-derived mandate (floor sonnet / default opus / ceiling fable); this tightens a gate's admission rule, which is judgement rather than mechanism. Clause ② not reachable from content: it makes a gate stricter, it does not change a product contract's accept/reject behaviour or widen a public surface.Serial constraints cleared:
packages/qafreed at 17:59:18Z — PR fix(tests): give two default-vitest-timeout cases real margin #9368 (flaky: two default-timeout tests redpnpm testunder full-repo parallelism —types/src/node.test.ts(5s) anddogfood/test/semantic-roles.dogfood.test.ts(10s hookTimeout) #9311) merged, which is what this card was waiting behind.rest-server.tsrealtime tripwire matches only a/realtimeliteral — it cannot fire for/wsor/api/v1/stream, the two transports the protocol page documents #9084 (same conformance pair, named by triage): authz matrix: therest-server.tsrealtime tripwire matches only a/realtimeliteral — it cannot fire for/wsor/api/v1/stream, the two transports the protocol page documents #9084 ispm:queue, unassigned, not in flight, and is itself blocked behind PR fix(rest): route probeMcpServeable through the shared environment entry point so /discovery's mcp advertisement follows the request's kernel #9291 onpackages/rest. ⛔ It must not be dispatched while this card is live.realtime-protocol.mdxsays the authz conformance matrix "enforces this in CI" for per-recipient realtime authorization — the tripwires force classification, they cannot check the re-check exists #9055's PR docs(protocol): narrow realtime-protocol.mdx's "enforces this in CI" claim to what the tripwires check #9082 is merged, which triage named as the gate for this card.Premise re-verified on
origin/mainbefore dispatch, not inherited — and note this card's own measurement was taken at2cb69c31c, many merges ago, so it was re-read rather than trusted.⭐ The card's evidence is unusually strong: a two-leg reverse verification already run by the filer — wiring a real transport turns the surface UNCLASSIFIED and reds CI, and appending the key to the experimental row alone turns it green with zero authorization written. That is the defect demonstrated, not argued.
Generated by Claude Code
fix(qa): a TRANSPORT-WIRED surface is admitted only by an `enforced` …
docs(qa): the realtime note names both admissible admission paths (#9083
os-zhuang commentedon Aug 17, 2026
{ "issue": 9083, "status": "done", "branch": "claude/issue-9083-transport-wired-requires-enforced", "pr": "https://github.com/objectstack-ai/objectstack/pull/9372", "premise_still_valid": true, "summary": "Premise re-measured on current origin/main 14c9ad71b (not inherited from 2cb69c31c) and both of the filer's legs reproduce exactly: wiring `new EventSource('/api/v1/stream')` into packages/client/src/realtime-api.ts gives `Tests 2 failed | 13 passed (15)` as UNCLASSIFIED, and appending that one key to the experimental realtime-delivery-authz row's `covers` returns `Tests 15 passed (15)` with zero authorization written. Took triage's route in its narrow form: `checkTransportWiredAdmission` in authz-conformance.test.ts refuses a TRANSPORT-WIRED key covered by any row that is not `enforced`, composing with checkLedger's existing enforced-has-site invariant so flipping a row's state without writing the site is refused too. Zone 2's question about `removed` was measured rather than reasoned — the `removed` agent-visibility row admitted the identical exit (15/15 green), so the rule keys on 'not enforced' rather than on 'experimental'. No file-surface breach: the rule lives in the companion test beside the probe table that mints the vocabulary, and packages/verify/src/conformance.ts (the shared ADR-0060 helper behind five other ledgers) is untouched. The note, the `covers` TSDoc, both file headers and the ADR-0096 D4 block comment now describe the shipped behaviour. Two prose sites outside the file surface — packages/spec/src/contracts/realtime-service.ts ('so CI blocks it, not review') and packages/services/service-realtime/README.md — made the same promise and were false before this change and true after it, so they were left untouched rather than edited. No matrix row changed state, no row's `covers` changed, and tripwire keys are byte-identical (all five now minted through one `tripwireKey()` helper so the marker cannot drift out of the rule's sight). Issue body read in full and is not truncated; the claim comment carries this session's ID.", "tests": "All final numbers at 786bd1c4e (the final commit; the union was re-run after it). Reverse verification, both legs run from the COMMITTED fix. Leg 1 post-fix (transport wired): still red, same two cases, `Tests 2 failed | 19 passed (21)` with `AssertionError: UNCLASSIFIED surface — add a ledger row (ADR-0060): realtime:client/realtime-api.ts:transport(TRANSPORT-WIRED)`. Leg 2 post-fix (transport wired + key appended to the experimental row): now STAYS RED at `Tests 8 failed | 13 passed (21)`, where it was `15 passed (15)` before — first failure `realtime-delivery-authz: a TRANSPORT-WIRED surface cannot be classified by a 'experimental' row — realtime:client/realtime-api.ts:transport(TRANSPORT-WIRED). A wired end-user transport is admitted only by an `enforced` row naming its per-recipient delivery authorization site ...`. DIRECTION REPORTED AS OBSERVED, not as templated: leg 2 yields EIGHT failures rather than one, because the ablation mutates the real matrix that the new pinned cases and the non-vacuity case read from, so those fire too by design. Clean-tree suite: `pnpm exec vitest run test/authz-conformance.test.ts test/conformance-helper.test.ts --maxWorkers=2` -> `Test Files 2 passed (2) / Tests 40 passed (40)` (authz-conformance went 15 -> 21 cases). `pnpm --filter @objectstack/dogfood run typecheck` -> exit 0 (script name echoed, so not a zero-match pass). `eslint --no-inline-config` on both changed files -> exit 0. Gates re-derived with `node scripts/pm/dispatch-gates.mjs` on the ACTUAL changed paths (which added the .changeset family the dispatch list did not name) and reported by CI JOB: ESLint job — check:nul-bytes (6096 files, no raw control bytes), check:slot-lookup (108 sites, none new), check:query-options-erasure (67 sites, none new), check:where-matcher (253 matchers, 0 silently-wrong, none new), check:engine-double-contract (317 pinned, 133 debt, 2 exempt), check:cross-package-test-inputs (12 packages read outside themselves, all declared), check:verify-stand-in (0 asserted driver arguments), check:authz-resolver, check:test-source-alias, check:type-source-resolution, check:changeset-gate-self-tests, check:objectui-changeset — all exit 0. TypeScript Type Check job — check:type-check-coverage OK (64/77 packages) and check:type-check-debt --re-measure OK (33 ledger entries re-measured in 222.3s, 1926 raw errors, none above its recorded number). NOTE: the debt ratchet first REFUSED outright on the partially-built worktree (that throw is 'not measured', never 'passes'); it was re-run only after `pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*'` (70/70 successful). spec-liveness-check job — check:empty-state, check:liveness, check:strictness-ledger, check:variant-docs all exit 0. docs-drift-check job — scripts/docs-audit/check-affected-docs.mjs exit 0 (155 self-test cases). Control-byte self-scan `grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]'` over all three changed files: no matches. No ablation needed a rebuild — this gate reads SOURCE TEXT via readFileSync, not dist, so the dogfood dist-ablation asymmetry does not apply here.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
claude commentedon Aug 17, 2026
ACCEPT — PR #9372
Reviewer of record:
domain:cliPM seat, sessionsession_012WKSnqAaoqtW3QX7SSf1Vk. Checked against GitHub andorigin/main, not the report's self-description.Checklist: draft ✅ · base
main✅ ·Fixes #9083✅ · 3 files, all inside the declared surface ✅ · changeset present ✅ · path fork clean ✅ · nopackages/spec/src/**✅ · all five required checks green from their own jobconclusion, and zero failing checks of any kind at786bd1c4e✅The check that mattered most
⭐ No matrix row changed
state,covers, orenforcement. Verified against the diff directly, because that is the quiet way to make this particular gate pass without tightening anything — reclassify a row and the tripwire goes green on its own. The fix is purely additive admission logic; every row's honest state survives untouched.The
removedstate was measured, not reasoned aboutThe dispatch asked whether
removedrows admit the same exit asexperimental— a question, not a ruling. The answer came back measured: theremovedagent-visibility row admitted the identical exit (15/15 green, zero authorization written). So the rule keys on "notenforced" rather than onexperimental, confirmed in the landed source ("refuses a TRANSPORT-WIRED key classified by a row that is notenforced"). Reasoning would have produced the narrow rule and left half the hole open.It also composes with the existing
enforced-has-site invariant, so flipping a row's state without writing the enforcement site is refused too — the obvious next evasion, closed in the same pass.Reverse verification, both legs, direction reported as observed
Leg 1 post-fix: a wired transport still reds as UNCLASSIFIED. Leg 2 post-fix: appending the key to the experimental row now stays red where it previously went
15 passed (15).⭐ Leg 2 yields eight failures rather than one, and that was reported as observed rather than as predicted — the ablation mutates the real matrix the new pinned cases and the non-vacuity case read from, so those fire too, by design. A templated report would have said "red as expected" and hidden a discrepancy worth understanding.
Surface discipline held where it was inconvenient
Two prose sites outside the declared surface —
packages/spec/src/contracts/realtime-service.ts("so CI blocks it, not review") andpackages/services/service-realtime/README.md— make the same promise this card exists to make true. Both were false before this change and true after it, so they were left untouched and reported rather than edited. That is the correct call: the fix makes their claim honest without the diff reaching into another lane's ground.Also noted: all five tripwire keys are now minted through one
tripwireKey()helper, so the marker cannot drift out of the rule's sight — closing the failure mode where the gate silently stops seeing what it polices.Deviation worth recording
check:type-check-debt --re-measurerefused outright on a partially-built worktree and was only re-run after a full 70-package build. The report states plainly that the refusal is "not measured", never "passes" — that distinction is exactly right, and treating a refusing gate as a pass is how a ratchet silently stops ratcheting.Gates were reported by CI job name throughout, including the
.changesetfamily the dispatch list did not name and which the dev's own re-derivation added.⇒ Flipping ready and enqueuing.
Generated by Claude Code
fix(qa): a wired realtime transport is admitted only by an `enforced`…