instar 1.2.25 → 1.2.27
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/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +43 -5
- package/dist/commands/server.js.map +1 -1
- package/dist/core/OrgIntentDriftAnalyzer.d.ts +87 -0
- package/dist/core/OrgIntentDriftAnalyzer.d.ts.map +1 -0
- package/dist/core/OrgIntentDriftAnalyzer.js +191 -0
- package/dist/core/OrgIntentDriftAnalyzer.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +30 -1
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +1 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +49 -0
- package/dist/server/routes.js.map +1 -1
- package/dist/utils/resolveNodeBinary.d.ts +71 -0
- package/dist/utils/resolveNodeBinary.d.ts.map +1 -0
- package/dist/utils/resolveNodeBinary.js +152 -0
- package/dist/utils/resolveNodeBinary.js.map +1 -0
- package/package.json +1 -1
- package/scripts/fix-better-sqlite3.cjs +24 -5
- package/scripts/resolve-node-binary.cjs +119 -0
- package/src/data/builtin-manifest.json +61 -61
- package/src/scaffold/templates/jobs/instar/org-intent-drift-audit.md +34 -0
- package/src/scaffold/templates.ts +1 -0
- package/upgrades/1.2.26.md +58 -0
- package/upgrades/1.2.27.md +40 -0
- package/upgrades/side-effects/fix-heal-execpath-staleness.md +92 -0
- package/upgrades/side-effects/org-intent-drift-detection.md +101 -0
|
@@ -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,92 @@
|
|
|
1
|
+
# Side-Effects Review — fix(heal): resilient process.execPath resolution
|
|
2
|
+
|
|
3
|
+
**Branch:** `echo/fix-heal-execpath-staleness`
|
|
4
|
+
**Origin:** Luna self-heal failure 2026-05-21 (topic 9976, "topic-intent-layer"). Justin's framing: "the deeper issue that needs to be fixed: why couldn't Luna self heal?"
|
|
5
|
+
**Scope guardrail:** sibling fix to the Topic Intent Layer work, not a fold-in. Heal-path only.
|
|
6
|
+
|
|
7
|
+
## Summary
|
|
8
|
+
|
|
9
|
+
When Homebrew updates Node mid-session, `process.execPath` becomes ENOENT. The native-module self-heal spawned subprocesses via that path, so the heal failed with `spawnSync … ENOENT` and the agent's SQLite-backed subsystems degraded. The fix resolves a stable Node binary through a fallback chain before spawning.
|
|
10
|
+
|
|
11
|
+
Two new files:
|
|
12
|
+
- `src/utils/resolveNodeBinary.ts` — TypeScript resolver used by `ensureSqliteBindings`.
|
|
13
|
+
- `scripts/resolve-node-binary.cjs` — CommonJS twin used by `fix-better-sqlite3.cjs`.
|
|
14
|
+
|
|
15
|
+
Two modified files:
|
|
16
|
+
- `src/commands/server.ts` — `ensureSqliteBindings` resolves the spawn target before invoking the rebuild path; emits DegradationReporter events on resolver-null and rebuild-failed paths.
|
|
17
|
+
- `scripts/fix-better-sqlite3.cjs` — Module-load resolution; every internal spawn uses the resolved `NODE_BIN`. `findNpmCli` resolves npm relative to the resolved Node, not raw `process.execPath`.
|
|
18
|
+
|
|
19
|
+
Two new test files:
|
|
20
|
+
- `tests/unit/resolveNodeBinary.test.ts` — 7 tests covering the fallback chain branch-by-branch.
|
|
21
|
+
- `tests/integration/heal-execpath-staleness.test.ts` — 6 tests reproducing the failure shape and verifying the fix.
|
|
22
|
+
|
|
23
|
+
## Over-block check
|
|
24
|
+
|
|
25
|
+
Does this fix do MORE than what was asked?
|
|
26
|
+
|
|
27
|
+
- The resolver does NOT change the once-per-process heal guard in `NativeModuleHealer`. That guard exists to prevent expensive rebuild loops and was deliberately left in place.
|
|
28
|
+
- The resolver does NOT bypass `verifyChildAbiMatches` in the fix script. The ABI-divergence defence is still in place — it now runs against a resolved Node binary instead of process.execPath, which is what makes the defence actually work in the failure mode we're fixing.
|
|
29
|
+
- The DegradationReporter events are NEW emissions but match the existing pattern (subsystem-init-failure events already use the same shape). They surface a previously-silent failure rather than introducing new noise.
|
|
30
|
+
|
|
31
|
+
## Under-block check
|
|
32
|
+
|
|
33
|
+
Does this fix leave the failure mode reachable through another path?
|
|
34
|
+
|
|
35
|
+
Other call sites that spawn via `process.execPath` were audited:
|
|
36
|
+
- `ServerSupervisor.preflightSelfHeal` — runs in the supervisor process, not the child server. Different process, different execPath lifecycle. Out of scope for this fix; supervisor process is short-lived and re-spawns on each cycle.
|
|
37
|
+
- `NativeModuleHealer.healBetterSqlite3Sync` — spawns `npmPath` (a separate variable resolved via `findNpmPath`) with `process.execPath` as args[0]. ENOENT on execPath here would also fail. **Follow-up:** apply the same resolver to this path in a separate small PR. Out of scope for this commit to keep the diff tight per the original ask.
|
|
38
|
+
|
|
39
|
+
The follow-up is tracked at the bottom of this artifact, not punted as an orphan TODO. (Per the `no-out-of-scope-trap` memory.)
|
|
40
|
+
|
|
41
|
+
## Level-of-abstraction fit
|
|
42
|
+
|
|
43
|
+
The resolver is a utility, not a subsystem. It does one thing: resolve a path. It's called from the existing heal-path call sites; it does not own any state, does not log autonomously, does not own the rebuild flow. The DegradationReporter emissions live in `ensureSqliteBindings`, not in the resolver — the resolver returns data, the caller decides whether to escalate.
|
|
44
|
+
|
|
45
|
+
This matches the signal-vs-authority pattern: the resolver is the signal layer (returns "here's a working Node, here's how I found it, or null if nothing works"); the caller is the authority that decides what to do with that signal.
|
|
46
|
+
|
|
47
|
+
## Signal-vs-authority compliance
|
|
48
|
+
|
|
49
|
+
The resolver emits no degradation events, no logs, no side effects beyond resolving a path. It returns a structured value or null.
|
|
50
|
+
|
|
51
|
+
The caller (`ensureSqliteBindings`) is the authority — it decides:
|
|
52
|
+
- Whether to proceed with the rebuild (only if resolver returned a path).
|
|
53
|
+
- Whether to emit a DegradationReporter event (yes when resolver returns null OR when rebuild fails after resolution).
|
|
54
|
+
- What recovery hint to surface in the degradation impact field.
|
|
55
|
+
|
|
56
|
+
Two emission sites, both with explicit recovery actions:
|
|
57
|
+
- `ensureSqliteBindings.nodeBinaryResolution` — fires when the resolver returns null. Recovery: reinstall Node and restart.
|
|
58
|
+
- `ensureSqliteBindings.rebuildFailed` — fires when rebuild fails after resolution. Recovery: restart agent OR run `npx instar update`.
|
|
59
|
+
|
|
60
|
+
## Interactions with other systems
|
|
61
|
+
|
|
62
|
+
- **NativeModuleHealer.openWithHeal** (the in-line heal path for direct constructor calls): NOT modified by this PR. That path still uses `process.execPath` internally. The follow-up applies the same resolver there.
|
|
63
|
+
- **ServerSupervisor.preflightSelfHeal**: NOT modified. Supervisor runs in a separate process with its own execPath lifecycle.
|
|
64
|
+
- **UpdateChecker.applyUpdate**: Already had its own rebuild path that worked on luna (visible in the server log at 02:35 and 03:21 — both successful rebuilds while the server kept running with stale bindings). No conflict: UpdateChecker's rebuilds happen in long-running processes that already hold the broken in-memory binding; they take effect on next restart. This fix ensures the next restart's preflight can actually run.
|
|
65
|
+
- **PostUpdateMigrator**: NOT touched. Migrator script propagation works as before; no agent-side script needs migration for this fix because both new files (.ts + .cjs) live in the instar package itself.
|
|
66
|
+
|
|
67
|
+
## Rollback cost
|
|
68
|
+
|
|
69
|
+
Single-commit revert removes the resolver, the import, the call-site change in `ensureSqliteBindings`, and the resolver-pinned spawns in `fix-better-sqlite3.cjs`. The behavior reverts to "heal fails when execPath is stale" — the failure mode we're fixing, not a new one. No data migration, no config changes, no agent-side script propagation required.
|
|
70
|
+
|
|
71
|
+
## Cross-framework portability (v1.0+)
|
|
72
|
+
|
|
73
|
+
The heal path is framework-agnostic — it operates on Node binaries, not on the agent runtime (Claude Code vs Codex). The resolver uses standard Node APIs (`fs`, `os`, `child_process`) with no framework-specific code paths. Verified by:
|
|
74
|
+
- Integration test passes under the test-running Node regardless of framework.
|
|
75
|
+
- The Codex CLI runtime spawns differently but uses the same Node-binary substrate, so the resolver applies equally there.
|
|
76
|
+
|
|
77
|
+
No `INSTAR_FRAMEWORK`-conditional code added; no framework-aware adapters needed for this fix.
|
|
78
|
+
|
|
79
|
+
## Telemetry / observability
|
|
80
|
+
|
|
81
|
+
Heal-attempt visibility before this PR: a single yellow log line on failure.
|
|
82
|
+
Heal-attempt visibility after this PR: that log line PLUS a DegradationReporter event with structured fields (feature, primary, fallback, reason, impact). The event surfaces through `GET /degradations` (existing route) and any subscribers to the degradation event channel (existing pattern).
|
|
83
|
+
|
|
84
|
+
When the resolver fires a non-execPath fallback, a `console.log(pc.dim(...))` line names which fallback fired and the resolved path. This is one extra log line per server startup that needed it; on healthy startups it doesn't fire.
|
|
85
|
+
|
|
86
|
+
## Follow-ups (tracked, not orphaned)
|
|
87
|
+
|
|
88
|
+
1. Apply `resolveStableNodeBinary` to `NativeModuleHealer.healBetterSqlite3Sync` so the in-line CLI-direct-construction heal path (CLI commands like `instar memory ...`, TokenLedger constructor, etc.) also benefits. Should be a small commit on a separate branch — the heal-path-fix scope intentionally excluded it to keep this diff tight.
|
|
89
|
+
|
|
90
|
+
2. Consider whether the once-per-process heal guard in `NativeModuleHealer` should distinguish "heal failed due to spawn-target ENOENT" (transient, retryable when operator restores Node) from "heal failed due to rebuild error" (durable, don't retry). The current guard treats both the same. Lower priority than (1).
|
|
91
|
+
|
|
92
|
+
Both items will be filed as initiatives so they don't drop. Not part of this commit.
|
|
@@ -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).
|