fix(service-automation): flow write-node family refusal ends on the shared prescription sentence (#21624) - #21707
Conversation
…hared prescription storedMetadataWriteRefusal keeps its node-specific lead and ends on STORED_METADATA_BODY_PRESCRIPTION from @objectstack/spec/kernel, the one sentence FlowSchema's save-time refusal of the same node ends on. The message-text pins read the imported constant instead of restating it. Claude-Session: https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 15 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 6 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 6 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 4c3440907644b0bfe553c2c80c184ec7ca2e850c && git checkout 4c3440907644b0bfe553c2c80c184ec7ca2e850c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 16d241a6af00be4acce9883190fc333a5f560825 1b9252e0f2525ea3c6b2e07e0f45233e370b54bf && git checkout -B drift-repro 16d241a6af00be4acce9883190fc333a5f560825 && git merge --no-ff 1b9252e0f2525ea3c6b2e07e0f45233e370b54bf
node scripts/docs-audit/affected-docs.mjs --json 16d241a6af00be4acce9883190fc333a5f560825
|
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 37190736783 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Fixes #21624
Clause-②: no
Part 3 of 3 on this card: the follow-up the seat's ACCEPT named and the spec lane's pointer handed back. Part 1, the run-time refusal, landed as PR #21649 (
f40bb3217f). Part 2, the save-timeFlowSchemarefusal, landed as PR #21687 (a2aadab1c6). With this PR, the run-time and save-time refusals end on one sentence, and the card is complete.What changes
packages/services/service-automation/src/builtin/crud-nodes.ts,storedMetadataWriteRefusal: the message keeps its node-specific lead (the node type, what it would have done and the target table, and "so the write was not run"). It now ends onSTORED_METADATA_BODY_PRESCRIPTION, imported from@objectstack/spec/kernelbesideisStoredMetadataBodyObject, which the file already imported from that subpath. Its docblock gains one paragraph that names the shared sentence.runAs: 'system') does not change this." and is now "Elevation (runAs, a system context) does not change this." The rest of the closing sentence was already byte-identical to the constant.write-nodes-stored-metadata-family-refusal.integration.test.ts: the two message-text assertions (the save-time issue message and the run-time run error) used to check for a restated fragment,toContain('the metadata protocol'). Each now asserts that the message ENDS on the imported constant, through a smallclosingPrescriptionOfhelper. Every refusal, no-write, code and identity assertion is unchanged, and none is deleted.patchchangeset for@objectstack/service-automation.What is unchanged: the set of refused writes, the
PERMISSION_DENIEDcode, the guard classification (afaultedge does not route it), and every non-family write.The dispatch's assumptions, as measured
runAs: 'system'(resolveRunDataContextgivesisSystem: true). "Elevation (runAs, a system context) does not change this" therefore holds for each run-time path. No sentence becomes false, and the constant was not touched.@objectstack/spec/kernelsubpath already reaches this package's build and tests.@objectstack/specis a declared dependency, andcrud-nodes.tsalready imported from@objectstack/spec/kernel. The vitest config has no source alias for@objectstack/spec, so tests reach it throughexports(the spec package's builtkernelentry). The source module and the pin read the same object. That pair is already recorded incheck-test-source-alias.mjs'sKNOWN_UNALIASED_TEST_IMPORTSfor this package, andcheck:test-source-aliasexits 0. The builtdist/index.jscarries 2 hits for the constant and 0 for the old clause, andcheck:dual-build-cjs-loadsexits 0.PRESCRIPTION(packages/runtime/src/stored-metadata-body-boundary.ts,domain:cli): it is byte-identical to the shared constant (211 bytes each, compared programmatically). It is a copy, not an import. Not edited; see the acceptance notes.Verification (all at
1b9252e0f2unless stated)Every heavy run went through
scripts/pm/os-verify-lock.sh, and each verdict below is read from itsVERDICT command-exitline.pnpm --filter @objectstack/service-automation test(vitest run): Test Files 170 passed (170), Tests 2098 passed (2098), exit 0. The same reading was taken atd589cd4418, beforeorigin/main(8843505d91, objectql only) was merged in and the closure rebuilt.d589cd4418: 17 passed (17).pnpm --filter @objectstack/service-automation typecheck: exit 0, andcheck:test-typecheckOK (0 files in the ledger).tsc --noEmit --listFiles -p tsconfig.jsonlists the pin file (1 hit) andcrud-nodes.ts(1 hit).+ STORED_METADATA_BODY_PRESCRIPTION,incrud-nodes.ts, and the replacement calls.replace('change this.', 'change this ABLATIONMARKER.')on it, applied withscripts/ablation-replace.mjs.expectRefused(6 node x identity cases without the security plugin, 3 node cases with it), each failing first on the run-time "ends on the family's prescription" assertion. 8 green. The save-time assertion never red. Package total: 9 failed / 2089 passed.86ed89180fto76faa10823. The subject is reached through relativesrcimports (../plugin.js, then./builtin/index.js), so nodistrebuild or preflight applies.AssertionErrors are the run-time prescription assertion, and 0 are the save-time one.86ed89180fand equals HEAD's, andgit diff HEADis empty. An owntrap(git checkout HEAD --on the absolute path, then a hash comparison) re-confirmed it with 0 diff lines, and porcelain was empty.pnpm lintitself belongs to CI):--format json --no-inline-config: 3 results, with 0 errors and 0 warnings on the 2 TS files.eslint.config.mjssets noparserOptions.projectand noprojectService, so the linting is not type-aware and this diff cannot move an untouched file's verdict.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands(no paths) derives 64 commands (30 pnpm, 34 node), the same list before and after the merge.check:dual-build-cjs-loadsexited 3 (PREREQUISITE NOT MET: unbuilt packages, nothing measured). After a fullturbo run build(72/72) it exited 0, measuring 106 entries across 66 packages.--ranreconciliation: 64 derived, 64 run, 0 NOT-MEASURED, 0 UNRUN, exit 0.check:nul-bytesexited 0, and a control-byte scan of the 3 changed files found none.content/docs/**(outsidereleases/) andskills/**hold 0 copies of the old run-time sentence. The positive control was the same patterns oncrud-nodes.tsbefore the edit (3 hits), and the pathspec control wascreate_recordincontent/docs/automation/flows.mdx(7 hits). Nothing to edit.Acceptance notes
PRESCRIPTIONis a second copy of the sentence, inpackages/runtime/src/stored-metadata-body-boundary.ts. Today it is byte-identical toSTORED_METADATA_BODY_PRESCRIPTION, so no author reads two wordings. But the constant's own docblock says to import it rather than restate it, and a later rewording would leave this copy behind. That file already importsisStoredMetadataBodyObjectfrom@objectstack/spec/kernel, so the change is one import. It is another lane's file (domain:cli) and is not edited here. Carrier: none.Generated by Claude Code