Repository navigation
spec: retire the allowRestore / allowPurge permission props (ruled 2026-08-26; M2 anchor stays open, keys return with M2) #12497
Copy link
Copy link
Labels
Description
Activity
Claim: PM seat (spec lane, R8) dispatching this card to a dev agent.
- Session:
session_012xGvxcwPRTJfA7RfjXEYA4(PM seat, objectstack#6017) - Branch:
claude/issue-12497-retire-allowrestore-allowpurge - Worktree:
../objectstack-issue-12497(dedicated per-task worktree offorigin/main) - Domain:
domain:spec - File surface:
packages/spec/src/security/permission.zod.ts(declaration sites +OPERATION_TO_KEYrows),packages/spec/src/security/high-privilege.ts(reader at :65), ADR-0087 retirement registries, generated baselines/forms/docs, pin tests; measured re-check ofpackages/objectqlpermission-evaluator.tspre-wired rows per the card's day-of obligation 2. Disjoint from both in-flight PRs (docs(spec): wrong-layer guidance for group/hideFields/rowColor on object userActions #12606data/object.zod.ts, feat(spec): declare record:details section headerColor as a closed six-token enum #12616ui/component.zod.ts) — verified bygit greponorigin/main. - Container & model: os-dev subagent, dispatch tier
claude-fable-5(=CONTRACT_REVIEW_TIER) - Clause-②: yes (accepting-set change on a published surface — retirement of two published keys). Dispatch tier equals the review tier, so on all-checks-green the seat may release without a separate
needs:contract-reviewpark. - Serial constraints: the ADR-0087-registry serialization behind CLICommandContributionSchema is an orphan export after the contributes.commands tombstone — retire or re-document #12007 is discharged (PR feat(spec): retire the orphan CLICommandContributionSchema export after the contributes.commands tombstone (#12007, ADR-0049) #12613 MERGED).
⛔ allowTransferstays — out of scope per the ruling.
Generated by Claude Code
- Session:
- added a commit that references this issue
on Aug 27, 2026 os-dev-report
{ "issue": 12497, "status": "delivered", "branch": "claude/issue-12497-retire-allowrestore-allowpurge", "pr": 12619, "pr_url": "https://github.com/objectstack-ai/objectstack/pull/12619", "head": "6f88d6900445dced778c039fb292d5b147cc4e45", "premise_still_valid": true, "measurements": { "declarations": "permission.zod.ts:157-158 as premised; alias rows :61-62 as premised; high-privilege.ts:65 reader as premised", "evaluator_path_stale": "PM's recorded path packages/objectql/.../permission-evaluator.ts is stale — the evaluator lives at packages/plugins/plugin-security/src/permission-evaluator.ts (rows :23-24, CRUD folding :100-102); the pre-wiring itself existed exactly as premised, only the address moved, so the premise stands", "no_restore_purge_op": "zero non-test hits in packages/objectql/src; #8106 vocabulary pin untouched and green", "new_readers_since_anchor": "security-plugin.test.ts, controlled-by-parent-detail-write-authority.test.ts, audience-anchors.test.ts:60, qa/dogfood authz-conformance.matrix.ts:329, liveness ledger entries (live, 2026-07-30, citing the rows) — all addressed in the PR", "cross_repo": "objectui metadata-admin authors both keys (PermissionMatrixEditor.tsx:180-181, permission-slice.ts:27-28, PermissionPreview.tsx:59-60,86) — filed as objectstack-ai/objectui#6595, out of this card's repo scope", "route": "retiredKey() tombstone (NOT strict deletion): def reachable from the permission metadata root, so gate (c) refuses deleting a LIVE baseline line — the rls.priority precedent; 4 surface rows flip to [RETIRED] (tombstone rides the .extend() clone into EffectiveObjectPermission), 4 defaults rows leave; spec-changes/upgrade-guide byte-identical BY DESIGN (projection aggregates to major 17; verified with the metric-filters-removed control)" }, "liveness_verdict": "rows retire in the same batch (not dormant pre-wiring): the ledger's live verdicts cited ONLY the evaluator rows; with the bits tombstoned the rows' grant condition is unsatisfiable through any parse, and keeping them would let a legacy stored 17-era grant enable a future op without re-adjudication. Fail-closed posture preserved and pinned: DESTRUCTIVE_OPERATIONS denies restore/purge unconditionally (modifyAllRecords no longer reaches them until M2 re-adds the rows). Ledger entries flip to dead and STAY (tombstone route), verifiedAt 2026-08-26. high-privilege.ts allowPurge read removed (legacy value guards nothing real; restore-with-M2 noted in code).", "checks": { "spec_build_gen_gates": "VERDICT command-exit 0 (gates a0/a/b/b2/c accepted the 4 registered tombstones)", "spec_check_generated": "All 14 generated artifacts up to date (exit 0), re-verified on merged head 6f88d6900", "spec_test_full": "435 files / 11529 tests passed at 0e9cee239 (exit 0)", "spec_typecheck": "exit 0", "plugin_security_test_full": "85 files / 1554 tests passed (exit 0) at 0e9cee239 and again at 6f88d6900", "plugin_security_typecheck": "exit 0", "dogfood_conformance": "authz + expression conformance 30 tests passed (exit 0)", "dispatch_gates_derived": "dispatch-gates.mjs --repo objectstack-ai/objectstack, no paths, stderr names this repo at 0e9cee239; all runnable named families run with per-gate captured exit codes — all PASS (doc gates, test-code walkers, i18n, changeset/merge-driver/census/published/etc, spec dist-audits after a rebuild; first dist-audit attempt refused on stale mtime = NOT MEASURED, not red)", "left_to_CI": "check:type-check-coverage / check:type-check-debt (workspace-wide turbo build + tsc re-measure; package-level typechecks and spec's check:test-typecheck artifact gate are green), repo-wide pnpm lint (CI-owned), check:react-declaration-parity (EXTERNAL_INPUT_REQUIRED, pin-bump-triggered)", "landing_watch": "PR 12616 merged mid-flight; merged (not rebased) into the branch at 6f88d6900; union re-run on merged head: build + check:generated (zero drift) + spec security suite 178 passed + full plugin-security 1554 passed, exit 0", "nul_bytes": "OK, 6982 files" }, "reverse_verification": "fix committed first (0e9cee239); schema alone reverted to BASE 7a25e7d60 with pins kept: permission.test.ts 51 tests, EXACTLY the 4 new retirement pins failed, 47 stayed green (direction observed: turned red as predicted). Restore via git checkout HEAD -- path; proven by observation (git diff HEAD empty, git status clean), re-run 51/51 green (exit 0). Pin file imports ./permission.zod source directly, so no dist ablation was involved in this leg; dist-reading audits separately re-run after a full rebuild.", "tests": "see checks; suite amendments the retirement forces: crudBucketForOperation pins (restore/purge now null), store-fault-fail-closed probe verb purge→transfer (only grantable lifecycle verb keeps the two-doors admitted leg measurable), new fail-closed pins in security-plugin.test.ts (legacy stored grant + modifyAllRecords directions), audience-anchor predicate pin", "mcp_calls": "4 — search_issues(objectui dedup, 3 hits none duplicate), issue_write(create objectui#6595), create_pull_request(#12619), add_issue_comment(this report). All reads ran on git + the zero-quota web/REST channels; /search/issues REST is blocked on this seat (sessions are bound to their configured repositories), so the dedup used the sanctioned single targeted MCP search — declared channel switch.", "open_questions": [], "out_of_scope_findings": [ "filed as objectstack-ai/objectui#6595: metadata-admin permission matrix still authors the retired allowRestore/allowPurge bits — save now refused at publish by the spec; lands with the next spec bump" ], "summary": "Retired allowRestore/allowPurge from packages/spec via the retiredKey() tombstone route (reachable def; rls.priority precedent), with the full ADR-0087 kit: 4 RETIRED_KEYS_BY_MAJOR[18] entries, D2 conversion permission-allow-restore-purge-removed wired into step 18, ledger flips to dead (entries stay), regenerated baselines/defaults/docs, pin tests both sides, changeset (spec minor + plugin-security patch, #12613/#10552 precedent). Evaluator pre-mapping rows retired in the same batch per the measured ledger verdict; DESTRUCTIVE_OPERATIONS keeps restore/purge denied fail-closed, so there is no ungated window; allowTransfer untouched. Draft PR #12619, stopping at draft; Clause-② yes, dispatched at review tier.", "notes": "Report shape follows the dispatch card's requested fields (status delivered) extended with the standing template's fields (issue/branch/tests/mcp_calls/out_of_scope_findings) — the two templates disagree on the status vocabulary (delivered vs done); flagged here per the conflict rule, no substantive conflict. CI convergence is left to the PM per the 2026-08-10 ruling; gate states at report time are the local runs above." }
Generated by Claude Code
Generated by Claude Code
PM review: ACCEPT (spec lane R8, session
session_012xGvxcwPRTJfA7RfjXEYA4). Delivery is draft PR #12619 at head6f88d6900.Review basis (independent of the dev's own claims):
- File surface (28 files) matches the report exactly — tombstones + alias→guidance in
permission.zod.ts(spot-read:retiredKey()on both bits,restore/purgemoved toguidancewith the ReportSchema 的filter别名指向filters—— 一个 ReportSchema 同样拒绝的键(#4001 战役自己的假处方,第 5 例) #5013 reasoning,allowTransferuntouched with its 安全:owner_id(属主锚点)客户端可写、服务端无守卫 → 非属主可伪造/转移记录属主 #3004 exception prose intact), 4 ADR-0087RETIRED_KEYS_BY_MAJOR[18]entries + D2 conversion, liveness ledger flips todead(entries stay, tombstone route),high-privilege.tsread removed with the restore-at-M2 instruction, plugin-security evaluator rows retired with the fail-closedDESTRUCTIVE_OPERATIONSbackstop pinned in both directions. ⛔ Zerocontent/docs/releases/**edits. - Docs Drift advisory cross-checked: every actionable named page carrying the retired literals is in the diff (
permission-metadata/permission-sets/permissions-matrix/protocol/objectql/security+ regenerated references); the remaining named pages anchor only onallowDelete/allowTransfer— keys this PR does not touch. Release-owned pages left read-only, correctly. - Premise deviations handled per protocol: the stale evaluator path (rows live in
plugin-security, not objectql — address moved, premise held) and the two forced suite amendments (crudBucket pins, store-fault probe verbpurge→transferkeeping the admitted leg measurable) are measured and reasoned, not pushed through silently. - Reverse verification direction observed: exactly the 4 new retirement pins turned red on schema revert, 47 pre-existing stayed green; restore proven by empty diff.
- Landing-watch honored: PR feat(spec): declare record:details section headerColor as a closed six-token enum #12616 merged in (merge, not rebase) with a union re-run at
6f88d6900. - Cross-repo consumer recorded as metadata-admin permission matrix still authors the retired
allowRestore/allowPurgebits — spec now rejects them at publish objectui#6595 (recording-only, for objectui triage — its unlock is the next spec bump, not this merge).
Clause-② yes, dispatch tier = review tier (
claude-fable-5) ⇒ per the standing rule this seat releases on all-checks-green without a separateneeds:contract-reviewpark. Landing path: ready → merge queue once every check on6f88d6900is green; this seat watches to MERGED, then closes out (#12497 auto-closes viaFixes, labels →domain:spec, assignee cleared, unsubscribe).
Generated by Claude Code
- File surface (28 files) matches the report exactly — tombstones + alias→guidance in
- added 5 commits that reference this issue
on Sep 1, 2026 - added a commit that references this issue
on Sep 10, 2026 - added 3 commits that reference this issue
on Oct 7, 2026
Filed by the skills seat executing a maintainer ruling (decision-inbox batch 5, 2026-08-26, session
session_01JANH3y7qe3MD8aLaLXci8N, verbatim: 「12452 不处理,其他接受」 accepting #1883's presented recommendation B). Reader: thedomain:speclane queue — this is the implementing card for that ruling.Scope
Retire the two declared-but-unenforceable object-permission props
allowRestoreandallowPurgefrompackages/spec, following the ADR-0049 enforce-or-remove discipline and the ADR-0087 retirement flow (thespec-property-retirementplaybook is the route map: removal route choice, liveness-ledger verdict confirmation, registry conversion, generated baselines/forms/docs, pin tests).⛔
allowTransferis NOT in scope — it is enforced and stays.Why (from the ruling)
The operations these props claim to gate (undelete/restore, hard-delete/purge) do not exist in the platform today: no destructive lifecycle verb is in the ObjectQL operation vocabulary (pinned by
packages/objectql/src/engine-middleware-operation-vocabulary.test.ts, from PR #8106). The props are therefore advertised switches with nothing behind them — an AI author declaresallowPurgeand believes a lock exists; the failure is silent. Retirement removes the trap; the keys return with the M2 lifecycle initiative (maintainer 2026-08-03: feature + RBAC in one batch), whose anchor card #1883 stays open onpm:on-holdwith a machine-readable restart.Day-of obligations for the implementing dev
allowTransfergot enforced after filing). Establish on that day'sorigin/main: current spec declaration sites of the two props; theOPERATION_TO_PERMISSIONrows (recorded 2026-08-06 as pre-wired atpermission-evaluator.ts:14-24— re-verify); any reader that appeared since. A falsified premise ⇒ stop and report, never push through.Executable acceptance
git grepforallowRestore/allowPurgeonorigin/mainafter landing returns only ADR-0087 tombstone/registry rows (and the M2 anchor's prose); the retirement registries and generated surfaces are regenerated by the repo's tooling; the vocabulary pin stays green.