instar 1.0.13 → 1.1.1
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/README.md +1 -1
- package/dist/cli.js +52 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/init.d.ts +17 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +156 -64
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +68 -4
- package/dist/commands/server.js.map +1 -1
- package/dist/commands/setup.d.ts +3 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +67 -28
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/worktree.d.ts +17 -4
- package/dist/commands/worktree.d.ts.map +1 -1
- package/dist/commands/worktree.js +28 -4
- package/dist/commands/worktree.js.map +1 -1
- package/dist/core/AgentWorktreeDetector.d.ts +63 -0
- package/dist/core/AgentWorktreeDetector.d.ts.map +1 -0
- package/dist/core/AgentWorktreeDetector.js +329 -0
- package/dist/core/AgentWorktreeDetector.js.map +1 -0
- package/dist/core/InstarWorktreeManager.d.ts +99 -0
- package/dist/core/InstarWorktreeManager.d.ts.map +1 -0
- package/dist/core/InstarWorktreeManager.js +447 -0
- package/dist/core/InstarWorktreeManager.js.map +1 -0
- package/dist/core/MachineIdentity.d.ts.map +1 -1
- package/dist/core/MachineIdentity.js +2 -0
- package/dist/core/MachineIdentity.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +25 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +258 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/memory/NativeModuleHealer.d.ts +15 -0
- package/dist/memory/NativeModuleHealer.d.ts.map +1 -1
- package/dist/memory/NativeModuleHealer.js +22 -0
- package/dist/memory/NativeModuleHealer.js.map +1 -1
- package/dist/monitoring/NativeHealDegradationBridge.d.ts +32 -0
- package/dist/monitoring/NativeHealDegradationBridge.d.ts.map +1 -0
- package/dist/monitoring/NativeHealDegradationBridge.js +61 -0
- package/dist/monitoring/NativeHealDegradationBridge.js.map +1 -0
- package/dist/monitoring/PromptGate.d.ts +12 -0
- package/dist/monitoring/PromptGate.d.ts.map +1 -1
- package/dist/monitoring/PromptGate.js +44 -0
- package/dist/monitoring/PromptGate.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +17 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +6 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/package.json +1 -1
- package/scripts/lint-no-direct-destructive.js +7 -0
- package/scripts/resolve-release-tier.mjs +192 -0
- package/src/data/builtin-manifest.json +57 -49
- package/src/scaffold/templates.ts +17 -0
- package/src/templates/scripts/instar-watchdog.sh +169 -6
- package/src/templates/scripts/instar-worktree-create.sh +79 -0
- package/upgrades/1.1.0.md +320 -0
- package/upgrades/1.1.1.md +58 -0
- package/upgrades/side-effects/agent-worktree-convention-layer-1-2-5.md +280 -0
- package/upgrades/side-effects/agent-worktree-convention-layer-3-4.md +285 -0
- package/upgrades/side-effects/feat-fix-parent-framework-intercept.md +59 -0
- package/upgrades/side-effects/feat-install-claude-gating.md +67 -0
- package/upgrades/side-effects/feat-install-framework-flag.md +65 -0
- package/upgrades/side-effects/feat-portability-shadow-capabilities.md +63 -0
- package/upgrades/side-effects/feat-setup-framework-flag.md +65 -0
- package/upgrades/side-effects/lockdown-layer-2.md +123 -0
- package/upgrades/side-effects/native-heal-degradation-bridge.md +206 -0
- package/upgrades/side-effects/watchdog-bind-failure-probe.md +174 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Side-effects review — parent --framework intercept hotfix
|
|
2
|
+
|
|
3
|
+
Per L6. Seven dimensions.
|
|
4
|
+
|
|
5
|
+
## 1. Over-block / under-block
|
|
6
|
+
|
|
7
|
+
Before (post-PR 3+4): UNDER — the flag silently fell back to claude-code
|
|
8
|
+
on every subcommand invocation; the install/wizard arc didn't actually
|
|
9
|
+
work for Codex. After: precisely correct. Subcommands receive the flag.
|
|
10
|
+
|
|
11
|
+
## 2. Level-of-abstraction fit
|
|
12
|
+
|
|
13
|
+
Fix sits exactly where the bug was — the program-level option definition
|
|
14
|
+
in `cli.ts`. Removed cleanly; a comment-at-call-site explains the
|
|
15
|
+
removal so a future editor doesn't reintroduce it.
|
|
16
|
+
|
|
17
|
+
## 3. Signal vs Authority compliance
|
|
18
|
+
|
|
19
|
+
No new authority. The bug was that Commander's program-level option
|
|
20
|
+
parsing was treated as an authoritative consumer of subcommand args.
|
|
21
|
+
Removing the program-level definition leaves subcommand parsers as the
|
|
22
|
+
sole authority for their own flags — which is the correct mental model.
|
|
23
|
+
|
|
24
|
+
## 4. Interactions with adjacent systems
|
|
25
|
+
|
|
26
|
+
- **PR 3+4 (`instar setup --framework`)** — fix consistent with how
|
|
27
|
+
`setup` already worked. `instar setup --framework codex-cli` was always
|
|
28
|
+
going to be the correct invocation; the bug just made `instar` (no
|
|
29
|
+
subcommand) intercept the flag and also broke `instar init`.
|
|
30
|
+
- **PR 1 (`instar init --framework`)** — now actually works on the CLI
|
|
31
|
+
path, not just when initProject is called directly. The PR 2 unit
|
|
32
|
+
tests still pass (they bypass the CLI layer); the smoke test is now
|
|
33
|
+
also green.
|
|
34
|
+
|
|
35
|
+
## 5. Rollback cost
|
|
36
|
+
|
|
37
|
+
Trivial. One option declaration removed plus a comment. `git revert`
|
|
38
|
+
reintroduces the bug — but the comment-at-call-site would also revert,
|
|
39
|
+
which is the safety net.
|
|
40
|
+
|
|
41
|
+
## 6. Backwards compatibility / drift surface
|
|
42
|
+
|
|
43
|
+
Fully backward-compatible. Users who already used `instar init
|
|
44
|
+
--framework codex-cli` had a broken result before; now they get the
|
|
45
|
+
correct codex-only install. Users who used the bareword `instar` with no
|
|
46
|
+
flags see no change. Users who used `instar --framework codex-cli`
|
|
47
|
+
(passing the flag before any subcommand) — that invocation never
|
|
48
|
+
actually worked because there's no `--framework` on the bareword now,
|
|
49
|
+
and even before this fix, bareword wouldn't have reached init/setup
|
|
50
|
+
with the flag.
|
|
51
|
+
|
|
52
|
+
## 7. Authorization / Trust posture
|
|
53
|
+
|
|
54
|
+
No new authority. No privilege change. No new resources accessed.
|
|
55
|
+
|
|
56
|
+
## Outcome
|
|
57
|
+
|
|
58
|
+
Ship. One-line code fix verified by smoke test on this machine. Closes
|
|
59
|
+
the install/wizard portability arc functionally.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Side-effects review — Codex-only init zero .claude/ (PR 2 of 4)
|
|
2
|
+
|
|
3
|
+
Per L6. Seven dimensions.
|
|
4
|
+
|
|
5
|
+
## 1. Over-block / under-block
|
|
6
|
+
|
|
7
|
+
Before: UNDER — `--framework codex-cli` was expressed but ignored; all
|
|
8
|
+
`.claude/` writes fired anyway. After: precisely gated. Default (no flag,
|
|
9
|
+
or `claude-code`, or `both`) is byte-identical to v1.0.15. Only an explicit
|
|
10
|
+
codex-only choice changes anything.
|
|
11
|
+
|
|
12
|
+
## 2. Level-of-abstraction fit
|
|
13
|
+
|
|
14
|
+
`claudeEnabled` is derived once per init function (or per refresh helper)
|
|
15
|
+
from the canonical `enabledFrameworks` value. The pure
|
|
16
|
+
`resolveEnabledFrameworks(choice)` helper (PR 1) is the single resolver
|
|
17
|
+
for flag-provided values; `refreshHooksAndSettings` and `refreshScripts`
|
|
18
|
+
read the persisted config directly with the same default semantics. No
|
|
19
|
+
brittle inline re-derivation.
|
|
20
|
+
|
|
21
|
+
## 3. Signal vs Authority compliance
|
|
22
|
+
|
|
23
|
+
The flag (PR 1) and the persisted `enabledFrameworks` are the SIGNAL of
|
|
24
|
+
operator intent. Each init/refresh function's local `claudeEnabled`
|
|
25
|
+
variable is the single AUTHORITY for that function's gating. No leak of
|
|
26
|
+
the framework concept into the installer functions themselves
|
|
27
|
+
(`installClaudeSettings`, `installSmartFetch`, etc.) — they remain
|
|
28
|
+
unchanged.
|
|
29
|
+
|
|
30
|
+
## 4. Interactions with adjacent systems
|
|
31
|
+
|
|
32
|
+
- **PR 1 (`--framework` flag)** — built on top. PR 2 makes the flag's
|
|
33
|
+
persisted value actually do something at install/refresh time.
|
|
34
|
+
- **`PostUpdateMigrator.getEnabledFrameworks()`** (v1.0.11) — uses the
|
|
35
|
+
same default semantics. The two readers are consistent.
|
|
36
|
+
- **`IdentityRenderer.renderNonClaudeIdentityShadows`** — unchanged, still
|
|
37
|
+
runs unconditionally for AGENTS.md/GEMINI.md.
|
|
38
|
+
- **`installHooks(stateDir)`** — unchanged; it writes to `.instar/hooks/`,
|
|
39
|
+
framework-neutral.
|
|
40
|
+
- **`migrateScripts` in PostUpdateMigrator** — already framework-aware for
|
|
41
|
+
the relay script (Gap 4 / v1.0.10). The `.claude/scripts/` writes in
|
|
42
|
+
`refreshScripts` are now consistently gated.
|
|
43
|
+
|
|
44
|
+
## 5. Rollback cost
|
|
45
|
+
|
|
46
|
+
Low. Five gates in init.ts (all near-identical `if (claudeEnabled)` wraps)
|
|
47
|
+
+ one new test file. `git revert` restores prior behavior. Existing
|
|
48
|
+
configs with or without `enabledFrameworks` continue to be readable by
|
|
49
|
+
older versions.
|
|
50
|
+
|
|
51
|
+
## 6. Backwards compatibility / drift surface
|
|
52
|
+
|
|
53
|
+
Fully backward-compatible. Default behavior preserved. Drift surface:
|
|
54
|
+
none new — five gate sites read from the same persisted field with the
|
|
55
|
+
same default.
|
|
56
|
+
|
|
57
|
+
## 7. Authorization / Trust posture
|
|
58
|
+
|
|
59
|
+
No new authority. The gates only *prevent* writes for codex-only installs;
|
|
60
|
+
they never grant new writes. Unreadable config defaults to `['claude-code']`
|
|
61
|
+
(status quo). Cannot escalate, cannot read additional resources.
|
|
62
|
+
|
|
63
|
+
## Outcome
|
|
64
|
+
|
|
65
|
+
Ship. Operator-scoped, default-safe, fully tested (the codex-only zero-
|
|
66
|
+
`.claude/` guarantee is pinned by an isolated-fs end-to-end test). PR 2 of
|
|
67
|
+
4 of the install/wizard portability series.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Side-effects review — install --framework flag (PR 1 of 4)
|
|
2
|
+
|
|
3
|
+
Per L6. Seven dimensions.
|
|
4
|
+
|
|
5
|
+
## 1. Over-block / under-block
|
|
6
|
+
|
|
7
|
+
Before: UNDER — no UI for framework choice; even with all downstream
|
|
8
|
+
machinery in place (PostUpdateMigrator, ParitySentinel, IdentityRenderer)
|
|
9
|
+
the install always produced Claude-shaped scaffolding. After: no
|
|
10
|
+
over-block. Default (no flag) is `['claude-code']` — byte-identical
|
|
11
|
+
to today's behavior. Only an explicit `--framework codex-cli` or `both`
|
|
12
|
+
changes the persisted config.
|
|
13
|
+
|
|
14
|
+
## 2. Level-of-abstraction fit
|
|
15
|
+
|
|
16
|
+
CLI option lives in `cli.ts`. The flag value resolution is a pure helper
|
|
17
|
+
(`resolveEnabledFrameworks`) — testable in isolation, no side effects.
|
|
18
|
+
Config-write integration is at the three init paths' existing config
|
|
19
|
+
object literals. Correct altitude — option parsing at CLI boundary,
|
|
20
|
+
business logic in pure function, persistence at the existing write sites.
|
|
21
|
+
|
|
22
|
+
## 3. Signal vs Authority compliance
|
|
23
|
+
|
|
24
|
+
The `--framework` flag is the SIGNAL of operator intent.
|
|
25
|
+
`resolveEnabledFrameworks()` is the single AUTHORITY for the persisted
|
|
26
|
+
shape. PostUpdateMigrator's `getEnabledFrameworks()` (added v1.0.11)
|
|
27
|
+
is the single reader. No brittle inline duplication.
|
|
28
|
+
|
|
29
|
+
## 4. Interactions with adjacent systems
|
|
30
|
+
|
|
31
|
+
- **`PostUpdateMigrator.getEnabledFrameworks()`** (v1.0.11) already
|
|
32
|
+
reads this field with the same default. Now the field actually gets
|
|
33
|
+
written at install time. Consistent.
|
|
34
|
+
- **`FrameworkParitySentinel`** has its own `enabledFrameworks` config
|
|
35
|
+
(different object); names intentionally mirror, no shared mutable
|
|
36
|
+
state, no coupling introduced.
|
|
37
|
+
- **PRs 2-4** depend on this field being written; this PR makes them
|
|
38
|
+
possible without itself changing any gating.
|
|
39
|
+
- **Existing Claude-only installs** are unaffected (default `['claude-code']`
|
|
40
|
+
preserves all current writes).
|
|
41
|
+
|
|
42
|
+
## 5. Rollback cost
|
|
43
|
+
|
|
44
|
+
Low. One CLI option add + one type field + one helper + three identical
|
|
45
|
+
config-literal additions + one test file. `git revert` restores prior
|
|
46
|
+
behavior; existing configs that have `enabledFrameworks` are still
|
|
47
|
+
readable by older versions (the field is optional everywhere).
|
|
48
|
+
|
|
49
|
+
## 6. Backwards compatibility / drift surface
|
|
50
|
+
|
|
51
|
+
Fully backward-compatible: the flag is optional; the helper defaults
|
|
52
|
+
to historical behavior. Configs created without the flag have no
|
|
53
|
+
`enabledFrameworks` field; PostUpdateMigrator's helper already defaults
|
|
54
|
+
to `['claude-code']` when the field is unset. Drift surface: none — one
|
|
55
|
+
helper, one config field, three call sites using identical syntax.
|
|
56
|
+
|
|
57
|
+
## 7. Authorization / Trust posture
|
|
58
|
+
|
|
59
|
+
No new authority. The flag only changes what's persisted at install
|
|
60
|
+
time. Cannot escalate privileges, cannot read additional resources.
|
|
61
|
+
Invalid flag values exit with error at parse time.
|
|
62
|
+
|
|
63
|
+
## Outcome
|
|
64
|
+
|
|
65
|
+
Ship. Foundation for PRs 2-4. Default-safe, drift-reducing, tested.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Side-effects review — Shadow capability mirror (Gap 6)
|
|
2
|
+
|
|
3
|
+
Per L6. Seven dimensions.
|
|
4
|
+
|
|
5
|
+
## 1. Over-block / under-block
|
|
6
|
+
|
|
7
|
+
Before: UNDER — Codex/Gemini agents lacked capability instructions. After:
|
|
8
|
+
no over-block. Mirror runs only when a non-Claude shadow exists; appends
|
|
9
|
+
only sections absent from the shadow; never modifies CLAUDE.md.
|
|
10
|
+
|
|
11
|
+
## 2. Level-of-abstraction fit
|
|
12
|
+
|
|
13
|
+
Sits alongside `migrateClaudeMd` in PostUpdateMigrator, runs immediately
|
|
14
|
+
after it. Same altitude. Slices from the live CLAUDE.md rather than
|
|
15
|
+
extracting section bodies into a shared array — a deliberate scope choice
|
|
16
|
+
per the operator's "minimal shim" decision.
|
|
17
|
+
|
|
18
|
+
## 3. Signal vs Authority compliance
|
|
19
|
+
|
|
20
|
+
CLAUDE.md (just patched by `migrateClaudeMd`) is the AUTHORITY for section
|
|
21
|
+
content; the marker list is the SIGNAL of which sections exist. The shim
|
|
22
|
+
never invents content — it copies bytes from CLAUDE.md.
|
|
23
|
+
|
|
24
|
+
## 4. Interactions with adjacent systems
|
|
25
|
+
|
|
26
|
+
- **migrateClaudeMd** — untouched; runs first, the shim reads what it
|
|
27
|
+
produced.
|
|
28
|
+
- **IdentityRenderer** (Gap 1) — untouched; runs at a different layer
|
|
29
|
+
(identity vs capability). The shim appends only capability sections, so
|
|
30
|
+
the AGENT.md-derived identity content at the top of AGENTS.md/GEMINI.md
|
|
31
|
+
is preserved (tested).
|
|
32
|
+
- **migrateFrameworkShadowCapabilities ordering** — runs before
|
|
33
|
+
migrateScripts and others; section appending is pure file I/O on the
|
|
34
|
+
shadows and does not block downstream steps.
|
|
35
|
+
- **Claude-only installs** — no shadow exists, no-op. Verified.
|
|
36
|
+
|
|
37
|
+
## 5. Rollback cost
|
|
38
|
+
|
|
39
|
+
Low. One new private method + one new call site + one new test. `git
|
|
40
|
+
revert` restores prior behavior; on-disk shadows keep the appended
|
|
41
|
+
sections (inert; they'd just look like extra documentation). No state
|
|
42
|
+
migration.
|
|
43
|
+
|
|
44
|
+
## 6. Backwards compatibility / drift surface
|
|
45
|
+
|
|
46
|
+
Claude installs: byte-identical. Non-Claude shadows: strictly better (now
|
|
47
|
+
have capability instructions). Drift surface: **reduced** — section
|
|
48
|
+
bodies live in exactly one place (CLAUDE.md, written by migrateClaudeMd)
|
|
49
|
+
and are sliced from there into shadows; there is no separate source array
|
|
50
|
+
to drift.
|
|
51
|
+
|
|
52
|
+
## 7. Authorization / Trust posture
|
|
53
|
+
|
|
54
|
+
No new authority. Reads CLAUDE.md and the existing shadows the migrator
|
|
55
|
+
already owns. Writes only when appending material that came from
|
|
56
|
+
CLAUDE.md. Failure paths surface as captured errors in the MigrationResult,
|
|
57
|
+
never throw out of migrate().
|
|
58
|
+
|
|
59
|
+
## Outcome
|
|
60
|
+
|
|
61
|
+
Ship. Operator-scoped, source-of-truth preserved (no duplicated section
|
|
62
|
+
bodies), idempotent, identity-preserving, Claude-only-safe, six-case
|
|
63
|
+
tested. Closes the v1.0.0 cross-framework portability audit at 6/6.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Side-effects review — instar setup --framework + wizard launch (PR 3+4)
|
|
2
|
+
|
|
3
|
+
Per L6. Seven dimensions.
|
|
4
|
+
|
|
5
|
+
## 1. Over-block / under-block
|
|
6
|
+
|
|
7
|
+
Before: UNDER — `setup.ts` exited if Claude wasn't installed, even for
|
|
8
|
+
Codex operators. After: precisely targeted. Default (no flag) is
|
|
9
|
+
'claude-code', byte-identical to v1.0.16. Only an explicit
|
|
10
|
+
`--framework codex-cli` changes detection and spawn behavior.
|
|
11
|
+
|
|
12
|
+
## 2. Level-of-abstraction fit
|
|
13
|
+
|
|
14
|
+
`runSetup({ framework? })` accepts the choice; `checkFrameworkPrerequisite`
|
|
15
|
+
from `src/core/Config.ts` is the single AUTHORITY for "is the chosen
|
|
16
|
+
runtime's binary installed?" — reused, not duplicated. Spawn shape is
|
|
17
|
+
two parallel branches (Claude and Codex) sharing the same wizard prompt
|
|
18
|
+
content. Correct altitude.
|
|
19
|
+
|
|
20
|
+
## 3. Signal vs Authority compliance
|
|
21
|
+
|
|
22
|
+
The `--framework` flag is the operator-intent SIGNAL. `checkFrameworkPrerequisite`
|
|
23
|
+
is the single AUTHORITY for binary detection across the whole codebase
|
|
24
|
+
(also used by Config.load runtime startup). No new brittle inline checks.
|
|
25
|
+
|
|
26
|
+
## 4. Interactions with adjacent systems
|
|
27
|
+
|
|
28
|
+
- **PR 1 + PR 2** — built on top. The flag now drives both install-time
|
|
29
|
+
scaffolding (PR 1+2) and wizard-time launch (PR 3+4) consistently.
|
|
30
|
+
- **`detectClaudePath` / `detectCodexPath`** — both already exported from
|
|
31
|
+
`src/core/Config.ts`. Used here without modification.
|
|
32
|
+
- **`checkFrameworkPrerequisite`** — existing function, used as-is.
|
|
33
|
+
- **Wizard skill content** (`.claude/skills/setup-wizard/SKILL.md`,
|
|
34
|
+
`secret-setup/SKILL.md`) — unchanged. Both runtimes are pointed at the
|
|
35
|
+
same files.
|
|
36
|
+
- **Playwright Telegram-setup flow** inside the wizard — unchanged. The
|
|
37
|
+
flow was already runtime-portable; this PR makes the entry path work
|
|
38
|
+
for Codex too.
|
|
39
|
+
|
|
40
|
+
## 5. Rollback cost
|
|
41
|
+
|
|
42
|
+
Low. One function-signature change, one detection swap, two spawn-arg
|
|
43
|
+
branches, two CLI flag additions. `git revert` restores prior Claude-only
|
|
44
|
+
behavior. Existing Claude users are unaffected even mid-rollback.
|
|
45
|
+
|
|
46
|
+
## 6. Backwards compatibility / drift surface
|
|
47
|
+
|
|
48
|
+
Fully backward-compatible. The optional `framework` parameter defaults
|
|
49
|
+
to `'claude-code'`. The bareword `npx instar` and `instar setup` without
|
|
50
|
+
the flag run exactly as v1.0.16 did. Drift surface: none — wizard
|
|
51
|
+
content remains in one place.
|
|
52
|
+
|
|
53
|
+
## 7. Authorization / Trust posture
|
|
54
|
+
|
|
55
|
+
No new authority. The flag only changes which binary is spawned; both
|
|
56
|
+
existing spawn paths use `--dangerously-skip-permissions` (Claude) or
|
|
57
|
+
`--dangerously-bypass-approvals-and-sandbox` (Codex) — these are the same
|
|
58
|
+
sandbox-bypass postures the runtime-spawn builders already use elsewhere
|
|
59
|
+
in the codebase (`src/core/frameworkSessionLaunch.ts`). No new privilege.
|
|
60
|
+
|
|
61
|
+
## Outcome
|
|
62
|
+
|
|
63
|
+
Ship. Operator-scoped, default-safe, no Claude-user regression risk,
|
|
64
|
+
the cohesive end-to-end Codex setup story finally works. End-to-end
|
|
65
|
+
verification follows as the next task (smoke test on this machine).
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Side-effects review — Deployment Lockdown Layer 2 (release-tier gate)
|
|
2
|
+
|
|
3
|
+
Per L6. Seven dimensions.
|
|
4
|
+
|
|
5
|
+
## 1. Over-block / under-block
|
|
6
|
+
|
|
7
|
+
**Before.** UNDER: the publish workflow had no concept of a holding pattern.
|
|
8
|
+
Any merge to main with a non-template NEXT.md auto-published. There was no
|
|
9
|
+
expressible "no deploy" state in code — the operator could only say it in
|
|
10
|
+
chat, and chat does not feed the workflow. This is the exact gap that allowed
|
|
11
|
+
the 2026-05-19 misalignment (four v1.0.x PRs shipped as v0.28.x patches
|
|
12
|
+
during a session marked "no deploy").
|
|
13
|
+
|
|
14
|
+
**After.** No new over-block of routine maintenance: when an operator flips
|
|
15
|
+
`.instar/release-tier.json` to `tier: patch`, behavior is byte-for-byte
|
|
16
|
+
identical to v1.0.8's post-Layer-1 path. The new path is `tier: hold`, which
|
|
17
|
+
is precisely the previously-unexpressible "no deploy" state, plus `tier:
|
|
18
|
+
minor` and `tier: major`, which enforce that the package.json bump actually
|
|
19
|
+
matches the declared tier rather than letting any leap silently ship.
|
|
20
|
+
|
|
21
|
+
The single deliberate over-block is `tier: major`, which refuses publishes
|
|
22
|
+
until Layer 5 (multi-signature) ships. This is intentional — the spec
|
|
23
|
+
requires both `LOCAL.major > NPM.major` AND signature verification before a
|
|
24
|
+
major publish. Shipping a major with only single-agent authority would
|
|
25
|
+
defeat the purpose of the lockdown.
|
|
26
|
+
|
|
27
|
+
## 2. Level-of-abstraction fit
|
|
28
|
+
|
|
29
|
+
The resolution policy lives in `scripts/resolve-release-tier.mjs` — pure
|
|
30
|
+
functions (`validateTierConfig`, `readTierConfig`, `resolveReleaseTier`)
|
|
31
|
+
with a thin CLI shim. The workflow calls it and routes on the printed
|
|
32
|
+
decision. Decision logic in testable code, orchestration in the workflow.
|
|
33
|
+
No logic buried in inline bash.
|
|
34
|
+
|
|
35
|
+
The committed `.instar/release-tier.json` file is the operator's authority;
|
|
36
|
+
the script is the deterministic reader; the workflow is the consumer. Three
|
|
37
|
+
roles, three files, no conflation.
|
|
38
|
+
|
|
39
|
+
## 3. Signal vs Authority compliance
|
|
40
|
+
|
|
41
|
+
`.instar/release-tier.json` is the AUTHORITATIVE declaration of the active
|
|
42
|
+
release line. The script never blocks of its own initiative — it reads the
|
|
43
|
+
operator's committed authority and reports the corresponding decision. The
|
|
44
|
+
workflow honors that decision. This is signal-vs-authority done right: a
|
|
45
|
+
deterministic reader translating a committed operator signal into a
|
|
46
|
+
deterministic outcome.
|
|
47
|
+
|
|
48
|
+
No new authority is created by this layer. The publish authority (NPM_TOKEN)
|
|
49
|
+
is unchanged and still gated by branch protection + CI required checks. The
|
|
50
|
+
gate runs strictly before any side-effectful step, so a `skip` decision
|
|
51
|
+
prevents `npm publish`, `npm version`, the NEXT.md rename, and the
|
|
52
|
+
release-commit push from running at all.
|
|
53
|
+
|
|
54
|
+
## 4. Interactions with adjacent systems
|
|
55
|
+
|
|
56
|
+
- **Layer 1 (`resolve-publish-version.mjs`).** Untouched. Layer 2 runs
|
|
57
|
+
BEFORE Layer 1 in the workflow: if the tier blocks, the version-bump step
|
|
58
|
+
never runs. If the tier allows, Layer 1 proceeds with its existing
|
|
59
|
+
package.json-as-authority logic.
|
|
60
|
+
- **NEXT.md skip-gate.** Untouched. The existing template-detection logic
|
|
61
|
+
still runs first; Layer 2's gate is only consulted when NEXT.md has
|
|
62
|
+
content. Tier=hold + template NEXT.md = both gates skip (overdetermined,
|
|
63
|
+
safe).
|
|
64
|
+
- **`check-upgrade-guide.js`.** Untouched. Only runs when both Layer 2 and
|
|
65
|
+
Layer 1 have allowed publish.
|
|
66
|
+
- **Pre-push hook / Husky.** Untouched. This is build-time infra in a
|
|
67
|
+
workflow YAML, not in a code path the pre-push hook examines.
|
|
68
|
+
- **Agent runtime.** No agent runtime code is modified. The lockdown is
|
|
69
|
+
infrastructure-only.
|
|
70
|
+
|
|
71
|
+
## 5. Rollback cost
|
|
72
|
+
|
|
73
|
+
Low. Four files:
|
|
74
|
+
|
|
75
|
+
- `scripts/resolve-release-tier.mjs` (new)
|
|
76
|
+
- `tests/unit/resolve-release-tier.test.ts` (new)
|
|
77
|
+
- `.instar/release-tier.json` (new)
|
|
78
|
+
- `.github/workflows/publish.yml` (one added step, four downstream `if:`
|
|
79
|
+
conditions extended)
|
|
80
|
+
|
|
81
|
+
`git revert <merge-sha>` restores the pre-Layer-2 behavior. No state
|
|
82
|
+
migration. No deployed-agent impact. If the workflow YAML edit ever needs
|
|
83
|
+
to be undone without a revert, removing the `tier-gate` step and the four
|
|
84
|
+
extended `if:` clauses restores the prior path.
|
|
85
|
+
|
|
86
|
+
Operator can also unblock publishes immediately by changing
|
|
87
|
+
`.instar/release-tier.json` from `hold` to `patch` — a one-line commit, no
|
|
88
|
+
code changes required.
|
|
89
|
+
|
|
90
|
+
## 6. Backwards compatibility / drift surface
|
|
91
|
+
|
|
92
|
+
The script defaults to `tier: patch` (pre-Layer-2 behavior) when
|
|
93
|
+
`.instar/release-tier.json` is absent. This means an older checkout, a
|
|
94
|
+
cherry-pick to a branch without the file, or any environment that loses
|
|
95
|
+
the file mid-flight degrades gracefully to the existing behavior — never
|
|
96
|
+
to a blocked state.
|
|
97
|
+
|
|
98
|
+
Drift surface: small. The tier file is the only new persistent input. It's
|
|
99
|
+
JSON with four allowed values; the validator rejects anything else with a
|
|
100
|
+
clear error. The `setAt` / `setBy` / `reason` fields are informational only
|
|
101
|
+
and the script does not use them for decisions, so format drift on those
|
|
102
|
+
fields is non-blocking.
|
|
103
|
+
|
|
104
|
+
## 7. Authorization / Trust posture
|
|
105
|
+
|
|
106
|
+
No new authority is granted. The script reads a committed file and prints
|
|
107
|
+
a string. It cannot publish, cannot mutate state, cannot escalate. The
|
|
108
|
+
operator's authority is expressed via a normal git commit to
|
|
109
|
+
`.instar/release-tier.json` — reviewable, traceable, revertable, and
|
|
110
|
+
governed by the same branch protection that protects every other code
|
|
111
|
+
change.
|
|
112
|
+
|
|
113
|
+
The "hold" default in this release means the lockdown's headline guarantee
|
|
114
|
+
is engaged immediately on merge — no further auto-publish can occur until
|
|
115
|
+
an operator deliberately changes the tier. This is the spec's
|
|
116
|
+
operator-intent-first posture: opt out of hold rather than opt in.
|
|
117
|
+
|
|
118
|
+
## Outcome
|
|
119
|
+
|
|
120
|
+
Ship. Minimal, incident-driven, fully unit-tested. The committed `hold`
|
|
121
|
+
tier delivers the headline "no chance of accidental major-feature
|
|
122
|
+
deployment" guarantee immediately on merge. Routine maintenance resumes
|
|
123
|
+
with a one-line tier change. Layers 3–7 ship as separate PRs.
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Side-Effects Review — Native-Module Heal → DegradationReporter bridge
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `native-heal-degradation-bridge`
|
|
4
|
+
**Date:** `2026-05-20`
|
|
5
|
+
**Author:** Echo (instar developer agent)
|
|
6
|
+
**Second-pass reviewer:** voluntary independent audit run despite the
|
|
7
|
+
change not falling under the mandatory-review categories — the bridge
|
|
8
|
+
emits into the outbound-messaging path (via DegradationReporter), which
|
|
9
|
+
is adjacent enough to warrant a second opinion.
|
|
10
|
+
|
|
11
|
+
**Second-pass result:** *Concur with the review.* Reviewer independently
|
|
12
|
+
verified:
|
|
13
|
+
- `listeners` Set is effectively process-global (healer is a module-level
|
|
14
|
+
singleton); per-bridge `Set` dedupe is the right second layer for
|
|
15
|
+
future per-module retries.
|
|
16
|
+
- F-8 Remediator `invokeFromRemediator` and legacy `openWithHeal` both
|
|
17
|
+
route through `logHealEvent` and share the `healAttempted` one-shot
|
|
18
|
+
guard, so no double-fire is possible today.
|
|
19
|
+
- Listener-error swallow is bounded to the observability-consumer call
|
|
20
|
+
site and does not hide heal exceptions (those remain thrown by
|
|
21
|
+
`healBetterSqlite3Sync`).
|
|
22
|
+
- Tests 7–9 exercise the real production listener-dispatch path via
|
|
23
|
+
`healBetterSqlite3Sync` with a mocked install-prefix resolver; tests
|
|
24
|
+
1–6 use a synthetic fanout helper appropriate for unit-isolating the
|
|
25
|
+
bridge translation logic.
|
|
26
|
+
- Forward-looking note (not blocking): if per-module heal retries are
|
|
27
|
+
added later, success events should clear the dedupe entry and emit a
|
|
28
|
+
"recovered" degradation so a retry-success-after-failure sequence
|
|
29
|
+
doesn't leave the user with a stale failure alert. Today's one-shot
|
|
30
|
+
semantics make this impossible to hit, so the bridge is correct as
|
|
31
|
+
written; the comment in the bridge already anticipates the future
|
|
32
|
+
surface.
|
|
33
|
+
|
|
34
|
+
## Summary of the change
|
|
35
|
+
|
|
36
|
+
`NativeModuleHealer` already writes `HealEvent`s to
|
|
37
|
+
`<stateDir>/native-module-heals.jsonl` and `console.error`s on failure. Its
|
|
38
|
+
own jsdoc names `DegradationReporter` as a consumer ("consumed by health
|
|
39
|
+
checks and DegradationReporter") but no consumer was ever wired. This PR
|
|
40
|
+
fills the gap with the minimum surface area required:
|
|
41
|
+
|
|
42
|
+
1. `NativeModuleHealer.onHealEvent(listener)` — public listener
|
|
43
|
+
registration on the existing singleton. Listener fires after every
|
|
44
|
+
persisted `HealEvent` (success and failure). Returns an unsubscribe
|
|
45
|
+
function. Listener errors are swallowed inside the dispatch loop so
|
|
46
|
+
observability cannot break the heal path itself.
|
|
47
|
+
2. `src/monitoring/NativeHealDegradationBridge.ts` — new ~70-line module.
|
|
48
|
+
Subscribes to the healer; on a failure event, calls
|
|
49
|
+
`DegradationReporter.report(...)` with a component-specific impact
|
|
50
|
+
line. Successful heals are silent on purpose (the feature recovered,
|
|
51
|
+
no user action required). Dedupes per component within the process
|
|
52
|
+
life so a future heal-retry surface cannot spam the alert path.
|
|
53
|
+
3. `AgentServer` — one new import + one new call (`bridgeNativeHealToDegradation()`)
|
|
54
|
+
placed immediately after the existing `NativeModuleHealer.configure(...)`
|
|
55
|
+
line, inside the same `if (options.config.stateDir)` block.
|
|
56
|
+
|
|
57
|
+
Files touched:
|
|
58
|
+
- `src/memory/NativeModuleHealer.ts` — adds `HealEventListener` export,
|
|
59
|
+
`listeners` Set, `onHealEvent()` method, listener-clear in
|
|
60
|
+
`resetForTesting()`, listener-dispatch in `logHealEvent()`.
|
|
61
|
+
- `src/monitoring/NativeHealDegradationBridge.ts` — new module.
|
|
62
|
+
- `src/server/AgentServer.ts` — one import + one call.
|
|
63
|
+
- `tests/unit/NativeHealDegradationBridge.test.ts` — 9 new tests covering
|
|
64
|
+
failure→report, success-silent, per-component dedupe, unknown-component
|
|
65
|
+
fallback impact, missing-field reason fallback, unsubscribe, real
|
|
66
|
+
listener dispatch through `healBetterSqlite3Sync` with a mocked install
|
|
67
|
+
prefix, listener-error isolation, and listener cleanup.
|
|
68
|
+
- `upgrades/NEXT.md` — entry, evidence paragraph, user message, two
|
|
69
|
+
capability-table rows.
|
|
70
|
+
|
|
71
|
+
## Decision-point inventory
|
|
72
|
+
|
|
73
|
+
- `NativeModuleHealer.logHealEvent` (deterministic authority over heal
|
|
74
|
+
persistence) — **extend** — adds a listener-dispatch step after the
|
|
75
|
+
existing jsonl write. Listener errors are swallowed; the heal contract
|
|
76
|
+
is unchanged.
|
|
77
|
+
- `AgentServer` startup (composition root) — **extend** — adds a single
|
|
78
|
+
subscriber registration call. No new state.
|
|
79
|
+
- `DegradationReporter.report` — **consume only**, no surface change.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 1. Over-block
|
|
84
|
+
|
|
85
|
+
**What legitimate inputs does this change reject that it shouldn't?**
|
|
86
|
+
|
|
87
|
+
None. No new gating, no new filtering, no rejection logic. The bridge
|
|
88
|
+
adds a degradation event on heal *failure* and stays silent on heal
|
|
89
|
+
*success*. Successful heals continue to behave identically to today
|
|
90
|
+
(the feature opens, the user sees nothing).
|
|
91
|
+
|
|
92
|
+
## 2. Under-block
|
|
93
|
+
|
|
94
|
+
**What failure modes does this still miss?**
|
|
95
|
+
|
|
96
|
+
- **CLI commands that hit the healer outside the server process.** A
|
|
97
|
+
user running `instar memory ...` against an ABI-mismatched
|
|
98
|
+
better-sqlite3 will get the existing `console.error` and a thrown error
|
|
99
|
+
but no Telegram alert — `AgentServer` is the only registration site.
|
|
100
|
+
This is the same scope as the existing DegradationReporter wiring
|
|
101
|
+
(CLI commands never had the Telegram alert path anyway). The CLI user
|
|
102
|
+
sees the error on stdout, which is the appropriate surface for a
|
|
103
|
+
foreground command.
|
|
104
|
+
- **Multiple AgentServer instantiations in one process.** Each
|
|
105
|
+
`bridgeNativeHealToDegradation()` call adds a new listener with a
|
|
106
|
+
fresh dedupe Set. In production AgentServer is a singleton; in tests
|
|
107
|
+
callers pass an explicit reporter. Not a real risk; documented as a
|
|
108
|
+
YAGNI in the bridge module.
|
|
109
|
+
- **Build manifest / pre-load drift detection.** Out of scope — covered
|
|
110
|
+
in topic 11013's earlier design proposal as a separate (lower
|
|
111
|
+
priority) item. The heal works without it.
|
|
112
|
+
|
|
113
|
+
## 3. Level-of-abstraction fit
|
|
114
|
+
|
|
115
|
+
**Is this at the right layer?**
|
|
116
|
+
|
|
117
|
+
Yes. The healer is the existing authority over native-module rebuilds
|
|
118
|
+
and the canonical producer of `HealEvent`s. DegradationReporter is the
|
|
119
|
+
existing authority over "feature-fallback → user-visible alert."
|
|
120
|
+
Bridging them in a third file keeps each side single-responsibility:
|
|
121
|
+
the healer doesn't import the reporter (no `src/memory/*` →
|
|
122
|
+
`src/monitoring/*` coupling beyond the bridge), the reporter doesn't
|
|
123
|
+
know about the healer at all, and the bridge is a pure subscriber that
|
|
124
|
+
can be unit-tested with a synthesized `HealEvent`.
|
|
125
|
+
|
|
126
|
+
`AgentServer` is the correct composition root for the registration —
|
|
127
|
+
it's where `NativeModuleHealer.configure(...)` already happens, so the
|
|
128
|
+
two lines stay co-located.
|
|
129
|
+
|
|
130
|
+
## 4. Signal vs authority compliance
|
|
131
|
+
|
|
132
|
+
**Reference:** the principle in MEMORY.md (`feedback_signal_vs_authority`):
|
|
133
|
+
"Brittle/low-context filters detect and emit signals. Only a higher-level
|
|
134
|
+
intelligent gate with full context has blocking authority."
|
|
135
|
+
|
|
136
|
+
- The **healer** is the deterministic authority over native-module
|
|
137
|
+
rebuilds. Unchanged.
|
|
138
|
+
- The **bridge** is a pure signal transducer — `HealEvent` (signal) →
|
|
139
|
+
`DegradationEvent` (signal). It has no blocking authority, no decision
|
|
140
|
+
surface, no LLM call.
|
|
141
|
+
- `DegradationReporter` is the higher-level alert authority. Its
|
|
142
|
+
existing dispatch logic (Remediator routing, tone-gate, alert
|
|
143
|
+
cooldown) is unchanged. The bridge is just a new emitter feeding the
|
|
144
|
+
same authority.
|
|
145
|
+
|
|
146
|
+
## 5. Interactions / cross-component effects
|
|
147
|
+
|
|
148
|
+
- **Existing W-1 Remediator path.** When `NativeModuleHealer.invokeFromRemediator(ctx)`
|
|
149
|
+
is the entry point, it still calls `healBetterSqlite3Sync` →
|
|
150
|
+
`logHealEvent` → listener dispatch. The bridge fires once for the
|
|
151
|
+
same component just as it does for the legacy `openWithHeal` path —
|
|
152
|
+
per-component dedupe absorbs both paths. No double-alert risk.
|
|
153
|
+
- **DegradationReporter remediator-routing.** Reports flow through
|
|
154
|
+
`_normalize()` like any other legacy `.report(...)` call. They will be
|
|
155
|
+
tagged `provenance: 'free-text'` and (per §A6) won't match any runbook
|
|
156
|
+
prefilter — they route to `no-matching-runbook` and feed
|
|
157
|
+
NovelFailureReviewer's clustering pipeline, which is appropriate for
|
|
158
|
+
"the W-1 heal couldn't repair its own target" alerts.
|
|
159
|
+
- **Telegram tone-gate.** Goes through the existing pipeline; the
|
|
160
|
+
default safe fallback template applies if the candidate text is
|
|
161
|
+
rejected.
|
|
162
|
+
- **Persistence.** `degradations.json` (existing) gains entries for
|
|
163
|
+
heal failures. Bounded to last 100 events per existing rotation.
|
|
164
|
+
|
|
165
|
+
## 6. Rollback cost
|
|
166
|
+
|
|
167
|
+
Trivial. Revert is 5 file edits (3 modifications + 2 deletions). No
|
|
168
|
+
schema, no config, no migration, no on-disk format change. The
|
|
169
|
+
`native-module-heals.jsonl` log and `console.error` path are unchanged,
|
|
170
|
+
so observability survives a revert.
|
|
171
|
+
|
|
172
|
+
## 7. Migration parity check
|
|
173
|
+
|
|
174
|
+
Not applicable. The change is server-internal. No agent-installed file
|
|
175
|
+
changes (no `.claude/settings.json`, no `.instar/config.json`, no
|
|
176
|
+
CLAUDE.md template, no hook scripts, no built-in skills) — existing
|
|
177
|
+
agents pick this up automatically when they restart against the new
|
|
178
|
+
`instar` binary, which is the normal update path. The Migration Parity
|
|
179
|
+
Standard's enumerated trigger list does not include `src/server/*`.
|
|
180
|
+
|
|
181
|
+
## 8. Test integrity
|
|
182
|
+
|
|
183
|
+
Per the Testing Integrity Standard (`docs/specs/TESTING-INTEGRITY-SPEC.md`):
|
|
184
|
+
|
|
185
|
+
- **Tier 1 (unit).** 9 tests in
|
|
186
|
+
`tests/unit/NativeHealDegradationBridge.test.ts` — listener-dispatch,
|
|
187
|
+
failure-reports / success-silent / per-component-dedupe / unknown-component-fallback /
|
|
188
|
+
missing-field reason / unsubscribe; plus three on
|
|
189
|
+
`NativeModuleHealer.onHealEvent` exercising the real
|
|
190
|
+
`healBetterSqlite3Sync` early-exit path with a mocked install-prefix
|
|
191
|
+
resolver (listener invocation, listener-error isolation, unsubscribe).
|
|
192
|
+
- **Tier 2 (integration).** Not added — the bridge is a process-internal
|
|
193
|
+
subscriber with no HTTP surface. The existing
|
|
194
|
+
`tests/integration/degradation-reporter-*` suites continue to cover
|
|
195
|
+
the reporter's HTTP-visible behavior; this PR adds one more emitter
|
|
196
|
+
to that same path, no new routes.
|
|
197
|
+
- **Tier 3 (E2E lifecycle).** Not added — the wiring lives inside the
|
|
198
|
+
same `if (options.config.stateDir)` block that already conditions
|
|
199
|
+
`NativeModuleHealer.configure(...)` and `TokenLedger` construction
|
|
200
|
+
(which both have Tier-3 coverage). Adding a parallel boot-time
|
|
201
|
+
bridge-presence test would only restate that block's existing E2E
|
|
202
|
+
guarantee.
|
|
203
|
+
|
|
204
|
+
Pre-existing related suites (`NativeModuleHealer.test.ts` 17 tests,
|
|
205
|
+
`NativeModuleHealer-invokeFromRemediator.test.ts`, `degradation-reporter*`
|
|
206
|
+
22 tests) pass unchanged after this PR.
|