test(plugin-approvals): pin the lock release in the tenant-admin reverse check - #14750
Conversation
…rse check The `#12775` override-narrowing block's tenant-admin reverse check ("the narrowing is about status, not posture") proved a strict subset of the platform-admin case it mirrors: `recalled` status plus the action row, but nothing about the record lock. The platform-admin case pins `locked → released` around the recall; this makes the pair symmetric with the same two lines, in the same idiom. Both lines, not just the release: asserting only the release would pass vacuously if the lock never engaged for this posture, which is the same "proves a subset of what it mirrors" weakness one level down. Test-only. No product change; the ruled behaviour from #12775 is untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8
…nant-admin-lock-release-pin
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
Dequeued 01:42Z — not this PR's failure. Re-arming once, and saying why.The failing check, read rather than assumedQueue build 33702957362. One job red —
This PR's diff is one file — Why the fix is not portable into this PRPR #14715 fixes it ( What happens nowRe-arming auto-merge once. This is this PR's first ejection, and the same one-re-queue allowance #14528 got. The queue is not stalled — #14712, #14718 and #14730 all merged through it in the last half hour — so a second attempt is a real chance rather than a ritual. ⛔ One only. A second ejection on this signature is data for #14648, not a third arming. Nothing about this PR changes in the meantime: it is green on Generated by Claude Code |
Fixes #14602
The
#12775override-narrowing block's tenant-admin reverse check — "the narrowing is about status, not posture" — proved a strict subset of the platform-admin case it mirrors:recalledstatus plus the action row, and nothing at all about the record lock. This makes the pair symmetric.The diff — one file, two lines
packages/plugins/plugin-approvals/src/approval-revise.test.ts, insidereverse check (tenant admin):Both lines were added, not just the release one. The release assertion alone would have left the case proving a strict subset of what it mirrors one level down: it passes vacuously if the lock never engages for this posture, and "released at the end" is a weaker claim than the
locked → releasedthe platform-admin case pins. The pre-condition is what makes released mean released by the recall. Both lines are byte-copies of the platform-admin case four cases above, comment column included.No product file was touched, and nothing in #12775's ruled behaviour is reopened.
What was measured
Suite —
pnpm --filter @objectstack/plugin-approvals test, before and after, bothTest Files 36 passed (36)/Tests 662 passed (662). The count is identical because both assertions land inside an existingit, so the suite total is not evidence the case ran. The targeted run is:The after-run above is on this PR's head
423a3405d, afterorigin/mainwas merged in.Ablation — the new lines are load-bearing, not decorative. Committed first, then the release assertion was moved to before the recall; the mutation was confirmed on disk (the assertion's in-case line index moved ahead of the recall's) before the run. Predicted direction: red, because the record is still locked at that point. Observed:
Restored with
git checkout HEAD -- ABS_PATHunder anEXIT INT TERMtrap; restoration proven by blob hash,e0a7dc8da831ff1ff9fbe837dbd743e3ebd34913before and after, withgit diff HEADempty. So the lock genuinely engages on this path and only the recall releases it.Typecheck —
pnpm --filter @objectstack/plugin-approvals typecheckexit 0. The package's owntsconfig.jsonexcludes**/*.test.ts, so that leg says nothing about the edited file; the leg that does ischeck:test-typecheck(tsconfig.test.json), andtsc --listFiles -p tsconfig.test.jsonwas used to confirm the edited file is in that program rather than assumed to be. Gate verdict:check:test-typecheck: OK — @objectstack/plugin-approvals's test layer compiles … 8 file(s) / 324 error(s) / 27 pinned signature(s) held in test-typecheck-debt.json (shrink-only and identity-pinned).Gates — derived from the actual change set rather than recalled, via
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsre-run on the merged tree: 29 commands, 25 exit 0. The four non-zero all refuse for an unmet local prerequisite and say so themselves — none is a finding, and none is a pass:check-test-completeness.mjsturbo run testlog, and no log was named… the local reading for this gate is NOT MEASURED"check:dual-build-cjs-loadscheck:i18ncheck:type-check-debtCI runs all four against a built tree.
Clause ② —
no, re-derived from this diffgit diff -U0 origin/main...HEAD | grep -E '^[+-].*export'returns no matches (grep exit 1, 0 lines). The change set is one path,packages/plugins/plugin-approvals/src/approval-revise.test.ts— a test file that exports nothing to the package entry point, changes no accept/reject behaviour and widens no public surface.Changeset — none owed;
skip-changesetappliedThe diff publishes nothing from any package: one
.test.ts, excluded from the built output. No empty-frontmatter changeset was added to look complete —check-empty-changeset.mjsrejects exactly that.Seen, deliberately not touched
src/approval-revise.test.ts:22importsAPPROVALS_HOOK_PACKAGEand never uses it — pre-existing onorigin/main, and already carried as frozen debt (test-typecheck-debt.jsonholdsTS6133: 'APPROVALS_HOOK_PACKAGE' is declared but its value is never read.at count 1 for this file). Different defect class from this card, and clearing it would move a ledger; left alone and reported instead.🤖 Generated with Claude Code
https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8
Generated by Claude Code