instar 1.2.26 → 1.2.28

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.
@@ -1,51 +1,58 @@
1
1
  # Upgrade Guide — vNEXT
2
2
 
3
- <!-- bump: patch -->
4
- <!-- patch = bug fixes, refactors, test additions, doc updates -->
3
+ <!-- bump: minor -->
4
+ <!-- minor = new capability (ORG-INTENT drift detection — Phase 4, completes the four-phase project). Aggregates fix(heal) from PR #316 too. -->
5
5
 
6
6
  ## What Changed
7
7
 
8
- **fix(heal): better-sqlite3 self-heal now survives Homebrew Node updates that strand process.execPath.**
8
+ **feat(org-intent-runtime): periodic drift detection job + analyzer (Phase 4 of 4 — completes the project).**
9
9
 
10
- When Homebrew (or any package manager) updates Node while an instar server is running, the brew install swaps the `/opt/homebrew/bin/node` symlink and removes the previous Cellar directory. The server process keeps an open file descriptor to the deleted binary, so it continues executing — but any new `spawnSync(process.execPath, ...)` call returns ENOENT. The native-module self-heal path runs through exactly that kind of spawn, so the heal silently fails. The agent then degrades to legacy-memory fallbacks and stays there across restarts (the next startup hits the same stale execPath).
10
+ The per-message Coherence Gate from Phase 1 catches individual constraint violations. The session-start injection from Phase 2 ensures the agent reasons with the contract from message one. The tradeoff helper from Phase 3 lets non-reviewer code paths resolve values collisions deterministically. But none of these catch the slow accumulation pattern — every individual message passes the gate, yet week-over-week the agent has gradually drifted toward optimizing for the wrong objective. That is the Klarna failure mode at action scale, and it is what this phase catches.
11
11
 
12
- This was observed live on luna 2026-05-21. Server log showed the heal firing at 02:46:00, then 150ms later: `"rebuild failed (spawnSync /opt/homebrew/Cellar/node/25.6.1/bin/node ENOENT). SQLite subsystems may degrade."` Knowledge graph, conversation summaries, and the pending-relay queue all went offline while a healthy Node (a different Cellar version) was sitting on disk a few millimetres away.
12
+ Three new surfaces, all signal-only:
13
13
 
14
- The fix:
14
+ - **`src/core/OrgIntentDriftAnalyzer.ts`** — pure function. Given recent Coherence Gate review history + parsed `ORG-INTENT.md`, produces a `DriftAnalysis` with a trend label (`stable` | `rising` | `concerning` | `insufficient-data` | `no-org-intent`), per-reviewer block-rate stats, half-window comparison, and cross-references against ORG-INTENT constraints / goals / values.
15
+ - **`GET /intent/org/drift?lookbackDays=N`** — HTTP route exposing the analyzer. Returns 503 when the response review pipeline is not enabled (graceful degradation).
16
+ - **Weekly job template** (`src/scaffold/templates/jobs/instar/org-intent-drift-audit.md`) — `enabled: false` by default. Operators with an authored `ORG-INTENT.md` can flip it on; the job runs every Monday at 10:00 local time, calls the route, and sends a Telegram heads-up only when `shouldSurface: true` (trend is rising or concerning). Most weeks stay silent — that means the agent is staying aligned.
15
17
 
16
- 1. New `resolveStableNodeBinary()` helper at `src/utils/resolveNodeBinary.ts` (with a CommonJS twin at `scripts/resolve-node-binary.cjs`). It walks a fallback chain — `process.execPath` → `fs.realpathSync(execPath)` → optional caller-supplied bundled Node → `/opt/homebrew/bin/node` (the stable symlink Homebrew always maintains) → `/usr/local/bin/node` → `/usr/bin/node` → `which node` — and returns the first existing-and-executable path, or null when every candidate fails.
18
+ Per `feedback_signal_vs_authority`: every surface in this phase is SIGNAL. The Coherence Gate from Phase 1 remains AUTHORITY. Nothing here writes to `ORG-INTENT.md`, modifies the gate's behavior, or blocks anything.
17
19
 
18
- 2. `ensureSqliteBindings()` in `src/commands/server.ts` now resolves the spawn target through `resolveStableNodeBinary()` before invoking either the bundled `fix-better-sqlite3.cjs` or the npm-rebuild fallback. If resolution returns null, a structured DegradationReporter event is emitted with an explicit recovery hint instead of the previous "rebuild failed (ENOENT)" log line.
20
+ This is the final phase of the four-phase ORG-INTENT runtime project that started with topic 11378 on 2026-05-21. With this release, `ORG-INTENT.md` is fully load-bearing infrastructure: enforced at message review (Phase 1), surfaced at session start (Phase 2), consultable for tradeoffs (Phase 3), and monitored for drift (Phase 4).
19
21
 
20
- 3. `scripts/fix-better-sqlite3.cjs` resolves once at module load and uses the resolved path for every internal spawn (testBinary probe, verifyChildAbiMatches defence, source-build via npm). `findNpmCli` also resolves the npm sibling relative to the resolved Node so a stale execPath doesn't poison npm discovery.
22
+ Spec: `docs/specs/ORG-INTENT-DRIFT-DETECTION-SPEC.md`. ELI16 companion: `docs/specs/ORG-INTENT-DRIFT-DETECTION-SPEC.eli16.md`. Side-effects review: `upgrades/side-effects/org-intent-drift-detection.md`.
21
23
 
22
- 4. When the rebuild itself still fails after resolution, `ensureSqliteBindings()` emits a second DegradationReporter event (`ensureSqliteBindings.rebuildFailed`) with a user-actionable recovery hint. Previously the failure was a single yellow log line; now it surfaces through the same degradation channel operators already monitor.
24
+ ---
23
25
 
24
- Fail-closed semantics preserved: if no Node binary is reachable anywhere, the heal does NOT proceed (the alternative — guessing wrong — risks producing an ABI-mismatched binary that fails silently later). The DegradationReporter event names the missing Node so the operator can fix it.
26
+ **Also included: fix(heal) — better-sqlite3 self-heal now survives Homebrew Node updates that strand `process.execPath`.**
25
27
 
26
- ## Evidence
28
+ When Homebrew (or any package manager) updates Node while an instar server is running, the brew install swaps the `/opt/homebrew/bin/node` symlink and removes the previous Cellar directory. The server process keeps an open file descriptor to the deleted binary, so it continues executing — but any new `spawnSync(process.execPath, ...)` call returns ENOENT. The native-module self-heal path runs through exactly that kind of spawn, so the heal silently fails. The agent then degrades to legacy-memory fallbacks and stays there across restarts (the next startup hits the same stale execPath).
27
29
 
28
- Failure mode reproduced and verified to stop:
30
+ This was observed live on luna 2026-05-21. The fix adds a `resolveStableNodeBinary()` helper that walks a fallback chain — `process.execPath` → `fs.realpathSync(execPath)` → optional caller-supplied bundled Node → `/opt/homebrew/bin/node` (the stable symlink Homebrew always maintains) → `/usr/local/bin/node` → `/usr/bin/node` → `which node` — and returns the first existing-and-executable path. `ensureSqliteBindings()` in `src/commands/server.ts` and `scripts/fix-better-sqlite3.cjs` both route through the resolver before spawning. Fail-closed semantics preserved: if no Node binary is reachable, the heal does not proceed and a structured DegradationReporter event surfaces.
29
31
 
30
- Reproduction (`tests/integration/heal-execpath-staleness.test.ts`) covers six scenarios drawn directly from the luna incident:
32
+ ## What to Tell Your User
31
33
 
32
- 1. `spawnSync` against a stale Cellar path (`/opt/homebrew/Cellar/node/0.0.0-removed-by-brew/bin/node`) returns ENOENT — the same shape as the luna server log line.
33
- 2. The resolver returns a working fallback when execPath is stale. A child process spawned against the resolved path executes successfully.
34
- 3. `fix-better-sqlite3.cjs` resolves a stable Node binary at module load (resolver and script both present, exported helpers callable).
35
- 4. The resolver fails closed when no Node is reachable anywhere — does not silently pick a phantom path.
36
- 5. The resolver rejects non-executable files even when they exist at the requested path (defence against `chmod -x` or write-only files).
34
+ If you have an organizational intent file authored AND you opt into the new weekly job, you'll get a Telegram heads-up on Monday mornings when your agent's outbound-message review block rate is rising or concerning. Most weeks stay silent — that means the agent is staying aligned with your organizational intent. The heads-up tells you the trend, what flagged most, and one or two suggestions for where to look. It surfaces patterns; it doesn't fix anything autonomously. The fix is your call.
37
35
 
38
- Plus seven unit tests in `tests/unit/resolveNodeBinary.test.ts` covering the full fallback chain branch-by-branch.
36
+ If you haven't authored an organizational intent file, nothing changes. This is a zero-cost upgrade for those agents.
39
37
 
40
- The live fix on luna (manually-invoked `fix-better-sqlite3.cjs` via the resolved Node binary) brought her degradation count from 8 to 1, with SemanticMemory, TopicMemory, FeatureRegistry, and the pending-relay queue all returning to healthy. With this PR landed, the same recovery will run automatically on every server restart that detects the mismatch.
38
+ This is the last of four phases in the ORG-INTENT runtime project. With this release, `ORG-INTENT.md` is fully load-bearing infrastructure: enforced at message review, surfaced at session start, consultable for tradeoffs, and monitored for drift.
41
39
 
42
- ## What to Tell Your User
43
-
44
- If your agent ever logged "rebuild failed (spawnSync ... ENOENT)" after a Homebrew Node update — most often visible as your knowledge graph or conversation summaries going offline while the agent still appears to be running — this fix lets the agent recover on the next restart without manual help. The heal now tries multiple Node paths instead of just the one the server happened to launch with, so a brew-swept-out-from-under-us Cellar path no longer blocks rebuild.
40
+ A second change in this release (the heal fix): if your agent ever logged "rebuild failed (spawnSync ... ENOENT)" after a Homebrew Node update — most often visible as your knowledge graph or conversation summaries going offline while the agent still appears to be running — this fix lets the agent recover on the next restart without manual help.
45
41
 
46
42
  ## Summary of New Capabilities
47
43
 
48
- | Capability | How to Use |
49
- |-----------|-----------|
50
- | Heal survives stale `process.execPath` | automatic on next server restart |
51
- | DegradationReporter event on heal failure | automatic — surfaces through existing `/degradations` and any consumers watching that channel |
44
+ - **GET /intent/org/drift** — new HTTP route returning a drift digest derived from recent Coherence Gate review history.
45
+ - **`analyzeOrgIntentDrift()` exported function** — pure logic helper for any callsite that wants the same analysis.
46
+ - **Weekly drift audit job** — agentmd template, off by default, sends a Telegram heads-up only when drift surfaces.
47
+ - **Stable-Node-resolver self-heal** — automatic on next server restart; surfaces a structured DegradationReporter event if no Node binary is reachable.
48
+ - **Migration parity** — existing agents' CLAUDE.md gains a Phase 4 curl line automatically.
49
+
50
+ ## Evidence
51
+
52
+ - Tier 1 unit tests: `tests/unit/OrgIntentDriftAnalyzer.test.ts` (11 new tests, all passing). `tests/unit/PostUpdateMigrator-org-intent-runtime.test.ts` extended with 1 new test for Phase 4 migration (9 total, all passing).
53
+ - Tier 2 integration tests: `tests/integration/org-intent-routes.test.ts` extended with 1 new test for the drift route (17 total, all passing).
54
+ - Tier 3 E2E lifecycle tests: `tests/e2e/org-intent-drift-lifecycle.test.ts` (4 tests mirroring production wiring through `AgentServer` and `createRoutes` with a real `CoherenceGate` seeded with synthetic history, all passing — including the "feature is alive" 200-not-503 check).
55
+ - Heal fix evidence: `tests/integration/heal-execpath-staleness.test.ts` reproduces the luna failure mode (6 scenarios) + 7 unit tests in `tests/unit/resolveNodeBinary.test.ts` covering the resolver fallback chain branch-by-branch. The live fix on luna brought her degradation count from 8 to 1.
56
+ - Type-check: `npx tsc --noEmit` clean.
57
+ - Lint: clean.
58
+ - The full test suite must remain green before merge per Zero-Failure Standard.
@@ -0,0 +1,40 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: minor -->
4
+ <!-- minor = new capability (ORG-INTENT drift detection — Phase 4, code already on main but unreleased due to a rebase artifact). -->
5
+
6
+ ## What Changed
7
+
8
+ **chore(release): cut Phase 4 of the ORG-INTENT runtime project.**
9
+
10
+ Phase 4 (periodic drift detection) merged on main in PR #319 but did not publish to npm at that time. The Phase 4 NEXT.md content was absorbed into `upgrades/1.2.26.md` (the heal-fix release) during the rebase that resolved the conflict between my branch and the just-cut release. The Phase 4 CODE — `OrgIntentDriftAnalyzer`, the `GET /intent/org/drift` route, the weekly audit job template, and the CLAUDE.md migration — has been sitting on main since PR #319 merged but is not yet on npm.
11
+
12
+ This PR is a fresh NEXT.md to trigger the publish workflow. No code changes — Phase 4 is already in the tree.
13
+
14
+ After this publishes, the complete four-phase ORG-INTENT runtime project will be available on npm:
15
+
16
+ - Phase 1 (v1.2.23): gate-time enforcement of constraints.
17
+ - Phase 2 (v1.2.24): session-start injection of the three-rule contract.
18
+ - Phase 3 (v1.2.25): deterministic tradeoff helper.
19
+ - Phase 4 (this release): periodic drift detection + weekly audit job.
20
+
21
+ Spec: `docs/specs/ORG-INTENT-DRIFT-DETECTION-SPEC.md`.
22
+ ELI16 companion: `docs/specs/ORG-INTENT-DRIFT-DETECTION-SPEC.eli16.md`.
23
+ Side-effects review: `upgrades/side-effects/org-intent-drift-detection.md`.
24
+
25
+ ## What to Tell Your User
26
+
27
+ The drift detection capability that was supposed to ship in the prior release was a release-pipeline misfire — the code landed but the publish skipped. This release re-cuts that work so the new endpoint and the weekly audit job become available through the normal instar update path. No behavior change from the prior release if you don't act on it; if you have an organizational intent file authored and want the weekly drift check, ask your agent to enable the new drift-audit job for you.
28
+
29
+ ## Summary of New Capabilities
30
+
31
+ (All shipped in PR #319 — this release just publishes them.)
32
+
33
+ - **GET /intent/org/drift** — HTTP route returning a drift digest derived from recent Coherence Gate review history.
34
+ - **`analyzeOrgIntentDrift()` exported function** — pure logic helper.
35
+ - **Weekly drift audit job template** — `.instar/jobs/instar/org-intent-drift-audit.md`, off by default.
36
+ - **Migration parity** — CLAUDE.md gains Phase 4 curl line automatically on next update.
37
+
38
+ ## Evidence
39
+
40
+ All test evidence is in PR #319's release notes (now in `upgrades/1.2.26.md`). This PR is a no-op release cut.
@@ -0,0 +1,38 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: patch -->
4
+ <!-- patch = bug fixes, refactors, test additions, doc updates -->
5
+
6
+ ## What Changed
7
+
8
+ **docs(coverage): second-pass refresh — route + class category bump, sidebar update, floor ratchet.**
9
+
10
+ Follow-on to the docs-coverage script and per-PR gate (v1.2.21) and the bulk docs refresh (v1.2.22). This release bumps the two categories that were still near the floor: routes (fifteen percent up to fifty-nine) and classes (fifteen percent up to sixty-two).
11
+
12
+ Five concrete moves:
13
+
14
+ 1. **Route inventory appendix in `reference/api.md`.** Every registered route grouped by prefix, listed by HTTP method and path. The curated sections at the top stay as the front; the inventory is the grep-friendly back.
15
+
16
+ 2. **Class inventory appendix in `architecture/under-the-hood.md`.** Every top-level class shipped under each source subsystem, grouped under subsystem headers. Navigation aid, not a substitute for the per-subsystem feature pages.
17
+
18
+ 3. **Four new feature pages** for the subsystems that previously had zero documentation home: paste handling, privacy routing, the Self-Healing Remediator, and task flows.
19
+
20
+ 4. **Sidebar update** in `site/astro.config.mjs` so the new pages plus the ones from round one (Slack, observability, cross-framework portability, coherence gate, the living system, threadline protocol wire format) actually appear in site navigation when the deploy works.
21
+
22
+ 5. **Floor ratchet** in the docs-coverage script. New floors: overall 55, route 55, command 60, job 85, hook 70, skill 90, class 55.
23
+
24
+ Spec: `docs/specs/docs-coverage-bump.md`. ELI16: `docs/specs/docs-coverage-bump.eli16.md`. Side-effects review: `upgrades/side-effects/docs-coverage-bump.md`.
25
+
26
+ ## What to Tell Your User
27
+
28
+ Nothing user-visible. This release further hardens instar's own documentation pipeline. Agents continue to behave identically.
29
+
30
+ Contributors will notice the docs-coverage CI check has a tighter bar — pull requests now need to keep coverage above the fifty-five percent overall floor and similar per-category floors. Most pull requests pass naturally; a pull request that adds many features without doc updates will get flagged with the offending category named.
31
+
32
+ ## Summary of New Capabilities
33
+
34
+ This release is pure infrastructure plus documentation. No new runtime capabilities for agents. The new feature pages cover already-shipped subsystems that lacked user-facing documentation.
35
+
36
+ ## Evidence
37
+
38
+ Coverage script run against current main before and after the changes in this release: before, overall twenty percent, route fifteen percent, class fifteen percent. After, overall sixty-two percent, route fifty-nine percent, class sixty-two percent. All seven categories sit comfortably above their new floors. The script passes its --check mode with the ratcheted thresholds. Lint clean. Local Astro build produces forty-six pages including the four new feature pages.
@@ -0,0 +1,77 @@
1
+ # Side-effects review — docs-coverage second pass
2
+
3
+ Spec: `docs/specs/docs-coverage-bump.md`
4
+ ELI16: `docs/specs/docs-coverage-bump.eli16.md`
5
+ Companion to: the first round (`docs/specs/docs-coverage.md`) and the weekly audit (`docs/specs/docs-coverage-weekly.md`).
6
+
7
+ ## Surface map
8
+
9
+ | Change | File(s) | Type |
10
+ |---|---|---|
11
+ | Floor ratchet | `scripts/docs-coverage.mjs` | Numeric edit to 7 default floors |
12
+ | New feature pages | `site/src/content/docs/features/{paste-handling,privacy-routing,remediator,task-flows}.md` | Pure docs |
13
+ | Route inventory | `site/src/content/docs/reference/api.md` (appended section) | Pure docs |
14
+ | Class inventory | `site/src/content/docs/architecture/under-the-hood.md` (appended section) | Pure docs |
15
+ | Sidebar additions | `site/astro.config.mjs` | Static-site nav config |
16
+ | Sidebar update for prior round | (same file) | Adds slack/observability/portability/coherence-gate/the-living-system/threadline-protocol that landed in v1.2.22 but weren't in the sidebar |
17
+
18
+ No production code touched. No agent behavior changes. No state migrations.
19
+
20
+ ## Over-block analysis
21
+
22
+ **Could the floor ratchet block a legitimate PR?**
23
+
24
+ The new floors are set 3-8 percentage points below current measured coverage, which is the same buffer used in the first round. A PR that does normal feature work without doc updates would consume some of that buffer; a PR that adds many features without docs would consume more. The ratchet is calibrated to allow normal churn but require *some* doc work for new features. That's the intended behavior.
25
+
26
+ Escape hatches:
27
+ - Per-category env overrides for one-off PRs (`INSTAR_DOCS_COVERAGE_ROUTE_MIN=0` etc.)
28
+ - The bypass requires explicit env-var-setting, so it's visible in CI logs
29
+
30
+ **Could the route inventory misrepresent the API?**
31
+
32
+ The inventory is auto-generated from `src/server/routes.ts` by walking `router.<verb>(...)` registrations. Same regex the docs-coverage script uses. The inventory matches what the server actually registers; any drift would be a script bug affecting both the inventory and the gate.
33
+
34
+ The curated sections above the inventory still apply for non-obvious routes. Routes that need careful documentation (auth requirements, schema, error semantics) live in the curated section; routes whose path-and-method tell the story live in the inventory.
35
+
36
+ ## Under-block analysis
37
+
38
+ The route inventory and class inventory provide *coverage*, not *understanding*. A reader pulling a class name out of the inventory still has to read the source file to know what the class does. This is a deliberate trade-off — the alternative is months of detailed prose for hundreds of items, most of which are internal.
39
+
40
+ The four new feature pages do provide understanding. They cover the highest-value gaps from the Pass 6 audit findings.
41
+
42
+ ## Level-of-abstraction fit
43
+
44
+ The new feature pages live in `site/src/content/docs/features/` next to the others. Their depth is calibrated to user-facing concerns (how to use the system, when to choose it) rather than implementation details.
45
+
46
+ The inventories live in the appendix position of their respective pages — at the bottom, after the curated content. Readers who want detail get it from the curated content; readers who want navigation get it from the inventory.
47
+
48
+ The floor ratchet lives in the same numeric block as the original calibration, with the same env-var override pattern. No new config surface, just bigger numbers.
49
+
50
+ ## Signal-vs-authority compliance
51
+
52
+ The floor ratchet is **authoritative** — it gates CI. The inventories are **signals** — they show what exists without claiming completeness of explanation. The four new feature pages are content, not signal/authority distinctions.
53
+
54
+ ## Interactions with existing systems
55
+
56
+ - **Per-PR docs-coverage gate** — sees higher floors, behaves identically.
57
+ - **Weekly audit job** — observes the higher coverage, won't surface drift unless coverage *drops*.
58
+ - **CI workflow** — runs against the same script with the same arguments.
59
+ - **Astro Starlight site** — picks up the new sidebar entries on next deploy.
60
+
61
+ ## Rollback cost
62
+
63
+ Trivially reversible. The script's threshold block is a single object literal; reverting is a one-line restore. The new feature pages can be deleted; the inventories can be removed (they're each delimited by a top-level horizontal rule).
64
+
65
+ ## Risk summary
66
+
67
+ - **Low risk of regression.** Pure docs and one numeric edit.
68
+ - **Low risk of friction.** The new floors are below current state; normal PRs won't hit them. The escape hatch exists for the rare case where they would.
69
+ - **No risk of data loss.** No state mutation.
70
+
71
+ ## Verification done before commit
72
+
73
+ - `node scripts/docs-coverage.mjs --check` passes with the new floors.
74
+ - Coverage report shows overall 62%, route 59%, class 62% — all above new floors.
75
+ - All four new feature pages render correctly in the local Astro build (verified — `npx astro build` produces 46 pages including the four new ones).
76
+ - Sidebar order verified.
77
+ - Spec carries `approved: true` per direct principal authorization in Telegram topic 11235.
@@ -0,0 +1,101 @@
1
+ # Side-effects review — org-intent drift detection (Phase 4)
2
+
3
+ Spec: `docs/specs/ORG-INTENT-DRIFT-DETECTION-SPEC.md`
4
+ ELI16: `docs/specs/ORG-INTENT-DRIFT-DETECTION-SPEC.eli16.md`
5
+ Phase: 4 of 4 (final). Phases 1, 2, and 3 shipped in prior PRs.
6
+
7
+ ## Surface map
8
+
9
+ | Change | File | Type |
10
+ |---|---|---|
11
+ | Pure `analyzeOrgIntentDrift()` + `DriftAnalysis` type | `src/core/OrgIntentDriftAnalyzer.ts` (new file) | Additive module |
12
+ | New `GET /intent/org/drift` HTTP route | `src/server/routes.ts` | Additive route |
13
+ | Weekly drift-audit agentmd job template, `enabled: false` | `src/scaffold/templates/jobs/instar/org-intent-drift-audit.md` (new file) | Job template — distributed via `installBuiltinJobs()` |
14
+ | CLAUDE.md ORG-INTENT subsection adds Phase 4 curl line | `src/scaffold/templates.ts` + `src/core/PostUpdateMigrator.ts` | Doc + migration |
15
+ | Tier 1 unit tests (analyzer + migration) | `tests/unit/OrgIntentDriftAnalyzer.test.ts` (new), `tests/unit/PostUpdateMigrator-org-intent-runtime.test.ts` (extended) | Test addition |
16
+ | Tier 2 integration test | `tests/integration/org-intent-routes.test.ts` (extended) | Test addition |
17
+ | Tier 3 E2E test | `tests/e2e/org-intent-drift-lifecycle.test.ts` (new) | Test addition |
18
+
19
+ ## Over-block analysis
20
+
21
+ **Could the new route or analyzer block anything?**
22
+
23
+ No. Pure read path. The analyzer takes a snapshot of recent review history (already retained by the gate per its existing retention policy) and produces a derived digest. The route returns the digest. Nothing writes to `ORG-INTENT.md` or to the gate's history. Per `feedback_signal_vs_authority`: SIGNAL only.
24
+
25
+ **Could the weekly job spam an operator?**
26
+
27
+ The job ships `enabled: false`. Operators must explicitly opt in. Once enabled, the digest is only sent when `shouldSurface: true` (trend = `rising` or `concerning`). The default thresholds (15% concerning, 5% rising-delta) are calibrated so most weeks stay silent.
28
+
29
+ **Could the analyzer flag false positives?**
30
+
31
+ Yes — by design. The trend labels are heuristic. A week with a single bad day might surface as `rising` even if the underlying pattern is noise. This is acceptable because:
32
+
33
+ - The job's output is a Telegram message, not a code change. The operator reads it on their phone and decides.
34
+ - The suggestions are advisory, not directive. "Check whether the constraints need updating" — not "I updated the constraints."
35
+ - False negatives (missing real drift) are worse than false positives (occasional noise on a stable agent). The defaults err toward surfacing.
36
+
37
+ Operators who want different thresholds can pass `lookbackDays` to the route or update the job template's body to override.
38
+
39
+ ## Under-block analysis
40
+
41
+ **What does the analyzer NOT catch?**
42
+
43
+ - Drift that doesn't show up in gate review history. If the agent does something off-mission that the gate's reviewers don't flag (e.g. answer the wrong question instead of violating a constraint), the analyzer has no signal to work with. This is the natural limit of any system that operates on gate output.
44
+ - Channel-specific patterns. The analyzer aggregates across channels. A surge of blocks on Telegram but stable performance on direct/CLI would show up as overall rising. Future iteration may add per-channel breakdowns.
45
+ - Recovery patterns. If the agent drifted in week 1 and corrected in week 2, the half-window comparison would show falling rates, but the analyzer doesn't surface "drift detected but recovered" as a positive signal. Out of scope for v1.
46
+
47
+ ## Level-of-abstraction fit
48
+
49
+ The analyzer is a pure function over a typed input. The route is a thin wrapper. The job is an agentmd template that just calls the route and renders. No new abstractions introduced. The decomposition mirrors `IntentDriftDetector` (the existing decision-journal-based detector) but operates on a different input space (gate review history vs decision journal), so they're complementary rather than overlapping.
50
+
51
+ ## Signal-vs-authority compliance
52
+
53
+ Every surface in this phase is **SIGNAL**:
54
+
55
+ - The analyzer produces a digest, never blocks.
56
+ - The route returns the digest, never blocks.
57
+ - The job sends a Telegram message, never blocks.
58
+
59
+ The Coherence Gate from Phase 1 remains **AUTHORITY**. It continues to enforce constraints at message-review time. This phase consumes its output as one input among several; it never modifies the gate's behavior.
60
+
61
+ This is the right architectural pattern per `feedback_signal_vs_authority`. The drift surface can be wrong without compromising any actual outcome — the worst case is "operator gets a false-alarm Telegram message," not "agent loses a constraint enforcement."
62
+
63
+ ## Interactions with existing systems
64
+
65
+ | System | Interaction | Risk |
66
+ |---|---|---|
67
+ | Coherence Gate (Phase 1) | Reads `getReviewHistory()` | None — read-only |
68
+ | Session-start injection (Phase 2) | None — different surface | None |
69
+ | Tradeoff helper (Phase 3) | None — orthogonal | None |
70
+ | Decision journal `IntentDriftDetector` | Sibling system on a different input space | None — they observe different things |
71
+ | `installBuiltinJobs()` | New template added to the source dir; will be copied on update | None — non-destructive (installs missing, doesn't overwrite user edits) |
72
+ | Existing weekly job slots | New job is independent and off by default | None |
73
+
74
+ ## Rollback cost
75
+
76
+ Low. Pure additive feature. Three options:
77
+
78
+ 1. **Code revert**: `git revert <PR-merge-sha>` removes new module + route + job template + test files. No data migration to roll back.
79
+ 2. **Disable the job**: The job ships `enabled: false`. Operators who never enable it see zero behavior change.
80
+ 3. **Ignore the route**: 404 the route via a config flag if desired (future iteration); currently the route is opt-in via being called.
81
+
82
+ ## Test coverage summary
83
+
84
+ | Tier | File | Tests | Status |
85
+ |---|---|---|---|
86
+ | 1 (unit) | `tests/unit/OrgIntentDriftAnalyzer.test.ts` (new) | 11 | ✓ passing |
87
+ | 1 (unit) | `tests/unit/PostUpdateMigrator-org-intent-runtime.test.ts` (extended) | 9 (1 new for Phase 4) | ✓ passing |
88
+ | 2 (integration) | `tests/integration/org-intent-routes.test.ts` (extended) | 17 (1 new for Phase 4) | ✓ passing |
89
+ | 3 (E2E lifecycle) | `tests/e2e/org-intent-drift-lifecycle.test.ts` (new) | 4 | ✓ passing |
90
+
91
+ ## This is the final phase
92
+
93
+ This PR closes the four-phase ORG-INTENT runtime project that started with topic 11378 on 2026-05-21. All four phases shipped — gate / session-start / tradeoff / drift — with three-tier test coverage at every step.
94
+
95
+ ## Open follow-ups (NOT this PR — future iterations)
96
+
97
+ - Per-channel drift breakdowns.
98
+ - LLM-supervised drift narrative (Tier 1 supervisor over the digest output).
99
+ - Recovery-pattern detection (positive signal when drift corrects).
100
+ - Drift digest persistence (week-over-week comparison artifacts).
101
+ - Action-level drift (catching off-mission actions that don't trip the gate).