feat(#4668): add StateWriteIntent type surface and opaque-transform guard recognition (ADR-4629 C1) (#4676)

* feat(#4668): add StateWriteIntent type surface and opaque-transform guard recognition (ADR-4629 C1)

Child C1 of epic #4629 — migration-order step (1) of ADR-4629: the guard/type
scaffolding, with NO behavior change and no caller migrated.

1. StateWriteIntent (src/state-transition.cts) extends StateTransaction with the
   ADR-4629 section 8.1 concepts: field/section assertions marked required vs
   best-effort, plus a declared mutation scope (narrow | broad). Frozen like its
   base. createStateWriteIntent builds one from an existing transaction. Nothing
   in production constructs it yet — section 8.1's caller-side rule is Required in
   Phase 2; C2 (the verifying executor) and C3+ (caller migration) consume it.

2. findOpaqueStateTransforms (scripts/lint-state-write-path-drift.cjs) recognizes
   a residual readModifyWriteStateMd(path, (content) => ...) write whose transform
   is an inline anonymous arrow/function — the opaque shape section 8.1 replaces
   with a declared StateWriteIntent. readModifyWriteStateMd goes THROUGH the seam
   (it is not a raw-write bypass, Axis 2's concern), but its opaque body transform
   is neither verified (section 8.2) nor bounded (section 8.3).

   This ships recognition as a CAPABILITY: exported and unit-tested (positive
   control on a seeded fixture) but DELIBERATELY NOT wired into collect()'s
   failing scan. Wiring it now would turn the ~16 residual callers red at once,
   and ADR-3473 section 8.6 retired the ratchet that would otherwise absorb them.
   C2 wires it terminal as the verifying executor lands and callers migrate under
   ADR-3408 section 6 phasing.

No behavior change: the guard is green on the tree (detection not wired), every
state verb's output is unchanged, and the relevant suites (1763 tests) plus
lint:ci pass. Regression tests are failing-first: positive/negative controls for
the guard capability and a shape test for the type, plus a pin that collect() has
no opaque-transform findings (C1 must not enforce; that is C2).

Closes #4668

* chore(#4668): backfill changeset pr field to the real PR number (#4676)

pr: 0 is rejected by parseFragment as invalid_pr (it is not a valid placeholder);
the fragment must carry the real PR number, which fixes both changeset-lint and
docs-lint (fail_invalid_fragment / fail_malformed_fragment).

---------

Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
Rezolv
2026-09-14 21:43:49 -04:00
committed by GitHub
parent 5d4c98cde7
commit 85026f6a05
5 changed files with 270 additions and 0 deletions

View File

@@ -4826,3 +4826,46 @@ describe('stateReplaceProgressPercent — anchored bold form leaves prose lookal
);
});
});
// ---------------------------------------------------------------------------
// C1 of epic #4629 (ADR-4629 §8.1) — the StateWriteIntent type surface.
// StateWriteIntent EXTENDS StateTransaction, adding the §8.1 concepts: field/
// section assertions marked required-vs-best-effort, plus a declared mutation
// scope (narrow | broad). C1 ships the type + constructor (no caller migrated —
// §8.1's caller-side rule is Required — Phase 2); C2+ consume it. Frozen like its
// base so a transaction cannot be mutated after construction.
describe('C1 (ADR-4629 §8.1): StateWriteIntent type surface', () => {
const { createStateWriteIntent } = require('../gsd-core/bin/lib/state-transition.cjs');
test('createStateWriteIntent extends a StateTransaction with assertions + declared scope', () => {
const base = openStateTransaction({ snapshot: {} });
const intent = createStateWriteIntent(base, {
assertions: [
{ field: 'Progress', requirement: 'required' },
{ field: 'Notes', requirement: 'best-effort' },
],
scope: 'narrow',
});
// still a StateTransaction (IS-A: every base field carried through)
assert.strictEqual(intent.kind, 'open');
assert.strictEqual(typeof intent.snapshot, 'object');
assert.strictEqual(intent.resync, false);
// plus the §8.1 additions
assert.deepStrictEqual(
intent.assertions.map((a) => [a.field, a.requirement]),
[['Progress', 'required'], ['Notes', 'best-effort']],
);
assert.strictEqual(intent.scope, 'narrow');
assert.ok(Object.isFrozen(intent), 'StateWriteIntent must be frozen like StateTransaction');
});
test('createStateWriteIntent defaults scope to narrow and assertions to [] when omitted', () => {
const intent = createStateWriteIntent(openStateTransaction({ snapshot: {} }), {});
assert.deepStrictEqual(intent.assertions, []);
assert.strictEqual(intent.scope, 'narrow');
});
test('createStateWriteIntent rejects an absent base transaction (construction failure, per §8.6 posture)', () => {
assert.throws(() => createStateWriteIntent(null, { scope: 'narrow' }), /transaction/i);
});
});