instar 1.3.975 → 1.3.977
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/StageTransitionValidator.d.ts.map +1 -1
- package/dist/core/StageTransitionValidator.js +16 -1
- package/dist/core/StageTransitionValidator.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +86 -12
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +46 -46
- package/upgrades/1.3.976.md +60 -0
- package/upgrades/1.3.977.md +53 -0
- package/upgrades/side-effects/honest-merge-base-verification.md +132 -0
- package/upgrades/side-effects/learning-velocity-counts-finishing.md +102 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Side-Effects Review — learning velocity counts finishing, not filing (ACT-1244)
|
|
2
|
+
|
|
3
|
+
## Summary of the change
|
|
4
|
+
|
|
5
|
+
`GET /metrics/learning-velocity` counted EVERY evolution action at its `createdAt`, so the metric
|
|
6
|
+
answering "are we learning?" was almost purely a measure of FILING RATE. Verified 2026-07-25: 739 of
|
|
7
|
+
771 events (96%) were filed items; on the real queue 494 of those carry the sweep's resolution
|
|
8
|
+
"Abandoned without active tracking since creation date". The metric read 88/100 "accelerating" on
|
|
9
|
+
the morning the operator halted work because the opposite was visibly true — **the faster work was
|
|
10
|
+
abandoned, the higher the score climbed.**
|
|
11
|
+
|
|
12
|
+
An action now contributes a learning event ONLY when `status === 'completed'`, stamped at
|
|
13
|
+
`completedAt` (falling back to `updatedAt`). Everything else is excluded and ACCOUNTED FOR by
|
|
14
|
+
reason. The response gains `counting` (the rule, in one line) and `evolutionActions`
|
|
15
|
+
(`considered` / `counted` / `excluded` by reason, with `auto-abandoned` named specifically).
|
|
16
|
+
|
|
17
|
+
Measured before/after on this agent's live queue, 30-day window, rule change only:
|
|
18
|
+
**784 events → 18.** Considered 1,288; excluded 743 `not-completed:pending`, 494 `auto-abandoned`,
|
|
19
|
+
29 `not-completed:cancelled`, 2 `not-completed:in_progress`.
|
|
20
|
+
|
|
21
|
+
## Decision-point inventory
|
|
22
|
+
|
|
23
|
+
- `GET /metrics/learning-velocity` — a READ-ONLY observability route. It gates nothing, blocks
|
|
24
|
+
nothing, and no code path branches on its output. The change alters what it REPORTS, never what
|
|
25
|
+
anything DOES.
|
|
26
|
+
- `computeLearningVelocity` (`src/core/LearningVelocityScorer.ts`) — **untouched.** It remains a
|
|
27
|
+
pure function over an event list; only the route's event GATHERING changed. Its 6 unit tests pass
|
|
28
|
+
unmodified, which is the evidence that the scorer's contract is unchanged.
|
|
29
|
+
- No gate, hook, reaper, sentinel, scheduler, migration or config surface is touched.
|
|
30
|
+
|
|
31
|
+
## 1. Over-block
|
|
32
|
+
|
|
33
|
+
Not applicable — nothing here blocks. The adjacent risk is over-EXCLUSION: an action that genuinely
|
|
34
|
+
represents learning but is not marked `completed` no longer counts. That is the intended semantics
|
|
35
|
+
(the whole finding is that filing ≠ learning), and it is not silent: every exclusion is itemised by
|
|
36
|
+
reason in the response, so a reader can see exactly what was set aside and disagree with the rule if
|
|
37
|
+
they wish.
|
|
38
|
+
|
|
39
|
+
Two exclusions were chosen deliberately and could be argued:
|
|
40
|
+
|
|
41
|
+
- **`completed` with no `completedAt`/`updatedAt` → excluded, not back-dated to `createdAt`.**
|
|
42
|
+
Back-dating would re-import the entire filing bias through the one remaining gap. On the live
|
|
43
|
+
queue this bucket is EMPTY (all 20 completions carry `completedAt`), so the strict choice costs
|
|
44
|
+
nothing today and closes the door for later.
|
|
45
|
+
- **`cancelled` for a considered reason → still excluded.** A deliberate cancellation may well
|
|
46
|
+
embody a lesson, but it is not a completion, and admitting it would reopen the "closing a row
|
|
47
|
+
counts as learning" path that produced the inversion.
|
|
48
|
+
|
|
49
|
+
## 2. Under-block
|
|
50
|
+
|
|
51
|
+
The score can now READ LOWER than reality if completions are recorded without status/timestamp
|
|
52
|
+
hygiene. That is a reporting risk, not a safety one, and it is bounded by the accounting block: a
|
|
53
|
+
low score with `counted: 1, excluded: {not-completed:pending: 743}` is legible as "almost nothing
|
|
54
|
+
has finished", which is the true statement. The previous behaviour had the opposite failure — a high
|
|
55
|
+
score that was structurally unfalsifiable — and an honest low number is strictly better than a
|
|
56
|
+
flattering unfalsifiable one.
|
|
57
|
+
|
|
58
|
+
`byType.learning` (registered learnings) and `byType.correction` (the correction ledger) are
|
|
59
|
+
unchanged: both are genuine learning artifacts rather than filings, so neither was touched.
|
|
60
|
+
|
|
61
|
+
## 3. Blast radius
|
|
62
|
+
|
|
63
|
+
One route handler in `src/server/routes.ts`. Consumers checked:
|
|
64
|
+
|
|
65
|
+
- `grep -rn "learning-velocity"` across `src/`, `scripts/`, `tests/` — the route is read by the
|
|
66
|
+
CLAUDE.md template docs and the dashboard's plain-language surface; no code branches on
|
|
67
|
+
`adaptabilityScore` or `byType.evolution`.
|
|
68
|
+
- The response is ADDITIVE (`counting`, `evolutionActions`); no existing field was renamed or
|
|
69
|
+
removed, so a consumer reading `totalEvents`/`byType`/`adaptabilityScore` still parses.
|
|
70
|
+
- `tests/unit/LearningVelocityScorer.test.ts` — 6 tests, unmodified, pass. The scorer is untouched.
|
|
71
|
+
|
|
72
|
+
## 4. Rollback plan
|
|
73
|
+
|
|
74
|
+
Single-commit revert. No state, no config key, no migration, no persisted artifact; the metric is
|
|
75
|
+
recomputed from disk on every request. Reverting restores the old counting immediately.
|
|
76
|
+
|
|
77
|
+
## 5. Test coverage (all three tiers)
|
|
78
|
+
|
|
79
|
+
- **Unit** — `LearningVelocityScorer.test.ts` unchanged and passing (proves the pure scorer's
|
|
80
|
+
contract did not move).
|
|
81
|
+
- **Integration** — `tests/integration/learning-velocity-routes.test.ts`, 7 tests. Three existing
|
|
82
|
+
cases UPDATED (see §6) and three added: the real queue in miniature (5 filed-or-abandoned + 1
|
|
83
|
+
completion → exactly 1 event, `auto-abandoned` counted as 2 and named); a `completed` action with
|
|
84
|
+
no timestamp excluded rather than back-dated; and a completion outside the window excluded by
|
|
85
|
+
DATE while still appearing in `counted`, so "counts completions" cannot become "counts every
|
|
86
|
+
completion ever".
|
|
87
|
+
- **E2E** — `tests/e2e/learning-velocity-lifecycle.test.ts`: the `counting` rule and the
|
|
88
|
+
`evolutionActions` accounting must survive the PRODUCTION initialization path, because the live
|
|
89
|
+
endpoint is where a reader actually reads the number.
|
|
90
|
+
|
|
91
|
+
## 6. Three existing tests were encoding the defect
|
|
92
|
+
|
|
93
|
+
`learning-velocity-routes.test.ts` used action fixtures carrying only `createdAt` and asserted they
|
|
94
|
+
counted as learning events. Those assertions were a specification of the inversion: they would have
|
|
95
|
+
failed any correct implementation. Rewritten to use completions, with the filed-but-unfinished case
|
|
96
|
+
retained and asserted as an EXCLUSION rather than deleted.
|
|
97
|
+
|
|
98
|
+
This is the **third** time in one day a passing test was found protecting the defect beside it (the
|
|
99
|
+
others: the standards parser's five-family allowlist, and a fixture whose meaning changed with the
|
|
100
|
+
wall clock). Recorded rather than tidied away, because three instances in a day is a pattern about
|
|
101
|
+
how our tests get written — they pin current behaviour, and current behaviour is what the audit
|
|
102
|
+
found wanting. The class is worth a standard; it is not fixed here.
|