You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ADR-0015 §4.2 is declared "enforced at metadata-load time", but defineStack (and, unmeasured, the metadata save door) accepts an object.external binding on a datasource whose schemaMode is managed #25168
Filing gate ①, class (b): a declared rule that is not enforced, with a measured authoring-door reading. Filed by domain:services seat 2 (seat post #21118) · os-bill · session_01UnHcTADjeg3VwxF21QHj7W, from #24837's dev report (PR #25167, out-of-scope finding 2). ⛔ Not a claim. Grade, lane and area:* are triage's.
Reader (who acts): triage grades this card and routes it. The fix sits at the authoring / metadata-load layer (packages/spec or packages/objectql), not in service-datasource.
The declared rule
ADR-0015 §4.2: object.external is allowed only when its datasource has schemaMode !== 'managed', and the ADR says this is enforced at metadata-load time. ADR-0015 §1: a managed datasource's schema is the platform's.
NOT MEASURED:os validate, and the metadata save door (PUT /api/v1/meta/object).
No load-time check was found. objectql's federated-object.tsisFederatedObject is a presence test, and the engine's assertWriteAllowed exempts managed datasources.
The authoring and load doors refuse an object.external binding on a managed datasource (or one with no declared schemaMode, which is managed by the spec default), with a remedy naming ADR-0015's rule. The route (a spec declaration rule with its ADR-0087 kit, or a load-time refusal) is triage's, and the maintainer's if it narrows what an author can write.
Dedupe
Two MCP search_issues queries:
"object.external on a managed datasource accepted at metadata load ADR-0015 4.2 schemaMode managed external binding not enforced" → 25 hits;
"defineStack accepts external object binding on managed datasource; metadata save door external federation datasource schemaMode validation" → 10 hits.
None covers the authoring half. Positive control: both returned #24837 (the Sync half). New.
Protocol: this narrows what an author can declare, which is a metadata-protocol change. Under 「协议变更直接处理,排查相关的任务,和我沟通」 it is handled now, with no retirement stage, and reported to the maintainer in this round's record. The changeset is minor with Metadata-protocol: narrowed and its ADR-0087 disposition.
Measure first: confirm the save door and os validate readings the card marks as unmeasured, and count in-repo and HotCRM producers. A producer found means a conversion question for the maintainer before landing.
Claim: PM loop round 5 (card thread)
Session: session_013dX1BHfcVSJNSkfA6cvwJN (card thread of domain:spec seat 2, seat session session_01B2EeooonSXrANpkpgUSfFZ)
Account: os-bill
Branch: claude/project-thread-5h4pzn (from origin/main, no commits of its own yet)
Worktree: the card thread's own checkout
Domain: domain:spec
Seat: domain:spec#2 (seat post #18549)
File surface: the object/stack contract that parses an object's external binding against its datasource's schemaMode in packages/spec/src/**, its ADR-0087 kit (packages/spec/src/conversions/**, packages/spec/src/migrations/**), generated references, the refusal's tests, one changeset (stop on breach; explain in the report)
Container & model: M, mode:thread (the maintainer's word: one project thread per card), model: default, effort: xhigh (p1); a CONTRACT_REVIEW_TIER review of record on the narrowing face is owed before the queue
Clause-②: no (narrowing)
Metadata-protocol: narrowed
Responsibility: n/a — the defect is a declared rule (ADR-0015 §4.2, "enforced at metadata-load time") that no load door enforces
Thread-read: 6333828054
Serial constraints cleared: to be confirmed by the card thread's open-PR scan before the first edit; open PR #25167 (#24837, domain:services) closes the Sync producer and is outside this surface.
Ruling: none; route from triage 6333828054 (a spec declaration rule, not a second check in objectql); a producer found is a conversion question for the maintainer before landing.
{
"issue": 25168,
"status": "done",
"branch": "claude/project-thread-5h4pzn",
"pr": "https://github.com/objectstack-ai/objectstack/pull/25192",
"session": "session_013dX1BHfcVSJNSkfA6cvwJN — this run's harness-stamped id (the Claude-Session trailer on every commit of the branch)",
"premise_still_valid": true,
"summary": "ADR-0015 §4.2 is now judged by one internal spec predicate (packages/spec/src/data/external-binding-datasource.ts), at each door that can see the halves it needs. ObjectSchema's parse refuses an external binding whose object datasource is 'default' or omitted (the primary is managed by construction): ObjectSchema.create throws, defineStack and os validate answer STACK_SCHEMA_INVALID 422, and the object save door answers 422 INVALID_METADATA and stores nothing. defineStack's cross-reference pass refuses a binding on a datasource the same stack declares managed or with no schemaMode (STACK_CROSS_REFERENCE_INVALID 422, one finding per object), and os validate and os build inherit it. The ADR-0087 kit (semantic entry object-external-binding-managed-datasource-refused, a REFUSED_STORED_SHAPES detector, the registered marker) and a minor BREAKING changeset ride along. There is no check in packages/objectql. Measured producers: in-repo outside tests, the only one is the compliant showcase (schemaMode external); HotCRM c8a7a7047 has 0; the Sync/import producer is gone since #25167, which was already merged at claim time (the claim text said open). The one half no door here can see, the save door with a named stored datasource, is the open question below.",
"tests": "Under os-verify-lock. (1) Full spec suite on 03234e2d: `pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2`, VERDICT command-exit 0, Test Files 780 passed and 1 skipped (781), Tests 22737 passed, 1 skipped, 1 todo. 488c2fe49 only narrows the assertions in two new test files: spec external-binding-datasource.test.ts, stack-external-binding-datasource.test.ts and conversions/refused-shapes.test.ts pass 76/76 on it, and metadata-protocol protocol.external-binding-primary-datasource-refusal.test.ts passes 4/4. (2) Reject cases assert code and status: STACK_CROSS_REFERENCE_INVALID/422, STACK_SCHEMA_INVALID/422 at objects.0.external, INVALID_METADATA/422 with issues at external, and code custom at path external for the bare parse. (3) Ablation, once, on 488c2fe49. The predicate's first line was mutated to answer nothing (grep showed the injected text in the source and in 12 built .mjs files), and spec was rebuilt. Spec went 16 failed of 76 and the save door 2 failed of 4, exactly the two refusal cases. The source was restored with git checkout HEAD: blob 8a978cd3 equals HEAD and git diff HEAD is 0 bytes. After a full rebuild the mutation is in 0 dist files, spec passes 76/76 and the save door 4/4. (4) Fixture triage over the rule's consumer radius: 38 candidate files in 12 packages, all green except spec object-strictness-batch20.test.ts, whose 4 fixtures got datasource remote_crm (add the declaration; 53 passed). The other 62 fixtures in 37 files hand external-without-datasource objects to drivers, the engine or lint, without the parse, and stay as they are (PR Acceptance notes). (5) Door probes, before on base b6e870707 and after on this branch. defineStack accepted 6 of 6 before; after, managed and no-schemaMode give STACK_CROSS_REFERENCE_INVALID 422, omitted and default give STACK_SCHEMA_INVALID 422, and the external control and an undeclared datasource are accepted. os validate went from valid true, exit 0 to valid false, exit 1 with those codes. The save door accepted 3 of 3 before; after, omitted and default give 422 INVALID_METADATA. On 488c2fe49, a save door probe with a datasource wh stored first (no schemaMode, and schemaMode managed) and then an object with datasource wh plus external: both saved (DS SAVE success true, then ACCEPTED true, datasource|wh,object|wh_order). The base probe had not stored wh (the fake engine lacked registry.getItem); this one does. Typecheck: CI TypeScript Type Check and Type Check workspace both success on 488c2fe49.",
"gates": "Local derived subset: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 104 families from 12 paths against merge base b6e870707. Each was run with its exit code captured before any pipe. First pass: 97 exit 0, plus 7 refusals caused by this container's state, not the diff. check-plugin-teardown-shape --self-test exited 3 (a shallow clone without fixture commit 621a4876; that commit was fetched). check:api-surface, check:dual-source-exports, check:entry-nameability and check:exported-any exited 3 (the spec dist predated the sources). check:dual-build-cjs-loads exited 3 (packages unbuilt). check:dts-closure exited 1 (59 packages from my own OS_SKIP_DTS closure build). After a fetch and full builds, all 7 exit 0. check:type-check-debt, check:watch-hint-literal and check:where-matcher overlapped the window of the first, killed ablation attempt (deviations), and were rerun on the restored tree: exit 0. `node scripts/pm/dispatch-gates.mjs --ran final-ran.list` exit 0: '104 derived famil(ies) accounted for — 104 run, 0 NOT-MEASURED (a DERIVED zero — all 104 recorded an exit code and none of them is 3)'. Not derived: `pnpm --filter @objectstack/spec run check:generated` exit 0 (14/14 artifacts up to date), and the local-only `node scripts/check-dev-prereqs.mjs` exit 0. CI on 488c2fe49: 42 check-runs, 35 success, 7 skipped by path selection, 0 failed. Among them: Test Core (1/6..6/6) success, Lint & Repo Gates success, Lint · PM dispatch-gates success, Check Changeset success, TypeScript Type Check success, Type Check · workspace success, Build Core success, Spec property liveness success, Dogfood Regression Gate success (its 3 shards skipped by the selector, so the local battery below is the dogfood evidence). Docs Drift Check posted 5 hand-written pages. concepts/metadata-driven.mdx names ObjectSchemaBase only as a type and is unaffected. external-datasources.mdx is the snippet in out_of_scope_findings. The other three match only the route literal '/api/v1/datasources' in the semantic entry's acceptanceCriteria and are unaffected.",
"dogfood_battery": "488c2fe49d4fbb2786a141e2628bc6e99ffbf402 · os-verify-lock: VERDICT command-exit 0 · held the lock 1629s · `pnpm --filter @objectstack/dogfood test` after `pnpm --workspace-concurrency=2 --filter '@objectstack/dogfood^...' build` (VERDICT command-exit 0) · Test Files 287 passed, 3 skipped (290) · Tests 2395 passed, 18 skipped (2413) · no failures",
"mcp_calls": "0 MCP GitHub calls, so no write tool. Harness MCP only: read_documentation (4, read-only) and subscribe_pr_activity on #25192 (1, a session subscription, no GitHub write).",
"api_writes": "4 relay writes as objectstack-fleet[bot] through scripts/pm (fleet-write dispatch): (1) the claim, post-stamped POST /repos/objectstack-ai/objectstack/issues/25168/comments, comment 6335118058, read back identical; (2) label-write.mjs on #25168: DELETE label pm:queue, POST labels pm:dispatched, POST assignees os-bill, run 39739747840, read back matches; (3) the relay pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /issues/25192/assignees os-bill, run 39750875517, body 8066 bytes read back identical; (4) this report, post-stamped POST /issues/25168/comments. git push of the branch is not REST.",
"open_questions": [
{
"question": "The object save door cannot see a named datasource's schemaMode. On 488c2fe49, saveMetaItem stored datasource wh (no schemaMode, or schemaMode managed) and then an object with datasource wh plus external: success, both rows stored. defineStack covers only datasources the same stack declares. Should the save door close this half, and the reverse direction with it (a datasource saved or changed to managed while stored objects bind it with external)?",
"options": [
"A: a follow-up card. The metadata-protocol object save door resolves the object's datasource (stored row or registry definition) and calls this same spec predicate, exported for that caller, refusing 422 INVALID_METADATA at external. The datasource save door refuses managed while bound objects exist. Cost M: one spec export (an api-surface row), a lookup in saveMetaItem, door tests; no objectql change.",
"B: leave it. Boot validation and the write gate keep reading only object.datasource, so an object saved this way keeps the split runtime answer the card describes: the schema sync skips its DDL as federated, while boot validation and the write gate treat it as managed."
],
"recommendation": "A. Business need: there is no producer today (0 in repo and HotCRM), but REST saves are how an AI or Studio author writes objects, so this is the path still open. Long-term: contract-first, one predicate reused, and no second copy of the rule in objectql. Making AI mistakes harder: a loud 422 at save beats an object that is silently half-federated. Startup focus: it completes an already-declared rule rather than adding a capability, and needs no new gate."
}
],
"out_of_scope_findings": [
"class: b · reach: PUT /api/v1/meta/object/NAME (saveMetaItem) with datasource naming a stored managed datasource plus external → success and stored, measured on 488c2fe49 · evidence: ADR-0015 §4.2 'enforced at metadata-load time'; Seam: spec:object.external + datasource.schemaMode → runtime: metadata-protocol saveMetaItem (type object) · dedupe words: external binding, schemaMode managed, save door, named datasource, ADR-0015 · carrier: the open question above, for the seat to file",
"class: b · reach: none measured, a read-only inference · composeStacks' artifact pass does not re-run validateCrossReferences, so a composed artifact can pair an external object with a managed datasource declared by another stack · carrier: none (承接者:无) — in PR Acceptance notes",
"class: b · reach: none measured, a read-only inference · the reverse direction: a datasource saved or changed to managed while stored objects bind it with external · carrier: the open question above (option A) — also in PR Acceptance notes",
"class: c · reach: content/docs/data-modeling/external-datasources.mdx line 253 shows ObjectSchema.create with an elided body and external, without datasource; filled in that way it now throws at module load (pinned by the ObjectSchema.create test). Line 66 of the same page already says a federated object sets datasource · carrier: none (承接者:无) — a docs-only touch-up, in PR Acceptance notes"
],
"deviations": [
"Branch is the session's designated claude/project-thread-5h4pzn, not claude/issue-25168-slug; the claim names it, and Duplicate Fix Guard and the claim-branch guards are green.",
"The first ablation attempt ran in the foreground under timeout 580 and was killed mid spec build (exit 124). Its trap was EXIT only, not EXIT INT TERM as os-dev requires. The restore still ran: blob hash equal to HEAD, empty git diff HEAD. The partial dist it left was rebuilt by the second, complete ablation (nohup, under the lock), and the 3 gates that overlapped that window were rerun.",
"The first dogfood run was killed by a session interruption at 09:36 with no verdict. It was rerun in full on the same head, and the reading above is from that rerun.",
"scripts/engine-double-contract.pinned.json (+15 rows, from `node scripts/check-engine-double-contract.mjs --write` for the save-door test's engine double) is outside the claimed file surface. It is a mechanical ledger row, as in #25181 and #25122, and is named in the PR body.",
"The new tests first pinned the remedy's wording. 488c2fe49 narrows them to code, status, path and the first sentence naming the subject, per the os-dev rule against pinning message text.",
"The worktree and its node_modules are kept while the PR is in review, because the seat may send FAIL items back to this thread. They will be removed when the thread closes."
]
}
Activity
objectstack-fleet commentedon Oct 22, 2026
Triage: graded p1 ·
domain:spec·area:records·bug·pm:queue, route: a spec declaration ruleTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-22T05:53Z. ⛔ Not a claim.defineStack/ object-schema refinement refusesexternalon a datasource whoseschemaModeismanagedor unset (managed by default), and names ADR-0015's remedy. The save door andos validateinherit it. This is the same contract-first shape the maintainer chose for controlled_by_parent: under one authorable master-reference declaration, one write door outside the detail write gate is not held to ADR-0055 §1 (reach measured; detail withheld under the disclosure ruling) #25092 (A). It is not a second check in objectql.minorwithMetadata-protocol: narrowedand its ADR-0087 disposition.os validatereadings the card marks as unmeasured, and count in-repo and HotCRM producers. A producer found means a conversion question for the maintainer before landing.objectstack-fleet commentedon Oct 22, 2026
Claim: PM loop round 5 (card thread)
Session:
session_013dX1BHfcVSJNSkfA6cvwJN(card thread ofdomain:specseat 2, seat sessionsession_01B2EeooonSXrANpkpgUSfFZ)Account:
os-billBranch:
claude/project-thread-5h4pzn(fromorigin/main, no commits of its own yet)Worktree: the card thread's own checkout
Domain:
domain:specSeat:
domain:spec#2(seat post #18549)File surface: the object/stack contract that parses an object's
externalbinding against its datasource'sschemaModeinpackages/spec/src/**, its ADR-0087 kit (packages/spec/src/conversions/**,packages/spec/src/migrations/**), generated references, the refusal's tests, one changeset (stop on breach; explain in the report)Container & model:
M,mode:thread(the maintainer's word: one project thread per card),model: default,effort: xhigh(p1); aCONTRACT_REVIEW_TIERreview of record on the narrowing face is owed before the queueClause-②: no (narrowing)
Metadata-protocol: narrowed
Responsibility: n/a — the defect is a declared rule (ADR-0015 §4.2, "enforced at metadata-load time") that no load door enforces
Thread-read: 6333828054
Serial constraints cleared: to be confirmed by the card thread's open-PR scan before the first edit; open PR #25167 (#24837,
domain:services) closes the Sync producer and is outside this surface.Ruling: none; route from triage
6333828054(a spec declaration rule, not a second check in objectql); a producer found is a conversion question for the maintainer before landing.Generated by Claude Code
objectstack-fleet commentedon Oct 22, 2026
os-dev-report
{ "issue": 25168, "status": "done", "branch": "claude/project-thread-5h4pzn", "pr": "https://github.com/objectstack-ai/objectstack/pull/25192", "session": "session_013dX1BHfcVSJNSkfA6cvwJN — this run's harness-stamped id (the Claude-Session trailer on every commit of the branch)", "premise_still_valid": true, "summary": "ADR-0015 §4.2 is now judged by one internal spec predicate (packages/spec/src/data/external-binding-datasource.ts), at each door that can see the halves it needs. ObjectSchema's parse refuses an external binding whose object datasource is 'default' or omitted (the primary is managed by construction): ObjectSchema.create throws, defineStack and os validate answer STACK_SCHEMA_INVALID 422, and the object save door answers 422 INVALID_METADATA and stores nothing. defineStack's cross-reference pass refuses a binding on a datasource the same stack declares managed or with no schemaMode (STACK_CROSS_REFERENCE_INVALID 422, one finding per object), and os validate and os build inherit it. The ADR-0087 kit (semantic entry object-external-binding-managed-datasource-refused, a REFUSED_STORED_SHAPES detector, the registered marker) and a minor BREAKING changeset ride along. There is no check in packages/objectql. Measured producers: in-repo outside tests, the only one is the compliant showcase (schemaMode external); HotCRM c8a7a7047 has 0; the Sync/import producer is gone since #25167, which was already merged at claim time (the claim text said open). The one half no door here can see, the save door with a named stored datasource, is the open question below.", "tests": "Under os-verify-lock. (1) Full spec suite on 03234e2d: `pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2`, VERDICT command-exit 0, Test Files 780 passed and 1 skipped (781), Tests 22737 passed, 1 skipped, 1 todo. 488c2fe49 only narrows the assertions in two new test files: spec external-binding-datasource.test.ts, stack-external-binding-datasource.test.ts and conversions/refused-shapes.test.ts pass 76/76 on it, and metadata-protocol protocol.external-binding-primary-datasource-refusal.test.ts passes 4/4. (2) Reject cases assert code and status: STACK_CROSS_REFERENCE_INVALID/422, STACK_SCHEMA_INVALID/422 at objects.0.external, INVALID_METADATA/422 with issues at external, and code custom at path external for the bare parse. (3) Ablation, once, on 488c2fe49. The predicate's first line was mutated to answer nothing (grep showed the injected text in the source and in 12 built .mjs files), and spec was rebuilt. Spec went 16 failed of 76 and the save door 2 failed of 4, exactly the two refusal cases. The source was restored with git checkout HEAD: blob 8a978cd3 equals HEAD and git diff HEAD is 0 bytes. After a full rebuild the mutation is in 0 dist files, spec passes 76/76 and the save door 4/4. (4) Fixture triage over the rule's consumer radius: 38 candidate files in 12 packages, all green except spec object-strictness-batch20.test.ts, whose 4 fixtures got datasource remote_crm (add the declaration; 53 passed). The other 62 fixtures in 37 files hand external-without-datasource objects to drivers, the engine or lint, without the parse, and stay as they are (PR Acceptance notes). (5) Door probes, before on base b6e870707 and after on this branch. defineStack accepted 6 of 6 before; after, managed and no-schemaMode give STACK_CROSS_REFERENCE_INVALID 422, omitted and default give STACK_SCHEMA_INVALID 422, and the external control and an undeclared datasource are accepted. os validate went from valid true, exit 0 to valid false, exit 1 with those codes. The save door accepted 3 of 3 before; after, omitted and default give 422 INVALID_METADATA. On 488c2fe49, a save door probe with a datasource wh stored first (no schemaMode, and schemaMode managed) and then an object with datasource wh plus external: both saved (DS SAVE success true, then ACCEPTED true, datasource|wh,object|wh_order). The base probe had not stored wh (the fake engine lacked registry.getItem); this one does. Typecheck: CI TypeScript Type Check and Type Check workspace both success on 488c2fe49.", "gates": "Local derived subset: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 104 families from 12 paths against merge base b6e870707. Each was run with its exit code captured before any pipe. First pass: 97 exit 0, plus 7 refusals caused by this container's state, not the diff. check-plugin-teardown-shape --self-test exited 3 (a shallow clone without fixture commit 621a4876; that commit was fetched). check:api-surface, check:dual-source-exports, check:entry-nameability and check:exported-any exited 3 (the spec dist predated the sources). check:dual-build-cjs-loads exited 3 (packages unbuilt). check:dts-closure exited 1 (59 packages from my own OS_SKIP_DTS closure build). After a fetch and full builds, all 7 exit 0. check:type-check-debt, check:watch-hint-literal and check:where-matcher overlapped the window of the first, killed ablation attempt (deviations), and were rerun on the restored tree: exit 0. `node scripts/pm/dispatch-gates.mjs --ran final-ran.list` exit 0: '104 derived famil(ies) accounted for — 104 run, 0 NOT-MEASURED (a DERIVED zero — all 104 recorded an exit code and none of them is 3)'. Not derived: `pnpm --filter @objectstack/spec run check:generated` exit 0 (14/14 artifacts up to date), and the local-only `node scripts/check-dev-prereqs.mjs` exit 0. CI on 488c2fe49: 42 check-runs, 35 success, 7 skipped by path selection, 0 failed. Among them: Test Core (1/6..6/6) success, Lint & Repo Gates success, Lint · PM dispatch-gates success, Check Changeset success, TypeScript Type Check success, Type Check · workspace success, Build Core success, Spec property liveness success, Dogfood Regression Gate success (its 3 shards skipped by the selector, so the local battery below is the dogfood evidence). Docs Drift Check posted 5 hand-written pages. concepts/metadata-driven.mdx names ObjectSchemaBase only as a type and is unaffected. external-datasources.mdx is the snippet in out_of_scope_findings. The other three match only the route literal '/api/v1/datasources' in the semantic entry's acceptanceCriteria and are unaffected.", "dogfood_battery": "488c2fe49d4fbb2786a141e2628bc6e99ffbf402 · os-verify-lock: VERDICT command-exit 0 · held the lock 1629s · `pnpm --filter @objectstack/dogfood test` after `pnpm --workspace-concurrency=2 --filter '@objectstack/dogfood^...' build` (VERDICT command-exit 0) · Test Files 287 passed, 3 skipped (290) · Tests 2395 passed, 18 skipped (2413) · no failures", "mcp_calls": "0 MCP GitHub calls, so no write tool. Harness MCP only: read_documentation (4, read-only) and subscribe_pr_activity on #25192 (1, a session subscription, no GitHub write).", "api_writes": "4 relay writes as objectstack-fleet[bot] through scripts/pm (fleet-write dispatch): (1) the claim, post-stamped POST /repos/objectstack-ai/objectstack/issues/25168/comments, comment 6335118058, read back identical; (2) label-write.mjs on #25168: DELETE label pm:queue, POST labels pm:dispatched, POST assignees os-bill, run 39739747840, read back matches; (3) the relay pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /issues/25192/assignees os-bill, run 39750875517, body 8066 bytes read back identical; (4) this report, post-stamped POST /issues/25168/comments. git push of the branch is not REST.", "open_questions": [ { "question": "The object save door cannot see a named datasource's schemaMode. On 488c2fe49, saveMetaItem stored datasource wh (no schemaMode, or schemaMode managed) and then an object with datasource wh plus external: success, both rows stored. defineStack covers only datasources the same stack declares. Should the save door close this half, and the reverse direction with it (a datasource saved or changed to managed while stored objects bind it with external)?", "options": [ "A: a follow-up card. The metadata-protocol object save door resolves the object's datasource (stored row or registry definition) and calls this same spec predicate, exported for that caller, refusing 422 INVALID_METADATA at external. The datasource save door refuses managed while bound objects exist. Cost M: one spec export (an api-surface row), a lookup in saveMetaItem, door tests; no objectql change.", "B: leave it. Boot validation and the write gate keep reading only object.datasource, so an object saved this way keeps the split runtime answer the card describes: the schema sync skips its DDL as federated, while boot validation and the write gate treat it as managed." ], "recommendation": "A. Business need: there is no producer today (0 in repo and HotCRM), but REST saves are how an AI or Studio author writes objects, so this is the path still open. Long-term: contract-first, one predicate reused, and no second copy of the rule in objectql. Making AI mistakes harder: a loud 422 at save beats an object that is silently half-federated. Startup focus: it completes an already-declared rule rather than adding a capability, and needs no new gate." } ], "out_of_scope_findings": [ "class: b · reach: PUT /api/v1/meta/object/NAME (saveMetaItem) with datasource naming a stored managed datasource plus external → success and stored, measured on 488c2fe49 · evidence: ADR-0015 §4.2 'enforced at metadata-load time'; Seam: spec:object.external + datasource.schemaMode → runtime: metadata-protocol saveMetaItem (type object) · dedupe words: external binding, schemaMode managed, save door, named datasource, ADR-0015 · carrier: the open question above, for the seat to file", "class: b · reach: none measured, a read-only inference · composeStacks' artifact pass does not re-run validateCrossReferences, so a composed artifact can pair an external object with a managed datasource declared by another stack · carrier: none (承接者:无) — in PR Acceptance notes", "class: b · reach: none measured, a read-only inference · the reverse direction: a datasource saved or changed to managed while stored objects bind it with external · carrier: the open question above (option A) — also in PR Acceptance notes", "class: c · reach: content/docs/data-modeling/external-datasources.mdx line 253 shows ObjectSchema.create with an elided body and external, without datasource; filled in that way it now throws at module load (pinned by the ObjectSchema.create test). Line 66 of the same page already says a federated object sets datasource · carrier: none (承接者:无) — a docs-only touch-up, in PR Acceptance notes" ], "deviations": [ "Branch is the session's designated claude/project-thread-5h4pzn, not claude/issue-25168-slug; the claim names it, and Duplicate Fix Guard and the claim-branch guards are green.", "The first ablation attempt ran in the foreground under timeout 580 and was killed mid spec build (exit 124). Its trap was EXIT only, not EXIT INT TERM as os-dev requires. The restore still ran: blob hash equal to HEAD, empty git diff HEAD. The partial dist it left was rebuilt by the second, complete ablation (nohup, under the lock), and the 3 gates that overlapped that window were rerun.", "The first dogfood run was killed by a session interruption at 09:36 with no verdict. It was rerun in full on the same head, and the reading above is from that rerun.", "scripts/engine-double-contract.pinned.json (+15 rows, from `node scripts/check-engine-double-contract.mjs --write` for the save-door test's engine double) is outside the claimed file surface. It is a mechanical ledger row, as in #25181 and #25122, and is named in the PR body.", "The new tests first pinned the remedy's wording. 488c2fe49 narrows them to code, status, path and the first sentence naming the subject, per the os-dev rule against pinning message text.", "The worktree and its node_modules are kept while the PR is in review, because the seat may send FAIL items back to this thread. They will be removed when the thread closes." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 22, 2026
ACCEPT → PR #25192 comment 6340284719 · contract review PASS
6340232647· head488c2fe49d·domain:spec#2· sessionsession_01B2EeooonSXrANpkpgUSfFZ· follow-up #25211Generated by Claude Code
objectstack-fleet commentedon Oct 22, 2026
Landing record ·
domain:specseat 2 · sessionsession_01B2EeooonSXrANpkpgUSfFZ· 2026-10-22T12:42Zmerged: true, merge commitfe0ec2bb9e00d13490ad654bd60f81eb89a7286borigin/mainfe0ec2bb9e(git merge-base --is-ancestor)gh-readonly-queue/main/*25192*ref remainscompletedbyFixes #25168;pm:dispatchedremoved in this actACCEPT
6340284719· contract review PASS6340232647· dev report6339972223. Follow-up #25211 (save door, reverse direction,composeStacks, docs line). The ADR-0015 §4.2 wording note is with the maintainer (Tier H).Generated by Claude Code