instar 1.3.962 → 1.3.963
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.
- package/dist/core/WriteDomainRegistry.d.ts.map +1 -1
- package/dist/core/WriteDomainRegistry.js +7 -0
- package/dist/core/WriteDomainRegistry.js.map +1 -1
- package/dist/monitoring/CheckInReminderReconciler.d.ts +92 -0
- package/dist/monitoring/CheckInReminderReconciler.d.ts.map +1 -0
- package/dist/monitoring/CheckInReminderReconciler.js +167 -0
- package/dist/monitoring/CheckInReminderReconciler.js.map +1 -0
- package/dist/monitoring/CommitmentTracker.d.ts +39 -0
- package/dist/monitoring/CommitmentTracker.d.ts.map +1 -1
- package/dist/monitoring/CommitmentTracker.js.map +1 -1
- package/dist/monitoring/checkInReminderCore.d.ts +77 -0
- package/dist/monitoring/checkInReminderCore.d.ts.map +1 -0
- package/dist/monitoring/checkInReminderCore.js +104 -0
- package/dist/monitoring/checkInReminderCore.js.map +1 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +106 -0
- package/dist/server/routes.js.map +1 -1
- package/dist/testing/selfActionRegistry.d.ts.map +1 -1
- package/dist/testing/selfActionRegistry.js +62 -0
- package/dist/testing/selfActionRegistry.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +46 -46
- package/src/scaffold/templates/jobs/instar/commitment-checkin-reminder.md +38 -0
- package/upgrades/1.3.963.md +135 -0
- package/upgrades/side-effects/dated-commitment-reminder.md +240 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
A commitment can now carry a **check-in date**, and a background reconciler
|
|
9
|
+
posts one reminder into that commitment's own topic when the date arrives.
|
|
10
|
+
|
|
11
|
+
Before this, "I'll report back by Friday" lived in the session that said it. If
|
|
12
|
+
that session ended, restarted, or compacted, Friday arrived and nothing
|
|
13
|
+
happened. The PromiseBeacon nudges on *rhythm*, not on dates — it has no concept
|
|
14
|
+
of a specific promised moment.
|
|
15
|
+
|
|
16
|
+
**What ships (ACT-724 step 1 of 2):**
|
|
17
|
+
|
|
18
|
+
- `Commitment.checkInAt` — an absolute instant, deliberately separate from the
|
|
19
|
+
beacon's `nextUpdateDueAt` / `softDeadlineAt` so a one-time dated reminder and
|
|
20
|
+
a rolling nudge cadence are not side effects of each other.
|
|
21
|
+
- `CheckInReminderReconciler` — a scan over open commitments, driven by the new
|
|
22
|
+
`commitment-checkin-reminder` built-in job (every 5 minutes, `enabled: false`).
|
|
23
|
+
- `POST /commitments/check-in-reminder/pass` and `GET /commitments/check-in-reminder`.
|
|
24
|
+
|
|
25
|
+
**One recurring scan, not one alarm per commitment.** ACT-724 sketched a
|
|
26
|
+
per-commitment scheduler entry; taken literally that reproduces two of the three
|
|
27
|
+
defects the action itself lists — the two-file job dance (defect b) and
|
|
28
|
+
self-disable by file edit (defect c). A scan has neither: coverage is a property
|
|
29
|
+
of the scan, so there is no registration step that can be skipped, and teardown
|
|
30
|
+
is just the commitment reaching a terminal status.
|
|
31
|
+
|
|
32
|
+
**The ordering was wrong first, and the fix's own justification was unwired.**
|
|
33
|
+
The first design stamped `checkInReminderSentAt` *before* sending — which made
|
|
34
|
+
zero delivery a designed outcome, since a failed send left the commitment marked
|
|
35
|
+
delivered and permanently ineligible. Inverted to send-then-stamp with bounded
|
|
36
|
+
retry (5 attempts, then a loud `checkInReminderFailedAt`). The second review
|
|
37
|
+
round then caught that the duplicate mitigation justifying that inversion —
|
|
38
|
+
"the relay dedups" — was not connected: `sendToTopic` does not dedup, that lives
|
|
39
|
+
in the `/telegram/reply` route. The send is now explicitly routed through the
|
|
40
|
+
same durably-backed `OutboundContentDedup`.
|
|
41
|
+
|
|
42
|
+
**Honest guarantee: at-least-once, deduped at the delivery layer** — not
|
|
43
|
+
exactly-once. Both remaining windows are stated in the spec rather than designed
|
|
44
|
+
away.
|
|
45
|
+
|
|
46
|
+
A newly created scheduled job fired immediately on the next server restart —
|
|
47
|
+
including one scheduled months ahead.
|
|
48
|
+
|
|
49
|
+
At startup the scheduler sweeps for work that was missed while the machine was
|
|
50
|
+
off. To decide, it compares now against the job's last run. For a job that has
|
|
51
|
+
**never** run there is no last run, and the code resolved that case by treating
|
|
52
|
+
it as overdue and triggering it. That is right for a job added while the server
|
|
53
|
+
was down that slept through its window, and wrong for a job created minutes ago
|
|
54
|
+
whose first window is in December — "has never run" describes both identically.
|
|
55
|
+
|
|
56
|
+
The comment directly above that branch stated the correct rule —
|
|
57
|
+
|
|
58
|
+
> *"Jobs that have never run: trigger on startup if their first expected run
|
|
59
|
+
> time has already passed"*
|
|
60
|
+
|
|
61
|
+
— and the code never checked the condition. It couldn't: nothing recorded when a
|
|
62
|
+
job started existing, so there was no fact to reason from.
|
|
63
|
+
|
|
64
|
+
`JobState.firstSeenAt` is now stamped once at registration, and the sweep applies
|
|
65
|
+
the intended rule: a never-run job is missed only if it has existed longer than
|
|
66
|
+
one of its own intervals. A December reminder created today waits; a job that has
|
|
67
|
+
sat unrun through six four-hour windows still catches up.
|
|
68
|
+
|
|
69
|
+
Every unknown resolves to **not firing** — missing or unparseable `firstSeenAt`,
|
|
70
|
+
uncomputable interval. That asymmetry is deliberate: a skipped catch-up costs one
|
|
71
|
+
delayed run, while a future-dated job that fires early marks itself delivered and
|
|
72
|
+
never fires again, so nobody goes looking for it.
|
|
73
|
+
|
|
74
|
+
**Why now:** this is the prerequisite for dated commitments materializing real
|
|
75
|
+
scheduled reminders (ACT-724). A reminder built on a scheduler that fires
|
|
76
|
+
future-dated jobs at boot would discharge itself before its date — worse than
|
|
77
|
+
never having built it. The standard itself is not in this change.
|
|
78
|
+
|
|
79
|
+
## What to Tell Your User
|
|
80
|
+
|
|
81
|
+
Nothing changes until it's switched on. When it is: on the day the agent said it
|
|
82
|
+
would check in on something, you get one short message in that conversation
|
|
83
|
+
saying the date has arrived.
|
|
84
|
+
|
|
85
|
+
It will not claim the work is done — a reminder that implies completion closes
|
|
86
|
+
the question instead of reopening it.
|
|
87
|
+
|
|
88
|
+
**Not yet true:** ACT-724 asks that a dated commitment without a reminder be
|
|
89
|
+
structurally impossible. This ships dark, and a disabled watcher guarantees
|
|
90
|
+
nothing. What is true today: if the reconciler is running, no dated commitment
|
|
91
|
+
can slip past it individually. The creation-time gate that closes the rest is
|
|
92
|
+
step 2, and it cannot itself ship dark.
|
|
93
|
+
|
|
94
|
+
If you've ever seen a scheduled job run for no obvious reason shortly after a
|
|
95
|
+
restart, this was probably it.
|
|
96
|
+
|
|
97
|
+
The practical effect: reminders and scheduled work now happen when they're
|
|
98
|
+
scheduled, not when the server next happens to reboot. A job that's been waiting
|
|
99
|
+
less than one of its own cycles will now wait for its proper turn rather than
|
|
100
|
+
firing at startup — that delay is the point.
|
|
101
|
+
|
|
102
|
+
## Summary of New Capabilities
|
|
103
|
+
|
|
104
|
+
- Commitments carry a first-class check-in date that produces a real reminder.
|
|
105
|
+
- A reminder is never recorded as delivered unless it actually was; failures
|
|
106
|
+
retry and then give up visibly.
|
|
107
|
+
- Two read/drive endpoints and a built-in job, all dark by default.
|
|
108
|
+
|
|
109
|
+
- Scheduled jobs with a future first window are no longer discharged at server
|
|
110
|
+
startup.
|
|
111
|
+
- Job state records when a job was first registered, so "brand new" and "long
|
|
112
|
+
overdue" are distinguishable facts rather than a guess.
|
|
113
|
+
- The startup sweep fails toward not-running on every uncertainty.
|
|
114
|
+
|
|
115
|
+
## Evidence
|
|
116
|
+
|
|
117
|
+
- Tier 1 (41): the full eligibility predicate on both sides of every clause;
|
|
118
|
+
idempotency across repeated passes; **a failed send never reads as sent** (the
|
|
119
|
+
regression test for the round-1 finding); bounded retry to a loud terminal
|
|
120
|
+
state; per-pass cap deferring rather than dropping; the reminder text never
|
|
121
|
+
asserting completion.
|
|
122
|
+
- Tier 2 (10): real Express routes + auth — delivery, HTTP-boundary idempotency,
|
|
123
|
+
dark 503, missing-transport 503, and `dryRun` defaulting ON when config omits it.
|
|
124
|
+
- Tier 3 (4): real `AgentServer` boot with a real injected `CommitmentTracker`.
|
|
125
|
+
|
|
126
|
+
- `tests/unit/job-scheduler-never-run-future-job.test.ts` (3 cases): a never-run
|
|
127
|
+
annual job and a freshly-registered daily job both stay at zero triggers; a job
|
|
128
|
+
registered 6h ago on a 5-minute cron still catches up. Written failing first —
|
|
129
|
+
the two "must not fire" cases failed against the old code, the catch-up case
|
|
130
|
+
already passed.
|
|
131
|
+
- `tests/unit/job-scheduler-edge.test.ts`: the existing test that asserted the
|
|
132
|
+
buggy behaviour was **updated, not deleted** — it now seeds `firstSeenAt` in the
|
|
133
|
+
past so it exercises the scenario it was written for (a job that genuinely
|
|
134
|
+
missed windows), plus a new sibling pinning the other side of the boundary.
|
|
135
|
+
- Full scheduler suite: 76 tests across 8 files, green.
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# Side-effects review — dated-commitment-reminder
|
|
2
|
+
|
|
3
|
+
**Change:** commitments gain a first-class `checkInAt`; a recurring reconciler
|
|
4
|
+
posts exactly one fixed-template reminder to the commitment's topic when that
|
|
5
|
+
instant arrives; a built-in job drives it. ACT-724 (critical, pinned).
|
|
6
|
+
|
|
7
|
+
**Spec:** `docs/specs/dated-commitment-reminder.md` (2 review rounds; round 1
|
|
8
|
+
returned SERIOUS ISSUES and inverted the send/stamp ordering).
|
|
9
|
+
|
|
10
|
+
**Ships dark:** dev-agent gated + `dryRun` defaulting true + job manifest
|
|
11
|
+
`enabled: false`.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. Over-block — what legitimate inputs does this reject that it shouldn't?
|
|
16
|
+
|
|
17
|
+
- **A dated commitment with no `topicId` is never reminded** (`no-topic`). A
|
|
18
|
+
reminder with nowhere to go is not a reminder; it is reported as a skip reason
|
|
19
|
+
rather than silently dropped, and appears in the `pending` list.
|
|
20
|
+
- **An unparseable `checkInAt` fails closed.** Deliberate: coercing it would make
|
|
21
|
+
the commitment look overdue since epoch and fire immediately — the exact
|
|
22
|
+
boot-fire shape fixed in `scheduler-never-run-window` days earlier.
|
|
23
|
+
- **A pass is capped at 25.** An overdue backlog drains across passes rather than
|
|
24
|
+
flooding a topic. The cap logs when it drops work, so a truncated pass is never
|
|
25
|
+
reported as a complete one.
|
|
26
|
+
- **Only `pending` counts as open.** A status added later is treated as not-open
|
|
27
|
+
(no reminder) until classified deliberately. Safe direction, but it does mean a
|
|
28
|
+
future status would silently stop reminders until someone adds it to the set.
|
|
29
|
+
|
|
30
|
+
## 2. Under-block — what failure modes does this still miss?
|
|
31
|
+
|
|
32
|
+
- **"Structurally impossible" is not yet true.** ACT-724 asks that a dated
|
|
33
|
+
commitment without a reminder be impossible. Shipping dark means the watcher
|
|
34
|
+
can be off, so a dated commitment CAN exist with nothing watching. What IS
|
|
35
|
+
structural today: given the reconciler runs, no individual commitment can be
|
|
36
|
+
missed (coverage is a property of the scan, not of a per-commitment
|
|
37
|
+
registration). The creation-time gate that closes the rest is the declared
|
|
38
|
+
graduation step, and it cannot itself ship dark. Called out because the spec's
|
|
39
|
+
own framing over-claimed until review pushed back.
|
|
40
|
+
- **A duplicate is possible in one narrow window** — crash between send and
|
|
41
|
+
stamp. Accepted, and mitigated by the relay's existing content dedup rather
|
|
42
|
+
than by design novelty. Stated plainly instead of claimed away.
|
|
43
|
+
- **Clock jumps** shift a reminder in time (late on a backwards jump, early on a
|
|
44
|
+
forwards one) but cannot double-send or permanently suppress.
|
|
45
|
+
- **No escalation if a reminder is ignored.** Out of scope: this delivers the
|
|
46
|
+
date, it does not chase. The beacon owns chasing.
|
|
47
|
+
|
|
48
|
+
## 3. Level-of-abstraction fit
|
|
49
|
+
|
|
50
|
+
The decision (`isCheckInReminderDue`) is pure and lives in
|
|
51
|
+
`checkInReminderCore.ts`; the effects (CAS, send, retry) live in the reconciler;
|
|
52
|
+
the cadence lives in a built-in job. That split is what let both sides of every
|
|
53
|
+
clause be tested without a store, a clock, or a transport.
|
|
54
|
+
|
|
55
|
+
**The alternative I rejected is the more standard one.** A transactional outbox
|
|
56
|
+
(`reminder_deliveries` with pending/sent/failed) is the textbook answer and is
|
|
57
|
+
genuinely better at volume. Rejected here because the commitment record already
|
|
58
|
+
IS durable single-writer state: a second store would add a consistency boundary
|
|
59
|
+
— a new class of divergence bug — to serve tens of commitments. The fields on the
|
|
60
|
+
commitment are a degenerate outbox with exactly one row per commitment. If volume
|
|
61
|
+
ever justifies it, the migration is mechanical.
|
|
62
|
+
|
|
63
|
+
## 4. Signal vs authority compliance
|
|
64
|
+
|
|
65
|
+
`docs/signal-vs-authority.md`. Both decision points are `invariant`:
|
|
66
|
+
|
|
67
|
+
- **The due predicate** is arithmetic over durable state (open ∧ date arrived ∧
|
|
68
|
+
unstamped ∧ routed ∧ retries remain). No competing signals, nothing to weigh.
|
|
69
|
+
The failure being fixed was an ABSENT mechanism, not a bad judgment.
|
|
70
|
+
- **The reminder text** is a fixed template, deliberately not model-authored.
|
|
71
|
+
Generating it would add a judgment where none is wanted and a failure mode
|
|
72
|
+
(provider down) on a path whose entire purpose is to not fail.
|
|
73
|
+
|
|
74
|
+
**Deliberate gate bypass, argued:** the reminder rides the deterministic delivery
|
|
75
|
+
path, not the LLM tone gate. The tone gate fails closed, and a reminder that can
|
|
76
|
+
be held by a failing gate is the defect this feature exists to fix. This is not
|
|
77
|
+
weakening a safety control — the control is inapplicable, because the message
|
|
78
|
+
carries no agent prose for it to judge. Nothing model-generated reaches the user
|
|
79
|
+
through this path.
|
|
80
|
+
|
|
81
|
+
## 5. Interactions
|
|
82
|
+
|
|
83
|
+
- **PromiseBeacon** — untouched. `checkInAt` is a NEW field precisely so beacon
|
|
84
|
+
cadence (`nextUpdateDueAt`, `softDeadlineAt`) is not overloaded; a one-time
|
|
85
|
+
dated reminder and a rolling nudge cadence stay independent.
|
|
86
|
+
- **`CommitmentTracker.mutate`** — the existing single-writer CAS is reused, not
|
|
87
|
+
replaced. Two passes racing cannot double-stamp.
|
|
88
|
+
- **The relay's content dedup** (`outboundContentDedup`) — now load-bearing for
|
|
89
|
+
this feature's duplicate story. Documented in both the spec and the reconciler
|
|
90
|
+
so a future change to that window is understood to affect this.
|
|
91
|
+
- **The scheduler** — this is why `scheduler-never-run-window` shipped first. A
|
|
92
|
+
reminder job on the old scheduler would have fired at boot.
|
|
93
|
+
- **`installBuiltinJobs`** — writes both `jobs/instar` and `jobs/schedule` for
|
|
94
|
+
built-ins, which is exactly why ACT-724's defect (b) (the two-file dance) does
|
|
95
|
+
not apply: that dance is only manual for USER jobs.
|
|
96
|
+
|
|
97
|
+
## 6. External surfaces
|
|
98
|
+
|
|
99
|
+
- **New:** `POST /commitments/check-in-reminder/pass`, `GET
|
|
100
|
+
/commitments/check-in-reminder`. Both Bearer-authed, both 503 when dark.
|
|
101
|
+
- **New built-in job** `commitment-checkin-reminder`, `enabled: false`.
|
|
102
|
+
- **New commitment fields** — additive and optional; existing records load
|
|
103
|
+
unchanged, no migration.
|
|
104
|
+
- **User-visible:** one message per dated commitment, at its date, in its own
|
|
105
|
+
topic. Fixed template. The only user-facing text this change introduces.
|
|
106
|
+
- **A transport is required only to SEND.** A dry run needs none — surfaced by
|
|
107
|
+
the Tier-3 test, which would otherwise have reported the feature dead on an
|
|
108
|
+
agent with no messaging configured.
|
|
109
|
+
|
|
110
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
111
|
+
|
|
112
|
+
**Unified, with a single-executor rule.**
|
|
113
|
+
|
|
114
|
+
- `checkInAt` / `checkInReminderSentAt` / `checkInReminderAttempts` /
|
|
115
|
+
`checkInReminderFailedAt` live on the commitment record and inherit the
|
|
116
|
+
commitment store's existing posture. No new state surface.
|
|
117
|
+
- **The pass must run on ONE machine.** The built-in job is per-machine (each
|
|
118
|
+
machine runs its own scheduler), so on a pool the pass must be lease-gated the
|
|
119
|
+
way the benchmark-divergence analysis pass is. Belt-and-braces: even if two
|
|
120
|
+
ran, the CAS stamp makes the second a no-op — the lease prevents wasted work,
|
|
121
|
+
the CAS prevents the duplicate.
|
|
122
|
+
- **HONEST GAP:** the lease gate is specified in §Multi-machine posture but the
|
|
123
|
+
job ships `enabled: false`, so it is not yet wired in this change. Enabling the
|
|
124
|
+
job on a multi-machine pool BEFORE adding the lease check would risk two
|
|
125
|
+
machines both passing — the CAS still prevents a duplicate reminder, but the
|
|
126
|
+
work would be duplicated. Recorded as a precondition of enabling, not as done.
|
|
127
|
+
- No notice routing, no generated URL, nothing that strands on topic transfer.
|
|
128
|
+
|
|
129
|
+
## 8. Rollback cost
|
|
130
|
+
|
|
131
|
+
Disable `commitments.checkInReminder` or the job manifest. The reconciler is the
|
|
132
|
+
only reader of these fields; commitments keep them inertly. No migration, no
|
|
133
|
+
durable cleanup, no agent-state repair. Reverting the commit is equally safe.
|
|
134
|
+
|
|
135
|
+
## Second-pass review
|
|
136
|
+
|
|
137
|
+
**Required** — this sends messages to users (outbound messaging is on the Phase-5
|
|
138
|
+
trigger list).
|
|
139
|
+
|
|
140
|
+
Two rounds of external cross-model review, the first of which returned **SERIOUS
|
|
141
|
+
ISSUES** and changed the design:
|
|
142
|
+
|
|
143
|
+
1. **Stamp-before-send made zero delivery a designed outcome.** A failed send
|
|
144
|
+
left the commitment marked `checkInReminderSentAt` and permanently
|
|
145
|
+
ineligible. Inverted to send-then-stamp with bounded retry; the duplicate risk
|
|
146
|
+
is covered by the relay's existing dedup. A regression test asserts a failed
|
|
147
|
+
send never reads as sent.
|
|
148
|
+
2. **"Structurally impossible" over-claimed** for a feature behind a disableable
|
|
149
|
+
flag. Split into what is true now versus at graduation.
|
|
150
|
+
3. **Time semantics under-specified** — `checkInAt` is now defined as an absolute
|
|
151
|
+
instant with normalization at the creation boundary, and DST/clock-jump
|
|
152
|
+
behaviour is stated.
|
|
153
|
+
4. **Cadence and scale omitted** — 5-minute cadence, worst-case lateness, the
|
|
154
|
+
in-memory store's actual size, per-pass cap and backlog drain are now stated.
|
|
155
|
+
5. **Alternatives not compared** — outbox, durable queue, one-shot entries, and
|
|
156
|
+
per-commitment scheduler entries are now compared with reasons.
|
|
157
|
+
|
|
158
|
+
Self-review found one thing the reviewer did not: the Tier-3 test initially
|
|
159
|
+
asserted that `AgentServer` self-constructs the `CommitmentTracker`. It does
|
|
160
|
+
not — `server.ts` injects it — so the routes 503'd on the real boot path while
|
|
161
|
+
Tier 2 passed happily against a hand-assembled context. That is exactly the
|
|
162
|
+
wiring assumption Tier 3 exists to break, and the test now mirrors the
|
|
163
|
+
production entrypoint. It also surfaced the dry-run/transport coupling in §6.
|
|
164
|
+
|
|
165
|
+
Tests: 41 unit + 10 integration + 4 e2e = 55.
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
## Class-Closure Declaration
|
|
169
|
+
|
|
170
|
+
**Class:** `unbounded-self-action` · **Closure:** `guard` · **Enforcement:** ratchet
|
|
171
|
+
**Citation:** `tests/unit/self-action-convergence.test.ts`
|
|
172
|
+
|
|
173
|
+
This change adds a self-triggered controller: a cadenced pass that SENDS
|
|
174
|
+
messages to users without a human in the loop. Under the "Capacity Safety — No
|
|
175
|
+
Unbounded Self-Action" standard that has to be proven to CONVERGE under
|
|
176
|
+
sustained pressure, not merely be individually correct.
|
|
177
|
+
|
|
178
|
+
Registered as `check-in-reminder-pass` in `src/testing/selfActionRegistry.ts`.
|
|
179
|
+
|
|
180
|
+
**Steady-state bound.** The emit is gated by a durable per-commitment attempt
|
|
181
|
+
counter (`CHECK_IN_MAX_ATTEMPTS = 5`), persisted through the CAS `mutate()`
|
|
182
|
+
BEFORE the send — so a crash-loop cannot buy fresh attempts, and the registry's
|
|
183
|
+
restart model (which reconstructs from durable state) settles at the same bound.
|
|
184
|
+
|
|
185
|
+
**Settling brake.** Two terminal states, both durable: a successful send writes
|
|
186
|
+
`checkInReminderSentAt`, making the commitment permanently ineligible; repeated
|
|
187
|
+
failure exhausts the counter and writes a loud `checkInReminderFailedAt`.
|
|
188
|
+
|
|
189
|
+
**Modeled at the worst case.** The registry model has every send FAIL, so the
|
|
190
|
+
success stamp never lands and only the attempt counter can stop it. Without that
|
|
191
|
+
counter the model emits once per tick forever and the ratchet fails — which is
|
|
192
|
+
the point: the bound is load-bearing, not decorative.
|
|
193
|
+
|
|
194
|
+
**Instantaneous mass** is separately bounded by the per-pass cap (25), which logs
|
|
195
|
+
when it defers rather than dropping silently.
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Addendum — three CI gates, and a coverage gap they led me to
|
|
201
|
+
|
|
202
|
+
**1. Write-domain classification.** `POST /commitments/check-in-reminder/pass`
|
|
203
|
+
mutates the commitment store and was undeclared. Classified `cluster-shared` in
|
|
204
|
+
`WriteDomainRegistry`, matching the requirement that the pass run on the
|
|
205
|
+
serving-lease holder only — the same single-writer shape as the feedback-factory
|
|
206
|
+
routes. This does NOT weaken the honest gap in section 7: the lease gate is still
|
|
207
|
+
not wired, and the classification records the intended posture rather than
|
|
208
|
+
asserting the wiring exists.
|
|
209
|
+
|
|
210
|
+
**2. Agent-id header.** The job's curl shipped Bearer-only; I dropped the
|
|
211
|
+
AGENT_ID line when adapting the template I copied from. Restored. Worth noting
|
|
212
|
+
as a pattern: adapting a template is where declared requirements silently fall
|
|
213
|
+
out.
|
|
214
|
+
|
|
215
|
+
**3. Docs coverage.** Job coverage fell 85% to 83% because the new built-in job
|
|
216
|
+
was undocumented. Added to the default-jobs reference. That floor caught a real
|
|
217
|
+
Agent-Awareness omission, not a bureaucratic one — an undocumented job is one no
|
|
218
|
+
operator can discover.
|
|
219
|
+
|
|
220
|
+
**And the thing worth more than all three: a coverage gap in the tone-gate suite.**
|
|
221
|
+
|
|
222
|
+
While dogfooding I hit a live advisory I could not override — four attempts,
|
|
223
|
+
byte-identical text, matching rule, sufficient reason, refused every time. That
|
|
224
|
+
is the RELEASING half of the advisory migration failing in production.
|
|
225
|
+
|
|
226
|
+
Investigating: every existing test in the advisory-migration integration file
|
|
227
|
+
pins ONE verdict for the whole run via a fixed mock, so the rule is constant by
|
|
228
|
+
construction and an ack always matches. The live gate RE-REVIEWS on every attempt
|
|
229
|
+
and can return a different rule — mine went B20_INTERNAL_ID_LEAK then
|
|
230
|
+
B11_STYLE_MISMATCH. That variability was not covered at all.
|
|
231
|
+
|
|
232
|
+
Two tests added: (a) re-review returns the SAME acked rule, ack honored, message
|
|
233
|
+
sent; (b) re-review returns a DIFFERENT rule, the stale ack is refused rather
|
|
234
|
+
than silently passing an objection nobody reviewed.
|
|
235
|
+
|
|
236
|
+
BOTH PASS, which exonerates the ack mechanism and kills my working hypothesis. I
|
|
237
|
+
therefore have a reproducible symptom and NO diagnosis, and I am shipping the
|
|
238
|
+
closed coverage gap rather than a guess dressed as a fix. The evidence and the
|
|
239
|
+
candidates I have not ruled out are in the run log; the four tone-gate tracking
|
|
240
|
+
actions stay OPEN because the releasing half is unproven in production.
|