instar 1.3.1313 → 1.3.1315
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/types.d.ts +2 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/data/standards-guard-index.json +1 -1
- package/dist/data/standards-guard-index.meta.json +2 -2
- package/dist/data/standards-registry.meta.json +1 -1
- package/dist/feedback-factory/drain/FeedbackDrainService.d.ts +26 -0
- package/dist/feedback-factory/drain/FeedbackDrainService.d.ts.map +1 -1
- package/dist/feedback-factory/drain/FeedbackDrainService.js +133 -48
- package/dist/feedback-factory/drain/FeedbackDrainService.js.map +1 -1
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +2 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +3 -3
- package/src/data/standards-guard-index.json +1 -1
- package/src/data/standards-guard-index.meta.json +2 -2
- package/src/data/standards-registry.meta.json +1 -1
- package/src/scaffold/templates/jobs/instar/feedback-factory-process.md +9 -5
- package/upgrades/1.3.1314.md +52 -0
- package/upgrades/1.3.1315.md +51 -0
- package/upgrades/feedback-arbiter-chunking.eli16.md +35 -0
- package/upgrades/feedback-drain-degraded-label.eli16.md +36 -0
- package/upgrades/side-effects/feedback-arbiter-chunking.md +224 -0
- package/upgrades/side-effects/feedback-drain-degraded-label.md +207 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Side-Effects Review — out of tick time is a clean stop; the drain job reads slow runs
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `feedback-drain-degraded-label`
|
|
4
|
+
**Date:** `2026-10-01`
|
|
5
|
+
**Author:** `Echo`
|
|
6
|
+
**Second-pass reviewer:** `independent reviewer subagent (see below)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Live on 2026-10-01 (Mac Studio, v1.3.1314, authority gen 4 `maxBatch` 50, 90 s tick), drain
|
|
11
|
+
runs ef0f6cbf and 10674e04 each ran 70 s. Each made two good readiness calls: ~32 s and ~62 s
|
|
12
|
+
in, 10 + 9 and 10 + 6 candidates. Then each started a ONE-candidate call with 7.6 s / 7.8 s
|
|
13
|
+
left. The pace-based sizing (`remaining / (1.25 × ms per candidate)`) assumes a call costs
|
|
14
|
+
only its per-candidate time. Live calls also pay about 9 s of start-up (6 candidates 23.2 s,
|
|
15
|
+
9 candidates 30.2 s, 10 candidates 32.2 s). So the service's own `withStageBudget` timer cut
|
|
16
|
+
the call at exactly 70.0 s, the readiness deadline. `failed()` then marked the row
|
|
17
|
+
`readiness-authority-failed` and the run `degraded`, with zero provider errors in
|
|
18
|
+
`/metrics/features`.
|
|
19
|
+
|
|
20
|
+
`FeedbackDrainService.reviewReadinessInChunks`:
|
|
21
|
+
- A call after the first needs at least `MIN_LATER_READINESS_CALL_MS` (25 s) left. Below
|
|
22
|
+
that, the loop stops.
|
|
23
|
+
- If the stage timer (`StageBudgetExceeded`, a new private Error subclass with the same
|
|
24
|
+
message) cuts a LATER call whose budget was shortened by the tick clock
|
|
25
|
+
(`callBudgetMs < readinessStageBudgetMs`), that is out of time, not a provider failure.
|
|
26
|
+
The loop stops, and the call's rows stay due, untouched.
|
|
27
|
+
- In both cases, after at least one successful call, the tick records `out.note =
|
|
28
|
+
'readiness-time-exhausted-rest-due'`. `FeedbackDrainTickResult.note` is a new optional,
|
|
29
|
+
informational field: it never degrades the run. `transitionRun` stores `reason ?? note`,
|
|
30
|
+
so a `succeeded` row can carry the note in its reason column.
|
|
31
|
+
- Unchanged: no call made at all → degraded `readiness-wall-clock-exhausted`. A first call
|
|
32
|
+
that times out, a provider timeout or error (`CodexExecJsonTimeoutError` etc.), a contract
|
|
33
|
+
violation and the spend brake are all still degraded.
|
|
34
|
+
|
|
35
|
+
The `feedback-factory-process` job body (`src/scaffold/templates/jobs/instar/feedback-factory-process.md`):
|
|
36
|
+
- The installed body WAS the new script (PR #2111). Its 60 s poll ended before the 70 s runs
|
|
37
|
+
finished, and it printed `still in flight; the next cadence observes it` (both job
|
|
38
|
+
transcripts show this) and exited as a success.
|
|
39
|
+
- The scheduler's declared-failure file (`INSTAR_JOB_FAILURE_FILE` → `readJobDeclaredFailure`
|
|
40
|
+
in `JobScheduler.notifyJobComplete`) is wired correctly. It was never written.
|
|
41
|
+
- Now: the default poll is 180 s (above the service's 115 s wall-clock ceiling). A run still
|
|
42
|
+
in flight after the poll FAILS the job. `expectedDurationMinutes` goes from 2 to 5, so the
|
|
43
|
+
session budget covers a 180 s poll plus model start-up.
|
|
44
|
+
|
|
45
|
+
## Decision-point inventory
|
|
46
|
+
|
|
47
|
+
- Drain readiness loop stop/continue (`FeedbackDrainService`): modify. One extra stop
|
|
48
|
+
condition (a time floor for later calls), and a stage-timer cut of a time-shortened later
|
|
49
|
+
call is reclassified as a clean stop.
|
|
50
|
+
- Run outcome labelling: modify. A tick that stops for time after a successful call is
|
|
51
|
+
`succeeded` with a note, not `degraded`.
|
|
52
|
+
- Job tier-1 supervision (`feedback-factory-process` script): modify. "Still in flight at
|
|
53
|
+
the poll limit" goes from success to a declared failure, and the poll is 3× longer.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 1. Over-block
|
|
58
|
+
|
|
59
|
+
- The job now fails on a run still in flight after 180 s. The service throws
|
|
60
|
+
`feedback drain wall-clock budget exceeded` past 115 s, and the run lease is 120 s, so a
|
|
61
|
+
run alive at 180 s is stuck. Failing the job surfaces it. One false failure is possible if
|
|
62
|
+
the status route itself is unreachable for the whole poll. That is a real fault too.
|
|
63
|
+
- The 25 s floor can leave a few candidates unreviewed in a tick where a short call might
|
|
64
|
+
just have fitted. They stay due and go first next tick (due order `next_review_at,
|
|
65
|
+
cluster_id`). It costs about 1-2 candidates per tick and never rejects anything.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 2. Under-block
|
|
70
|
+
|
|
71
|
+
- A later call cut by the tick clock after an earlier success no longer counts toward
|
|
72
|
+
degradation. If the provider is really slow on that one call, it is hidden in that tick.
|
|
73
|
+
The next tick's first call has the full budget, and if it fails it is degraded as before.
|
|
74
|
+
A provider that keeps getting slow therefore still surfaces.
|
|
75
|
+
- The reclassification requires `StageBudgetExceeded` AND a shortened budget AND an earlier
|
|
76
|
+
success. A provider timeout raises its own error class, so it is never reclassified.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 3. Level-of-abstraction fit
|
|
81
|
+
|
|
82
|
+
This is the right layer. The tick budget and its run labelling belong to the drain service.
|
|
83
|
+
The job script is the tier-1 supervisor of exactly this run outcome. No new gate or table is
|
|
84
|
+
added.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 4. Signal vs authority compliance
|
|
89
|
+
|
|
90
|
+
- [x] No — this change has no block/allow surface.
|
|
91
|
+
|
|
92
|
+
These are resource and timekeeping bounds plus honest outcome labelling. The readiness
|
|
93
|
+
judgment stays with the registered LLM authority.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 4b. Judgment-point check (Judgment Within Floors standard)
|
|
98
|
+
|
|
99
|
+
No new heuristic at a competing-signals decision point. The 25 s floor is a measured resource
|
|
100
|
+
bound. Fitted to the live calls, a call costs ~9 s plus ~2.4 s per candidate. The pace estimate
|
|
101
|
+
(with its 25% margin) only stays safe with about 23 s or more left; the reviewer derived this,
|
|
102
|
+
and the floor was raised from 15 s to 25 s to match.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 5. Interactions
|
|
107
|
+
|
|
108
|
+
- `onRecoverableStall` fired on every degraded run. These two live runs called it. A
|
|
109
|
+
time-stopped run no longer does.
|
|
110
|
+
- `pruneOperationalHistory` runs only for non-degraded runs, so it now also runs after a
|
|
111
|
+
time-stopped tick. That is the same as any succeeded tick.
|
|
112
|
+
- Transient-failure counter: unchanged. Any successful call clears it, and a clean stop is
|
|
113
|
+
not a failure.
|
|
114
|
+
- A cut later call's codex child keeps running until its own 60 s timeout (named in PR #2112
|
|
115
|
+
as well). The 25 s floor makes this rarer. 30-minute ticks never overlap.
|
|
116
|
+
- `FeedbackDrainTickProxy` and the status route pass the run's `reason` through. A succeeded
|
|
117
|
+
run can now show the note there. The job script reads `reason` only for non-success
|
|
118
|
+
states.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## 6. External surfaces
|
|
123
|
+
|
|
124
|
+
- The job body and its manifest are replaced on update by `installBuiltinJobs`. That covers
|
|
125
|
+
the body, `expectedDurationMinutes` and the operator's `enabled`, which is preserved
|
|
126
|
+
(Migration Parity is covered by the existing body-refresh test). No config, route, schema
|
|
127
|
+
or CLAUDE.md template change.
|
|
128
|
+
- Model spend: slightly lower, because the doomed last call is no longer made.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 6b. Operator-surface quality
|
|
133
|
+
|
|
134
|
+
Not applicable. No operator surface is touched (the run reason is already shown as-is).
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
139
|
+
|
|
140
|
+
Unchanged. Readiness runs only on the canonical owner. A proxied tick still finishes the job
|
|
141
|
+
at once ("the owner records its outcome").
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 8. Rollback cost
|
|
146
|
+
|
|
147
|
+
Low. Revert the commit. There are no data-shape changes. A `succeeded` drain_runs row may
|
|
148
|
+
hold the note in `reason`, which older code reads as an ordinary bounded string.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Conclusion
|
|
153
|
+
|
|
154
|
+
The run label now tells the truth in both directions. A tick whose calls all succeeded but
|
|
155
|
+
ran out of time is `succeeded` (with a note), and the rest stays due. A degraded run reaches
|
|
156
|
+
the operator, because the job now waits past the drain's wall clock instead of reporting
|
|
157
|
+
success at 60 s.
|
|
158
|
+
|
|
159
|
+
## Second-pass review (if required)
|
|
160
|
+
|
|
161
|
+
**Reviewer:** independent reviewer subagent
|
|
162
|
+
**Independent read of the artifact: concur**
|
|
163
|
+
|
|
164
|
+
The reviewer ran the targeted tests: unit 15/15, integration 17/17, e2e 1/1. Its notes, and
|
|
165
|
+
what was done with each:
|
|
166
|
+
- The 15 s floor was below the ~23 s at which the start-up-blind pace estimate is safe.
|
|
167
|
+
Applied: the floor is now 25 s.
|
|
168
|
+
- Nothing tested that our timer cutting a later call that had its FULL budget stays degraded.
|
|
169
|
+
Applied: a new unit case.
|
|
170
|
+
- One test name said "full budget" when the budget was shortened. Applied: renamed. That case
|
|
171
|
+
proves a provider error is never reclassified.
|
|
172
|
+
- The job's in-flight failure did not say what it last saw. Applied: it now includes the last
|
|
173
|
+
`lastRun` id and state, or the HTTP code.
|
|
174
|
+
- Left as is (named): the real-timer `setTimeout` race path is not separately unit-tested.
|
|
175
|
+
Both paths throw the same class, and the post-check path is covered.
|
|
176
|
+
|
|
177
|
+
## Evidence pointers
|
|
178
|
+
|
|
179
|
+
- Live store (read-only `.backup` at /tmp/fac-degraded/drain.db): drain_runs ef0f6cbf
|
|
180
|
+
(22:49:40) and 10674e04 (23:00:24). Readiness rows were stamped at +32.2 s / +62.4 s /
|
|
181
|
+
+70.0 s and +39.0 s / +62.2 s / +70.0 s. Exactly one `readiness-authority-failed` row per
|
|
182
|
+
run, at the 70 s deadline.
|
|
183
|
+
- Job transcripts: `FEEDBACK_DRAIN_RESULT run run:ef0f6cbf-… still in flight; the next
|
|
184
|
+
cadence observes it` (and the same for 10674e04).
|
|
185
|
+
- `tests/unit/feedback-factory/drain-readiness-chunking.test.ts`: five new cases cover the
|
|
186
|
+
live shape (9 s + 2.33 s per candidate → calls [10, 9], succeeded with the note, 31 rows
|
|
187
|
+
untouched), a stage-timer cut of a later call (clean), the same cut on the first call
|
|
188
|
+
(degraded), a provider timeout with a shortened budget (degraded), and our timer cutting a
|
|
189
|
+
later call that had its full budget (degraded). Three existing
|
|
190
|
+
expectations move from [10, 10, 2] to [10, 10] because of the floor.
|
|
191
|
+
- `tests/integration/feedback-drain-time-exhausted.test.ts`: production wiring + HTTP status
|
|
192
|
+
report `succeeded` with the note. It fails on the pre-fix source.
|
|
193
|
+
- `tests/integration/feedback-factory-process-job-body.test.ts`: four new cases cover the
|
|
194
|
+
default poll above 115 s, a slow degraded run that still fails the job, a slow succeeded
|
|
195
|
+
run with the note counted as success, and in flight after the poll → failure. Two fail on
|
|
196
|
+
the pre-fix body.
|
|
197
|
+
- `tests/e2e/feedback-drain-time-exhausted.test.ts`: the shipped job script against the
|
|
198
|
+
production AgentServer with the live latency shape. Pre-fix calls are [5, 4, 1] (the live
|
|
199
|
+
shape) and the run is degraded. Post-fix calls are [5, 4], the run succeeded with the note,
|
|
200
|
+
and the job records success.
|
|
201
|
+
|
|
202
|
+
## Class-Closure Declaration (display-only mirror)
|
|
203
|
+
|
|
204
|
+
No agent-authored-artifact defect class: the defects are a missing fixed-cost term in a
|
|
205
|
+
TypeScript time estimate, and a poll shorter than the thing it polls. Convergence of the
|
|
206
|
+
modified loop: at most `ceil(maxBatch / readinessChunkSize)` calls per tick. It stops at the
|
|
207
|
+
floor or the deadline, and any non-time failure still ends the readiness stage.
|