Skip to content

feat(plugin-security,rest,spec)!: retire the permission-set overlay discard (ADR-0131 cutover stage 7-pre) - #22776

Merged
objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-15204-s7pre-overlay-discard
Oct 11, 2026
Merged

objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-15204-s7pre-overlay-discard

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #15204
Clause-②: no (narrowing)

Stage 7-pre of the ADR-0131 cutover (claim amendment 6105462924; stage 0 item U1 of 6095755866). #15204 stays open: this is one stage of its plan, and the later stages (6b-2, 7a, 7b, 8) are not addressed here.

U1, measured on main a18c514965 (re-read after merging 490cb6d9fa): the action retires

1. What an "overlay" is now. An active, environment-wide sys_metadata row (organization_id null) of type permission or the legacy permissions, stored under the name of a permission set a code package ships (classifyPackagedPermissionSet verdict packaged). It is not a sys_permission_set row. The removed module found it with findActiveOverlayRows and gated it on that classifier.

2. Can any caller-reachable path still create one? No. Every door was measured:

  • Metadata door. saveMetaItem asks refusePackagedBaseOverride, which refuses an in-place write over a packaged item of a type with no overlay channel (isSealedManagedItem). permission is registered allowOrgOverride: false. ADR-0131 D6: the OS_METADATA_WRITABLE hatch "is not consulted" for managed content, on every topology. SysMetadataRepository.assertAllowed repeats the refusal at the store (packages/metadata-protocol/src/protocol.ts, packagedBaseRefusal and its header).
  • Data door on sys_metadata. The object is apiMethods: ['get', 'list'] (packages/metadata-core/src/objects/sys-metadata.object.ts), so it is read-only over the data API.
  • Data door on sys_permission_set and Setup. packaged-permission-set-lock.ts refuses a save over a packaged set with 403 NOT_OVERRIDABLE, naming Clone ("No silent overlay row is ever minted again"). The metadata-door half of that lock is packaged-permission-set-lock-gate.ts.
  • Package install. A package that declares a name the environment catalog holds is refused with 422 NAMESPACE_CONFLICT, on a hot install and on a cold boot (refuseSecurityCatalogNameConflicts; BUILT_IN_SECURITY_CATALOG_NAMES.permission is empty, so no permission-set name is exempt).
  • Rows stored before those refusals. The cold boot refuses the deployment right after sys_metadata hydrates: ObjectQLPlugin.refuseEnvironmentHeldSecurityCatalogNames reads the registry's environmentHeldSecurityCatalogConflicts (ADR-0048 N.3, registered as security-catalog-environment-overlay-refused; pinned by packages/runtime/src/standalone-stack-security-catalog-one-holder.test.ts, "artifact boot over one database …"). So a server that could answer the discard route never holds an overlay. The remedy that exists for those rows is the offline os migrate security-catalog-overlays [--apply] (maintainer ruling letter B on [decision] after #22307 a cold boot refuses any environment row over a package-held permission set or position, so the 2026-08-24 legacy-overlay remedies (boot overlay reading, drift overlay_shadow, Discard Overlay) find nothing on v18: keep or retire? #22371).

3. What the discard did, and whether the resolver reads it. It deleted the overlay row(s) under SYSTEM_CTX, then re-projected the sys_permission_set row through projectPermissionMutation. An overlay row would be read: hydration puts it in the registry's bare slot, which the security catalog reader serves. But no running server holds one (point 2). The re-projected row is not read for grants: since stage 1 the resolver reads the security catalog, and since stage 2a/2b the services readers do too.

Decision rule → RETIRE. No path creates an overlay. The operator workflow for existing databases is already served by the offline step, so no product choice is left open.

