instar 1.3.136 → 1.3.138
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/dashboard/index.html +62 -0
- package/dashboard/preferences-learning.js +388 -0
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +15 -0
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +3 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +31 -10
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/types.d.ts +11 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/monitoring/CorrectionLedger.d.ts +6 -0
- package/dist/monitoring/CorrectionLedger.d.ts.map +1 -1
- package/dist/monitoring/CorrectionLedger.js +17 -0
- package/dist/monitoring/CorrectionLedger.js.map +1 -1
- package/dist/monitoring/CorrectionLoopDriver.d.ts +59 -9
- package/dist/monitoring/CorrectionLoopDriver.d.ts.map +1 -1
- package/dist/monitoring/CorrectionLoopDriver.js +87 -12
- package/dist/monitoring/CorrectionLoopDriver.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +3 -2
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/CapabilityIndex.js +4 -4
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +88 -6
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/scripts/framework-issue-backfill/codex-session-findings.json +116 -0
- package/scripts/framework-issue-backfill/import-findings.mjs +75 -0
- package/src/data/builtin-manifest.json +62 -62
- package/src/scaffold/templates.ts +3 -2
- package/upgrades/1.3.135.md +80 -36
- package/upgrades/1.3.137.md +97 -0
- package/upgrades/1.3.138.md +58 -0
- package/upgrades/side-effects/correction-preference-learning-sentinel-slice2.md +86 -0
- package/upgrades/side-effects/framework-issue-observe-write-path.md +44 -0
package/upgrades/1.3.135.md
CHANGED
|
@@ -1,53 +1,97 @@
|
|
|
1
1
|
# Upgrade Guide — vNEXT
|
|
2
2
|
|
|
3
|
-
<!-- bump:
|
|
3
|
+
<!-- bump: minor -->
|
|
4
4
|
|
|
5
5
|
## What Changed
|
|
6
6
|
|
|
7
|
-
**
|
|
8
|
-
|
|
7
|
+
**The Correction & Preference Learning Sentinel now has a human read surface — a
|
|
8
|
+
calm "Preferences" dashboard tab — plus the scalability and closed-loop hardening
|
|
9
|
+
that lets it run unattended.** This is Slice 2 of the feature; it still ships dark
|
|
10
|
+
and is still signal-only (it never blocks, rewrites, or delays a message).
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
The new **Preferences tab** in the dashboard shows, in plain language, the
|
|
13
|
+
preferences the agent has picked up about how you like to work (the same block the
|
|
14
|
+
session-start hook injects on every boot) and the recent corrections it has
|
|
15
|
+
distilled, each with a short scrubbed summary and its status. The exact words you
|
|
16
|
+
used are never stored or shown — only a neutral summary. When the feature is off,
|
|
17
|
+
the tab shows a friendly "not turned on yet" state instead of an error.
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
cycle so it snaps back to full speed the moment work resumes. The first poller to
|
|
18
|
-
adopt it is the token-usage scanner (`TokenLedgerPoller`): when no session is
|
|
19
|
-
running there are no new tokens to attribute, so it backs off from every 60s to
|
|
20
|
-
every 5 minutes. The mechanism is built so the other pollers — and, later, a full
|
|
21
|
-
agent-sleep mode — can adopt it incrementally.
|
|
19
|
+
Under the hood, the loop is now bounded so it can run on a schedule without ever
|
|
20
|
+
running away:
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
- The recurrence analyzer routes at most a handful of learnings per run; the rest
|
|
23
|
+
stay open and route on the next run, so a backlog can never flood the agent in
|
|
24
|
+
one pass.
|
|
25
|
+
- When auto-feedback is on, infra-gap proposals are sent one at a time with a delay
|
|
26
|
+
between them and stop on the first rate-limit response, so a converged batch
|
|
27
|
+
never trips the feedback route's limit or silently drops a learning.
|
|
28
|
+
- The drift canary that watches for changing phrasing gets its own small daily LLM
|
|
29
|
+
budget, separate from the main one, so it can never starve the main path.
|
|
30
|
+
- The correction ledger gained a composite index backing its restart-proof
|
|
31
|
+
distinct-calendar-days count.
|
|
32
|
+
- The closed-loop verify step now runs on the infra-gap path too: if the same
|
|
33
|
+
friction recurs after a feedback proposal it reopens; if it goes quiet, the
|
|
34
|
+
result is marked inconclusive rather than verified — because an infrastructure
|
|
35
|
+
fix is the upstream project's to ship, so the agent never claims a result it
|
|
36
|
+
cannot prove it caused.
|
|
37
|
+
|
|
38
|
+
The corrections list also gained pagination — a since lower-bound and a keyset
|
|
39
|
+
cursor — so a large ledger can be walked page by page.
|
|
26
40
|
|
|
27
41
|
## What to Tell Your User
|
|
28
42
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
43
|
+
There is a new Preferences tab in your dashboard. It is the calm, plain-language
|
|
44
|
+
place to see what the agent has learned about how you like to work, and the recent
|
|
45
|
+
moments it noticed you correcting it. The exact words you used are never kept —
|
|
46
|
+
only a short, scrubbed summary. It is read-only and never changes or blocks a
|
|
47
|
+
message; these are signals the agent applies by default, and a real instruction or
|
|
48
|
+
a safety rule always wins. The whole feature still ships turned off by default, so
|
|
49
|
+
until someone enables it the tab simply shows a friendly note that it is not turned
|
|
50
|
+
on yet. When the user asks what the agent has learned about them, point them to the
|
|
51
|
+
Preferences tab rather than reading raw output.
|
|
35
52
|
|
|
36
53
|
## Summary of New Capabilities
|
|
37
54
|
|
|
38
|
-
- New
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
-
|
|
42
|
-
|
|
43
|
-
-
|
|
55
|
+
- New dashboard "Preferences" tab (`dashboard/preferences-learning.js` + the tab
|
|
56
|
+
wiring in `dashboard/index.html`) — the human read surface for learned
|
|
57
|
+
preferences + scrubbed corrections; pure renderers + a visibility-gated polling
|
|
58
|
+
controller; textContent-only DOM writes so raw text can never reach the DOM;
|
|
59
|
+
friendly disabled state in plain prose. Backed by the existing
|
|
60
|
+
`/preferences/session-context` + `/corrections` endpoints (no new server route).
|
|
61
|
+
- `GET /corrections` pagination — `?since=<ISO>` lower-bound alongside the
|
|
62
|
+
`?before=<ISO>` keyset cursor and `?limit`.
|
|
63
|
+
- `CorrectionLoopDriver` hardening — a per-tick add ceiling (`maxRoutesPerTick`,
|
|
64
|
+
default 5) with overflow carried to the next run; batched, rate-limit-aware
|
|
65
|
+
loopback feedback (`feedbackPostDelayMs`, default 7000; stops on 429, carries
|
|
66
|
+
the rest); and an infra-gap closed-loop verify (reopen-on-recurrence with the
|
|
67
|
+
14-day window; silence → inconclusive). `POST /corrections/analyze` now reports
|
|
68
|
+
`routed.overflow` + `routed.rateLimited`.
|
|
69
|
+
- `CorrectionLedger` composite index `idx_corr_dedupe_day` backing the distinct-
|
|
70
|
+
calendar-days recurrence count.
|
|
71
|
+
- New config `monitoring.correctionLearning.driftCanaryDailyCents` (5),
|
|
72
|
+
`maxRoutesPerTick` (5), `feedbackPostDelayMs` (7000) — auto-migrated to existing
|
|
73
|
+
agents via ConfigDefaults deep-merge. The drift canary gets its own dedicated
|
|
74
|
+
LlmQueue sub-budget so it cannot starve the main 25-cent distill cap.
|
|
44
75
|
|
|
45
76
|
## Evidence
|
|
46
77
|
|
|
47
|
-
- `tests/unit/
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
- `tests/unit/
|
|
53
|
-
|
|
78
|
+
- `tests/unit/CorrectionLoopDriver.test.ts` (15) — per-tick ceiling caps + carries
|
|
79
|
+
overflow; a second run routes the carried-over overflow; a 429 stops the batch
|
|
80
|
+
and the remaining infra-gap records stay open; legacy-boolean feedback still
|
|
81
|
+
honored; infra-gap recurrence → reopened (14-day window); infra-gap silence →
|
|
82
|
+
inconclusive (never verified).
|
|
83
|
+
- `tests/unit/CorrectionLedger.test.ts` (13) — `idx_corr_dedupe_day` is created and
|
|
84
|
+
`distinctCounts` still computes correct day counts with the composite index.
|
|
85
|
+
- `tests/unit/preferences-learning-render.test.ts` (20, jsdom) — every renderer +
|
|
86
|
+
the polling controller against a real DOM; the disabled state; a record carrying
|
|
87
|
+
an unexpected raw `learning` field is never rendered.
|
|
88
|
+
- `tests/integration/corrections-routes.test.ts` (12) — `?since` lower-bounds
|
|
89
|
+
detected_at; the `?before` keyset cursor walks the full set without overlap; a
|
|
90
|
+
malformed `?since`/`?before` is tolerated (never a 500).
|
|
91
|
+
- `tests/e2e/preferences-learning-tab-lifecycle.test.ts` (5) — the tab is alive on
|
|
92
|
+
the production route path (200 enabled / 503 disabled); the SHIPPED controller
|
|
93
|
+
renders the learned preference + scrubbed correction from a live server; the raw
|
|
94
|
+
`learning` text never reaches the DOM; the disabled state shows no config-key
|
|
95
|
+
string and no monospace element.
|
|
96
|
+
- `node scripts/docs-coverage.mjs --check` — exit 0 (class 55% / route 55% floors
|
|
97
|
+
held).
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- bump: minor -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
**The Correction & Preference Learning Sentinel now has a human read surface — a
|
|
8
|
+
calm "Preferences" dashboard tab — plus the scalability and closed-loop hardening
|
|
9
|
+
that lets it run unattended.** This is Slice 2 of the feature; it still ships dark
|
|
10
|
+
and is still signal-only (it never blocks, rewrites, or delays a message).
|
|
11
|
+
|
|
12
|
+
The new **Preferences tab** in the dashboard shows, in plain language, the
|
|
13
|
+
preferences the agent has picked up about how you like to work (the same block the
|
|
14
|
+
session-start hook injects on every boot) and the recent corrections it has
|
|
15
|
+
distilled, each with a short scrubbed summary and its status. The exact words you
|
|
16
|
+
used are never stored or shown — only a neutral summary. When the feature is off,
|
|
17
|
+
the tab shows a friendly "not turned on yet" state instead of an error.
|
|
18
|
+
|
|
19
|
+
Under the hood, the loop is now bounded so it can run on a schedule without ever
|
|
20
|
+
running away:
|
|
21
|
+
|
|
22
|
+
- The recurrence analyzer routes at most a handful of learnings per run; the rest
|
|
23
|
+
stay open and route on the next run, so a backlog can never flood the agent in
|
|
24
|
+
one pass.
|
|
25
|
+
- When auto-feedback is on, infra-gap proposals are sent one at a time with a delay
|
|
26
|
+
between them and stop on the first rate-limit response, so a converged batch
|
|
27
|
+
never trips the feedback route's limit or silently drops a learning.
|
|
28
|
+
- The drift canary that watches for changing phrasing gets its own small daily LLM
|
|
29
|
+
budget, separate from the main one, so it can never starve the main path.
|
|
30
|
+
- The correction ledger gained a composite index backing its restart-proof
|
|
31
|
+
distinct-calendar-days count.
|
|
32
|
+
- The closed-loop verify step now runs on the infra-gap path too: if the same
|
|
33
|
+
friction recurs after a feedback proposal it reopens; if it goes quiet, the
|
|
34
|
+
result is marked inconclusive rather than verified — because an infrastructure
|
|
35
|
+
fix is the upstream project's to ship, so the agent never claims a result it
|
|
36
|
+
cannot prove it caused.
|
|
37
|
+
|
|
38
|
+
The corrections list also gained pagination — a since lower-bound and a keyset
|
|
39
|
+
cursor — so a large ledger can be walked page by page.
|
|
40
|
+
|
|
41
|
+
## What to Tell Your User
|
|
42
|
+
|
|
43
|
+
There is a new Preferences tab in your dashboard. It is the calm, plain-language
|
|
44
|
+
place to see what the agent has learned about how you like to work, and the recent
|
|
45
|
+
moments it noticed you correcting it. The exact words you used are never kept —
|
|
46
|
+
only a short, scrubbed summary. It is read-only and never changes or blocks a
|
|
47
|
+
message; these are signals the agent applies by default, and a real instruction or
|
|
48
|
+
a safety rule always wins. The whole feature still ships turned off by default, so
|
|
49
|
+
until someone enables it the tab simply shows a friendly note that it is not turned
|
|
50
|
+
on yet. When the user asks what the agent has learned about them, point them to the
|
|
51
|
+
Preferences tab rather than reading raw output.
|
|
52
|
+
|
|
53
|
+
## Summary of New Capabilities
|
|
54
|
+
|
|
55
|
+
- New dashboard "Preferences" tab (`dashboard/preferences-learning.js` + the tab
|
|
56
|
+
wiring in `dashboard/index.html`) — the human read surface for learned
|
|
57
|
+
preferences + scrubbed corrections; pure renderers + a visibility-gated polling
|
|
58
|
+
controller; textContent-only DOM writes so raw text can never reach the DOM;
|
|
59
|
+
friendly disabled state in plain prose. Backed by the existing
|
|
60
|
+
`/preferences/session-context` + `/corrections` endpoints (no new server route).
|
|
61
|
+
- `GET /corrections` pagination — `?since=<ISO>` lower-bound alongside the
|
|
62
|
+
`?before=<ISO>` keyset cursor and `?limit`.
|
|
63
|
+
- `CorrectionLoopDriver` hardening — a per-tick add ceiling (`maxRoutesPerTick`,
|
|
64
|
+
default 5) with overflow carried to the next run; batched, rate-limit-aware
|
|
65
|
+
loopback feedback (`feedbackPostDelayMs`, default 7000; stops on 429, carries
|
|
66
|
+
the rest); and an infra-gap closed-loop verify (reopen-on-recurrence with the
|
|
67
|
+
14-day window; silence → inconclusive). `POST /corrections/analyze` now reports
|
|
68
|
+
`routed.overflow` + `routed.rateLimited`.
|
|
69
|
+
- `CorrectionLedger` composite index `idx_corr_dedupe_day` backing the distinct-
|
|
70
|
+
calendar-days recurrence count.
|
|
71
|
+
- New config `monitoring.correctionLearning.driftCanaryDailyCents` (5),
|
|
72
|
+
`maxRoutesPerTick` (5), `feedbackPostDelayMs` (7000) — auto-migrated to existing
|
|
73
|
+
agents via ConfigDefaults deep-merge. The drift canary gets its own dedicated
|
|
74
|
+
LlmQueue sub-budget so it cannot starve the main 25-cent distill cap.
|
|
75
|
+
|
|
76
|
+
## Evidence
|
|
77
|
+
|
|
78
|
+
- `tests/unit/CorrectionLoopDriver.test.ts` (15) — per-tick ceiling caps + carries
|
|
79
|
+
overflow; a second run routes the carried-over overflow; a 429 stops the batch
|
|
80
|
+
and the remaining infra-gap records stay open; legacy-boolean feedback still
|
|
81
|
+
honored; infra-gap recurrence → reopened (14-day window); infra-gap silence →
|
|
82
|
+
inconclusive (never verified).
|
|
83
|
+
- `tests/unit/CorrectionLedger.test.ts` (13) — `idx_corr_dedupe_day` is created and
|
|
84
|
+
`distinctCounts` still computes correct day counts with the composite index.
|
|
85
|
+
- `tests/unit/preferences-learning-render.test.ts` (20, jsdom) — every renderer +
|
|
86
|
+
the polling controller against a real DOM; the disabled state; a record carrying
|
|
87
|
+
an unexpected raw `learning` field is never rendered.
|
|
88
|
+
- `tests/integration/corrections-routes.test.ts` (12) — `?since` lower-bounds
|
|
89
|
+
detected_at; the `?before` keyset cursor walks the full set without overlap; a
|
|
90
|
+
malformed `?since`/`?before` is tolerated (never a 500).
|
|
91
|
+
- `tests/e2e/preferences-learning-tab-lifecycle.test.ts` (5) — the tab is alive on
|
|
92
|
+
the production route path (200 enabled / 503 disabled); the SHIPPED controller
|
|
93
|
+
renders the learned preference + scrubbed correction from a live server; the raw
|
|
94
|
+
`learning` text never reaches the DOM; the disabled state shows no config-key
|
|
95
|
+
string and no monospace element.
|
|
96
|
+
- `node scripts/docs-coverage.mjs --check` — exit 0 (class 55% / route 55% floors
|
|
97
|
+
held).
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
review-convergence: complete
|
|
3
|
+
approved: true
|
|
4
|
+
approved-by: echo (standing 12h deploy mandate, topic 13435; Justin directive to track all codex onboarding issues for next-framework onboarding)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Upgrade Guide — vNEXT
|
|
8
|
+
|
|
9
|
+
<!-- bump: patch -->
|
|
10
|
+
|
|
11
|
+
## What Changed — a durable write path for the framework-onboarding issue ledger
|
|
12
|
+
|
|
13
|
+
The Framework-Onboarding Mentor System keeps a bucket-tagged ledger of issues found while
|
|
14
|
+
onboarding a non-Claude agent framework (Codex now; Cursor/Aider/Gemini next). Until now it
|
|
15
|
+
was fed by ONE source — the automated mentor tick's own forensics — so issues discovered the
|
|
16
|
+
other way (an engineer auditing and fixing the code) never reached it. The generalizable
|
|
17
|
+
buckets that feed the next-framework playbook stayed empty of exactly the deepest lessons.
|
|
18
|
+
|
|
19
|
+
This adds `POST /framework-issues/observe` — a small authenticated write path over the
|
|
20
|
+
ledger's already-validated `recordObservation` (+ an optional status transition, so an
|
|
21
|
+
already-fixed issue can be backfilled in one call). New framework strings are intentionally
|
|
22
|
+
allowed (onboarding the next framework introduces one); the write is idempotent on `dedupKey`.
|
|
23
|
+
A reusable importer script (`scripts/framework-issue-backfill/`) bulk-loads a findings JSON.
|
|
24
|
+
|
|
25
|
+
## Summary of New Capabilities
|
|
26
|
+
|
|
27
|
+
- `POST /framework-issues/observe` records an engineering-discovered framework issue:
|
|
28
|
+
`{ framework, bucket, severity?, title, dedupKey, evidence?, observedVersion?, status?,
|
|
29
|
+
fixedInVersion?, wontFixReason?, bucketPrimary?, signature?, relatedSpec? }`. bucket /
|
|
30
|
+
severity / status enums are validated by the ledger (400 on bad input). Idempotent on
|
|
31
|
+
`dedupKey`. Returns `{ issueId, created, recurrenceCount, issue }`.
|
|
32
|
+
- The two generalizable buckets (`framework-limitation`, `instar-integration-gap`) feed the
|
|
33
|
+
existing `/framework-issues/playbook?targetFramework=X` for the next framework onboarded.
|
|
34
|
+
- Agents learn the write path via CLAUDE.md (template registry row + migrator section), with a
|
|
35
|
+
proactive trigger: log a framework-compat issue the moment you fix or won't-fix it.
|
|
36
|
+
|
|
37
|
+
## What to Tell Your User
|
|
38
|
+
|
|
39
|
+
When you are onboarding a non-Claude agent and you find a compatibility issue by fixing the
|
|
40
|
+
code, you can now record it in the durable onboarding ledger, not just the ones the automated
|
|
41
|
+
mentor loop happens to trip over. Those records carry forward as a playbook for the next
|
|
42
|
+
framework you bring onto Instar. Nothing to configure.
|
|
43
|
+
|
|
44
|
+
## Evidence
|
|
45
|
+
|
|
46
|
+
- New route in src/server/routes.ts (thin wrapper over the existing recordObservation +
|
|
47
|
+
updateIssue; both already enum-validate and throw, surfaced as HTTP 400).
|
|
48
|
+
- Tier-2 integration: tests/integration/framework-issues-routes.test.ts — records a new issue,
|
|
49
|
+
backfills an already-fixed issue in one call, idempotent re-record, allows a new framework
|
|
50
|
+
string, 400 on missing field / invalid bucket / wont-fix-without-reason (23 tests pass).
|
|
51
|
+
- Tier-3 E2E: tests/e2e/framework-issue-ledger-lifecycle.test.ts — the write route is alive on
|
|
52
|
+
the production init path (POST returns 200 not 503; the finding surfaces through the live GET).
|
|
53
|
+
- Agent Awareness + Migration Parity: templates.ts registry row + a content-sniffed,
|
|
54
|
+
idempotent migrateClaudeMd paragraph for existing agents (feature-delivery-completeness
|
|
55
|
+
allowlist updated; 235 PostUpdateMigrator tests pass).
|
|
56
|
+
- tsc --noEmit clean; npm run lint clean.
|
|
57
|
+
- Spec: docs/specs/framework-issue-observe-write-path.md (+ .eli16.md).
|
|
58
|
+
- Side-effects: upgrades/side-effects/framework-issue-observe-write-path.md.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Side-Effects Review — Correction & Preference Learning Sentinel, Slice 2
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `correction-preference-learning-sentinel-slice2`
|
|
4
|
+
**Date:** `2026-05-30`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `not required` (signal-only, ships dark; read-only dashboard surface; see §7)
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Slice 2 adds the human read surface and the scalability/closed-loop hardening on top of the shipped Slice 1a/1b loop:
|
|
11
|
+
|
|
12
|
+
- **Dashboard "Preferences" tab** — a calm, plain-language read-only surface (`dashboard/preferences-learning.js` + tab wiring in `dashboard/index.html`) mirroring the Process Health tab precedent. It shows the active learned preferences (from `/preferences/session-context`) and the recent scrubbed corrections (from `/corrections`). No new server route — it consumes the two existing Slice-1a/1b endpoints.
|
|
13
|
+
- **`/corrections` `?since` pagination** — a lower-bound (`detected_at >= since`) alongside the existing `?before` keyset cursor + `?limit`.
|
|
14
|
+
- **`CorrectionLoopDriver` hardening** — a per-tick add ceiling (`maxRoutesPerTick`, default 5; overflow stays `open`, re-routes next run), batched + rate-limit-aware loopback feedback (`feedbackPostDelayMs`, default 7000; stops on the first 429 and carries the rest), and the infra-gap closed-loop verify (reopen-on-recurrence with the 14-day window; silence → inconclusive, never verified).
|
|
15
|
+
- **`CorrectionLedger` composite index** `idx_corr_dedupe_day` backing the distinct-calendar-days recurrence count.
|
|
16
|
+
- **Drift-canary sub-budget** — a dedicated `LlmQueue` instance with its own small daily cap (`driftCanaryDailyCents`, default 5) so the canary can never starve the main 25¢ distill cap.
|
|
17
|
+
|
|
18
|
+
The loop is still **SIGNAL-ONLY** (it never blocks or rewrites an outbound message) and still ships **dark** behind `monitoring.correctionLearning.enabled`. The new dashboard tab is read-only.
|
|
19
|
+
|
|
20
|
+
**Files touched:**
|
|
21
|
+
- `dashboard/preferences-learning.js` (new — pure renderers + polling controller), `dashboard/index.html` (tab button + panel + registry entry + lazy-load wiring).
|
|
22
|
+
- `src/monitoring/CorrectionLedger.ts` (composite index + `listOccurrenceIndexes()`), `src/monitoring/CorrectionLoopDriver.ts` (per-tick ceiling, batched/429-retry, infra-gap verify + audit).
|
|
23
|
+
- `src/server/routes.ts` (`?since`; structured `feedbackLoopbackPost`; `maxRoutesPerTick`/`feedbackPostDelayMs`/audit wiring; `routed.overflow`/`rateLimited` response), `src/commands/server.ts` (drift-canary sub-budget LlmQueue).
|
|
24
|
+
- `src/core/types.ts` + `src/config/ConfigDefaults.ts` (`driftCanaryDailyCents`, `maxRoutesPerTick`, `feedbackPostDelayMs` + defaults).
|
|
25
|
+
- `src/server/CapabilityIndex.ts` (`?since` + dashboard tab), `src/scaffold/templates.ts` + `src/core/PostUpdateMigrator.ts` (dashboard tab awareness + idempotent backfill), `src/data/builtin-manifest.json` (regenerated by build).
|
|
26
|
+
- `site/src/content/docs/features/correction-preference-learning.md` (Preferences tab + API + Scalability bounds + infra-gap verify).
|
|
27
|
+
- `tests/unit/CorrectionLedger.test.ts`, `tests/unit/CorrectionLoopDriver.test.ts`, `tests/unit/preferences-learning-render.test.ts` (new), `tests/integration/corrections-routes.test.ts`, `tests/e2e/preferences-learning-tab-lifecycle.test.ts` (new).
|
|
28
|
+
- `upgrades/NEXT.md`, this side-effects artifact.
|
|
29
|
+
|
|
30
|
+
## Decision-point inventory
|
|
31
|
+
|
|
32
|
+
- **Dashboard tab** — *read-only render*. Pure data→DOM with `sanitizeForDisplay` + textContent-only writes. No authority; cannot mutate anything.
|
|
33
|
+
- **Per-tick add ceiling** — *bounded throughput, code-determined*. Caps routed records/run; overflow stays `open` and re-routes next run. Cannot drop a learning.
|
|
34
|
+
- **429-retry batch control** — *carry, not drop*. A rate-limited feedback POST leaves the record `open` for the next run. Cannot silently lose a learning.
|
|
35
|
+
- **Drift-canary sub-budget** — *resource isolation*. A separate `LlmQueue` instance; cannot affect routing or authority.
|
|
36
|
+
- **Infra-gap verify verdict** — *signal*. Silence → `inconclusive` (never a self-attributed "verified"); recurrence → reopen (capped). No authority change.
|
|
37
|
+
|
|
38
|
+
## 1. Over-block
|
|
39
|
+
|
|
40
|
+
No block/allow surface — over-block not applicable. The dashboard tab is read-only; the driver still never blocks a message, tool call, or user. The per-tick ceiling bounds throughput but never blocks a learning — overflow is carried, not dropped.
|
|
41
|
+
|
|
42
|
+
## 2. Under-block
|
|
43
|
+
|
|
44
|
+
No block/allow surface — under-block not applicable. The closest analog is a learning deferred to the next run by the per-tick ceiling or a 429; in both cases the record stays `open` and re-routes — it is delayed, never lost, never an unsafe action.
|
|
45
|
+
|
|
46
|
+
## 3. Level-of-abstraction fit
|
|
47
|
+
|
|
48
|
+
Right layer. The dashboard tab is a self-contained browser-native ESM module + index.html wiring, mirroring the Process Health tab precedent exactly (lazy dynamic-import, registry entry with onActivate/onDeactivate, reused `ph-*` calm-type CSS, `sanitizeForDisplay` + textContent-only safety). The driver/ledger/config changes sit in the same `src/monitoring/` + `src/config/` modules as the rest of the loop. No new server route was added — the tab consumes the existing Slice-1a/1b endpoints (the discoverability-lint allowlist is unchanged).
|
|
49
|
+
|
|
50
|
+
## 4. Signal vs authority compliance
|
|
51
|
+
|
|
52
|
+
**Required reference:** docs/signal-vs-authority.md
|
|
53
|
+
|
|
54
|
+
**Does this change hold blocking authority with brittle logic?**
|
|
55
|
+
|
|
56
|
+
- [x] **No — every output is a signal consumed by an existing smart gate or a human, or a read-only render.** The dashboard tab is read-only. The driver's authority surface is unchanged from Slice 1b: `CorrectionLoopDeps` still carries exactly `addAction`, `createInitiative`, `feedbackLoopbackPost`, `recordPreference`, `attentionRoute` — Slice 2 adds only bounding parameters (`maxRoutesPerTick`, `feedbackPostDelayMs`, `sleep`, `audit`) and a richer return type on `feedbackLoopbackPost` (`{posted, rateLimited}`), none of which add a mutation capability. The existing by-construction authority test still holds (no proposal-mint, no memory-write field). The infra-gap verify verdict is signal-only and deliberately refuses to self-attribute a "verified" the agent cannot prove.
|
|
57
|
+
|
|
58
|
+
## 5. Interactions
|
|
59
|
+
|
|
60
|
+
- **Shadowing:** the dashboard tab registry entry chains after Process Health; it has its own panel ids (`pl*`) so it cannot shadow the Process Health tab (`ph*`). The lazy-load controller is independent.
|
|
61
|
+
- **Double-fire:** the per-tick ceiling + the analyzer's `status:'open'` filter mean a record routes once; a record left as overflow stays `open` and is re-evaluated next run (idempotent — the second-run test pins it routes the carried-over overflow exactly once). The 429 path leaves the record `open` (never marked `acted-on`), so it re-routes next run without duplicating.
|
|
62
|
+
- **Races:** the driver is single-threaded per `route()` call. The composite index does not change `distinctCounts` results (a test pins identical day counts) — it only speeds the query. The drift-canary `LlmQueue` is a separate instance; it cannot race the main distill queue's cap.
|
|
63
|
+
- **Feedback loops:** the batched feedback POSTs serialize with a delay and stop on the first 429, so the loop cannot hammer its own `/feedback` route. The dashboard tab polls read-only (`/preferences/session-context` + `/corrections`) on a 60s cadence, visibility-gated and abort-safe — it cannot re-enter the capture path.
|
|
64
|
+
- **Startup ordering:** the drift-canary sub-budget LlmQueue is constructed inside the existing capture-loop boot block, only when `driftCanary` is on (it ships dark, so normally null). The dashboard files ship with the package and are served by `express.static` — no boot wiring.
|
|
65
|
+
|
|
66
|
+
## 6. External surfaces
|
|
67
|
+
|
|
68
|
+
- **Other agents on the same machine:** none. No change to the cross-agent (Rising Tide) path beyond the batched/429 serialization, which only makes the agent a politer client of its own `/feedback` route.
|
|
69
|
+
- **Other users of the install base:** purely additive and dark. Existing agents that don't flip `monitoring.correctionLearning.enabled` see byte-identical behavior. The new config keys (`driftCanaryDailyCents`, `maxRoutesPerTick`, `feedbackPostDelayMs`) backfill via ConfigDefaults deep-merge with safe defaults. The dashboard tab appears on the next update (package-served) but renders the friendly disabled state until the flag is on.
|
|
70
|
+
- **External systems:** the drift-canary sub-budget provisions an additional small daily LLM spend cap (default 5¢, only when the canary is enabled — dark by default). No new egress beyond what Slice 1b disclosed; the canary is not yet wired to fire (its budget is provisioned ahead of the sampler).
|
|
71
|
+
- **Persistent state:** new composite index in the existing `correction-ledger.db` (no new file; created idempotently via `CREATE INDEX IF NOT EXISTS`). The driver's audit hook appends route/verify decisions to the existing `<stateDir>/logs/correction-learning-audit.jsonl` (metadata only, `origin: correction-loop`). No new persistent state.
|
|
72
|
+
- **Timing:** the per-tick ceiling and the inter-POST delay are bounded and only apply during the off-hot-path weekly analyzer run. The dashboard polling is read-only and visibility-gated.
|
|
73
|
+
|
|
74
|
+
## 7. Rollback cost
|
|
75
|
+
|
|
76
|
+
- **Code:** flip `monitoring.correctionLearning.enabled` back to false (or revert the commits). With the flag off, the `/corrections` + `/preferences/session-context` routes 503, the dashboard tab renders the disabled state, and the driver is not wired.
|
|
77
|
+
- **Persistent state:** the composite index is harmless when the feature is off (read by nothing). The audit JSONL stays on disk, read by nothing. No new state file to remove.
|
|
78
|
+
- **Dashboard:** the tab is package-served; reverting the `dashboard/` changes removes it. There is no per-agent dashboard migration to undo.
|
|
79
|
+
- **Agent state repair:** none required.
|
|
80
|
+
- **User visibility during rollback:** none — the feature is dark by default and the tab shows the disabled state.
|
|
81
|
+
|
|
82
|
+
Total rollback time: under 1 minute (flip one config flag, or revert the commits).
|
|
83
|
+
|
|
84
|
+
## Conclusion
|
|
85
|
+
|
|
86
|
+
Read-only dashboard surface + bounding parameters + an index + a sub-budget — no new blocking authority, no new mutation capability, no new egress beyond the (dark) canary budget. The per-tick ceiling and 429-retry carry learnings rather than dropping them; the infra-gap verify refuses to self-attribute a "verified". The dashboard tab mirrors the established Process Health precedent (sanitize + textContent-only) so raw text can never reach the DOM. Signal-only, dark-by-default, and fully bounded.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Side-effects artifact — framework-issue ledger write path (POST /framework-issues/observe)
|
|
2
|
+
|
|
3
|
+
## 1. What files/state does this change touch at runtime?
|
|
4
|
+
Only the existing `framework-issue-ledger.db` SQLite file (under `<stateDir>/server-data/`),
|
|
5
|
+
via the ledger's existing `recordObservation` + `updateIssue` methods (WAL, busy_timeout
|
|
6
|
+
already configured). No new files, tables, or schema. No config keys added.
|
|
7
|
+
|
|
8
|
+
## 2. Does it gate, block, or constrain any agent/session/message?
|
|
9
|
+
No. The ledger is observability-only and this is a write surface for it. It never gates a
|
|
10
|
+
job, blocks a message, kills a session, or influences any decision path. Purely additive
|
|
11
|
+
record-keeping.
|
|
12
|
+
|
|
13
|
+
## 3. What happens on bad/malicious input?
|
|
14
|
+
Required fields missing → 400. Invalid bucket/severity/status enum → the ledger's
|
|
15
|
+
`assertEnum` throws → 400. `status:'wont-fix'` without a reason → ledger throws (§13.7) → 400.
|
|
16
|
+
Free-text fields are length-bounded + secret-scanned by the ledger's existing
|
|
17
|
+
`sanitizeFreeText`/`scanForSecret` (unchanged). No SQL injection surface (parameterized
|
|
18
|
+
statements inside the ledger). Bearer-auth required (same middleware as all non-/health routes).
|
|
19
|
+
|
|
20
|
+
## 4. Migration parity — do existing agents get it?
|
|
21
|
+
The route ships in code (no migration needed for the endpoint itself — it rides the existing
|
|
22
|
+
`ctx.frameworkIssueLedger` wiring that GET routes already use). Agent awareness IS migrated:
|
|
23
|
+
templates.ts registry row (new agents) + an idempotent, content-sniffed migrateClaudeMd
|
|
24
|
+
paragraph (existing agents) guarded on `framework-issues/observe`. Re-running the migration is
|
|
25
|
+
safe (content-sniff). The feature-delivery-completeness allowlist was updated so CI tracks it.
|
|
26
|
+
|
|
27
|
+
## 5. Could it spam / flood / burn resources?
|
|
28
|
+
No. A write is one SQLite upsert. There is no loop, no polling, no LLM call, no message send,
|
|
29
|
+
no spawn. Idempotent on dedupKey, so repeated imports collapse. No notification is emitted.
|
|
30
|
+
|
|
31
|
+
## 6. Rollback / off-switch?
|
|
32
|
+
The route is dormant when the ledger is unavailable (returns 503 — same as the GET routes).
|
|
33
|
+
Reverting the PR removes the route with zero residual state (any recorded rows remain valid
|
|
34
|
+
ledger data and are harmless; they can be left or pruned). No flags, no dark-launch needed —
|
|
35
|
+
the surface is inert until called.
|
|
36
|
+
|
|
37
|
+
## 7. Concurrency / multi-process safety?
|
|
38
|
+
The ledger uses WAL + busy_timeout=5000 (existing). The server is the in-process writer; the
|
|
39
|
+
backfill importer writes THROUGH the server's HTTP route (not a second DB handle), so there is
|
|
40
|
+
a single writer. The one-shot importer is run manually, not on a schedule. No new concurrency.
|
|
41
|
+
|
|
42
|
+
## Blast radius
|
|
43
|
+
Minimal. New additive HTTP route + one CLAUDE.md awareness paragraph + a one-shot importer
|
|
44
|
+
script + tests. No change to any existing route, gate, sentinel, schema, or decision path.
|