instar 1.3.1305 → 1.3.1306

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.
Files changed (35) hide show
  1. package/dashboard/feedback-readiness-authority.js +155 -0
  2. package/dashboard/index.html +34 -0
  3. package/dist/core/IntelligenceRouter.d.ts +19 -0
  4. package/dist/core/IntelligenceRouter.d.ts.map +1 -1
  5. package/dist/core/IntelligenceRouter.js +37 -0
  6. package/dist/core/IntelligenceRouter.js.map +1 -1
  7. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  8. package/dist/core/PostUpdateMigrator.js +10 -0
  9. package/dist/core/PostUpdateMigrator.js.map +1 -1
  10. package/dist/data/standards-guard-index.json +1 -1
  11. package/dist/data/standards-guard-index.meta.json +2 -2
  12. package/dist/data/standards-registry.meta.json +1 -1
  13. package/dist/feedback-factory/drain/readinessAuthorityProposal.d.ts +70 -0
  14. package/dist/feedback-factory/drain/readinessAuthorityProposal.d.ts.map +1 -0
  15. package/dist/feedback-factory/drain/readinessAuthorityProposal.js +93 -0
  16. package/dist/feedback-factory/drain/readinessAuthorityProposal.js.map +1 -0
  17. package/dist/scaffold/templates.d.ts.map +1 -1
  18. package/dist/scaffold/templates.js +4 -0
  19. package/dist/scaffold/templates.js.map +1 -1
  20. package/dist/server/AgentServer.d.ts.map +1 -1
  21. package/dist/server/AgentServer.js +3 -1
  22. package/dist/server/AgentServer.js.map +1 -1
  23. package/dist/server/routes.d.ts +5 -0
  24. package/dist/server/routes.d.ts.map +1 -1
  25. package/dist/server/routes.js +44 -1
  26. package/dist/server/routes.js.map +1 -1
  27. package/package.json +1 -1
  28. package/src/data/builtin-manifest.json +64 -64
  29. package/src/data/standards-guard-index.json +1 -1
  30. package/src/data/standards-guard-index.meta.json +2 -2
  31. package/src/data/standards-registry.meta.json +1 -1
  32. package/src/scaffold/templates.ts +4 -0
  33. package/upgrades/1.3.1306.md +125 -0
  34. package/upgrades/side-effects/feedback-readiness-authority-card.md +105 -0
  35. package/upgrades/1.3.1305.md +0 -48
