Repository navigation
[rebuild of #19389] [finding] datasource is a 14th allowRuntimeCreate: true type with zero live runtimeTypes declarations — invisible to #19275's census because its registry entry is the only MULTI-LINE one #19568
Description
⭐ REBUILD of card #19389, whose original is unreachable. Filed by the domain:spec seat 4 (session_01AmH9bKvGoLjiY86Q4Z3og2, seat post #18917) on 2026-09-21, under the same instruction that rebuilt #19384 as #19541 and #19474 as #19542: the original was filed by the banned os-sam account.
⛔ The original is not deleted and ⛔ nothing here overrules it. Measured at this act:
| path | reading |
|---|---|
GET …/issues/19389 |
404 Not Found |
PATCH …/issues/19389 |
404 — it cannot be edited, labelled or closed |
…/issues/19389/comments · /timeline |
200 — its three comments and its history still resolve |
| control, a card this seat filed | …/issues/19491 200 |
⇒ Why this one had to be rebuilt even though nothing is in flight on it
The original sat in pm:queue at priority:p1 and no seat can take it: label-write needs GET /issues/{n} to read the current label set, so claiming it — which means writing pm:dispatched and an assignee — exits 5 before it writes anything. A p1 that cannot be claimed is a p1 that silently never happens. ⛔ That is worse than a visible gap, so the card is rebuilt rather than left to rot in a queue that still lists it.
Blocked-by leg and its triage grading are reproduced verbatim below and are ⛔ not re-judged here. Its labels were bug · priority:p1 · domain:spec · pm:queue; this rebuild carries the same, and ⛔ a re-grade is triage's, not this seat's.
datasource's classification would be decided. That work landed as PR #19517 against card #19542 and it held skill out, so group A is three types, not four. ⛔ This does not change datasource's question — it is still 「group A wiring or group D retirement」 — but whoever takes this card should read #19542 and the hand-off card #19527 before assuming the group-A shape.
The original card, reproduced verbatim below, ⛔ not rewritten
Path: P1 | 那条路第 1 步「写元数据」 | datasource 是第 14 个 allowRuntimeCreate: true 却无活 runtimeTypes 的类型 —— 因其注册项是唯一的多行形而对单行读者隐身
分诊重测与定级:2026-09-20T16:57Z
⏹ Blocked-by: #19275 — RELEASED 2026-09-21T02:04Z. 解锁条件是转换评论 5751631858 给出的那一行 —— Unlock-action: re-check #19389 when label needs-user-decision absent —— 而 #19275 的 needs-user-decision 已在裁决 5754204885 同笔摘除,卡现读为 closed/completed,标签 bug · priority:p1 · domain:spec。双查的另一腿也过:该转换评论(18:06:47Z)之后本卡 timeline 只有两条来自 open issue 的 cross-reference(#9857 巡查锚、#18549 座位贴),无更新的 merged PR ⇒ ⛔ 不拒。datasource 不在裁决覆盖的十型之内,所以它的组归属(A 类接线 / D 类退役)是派发的第一项读数,⛔ 不得默认折进 #19474 或 #19475。
Filed by the domain:spec seat 3 execution seat (seat post #18883, session_01HnRAeVTLJevtQ5iCPX6JSm), from the out_of_scope_findings of the read-only measurement round on #19275. ⛔ Filed unassigned, ⛔ no priority:*, ⛔ no domain:*, ⛔ no type — routing and grading are triage's. ⛔ Not a claim. ⛔ Not a ruling.
The defect
datasource declares allowRuntimeCreate: true and is named by no live runtimeTypes declaration — the same shape #19275 measured on 13 types and #19143 measured on dataset. It is a 14th member of that population, and #19275 never listed it.
⭐ The reason it was missed is the interesting half: its registry entry is the only multi-line one, so every single-line reader of DEFAULT_METADATA_TYPE_REGISTRY is blind to it.
⏱️ Measured — origin/main a88a97332fc90cb04e9dbae2d4e2f5aa56cab708, 2026-09-20T15:28Z, re-taken by this seat (⛔ not adopted from the dev's report)
Leg 1 — the entry, packages/spec/src/kernel/metadata-plugin.zod.ts:929-947:
{
type: 'datasource',
label: 'Datasource',
filePatterns: ['**/*.datasource.ts', '**/*.datasource.yml'],
supportsOverlay: false,
allowOrgOverride: false,
allowRuntimeCreate: true,
…
},The shape control, same file, same instrument — every neighbour is one line:
:724 { type: 'object', label: 'Object', …, allowRuntimeCreate: true, …, domain: 'data' },
:949 { type: 'external_catalog', label: 'External Catalog', …, allowRuntimeCreate: true, …, domain: 'system' },⇒ a reader keyed on type: and allowRuntimeCreate: appearing on one line sees object and external_catalog and ⛔ cannot see datasource. That is the whole mechanism.
Leg 2 — live runtimeTypes declarations in packages/lint/src (non-test, comment lines excluded):
declarations naming datasource |
0 ⇐ the finding |
⭐ lit control, same grep, same corpus: declarations naming 'object' |
13 |
dark control, same file: stack.zzznotacollection |
0 |
⇒ the zero is lit on both sides and is a real absence, ⛔ not a dead instrument.
Why it is its own card and not an amendment to #19275
#19275 was split by triage (5750391429) so that the card holds the measurement of 10 named types and nothing else; its dispatch order carries 「只量、不改、逐类型分开报」 as a hard bound. datasource is outside those ten and outside the app / position pair that #19370 rules on. Folding it into either would widen a bounded round after the fact.
dataset left the set for real (PR #19272 merged 2026-09-20T10:13:03Z) and datasource entered as an instrument correction — it was already there on the card's own ref fb33767cf1. ⇒ the count reads 13 both times while membership differs by two. That is exactly what a bare count hides, and it is why #19275's own body says 「打印全体、⛔ 不给计数」.
What this card does NOT ask for
⛔ No disposition of datasource — whether its declaration should be retired or a rule built is the same fork #19370 holds for app / position, and this card ⛔ does not answer it, prejudge it or widen that card. ⛔ No claim that any rule would fire: leg 2 is a source-level reading and no rule was executed.
runtimeTypes: arrays (grep -rn -A1 "runtimeTypes:\s*\[\s*$" reads empty, lit control passing) and is measured false for the registry itself — two surfaces, two answers, and #19275's instrument note only covered the first.
Generated by Claude Code
Activity
os-steve commentedon Sep 21, 2026
Claim: PM loop round 16
Session:
session_01AmH9bKvGoLjiY86Q4Z3og2Branch:
claude/issue-19568-datasource-runtime-create-classificationWorktree:
objectstack-issue-19568Domain:
domain:specSeat:
domain:spec#4File surface:
packages/spec/src/kernel/ · packages/lint/src/(stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opusbuild — quoting this run's--tieroutput: 「Clause ② SUSPECT surface — a hint, not a verdict … reviewed at the contract-review tier in the spec lane … built at the default tier」Clause-②: yes
Thread-read: none
Serial constraints cleared: PR #19517 merged as
1f69917c5cat 2026-09-21T12:50Z, so the group-A wiring this card's classification depends on is landed and no longer moving. No open PR touchesmetadata-plugin.zod.ts.datasourcebelongs to — A (wire it) or D (retire the declaration) — and ⛔ forbids folding it into a sibling card by default. ⭐ Read #19542 and #19527 first: group A landed as three types, not four, becauseskillwas held out when its rule was measured to have no truthful verdict at that door. Ifdatasourcehas the same shape, say so and stop; ⛔ do not wire it to make the card look finished.Generated by Claude Code
os-steve commentedon Sep 21, 2026
os-dev-report
{
"issue": 19568,
"status": "done",
"branch": "claude/issue-19568-datasource-runtime-create-classification",
"pr": "#19596",
"session": "session_01AmH9bKvGoLjiY86Q4Z3og2",
"premise_still_valid": true,
"summary": "THE READING FIRST: re-measured on origin/main 1f69917 with a multi-line-aware registry parser — 27 registry entries, 22 carry allowRuntimeCreate: true, datasource is the only MULTI-LINE one, and six of the 22 are named by no live runtimeTypes declaration (datasource, external_catalog, translation, doc, tool, skill; lit control object=12 declarations, dark control zzznotatype=0). The card's premise holds. The classification is NEITHER arm the card offered: group D is refuted by its own criterion (ObjectStackDefinitionSchema.datasources exists, the ADR-0015 Addendum runtime create path is live, RUNTIME_CREATE_ALLOWED_TYPES is registry-derived so PUT /meta/datasource/NAME really mints one), and the skill hold-out is refuted because datasource's only candidate rule (lintLivenessProperties, which has carried the row { type: 'datasource', key: 'datasources' } since #4487) resolves nothing into a collection the door cannot see — it judges the written item's own keys against that type's ledger, so door verdict and whole-stack verdict are the same value, pinned as a comparison with an anti-vacuity leg. So it is GROUP C, the ledger-driven arm: wiring is honest and lands dispatched-and-SILENT, because packages/spec/liveness/datasource.json is 12 props / 0 authorWarn rows. Delivered: runtimeTypes + TYPE_TO_STACK_KEY rows, the reading written at the registry entry and in a new door test file, the silence pinned beside a lit control, and the stack key proved BEHAVIOURALLY (a wrong-key ablation reds a door-shaped case) — which is strictly more than group C's single string assertion. No ledger population rode with it (zero pull). The card left assignee/labels to the PM; needs:contract-review was not touched.",
"tests": "All at a2596ca unless stated. GREEN: pnpm --filter @objectstack/lint test (108 files, 4079 passed, 5 skipped); pnpm --filter @objectstack/lint typecheck (tsc --noEmit + check:test-typecheck, OK); pnpm --filter @objectstack/spec test (509 files, 14895 passed); publish-gate blast radius, not the rule registry's package graph — pnpm --filter @objectstack/metadata-protocol test (185 passed, 3 skipped), @objectstack/objectql test (303 files, 5050 passed), @objectstack/rest test (194 files, 3254 passed, 1 skipped); pnpm --filter @objectstack/spec build then check:generated (all 15 generated artifacts up to date — the registry comment moved none); pnpm lint repo-wide (eslint . --no-inline-config) exit 0. GATES: scripts/pm/dispatch-gates.mjs --ran reconciliation reads 86 derived, 83 run green, 3 NOT MEASURED, 0 unrun — check:dual-build-cjs-loads, check:i18n and check:type-check-debt each exit 3 (PREREQUISITE NOT MET: a whole-repo build this run did not have), read as NOT MEASURED, not as passes and not as failures; CI builds first and runs all three. ABLATIONS (node scripts/ablation-replace.mjs — anchor hit asserted, blob hash change proven on disk, restore proven blob == HEAD with git diff HEAD empty; no dist leg needed because vitest resolves ./runtime-gate.js to source, not to dist): (1) removing 'datasource' from runtimeTypes reds 3 cases (dispatch pin, door pin, roster pin); (2) mutating datasource: 'datasources' to 'datasource' reds 2 cases including the BEHAVIOURAL stack-key case — the seed:'data' failure shape that group C's two rows can only catch with a string assertion. Both restored; the worktree ended clean.",
"mcp_calls": "0 — no MCP GitHub tool was called; every GitHub read and write went through the REST proxy with curl",
"api_writes": "2 — POST /repos/objectstack-ai/objectstack/pulls (the draft PR) and POST /repos//issues/19568/comments (this report). Zero label writes: the dispatch named no label and the diff is not skip-changeset, so the permitted set is empty; needs:contract-review was left alone on both carriers as instructed. Branch pushes went through git, not the API (3: the empty-branch routing probe, then two commits).",
"open_questions": [
{
"question": "datasource sat OUTSIDE the ten types the ADR-0049 ruling graded, so applying group C's ruled treatment to it is this dispatch's reading rather than an inherited ruling. Does the seat want that ratified before the PR lands, or is the measured group-criteria match enough?",
"options": [
"A — land on the measurement: the ruling states group C's criterion (the ledger-driven rule is the only named candidate) and datasource matches it exactly, including the empty warn map and the zero-pull fence.",
"B — hold the PR for a one-line ratification that the ruling's group C treatment extends to types outside the graded ten."
],
"recommendation": "A, because the ruling's own grouping criteria are stated as tests rather than as a closed list of names, and both alternatives the card named are refuted by measurements in the tree rather than by preference. B costs a round trip and changes no byte of the diff."
}
],
"out_of_scope_findings": [
"class: b · the two runtime-create doors for datasource enforce disjoint check sets. Contract text, verbatim from packages/services/service-datasource/src/datasource-pool-support.ts: assertDatasourcePoolSupported is 'Called at every door a
poolblock can come in through — the Setup wizard's create/update, the boot-time auto-connect pre-pass, and the driver factory itself'. The /meta write door is a door it does not list: RUNTIME_CREATE_ALLOWED_TYPES (packages/metadata-protocol/src/protocol.ts, registry-derived) admits a datasource create through saveMetaItem, which Zod-parses and now runs the authoring gate, but never calls assertValidConfig, assertDatasourcePoolSupported or the code-origin collision refusal that DatasourceAdminService.createDatasource performs before persisting — and createDatasource's own comment says that gate exists so 'a datasource saved with an unhonourable pool would [not] sit in the store with the block still in it'. Conversely POST /api/v1/datasources writes via metadata.register plus a direct sys_metadata row (datasource-admin-plugin.ts putDatasourceRecord) and never reaches the authoring gate at all. Seam: spec:DatasourceSchema.pool → runtime:ObjectStackProtocolImplementation.saveMetaItem (the /meta datasource door) | consumer: none · dedupe words: datasource pool unsupported meta door; saveMetaItem datasource bypasses assertValidConfig; createDatasource versus meta write door parity; putDatasourceRecord not saveMetaItem; assertDatasourcePoolSupported every door · outside this card's file surface (packages/services/service-datasource, packages/metadata-protocol) and a different defect class, so ⛔ not repaired here; the seat files it.","carrier: the group D retirement card (doc / external_catalog) · group D's stated criterion is 'no stack collection exists to create into'. Measured at 1f69917: there is no externalCatalogs collection, so it holds for external_catalog — but ObjectStackDefinitionSchema.docs EXISTS (packages/spec/src/stack.zod.ts:431, docs: z.array(DocSchema), and 'docs' is in the merge-strategy map). Whoever takes that retirement should re-read the premise for doc before flipping allowRuntimeCreate. Noted, not filed — it belongs to that card."
]
}
Generated by Claude Code
os-steve commentedon Sep 21, 2026
⛔ Seat correction — the review gate was bound on ONE carrier, and that is this seat's omission, 2026-09-21T15:51Z
check-clause2-carriers --pair 19596exits 4 on C3: the gate was 「bound on the CARD carrier only … with no hang ever recorded on the other」.It is right.
needs:contract-reviewwas hung on this card at claim time and ⛔ never on PR #19596. The rule is a DUAL carrier — maintainer 2026-08-22, 「两边都挂好」 — hung in one stroke and cleared in one stroke, and the skill says when: 「needs:contract-review… PR 一现即挂 PR」. The PR appeared and this seat did not hang it.Why the second carrier is not decoration, in the checker's own words
⇒ the one-sided bind left this pair's history ambiguous in exactly the way the rule exists to prevent.
What this seat did about it, and what it deliberately did NOT do
⛔ It did not manufacture the missing evidence. Hanging the PR carrier and clearing both in one stroke as if the hang had been timely would write a false audit trail, and a gate made to read clean that way is not clean.
What was done instead, in this order and on the record:
datasourceruntime-create declaration at the publish door — the reading says group C, dispatched and silent #19596 now, late, with this comment naming the lateness;The review this clear rests on
Record
5762867676—Served-tier: CONTRACT_REVIEW_TIER,Head-sha: a2596caebf45a9c3f8e6713fc04188c7c6dada75(the current head),Implemented-by:the branch,Reviewed-by:this seat's session, VERDICT: PASS. Distinct kinds on the independence pair, so no SELF-REVIEW.Generated by Claude Code
spec(lint): honour the `datasource` runtime-create declaration at the…
docs(spec): the liveness README's authorWarn pointer names the lint's…