Repository navigation
fix(spec)!: composeStacks refuses a datasource whose external.connector names no connector instance the composed inputs declare - #27939
Conversation
…ainst the connectors of every input The composed counterpart of the per-stack check (ADR-0015 amendment D7): an input built with strict: false carried a dangling connector name, a descriptor or an instance with nothing to lend into the artifact with no verdict. Also three doc-only should-fixes in injected-system-column-provenance and the IDataEngine.listDatasourceDefs member doc. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2cLiZUHvCtV2YGRWAd
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2cLiZUHvCtV2YGRWAd
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2cLiZUHvCtV2YGRWAd
📓 Docs Drift CheckThis PR changes 1 package(s): 11 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 168 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin c95fb0ac83995aa664d86372ca48e4d8f8793644 && git checkout c95fb0ac83995aa664d86372ca48e4d8f8793644
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin a2f434dcc076a34f42f47e64b27414d4a2c87f9e 1f471e098a7b9502c6745c076236e6a172f88e17 && git checkout -B drift-repro a2f434dcc076a34f42f47e64b27414d4a2c87f9e && git merge --no-ff 1f471e098a7b9502c6745c076236e6a172f88e17
node scripts/docs-audit/affected-docs.mjs --json a2f434dcc076a34f42f47e64b27414d4a2c87f9e
|
|
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV The seat's at-tier review call is refused by the model quota (Fable still out at the last probe, 2026-10-30T04:26Z), and per contract-review.md the tier is not lowered. Owed on this head: one same-shape
|
|
triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK Contract-review-claim: Generated by Claude Code |
|
triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK Contract reviewServed-tier: Review at ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Findings. Must-fix 1: Implemented-by: VERDICT: FAIL Generated by Claude Code |
… bodies and names a shadowed instance A multi-package input carries its connectors and datasources only in its package bodies, so reading the top level alone refused an instance the input carries. A connector declared twice whose kept entry cannot lend now names both declarations and prescribes the duplicate's fix. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2cLiZUHvCtV2YGRWAd
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2cLiZUHvCtV2YGRWAd
|
triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK Contract-review-claim: Generated by Claude Code |
|
triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK Contract reviewServed-tier: Delta review at ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Findings. Must-fix 1: the last-wins arm reads "the later declaration is the one the runtime keeps" onto the Implemented-by: VERDICT: FAIL Generated by Claude Code |
… the composed external.connector check The registry keeps one connector entry per package, and the lender answers the first registered, so last-wins holds only within one slot. Two packages that disagree on whether their entry can lend are refused as ambiguous in either order. An entry a nested input carries in both halves is read once. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2cLiZUHvCtV2YGRWAd
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2cLiZUHvCtV2YGRWAd
|
triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK Contract-review-claim: Generated by Claude Code |
|
triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK Contract reviewServed-tier: Delta review at ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Findings. No must-fix. Should-fix 1: the Implemented-by: VERDICT: PASS Generated by Claude Code |
|
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV 维护者速读
席位意见:ACCEPT。契约复核 PASS 已在案(分诊 6516406275, 席位核对
|
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Requested by 田騏 · project thread
Full review owed at the reviewer tier. Developed on the default tier while the reviewer tier's quota is out. This PR touches the published spec (
packages/spec/src/**), so it lands only after a full review at the reviewer tier and aCONTRACT_REVIEW_TIERcontract review record PASS on the current head.Fixes #27803
Clause-②: yes (narrowing)
Metadata-protocol: none
Skills-docs: follow-up #27786 (it teaches
external.connectorin the datasources skill rule and the two data-modeling guides; the composed check applies that same rule, with the same findings, so it adds nothing an author writes differently)维护者速读
composeStacks合成多个栈时,按所有输入的connectors检查每个数据源的external.connector:名字不存在、只是目录描述(无provider)、实例没有auth,三种情况都在构建时拒收,提示与单栈defineStack完全一致。另附三处只改注释的小修(injected-system-column-provenance.ts两处、IDataEngine.listDatasourceDefs一处)。strict: false构建的输入会把悬空的名字带进合成产物,作者要到首次连接才发现。packages/spec/src/stack.zod.ts新增的collectComposedDatasourceConnectorErrors及其在composeStacks第 3b 步的调用;stack-datasource-connector.test.ts新增的 composeStacks 一组。pnpm --filter @objectstack/spec exec vitest run src/stack-datasource-connector.test.ts,预期 25 个测试全过。What changes
packages/spec/src/stack.zod.ts: a newcollectComposedDatasourceConnectorErrors(stacks, preserve)runscollectDatasourceConnectorErrorsover thedatasourcesand theconnectorsof every input, in the shape ofcollectComposedExternalBindingDatasourceErrors.composeStackscalls it in step 3b, the artifact cross-reference pass, so its findings arrive under the sameStackCrossReferenceError(STACK_CROSS_REFERENCE_INVALID, 422) and read exactly as the per-stack ones do.provider) or an instance with noauth(orauth: { type: 'none' }) carried by an input built withstrict: false.providedCollectionEntriesreads each input's top level and everypackages[]body (theprovidedFlowNamesreading), so a nested multi-package input is judged on what its package bodies carry. An entry object a nested input holds in both halves is read once.preserveartifact, or a package body beside the flattened top level) the registry keeps both and the lender answers the first registered, so when one entry can lend and the other cannot, the borrow is refused as ambiguous in either declaration order. When both can lend, it composes.shadowedLenderhook for the duplicate's prescription, and three small predicates (connectorHasProvider,connectorHasCredential,connectorCanLend) shared by both collectors. All module-private.isInjectedColumnDefinition: plugin-sharing'sequalsShippedDefcopy is gone (it readsisOwnerAnchorUnprovable), so plugin-security's is the one left.isOwnerAnchorUnprovable's fail direction: ordinary owner scoping on a column the remote lacks matches no row or raises (Postgres and MySQL raise, SQLite matches no row); neither widens.IDataEngine.listDatasourceDefs?: each entry carries the storeddriverid, and omits the key for a definition that never named one (checked againstObjectQL.listDatasourceDefsinpackages/objectql/src/engine.ts)..changeset/27803-composed-datasource-connector.md:@objectstack/specpatch, the two declaration lines, a BREAKING banner naming every CLI command that loads a composed project, and an ADR-0087not-required (no-migration-prescription)marker.Why
The contract review of PR #27783 (record 6511230689, should-fix d) left the composed counterpart of the
external.connectorcheck out of that PR. D7 ruling 6486136301: "refused at build when the named instance is not declared". A composition is a build too.Verification
All on head
1f471e09.packages/spec/src/stack-datasource-connector.test.ts: 25, of which 17 in thecomposeStacksgroup. A cross-input pair composes in both orders underlastandpreserve; a dangling name in astrict: falseinput is refused under both; a descriptor and an auth-less instance another input declares are refused with their findings; a name declared twice in the flattened artifact is judged against the later entry and its finding names both declarations; two packages of apreserveartifact that disagree on whether their entry can lend are refused as ambiguous in both orders, and two lending packages compose in both orders; a nested multi-package input is read through its package bodies (a borrower resolves against a body's instance, a body's datasource is judged, a body beside another input's top level counts as two slots), and an entry it carries in both halves is read once; the per-stack strict refusal is unchanged. Each refusal asserts the envelope (STACK_CROSS_REFERENCE_INVALID, 422), one finding, and thecomposeStacks artifact cross-reference validation failed (1 issue):header.composeStacks, 4 of the first 8 cases red; package bodies not read, the 3 nested cases red; the identity de-duplication removed, the both-halves case red; every entry put in one slot, the 2preserveambiguity cases and the body-beside-top-level case red. The other cases stayed green each time.pnpm --filter @objectstack/spec build,testandtypecheckunder the verify lock: build exit 0; 756 test files passed, 22498 tests passed (1 todo); typecheck passed.node scripts/pm/dispatch-gates.mjs --commands, derived after mergingorigin/main, named 82 families; all were run with exit codes recorded before any pipe.check:type-check-debtexited 3 until apnpm installpicked up the lockfile changemainbrought, then exit 0.dispatch-gates --ran: "82 derived famil(ies) accounted for — 82 run, 0 NOT-MEASURED (a DERIVED zero)".check-adr-0087-registrationaccepts the changeset's disposition, andcheck-changeset-no-majorwith this body passes (patch, a non-protocol narrowing). No generated reference moved. The full gate battery is CI's.(narrowing)arm), after building the closure, under the verify lock:VERDICT command-exit 0, Test Files 357 passed, 3 skipped (360); Tests 2875 passed, 20 skipped (2895); no failure.Acceptance notes
os build,os validate,os lint,os dev,os serve), because the author's config runscomposeStacks; the changeset says so.defineStackstill resolvesexternal.connectoragainst its own stack only, so a package can borrow a sibling package's instance only by building the borrower withstrict: false. AnartifactConnectorspromise of theartifactFlowsshape would close that; it is the epic PM's to card.objectstack-issue-27803on the claimed branch, as the Claim names.Human Minimum
packages/specholds no runtime logic. (A build-time cross-reference collector beside its per-stack twin; no schema changed.)pnpm lintis CI's.main, one pull request.packages/spectouched: no generated artifact changes (only JSDoc and a build-time collector; the gate run left the tree clean).content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.🤖 Generated with Claude Code
https://claude.ai/code/session_017ihL2cLiZUHvCtV2YGRWAd