@@ -0,0 +1,125 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ The operated feedback drain refuses every tick with 403 "current registered
9
+ readiness agent required" until an operator registers the readiness
10
+ authority, which is the record that says which model may decide which
11
+ feedback reports become work items. Registering was possible only through
12
+ the PIN-gated `POST /feedback-factory/readiness-authorities` with about a
13
+ dozen technical fields, and there was no screen for it. On the Mac Studio
14
+ this left 1,001 reports waiting.
15
+
16
+ - New read-only `GET /feedback-factory/readiness-authorities/proposal`
17
+ (Bearer). The server computes every field the runtime later compares: the
18
+ agent id (`projectName`, which the drain job sends), the drain's owner
19
+ machine and epoch, the provider and model that the intelligence router
20
+ would actually pick for the readiness call, and the prompt, schema and
21
+ decision-point ids. Only the envelope can be edited: batch size (default
22
+ 50), daily spend cap (default $5), and token limit (default 1200). The
23
+ response also carries the status (none / active / proposal-only /
24
+ revoked), any plain-language blockers, and which action an Approve tap
25
+ would perform.
26
+ - The existing PIN route now accepts `useProposal: true` for create and
27
+ replace. When it is set, the server fills the binding fields from its own
28
+ proposal and takes only the envelope from the request. It stays PIN-only
29
+ and same-origin, and the agent still cannot register itself.
30
+ - New `IntelligenceRouter.previewPrimary()`, a read-only preview of the
31
+ primary (framework, model) that `evaluate()` would choose. It follows the
32
+ same enforced-nature or category logic.
33
+ - New dashboard card on the **Feedback Drain** tab: "Who decides which
34
+ feedback becomes work". It shows a one-line status, a plain sentence
35
+ ("…up to 50 reports per batch, at most $5 per day. Anything outside that
36
+ comes to you."), an "Adjust limits" drawer, a PIN box, and Approve (or
37
+ Restore / Save new limits) and Revoke buttons.
38
+ - CLAUDE.md template and migration: new "Feedback Readiness Authority
39
+ (operator approval)" section, also mirrored to Codex/Gemini agents.
40
+
41
+ The worktree monitor posted the same two notices ("Worktree activity detected
42
+ after session …" and "Stale worktrees detected") after almost every job
43
+ session and every five-minute scan. On 29 September that was 549 of 724
44
+ outbound messages, all about the same unmerged branches. It now remembers the
45
+ set of branches it last announced for each notice and posts only when that set
46
+ changes: a branch appears, or one resolves. A set that stays open is repeated
47
+ once a day. The remembered set is saved in
48
+ `state/worktree-monitor/announced.json`, so a restart does not post it again.
49
+ A failed send is not recorded, so the next scan retries it.
50
+
51
+ The topic-intent capture loop no longer sends `provenance: automation`
52
+ messages to the extractor LLM. That covers scheduler job sends and monitor,
53
+ sentinel and system notices. Those messages used about 64% of the extractor's
54
+ input tokens, and most of what they stored was repeated "unmerged branches"
55
+ facts. The check uses the structural provenance stamp set when a message is
56
+ sent, never the message text. User and agent conversation is extracted as
57
+ before, and so are unstamped legacy rows. The rolling topic summary still
58
+ includes automated messages, so the next conversational extraction still sees
59
+ them as context.
60
+
61
+ ## What to Tell Your User
62
+
63
+ The feedback drain has been waiting for your go-ahead before it sorts
64
+ reports into work items. You can now give it from your phone: open the
65
+ dashboard, go to the Feedback Drain tab, and find the card "Who decides
66
+ which feedback becomes work". It says in one sentence what you're agreeing
67
+ to (by default, up to 50 reports per batch and at most $5 a day). Enter
68
+ your PIN and tap Approve. You can revoke it from the same card at any time.
69
+ I can't approve it myself.
70
+
71
+ You'll see far fewer worktree reminders. A notice now means something changed,
72
+ and a branch that is still open gets one reminder a day.
73
+
74
+ ## Summary of New Capabilities
75
+
76
+ - Dashboard → Feedback Drain → "Who decides which feedback becomes work":
77
+ approve, change limits, restore or revoke the readiness authority with
78
+ the PIN.
79
+ - `GET /feedback-factory/readiness-authorities/proposal`: the read-only
80
+ answer to "what exactly would be approved, and why can't it be yet?"
81
+
82
+ - Worktree notices are sent only when the flagged set changes, with one reminder a day; `/hooks/worktrees/last-report` shows when an unchanged finding was suppressed.
83
+ - Automated messages are no longer run through topic-intent extraction.
84
+
85
+ ## Evidence
86
+
87
+ - `tests/integration/feedback-readiness-authority-routes.test.ts`, run
88
+ against a real AgentServer over HTTP. The test reads the proposal, checks
89
+ that a tick 403s, and checks that Bearer without a PIN and a wrong PIN are
90
+ both refused. It then approves with the PIN while also sending
91
+ attacker-supplied `agentId`/`provider`, which are ignored. The next tick
92
+ returns 202 and the run succeeds. The arbiter is called once and its
93
+ model check passes, so the authority stays `active`. It then revokes,
94
+ confirms the tick 403s again, and confirms an out-of-range batch is
95
+ refused.
96
+ - `tests/unit/intelligence-router-preview-primary.test.ts`: the preview
97
+ equals what the real `evaluate()` reports through `onModel` in four modes:
98
+ unconfigured, category override, enforced nature plan, and dryRun nature
99
+ plan. It returns null when the routed framework is unavailable.
100
+ - `tests/unit/feedback-readiness-authority-proposal.test.ts` covers every
101
+ status→action branch, the blockers and the envelope bounds. It also
102
+ replays real recorded shapes from the Mac Studio's `feature-metrics.db`
103
+ (221 codex-cli `gpt-6-astra` `capable` calls). An authority built from the
104
+ proposal passes the real arbiter on that shape and is refused on the
105
+ `gpt-5.6-sol` retirement-fallback shape.
106
+ - `tests/unit/feedback-readiness-authority-ui.test.ts`: covers the card's
107
+ buttons per state, the request bodies (no binding fields are sent), the
108
+ PIN and envelope submitted on Approve, and that values are rendered with
109
+ textContent only. It also checks that the page mounts the card, imports
110
+ the module, and renders the card on tab open but not on the 15-second
111
+ poll.
112
+ - `tests/e2e/feedback-factory-drain-lifecycle.test.ts`: on the production
113
+ init path the route answers 200, tracks the post-failover owner epoch,
114
+ offers no Approve when no routed model exists, and refuses the new path
115
+ without a PIN.
116
+
117
+ - A replay of the real 29 September message log through the new
118
+ `captureTurn`: 549 worktree notices and 64 other automated rows skipped; all
119
+ 111 agent and 19 user rows extracted. That is 130 extractor calls instead of
120
+ 743.
121
+ - A replay of the same day's worktree notice texts under the new rule: 11 of
122
+ 549 would be sent, each because the flagged set changed.
123
+ - Unit, integration (`/hooks/worktrees/last-report`, `/telegram/reply` →
124
+ capture metrics) and e2e tests cover both sides. The new tests fail when
125
+ the new logic is disabled.
@@ -0,0 +1,105 @@
1
+ # Side-Effects Review — Readiness authority approval from the dashboard
2
+
3
+ **Version / slug:** `feedback-readiness-authority-card`
4
+ **Date:** `2026-09-30`
5
+ **Author:** `Echo`
6
+ **Second-pass reviewer:** `independent reviewer subagent (see below)`
7
+
8
+ ## Summary of the change
9
+
10
+ The operated feedback drain on the Mac Studio is live-healthy with 1,001 unprocessed reports, but every tick 403s ("current registered readiness agent required"). The readiness authority can only be registered through the PIN-gated `POST /feedback-factory/readiness-authorities`, which has no human surface and needs a dozen technical fields. The operator (Justin, topic 95267, 2026-09-30 10:26 PDT) approved registering it with maxBatch 50 and maxDailySpendUsd 5. This change adds the operator surface:
11
+
12
+ - `src/feedback-factory/drain/readinessAuthorityProposal.ts` (new, pure): builds the record the runtime will accept. It also returns status, blockers, the Approve action and a plain summary.
13
+ - `src/core/IntelligenceRouter.ts`: new read-only `previewPrimary()` that mirrors `evaluateRouted`'s primary selection.
14
+ - `src/server/routes.ts`: new `GET /feedback-factory/readiness-authorities/proposal` (Bearer, read-only); `useProposal: true` on the existing PIN route makes the server fill the binding fields.
15
+ - `src/server/AgentServer.ts`: exposes `authorityBinding()`, the owner machine and epoch that `canAgentMutateReadiness` compares.
16
+ - `dashboard/feedback-readiness-authority.js` (new) + `dashboard/index.html`: the card on the Feedback Drain tab.
17
+ - CLAUDE.md template, migration, and shadow marker for Agent Awareness.
18
+
19
+ ## Decision-point inventory
20
+
21
+ - `POST /feedback-factory/readiness-authorities` authorization (PIN + X-Instar-Request + same-origin): **pass-through**. It is unchanged and still runs before any new code.
22
+ - `FeedbackDrainStore.mutateAuthority` validation: **pass-through**. The proposal mirrors its envelope bounds for display, and the store remains the authority.
23
+ - `FeedbackDrainService.canAgentMutateReadiness` and `FeedbackReadinessArbiter.decideBatch` provider/model/prompt checks: **pass-through**. Nothing loosens them; the proposal computes values that satisfy them.
24
+
25
+ ---
26
+
27
+ ## 1. Over-block
28
+
29
+ No new block/allow surface. The proposal's `blockers` only decide whether the card offers Approve, and the PIN route re-checks the same conditions and refuses with 409 when `useProposal` cannot build a record. One case: an operator who deliberately wants to register while no routed model exists (for example, before enabling a framework) cannot use the card. The raw-field PIN route still works for that expert path.
30
+
31
+ ## 2. Under-block
32
+
33
+ - The proposal predicts the primary route. If the call later lands elsewhere (the Codex retirement self-heal reports `gpt-5.6-sol`; a failure swap to another framework), the arbiter's existing check refuses and demotes the authority to `proposal-only`. The card then shows "Paused by a safety brake" and offers Approve (replace). This is the designed fail-safe direction and is replayed in `feedback-readiness-authority-proposal.test.ts`. The operator may need to approve again after a model retirement.
34
+ - Found during this build and not fixed here: the live `Config.ts` loader drops `sessions.natureRouting`, so the Studio runs nature routing observe-only even though its config says `dryRun:false`. `previewPrimary` reads the same runtime closure, so the proposal matches the real call (codex-cli / `gpt-6-astra`). If that loader bug is fixed, the next tick after a routing change demotes the authority, and the card offers a re-approval bound to the new route. Fixing the loader would make MessageSentinel's FAST chain fail closed (pi-cli is not enabled), so it is a separate decision. It is reported to the operator in the PR summary.
35
+
36
+ ## 3. Level-of-abstraction fit
37
+
38
+ This is the right layer. The server already owns the facts: the owner binding in AgentServer, routing in IntelligenceRouter, and the prompt/schema ids in the arbiter module. Computing them server-side and letting the operator choose only the envelope keeps the human decision where it belongs. `previewPrimary` sits on the router beside `for()` (the existing diagnostic resolver) instead of re-implementing routing in the route handler.
39
+
40
+ ## 4. Signal vs authority compliance
41
+
42
+ - [x] No — this change has no block/allow surface.
43
+
44
+ The proposal is advisory display. Authority stays with the PIN (human), the store's validation, and the arbiter's runtime checks, none of which changed.
45
+
46
+ ## 4b. Judgment-point check
47
+
48
+ No new static heuristic at a competing-signals decision point. The status→action mapping follows the store's own state machine: a revoked authority must be restored before it can be replaced, and create is refused if a record already exists.
49
+
50
+ ## 5. Interactions
51
+
52
+ - **Shadowing:** `useProposal` is read only after `feedbackMutationIntentValid` passes. Revoke and restore ignore it and reuse the stored record as before.
53
+ - **Double-fire:** the card renders on tab open and after an action, not on the 15-second status poll, so a PIN being typed is never wiped. The status poll is unchanged.
54
+ - **Races:** two approvals at once are serialized by the store's `.immediate()` transaction. The second create gets 409 "authority already exists", which the card shows verbatim.
55
+ - **Feedback loops:** none. The proposal reads state and writes nothing.
56
+
57
+ ## 6. External surfaces
58
+
59
+ - **Operator surface (Mobile-Complete):** this change adds the missing phone surface. It works from the tunnel URL with the dashboard PIN, and the POST is same-origin.
60
+ - Other agents and users: the new route is Bearer-only and read-only. Without the drain (fleet default) it returns 503, like its siblings.
61
+ - Persistent state: nothing new. Approvals write the same `authority_records` and `authority_audit` rows as before, with an `operatorDecisionRef` of the form `dashboard:<timestamp>:<random>`.
62
+
63
+ ## 6b. Operator-surface quality
64
+
65
+ 1. **Leads with the primary action?** Yes. The card shows its title, a one-line status, the plain sentence of what is being approved, the PIN box, then Approve. The numeric limits sit in a collapsed "Adjust limits" drawer because the approved defaults (50 / $5) are prefilled.
66
+ 2. **Zero raw internals as primary content?** Yes. The machine id, epoch and model appear only as a muted "Bound to:" line inside the collapsed drawer. Statuses read "Not set up yet", "Active (version 1): up to 50 reports per batch…", "Paused by a safety brake", "Revoked". No enum values are shown.
67
+ 3. **Destructive actions de-emphasized?** Yes. Revoke is a plain button after the green Approve / Save new limits, never above it, and appears only when there is something to revoke.
68
+ 4. **Plain language + phone width?** Plain language is checked in the UI tests. The card reuses the Spend tab's mobile-first classes: 44px tap targets, 16px inputs (no iOS zoom), full-width stacked fields. I did not take a separate phone-width screenshot of this card.
69
+
70
+ ## 7. Multi-machine posture
71
+
72
+ **Machine-local by design, proxied on the owner.** The authority binds to the drain's canonical owner machine and epoch, and `authorityBinding()` reports this server's view: the configured `operatedHostMachineId` and the coordinator lease epoch. On a non-owner machine, `ownerMachineId` is still the configured owner, so a record approved there binds to the owner. The store is per machine, though, so the approval must be made on the owner's dashboard; the tick proxy already routes ticks to the owner. The card emits no user notices and generates no URLs. Its only durable state is the existing authority tables, which are already in the drain backup/restore path.
73
+
74
+ ## 8. Rollback cost
75
+
76
+ This is a pure code change. Reverting removes the card, the proposal route, `useProposal` and `previewPrimary`. Records approved through the card are ordinary authority generations and keep working, and they can be revoked through the raw PIN route. No migration is needed. The CLAUDE.md section would remain in existing agents' files, pointing at a card that no longer exists, until a follow-up migration removes it by the same marker.
77
+
78
+ ## Conclusion
79
+
80
+ The review changed the design in three ways. Approval uses `useProposal` (server-derived fields) rather than posting client-side copies of technical fields. The card renders outside the 15-second poll. The model binding comes from a router preview that is proven equal to the real `evaluate()` selection, rather than from the static routing map. The static map would have proposed `gpt-5.5`, a retired id that the live call never uses, and the first tick would have demoted the authority. The change is clear to ship. The natureRouting loader drop is flagged to the operator as a separate finding.
81
+
82
+ ---
83
+
84
+ ## Second-pass review (if required)
85
+
86
+ **Reviewer:** independent reviewer subagent
87
+ **Independent read of the artifact: concur**
88
+
89
+ The reviewer found no path for a Bearer-only caller to register, widen or restore. `useProposal` is read only after the PIN, origin and header checks pass, and the store's envelope bounds still apply. They confirmed that `previewPrimary` matches `evaluateRouted` on the enforced, fail-closed, no-route and category paths. They also confirmed that the binding equals what `canAgentMutateReadiness` and the tick job compare. They raised three minor points:
90
+ - Restore ignores the edited limits. Fixed: the card now says Restore brings back the last approved limits, and a UI test covers it.
91
+ - An emptied limit field turned into 0. Fixed: empty fields are omitted, so the server default applies (JS and GET), with a UI test.
92
+ - A `claude-code` subscription-path agent reports `interactive-pool` as its model, so the arbiter would pause the authority (a false pause, fail-safe). This is noted here, and it does not affect the Studio route.
93
+
94
+ ---
95
+
96
+ ## Evidence pointers
97
+
98
+ - `tests/integration/feedback-readiness-authority-routes.test.ts`: before approval the tick 403s; after the PIN approval it returns 202 with a succeeded run and the authority stays active.
99
+ - `tests/unit/intelligence-router-preview-primary.test.ts`: the preview equals the real `onModel` in four routing modes.
100
+ - `tests/unit/feedback-readiness-authority-proposal.test.ts`: replays the recorded live shape codex-cli/`gpt-6-astra` (Mac Studio `.instar/server-data/feature-metrics.db`, 221 rows) and the `gpt-5.6-sol` retirement-fallback shape.
101
+ - `tests/e2e/feedback-factory-drain-lifecycle.test.ts`: the route is alive on the production init path.
102
+
103
+ ## Class-Closure Declaration (display-only mirror)
104
+
105
+ No agent-authored-artifact defect — not applicable. No self-triggered controller is added.
@@ -1,48 +0,0 @@
1
- # Upgrade Guide — vNEXT
2
-
3
- <!-- assembled-by: assemble-next-md -->
4
- <!-- bump: patch -->
5
-
6
- ## What Changed
7
-
8
- The worktree monitor posted the same two notices ("Worktree activity detected
9
- after session …" and "Stale worktrees detected") after almost every job
10
- session and every five-minute scan. On 29 September that was 549 of 724
11
- outbound messages, all about the same unmerged branches. It now remembers the
12
- set of branches it last announced for each notice and posts only when that set
13
- changes: a branch appears, or one resolves. A set that stays open is repeated
14
- once a day. The remembered set is saved in
15
- `state/worktree-monitor/announced.json`, so a restart does not post it again.
16
- A failed send is not recorded, so the next scan retries it.
17
-
18
- The topic-intent capture loop no longer sends `provenance: automation`
19
- messages to the extractor LLM. That covers scheduler job sends and monitor,
20
- sentinel and system notices. Those messages used about 64% of the extractor's
21
- input tokens, and most of what they stored was repeated "unmerged branches"
22
- facts. The check uses the structural provenance stamp set when a message is
23
- sent, never the message text. User and agent conversation is extracted as
24
- before, and so are unstamped legacy rows. The rolling topic summary still
25
- includes automated messages, so the next conversational extraction still sees
26
- them as context.
27
-
28
- ## What to Tell Your User
29
-
30
- You'll see far fewer worktree reminders. A notice now means something changed,
31
- and a branch that is still open gets one reminder a day.
32
-
33
- ## Summary of New Capabilities
34
-
35
- - Worktree notices are sent only when the flagged set changes, with one reminder a day; `/hooks/worktrees/last-report` shows when an unchanged finding was suppressed.
36
- - Automated messages are no longer run through topic-intent extraction.
37
-
38
- ## Evidence
39
-
40
- - A replay of the real 29 September message log through the new
41
- `captureTurn`: 549 worktree notices and 64 other automated rows skipped; all
42
- 111 agent and 19 user rows extracted. That is 130 extractor calls instead of
43
- 743.
44
- - A replay of the same day's worktree notice texts under the new rule: 11 of
45
- 549 would be sent, each because the flagged set changed.
46
- - Unit, integration (`/hooks/worktrees/last-report`, `/telegram/reply` →
47
- capture metrics) and e2e tests cover both sides. The new tests fail when
48
- the new logic is disabled.