Commit 5ac2ba1
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-time `FlowSchema` refusal,
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 on
`STORED_METADATA_BODY_PRESCRIPTION`, imported from
`@objectstack/spec/kernel` beside `isStoredMetadataBodyObject`, which
the file already imported from that subpath. Its docblock gains one
paragraph that names the shared sentence.
- The only wording change an author sees is the elevation clause. It was
"Elevation (`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 small `closingPrescriptionOf`
helper. Every refusal, no-write, code and identity assertion is
unchanged, and none is deleted.
- A `patch` changeset for `@objectstack/service-automation`.
What is unchanged: the set of refused writes, the `PERMISSION_DENIED`
code, the guard classification (a `fault` edge does not route it), and
every non-family write.
## The dispatch's assumptions, as measured
1. **The constant's wording is true for every run-time caller.** The
refusal runs before any identity is resolved, and the flow engine
elevates in one way only: `runAs: 'system'` (`resolveRunDataContext`
gives `isSystem: 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.
2. **The `@objectstack/spec/kernel` subpath already reaches this
package's build and tests.** `@objectstack/spec` is a declared
dependency, and `crud-nodes.ts` already imported from
`@objectstack/spec/kernel`. The vitest config has no source alias for
`@objectstack/spec`, so tests reach it through `exports` (the spec
package's built `kernel` entry). The source module and the pin read the
same object. That pair is already recorded in
`check-test-source-alias.mjs`'s `KNOWN_UNALIASED_TEST_IMPORTS` for this
package, and `check:test-source-alias` exits 0. The built
`dist/index.js` carries 2 hits for the constant and 0 for the old
clause, and `check:dual-build-cjs-loads` exits 0.
3. **The pins import the constant.** No assertion was deleted (see
above).
4. **Ablation:** see below. Predicted and observed agree.
5. **The runtime body boundary's private `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 `1b9252e0f2` unless stated)
Every heavy run went through `scripts/pm/os-verify-lock.sh`, and each
verdict below is read from its `VERDICT command-exit` line.
- `pnpm --filter @objectstack/service-automation test` (`vitest run`):
**Test Files 170 passed (170), Tests 2098 passed (2098)**, exit 0. The
same reading was taken at `d589cd4418`, before `origin/main`
(`8843505d91`, objectql only) was merged in and the closure rebuilt.
- The pin file alone, at `d589cd4418`: 17 passed (17).
- `pnpm --filter @objectstack/service-automation typecheck`: exit 0, and
`check:test-typecheck` OK (0 files in the ledger). `tsc --noEmit
--listFiles -p tsconfig.json` lists the pin file (1 hit) and
`crud-nodes.ts` (1 hit).
- **Ablation, with the direction predicted before the run.** The
mutation appends one word to the run-time closing sentence, inside the
sentence. The anchor was `+ STORED_METADATA_BODY_PRESCRIPTION,` in
`crud-nodes.ts`, and the replacement calls `.replace('change this.',
'change this ABLATIONMARKER.')` on it, applied with
`scripts/ablation-replace.mjs`.
- Prediction: 1 file red. 9 tests red, every case that goes through
`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.
- On disk: the anchor went 1 to 0 and the replacement 0 to 1, and the
blob changed from `86ed89180f` to `76faa10823`. The subject is reached
through relative `src` imports (`../plugin.js`, then
`./builtin/index.js`), so no `dist` rebuild or preflight applies.
- Observed, full package suite: **Test Files 1 failed and 169 passed
(170); Tests 9 failed and 2089 passed (2098)**. All 9 `AssertionError`s
are the run-time prescription assertion, and 0 are the save-time one.
- Restore, as reported by the tool: the blob after restore is
`86ed89180f` and equals HEAD's, and `git diff HEAD` is empty. An own
`trap` (`git checkout HEAD --` on the absolute path, then a hash
comparison) re-confirmed it with 0 diff lines, and porcelain was empty.
- Lint, as a proven narrowing (`pnpm lint` itself belongs to CI):
- Population, read from eslint's own config: 2 of the 3 changed paths
are linted. For the changeset, eslint answers "File ignored because no
matching configuration was supplied".
- Count, from `--format json --no-inline-config`: 3 results, with 0
errors and 0 warnings on the 2 TS files.
- Invariance: `eslint.config.mjs` sets no `parserOptions.project` and no
`projectService`, so the linting is not type-aware and this diff cannot
move an untouched file's verdict.
- Gates: `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.
- All 64 were run. 63 exited 0 on the first pass.
- `check:dual-build-cjs-loads` exited 3 (PREREQUISITE NOT MET: unbuilt
packages, nothing measured). After a full `turbo run build` (72/72) it
exited 0, measuring 106 entries across 66 packages.
- `--ran` reconciliation: **64 derived, 64 run, 0 NOT-MEASURED, 0
UNRUN**, exit 0. `check:nul-bytes` exited 0, and a control-byte scan of
the 3 changed files found none.
- Docs and skills: `content/docs/**` (outside `releases/`) and
`skills/**` hold 0 copies of the old run-time sentence. The positive
control was the same patterns on `crud-nodes.ts` before the edit (3
hits), and the pathspec control was `create_record` in
`content/docs/automation/flows.mdx` (7 hits). Nothing to edit.
## Acceptance notes
- **The runtime body boundary's `PRESCRIPTION` is a second copy of the
sentence**, in `packages/runtime/src/stored-metadata-body-boundary.ts`.
Today it is byte-identical to `STORED_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 imports `isStoredMetadataBodyObject` from
`@objectstack/spec/kernel`, so the change is one import. It is another
lane's file (`domain:cli`) and is not edited here. Carrier: none.
- The spec's ADR-0087 semantic migration prescriptions for the hook and
flow refusals restate the same elevation clause as frozen text. They are
in the spec lane and match the shared wording.
- The unreleased part 1 changeset describes the refusal in prose and
does not quote the elevation clause, so it is not made false. It is not
edited, since it is not this PR's changeset.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ)_
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7fd2c34 commit 5ac2ba1
3 files changed
Lines changed: 38 additions & 6 deletions
File tree
- .changeset
- packages/services/service-automation/src/builtin
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
Lines changed: 8 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
21 | | - | |
| 21 | + | |
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| |||
355 | 355 | | |
356 | 356 | | |
357 | 357 | | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
358 | 363 | | |
359 | 364 | | |
360 | 365 | | |
| |||
364 | 369 | | |
365 | 370 | | |
366 | 371 | | |
367 | | - | |
368 | | - | |
369 | | - | |
| 372 | + | |
| 373 | + | |
370 | 374 | | |
371 | 375 | | |
372 | 376 | | |
| |||
Lines changed: 17 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
54 | 54 | | |
55 | 55 | | |
56 | 56 | | |
| 57 | + | |
57 | 58 | | |
58 | 59 | | |
59 | 60 | | |
| |||
78 | 79 | | |
79 | 80 | | |
80 | 81 | | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
81 | 92 | | |
82 | 93 | | |
83 | 94 | | |
| |||
264 | 275 | | |
265 | 276 | | |
266 | 277 | | |
267 | | - | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
268 | 282 | | |
269 | 283 | | |
270 | 284 | | |
| |||
342 | 356 | | |
343 | 357 | | |
344 | 358 | | |
345 | | - | |
| 359 | + | |
| 360 | + | |
346 | 361 | | |
347 | 362 | | |
348 | 363 | | |
| |||
0 commit comments