Repository navigation
finding(security): an objectExtensions entry that adds a second required master_detail to a controlled_by_parent object passes defineStack and the posture lint, then fails closed at runtime — neither door judges the merged shape #23210
Description
Filing gate: (c) a trap that lets an author, or an AI, write metadata the runtime refuses. reach: the authoring doors defineStack and the security-posture rules that os validate runs, on the published objectExtensions surface (examples/app-showcase authors it). Measured by probes against source at PR #23179's head 6582c1f0d (dev report 6141838120 on #23100, out-of-scope finding 1). No measured producer writes the trap shape today: HotCRM at 0f84c9c declares no objectExtensions, and the showcase's entries add no master_detail. Reader who acts: triage, to grade and route (packages/spec and packages/lint are both domain:spec); then the owning seat.
Filed by domain:spec seat 2 (#18549) · os-justin · session session_01BRQ9Mp1yH7R1az6MaUM18H. ⛔ Not a claim.
What happens
The stack, in short:
objects:cbpx_header,cbpx_other, andcbpx_linewithsharingModel: 'controlled_by_parent'and one requiredmaster_detailfieldheaderpointing atcbpx_header.objectExtensions:[{ extend: 'cbpx_line', fields: { other: { type: 'master_detail', reference: 'cbpx_other', required: true } } }].
Measured at 6582c1f0d:
defineStackreturns clean, andObjectStackDefinitionSchema.safeParsesucceeds.validateSecurityPosture(packages/lint) returns no controlled-by-parent finding.- At boot the merge gives
cbpx_linetwo requiredmaster_detailfields. With PR feat(spec)!: a controlled_by_parent object derives its master from exactly one required master_detail; the runtime's fallback tiers retire (#23100) #23179 the runtime derives no master from that shape. Every read is denied, every write answers422 INVALID_METADATA, and the only signal is the once-per-object warn. - Control: the same merged object declared as one object is refused by
defineStack(objects.2.fields: … with 2 required master_detail fields (header, other)) and by the lint (security-controlled-by-parent-ambiguous-relation).
So the extension door carries a shape that both authoring doors refuse when it is written directly. On main without PR #23179, the runtime instead picks one of the two by field order, the tier that PR retires. That consequence was read from code, not measured.
Where
- The merge runs only at boot:
packages/objectql/src/engine.ts(:7487–:7489onorigin/mainat150da61aa) registers eachmanifest.objectExtensionsentry onto its target. packages/lint/src/validate-security-posture.tsandpackages/cli/src/commands/validate.tsnever readobjectExtensions(git grep -conorigin/main: 0 hits in each).- Sibling lint rules already treat extensions as a blind spot by design.
validate-object-references.ts:293says "objectExtensions[].fieldsis deliberately NOT walked", andvalidate-field-consumers.ts:167says object extensions "are not judged". An extension usually targets an object another package owns, which the stack in hand cannot see. In this case the target is in the same stack.
Acceptance
- When an
objectExtensionsentry's target is visible to the door (declared in the same stack, or in apackages[]manifest the door already folds), the controlled-by-parent master rule judges the merged field set. Acontrolled_by_parentobject that ends up with anything but exactly one requiredmaster_detailis refused at the same door, with the same issue, as the directly declared shape. - A target that the door cannot see stays unjudged, as today. The runtime's fail-closed refusal and its warn remain the backstop.
- Pins: the probe stack above is refused, and its control is unchanged. A routing alternative is for the extension schema to refuse a
master_detailfield onto any target. That changes the published surface, and triage or the owning seat decides it.
Dedupe
MCP search_issues on objectstack-ai/objectstack, closed included:
objectExtensions extension adds second master_detail to controlled_by_parent object, parse and lint do not merge extensions→ 7 hits: finding(security): controlled_by_parent derives its master through a REQUIRED LOOKUP (runtime and lint, third tier) where ADR-0055 §1 requires exactly one required master_detail and fails closed — a widening no record states #23100 (the carrier PR's card; not this door), skills: the published lint-rules table saysrelationship/master-detail-requiredis awarning, which PR #22109 makes false on acontrolled_by_parentobject #22111, lint: promoterelationship/master-detail-requiredfrom warning to error, scoped tocontrolled_by_parent— ruled for the v18 boundary (Direction 1 of #8772) #9139, spec builder: forcerequired: trueon amaster_detailreference undercontrolled_by_parent(ruled Direction 2 of #8772) #9138, Publish-time lint:sharingModel: controlled_by_parentwith nomaster_detailrelation is statically detectable and unreported #7503 (lint and builder rules on directly declared objects), A tenant object overlay drops everyobjectExtensionsfield from the resolved schema, andeffectivecontradictscodein the same response #8027 and [finding] mergeObjectDefinitions docblock promises 'other props: later value wins' but the implementation silently drops every non-enumerated prop #12680 (extension merge defects at runtime).objectExtensions merged shape not validated defineStack lint→ 16 hits, none about judging an extension's merged fields.objectExtensions fields not judged by validation until runtime merge→ A tenant object overlay drops everyobjectExtensionsfield from the resolved schema, andeffectivecontradictscodein the same response #8027 and [finding] mergeObjectDefinitions docblock promises 'other props: later value wins' but the implementation silently drops every non-enumerated prop #12680 only.
None is this. Control: the queries reach the controlled-by-parent family (#7503, #9139) and the extension-merge family (#8027).
Dedupe words: controlled_by_parent objectExtensions second master_detail; extension merged field set sharingModel parse; posture lint objectExtensions blind.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 13, 2026
Triage: first grade,
bug·priority:p3·domain:spec·area:access, intopm:queue. The door judges the merged shape when it can see the targetTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-13T09:55Z. ⛔ Not a claim, ⛔ not a dispatch.security: it fails closed. The runtime denies reads and refuses writes on the merged object, so nothing is exposed. It is an authoring trap, with no measured producer (HotCRM declares noobjectExtensions, and the showcase's add nomaster_detail). PR feat(spec)!: a controlled_by_parent object derives its master from exactly one required master_detail; the runtime's fallback tiers retire (#23100) #23179 (finding(security): controlled_by_parent derives its master through a REQUIRED LOOKUP (runtime and lint, third tier) where ADR-0055 §1 requires exactly one required master_detail and fails closed — a widening no record states #23100) has landed, so the runtime half is the fail-closed one the card describes.objectExtensionsentry's target is visible to the door, the controlled-by-parent master rule judges the merged field set. "Visible" means declared in the same stack, or in apackages[]manifest the door already folds.defineStack's refinement and theos validateposture lint. They give the same verdict and message the single-object control gives.packages/spec/src/stack.zod.ts(the stack refinement) andpackages/lint/src/validate-security-posture.ts(domain:spec); rationale: judge the shape the runtime will run.objectstack-fleet commentedon Oct 14, 2026
Regrade: p3 → p1 ·
target:v18added · a metadata-protocol narrowing · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-14T07:14ZWhy it moves: the maintainer directed, on 2026-10-14, that spec-lane queue cards which change the metadata protocol (what
@objectstack/specaccepts or declares, or what a metadata door accepts) be raised. The bar triage now applies: a protocol change getstarget:v18, so it lands in the 18 window and objectui, cloud and HotCRM mirror it once. A narrowing (breaking) one is p1, because missing 18 GA means waiting for 19. A widening (additive) one is at least p2.Read fresh. On
mainb5b1bf7277,packages/spec/src/stack.zod.tsstill has no refinement that judges anobjectExtensionsentry against itscontrolled_by_parenttarget.ObjectStackDefinitionSchema(defineStack) starts refusing a stack whoseobjectExtensionsadd a second requiredmaster_detailto acontrolled_by_parentobject. That narrows a published accept set: breaking, with an ADR-0087 entry, and it must ride the 18 major.no, and it owes the contract-tier review.objectstack-fleet commentedon Oct 14, 2026
Claim: PM loop round 1 (#23210, triage
6142996061and regrade6163015687: when anobjectExtensionsentry's target is visible to the door,defineStack's refinement and theos validateposture lint judge the merged field set under the controlled-by-parent master rule; a narrowing,target:v18) · 2026-10-14T07:38ZSession:
session_01YRp5z4kK6vgYkrdH97ztsyAccount:
huangyiirene(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-23210-extension-master-detailWorktree:
objectstack-issue-23210Domain:
domain:specSeat:
domain:spec#1(seat post #6017)File surface (at
origin/mainb5b1bf727or later; stop on a breach and explain it in the report):packages/spec/src/stack.zod.ts, a refinement that judges eachobjectExtensionsentry whose target the stack can see against the merged field set. It reusescontrolledByParentMasterRefusal(packages/spec/src/data/controlled-by-parent-master.ts) and its message rather than copying either. That file is edited only if the merged-shape call needs a signature it lacks.packages/lint/src/validate-security-posture.ts(security-controlled-by-parent-ambiguous-relationon the merged shape), pluspackages/cli/src/commands/validate.tsonly if the lint's input measurably lacksobjectExtensions.packages/spec/src/migrations/entries/**(class by census) and the registries it feeds..changeset/23210-*.md.packages/objectql/src/engine.ts(the runtime merge and its fail-closed refusal stay the backstop). ⛔ NotObjectExtensionSchema's accept set (the routing alternative the card names; triage did not take it).Container & model:
M,mode:subagent,model: default(dispatch-gates --tier: "no path-derived mandate … floor sonnet · default opus · ceiling fable"); the review before the queue runs at the ceiling,CONTRACT_REVIEW_TIER(reason: a narrowing of a published accept set owes one contract-tier review).Clause-②: no (narrowing)
Responsibility:
packages/spec's stack refinement andpackages/lint's posture rules, which judge acontrolled_by_parentobject's masters only as declared, never as merged with a visibleobjectExtensionsentry | the runtime merge at boot fails closed (every read denied, every write422 INVALID_METADATA, one warn) | any author or AI extending an object in the same stack or a foldedpackages[]manifest; no measured producer today (HotCRM declares noobjectExtensions; the showcase's add nomaster_detail)Thread-read: 6163015687
Serial constraints cleared: open PRs' file lists read in this act. PR feat(spec)!: publish refuses an anonymous flow endpoint whose target flow runs as system, in both directions (ADR-0138 D2b) #23411 (ADR-0138 E2: the closed list of doors recorded in the conformance matrix, denial by domain, and D2b's publish refusal (D2, D2b) #23225, this seat, in the merge queue) edits
stack.zod.tsaroundapplyApiEndpointGates(imports and:167–:200), a different region; the dev syncsmainafter it lands. The security:externalSharingModelis declared and authored but has no runtime reader — an object'sexternalSharingModel: privatechanges no answer for any principal (ADR-0090 D11 declared-not-enforced) #23268 claim (6161609217, seat 3) holdsvalidate-security-posture.tsfor theexternalSharingModelrules, a different rule. Neither is aSINGLE_CLAIM_PATHSpath, so ordinary concurrency applies and the later lander mergesmain.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
os-dev-report
{ "issue": 23210, "status": "done", "branch": "claude/issue-23210-extension-master-detail", "pr": "https://github.com/objectstack-ai/objectstack/pull/23454", "session": "session_01YRp5z4kK6vgYkrdH97ztsy — the dispatching PM's session (mode:subagent); every commit carries it as the Claude-Session trailer", "premise_still_valid": true, "summary": "Both authoring doors now judge ADR-0055 §1 on a controlled_by_parent object's MERGED field set when the stack declares the target. Spec side: a new superRefine, applyControlledByParentExtensionGate, on ObjectStackDefinitionSchema, chained after applyApiEndpointGates and applyPermissionSetOwnerBindingGate, calling the existing internal controlledByParentMasterRefusal. The issue sits at objectExtensions.N.fields (or packages.J.manifest.objectExtensions.N.fields) with the declared control's message byte-equal, and defineStack throws STACK_SCHEMA_INVALID/422. Lint side: validateSecurityPosture gains a merged-shape pass emitting security-controlled-by-parent-ambiguous-relation or -no-relation at the same extension, through two shared push sites so the wording is one. Visibility reading (H2/H3): exact `name` equality with `extend` (the registry keys by the string verbatim; a namespace__name spelling never merges, measured), and the artifact as the boundary. With packages[], any body's extension judges any body's object or a top-level residual object, while top-level objectExtensions are not read because the engine never registers them there. Unjudged: unseen targets, targets two bodies declare, targets already refused as declared, and two packages tied on one key at the deciding priority (registration order decides; measured). Also: a step-18 D3 entry cbp-extension-merged-master-refused (relevantWhen objectExtensions) and a BREAKING changeset with FROM→TO and the remedy. No export, signature or error code added, so Clause-② stays no (narrowing).", "tests": "spec unit layer (vitest --project local, 4daa50d71): Test Files 675 passed, Tests 20487 passed | 1 todo. lint full (4daa50d71): 139 files, 6718 passed. objectql unit layer (--project local, 4daa50d71): 415 files, 8087 passed. cli --project unit (4daa50d71): 285 files passed, 4278 tests passed, 29 skipped. 2 suites (published-subpath-console.pin, published-subpath-hook-body.pin) refused for want of packages/cli/dist; after `turbo run build --filter=@objectstack/cli` they ran: 2 files, 29 passed. cli integration layer declared to CI (no spawn entry or integration file touched). Typecheck: spec green at f9744329d (the later merge moved no spec source), lint green at 4daa50d71. New pins: spec stack-cbp-extension-master.test.ts 12/12 and lint validate-security-posture-extension-merge.test.ts 16/16, including an 8-shape parse-vs-lint parity table. The #5017 meta-tests in validate-security-posture.test.ts table the new reads against the live schema shapes. Ablations on the committed tree via scripts/ablation-replace.mjs (anchor must hit, blob verified, restore verified): A1 drops .superRefine(applyControlledByParentExtensionGate) (anchor 1→0, blob ecc31d4e3de3→b0e64ef6c625) and the spec pin file goes 9 failed | 3 passed (the 3 are the declared control, the already-refused object and the tie, none of which depend on the refinement); restored, blob == HEAD, git diff HEAD empty. A2 deletes the pushExtensionMergedCbpFindings call (blob 1b241d17dfd0→5a4bafa4383f) and the lint pin file goes 9 failed | 7 passed; restored and verified the same way. Both subjects resolve through source, so no dist rebuild was owed. Registry probe (scratch tsx against SchemaRegistry, not committed) for H1/H2: card shape gives master_detail=[header:true,other:true]; an extension redeclaring header required:false gives [header:false] (whole field replaced by name); sharingModel on an extend body is not carried; adder p200 + fixer p300 gives one master; p300/p200 gives two; p200/p200 is decided by the later registration; one package's two entries leave the later; an extender at p50 still folds over the owner's base; extend 'cbpx__cbpx_line' never merges into cbpx_line. H4: os validate parses (validate.ts step 2b) before the rule table, so the card's stack now stops at the parse; the rule input is the parsed union stack, which keeps objectExtensions.", "mcp_calls": "0 — no MCP GitHub tool called", "api_writes": "2 relay strokes (scripts/pm/fleet-write/dispatch.mjs, executed as objectstack-fleet[bot]): (1) pr_create, run 38545791715 = POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/23454/assignees [huangyiirene]; body read back byte-identical (11521 bytes). (2) comment = POST /repos/objectstack-ai/objectstack/issues/23210/comments (this os-dev-report). Each stroke's own transport write is POST /repos/objectstack-ai/objectstack/dispatches under the session. label-write: 0, because the dispatch names no label and skip-changeset does not apply (a changeset ships). git push (not REST): the empty-branch probe plus 4 pushes, the last 4daa50d71.", "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted, not filed — `os explain` text for security-controlled-by-parent-ambiguous-relation / -no-relation (packages/lint/src/rule-explanations.ts) still describes only the declared object, while both rules now also fire on a merged one. Outside this card's file surface; listed in the PR's Acceptance notes", "carrier: none · noted, not filed — validateSecurityPosture now reads stack.packages through packagesOf, so a malformed non-array packages throws INVALID_ARTIFACT_PACKAGES from this rule as its sibling readers already do. Behaviour change on an unparsed input only; listed in Acceptance notes" ], "gates": { "derivation": "node scripts/pm/dispatch-gates.mjs --commands at 4daa50d71: 91 families for 8 changed paths", "reconciliation": "dispatch-gates --ran: '✓ dispatch-gates --ran: 91 derived famil(ies) accounted for — 89 run, 2 NOT-MEASURED (1 DERIVED from a recorded exit 3, 1 claimed).'", "not_measured": [ "NOT MEASURED: check:dual-build-cjs-loads, reason: exit 3 PREREQUISITE NOT MET ('Run `pnpm build` first'); only the cli dependency closure was built", "NOT MEASURED: type-check-debt, reason: whole-repo tsc census not run locally; owed to CI's TypeScript Type Check job" ], "local_only": [ "pnpm check:skill-frame-freshness :: exit 0", "pnpm check:dev-prereqs :: exit 1, an unmet local precondition and not a verdict on this diff: 9 workspace packages outside the cli closure have no dist. spec/dist was built from this head's spec source" ], "key_verdict_lines": [ "check-adr-0087-registration --base origin/main: '✓ check-adr-0087-registration: 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition.' — registered cbp-extension-merged-master-refused (new here)", "check:api-surface: '@objectstack/spec public API surface + factory signatures unchanged ✓'", "spec check:generated: '✓ All 14 generated artifacts are up to date'", "check:nul-bytes: 'check-nul-bytes: OK (scanned 11376 text file(s) ... no raw ASCII control bytes)'", "check-spec-docblock-symbol-anchors: '✅ ... 0 NEW line anchors on tracked targets survive'" ], "commands": [ "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0", "node scripts/check-adr-0087-registration.mjs --self-test :: exit 0", "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0", "node scripts/check-changeset-no-major.mjs --self-test :: exit 0", "node scripts/check-ci-filter-parity.mjs :: exit 0", "node scripts/check-closing-keyword-parity.mjs :: exit 0", "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0", "node scripts/check-comment-mask-adoption.mjs :: exit 0", "node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0", "node scripts/check-comment-mask-corpus.mjs :: exit 0", "node scripts/check-dev-prereqs.mjs --self-test :: exit 0", "node scripts/check-dts-emitted.mjs --self-test :: exit 0", "node scripts/check-empty-changeset.mjs --base origin/main :: exit 0", "node scripts/check-empty-changeset.mjs --self-test :: exit 0", "node scripts/check-issue-citations.mjs :: exit 0", "node scripts/check-keyed-text-bounds.mjs :: exit 0", "node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0", "node scripts/check-platform-object-tenancy-census.mjs :: exit 0", "node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0", "node scripts/check-plugin-teardown-shape.mjs :: exit 0", "node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0", "node scripts/check-registry-log-declared.mjs :: exit 0", "node scripts/check-registry-log-declared.mjs --self-test :: exit 0", "node scripts/check-rest-log-spy-declared.mjs :: exit 0", "node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0", "node scripts/check-spec-docblock-symbol-anchors.mjs :: exit 0", "node scripts/check-spec-docblock-symbol-anchors.mjs --self-test :: exit 0", "node scripts/check-system-context-census.mjs :: exit 0", "node scripts/check-system-context-census.mjs --self-test :: exit 0", "node scripts/check-undeclared-dep-imports.mjs :: exit 0", "node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0", "node scripts/docs-audit/check-affected-docs.mjs :: exit 0", "node scripts/docs-audit/check-drift-comment.mjs :: exit 0", "node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0", "node scripts/release-pending-publish.mjs --self-test :: exit 0", "pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0", "pnpm --filter @objectstack/spec run check:api-surface :: exit 0", "pnpm --filter @objectstack/spec run check:authorable-surface :: exit 0", "pnpm --filter @objectstack/spec run check:browser-reachable-entries :: exit 0", "pnpm --filter @objectstack/spec run check:docs :: exit 0", "pnpm --filter @objectstack/spec run check:dual-source-exports :: exit 0", "pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0", "pnpm --filter @objectstack/spec run check:empty-state :: exit 0", "pnpm --filter @objectstack/spec run check:entry-nameability :: exit 0", "pnpm --filter @objectstack/spec run check:export-origins :: exit 0", "pnpm --filter @objectstack/spec run check:exported-any :: exit 0", "pnpm --filter @objectstack/spec run check:liveness :: exit 0", "pnpm --filter @objectstack/spec run check:llms-txt :: exit 0", "pnpm --filter @objectstack/spec run check:objectui-pin-citations :: exit 0", "pnpm --filter @objectstack/spec run check:skill-refs :: exit 0", "pnpm --filter @objectstack/spec run check:spec-changes :: exit 0", "pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0", "pnpm --filter @objectstack/spec run check:upgrade-guide :: exit 0", "pnpm --filter @objectstack/spec run check:variant-docs :: exit 0", "pnpm check:changeset-gate-self-tests :: exit 0", "pnpm check:cross-package-test-inputs :: exit 0", "pnpm check:dispatcher-error-vocabulary :: exit 0", "pnpm check:doc-authoring :: exit 0", "pnpm check:docs-transcript-drift :: exit 0", "pnpm check:driver-memory-census :: exit 0", "pnpm check:dts-closure :: exit 0", "pnpm check:dual-build-cjs-loads :: exit 3", "pnpm check:engine-double-contract :: exit 0", "pnpm check:error-status-conformance :: exit 0", "pnpm check:gitlink-declared :: exit 0", "pnpm check:issue-citations :: exit 0", "pnpm check:lean-entry-closure :: exit 0", "pnpm check:logger-receiver-detach :: exit 0", "pnpm check:merge-driver :: exit 0", "pnpm check:nul-bytes :: exit 0", "pnpm check:objectql-double-limit :: exit 0", "pnpm check:objectui-changeset :: exit 0", "pnpm check:org-identifier :: exit 0", "pnpm check:page-declaration-shape :: exit 0", "pnpm check:pm-changeset-deadline-census :: exit 0", "pnpm check:pm-prior-rulings :: exit 0", "pnpm check:published-files :: exit 0", "pnpm check:query-options-erasure :: exit 0", "pnpm check:refd-timer-probe :: exit 0", "pnpm check:role-word :: exit 0", "pnpm check:skill-top-level-keys :: exit 0", "pnpm check:slot-lookup :: exit 0", "pnpm check:sourcemap-no-sources-content :: exit 0", "pnpm check:spec-parsed-alias :: exit 0", "pnpm check:stack-collection-maps :: exit 0", "pnpm check:test-source-alias :: exit 0", "pnpm check:tier-file-adoption :: exit 0", "pnpm check:type-check-coverage :: exit 0", "pnpm check:watch-hint-literal :: exit 0", "pnpm check:where-matcher :: exit 0", "pnpm check:dev-prereqs :: exit 1", "pnpm check:skill-frame-freshness :: exit 0", "NOT-MEASURED pnpm check:type-check-debt :: not run locally — a whole-repo tsc census over every package, owed to CI's TypeScript Type Check job; the dispatch permits recording it as NOT MEASURED" ], "ci": "in_progress at report time: 24 check-runs on 4daa50d71, 2 completed and 22 in progress (Governed Surface Queue Guard and Check Changeset in_progress). Not awaited, per contract" }, "line_budget": "n/a — no skills/** path touched", "deviations": [ "Verify-lock contention: holder pid 20838 (issue-23357 final3.sh) held the lock 40+ min while this slot sat at the head of the queue (three 99 queue-timeouts). Two narrowed checks ran unlocked at one worker: the spec pin file, and the lint pins under a scratch vitest config aliasing @objectstack/spec to source. No verdict above rests on them; both were re-measured under the lock (spec full unit layer; lint pins and full suite against the rebuilt spec dist)", "Main merges: fast-forwarded to c857a93ee (#23411 landed) before the first edit, so BASE = c857a93ee. Merged a7a135dc2 through scripts/pm/os-regen-merge.sh as merge commit 4daa50d71 on the seat's #23412 overlap notice; it was clean, with no os-regen path moved. main later reached 05ab9ef89 (8 commits, none touching this diff's files, none adding a relevantWhen entry) and was not merged again", "The harness attribution reminder asked for a model-named Co-Authored-By trailer and a different PR footer. Per AGENTS.md, commits carry the model-free pair (Co-authored-by: Claude, Claude-Session) and the PR body the session-URL footer", "File surface: beyond the claim's list, packages/spec/src/migrations/semantic-relevance.test.ts (the reviewed enumeration of relevantWhen entries, 25→26 total, 20→21 for step 18) and packages/lint/src/validate-security-posture.test.ts (the #5017 receiver and key tables) changed, both pins the entry and the lint edit feed. controlled-by-parent-master.ts, cli validate.ts, objectql engine.ts and ObjectExtensionSchema are untouched", "Contract drift mid-run: #23405 landed a slimmer local-verification contract in os-dev.md. This run followed the loaded contract (full derived union, consumer readings named by the dispatch) and also ran the new contract's two local-only families", "Worktree /home/user/objectstack-issue-23210 removed after the PR opened. Scratch probe, logs and actions files remain under the issue scratchpad only" ], "files_changed": [ ".changeset/23210-cbp-extension-merged-master.md", "packages/lint/src/validate-security-posture-extension-merge.test.ts", "packages/lint/src/validate-security-posture.test.ts", "packages/lint/src/validate-security-posture.ts", "packages/spec/src/migrations/entries/semantic/18.cbp-extension-merged-master-refused.ts", "packages/spec/src/migrations/semantic-relevance.test.ts", "packages/spec/src/stack-cbp-extension-master.test.ts", "packages/spec/src/stack.zod.ts" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
ACCEPT → PR #23454 comment 6165781809 ·
domain:specseat 1 (#6017) · sessionsession_01YRp5z4kK6vgYkrdH97ztsy· 2026-10-14T10:00ZGenerated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Landed: PR #23454 →
4e0838d99(Fixes #23210) · 2026-10-14T10:52Zdomain:specseat 1 (#6017) ·huangyiirene· sessionsession_01YRp5z4kK6vgYkrdH97ztsy· holder of claim6163383778. Records: ACCEPT## 席位核对6165781809and contract review PASS6166058781, both on4daa50d71.GET /pulls/23454merged: trueat 10:52:05Z,merge_commit_sha4e0838d99GET /branches,gh-readonly-queue/*origin/maingit merge-base --is-ancestor 4e0838d99 origin/main4e0838d99is the tip, a squash on08ea7e368GET /issues/23210closed/completedon the merge. The PR's timeline also cross-references #23272, which stays open; it is not this cardState change: this act removes
pm:dispatched. The contract review named two residuals, and both are carried to the seat's round report:objectExtensionson apackages[]stack, which is inert at both doors.Generated by Claude Code