Four axes (.claude/skills/pm-dispatch/SKILL.md, 升级与决策):

  • Real business need (measured): none at runtime. On this tree the action's target state cannot exist on a running server. The pull it served (field-reported forks) is served by os migrate security-catalog-overlays, which runs where the action cannot: before boot.
  • Long-term soundness (leads): one remedy, offline, ahead of the boot that refuses, instead of a second in-server path that can no longer fire. A name-keyed rewrite would build a transition piece for a state the cutover removes ([Decision] ADR-0131 剩余部分(C2 余下阶段 + C3):继续分段、合并成一次切换,还是改成「registry 支撑的对象」 #22601 → B).
  • AI-error-proofing: a declared route, Setup action and contract member that can never succeed invite callers to rely on them. Removing them leaves one prescription, named by the cold-boot refusal itself.
  • Startup focus: retire now, with no deprecation window; about 1.5k lines leave the tree.

What changed

  • Deleted: plugin-security/src/permission-set-overlay-discard.ts and its test, and the dogfood eligibility test.
  • plugin-security: the service wiring and the five exports go. The Discard Overlay action on sys_permission_set goes (translations regenerated). The drift_status field description, the drift diagnostic's overlay_shadow detail and its comments lose their reference to the action (deletions in a 6b-2 module). The boot reading reportPackagedPermissionSetOverlays (packaged-permission-set-overlay-detection.ts) now names the offline step instead of the action. Comment references in the lock, the projection and write-refusals.ts are trimmed.
  • rest: the route and its route-ledger row go, and a RETIRED note takes the row's place. Its rows in two rest ledger tests go.
  • spec:
    • ISecurityService.discardPermissionSetOverlay and PermissionSetOverlayDiscardResult are removed, with their contract test.
    • ERROR_CODE_LEDGER['@objectstack/plugin-security'] drops INVALID_STATE and NOT_FOUND: the removed action was their only producer in that package. Both stay under @objectstack/rest, so the union is unchanged. The NOT_FOUND waiver reason and the ledger test follow.
    • New ADR-0087 semantic entry security-permission-set-overlay-discard-retired.
    • api-surface/ and export-origins/ are regenerated.
  • Kept, per the stage plan: packaged-permission-set-lock and its gate. packaged-permission-set-overlay-detection keeps its consumer: the boot reading in security-plugin.ts calls it, and the discard never imported it.
  • Docs: permission-sets.mdx (the "Overlay shadow" remedy), system-context.mdx (row 14 removed; census counts regenerated), metadata-lifecycle.mdx and environment-variables.mdx (the permission-set exception), and tenant-audit-census.mdx with its counts ledger (one write site fewer). Nothing under content/docs/releases/.
  • Gate ledgers: engine-double-contract.pinned.json (regenerated; 2 rows of the deleted test) and objectql-double-limit.baseline.json (its entry, shrink-only).
  • Changeset: spec, plugin-security and rest are minor, BREAKING (the epic's launch-window convention), with a removed → instead table and the ADR-0087 registered marker.

Acceptance notes

  • Contract review owed. This touches non-test packages/spec/src/** (the contract, the error-code ledger and the migration entry), so a same-head contract-tier PASS is owed before pr_ready.
  • Two pending changesets name the action (not edited here; check-empty-changeset refuses an edit to another PR's note, and correcting a pending release note is the seat's call):
    • .changeset/22307-cold-boot-catalog-refusal.md tells an operator to use Discard Overlay "on the release you run now", before upgrading. That stays true of the release they run, where the action exists.
    • .changeset/15206-reads-environment-only.md:26 says a packaged-set fork "keeps its own ruling (detection reading and Discard Overlay) and is still served". After this PR the "Discard Overlay" half is false. The suggested correction is "(the detection reading)".
    • .changeset/22719-rest-write-hook-refusal-sentence.md:16 (landed on main while this PR was open) lists POST /security/permission-sets/:id/discard-overlay among the routes it repaired. Once this PR lands, that route no longer exists. The suggested correction is to drop it from that row.
  • objectui pin: NOT MEASURED. No checkout of ../objectui is available here. The removed surface is a server route, a declarative action and a contract type. An objectui file importing PermissionSetOverlayDiscardResult or naming discard-overlay would be the Console Pin Gate's red.
  • Row 14 of system-context.mdx leaves a gap in its numbering (13 → 15). Renumbering would move every later row's key that other text cites.

Verification (head 8f992cde3d: the change plus one no-rebase merge of origin/main 490cb6d9fa)

  • Package suites:

    • plugin-security: test exit 0 (4003 passed, 45 skipped); typecheck exit 0.
    • rest: test exit 0 (5235 passed, 326 skipped); typecheck exit 0.
    • spec: test exit 0 (19220 passed); typecheck exit 0.
    • client: test exit 0 (653 passed); typecheck exit 0.

    Each was run as pnpm --filter PKG test and typecheck behind os-verify-lock, after pnpm install --frozen-lockfile and a closure build.

  • Targeted:

    • The runtime cold-boot refusal pin standalone-stack-security-catalog-one-holder.test.ts: 6/6.
    • Dogfood route-ledger-live-mount-parity, permission-set-lock-row-provenance and permission-set-clone-boot-unowned-warning: 25/25.
    • The full runtime and dogfood suites are left to CI. This is a declared narrowing: neither package's source is in the diff, and the one dogfood test file in the diff is deleted.
  • Derived gates: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran reports 131 derived, 131 run, 0 NOT MEASURED, 0 UNRUN, and every family exits 0. That includes check:generated for spec, check:i18n, check:i18n-stale-fill, the system-context and tenant-audit censuses, check:adr-0087-registration, check:empty-changeset, check:engine-double-contract and check:objectql-double-limit. check-route-ledger-census also exits 0.

  • Census edits, from the measurement:

    • execctx-consumer-census.test.ts: 63 → 62 sites, 78 → 77 mentions, 47 → 46 bare.
    • The hook-refusal ledger: the write-row floor goes 45 → 44, and the repaired rows 20 → 19.

Generated by Claude Code

…iscard (ADR-0131 cutover stage 7-pre)

U1 measured on main: no caller-reachable door creates an environment
overlay of a package-declared permission set any more (the metadata door
seals it on every topology, the data door's packaged lock refuses it, the
data API cannot write sys_metadata, and a package declaring a held name is
refused at install), and a database still holding one is refused at cold
boot (ADR-0048). The discard action therefore had nothing it could reach on
a running server, and the row it re-projected is no longer read for grants.

Removes the action module and its tests, the REST route and its ledger row,
the Setup action and its translations, the optional ISecurityService member
and its result type, and plugin-security's two error-code provenance rows
(both codes stay registered under @objectstack/rest). Registers the
retirement as ADR-0087 semantic entry
security-permission-set-overlay-discard-retired, and points every remaining
reference (docs, the boot reading, the drift diagnostic) at the offline
`os migrate security-catalog-overlays` step.

Claude-Session: https://claude.ai/code/session_019TtY6pnoZSemcQRzbRUm5e
Co-authored-by: Claude <noreply@anthropic.com>
…pre-overlay-discard

# Conflicts:
#	packages/rest/src/rest-write-route-hook-refusal-sentence.ledger.test.ts
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation tests tooling labels Oct 11, 2026
@github-actions

github-actions Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/metadata-protocol, @objectstack/plugin-security, @objectstack/rest, @objectstack/spec, touching 50 documentable anchor(s). ⚠️ 6 changed file(s) yielded no anchor (packages/plugins/plugin-security/src/index.ts, packages/plugins/plugin-security/src/packaged-permission-set-lock.ts, packages/plugins/plugin-security/src/write-refusals.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

34 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 5fc57b382e0eb7357af1594628e6e44e205f2d7a.

⛔ 14 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 6 changed file(s) yielded no anchor (packages/plugins/plugin-security/src/index.ts, packages/plugins/plugin-security/src/packaged-permission-set-lock.ts, packages/plugins/plugin-security/src/write-refusals.ts, …) — pages documenting those are invisible to this run
  • 9 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 145 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 5fc57b382e0eb7357af1594628e6e44e205f2d7a → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 349cdd4216b7e1e09e7dca8d606c63d2cb950dbe — the merge of head 11afbdb847a0e5c9d68f7c75ac72381cce76c0a3 into base 5fc57b382e0eb7357af1594628e6e44e205f2d7a, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 349cdd4216b7e1e09e7dca8d606c63d2cb950dbe && git checkout 349cdd4216b7e1e09e7dca8d606c63d2cb950dbe
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 5fc57b382e0eb7357af1594628e6e44e205f2d7a 11afbdb847a0e5c9d68f7c75ac72381cce76c0a3 && git checkout -B drift-repro 5fc57b382e0eb7357af1594628e6e44e205f2d7a && git merge --no-ff 11afbdb847a0e5c9d68f7c75ac72381cce76c0a3

node scripts/docs-audit/affected-docs.mjs --json 5fc57b382e0eb7357af1594628e6e44e205f2d7a

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 5fc57b382e0eb7357af1594628e6e44e205f2d7a → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…oute out

The route-ledger row and the rest-server registration site left with the
route: the matrix docblock reads 81 rest rows, and the blind-spot census
re-measures 82 -> 81 (rest-route-ledger.ts) and 71/19/52 -> 70/19/51 with
enforceAuth 58 -> 57 (rest-server.ts), totals 66/71 -> 65/70.

Claude-Session: https://claude.ai/code/session_019TtY6pnoZSemcQRzbRUm5e
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 1907a9bb127bb9c2b1d223d33d6c4720a96a01e4
Local-runs: none

Inputs: card #15204 (body and all 79 comments, in particular 6095755866 U1, 6105462924, 6106681412, 6105972564, 6106242585, 6106999102), PR #22776 (body, 47-file list, net diff +228/−1,587 against main), the 35 check-runs on the head (33 success, 2 skipped: Console Pin Gate, Packed-tarball smoke (opt-in)), and main read through the contents API at the paths named below. Nothing built, run or re-run.

① Derived judgments

U1 → RETIRE is justified by measurement. The decision rule of 6105462924 asks one question: after the cutover, can any caller-reachable path still create an environment overlay of a packaged permission set? Re-measured on main, the answer is no on every topology:

  • Metadata door. saveMetaItem calls refusePackagedBaseOverride unconditionally (protocol.ts:20021). Its predicate isSealedManagedItem(type, name) = isArtifactBacked(type, name) && !registryAllowsOverlay(type) (protocol.ts:16118); registryAllowsOverlay reads allowOrgOverride off DEFAULT_METADATA_TYPE_REGISTRY alone, and permission is allowOrgOverride: false (metadata-plugin.zod.ts:1117). The legacy plural folds first (permissions → permission, manifest-collection-spelling.ts:77), so both spellings are sealed. Inside the sealed branch the named-base limb only picks the sentence (ITEM_LOCKED for a read-only base); the 403 NOT_OVERRIDABLE throw after it is unconditional, so naming a writable runtime package does not open the door either. OS_METADATA_WRITABLE is not consulted. The store backstop SysMetadataRepository.assertAllowed (sys-metadata-repository.ts:1819) refuses an override-artifact write on a type without allowOrgOverride with the hatch open or shut, so the doors that reach put without saveMetaItem (draft promotion, restore, revert) are sealed too. Right.
  • Data door on sys_metadata. apiMethods: ['get', 'list'] (sys-metadata.object.ts:248). Right.
  • Package install, hot. refuseSecurityCatalogNameConflicts at the package door (registry.ts:2448, called at :4995 ahead of every mutation) reads holders with { builtIns: true, environment: true }; BUILT_IN_SECURITY_CATALOG_NAMES.permission is the empty set (objectql/src/security-catalog-namespace.ts:124), so no permission-set name is exempt. Right.
  • Package install, cold. ObjectQLPlugin.start() calls refuseEnvironmentHeldSecurityCatalogNames right after hydration (objectql/src/plugin.ts:1013); the reading is environmentHeldSecurityCatalogConflicts, which counts the bare slot "whatever _packageId it wears", so a legacy row bound to the package it collides with is refused too. Right.
  • Offline remedy exists. packages/cli/src/commands/migrate/security-catalog-overlays.ts (358 lines) lists, and under --apply deletes, the rows, each through the metadata write path with a history tombstone; it boots with hydration off and meets findPackageHeldSecurityCatalogNames with the stored rows. Right.

One over-statement, recorded, not a FAIL: the cold-boot refusal reads the registry's bare slot, which hydration fills only when environmentId === undefined || hydrateMetadataFromDb (plugin.ts:1002). A project kernel that skips hydration is not refused over a legacy row, but it does not put that row in the registry the security catalog reader serves either, so the discard would have had no shadowing to fix there. The PR body's "a server that could answer the route never holds one" is exact for the hydrating topologies and loose for that one. The retirement does not rest on it: the rule asks about creation, and creation is sealed by a topology-independent door.

Accept-set and public-surface changes the diff implies, each named:

  1. POST /api/v1/security/permission-sets/:id/discard-overlay: the routeManager.register block leaves rest-server.ts (−41), the ledger row becomes a RETIRED note in rest-route-ledger.ts, and the two rest ledger tests follow (tripwire disposition row removed; hook-refusal ledger floor 45 → 44, repaired rows 20 → 19, OWN_TERMINAL row removed). The execctx census moves 63 → 62 sites, 78 → 77 mentions, 47 → 46 bare, consistent with one bare site behind enforceAuth. Narrowing. Right.
  2. The Setup action discard_permission_set_overlay leaves sys-permission-set.object.ts; the four *.objects.generated.ts drop the action block and the drift_status.help clause naming it (hash b16445c654ee6cec → 168e382c7568b1f3, identical in es-ES, ja-JP, zh-CN); the three source-hash rows leave each *.source-hashes.generated.ts; rbac-objects.test.ts pins the three surviving actions. check:i18n and check:i18n-stale-fill are inside the green Lint & Repo Gates. Right.
  3. ISecurityService.discardPermissionSetOverlay? and PermissionSetOverlayDiscardResult leave spec/src/contracts/security-service.ts (−71) with their contract test; api-surface/contracts.json and export-origins/contracts.json each lose the one row; registered-security-service-members.pin.test.ts drops the member from DECLARED_MEMBERS and the compile-time witness. An optional member removed is a narrowing a feature-detecting caller survives at runtime and tsc names at compile. Right.
  4. @objectstack/plugin-security exports: discardPermissionSetOverlay, PermissionSetNotFoundError, PermissionSetOverlayStateError, and the types PermissionSetOverlayDiscardDeps, PermissionSetOverlayDiscardResult leave index.ts, five in all, matching the changeset table. The module (353) and its test (495) are deleted; the service wiring and the Object.assign member leave security-plugin.ts. Right.
  5. ERROR_CODE_LEDGER['@objectstack/plugin-security'] drops INVALID_STATE and NOT_FOUND. Read on main: INVALID_STATE is listed under @objectstack/rest and @objectstack/plugin-security; NOT_FOUND under @objectstack/rest, @objectstack/plugin-sharing and @objectstack/plugin-security. Each code keeps at least one owner, so the union is unchanged, as claimed. The NOT_FOUND waiver reason loses its plugin-security clause and the ledger test's two stamps rows go (no class in that package stamps either code now). Right.
  6. ADR-0087 semantic entry 18.security-permission-set-overlay-discard-retired.ts: id matches the changeset marker; surface, replacement, reason and acceptanceCriteria are present and name the route, the action, the contract member, the offline step and the Clone alternative. check:adr-0087-registration sits in the green Lint & Repo Gates. Right.
  7. Docs: permission-sets.mdx remedy rewritten to the offline step; system-context.mdx row 14 removed (123 → 122 sites, 58 → 57 files, 105 → 104 symbols; the numbering gap 13 → 15 is acknowledged and the cheaper choice); metadata-lifecycle.mdx and environment-variables.mdx replace the permission-set exception's "Discard Overlay" with the cold-boot refusal and the migrate command; tenant-audit-census.mdx and its counts ledger 239 → 238. The two census gates are inside the green repo gates. Nothing under content/docs/releases/. Right.
  8. Deletions in modules of later stages: permission-set-drift.ts (6b-2) loses the detail clause and three comments; permission-set-projection.ts (7a) loses two comment references; packaged-permission-set-lock.ts (kept) loses three comment references. All deletions, within 6105462924's exclusion. packaged-permission-set-overlay-detection.ts (kept, still called by the boot reading) has its message re-worded to the offline step: a kept module, so an edit is permitted. Right.
  9. protocol.ts: a 3-line doc-comment change only, so no @objectstack/metadata-protocol bump is owed; Check Changeset is green. Right.
  10. Dogfood census: authz-conformance.matrix.ts 82 → 81; authz-probe-blind-spot.census.ts re-measured by hand (81/81/0, 70/19/51, enforceAuth 58 → 57, totals 66/71 → 65/70) with a dated note; the eligibility dogfood test (278) deleted. Dogfood Regression Gate (3 shards) and Dogfood Verify CLI are green on this head. Right.
  11. engine-double-contract.pinned.json (−2 rows of the deleted test) and objectql-double-limit.baseline.json (−1 entry): shrink-only. Right.

No accept set widens anywhere in the diff. A note for 6b-2, not a defect here: the drift_status field help and the overlay_shadow detail still describe a state this PR shows cannot be reached on a hydrating server; retiring the diagnostic is 6b-2's.

② Semver level

.changeset/15204-s7pre-overlay-discard-retired.md bumps @objectstack/spec, @objectstack/plugin-security and @objectstack/rest as minor, titled with !, marked BREAKING under the launch-window convention, carrying Clause-②: no (narrowing) and the marker adr-0087: registered security-permission-set-overlay-discard-retired, with a removed → instead table whose four rows match items 1 to 4 above. The PR body carries the same Clause-②: no (narrowing) line. The diff publishes nothing outside those three packages (metadata-protocol: comment only; qa/dogfood: tests). Matches.

③ Boundary flags

  • Pending changesets naming the action (dev Q1 → A; seat 6106681412 condition (e)). Read on main: .changeset/15206-reads-environment-only.md:26 still says a stored fork "keeps its own ruling (detection reading and Discard Overlay) and is still served"; .changeset/22719-rest-write-hook-refusal-sentence.md:16 still lists POST /security/permission-sets/:id/discard-overlay among the repaired rows; .changeset/22307-cold-boot-catalog-refusal.md:23 tells the operator to use Discard Overlay "on the release you run now", which stays true of the release being upgraded from. Judgment: at merge time nothing published is false. Every content/docs/** sentence naming the action is corrected in this PR, and a .changeset/*.md is consumed only at the release cut. The two sentences become false in the release that also carries this retirement, which is exactly what condition (e) binds: no release carrying 7-pre ships before both are applied. Condition (e) carries it adequately. Two things the seat should hold onto: (e) is a seat-held condition with no mechanical gate behind it, and 22719's row stays a true statement of what [finding] rest: 20 REST write routes answer a sandboxed hook's refusal as the debug wrapper, because their hand-built error arms relay .message (approvals ×9, sharing rules ×3, security ×3, packages/publish, external datasources ×4) #22719 repaired, so dropping the route (the dev's correction) rather than annotating it is a wording choice the maintainer may take either way.
  • objectui pin (dev Q2 → B). Console Pin Gate is skipped on this head, so recommendation B measured nothing. What stands is the seat's own measurement in 6106681412: zero references to the route, the action or the contract member in objectui at the pinned .objectui-sha 20c6d351ad74, on objectui main and on C9's branch. Answered by the seat, not by CI.
  • Declared narrowing of the local runs (full runtime and dogfood suites left to CI): Test Core (6 shards), Dogfood Regression Gate (3 shards), Dogfood Verify CLI, Temporal Conformance and the four type-check gates are green on the head, and round 2 (6106999102) reports the full dogfood suite green locally as well. Closed.
  • Landing order. Ruling 1 of 6106681412 (maintainer 「直接入队(推荐)」) supersedes 6105462924's "after 6a" for 7-pre alone: it lands on this PASS. The PR is still a draft; pr_ready is the seat's act after this record.
  • Exclusions of 6105462924. No transition piece; no edit to a retired module except a deletion (item 8); no change to who may write the catalog; nothing under content/docs/releases/. All hold. The security-plugin.ts hunks (imports, the deps block, the Object.assign member, one boot comment) are disjoint from 6a's seeding region.
  • out_of_scope_findings. Row 14's numbering gap: noted, no action. The deletions in 6b-2's and 7a's modules: noted for those stages.
  • Not measured here, by design. The Clone action under a name a code package ships was not traced; it meets the same sealed door and, at the row, the packaged lock.

Implemented-by: session_019TtY6pnoZSemcQRzbRUm5e
Reviewed-by: session_01Rerax7QTjKMPCUZxQUtPFR

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

7-pre (#22776): the regen-merge round is checked; queued · epic PM session_01Rerax7QTjKMPCUZxQUtPFR · 2026-10-11T09:00Z

This is the landing round the ACCEPT 6107103394 named (landing-operations §A).


Generated by Claude Code

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

Labels

documentation Improvements or additions to documentation size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants