instar 1.3.450 → 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.
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +26 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/types.d.ts +47 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/messaging/slack/SlackAdapter.d.ts +8 -0
- package/dist/messaging/slack/SlackAdapter.d.ts.map +1 -1
- package/dist/messaging/slack/SlackAdapter.js +10 -0
- package/dist/messaging/slack/SlackAdapter.js.map +1 -1
- package/dist/monitoring/GrowthMilestoneAnalyst.d.ts +169 -0
- package/dist/monitoring/GrowthMilestoneAnalyst.d.ts.map +1 -0
- package/dist/monitoring/GrowthMilestoneAnalyst.js +500 -0
- package/dist/monitoring/GrowthMilestoneAnalyst.js.map +1 -0
- package/dist/permissions/AmbientContributionGate.d.ts +100 -0
- package/dist/permissions/AmbientContributionGate.d.ts.map +1 -1
- package/dist/permissions/AmbientContributionGate.js +85 -3
- package/dist/permissions/AmbientContributionGate.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +5 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +1 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +41 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +14 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts +5 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +77 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +47 -47
- package/src/scaffold/templates.ts +5 -0
- package/upgrades/1.3.451.md +57 -0
- package/upgrades/side-effects/growth-milestone-analyst-dev-gate.md +99 -0
- package/upgrades/side-effects/growth-milestone-analyst.md +92 -0
- package/upgrades/side-effects/slack-livetest-cleanups.md +75 -0
|
@@ -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.
|