instar 1.3.963 → 1.3.965

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -237,6 +237,9 @@ export const DP_COMPLETION_CLAIM_VERIFY = 'completion-claim-verify';
237
237
  /** Feedback cluster evidence → owned-work readiness judgment. */
238
238
  export const DP_FEEDBACK_READINESS = 'feedback-readiness';
239
239
 
240
+ /** The stop-justified authority (src/core/UnjustifiedStopGate.ts). */
241
+ export const DP_UNJUSTIFIED_STOP_GATE = 'unjustified-stop-gate';
242
+
240
243
  // ───────────────────────────────────────────────────────────────────────────
241
244
  // The census
242
245
  // ───────────────────────────────────────────────────────────────────────────
@@ -462,12 +465,36 @@ export const PROVENANCE_COVERAGE: ReadonlyArray<ProvenanceCoverageEntry> = [
462
465
  'Should-I-reply verdict over an inbound message; enrollment queued in the ACT-1193 uniform-provenance retrofit backlog.',
463
466
  },
464
467
  {
465
- decisionPoint: 'unjustified-stop-gate',
468
+ decisionPoint: DP_UNJUSTIFIED_STOP_GATE,
466
469
  component: 'UnjustifiedStopGate',
467
- status: 'pending:backlog:decision-quality-enrolment',
470
+ status: 'wired',
471
+ // The highest-volume UNENROLLED decision point in the census: ~1343 calls
472
+ // in the last 7 days (GET /metrics/features), second only to the tone gate
473
+ // among gates. A per-UTC-day COUNT budget rather than `full`: this fires on
474
+ // every stop attempt across every session, and an always-on gate must not
475
+ // grow the provenance archive without a hard ceiling. The ~250-byte
476
+ // decision_quality row is written for every settlement regardless, so the
477
+ // COUNTS stay complete even when the archive is valved.
478
+ volumeClass: 'budget:300',
479
+ // Content-bearing: the gate judges a session's stop rationale. It enters the
480
+ // row as IDENTITY ONLY — hashes, bounds, and code-derived features — never
481
+ // the rationale text or any transcript slice.
468
482
  contentClass: 'content-bearing',
483
+ // ── Grading posture ──────────────────────────────────────────────────
484
+ // MEASUREMENT-ONLY, declared rather than left as a silent gap.
485
+ gradingPosture: 'measurement-only',
486
+ gradingReason:
487
+ 'No honest outcome rule exists YET. Grading "was this stop justified?" needs a ' +
488
+ 'downstream fact — did the run resume and do real work after the gate allowed a ' +
489
+ 'stop, or did the agent genuinely have nothing left after the gate held one? Those ' +
490
+ 'signals exist (the resume queue, the autonomous liveness reconciler) but joining ' +
491
+ 'them to a decision row is real plumbing, not a predicate. Enrolling the DECISION ' +
492
+ 'side first is deliberate and is what this posture is for: the rows accumulate now, ' +
493
+ 'so when the outcome join lands there is history to grade instead of a cold start. ' +
494
+ 'Recording without grading is stated here so the census shows it rather than ' +
495
+ 'implying this point is measured when only half of it is.',
469
496
  reason:
470
- 'Stop-justified verdict over session state; enrollment queued in the ACT-1193 uniform-provenance retrofit backlog.',
497
+ 'The stop-justified authority the highest-volume unenrolled point in the census.',
471
498
  },
