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
packages/runtime's package-publish door has its own copy of the P9 seed-apply disclosure, outside metadata-protocol #8443
Filed by the dev on #8333 while landing the P6–P13 producer sweep (PR #8436). Unassigned; nobody is on it. Outside that card's scope, which enumerates eight producers in packages/metadata-protocol/src/protocol.ts.
The site
packages/runtime/src/domains/packages.ts, ~line 381 — the runtime package door's fallback when the protocol did not already produce a seedApplied:
Structurally identical to #8333's P9 (applySeedBodies' error), which PR #8436 has now converted inside metadata-protocol. Same field name, same sink, same argument: seedApplied rides on a 200 publish response as data, so no HTTP boundary's 5xx message withhold can reach it.
This try wraps applyPublishedSeeds, which goes at the engine — so a sys_metadata/table outage lands in that catch undeclared, exactly the population #8136 measured.
Status of the evidence
Read from source, NOT reproduced end to end. It was found by sweeping the repo for the strings #8333's sites used, after the in-package sites were fixed. Whoever takes it should drive a real driver failure through the runtime package-publish door and record the actual response body first — the same discipline #8086, #8136 and #8333 were each held to, and the step that changed the outcome on #8333 more than once.
The fix, if the premise holds
The same rule, already implemented next door: quote the caught sentence only when the error declared a 4xx status (ADR-0112), otherwise a stable sentence with the original sent to the log. ⚠️metadata-protocol does not export its declaresClientRefusal / clientFacingFailureText helpers, so a taker must decide between exporting them and restating the rule locally — worth settling deliberately rather than by copy-paste, since a second private copy is how the rule drifts.
Concentrated findings round (maintainer-directed, 2026-08-13): promoted finding → pm:queue, and re-routed domain:metadata → domain:cli per the anchoring rule — the fix lands in packages/runtime/src/domains/packages.ts (runtime is the cli lane's family), not in metadata-protocol, even though the pattern was found by #8333's sweep there. Type: Bug (the ADR-0112 rule is the declared contract: caught driver text must not ride a client-facing payload without a declared 4xx status; this site violates it), security kept.
Dispatch conditions, binding on the brief:
Reproduce first — the card is honest that this is read-from-source: drive a real driver failure through the runtime package-publish door and record the actual response body before changing anything. premise_still_valid: false + no PR is a legitimate deliverable if the fallback turns out unreachable.
The helpers question must be settled deliberately, producer-side preferred: metadata-protocol does not export declaresClientRefusal / clientFacingFailureText. Exporting them (a producer-side enabling change, reported as such) beats a second private restatement — a second copy is how the rule drifts. If exporting, that half touches packages/metadata-protocol (metadata lane's package): declare it in the file surface, it is enabling-export only, no behavior change there.
Size/model suggestion: S–M, mode:subagent, opus (the reproduce-first half needs judgment).
{
"issue": 8443,
"status": "done",
"branch": "claude/issue-8443-runtime-seed-apply-disclosure",
"pr": "https://github.com/objectstack-ai/objectstack/pull/8518",
"premise_still_valid": true,
"summary": "Reproduced BEFORE changing anything, through HttpDispatcher.handlePackages with a real SeedLoaderService. Recorded pre-change response, HTTP 200: {\"success\":true,\"data\":{\"success\":true,\"publishedCount\":1,\"failedCount\":0,\"published\":[{\"type\":\"seed\",\"name\":\"project_seed\",\"version\":\"h\"}],\"failed\":[],\"seedApplied\":{\"success\":false,\"error\":\"SQLITE_ERROR: no such table: sys_metadata\"}}}. Positive control in the same run: healthy engine gives seedApplied {success:true,inserted:2} with one real bulk insert('project',[Apollo,Gemini]) at the engine, so the injection perturbs a live path. The premise holds AND the reproduction found a SECOND carrier the card did not name: the per-read errors[] entries inside applyPublishedSeeds carried the same driver text, and that is the carrier a sys_metadata outage reaches FIRST (the seed body read-back runs before the loader is constructed) - fixing only the door's catch would have left the commonest outage shape disclosing while the PR claimed the door was fixed. Both converted. Helpers: exported producer-side from metadata-protocol (clientFacingFailureText + seedRequestValidationError), enabling-only, no behaviour change there. The seed request parse became safeParse minting a declared INVALID_METADATA/422, so the authoring population (a malformed seed body, previously a raw multi-line ZodError dump) is still quoted back curated instead of being blanked by the withhold. Work landed in exactly the two packages the dispatch named. main moved 14 commits mid-task; merged, one conflict in metadata-protocol/src/index.ts (two export blocks added at the same line, both kept), then rebuilt and re-ran everything.",
"tests": "Post-merge, on a full built closure (turbo build 70/70). pnpm --filter @objectstack/runtime test -> 'Test Files 152 passed (152) / Tests 2325 passed (2325)'. pnpm --filter @objectstack/metadata-protocol test -> 'Test Files 82 passed (82) / Tests 1223 passed (1223)'. pnpm --filter @objectstack/runtime typecheck -> clean. New pin packages/runtime/src/domains/packages-seed-apply-disclosure.test.ts, 6 cases, every one asserting the CONTENTS of seedApplied inside the 200 body (no bare toThrow, no status-only assertion). REVERSE VERIFICATION, direction predicted before running: with domains/packages.ts reverted to origin/main (export kept) predicted 4 failed | 2 passed - measured 4 failed | 2 passed, each red failing on the text: \"expected 'SQLITE_ERROR: no such table: sys_meta...' to be 'seed apply failed'\", \"expected 'read project_seed: SQLITE_ERROR: no s...' to be 'read project_seed: the reason is in t...'\", \"expected '[\\n {\\n \\\"code\\\": \\\"invalid_value\\\",...' to contain '[invalid_metadata]'\". Second variant (clientFacingFailureText forced to withhold unconditionally): predicted 3 failed | 3 passed - measured 3 failed | 3 passed, the three being the two authoring quotes and the declared-4xx guard, which is what makes that guard load-bearing rather than decorative. No missed prediction. Gates re-derived from the actual changed paths via scripts/pm/dispatch-gates.mjs and run green: check:nul-bytes, check:changeset-gate-self-tests, check:cross-package-test-inputs, check:durability-log-level, check:engine-double-contract, check:filter-alias-parity, check:objectui-changeset, check:route-envelope, check:query-options-erasure, check:error-code-casing, check:test-source-alias, check:type-check-coverage, and spec check:generated ('All 13 generated artifacts are up to date'). check:type-check-debt on the built closure: '33 ledger entries re-measured, 1969 raw tsc errors, none above its recorded number; surplus: none' - the ledger was NOT raised.",
"open_questions": [],
"out_of_scope_findings": [
"filed as #8516: the SAME publish-drafts handler puts caught text on the same 200 body through two more fields - unhideError (the ADR-0045 visibility flip) and rebindError (the metadata:reloaded announce). Read from source, not reproduced, so it gets its own card with the same reproduce-first condition rather than riding along here. NOTE for triage: labelled `finding` because the dispatch brief said to, but by the standing classification this is a concrete defect (same rule, same response, named producers) - it likely wants the same promotion this card got."
]
}
Activity
hotlong commentedon Aug 13, 2026
Concentrated findings round (maintainer-directed, 2026-08-13): promoted
finding→pm:queue, and re-routeddomain:metadata→domain:cliper the anchoring rule — the fix lands inpackages/runtime/src/domains/packages.ts(runtime is the cli lane's family), not inmetadata-protocol, even though the pattern was found by #8333's sweep there. Type: Bug (the ADR-0112 rule is the declared contract: caught driver text must not ride a client-facing payload without a declared 4xx status; this site violates it),securitykept.Dispatch conditions, binding on the brief:
premise_still_valid: false+ no PR is a legitimate deliverable if the fallback turns out unreachable.metadata-protocoldoes not exportdeclaresClientRefusal/clientFacingFailureText. Exporting them (a producer-side enabling change, reported as such) beats a second private restatement — a second copy is how the rule drifts. If exporting, that half touchespackages/metadata-protocol(metadata lane's package): declare it in the file surface, it is enabling-export only, no behavior change there.Size/model suggestion: S–M,
mode:subagent, opus (the reproduce-first half needs judgment).Generated by Claude Code
os-zhuang commentedon Aug 13, 2026
Claimed and dispatched by the
domain:cliseat (#6024).session_01P7vaLs7bhBPi9m3JyzkhDjclaude/issue-8443-runtime-seed-apply-disclosuremode:subagent(S–M per triage; the reproduce-first half needs judgment)串行状态(派工时测量)
packages/runtime/src/domains/packages.ts无人占用 —— 18 个开放 PR 中没有一个动packages/runtime。若按分诊建议走 producer-side 导出,packages/metadata-protocol同样无人占用(#8333 的 PR #8436 已落地)。两边都干净。两个派工条件已写进 brief,原样保留
premise_still_valid: false+ 不出 PR 是合法交付 —— 如果那个 fallback 其实不可达,如实报回来比硬修一个不存在的洞有价值得多。metadata-protocol没有导出declaresClientRefusal/clientFacingFailureText。导出它们(纯 enabling export,那一侧无行为变更,但要在 file surface 里声明)优于在 runtime 里再抄一份规则。第二份私有副本正是规则漂移的起点。seedApplied骑在 200 响应上作为数据返回,所以任何 HTTP 边界的 5xx 文案扣留都够不到它 —— 这正是本卡的要害。写测试时不要只断言「抛错了」或只看 HTTP 状态码,要断言200 响应体内那个字段的实际内容。Generated by Claude Code
os-zhuang commentedon Aug 13, 2026
{ "issue": 8443, "status": "done", "branch": "claude/issue-8443-runtime-seed-apply-disclosure", "pr": "https://github.com/objectstack-ai/objectstack/pull/8518", "premise_still_valid": true, "summary": "Reproduced BEFORE changing anything, through HttpDispatcher.handlePackages with a real SeedLoaderService. Recorded pre-change response, HTTP 200: {\"success\":true,\"data\":{\"success\":true,\"publishedCount\":1,\"failedCount\":0,\"published\":[{\"type\":\"seed\",\"name\":\"project_seed\",\"version\":\"h\"}],\"failed\":[],\"seedApplied\":{\"success\":false,\"error\":\"SQLITE_ERROR: no such table: sys_metadata\"}}}. Positive control in the same run: healthy engine gives seedApplied {success:true,inserted:2} with one real bulk insert('project',[Apollo,Gemini]) at the engine, so the injection perturbs a live path. The premise holds AND the reproduction found a SECOND carrier the card did not name: the per-read errors[] entries inside applyPublishedSeeds carried the same driver text, and that is the carrier a sys_metadata outage reaches FIRST (the seed body read-back runs before the loader is constructed) - fixing only the door's catch would have left the commonest outage shape disclosing while the PR claimed the door was fixed. Both converted. Helpers: exported producer-side from metadata-protocol (clientFacingFailureText + seedRequestValidationError), enabling-only, no behaviour change there. The seed request parse became safeParse minting a declared INVALID_METADATA/422, so the authoring population (a malformed seed body, previously a raw multi-line ZodError dump) is still quoted back curated instead of being blanked by the withhold. Work landed in exactly the two packages the dispatch named. main moved 14 commits mid-task; merged, one conflict in metadata-protocol/src/index.ts (two export blocks added at the same line, both kept), then rebuilt and re-ran everything.", "tests": "Post-merge, on a full built closure (turbo build 70/70). pnpm --filter @objectstack/runtime test -> 'Test Files 152 passed (152) / Tests 2325 passed (2325)'. pnpm --filter @objectstack/metadata-protocol test -> 'Test Files 82 passed (82) / Tests 1223 passed (1223)'. pnpm --filter @objectstack/runtime typecheck -> clean. New pin packages/runtime/src/domains/packages-seed-apply-disclosure.test.ts, 6 cases, every one asserting the CONTENTS of seedApplied inside the 200 body (no bare toThrow, no status-only assertion). REVERSE VERIFICATION, direction predicted before running: with domains/packages.ts reverted to origin/main (export kept) predicted 4 failed | 2 passed - measured 4 failed | 2 passed, each red failing on the text: \"expected 'SQLITE_ERROR: no such table: sys_meta...' to be 'seed apply failed'\", \"expected 'read project_seed: SQLITE_ERROR: no s...' to be 'read project_seed: the reason is in t...'\", \"expected '[\\n {\\n \\\"code\\\": \\\"invalid_value\\\",...' to contain '[invalid_metadata]'\". Second variant (clientFacingFailureText forced to withhold unconditionally): predicted 3 failed | 3 passed - measured 3 failed | 3 passed, the three being the two authoring quotes and the declared-4xx guard, which is what makes that guard load-bearing rather than decorative. No missed prediction. Gates re-derived from the actual changed paths via scripts/pm/dispatch-gates.mjs and run green: check:nul-bytes, check:changeset-gate-self-tests, check:cross-package-test-inputs, check:durability-log-level, check:engine-double-contract, check:filter-alias-parity, check:objectui-changeset, check:route-envelope, check:query-options-erasure, check:error-code-casing, check:test-source-alias, check:type-check-coverage, and spec check:generated ('All 13 generated artifacts are up to date'). check:type-check-debt on the built closure: '33 ledger entries re-measured, 1969 raw tsc errors, none above its recorded number; surplus: none' - the ledger was NOT raised.", "open_questions": [], "out_of_scope_findings": [ "filed as #8516: the SAME publish-drafts handler puts caught text on the same 200 body through two more fields - unhideError (the ADR-0045 visibility flip) and rebindError (the metadata:reloaded announce). Read from source, not reproduced, so it gets its own card with the same reproduce-first condition rather than riding along here. NOTE for triage: labelled `finding` because the dispatch brief said to, but by the standing classification this is a concrete defect (same rule, same response, named producers) - it likely wants the same promotion this card got." ] }Generated by Claude Code
os-zhuang commentedon Aug 13, 2026
已落地并结单 —— PR #8518 merged。
domain:cli座位 (#6024) 评分。落地探针取 PR 新增的
packages/runtime/src/domains/packages-seed-apply-disclosure.test.ts,在refs/heads/main上存在;同一次目录列举里的既有兄弟文件作正对照。评级:ACCEPT(本批最高)
这张卡的最大价值,是 dev 找到了卡片没点名的第二个载体。
派工的硬条件是「先复现」,dev 照做并记录了改前的 200 响应体:
同一 harness、健康引擎的正对照:
seedApplied: { success: true, inserted: 2 },伴随一次真实 bulk insert。于是「没有 driver 文本」永远不可能和「这条路径根本没跑」混淆 —— 这正是负结果必须带正对照的理由,而不是形式主义。然后复现改变了答案。 四类人群到达那个字段,卡片只点名了其中一类:
metadata.getObject抛错 —— loader 的依赖图读取protocol.getMetaItem抛错 —— seed body 读回sys_metadata故障最先打到的那个 —— 读回发生在 loader 被构造之前。只修门上那个catch(即卡片字面要求的范围)会让最常见的故障形态原样继续泄漏,而 PR 会声称这扇门已经修好。两个都转换了。这类发现不可能来自读代码,只能来自真的把故障打进去。派工条件写对了,dev 也真的执行了。
其余做对的地方
clientFacingFailureText和seedRequestValidationError导出,declaresClientRefusal不导出 —— 理由是「没有消费者的导出是已声明未拉动的表面」。这条克制值得单独表扬:分诊说的是「优先 producer-side」,不是「把那一族全导出去」。seedRequestValidationError对同一拒绝铸出的句子,于是未来任何一侧的本地重述都会在第一次编辑时变红。ZodError什么都没声明,直接套规则会把真实的创作错误替换成seed apply failed。改为safeParse并铸成已声明的INVALID_METADATA/ 422,创作者严格变得更好:一句点名 seed 和 key 的摘要,而不是 zod 内部结构的转储。clientFacingFailureText改成无条件扣留(一刀切空白),预测 3 红实测 3 红。没有这三条,整个文件会被「全部扣留」满足 —— 而那会删掉 3b — wire the flow executors toparse()their config, and tighten the undeclared-key warning into an error #4277 存在的理由,即自纠正的拒绝。这是「不可能失败的检查」的正确解药。check:type-check-debt在完整构建闭包上跑,账本未抬。main移动了 14 个提交(含两个动packages/metadata-protocol的),数字按合并后重跑而不是沿用合并前那一趟。衍生卡
#8516 —— 同一 handler 的
unhideError/rebindError两个字段,由另外两个 producer 把捕获文本放上同一个 200 响应体。读源码发现、未复现,所以单独开卡并沿用同样的「先复现」条件,没有夹带进本 PR 未经测量地一并改。这个处理是对的。Generated by Claude Code