instar 1.3.449 → 1.3.451

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 (45) hide show
  1. package/.claude/skills/autonomous/SKILL.md +42 -2
  2. package/dist/config/ConfigDefaults.d.ts.map +1 -1
  3. package/dist/config/ConfigDefaults.js +26 -0
  4. package/dist/config/ConfigDefaults.js.map +1 -1
  5. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  6. package/dist/core/PostUpdateMigrator.js +17 -6
  7. package/dist/core/PostUpdateMigrator.js.map +1 -1
  8. package/dist/core/types.d.ts +47 -0
  9. package/dist/core/types.d.ts.map +1 -1
  10. package/dist/core/types.js.map +1 -1
  11. package/dist/messaging/slack/SlackAdapter.d.ts +8 -0
  12. package/dist/messaging/slack/SlackAdapter.d.ts.map +1 -1
  13. package/dist/messaging/slack/SlackAdapter.js +10 -0
  14. package/dist/messaging/slack/SlackAdapter.js.map +1 -1
  15. package/dist/monitoring/GrowthMilestoneAnalyst.d.ts +169 -0
  16. package/dist/monitoring/GrowthMilestoneAnalyst.d.ts.map +1 -0
  17. package/dist/monitoring/GrowthMilestoneAnalyst.js +500 -0
  18. package/dist/monitoring/GrowthMilestoneAnalyst.js.map +1 -0
  19. package/dist/permissions/AmbientContributionGate.d.ts +100 -0
  20. package/dist/permissions/AmbientContributionGate.d.ts.map +1 -1
  21. package/dist/permissions/AmbientContributionGate.js +85 -3
  22. package/dist/permissions/AmbientContributionGate.js.map +1 -1
  23. package/dist/scaffold/templates.d.ts.map +1 -1
  24. package/dist/scaffold/templates.js +5 -0
  25. package/dist/scaffold/templates.js.map +1 -1
  26. package/dist/server/AgentServer.d.ts +1 -0
  27. package/dist/server/AgentServer.d.ts.map +1 -1
  28. package/dist/server/AgentServer.js +41 -0
  29. package/dist/server/AgentServer.js.map +1 -1
  30. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  31. package/dist/server/CapabilityIndex.js +14 -0
  32. package/dist/server/CapabilityIndex.js.map +1 -1
  33. package/dist/server/routes.d.ts +5 -0
  34. package/dist/server/routes.d.ts.map +1 -1
  35. package/dist/server/routes.js +77 -0
  36. package/dist/server/routes.js.map +1 -1
  37. package/package.json +1 -1
  38. package/src/data/builtin-manifest.json +65 -65
  39. package/src/scaffold/templates.ts +5 -0
  40. package/upgrades/1.3.450.md +24 -0
  41. package/upgrades/1.3.451.md +57 -0
  42. package/upgrades/side-effects/autonomous-legitimate-stops.md +108 -0
  43. package/upgrades/side-effects/growth-milestone-analyst-dev-gate.md +99 -0
  44. package/upgrades/side-effects/growth-milestone-analyst.md +92 -0
  45. package/upgrades/side-effects/slack-livetest-cleanups.md +75 -0