472
499
  {
473
500
  decisionPoint: 'coherence-review',
@@ -657,6 +657,11 @@ Two layers keep my machine-to-machine \"ropes\" (Tailscale / LAN / Cloudflare) h
657
657
  - Mark delivered when done: \`curl -X POST -H "Authorization: Bearer $AUTH" http://localhost:${port}/commitments/:id/deliver\`
658
658
  - The PromiseBeacon fires cadenced heartbeats on open commitments so you actually follow through (and surfaces atRisk items), and the commitment-check job surfaces overdue ones.
659
659
  - **A promise survives its owning session's death (Escalation, ships dark).** When a beacon-enabled commitment's session dies before delivering, the beacon no longer silently terminalizes it — it escalates: **Rung 1** revives a fresh session bound to the topic to follow through; **Rung 2** sends an honest, state-specific interim status ("my session ended; I'm picking it back up") when it can't revive; **Rung 3** is a bounded loud give-up to the operator's Attention queue. A revived session carries **revivalMode** — every side-effecting external operation is BLOCKED until it revalidates (\`POST /commitments/:id/revalidate\` with a restated current-intent summary), so a revive grants no new authority. \`GET /commitments/:id\` exposes the escalation fields (\`escalationAttempts\`, \`currentRung\`, \`revalidatedAt\`); \`GET /commitments/escalation-metrics\` is the operator-facing aggregate counters. Ships dark + dry-run-first (\`monitoring.promiseBeacon.escalation\`).
660
+ - **A promise with a DATE produces a real reminder (ACT-724 step 1, ships dark).** When you say "I'll check in on this by Friday", set \`checkInAt\` (an absolute ISO instant — resolve "Friday" to a real moment at creation time; never store a bare date) on the commitment. A recurring reconciler then posts EXACTLY ONE fixed-template reminder into that commitment's own topic when the instant arrives, and stops. Delivering or withdrawing the commitment first means no reminder ever fires — teardown is a status check, nothing to cancel.
661
+ - Read the dated backlog: \`curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/commitments/check-in-reminder\` → \`{ enabled, dryRun, datedCount, pending, undelivered }\`. **\`undelivered\` is the one to look at**: those are promises the user did NOT receive after retries were exhausted.
662
+ - Run one pass now: \`curl -X POST -H "Authorization: Bearer $AUTH" http://localhost:${port}/commitments/check-in-reminder/pass\` (idempotent — a re-run sends nothing).
663
+ - The guarantee is **at-least-once, deduped at the delivery layer** — not exactly-once. A reminder is marked sent ONLY after it actually sent, so a failed send retries (bounded) instead of being recorded as delivered; the crash-window duplicate is absorbed by the relay's content dedup.
664
+ - Ships dark (\`commitments.checkInReminder\`, dryRun defaulting TRUE, job \`commitment-checkin-reminder\` enabled:false). **Honest scope:** while the reconciler runs, no dated commitment can slip past it — but nothing yet guarantees the reconciler runs, so "structurally impossible to have a dated promise without a reminder" is NOT true yet. The creation-time gate that closes it is step 2.
660
665
  - **When to use** (PROACTIVE — this is the trigger): the moment you promise the user a future action, open a commitment. NEVER improvise the follow-through with a raw \`sleep\`/background timer or by "remembering" — those do not survive a session ending, a restart, or compaction, so the promise is silently dropped. A registered commitment is the ONLY durable path. (This is distinct from the Evolution Action Queue / \`/commit-action\`, which tracks self-improvement items, not promises to the user.)
661
666
  - **Action-Claim Follow-Through Sentinel (signal-only, dark by default).** A backstop for the word≠action gap (you say "relaunching now" / "I'll push the change" and then don't). A thin Stop hook posts each finished conversational turn to \`POST /action-claim/observe\`, which classifies a CONCRETE future-action claim (restart/relaunch/push/merge/deploy/fix/…) and opens an idempotent follow-through commitment for the topic — so the existing PromiseBeacon + the revival path make sure it actually happens. High-precision (vague "I'll take a look" never triggers it), de-duplicated by \`externalKey\` (a restated claim updates one commitment, not many), auto-expiring, per-topic capped. It NEVER blocks a message. Off by default; enable with the top-level \`actionClaim.enabled\` (dev-first soak before fleet — the block is top-level, NOT nested under \`messaging\`, which is an array of adapters). It now covers **Slack** conversations too (a promise born in a Slack thread registers a durable commitment bound to the conversation's minted id, delivered back into that exact thread across restarts — dev-gated dark behind \`messaging.actionClaim.slack\`, dryRun-first) and **time-boxed conversational promises** ("I'll post that in about 5 minutes / by EOD / I'll check in"), not just dev-ops verbs. Proactive: user asks "why did a commitment appear when I said I'd restart something / promised to post in 5 min?" → that's this sentinel tracking your stated action so it isn't silently dropped.
662
667
 
@@ -0,0 +1,123 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ `UnjustifiedStopGate` — the authority that decides whether an agent may stop
9
+ working — is now enrolled in the LLM-Decision Quality Meter.
10
+
11
+ At ~1343 calls / 7 days it was the **highest-volume unenrolled decision point in
12
+ the census** and the second-busiest gate overall, yet nothing recorded what it
13
+ decided or on what basis. The meter can only answer "is this judgment any good?"
14
+ for points that record; this one didn't, so the question was unanswerable in
15
+ principle.
16
+
17
+ **What the row carries — identity only.** The gate reads untrusted session
18
+ content (the stop rationale and up to ten recent conversation turns). None of it
19
+ is recorded. The context is an explicit allowlist of derived values:
20
+
21
+ - `stopReasonSha256` + `stopReasonChars` — distinguishes two rationales without
22
+ reproducing either
23
+ - `artifactCount` / `artifactKinds` — the evidence the authority could cite
24
+ - `signals` — code-derived booleans (not user text)
25
+ - `recentTurnCount` / `recentTurnChars` — conversation shape only
26
+
27
+ An allowlist rather than a filtered copy, so a future input field cannot appear
28
+ in the row by default. Tests plant a fake API key and password in the input and
29
+ assert neither reaches the serialized context.
30
+
31
+ **Declared `measurement-only`, on purpose.** Recording a decision is not grading
32
+ it. Grading this point needs a downstream fact — did work resume after an allowed
33
+ stop; did the agent genuinely continue after a refused one — and joining those
34
+ signals to a decision row is real plumbing. Rather than let `wired` imply more
35
+ than exists, the entry declares `gradingPosture: 'measurement-only'` with an
36
+ argued reason, so `findWiredWithoutGraders` reports it as an explicit posture
37
+ instead of a silent contradiction.
38
+
39
+ That distinction is the point: the easy version of this change moves a census
40
+ number and leaves a reader believing the busiest judgment call is being
41
+ evaluated. It isn't yet. Rows accumulate now so the grader has history when it
42
+ lands, rather than starting cold.
43
+
44
+ Volume is valved at `budget:300`/day (an always-on gate must not grow the archive
45
+ unbounded); the `decision_quality` row is written for every settlement regardless,
46
+ so counts stay complete.
47
+
48
+ Spec clarification for the (unshipped, dark) cross-machine capability registry: `scanState` and row
49
+ presence are INDEPENDENT. A machine with a model manifest and no doorway scan-state now returns
50
+ `scanState: "never-observed"` **together with** its manifest-derived rows at status `unknown`,
51
+ instead of zero rows.
52
+
53
+ The earlier wording ("`200` with `scanState: never-observed` and empty entries when no observation
54
+ exists") was ambiguous, and the ambiguity produced a real defect: the Increment-2 route derived ten
55
+ rows from the real manifest and served none of them, because "no observation" was read as "no scan".
56
+ Manifest rows ARE observations — the record admits them with `sourceDetail: doorway-manifest`, and
57
+ the status matrix gives `manifest-only` evidence the status `unknown` precisely so documented
58
+ existence is reportable without ever implying reachability.
59
+
60
+ The rule now carries its failing input: on a machine with a manifest and no scan-state the route
61
+ MUST return rows with status `unknown`; a change that makes it return zero rows must fail a test.
62
+
63
+ ## What to Tell Your User
64
+
65
+ Nothing changes in how the agent behaves. This is instrumentation on an internal
66
+ judgment — no new messages, no new settings, nothing you interact with.
67
+
68
+ What it buys: the check that decides whether the agent may stop working is the
69
+ busiest judgment call in the system, and until now nothing recorded what it
70
+ decided. Now it does. So the question "is that check actually any good — too
71
+ strict, too lenient, wrong half the time?" stops being unanswerable and becomes
72
+ answerable once enough history accumulates.
73
+
74
+ Worth being precise about what is NOT true yet: the decisions are recorded, not
75
+ graded. Knowing whether a given call was *right* needs a later fact (did work
76
+ resume after a stop was allowed?) that isn't connected up yet. The record says so
77
+ explicitly rather than implying the check is being evaluated when only half of
78
+ that is in place.
79
+
80
+ None of the conversation is stored — only a fingerprint of the stop reason and
81
+ counts describing the decision's shape.
82
+
83
+ Nothing. This is a design-document correction for a feature that is switched off everywhere, so
84
+ there is no user-visible change to announce. If a user asks why a capability list looked empty on a
85
+ machine that had never run a scan, the honest answer is that it was a specification ambiguity: the
86
+ catalog rows existed and were being discarded, and the corrected rule serves them marked as
87
+ unverified rather than hiding them.
88
+
89
+ ## Summary of New Capabilities
90
+
91
+ - The stop-justification authority now records every decision it makes, so its
92
+ quality can be evaluated rather than assumed.
93
+ - Recording is identity-only by construction: an explicit allowlist of derived
94
+ values, tested against planted secrets, so session content cannot leak into the
95
+ provenance store.
96
+ - The census now distinguishes "recorded and gradeable" from "recorded but not
97
+ yet gradeable", so the coverage number reflects what actually exists.
98
+
99
+ None. This amendment adds no capability — it removes an ambiguity that was silently costing one
100
+ (the ability to see what a machine's catalog claims before anything has probed it).
101
+
102
+ ## Evidence
103
+
104
+ - `tests/unit/unjustified-stop-gate-provenance-enrollment.test.ts` (11): the
105
+ census declares it wired with a volume valve and content class; the
106
+ measurement-only posture carries a real argument; the provenance block names
107
+ the typed constant with the prompt hash; **planted secrets never reach the
108
+ context**; the hash distinguishes different rationales (so it isn't a constant);
109
+ empty input doesn't throw; the verdict is unchanged and `attribution` survives
110
+ alongside the new block.
111
+ - Census ratchet: pending baseline shrinks by exactly one; typed-registration
112
+ check independently verifies the source imports `DP_UNJUSTIFIED_STOP_GATE`.
113
+ - 72 green across the affected suites.
114
+
115
+ Found by running the Increment-2 route's local-projection path against a real agent state dir rather
116
+ than reading the diff: `durable projection ABSENT / fallback readDoorwaySources entries = 10 /
117
+ scanState = never-observed`, and then observing that `classifyProjection` early-returned a single
118
+ `no-data-yet` row, discarding all ten.
119
+
120
+ ## Audience
121
+
122
+ Agent-only, maturity experimental. No user-visible behavior: the feature is dark everywhere, no
123
+ route ships enabled by this change, and the amendment is spec text only.
@@ -0,0 +1,58 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ The dated check-in reminder (shipped in #1630) is now in the agent CLAUDE.md
9
+ template — and reaches EXISTING agents, not just fresh installs.
10
+
11
+ #1630 shipped routes, a built-in job, and 55 tests, and nothing in the agent
12
+ template. Per the Agent Awareness Standard, "an agent that doesn't know about a
13
+ capability effectively doesn't have it." No agent would have set `checkInAt`, so
14
+ the reconciler would have scanned an empty set every five minutes, found nothing,
15
+ and reported success forever. Working code, passing tests, green health check,
16
+ zero reminders.
17
+
18
+ **The migration is the substantive half.** The existing Commitments arm is
19
+ guarded by `if (!content.includes('**Commitments & Follow-Through**'))` — every
20
+ running agent already has that section, so anything added inside that branch
21
+ reaches fresh installs ONLY. That is the exact Migration Parity failure the
22
+ standard names, and it looks correct in a diff. The new subsection therefore gets
23
+ its own arm keyed on "section present AND subsection absent", with a test
24
+ asserting that separation rather than trusting it.
25
+
26
+ A drifted CLAUDE.md gets an append rather than a skip: a misplaced paragraph
27
+ beats an agent permanently unaware of a capability because it customised its own
28
+ notes.
29
+
30
+ The text states honest scope — dark by default, at-least-once rather than
31
+ exactly-once, and that "structurally impossible to have a dated promise without a
32
+ reminder" is NOT true yet. A briefing that oversells produces an agent
33
+ confidently relying on something that isn't running.
34
+
35
+ ## What to Tell Your User
36
+
37
+ Nothing changes in behaviour. This is the difference between the reminder
38
+ feature existing and it being usable — without it, no agent would ever know to
39
+ attach a date to a promise, and the feature would have run over an empty list
40
+ indefinitely while looking healthy.
41
+
42
+ ## Summary of New Capabilities
43
+
44
+ - Agents now know dated commitments produce reminders, which field to set, and
45
+ which field reveals promises that were never delivered.
46
+ - Existing agents receive it on update, not only new installs.
47
+ - The briefing states what the feature does NOT yet guarantee, so an agent
48
+ cannot over-rely on a capability that ships switched off.
49
+
50
+ ## Evidence
51
+
52
+ - `tests/unit/claudemd-check-in-reminder-awareness.test.ts` (10): the template
53
+ documents the endpoint, names the `checkInAt` field an agent must set, carries
54
+ a proactive trigger rather than a bare endpoint, states the honest scope, and
55
+ points at `undelivered`; the migrator arm exists, is SEPARATE from the
56
+ Commitments arm, is idempotent, records what it did, and appends rather than
57
+ skipping on drift.
58
+ - `feature-delivery-completeness`: 126 tests green.
@@ -0,0 +1,126 @@
1
+ # Side-effects review — enrol-unjustified-stop-gate
2
+
3
+ **Change:** enrol `UnjustifiedStopGate` into the LLM-Decision Quality Meter
4
+ (`pending:backlog:decision-quality-enrolment` → `wired`), with an identity-only
5
+ provenance context and an explicitly declared `measurement-only` grading posture.
6
+
7
+ **Why this point:** at ~1343 calls / 7 days (`GET /metrics/features`) it is the
8
+ highest-volume UNENROLLED decision point in the census, and the second-busiest
9
+ gate overall. This is the second half of the run's census task — the half where
10
+ the backlog actually shrinks rather than merely becoming legible.
11
+
12
+ **Tier:** 1 — one census entry, one context builder, one provenance block. No new
13
+ route, config key, flag, or durable store.
14
+
15
+ ---
16
+
17
+ ## 1. Over-block — what legitimate inputs does this reject that it shouldn't?
18
+
19
+ **Nothing.** Enrollment adds an `options.provenance` block to an existing
20
+ provider call. It introduces no predicate, no branch, and no rejection path. The
21
+ gate's continue/allow/escalate verdict is computed exactly as before — asserted
22
+ directly by a test rather than argued.
23
+
24
+ The one build-time narrowing: the census ratchet now requires this point to stay
25
+ declared. Removing the enrollment without updating the census fails CI, which is
26
+ the intent.
27
+
28
+ ## 2. Under-block — what failure modes does this still miss?
29
+
30
+ - **It records decisions; it does not grade them.** This is the honest half and
31
+ is declared as `gradingPosture: 'measurement-only'` with a ≥40-char argument,
32
+ so `findWiredWithoutGraders` reports it as an explicit posture rather than a
33
+ silent contradiction. **The census number improves and the point is still not
34
+ measured end-to-end** — anyone reading "wired" without reading the posture will
35
+ overestimate what exists. That is why the posture is declared, tested, and
36
+ stated in the release note.
37
+ - **Volume is capped at `budget:300`/day.** A day with more than 300 stop
38
+ decisions archives the first 300; the `decision_quality` row is still written
39
+ for every settlement, so counts stay complete while the archive is valved. A
40
+ loud `droppedByBudget` counter surfaces the truncation.
41
+ - **The context is a fixed projection.** If a future reader needs a field I did
42
+ not include, the history will not have it retroactively. I chose the minimum
43
+ that reconstructs decision shape; widening later is additive but not backfillable.
44
+
45
+ ## 3. Level-of-abstraction fit
46
+
47
+ The context builder is a private method on the gate, because only the gate knows
48
+ which of its inputs are trusted evidence and which are untrusted session text.
49
+ Building it anywhere else would require exporting that distinction — and a
50
+ downstream builder that guesses wrong is precisely how a transcript ends up in
51
+ a provenance store.
52
+
53
+ The provenance block rides the existing `options` argument alongside
54
+ `attribution`, so enrollment adds a signal rather than displacing one (tested).
55
+
56
+ ## 4. Signal vs authority compliance
57
+
58
+ `docs/signal-vs-authority.md`. Enrollment holds **no authority at all** — it is
59
+ pure observability. The settlement seam consumes the block and records on its own
60
+ path; it never reaches the model and never alters the verdict.
61
+
62
+ The gate itself remains what it was: an LLM authority with a deterministic
63
+ fail-open contract. This change deliberately does not touch that.
64
+
65
+ **The content-bearing contract is the safety property here**, and it is enforced
66
+ by test rather than by care: the fixtures plant a fake API key and a fake
67
+ password in the untrusted input, and the tests assert neither appears anywhere in
68
+ the serialized context. A hash-distinctness test prevents a constant or empty
69
+ hash from making the identity field useless while still passing the leak tests.
70
+
71
+ ## 5. Interactions
72
+
73
+ - **`/metrics/features`** — unchanged; `attribution.component` is preserved
74
+ (explicitly tested, because losing it would silently drop this component from
75
+ the cost surface).
76
+ - **The census ratchet** — the pending baseline shrinks by exactly one line, which
77
+ is the direction it is allowed to move. The typed-registration check
78
+ independently verifies the enrolling source imports `DP_UNJUSTIFIED_STOP_GATE`
79
+ rather than restating the string.
80
+ - **The breaker / fail-open path** — untouched. A provenance write failure is
81
+ contained by the recorder's own fail-open contract, so it cannot convert into a
82
+ stop-decision failure.
83
+ - **The prompt hash** — reused as `promptId`, so a row records WHICH prompt
84
+ decided. The gate already computed it; nothing new is derived.
85
+
86
+ ## 6. External surfaces
87
+
88
+ - No route, config key, flag, env var, CLI, message, or notification.
89
+ - `GET /decision-quality` will show one more `wired` point and one fewer
90
+ `pending`. Field shapes unchanged.
91
+ - No user-visible behaviour whatsoever.
92
+
93
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
94
+
95
+ **Unified.** The census is a shipped source constant, byte-identical on every
96
+ install, so every machine enrolls the same point with the same volume valve and
97
+ content class. The provenance rows themselves inherit the meter's existing
98
+ posture — this change adds no new state surface, no notice, and no generated URL.
99
+
100
+ ## 8. Rollback cost
101
+
102
+ Revert. The census entry returns to `pending:` (restoring the baseline line), the
103
+ provenance block disappears, and previously-recorded rows remain inertly in the
104
+ store — harmless, and still readable if the enrollment returns.
105
+
106
+ ## Second-pass review
107
+
108
+ **Not required** by the Phase-5 trigger list: enrollment makes no block/allow
109
+ decision, touches no session lifecycle, and adds no gate. The gate it observes is
110
+ unchanged.
111
+
112
+ Self-review, recorded rather than skipped:
113
+
114
+ 1. **Is this the honest version or the number-improving version?** The
115
+ temptation was real: flipping `pending` → `wired` shrinks census debt whether
116
+ or not anything can be graded. I took the declared `measurement-only` path
117
+ because the census distinguishes it and `findWiredWithoutGraders` surfaces it
118
+ — the gap stays visible instead of being absorbed into a better-looking
119
+ number. If that posture did not exist I would not have shipped this as-is.
120
+ 2. **Could the untrusted text leak by a route I did not consider?** The context is
121
+ an explicit allowlist of derived values, not a filtered copy of the input, so
122
+ a new input field cannot appear in the row by default. The leak tests assert
123
+ absence of planted secrets rather than presence of expected keys, which is the
124
+ direction that catches accidental widening.
125
+
126
+ Tests: 11 new unit tests; 72 green across the affected suites.
@@ -0,0 +1,124 @@
1
+ # Side-effects review — reminder-agent-awareness
2
+
3
+ **Change:** add the dated check-in reminder (ACT-724 step 1, merged in #1630) to
4
+ the agent CLAUDE.md template, with a SEPARATE `PostUpdateMigrator` arm so
5
+ existing agents receive it, plus tests pinning both halves.
6
+
7
+ **Why:** #1630 shipped routes, a job, and 55 tests — and nothing in the agent
8
+ template. Per the Agent Awareness Standard, "an agent that doesn't know about a
9
+ capability effectively doesn't have it." No agent would ever set `checkInAt`, so
10
+ the reconciler would scan an empty set forever and report success doing it.
11
+
12
+ **Tier:** 1 — template text, one migration arm, tests. No runtime behaviour.
13
+
14
+ ---
15
+
16
+ ## 1. Over-block — what legitimate inputs does this reject that it shouldn't?
17
+
18
+ Nothing. This adds documentation and one idempotent migration branch. No
19
+ predicate, no gate, no rejection path.
20
+
21
+ ## 2. Under-block — what failure modes does this still miss?
22
+
23
+ - **The Agent Awareness Standard remains unenforced in general.** This change
24
+ gates THIS capability; the next feature can still ship template-less and no
25
+ test will notice. That is the real defect and it is bigger than this PR — the
26
+ standard is prose with no structure behind it, which is precisely why I missed
27
+ it while shipping a feature about not relying on memory. Recorded rather than
28
+ quietly fixed for one case and forgotten.
29
+ - **Drifted CLAUDE.md files get an append, not a placement.** If an agent
30
+ customised the section wording, the marker won't match and the subsection is
31
+ appended near the block instead of at the intended point. Deliberate: a
32
+ misplaced paragraph beats a permanently unaware agent.
33
+ - **The template text can still drift from the shipped routes.** Nothing
34
+ cross-checks the documented curl against the real route path. A rename would
35
+ leave the briefing confidently wrong.
36
+
37
+ ## 3. Level-of-abstraction fit
38
+
39
+ Right layer, and the migration arm placement is the substantive decision.
40
+
41
+ The existing Commitments arm is guarded by
42
+ `if (!content.includes('**Commitments & Follow-Through**'))`. Adding the
43
+ subsection inside that branch would reach FRESH INSTALLS ONLY — every currently
44
+ running agent already has that section and never re-enters the branch. That is
45
+ the exact Migration Parity failure mode the standard names, and it looks
46
+ completely correct in a diff.
47
+
48
+ Hence a separate arm keyed on "section present AND subsection absent", with a
49
+ test asserting that separation rather than trusting it.
50
+
51
+ ## 4. Signal vs authority compliance
52
+
53
+ `docs/signal-vs-authority.md`. No authority: documentation plus an idempotent
54
+ text patch. The migration decides only whether text is already present — a
55
+ `String.includes` check at a dev/update chokepoint, not a runtime judgment.
56
+
57
+ ## 5. Interactions
58
+
59
+ - **The Commitments migration arm** — untouched; the new arm runs after it and
60
+ is reachable in the state the old arm skips.
61
+ - **Shadow-capability slicing** — the subsection sits INSIDE the existing
62
+ Commitments block rather than creating a new top-level marker, so the slicer's
63
+ boundaries are unchanged.
64
+ - **`feature-delivery-completeness`** — passes (126 tests); this change adds
65
+ awareness for a feature that suite already tracks.
66
+
67
+ ## 6. External surfaces
68
+
69
+ - **The agent-facing CLAUDE.md gains one subsection.** That IS the point: it is
70
+ the surface through which an agent discovers the capability.
71
+ - No route, config key, flag, env var, or runtime behaviour.
72
+ - The text states the honest scope — dark by default, at-least-once (not
73
+ exactly-once), and that "structurally impossible to have a dated promise
74
+ without a reminder" is NOT yet true. A briefing that oversells produces an
75
+ agent confidently relying on something that isn't running.
76
+
77
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
78
+
79
+ **Unified.** The template is a shipped source constant and the migration runs
80
+ per-machine against each machine's own CLAUDE.md, producing identical text
81
+ everywhere. No new state, no notice, no generated URL, nothing to strand on a
82
+ topic transfer.
83
+
84
+ ## 8. Rollback cost
85
+
86
+ Revert. Agents that already received the subsection keep harmless extra
87
+ documentation; the idempotency check means a re-run after revert re-adds it
88
+ rather than duplicating.
89
+
90
+ ## Second-pass review
91
+
92
+ **Not required** — no block/allow decision, no session lifecycle, no gate.
93
+
94
+ Self-review, the one thing I went looking for: **did I put the migration where it
95
+ would actually run?** I nearly appended to the Commitments arm, which would have
96
+ been invisible in review and would have reached zero existing agents. The test
97
+ `that arm is SEPARATE from the Commitments-section arm` exists specifically
98
+ because I caught myself about to do the wrong thing, and a comment saying "don't
99
+ do that" would not have survived the next edit.
100
+
101
+
102
+ ## Addendum — the migration emitted a literal placeholder
103
+
104
+ CI rejected the first version, correctly.
105
+
106
+ The subsection's example commands carry the agent's port, which the surrounding
107
+ template literal interpolates. I authored the section inside a shell heredoc and
108
+ escaped the interpolation so the shell would not consume it — and the escape
109
+ survived into the TypeScript. A patched CLAUDE.md would therefore have contained
110
+ the literal placeholder text instead of a port number, making **every curl
111
+ example in the new section copy-paste-broken**, in the one document agents rely
112
+ on to know how to call things.
113
+
114
+ Caught by `tests/unit/PostUpdateMigrator-coordinationAwareness.test.ts`, which
115
+ asserts no literal placeholder survives into a patched file. That guard exists
116
+ because this has happened before; it earned its keep.
117
+
118
+ **The generalizable bit:** authoring template-literal content through a shell
119
+ heredoc puts two escaping regimes in series, and the second one is invisible in
120
+ the diff — the TypeScript looks correct in isolation. Worth writing such content
121
+ with a file-write tool rather than a heredoc when it contains interpolations.
122
+
123
+ Side effects of the fix: none beyond the corrected string. No test was weakened;
124
+ the existing guard is what caught it.
@@ -0,0 +1,13 @@
1
+ # Side effects — spec round 10 (scanState vs row presence)
2
+
3
+ - **No code changes in this PR.** Spec text only; the feature is dark on every install.
4
+ - **Downstream obligation created:** the Increment-2 route (merged behavior) currently returns zero
5
+ rows on a machine that has never scanned. Once this amendment lands, that behavior contradicts the
6
+ spec and must be fixed — tracked for Increment 3, with the failing input stated in the spec so the
7
+ fix is verifiable rather than asserted.
8
+ - **Risk if NOT amended:** on any machine whose doorway-scan job is disabled (the shipped default),
9
+ the read surface would report nothing while holding a catalog it had already derived — the exact
10
+ "empty reads as unknown" conflation the three-way `scanState` exists to prevent.
11
+ - **Rollback:** revert the spec paragraph; no deployed behavior changes either way.
12
+ - **Provenance note:** this ambiguity was mine as the converging reviewer, not a build defect — the
13
+ implementation followed the spec as written. Recorded so the review history stays honest.