Skip to content

reader program 5/4 — @objectstack/verify: an option-B artifact makes os verify report a green run that measured nothing #15229

Description

@hotlong

Part of #14122 · blocks #14512 · split out of #15210 (the by-shape sweep record — read it for the sweep method and the triaged false positives) · acceptance pin is #15004.

Reader half of the program ruled on #14512 (comment 5528589044). The artifact stays additive through this card.

Why this one carries priority:p2 while the rest of the program is p3

Every other reader card loses a capability. This one loses the verification itself, and reports success while doing it.

file:line Reads What an option-B artifact does to it
packages/verify/src/derive.ts:176 config.objects deriveCrudCases derives ZERO CRUD round-trip cases
packages/verify/src/derive.ts:180 config.datasources the datasource-by-name map is empty, so every derived case loses its datasource
packages/verify/src/rls.ts:134 config.positions declaredPositionNames returns [] — no persona is built for any declared position
packages/verify/src/rls.ts:346 config.objects rlsProbePermissionSet builds an EMPTY probe permission set — the persona that makes an RLS run a probe grants nothing and narrows nothing

Reached today from packages/cli/src/commands/verify.ts:155 (declaredPositionNames(config) on the loaded config; rlsProbeSecurity / deriveCrudCases run off the same object).

So a multi-package app under option B would get a passing os verify that asserts nothing about any of its objects. A missing collection is at least missing — someone eventually notices the gap. Zero derived cases dressed as a green run is the failure mode #15004 exists to make loud, arriving in the one place where a false green is most expensive: the command whose entire job is to tell you the app works.

The pin, already on main

#15004 merged. Read all three before writing implementation:

  • packages/cli/test/fixtures/option-b-collection-zoo.ts
  • packages/cli/test/fixtures/option-b-reader-probe.ts
  • packages/cli/test/option-b-reader-acceptance.pin.test.ts

⚠️ Neither @objectstack/verify site has a row in OPTION_B_LOSSES today — which is exactly why the by-shape sweep, and not the pin, is what found them. So this card does both halves: add the rows that ledger the loss, then fix the readers and delete them. Do it in that order and say so in the PR, because a row that never existed is a row nobody can check you against.

The pin asserts by set equality: adding a row for a loss that is already fixed goes red just as surely as leaving a row for one that is not. ⛔ Never weaken the pin to pass — do not relax set equality, stop asserting a subsystem, or add rows to silence a red.

