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.
@@ -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.