@@ -0,0 +1,57 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ Added **`GrowthMilestoneAnalyst`** — a new proactive growth & milestone analyst
9
+ (`src/monitoring/`). It composes the existing tracking surfaces (`InitiativeTracker`
10
+ rollout stages + staleness, `ApprovalLedger` approve-vs-change, `CorrectionLedger`
11
+ recurrence) into one opinionated digest with five explicit notify-rules:
12
+
13
+ - **R1** promotion-ready (a feature past its incubation window that proved itself)
14
+ - **R2** incubation-expired-unproven (past window, never proved itself → extend/repair/retire)
15
+ - **R3** initiative-stalling (reuses `tracker.digest()`)
16
+ - **R4** spec approve-vs-change pattern
17
+ - **R5** recurring-correction pattern
18
+
19
+ The key lever is a TIGHT incubation window (3d low-risk / 7d standard) whose **expiry
20
+ is itself the trigger**, so a feature can never be silently left behind. Promotion
21
+ requires real proof-of-life, never elapsed time alone (a feature whose evidence
22
+ source isn't wired is surfaced honestly as `proved:'unknown'` and can never be
23
+ promotion-ready).
24
+
25
+ Wires four read-only routes — `GET /growth/digest`, `GET /growth/findings`,
26
+ `GET /growth/status`, `POST /growth/tick` — plus a `monitoring.growthAnalyst` config
27
+ block (defaults OFF). Ships **DARK**: this slice computes and exposes findings only;
28
+ it does NOT send to Telegram. Cadence/event-delivery and enabling the muted
29
+ analyzers ride a later, flood-sensitive slice.
30
+
31
+ ## What to Tell Your User
32
+
33
+ Nothing yet — this ships dark and disabled by default, and is agent-only. It does not
34
+ message the user, change any existing behavior, or turn on any new surface until it
35
+ is explicitly enabled in a later promotion slice. When asked "is anything checking on
36
+ my initiatives, feature maturity, or patterns?", you can note the analyst exists and
37
+ is being rolled out carefully from dark to live, and that you will be able to read its
38
+ computed digest once it is turned on.
39
+
40
+ ## Summary of New Capabilities
41
+
42
+ - New monitoring component `GrowthMilestoneAnalyst` (observe-only; no new sensors).
43
+ - New read routes `GET /growth/digest|findings|status`, `POST /growth/tick`
44
+ (503 when disabled — the dark default).
45
+ - New config block `monitoring.growthAnalyst` (ships disabled; tunable incubation
46
+ windows, proof-of-life threshold, per-rule flags) — auto-applies to existing
47
+ agents via ConfigDefaults deep-merge.
48
+ - CLAUDE.md template documents the routes (Agent Awareness Standard).
49
+
50
+ ## Evidence
51
+
52
+ Not a bug fix and not reproducible in dev — this is a new, dark-by-default capability,
53
+ so there is no before/after to reproduce. It changes no existing behavior: with the
54
+ flag off (the default) every new route returns 503 and nothing else references the
55
+ analyst. Behavior is covered by 41 tests across all three tiers (unit semantic
56
+ coverage on both sides of every window/proof boundary, integration route gating,
57
+ wiring against a real InitiativeTracker, and an e2e "feature is alive" lifecycle).
@@ -0,0 +1,108 @@
1
+ # Side-Effects Review — Autonomous "Legitimate Stop Conditions" allowlist
2
+
3
+ **Version / slug:** `autonomous-legitimate-stops`
4
+ **Date:** `2026-06-09`
5
+ **Author:** `Instar Agent (echo)`
6
+ **Second-pass reviewer:** `not required`
7
+
8
+ ## Summary of the change
9
+
10
+ Adds a new top-level section to the autonomous skill's instruction file — **"## Legitimate Stop Conditions (the ONLY valid reasons to exit)"** — enumerating the only three valid reasons a pre-approved autonomous session may exit: (a) a genuine hard external blocker the agent cannot resolve, (b) duration expiry, (c) the completion condition/promise genuinely met. It pairs that with an explicit NON-stops table (reversible decisions, milestones, late-hour, "needs your steer/opinion," "good stopping point," quiet off-ramp-with-no-reply) and reinforces the existing Anti-Patterns list with two new entries ("This Needs Your Steer" and "Quiet Off-Ramp"). Files touched: `.claude/skills/autonomous/SKILL.md` (the bundled shipping source — new agents get it via `init`/`installAutonomousSkill`), `src/core/PostUpdateMigrator.ts` (bumps the existing `migrateAutonomousStopHookTopicKeyed` SKILL.md marker from `PER-TOPIC (setup-race hardening)` to `LEGITIMATE_STOP_CONDITIONS` so existing agents receive the section on update), and `tests/unit/PostUpdateMigrator-autonomousStopHook.test.ts` (four new migration tests). This is prompt/skill CONTENT plus its Migration-Parity migration — NOT a change to any decision-point code logic.
11
+
12
+ ## Decision-point inventory
13
+
14
+ The change touches **no runtime decision point.** It does NOT modify the autonomous stop hook's blocking logic (`autonomous-stop-hook.sh`), the completion-condition judge, the emergency-stop check, or any gate/sentinel/watchdog. The stop hook continues to decide exit exactly as before; this change only edits the prose the agent reads and adds a content-sniffing migration that re-deploys that prose to existing installs.
15
+
16
+ - `migrateAutonomousStopHookTopicKeyed` (SKILL.md upgrade arm) — modify (marker bump only) — re-deploys the bundled SKILL.md to existing agents whose installed copy lacks the new sentinel.
17
+
18
+ ---
19
+
20
+ ## 1. Over-block
21
+
22
+ **What legitimate inputs does this change reject that it shouldn't?**
23
+
24
+ No block/allow surface — over-block not applicable. The migration's `upgrade()` helper writes a file only when the installed copy lacks the new marker AND matches the stock fingerprint (`ALL_TASKS_COMPLETE`); a customized SKILL.md is left untouched (reported as `skipped`), so it cannot "over-write" operator customizations.
25
+
26
+ ---
27
+
28
+ ## 2. Under-block
29
+
30
+ **What failure modes does this still miss?**
31
+
32
+ No block/allow surface — under-block not applicable. As prose guidance, the section is advisory: the structural stop hook is what actually enforces continuation. An agent could still rationalize an early stop despite the guidance — but that is the stop-hook's job to catch (out of scope here, tracked as the separate Tier-2 stop-hook logic change). This change strengthens the guidance the agent reads each iteration; it does not claim to be the enforcement layer.
33
+
34
+ ---
35
+
36
+ ## 3. Level-of-abstraction fit
37
+
38
+ **Is this at the right layer?**
39
+
40
+ Yes. This is skill CONTENT (prompt-level guidance), which is the correct layer for "here are the only valid reasons to exit." The structural enforcement (the stop hook) is a separate, lower layer that this content complements. The migration rides the EXISTING `migrateAutonomousStopHookTopicKeyed` path — the established, correct home for autonomous SKILL.md content updates — rather than introducing a parallel migration. The marker-bump mechanism is the same content-sniffing pattern already used for the prior SKILL.md fixes (per-topic write, registration-path fix), so it reuses rather than re-implements.
41
+
42
+ ---
43
+
44
+ ## 4. Signal vs authority compliance
45
+
46
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
47
+
48
+ **Does this change hold blocking authority with brittle logic?**
49
+
50
+ - [x] No — this change has no block/allow surface.
51
+
52
+ This is documentation/skill-content plus an idempotent file-deployment migration. It makes no allow/block decision, holds no authority over message flow or session lifecycle, and adds no brittle detector. The migration only writes a markdown file under content-sniffing guards.
53
+
54
+ ---
55
+
56
+ ## 5. Interactions
57
+
58
+ **Does this interact with existing checks, recovery paths, or infrastructure?**
59
+
60
+ - **Shadowing:** None. The migration's SKILL.md arm shares the same `upgrade()` helper as the stop-hook and setup-script arms within `migrateAutonomousStopHookTopicKeyed`; each arm targets a distinct file path, so they cannot shadow each other. The marker bump on the SKILL.md arm does not affect the hook/setup arms (independent markers).
61
+ - **Double-fire:** None. `installAutonomousSkill()` (init path) is install-if-missing and won't overwrite; the migration is the single content-update path. They do not both write the same file in the same run (init only writes when absent).
62
+ - **Races:** None. The migration runs synchronously inside the PostUpdateMigrator sequence at update time; no shared concurrent state.
63
+ - **Feedback loops:** None.
64
+
65
+ One real interaction verified: the existing test `PostUpdateMigrator-autonomousStopHook.test.ts` already exercises this migration; the marker bump from `PER-TOPIC (setup-race hardening)` to `LEGITIMATE_STOP_CONDITIONS` required no change to the existing hook/setup test cases (those assert their own markers, not the SKILL.md marker), and the new SKILL.md tests were added alongside. Verified the bundled SKILL.md contains BOTH the new marker (so `next.includes(marker)` passes and the upgrade actually writes) AND the fingerprint.
66
+
67
+ ---
68
+
69
+ ## 6. External surfaces
70
+
71
+ **Does this change anything visible outside the immediate code path?**
72
+
73
+ - Other agents on the same machine: only their autonomous SKILL.md content updates on their next PostUpdateMigrator run — the intended Migration-Parity effect.
74
+ - Other users of the install base: same — they get the new guidance on update.
75
+ - External systems (Telegram, Slack, GitHub, Cloudflare): none.
76
+ - Persistent state: none beyond rewriting the one markdown file the migration targets (only when stale + stock).
77
+ - Timing/runtime conditions: none.
78
+
79
+ ---
80
+
81
+ ## 7. Rollback cost
82
+
83
+ **If this turns out wrong in production, what's the back-out?**
84
+
85
+ Pure content change — revert the SKILL.md edit and the marker bump, ship as the next patch. No persistent state to clean up, no data migration, no agent-state repair. Existing agents that already received the re-deployed SKILL.md would simply receive the reverted version on a subsequent marker bump (or keep the harmless prose section). No user-visible regression during the rollback window.
86
+
87
+ ---
88
+
89
+ ## Conclusion
90
+
91
+ This review confirms the change is content-only with no decision-point surface. It adds the operator-requested "Legitimate Stop Conditions" allowlist + NON-stops table to the autonomous skill, ships it to new agents via the bundled source, and reaches existing agents via the established content-sniffing SKILL.md migration arm (marker bump, idempotent, customization-preserving). No design changes were required by the review. Clear to ship pending human review.
92
+
93
+ ---
94
+
95
+ ## Second-pass review (if required)
96
+
97
+ **Reviewer:** not required
98
+
99
+ Tier 1, content-only, no block/allow or session-lifecycle decision logic touched — the Phase 5 second-pass triggers do not apply.
100
+
101
+ ---
102
+
103
+ ## Evidence pointers
104
+
105
+ - `npx tsc --noEmit` → exit 0 (clean).
106
+ - `npx vitest run tests/unit/PostUpdateMigrator-autonomousStopHook.test.ts` → 12 passed (8 prior + 4 new SKILL.md cases).
107
+ - `npx vitest run tests/unit/autonomous-skill-deployment.test.ts tests/unit/migration-parity.test.ts tests/unit/PostUpdateMigrator-autonomousHookPath.test.ts tests/unit/autonomous-stop-hook-idle-backoff.test.ts` → 52 passed.
108
+ - Bundled SKILL.md verified to contain both `LEGITIMATE_STOP_CONDITIONS` (marker) and `ALL_TASKS_COMPLETE` (fingerprint).
@@ -0,0 +1,99 @@
1
+ # Side-Effects Review — GrowthMilestoneAnalyst dev-agent gate correction
2
+
3
+ **Change:** Make the GrowthMilestoneAnalyst honor the standard `developmentAgent`
4
+ dark-feature gate instead of a hardcoded `enabled: false`. The original slice-1
5
+ implementation shipped dark for **every** agent (including dev agents like echo),
6
+ which contradicts the established convention used by secret-sync, boot
7
+ self-knowledge, the resource sampler, warm-session A2A, etc.: dark-features OMIT
8
+ `enabled` from their config default and resolve `enabled ?? !!config.developmentAgent`
9
+ at runtime so the dev agent (the dogfooding ground) runs them LIVE while the fleet
10
+ stays DARK.
11
+
12
+ **Files:**
13
+ - `src/config/ConfigDefaults.ts` — removed the hardcoded `enabled: false` from the
14
+ `monitoring.growthAnalyst` block (and rewrote the comment to describe the gate).
15
+ - `src/server/AgentServer.ts` — construction now resolves
16
+ `growthAnalystEnabled = monitoring?.growthAnalyst?.enabled ?? !!developmentAgent`,
17
+ and feeds that resolved value into `resolveGrowthSettings({ ...block, enabled })`
18
+ so `GET /growth/status` honestly reports `enabled: true` on a dev agent. The
19
+ `resolveGrowthSettings` call is now optional-chained so the gate can be true even
20
+ with no `monitoring.growthAnalyst` block present (dev agent, defaults only).
21
+ - `tests/unit/growth-analyst-gate-wiring.test.ts` — new wiring-integrity tests
22
+ (config omits `enabled`, migration parity, both-sides resolution, source-level
23
+ gate assertions).
24
+
25
+ ## 1. Over-block — what legitimate inputs does this reject that it shouldn't?
26
+ None. The change ENABLES the feature on dev agents; it never rejects an input. An
27
+ operator who explicitly sets `monitoring.growthAnalyst.enabled: false` still
28
+ force-darks the feature even on a dev agent (explicit value wins via `??`), and an
29
+ explicit `true` still enables it fleet-wide — both preserved and tested.
30
+
31
+ ## 2. Under-block — what failure modes does this still miss?
32
+ The analyst remains slice-1: it COMPUTES + serves the digest over `/growth/*` but
33
+ does NOT message the user on a schedule (that's the deliberately-separate next
34
+ slice). So a dev agent now runs the analyst live but still won't proactively speak
35
+ until the messaging slice lands. This is by design (avoid swinging from too-quiet
36
+ to too-noisy), not a regression introduced here. No new under-block.
37
+
38
+ ## 3. Level-of-abstraction fit — right layer?
39
+ Yes. The gate resolution lives in `AgentServer` construction exactly where every
40
+ other dark-feature gate resolves (`standard_development_agent_dark_feature_gate`),
41
+ and the default omission lives in `ConfigDefaults` exactly like warmSessionA2A and
42
+ boot self-knowledge. No new layer; it joins the existing one.
43
+
44
+ ## 4. Signal vs authority compliance (docs/signal-vs-authority.md)
45
+ Compliant. The GrowthMilestoneAnalyst is a SIGNAL-PRODUCER: it reads rollout
46
+ stages, the approval ledger, and the correction ledger and computes findings. It
47
+ holds NO blocking authority — it never filters a message, blocks an action, or
48
+ gates information flow. This change only determines WHETHER that signal-producer
49
+ runs on a given agent; it adds no brittle-check-with-authority. The routes remain
50
+ read-only.
51
+
52
+ ## 5. Interactions — shadowing, double-fire, races?
53
+ None. The construction is guarded by its own try/catch (failure is non-fatal and
54
+ null-instances the analyst → `/growth/*` 503-stubs). No PostUpdateMigrator entry
55
+ writes `growthAnalyst`, so the gate is the sole resolver — there's no migration
56
+ that could write `enabled` to disk and shadow the runtime gate. The
57
+ `resolveGrowthSettings({ ...block, enabled })` merge does not mutate config.
58
+
59
+ ## 6. External surfaces — visible to other agents/users/systems?
60
+ On the dev agent only, `/growth/digest`, `/growth/findings`, `/growth/status`,
61
+ `POST /growth/tick` flip from 503 to 200 after the next server boot (Claude Code
62
+ loads config once at session/server start — running processes pick it up on
63
+ restart). Fleet agents are unaffected (still 503). No agent-to-agent surface, no
64
+ Telegram surface (the analyst does not message in this slice). The
65
+ `/growth/status` `enabled` field now reads `true` on a dev agent (was a latent
66
+ `false` had the routes ever been forced live) — this is the honesty fix.
67
+
68
+ ## 7. Rollback cost — back-out if wrong?
69
+ Trivial and reversible. Two ways: (a) operator sets
70
+ `monitoring.growthAnalyst.enabled: false` in `.instar/config.json` and restarts —
71
+ force-darks it immediately, no code change; (b) git revert of this commit restores
72
+ the hardcoded `enabled: false`. No data migration, no state repair — the analyst's
73
+ only durable artifact is `state/growth-milestone-analyst/stage-journal.json`, which
74
+ is observe-only and self-heals (a missing/stale journal just recomputes on the next
75
+ observe). Worst case is a dev agent serving an unused read route.
76
+
77
+ ## No deferrals
78
+ This change is complete: the gate is wired, the default is corrected, the status
79
+ honesty is fixed, and the wiring tests cover both sides. Nothing is deferred.
80
+
81
+ ## Second-pass review (independent)
82
+ **VERDICT: Concur with the review.** An independent reviewer audited the diff,
83
+ the analyst source, the routes, and the migrators:
84
+ - Read-only/signal-only confirmed: `computeFindings`/`buildDigest`/`getStatus`
85
+ never gate on `settings.enabled`; `isEnabled()` only feeds the informational
86
+ status field; routes guard solely on `if (!ctx.growthMilestoneAnalyst)` — so
87
+ constructing the analyst is sufficient to make it live, and the component holds
88
+ no blocking authority.
89
+ - Gate matches the established convention (byte-identical to the resource
90
+ sampler's `?? !!options.config.developmentAgent`); explicit `enabled` wins both
91
+ ways; both sides tested.
92
+ - No fleet leak / no disk write: non-dev agents resolve to false → null → 503;
93
+ grep of PostUpdateMigrator returned zero `growthAnalyst` references, so no
94
+ migration writes `enabled` to disk; `applyDefaults` only backfills missing
95
+ fields (operator `enabled:false` survives).
96
+ - No optional-chaining/merge bug: `resolveGrowthSettings({ ...(block ?? {}),
97
+ enabled })` is safe when the block is absent and cannot throw; the spread sets
98
+ `enabled` last so it deterministically wins; no needed field dropped.
99
+ - All seven side-effects claims match the code; new test file passes 6/6.
@@ -0,0 +1,92 @@
1
+ # Side-Effects Review — Growth & Milestone Analyst (slice 1)
2
+
3
+ **Version / slug:** `growth-milestone-analyst`
4
+ **Date:** `2026-06-08`
5
+ **Author:** `echo`
6
+ **Tier:** `1` (ships dark, flag-off, compute + read-only, fully reversible)
7
+ **Second-pass reviewer:** `not required`
8
+
9
+ ## Summary of the change
10
+
11
+ Adds `GrowthMilestoneAnalyst` (`src/monitoring/`) — a pure observer that composes
12
+ the existing `InitiativeTracker` (rollout stages + staleness), `ApprovalLedger`
13
+ (approve-vs-change), and `CorrectionLedger` (recurrence) into one digest with five
14
+ explicit notify-rules (R1 promotion-ready, R2 incubation-expired-unproven, R3
15
+ initiative-stalling, R4 spec-pattern, R5 correction-pattern). It keeps ONE piece of
16
+ internal bookkeeping — a stage-observation journal — so "days in stage" is robust
17
+ where the rollout engines do not stamp it. Wires four read routes (`GET
18
+ /growth/digest|findings|status`, `POST /growth/tick`), a `monitoring.growthAnalyst`
19
+ config block (defaults OFF), and the AgentServer construction (guarded; null →
20
+ routes 503). Ships DARK: compute + read only, no Telegram sending.
21
+
22
+ ## Decision-point inventory
23
+
24
+ - **`classifyRollout`** (the window-expiry verdict): in-window / at-boundary /
25
+ expired × proved / unproved / unknown × stage. Both sides of every edge are unit
26
+ tested.
27
+ - **Per-rule enable gates** (`settings.rules.*`): each rule suppresses iff its flag
28
+ is false.
29
+ - **Construction gate** in AgentServer: analyst built only when
30
+ `monitoring.growthAnalyst.enabled === true` AND a tracker + stateDir exist.
31
+
32
+ ## 1. Over-block
33
+
34
+ **What legitimate inputs does this change reject?** Nothing user-facing is rejected.
35
+ The only "rejection" is structural: when the feature is OFF (the default), every
36
+ `/growth/*` route returns 503 — by design, mirroring the established dark-feature
37
+ contract (e.g. `correctionLearning`). No existing route, job, message, or gate
38
+ changes behavior. The analyst never blocks, delays, or rewrites anything.
39
+
40
+ ## 2. Under-block
41
+
42
+ **What does this still miss?** Three deliberate gaps, all deferred to later slices:
43
+ (a) no Telegram/event delivery — it computes but does not speak, so a real milestone
44
+ won't reach the operator until the sending slice; (b) proof-of-life
45
+ (`evidenceCounter`) is unwired, so every expiry currently classifies as
46
+ R2-unknown rather than R1 until a per-feature activation source is plumbed — this is
47
+ surfaced honestly as `proved:'unknown'`, never masked; (c) the muted analyzers
48
+ (`correctionLearning`, `failureLearning`) are NOT enabled here — that flood-sensitive
49
+ step is its own slice.
50
+
51
+ ## 3. Level-of-abstraction fit
52
+
53
+ **Right layer?** Yes. The analyst lives in `src/monitoring/` beside the other
54
+ observe-only components (TokenLedger, ResourceLedger). It reads through the engines'
55
+ public methods (`tracker.list/digest`, `approvalLedger.summarize`,
56
+ `correctionLedger.list`) rather than reaching into their storage. The stage journal
57
+ is analyst-private state under `stateDir/state/growth-milestone-analyst/`.
58
+
59
+ ## 4. Reversibility
60
+
61
+ Fully reversible: the feature ships behind `monitoring.growthAnalyst.enabled`
62
+ (default false). Disabling it (or removing the construction block) returns the
63
+ system to byte-identical prior behavior — the routes 503, nothing else references
64
+ the analyst. The only persisted artifact is a small JSON journal that is pruned and
65
+ self-heals.
66
+
67
+ ## 5. Anti-flood discipline (relevant because this REVERSES an over-silence)
68
+
69
+ The whole point is to speak more, so overshooting into a new flood is the risk to
70
+ guard. This slice does not send at all. The digest is structurally ONE object — a
71
+ burst of N window-expiries aggregates into one digest with counts, never N messages
72
+ (unit-tested at N=500). When the sending slice lands it must route through the
73
+ existing budget-guarded attention/post-update surfaces and aggregate, never create
74
+ one topic per feature. That guardrail is stated here so the next slice inherits it.
75
+
76
+ ## 6. Framework generality
77
+
78
+ Not framework-specific. The analyst is a server-side monitoring component that reads
79
+ instar's own ledgers/trackers; it does not spawn sessions, call an LLM, or route
80
+ through any agentic framework (Claude/Codex/Gemini/pi). It behaves identically
81
+ regardless of which framework the agent runs on. No per-framework branching exists
82
+ or is needed.
83
+
84
+ ## 7. Migration parity
85
+
86
+ - Config defaults auto-apply to existing agents via `ConfigDefaults` +
87
+ `applyDefaults` deep-merge (the established no-separate-migrateConfig path for a
88
+ new `monitoring.*` sub-key).
89
+ - The CLAUDE.md template (`generateClaudeMd`) documents the routes for NEW agents.
90
+ - A `migrateClaudeMd` content-sniff + the scheduled digest job are deferred to the
91
+ promotion slice — a dark, route-only feature needs no live agent-awareness
92
+ migration until it actually speaks.
@@ -0,0 +1,75 @@
1
+ # Side-Effects Review — Slack live-test cleanups (#2 ambient-silence observability, #5 spec/type alignment)
2
+
3
+ **Version / slug:** `slack-livetest-cleanups`
4
+ **Date:** `2026-06-09`
5
+ **Author:** `Instar Agent (echo)`
6
+ **Second-pass reviewer:** `not required` (Tier-1; observability-only + doc-accuracy; no decision authority touched)
7
+
8
+ ## Summary of the change
9
+
10
+ Two small cleanups the Slack-org live-test plan flagged.
11
+
12
+ **Cleanup #2 (observability):** the `AmbientContributionGate` previously only left a trace when it SPOKE (the speak-path `console.log` + the `onDecision` log fires on `speak:true`). A wrongful SILENCE left no measurable record, so the ambient false-positive (wrongful-silence) / false-negative rate could not be measured during the observe-only live test. This adds a bounded, in-memory aggregate inside the gate — per-channel `{evaluated, spoke, silent, nearMissSilent, silentByReason}` plus a bounded ring of the most-recent near-miss silences — read via a new `getStats()` method, a `SlackAdapter.getAmbientStats()` passthrough, and a read-only `GET /permissions/ambient-stats` route (reads the live `ctx.slack` instance). Files: `src/permissions/AmbientContributionGate.ts`, `src/messaging/slack/SlackAdapter.ts`, `src/server/routes.ts`, plus unit + integration tests.
13
+
14
+ **Cleanup #5 (doc accuracy):** `SLACK-ORG-INTEGRATION-SPEC.md` named a `respondMode: 'considered'` third mode, but the shipped `SlackRespondMode` type is `'all' | 'mention-only'`; ambient rides on `ambientContribution.enabledChannelIds` ON TOP of `mention-only`. The spec (§5.2 and the §15 migration-parity bullet) is rewritten to match the shipped reality — the lower-risk fix. No type value added; no code path needed it.
15
+
16
+ ## Decision-point inventory
17
+
18
+ - `AmbientContributionGate.shouldSpeak` (the speak/silence decision) — **pass-through** — the verdict is computed exactly as before; the aggregate is updated AFTER the verdict is formed, inside `decide()`, wrapped in try/catch, and `decide()` returns the decision unchanged. The recording cannot alter the outcome.
19
+ - `GET /permissions/ambient-stats` — **add** — read-only observability surface; performs no mutation and no messaging.
20
+
21
+ ---
22
+
23
+ ## 1. Over-block
24
+
25
+ **What legitimate inputs does this change reject that it shouldn't?**
26
+
27
+ No block/allow surface — over-block not applicable. The aggregate is signal-only and the new route is read-only. The speak/silence decision is unchanged (a dedicated regression test asserts identical verdicts with the aggregate present).
28
+
29
+ ---
30
+
31
+ ## 2. Under-block
32
+
33
+ **What failure modes does this still miss?**
34
+
35
+ No block/allow surface — under-block not applicable. The aggregate is an in-memory FP-measurement counter, not a gate. It is reset on restart, which is acceptable for an FP-rate-measurement surface (the durable per-decision record is the file-backed `/permissions/decisions`). Worst case is a momentary loss of accumulated counts after a restart — never an over- or under-speak.
36
+
37
+ ---
38
+
39
+ ## 3. Level-of-abstraction fit
40
+
41
+ **Is this at the right layer?**
42
+
43
+ Yes. The aggregate lives ON the gate that produces the decisions it counts, so every decision (including the previously-invisible silences) is observed at the single chokepoint `decide()` — no parallel detector, no double-counting. The route reads the live `ctx.slack` adapter (already in `RouteContext`), mirroring how the existing observe-only `/permissions/*` surfaces expose internal state. A durable counter could replace the in-memory map later without changing the contract.
44
+
45
+ ---
46
+
47
+ ## 4. Signal vs authority compliance
48
+
49
+ **Does this hold blocking authority with brittle logic, or produce a signal?**
50
+
51
+ Pure signal. Per `docs/signal-vs-authority.md`: the aggregate is a detector/observability surface with ZERO authority — it never blocks, delays, or rewrites a message, and it cannot change the speak/silence verdict (recording runs after the verdict and returns it unchanged; a stats bug is swallowed by try/catch). The ambient gate's own fail-to-silence authority is untouched.
52
+
53
+ ---
54
+
55
+ ## 5. Interactions
56
+
57
+ **Does it shadow another check, get shadowed, double-fire, or race?**
58
+
59
+ No. The new counters are independent of the existing `onDecision` hook and the speak-path `console.log` (both retained). No timer, no shared mutable state with another component. The `recordSpoke()` rate-limit budget is a separate concern and is not affected. The new route shares the `/permissions/*` prefix, already allowlisted as dark/internal in `CapabilityIndex` (line ~1023), so it does not trip the route-discoverability scan.
60
+
61
+ ---
62
+
63
+ ## 6. External surfaces
64
+
65
+ **Does it change anything visible to other agents / users / systems?**
66
+
67
+ Only the new authenticated read-only route `GET /permissions/ambient-stats` (returns `{ present: false }` when no ambient gate is attached — the default). It creates NO Telegram message and NO forum topic, so it cannot contribute to a notification flood (verified: the notification-flood burst-invariant test still passes). It is dark unless a channel is opted into ambient contribution. No timing/conversation-state dependency.
68
+
69
+ ---
70
+
71
+ ## 7. Rollback cost
72
+
73
+ **If this turns out wrong in production, what's the back-out?**
74
+
75
+ Trivial. The aggregate is in-memory (no persistence, no migration, no agent-state repair) and the route is additive. Reverting the commit removes both with no residue. Cleanup #5 is a doc edit with zero runtime effect.