Acceptance

  • Both files resolve their collections through resolveArtifactPackageOrder (@objectstack/core, ADR-0130 D4+D5, since fix(metadata): register a packages[] artifact per package at the metadata door so every object has one owner across every door (#14599) #14643) — flattened top level first, so today's additive artifact answers bit-identically and packages[] only supplies what the top level lacks. That ordering is what makes this card revertible on its own and safe before the emitter half.
  • ⛔ Do not iterate config.packages directly — a second traversal is a second ordering.
  • The probe grows a row per subsystem above, and each is deleted by the fix in the same PR.
  • ⛔ Do not change composeStacks, packages/spec/src/stack.zod.ts, or what any command emits.
  • ⛔ Never edit content/docs/releases/.

Sibling cards: #15005 (@objectstack/runtime), #15006 (@objectstack/cli), #15007 (@objectstack/plugin-security, landed as PR #15226), 6/4 (@objectstack/plugin-dev). Emitter half: #14512.

Activity

  1. hotlong commented on Sep 4, 2026

    @hotlong
    ContributorAuthor

    Claiming this card.

    • session: session_01UHvF5hyiZjnCyExFnfQB8m
    • branch: claude/issue-15229-verify-option-b-readers

    Scope stays inside @objectstack/verify plus the @objectstack/cli acceptance probe rows (#15004). Order is the one the card fixes: add the ledger rows and show them RED first, then fix the four readers, then delete the rows.

    Generated by Claude Code


    Generated by Claude Code

  2. hotlong commented on Sep 4, 2026

    @hotlong
    ContributorAuthor

    os-dev-report

    {
      "issue": 15229,
      "status": "done",
      "branch": "claude/issue-15229-verify-option-b-readers",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/15281",
      "premise_still_valid": true,
      "summary": "All four `@objectstack/verify` reads now resolve through `resolveArtifactPackageOrder` (flattened top level FIRST, `packages[]` only where that came back falsy), so an option-B artifact no longer yields an `os verify` that derives zero CRUD cases, builds an empty RLS probe permission set, mints no persona for any declared position, and prints a green run. Done in the card's order and the history carries it: commit 8583bfbba adds four probe rows (all calling shipped readers) and shows them RED as unledgered losses, then ledgers them; commit 5c0de742c fixes the readers, the pin goes RED naming the four lines to delete, and the same commit deletes them; commit 4525eee11 adds the alias/`paths` rules `check:test-source-alias` and `check:type-source-resolution` prescribe for the two new cross-package imports (neither shrink-only registry widened). The datasource site needed a fixture member the zoo lacked — a write-opted-in federated object in one package gated by a federated datasource in the other — so the pin's anti-vacuity control list grows by that object; set equality, the asserted subsystems and the control itself are untouched, and the ledger is 24 rows before and after. `composeStacks`, `packages/spec/src/stack.zod.ts`, command emission and `content/docs/releases/` are untouched. Two contract notes for the PM: (1) per the dispatch base contract I never write `assignee` — the card was unassigned when it reached me and the claim comment carrying this session id and branch is the identity marker, which conflicts with this dispatch prompt's instruction to self-assign; (2) the REST read channel is 403 in this container and `gh` is absent, so GitHub reads and writes went through MCP.",
      "tests": "All measurements on the final head 4525eee11 (a merge of origin/main 460134af8), heavy runs through scripts/pm/os-verify-lock.sh. RED #1 (rows in, ledger lines absent): `A subsystem lost a collection that the ledger does not carry ... B2 · verify declaredPositionNames · positions / B2 · verify deriveCrudCases · objects / B2 · verify deriveCrudCases federated write gate · datasources / B2 · verify rlsProbePermissionSet · objects`, observed 0 / 'no case derived at all' / 0 / '0 granted object(s), 0 owner-scope rule(s)', with BASELINE green in the same run. RED #2 (readers fixed, ledger untouched): `A ledgered subsystem now SEES its collections under option B ... Delete these lines from OPTION_B_LOSSES:` naming the same four. GREEN after deletion: `pnpm --filter @objectstack/cli exec vitest run test/option-b-reader-acceptance.pin.test.ts` = Test Files 1 passed, Tests 6 passed. `pnpm --filter @objectstack/verify exec vitest run` (whole package, re-run after the vitest alias landed) = Test Files 10 passed, Tests 58 passed. `pnpm --filter @objectstack/verify typecheck` clean; `pnpm --filter @objectstack/cli typecheck` clean with its test-typecheck debt unchanged (3 files / 28 errors / 6 pinned signatures). Gates: `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` derived 42 for this change set, re-derived identical after the config commit; all 42 run, 40 green. NOT MEASURED, never counted green: `pnpm check:dual-build-cjs-loads` exit 3 `PREREQUISITE NOT MET` (reads built output; only the @objectstack/cli closure was built here, 12 packages have no dist/), and `node scripts/check-test-completeness.mjs` exit 3 (parses a full `turbo run test` summary a targeted run does not produce). `check:test-source-alias` and `check:type-source-resolution` were RED on first pass and are green after 4525eee11. 7 extra gates run beyond the derived list (console-intercept-disarm, test-completeness, published-files, type-check-coverage, cross-package-test-inputs, nul-bytes, cli-test-child-env). Every exit code captured before any pipe (`cmd > log 2>&1; EXIT=$?`); no ablation was performed. No CI wait: report filed at draft-PR time per the dispatch contract.",
      "mcp_calls": "11 — 5 issue reads (#15229 + its comments, #15210, #15004, #15007), 1 dedupe search_issues, 1 claim comment, 1 create_pull_request, 1 pull_request_read (body read-back), plus this report comment and its read-back.",
      "open_questions": [],
      "out_of_scope_findings": [
        "already filed as #15145: `packages/verify/tsconfig.json` excludes `**/*.test.ts`, so the package's `typecheck` script covers none of its 10 test files, including the one this PR adds. Re-measured here with a throwaway program that includes them: 3 errors, all TS2835 in three pre-existing `harness.*.test.ts` files, 0 in the new file. No new issue filed; the `paths` rule this PR adds lives in the build program and carries over unchanged when that card lands the `tsconfig.test.json` sibling."
      ]
    }

    Generated by Claude Code


    Generated by Claude Code

  3. self-assigned this
    on Sep 4, 2026
  4. hotlong commented on Sep 4, 2026

    @hotlong
    ContributorAuthor

    os-dev-report

    {
      "issue": 15229,
      "status": "done",
      "branch": "claude/issue-15229-verify-option-b-readers",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/15281",
      "premise_still_valid": true,
      "summary": "Task was conflict resolution on the already-reviewed PR #15281, not new implementation. Merged origin/main (638ea042d) into the branch as a MERGE commit ad514692f (no rebase, no amend, no force-push, head 4525eee11 stays in history). Two conflicts, both semantic: main's #15261 emptied OPTION_B_LOSSES and re-anchored the baseline anti-vacuity floor to a measured literal, while this PR adds four @objectstack/verify rows to the same probe. Resolution keeps main's empty ledger untouched (no line added to make anything pass), keeps BOTH row blocks in the probe (main's #15006 seam block first, this card's verify block after), and raises the floor 30 to 35 - the merged probe's measured row count. Set equality, the registryObjectsFromArtifact control, the five-boundary coverage test and main's #15006 four-site coverage test are all byte-untouched. The raise also closed an off-by-one it inherited: the floor read 30 against a probe that measured 31, so one row could have stopped being measured with nothing going red; it is now the exact count with no slack. Diff vs main is unchanged at the same 11 paths the PR always carried. Pushed head 5c9b4e872; mergeable_state now reads 'blocked' (required checks not yet concluded), no longer 'unknown' - the silent-conflict state that hid this for five hours is gone, and git merge-tree against the current origin/main (226e72443, 3 commits newer) returns a clean tree with exit 0. Auto-merge was NOT armed and nothing was approved or merged. PR body left byte-intact rather than PATCHed - its Tests table now understates the pin at 6 tests on 4525eee11 (7 on 5c9b4e872); the merge evidence is posted as PR comment 5541684675 instead, so no correct card was rewritten.",
      "tests": "All runs on final head 5c9b4e872 (git rev-parse --short HEAD), tree clean, heavy runs through scripts/pm/os-verify-lock.sh, exit codes captured by redirecting to a file BEFORE any pipe.\n\nFLOOR MEASUREMENT (the number is measured, not remembered). Mutation on a COMMITTED file, restore via 'git checkout HEAD -- ABSOLUTE_PATH' under trap EXIT INT TERM, absolute path seeded from git rev-parse --show-toplevel. Pre-check: tree hash 649b98e12 equals the HEAD blob hash 649b98e12 (a non-empty hash, compared, not assumed). Mutation proof on disk BEFORE reading anything: injected-text-count=1, removed-text-count=0, hash 649b98e12 -> 2ad17aa2a. With the assertion temporarily written toBe(-1) the run reports: 'AssertionError: The probe measured 35 rows ... expected 35 to be -1 // Object.is equality' (Test Files 1 failed (1), Tests 1 failed | 6 passed (7)). Restore verified: post-run hash 649b98e12 == HEAD blob, 'git diff HEAD' empty. First attempt at this measurement was VOID and is reported as such: the vitest path was passed repo-relative into a cwd of packages/cli, matched zero files and exited 1 with ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL - not a red gate, a NOT MEASURED run; re-run with the package-relative path.\n\nBOUNDARY REVERSE-VERIFICATION of the committed floor of 35 (predicted direction: turn red, and it did). LEG A, floor 35, no mutation, tree hash 162f6308b == HEAD blob 162f6308b: vitest exit 0, 'Test Files  1 passed (1)', 'Tests  7 passed (7)'. LEG B, floor mutated 35 to 36, mutation proof injected count=1 / removed count=0 / hash 162f6308b -> 1883792b8: vitest exit 1, 'AssertionError: The probe measured 35 rows, fewer than the 35 it measured when this floor was set ... expected 35 to be greater than or equal to 36'. RESTORE: post-restore hash 162f6308b == HEAD blob 162f6308b, 'git diff HEAD' empty.\n\nCROSS-PACKAGE BUILD DISCIPLINE: @objectstack/verify is a listed entry in KNOWN_UNALIASED_TEST_IMPORTS['@objectstack/cli'] in scripts/check-test-source-alias.mjs, so the CLI pin reaches it through its exports map to dist/ - every run above followed 'pnpm --workspace-concurrency=2 --filter @objectstack/cli^... build' (exit 0, packages/verify DTS emitted 2/2), and the whole workspace was later built with 'pnpm build --concurrency=2' (72/72 tasks successful) before the one dist-reading gate.\n\nSUITES: 'pnpm --filter @objectstack/cli exec vitest run test/option-b-reader-acceptance.pin.test.ts --maxWorkers=2' exit 0 - Test Files 1 passed (1), Tests 7 passed (7). 'pnpm --filter @objectstack/verify test' exit 0 - Test Files 10 passed (10), Tests 58 passed (58). 'pnpm --filter @objectstack/cli typecheck' exit 0 - tsc --noEmit clean and 'check:test-typecheck: OK - @objectstack/cli's test layer compiles under packages/cli/tsconfig.test.json; 3 file(s) / 28 error(s) / 6 pinned signature(s) held in test-typecheck-debt.json'; ledger byte-unchanged (git status empty after the run). 'pnpm --filter @objectstack/verify typecheck' exit 0.\n\nSCOPING PROOF for not running the full 185-file CLI suite: 'git grep -ln' over packages/ shows the two edited fixtures are reached only by packages/cli/test/option-b-reader-acceptance.pin.test.ts and packages/cli/vitest.config.ts. Of the 52 incoming main commits, the only ones touching packages/cli/test are the five option-B reader cards, all of which land in the pin and probe this run exercises; packages/verify received only a version bump.\n\nGATE FAMILY, derived not recalled: 'node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack' (exit 0, --repo assertion holds against this checkout's origin), change set = the same 11 paths the PR always carried. All 17 derived gates green: check:changeset-gate-self-tests, check:cli-test-child-env, check:cross-package-test-inputs, check:doc-authoring, check:dual-build-cjs-loads, check:logger-receiver-detach, check:objectql-double-limit, check:objectui-changeset, check:page-declaration-shape, check:pm-half-states, check:published-files, check:slot-lookup, check:test-source-alias, check:type-check-coverage, check:type-check-debt, check:type-source-resolution, check:where-matcher. check:dual-build-cjs-loads first returned exit 3 'PREREQUISITE NOT MET - this gate reads built output ... This is NOT a pass: nothing was measured' (12 packages outside the CLI closure had no dist/); after the full workspace build it re-ran exit 0 with its own verdict line '103 published require entry point(s) across 66 package(s) load; 619 emitted CommonJS file(s) parse'. The exit 3 was never recorded as a failure. check:nul-bytes run in addition (any edit owes it): OK, 7464 text files, no raw ASCII control bytes; plus a self-scan of every file in the diff for control bytes - zero hits. dispatch-gates printed a STALE TREE warning naming scripts/pm/dispatch-gates.mjs; diffed HEAD against origin/main for that file - the only change is comment prose inside selfTest() (commit b03d01ed3), so the derived family is unaffected. No gate or tooling script is in this diff, so no gate script's own vitest pin suite is owed.\n\nCI on the pushed head is in_progress at report time (combined status pending: Vercel deploying); per the dispatch contract the report is delivered at push time and CI convergence is the PM's read.",
      "mcp_calls": "5 - pull_request_read get, add_issue_comment on PR 15281, pull_request_read get_status, this comment, and the read-back of this comment. gh CLI is absent in this container, so the REST probe failed and MCP was the only write channel; all card and branch reading went through git.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code


    Generated by Claude Code

  5. github-actions commented on Sep 6, 2026

    @github-actions
    Contributor

    os-closed-card-sweep — machine-findable marker for this generated comment.

    Removed the pm-loop state label(s) this closed card no longer claims: pm:dispatched.

    A state label claims work is in flight. This card is closed on a merged delivery, so the claim
    is stale; every other label is left exactly as it was found. Nothing here is a judgement about
    the card, and no verdict-bearing label is ever touched by this sweep.

    posted by half-state-patrol run 34005012908 · trigger schedule

    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions