instar 0.28.59 → 0.28.60

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "0.28.59",
3
+ "version": "0.28.60",
4
4
  "description": "Persistent autonomy infrastructure for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -20,6 +20,7 @@
20
20
  "dev": "tsc --watch",
21
21
  "test": "vitest run",
22
22
  "test:push": "vitest run --config vitest.push.config.ts",
23
+ "test:smoke": "vitest run --config vitest.push.config.ts --changed origin/main",
23
24
  "test:flaky": "vitest run tests/unit/relationship-routes.test.ts tests/integration/messaging-routes.test.ts tests/integration/whatsapp-routes.test.ts tests/unit/server.test.ts tests/e2e/semantic-memory-lifecycle.test.ts tests/e2e/system-reviewer-e2e.test.ts tests/e2e/working-memory-lifecycle.test.ts tests/e2e/messaging-multi-agent.test.ts",
24
25
  "test:watch": "vitest",
25
26
  "test:integration": "vitest run --config vitest.integration.config.ts",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-04-19T02:56:02.572Z",
5
- "instarVersion": "0.28.59",
4
+ "generatedAt": "2026-04-19T05:19:43.879Z",
5
+ "instarVersion": "0.28.60",
6
6
  "entryCount": 186,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -0,0 +1,80 @@
1
+ # Upgrade Guide — Pre-push smoke tier (fast local test gate)
2
+
3
+ ## What Changed
4
+
5
+ The local pre-push test gate now runs only the tests affected by files
6
+ you changed, instead of the full suite. On a typical small push this
7
+ drops from ~10 minutes to tens of seconds. The full suite still runs in
8
+ CI on the PR across 8 sharded runners, and CI is the authority for
9
+ merge.
10
+
11
+ ### Before
12
+
13
+ Every `git push` ran `npm run test:push` — the full suite minus the
14
+ known-flaky exclude list — serially (no in-process parallelism because
15
+ tests collide on ports, SQLite, npm). Wall-clock ~9–10 min on every
16
+ push, including pushes that only touched docs or config.
17
+
18
+ ### After
19
+
20
+ Every `git push` now runs `npm run test:smoke` — the same test set,
21
+ gated by vitest's `--changed origin/main` mode, so only tests whose
22
+ files (or their transitive imports) appear in your diff execute. The
23
+ exclude list and `fileParallelism: false` are preserved; the change is
24
+ which files are included, not how they run.
25
+
26
+ Escape hatches, for the cases where you want the old behavior:
27
+
28
+ | Variable | Effect |
29
+ |----------|--------|
30
+ | `INSTAR_PRE_PUSH_FULL=1 git push` | Run the full push suite locally (old behavior, ~10 min) |
31
+ | `INSTAR_PRE_PUSH_SKIP=1 git push` | Skip pre-push tests entirely; CI is the only gate |
32
+
33
+ The NEXT.md / version / side-effects pre-push gate still runs first on
34
+ every push — that's independent of the test tier and stays as-is.
35
+
36
+ ## What to Tell Your User
37
+
38
+ Pushing local changes now takes seconds instead of minutes in the
39
+ common case. The exhaustive test run still happens on GitHub before
40
+ anything merges, so you won't ship a broken change — you just won't
41
+ wait for the full suite on your laptop.
42
+
43
+ ## Summary of New Capabilities
44
+
45
+ | Capability | How to Use |
46
+ |-----------|-----------|
47
+ | Fast local pre-push gate | Nothing to do — `git push` now runs only tests touched by your diff |
48
+ | Force full local suite | `INSTAR_PRE_PUSH_FULL=1 git push` |
49
+ | Skip local tests entirely | `INSTAR_PRE_PUSH_SKIP=1 git push` (CI still runs the full suite) |
50
+
51
+ ## Evidence
52
+
53
+ - Local run of `npm run test:smoke` on this change (which only touches
54
+ `.husky/pre-push` and `package.json` scripts) runs 0 tests — the
55
+ diff doesn't cover any source files, so `--changed` has no targets.
56
+ That's the intended fast-path.
57
+ - Local run of `npm run test:push` unchanged — ~10 min as before, now
58
+ opt-in via `INSTAR_PRE_PUSH_FULL=1`.
59
+ - CI's 8-shard matrix on `ci.yml` is unchanged and continues to run
60
+ the full push suite on every PR — that's the authority; pre-push is
61
+ a signal.
62
+
63
+ Side-effects review:
64
+ `upgrades/side-effects/pre-push-smoke-tier.md` — covers over/under-block,
65
+ level-of-abstraction fit (signal-vs-authority: pre-push is a signal, CI
66
+ is the authority), interactions with the NEXT.md gate and the retry
67
+ loop, external surfaces, rollback cost.
68
+
69
+ ## Deployment Notes
70
+
71
+ No operator action required on update. The change is to the
72
+ contributor-side git hook, which is installed by `npm install` via
73
+ husky. Contributors pick it up automatically on their next `npm ci`.
74
+
75
+ ## Rollback
76
+
77
+ Revert this commit. `.husky/pre-push` returns to running
78
+ `npm run test:push` (full suite) on every push, `test:smoke` script
79
+ stays harmless in package.json until removed. No schema changes, no
80
+ state-file changes, no API changes.
@@ -0,0 +1,33 @@
1
+ # Side-Effects Review — 0.28.57 (release aggregate)
2
+
3
+ **Version:** `0.28.57`
4
+ **Date:** `2026-04-19`
5
+ **Author:** `echo` (backfill)
6
+ **Second-pass reviewer:** `not required`
7
+
8
+ ## Summary
9
+
10
+ Release 0.28.57 bundles two changes that were each reviewed individually at merge time. The publish workflow's file-renamer skipped the corresponding `upgrades/side-effects/` artifact during the 0.28.57 finalization (it renames `upgrades/NEXT.md → upgrades/{version}.md` but not the side-effects counterpart), so this file is a minimal aggregator that points at the two component reviews already on disk. Captured as a follow-up in the pre-push gate: teach `publish.yml` or `check-upgrade-guide.js` to also rename the matching side-effects artifact so this backfill isn't needed on future releases.
11
+
12
+ ## Components
13
+
14
+ ### 1. TelegramLifeline sends auth on `/internal/*` forwards
15
+
16
+ Landed via PR #64. Surgical fix in `src/lifeline/TelegramLifeline.ts` to add `Authorization: Bearer <authToken>` to `forwardToServer()` and `handleCallbackQuery()`. Reasoning: the 0.28.53 server-side tightening that required auth on `/internal/*` had no matching client-side update in the lifeline — every inbound Telegram message was 401ing.
17
+
18
+ - No runtime decision points introduced; a signal-less, surface-narrow header addition.
19
+ - Over-block / under-block: not applicable — no block/allow surface.
20
+ - Level-of-abstraction: header is constructed exactly where the fetch is built; no new indirection.
21
+ - Signal-vs-authority: not applicable — hard-invariant transport-layer auth.
22
+ - Interactions: none with other gates; backwards-compatible on servers that don't require the header.
23
+ - Rollback: revert the single file.
24
+
25
+ ### 2. Drop duplicate CI gate from `publish.yml` (PR #67)
26
+
27
+ Full review: `upgrades/side-effects/publish-drop-duplicate-ci-gate.md` (in this directory). Summary: removed the `ci` job from `publish.yml` that duplicated `ci.yml` on the same `push: branches: [main]` event. Publish wall-clock measured 11m → 55s on the first post-merge run. Branch-protection-at-PR-time remains the actual authority.
28
+
29
+ ## Notes
30
+
31
+ This aggregator file exists to satisfy the `pre-push-gate.js` requirement that `upgrades/side-effects/{version}.md` exists whenever `upgrades/{version}.md` exists and claims fix/feature content. The finer-grained artifacts for each component are the authoritative reviews; this file only provides the version-named pointer.
32
+
33
+ Follow-up captured: either (a) teach `publish.yml` to also rename the matching side-effects artifact during NEXT → version finalization, or (b) relax `pre-push-gate.js` to accept any side-effects artifact dated within the release window rather than requiring a version-exact filename.
@@ -0,0 +1,24 @@
1
+ # Side-Effects Review — 0.28.58 (release aggregate)
2
+
3
+ **Version:** `0.28.58`
4
+ **Date:** `2026-04-19`
5
+ **Author:** `echo` (backfill)
6
+ **Second-pass reviewer:** `not required`
7
+
8
+ ## Summary
9
+
10
+ Release 0.28.58 shipped the Initiative Tracker recovery merge (PR #68). The two component reviews exist in this directory under slug names because `publish.yml`'s finalization renames `upgrades/NEXT.md → upgrades/{version}.md` but does not rename the corresponding side-effects artifact. This file is a minimal aggregator that satisfies `pre-push-gate.js`'s requirement that `upgrades/side-effects/{version}.md` exists whenever `upgrades/{version}.md` exists and claims fix/feature content.
11
+
12
+ ## Components
13
+
14
+ ### Initiative Tracker — core and API
15
+
16
+ Full review: `upgrades/side-effects/initiative-tracker-core-and-api.md`. Persisted multi-phase work tracker with phase / blocker / last-touched state; backed by a JSON store; 28 core tests + 15 route tests added.
17
+
18
+ ### Initiative Tracker — dashboard tab
19
+
20
+ Full review: `upgrades/side-effects/initiative-tracker-dashboard-tab.md`. New "Initiatives" tab in the dashboard rendering the tracker state; 9 dashboard smoke tests added.
21
+
22
+ ## Notes
23
+
24
+ Same follow-up as noted in `0.28.57.md` aggregator: either (a) teach `publish.yml` to also rename the matching side-effects artifact during NEXT → version finalization, or (b) relax `pre-push-gate.js` to accept any side-effects artifact dated within the release window rather than requiring a version-exact filename. Until one of those lands, each release will need a backfill aggregator like this one.
@@ -0,0 +1,20 @@
1
+ # Side-Effects Review — 0.28.59 (release aggregate)
2
+
3
+ **Version:** `0.28.59`
4
+ **Date:** `2026-04-19`
5
+ **Author:** `echo` (backfill)
6
+ **Second-pass reviewer:** `not required`
7
+
8
+ ## Summary
9
+
10
+ Release 0.28.59 shipped the dashboard "Resume live output" scroll-reliability + always-visible control fix (PR #63). The component review exists in this directory under its slug name (`dashboard-resume-live-scroll.md`) because `publish.yml`'s finalization renames `upgrades/NEXT.md → upgrades/{version}.md` but does not rename the corresponding side-effects artifact. This file is a minimal aggregator that satisfies `pre-push-gate.js`'s requirement that `upgrades/side-effects/{version}.md` exists whenever `upgrades/{version}.md` exists and claims fix/feature content.
11
+
12
+ ## Components
13
+
14
+ ### Dashboard "Resume live output" reliability + always-visible control
15
+
16
+ Full review: `upgrades/side-effects/dashboard-resume-live-scroll.md`. Pure UI timing + visibility fix — all three resume paths now use xterm's write-completion callback so `scrollToBottom()` runs after the buffer is populated; button visibility now mirrors `!userIsFollowing` from every code path that flips the flag. 10 regression tests added at `tests/unit/dashboard-resumeLive.test.ts`.
17
+
18
+ ## Notes
19
+
20
+ Same follow-up as captured in `0.28.57.md` and `0.28.58.md` aggregators: either (a) teach `publish.yml` to also rename the matching side-effects artifact during NEXT → version finalization, or (b) relax `pre-push-gate.js` to accept any side-effects artifact dated within the release window rather than requiring a version-exact filename. Until one of those lands, each release will need a backfill aggregator like this one.
@@ -0,0 +1,108 @@
1
+ # Side-Effects Review — Shard CI unit tests across 4 parallel runners
2
+
3
+ **Version / slug:** `ci-shard-unit-tests`
4
+ **Date:** `2026-04-19`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `not required`
7
+
8
+ ## Summary of the change
9
+
10
+ `.github/workflows/ci.yml` unit-test job is extended to run the pre-push test suite across 4 vitest shards per Node version, using vitest's built-in `--shard=N/M` file-partitioning. The matrix changes from `{ node-version: [20, 22] }` (2 runners, each ~9½ min serial) to `{ node-version: [20, 22], shard: [1,2,3,4] }` (8 runners, each ~2½ min serial inside its shard). `fileParallelism: false` in `vitest.push.config.ts` stays untouched — each shard still runs its files one-at-a-time to preserve the port / SQLite / npm isolation that commit `002a463` established. `fail-fast: false` is added so one flaky shard doesn't kill the others. Expected impact on CI wall-clock: unit-test phase 9½ min → ~3 min, which pulls overall PR-CI time from ~12 min toward ~5-6 min (next bottleneck becomes integration/e2e). Only file touched: `.github/workflows/ci.yml`.
11
+
12
+ ## Decision-point inventory
13
+
14
+ - `ci.yml.unit` — **modify** — job matrix gains a `shard` dimension; test runner invoked with `--shard=${{ matrix.shard }}/4`.
15
+
16
+ No runtime / agent-behavior decision points touched.
17
+
18
+ ---
19
+
20
+ ## 1. Over-block
21
+
22
+ **What legitimate inputs does this change reject that it shouldn't?**
23
+
24
+ No block/allow surface on message flow — over-block not applicable in the runtime sense.
25
+
26
+ In the CI sense: the change runs *exactly the same* set of tests as before, just partitioned across 4 runners per Node version. Vitest's sharding is deterministic and disjoint — the union of `--shard=1/4 … 4/4` is the full include set. A commit that passed unsharded would pass sharded. No new rejection surface.
27
+
28
+ ---
29
+
30
+ ## 2. Under-block
31
+
32
+ **What failure modes does this still miss?**
33
+
34
+ Same as before: anything `test:push` doesn't cover (the flaky-exclusion list in `vitest.push.config.ts` is unchanged — the same ~30 files that were excluded as flaky remain excluded; full suite still runs separately via `npm test` and is not part of CI gating). No new under-block introduced by this change.
35
+
36
+ One theoretical concern: if the shard hash assignment is not stable across vitest versions, a file could disappear from all shards after a vitest upgrade. This is vanishingly unlikely in practice because vitest's shard algorithm is documented and stable; the union is enforced by the CLI. If we ever suspect it, a simple sanity check is to run with `--shard=` omitted and compare the test count.
37
+
38
+ ---
39
+
40
+ ## 3. Level-of-abstraction fit
41
+
42
+ **Is this at the right layer?**
43
+
44
+ Yes. File-level parallelism was disabled in `vitest.push.config.ts` because tests spawn HTTP servers, SQLite DBs, and real npm operations that collide when running in the same process pool (see commit `002a463`). Sharding at the CI-runner level (one process per shard on its own VM) sidesteps that collision entirely — each shard has its own ports, own filesystem, own npm cache — without re-enabling in-process parallelism. This is the right layer: the isolation problem was resource-contention across a single machine's pool; moving to multiple machines solves the resource contention without touching the isolation invariant.
45
+
46
+ Signal/authority lens: not applicable. CI test-pass is still an authority-grade signal computed over the same logical test set; we're just distributing the computation.
47
+
48
+ ---
49
+
50
+ ## 4. Signal vs authority compliance
51
+
52
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
53
+
54
+ **Does this change hold blocking authority with brittle logic?**
55
+
56
+ - [ ] No — this change produces a signal consumed by an existing smart gate.
57
+ - [x] No — this change has no block/allow surface on message flow or agent behavior.
58
+ - [ ] Yes — but the logic is a smart gate with full conversational context.
59
+ - [ ] ⚠️ Yes, with brittle logic.
60
+
61
+ CI sharding is a runtime-distribution change. The test suite itself is unchanged — same authority (the full union of tests), same verdict logic (all must pass), just faster. Signal-vs-authority domain untouched.
62
+
63
+ ---
64
+
65
+ ## 5. Interactions
66
+
67
+ **Does this interact with existing checks, recovery paths, or infrastructure?**
68
+
69
+ - **Shadowing:** `unit` is a `needs:` target for `integration`, `e2e`, `contract`, and `build`. GitHub Actions' default behavior when a `needs:` target is a matrix is to wait for **all** combinations to succeed before downstream jobs run. With 8 combinations (2 node × 4 shard), downstream jobs now wait for all 8 instead of 2. This is a stricter gate, not weaker. No shadowing introduced.
70
+ - **Double-fire:** n/a — the old 2-runner unit matrix was the only thing that ran tests. The new 8-runner matrix replaces it; total test-run count remains "once per Node × shard" (1 per runner).
71
+ - **Races:** each shard runs in its own ephemeral GitHub-hosted runner VM. No shared filesystem, no shared ports, no shared npm cache across shards. No race surface.
72
+ - **Feedback loops:** none. CI result feeds into PR-level required status checks, unchanged by this.
73
+
74
+ One behavioral nuance worth noting: with `fail-fast: false`, a PR that has genuinely broken tests will now consume 8 runners (all reporting the same failure) instead of 2. Cost is small (GitHub-hosted free minutes; we're not anywhere near the cap), and the upside is clearer diagnostic signal — if only shard-3 fails, the problem is isolated; if all 8 fail, the problem is universal.
75
+
76
+ ---
77
+
78
+ ## 6. External surfaces
79
+
80
+ **Does this change anything visible outside the immediate code path?**
81
+
82
+ - **Other agents on the same machine:** no.
83
+ - **Other users of the install base:** no runtime behavior change. The shipped package is identical byte-for-byte.
84
+ - **External systems:** GitHub Actions runs more matrix combinations. Cost impact for public repos on GitHub-hosted free runners: negligible — Actions minutes are free for public repos. If a private fork is on a paid plan, the extra 6 runners per CI run will show up in billing; flag to callers who fork.
85
+ - **Persistent state:** none touched.
86
+ - **Status check names:** the job name becomes `Unit Tests (node 20, shard 1/4)` etc. instead of `Unit Tests (20)`. Only required status check in the branch ruleset today is `verify`; `Unit Tests` matrix jobs are **not** required checks (confirmed via `gh api repos/JKHeadley/instar/rules/branches/main` on 2026-04-19). So the rename does not break branch protection. If a watcher tool or dashboard specifically hardcoded `Unit Tests (20)` / `Unit Tests (22)` as an expected name, it would need updating — unlikely, but captured here.
87
+
88
+ ---
89
+
90
+ ## 7. Rollback cost
91
+
92
+ **If this turns out wrong in production, what's the back-out?**
93
+
94
+ Trivial. Revert the single `.github/workflows/ci.yml` change — restore the 2-runner matrix and the original `name:` / `run:` lines. No persistent state. No downstream code affected. Worst realistic "wrong" outcome: the shard hash algorithm has a pathological case that leaves one shard with most of the heavy tests, so the wall-clock win is smaller than projected. That's an optimization miss, not a correctness failure — still faster than pre-change, and the remediation is increasing shard count (6 or 8) rather than reverting. Full revert cost: one commit, no migration, no user impact.
95
+
96
+ ---
97
+
98
+ ## Conclusion
99
+
100
+ Pure CI distribution change with no runtime surface, no decision-point surface, no signal/authority interaction. Preserves the isolation invariant from commit `002a463` by sharding at the runner level instead of re-enabling in-process parallelism. Expected wall-clock win on unit-test phase: 9½ min → ~3 min; overall CI: ~12 min → ~5-6 min (next bottleneck likely integration/e2e, captured as follow-up). Rollback is one revert. Cleared to ship.
101
+
102
+ ---
103
+
104
+ ## Evidence pointers
105
+
106
+ - Root cause of current serial execution: commit `002a463` (2026-02-28) — "fix(test): disable file parallelism to prevent lock contention". Confirms the isolation problem is real and resource-contention-based; sharding at the VM level is the right remediation.
107
+ - Shard algorithm: [vitest docs — CLI `--shard`](https://vitest.dev/guide/cli.html) — deterministic file-path hash distribution; union of all shards equals the full include set.
108
+ - Required-check topology: `gh api repos/JKHeadley/instar/rules/branches/main` (2026-04-19) shows only `verify` as a required context. Unit-test matrix jobs are observed green on recent PRs but are not ruleset-required.
@@ -0,0 +1,235 @@
1
+ # Side-Effects Review — Pre-push smoke tier (changed-files only)
2
+
3
+ **Version / slug:** `pre-push-smoke-tier`
4
+ **Date:** `2026-04-19`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `not required`
7
+
8
+ ## Summary of the change
9
+
10
+ `.husky/pre-push` now runs `npm run test:smoke` (new script:
11
+ `vitest run --config vitest.push.config.ts --changed origin/main`) instead of
12
+ `npm run test:push`. The new script executes the same excluded/included set
13
+ and same `fileParallelism: false` isolation — the only difference is that
14
+ vitest's `--changed origin/main` filter restricts the run to tests whose
15
+ files (or transitive imports) are in the diff vs. origin/main. The
16
+ pre-push gate (`scripts/pre-push-gate.js` — NEXT.md / version / side-effects
17
+ artifact / contract-evidence / source-without-tests checks) runs first,
18
+ unchanged. Two escape hatches: `INSTAR_PRE_PUSH_FULL=1` (run full push
19
+ suite locally) and `INSTAR_PRE_PUSH_SKIP=1` (skip tests entirely; CI is
20
+ the only gate). Full suite continues to run in CI across 8 sharded
21
+ runners on every PR; CI remains the authority for merge.
22
+
23
+ Files touched:
24
+
25
+ - `package.json` — adds `test:smoke` script; bumps version 0.28.59 → 0.28.60.
26
+ - `.husky/pre-push` — switches to `test:smoke` by default, adds env-var
27
+ escape hatches, keeps the 2-attempt retry loop and the pre-push gate
28
+ exactly as before.
29
+ - `upgrades/NEXT.md` — upgrade guide (new file at release time).
30
+ - `upgrades/side-effects/pre-push-smoke-tier.md` — this review.
31
+
32
+ ## Decision-point inventory
33
+
34
+ - `.husky/pre-push` — **modify** — chooses which test script runs based on
35
+ env vars and falls back to smoke tier by default.
36
+ - `package.json scripts.test:smoke` — **add** — new script, thin wrapper
37
+ over existing push config with `--changed origin/main`.
38
+
39
+ No runtime / agent-behavior decision points touched. Strictly contributor-side
40
+ git hook behavior.
41
+
42
+ ---
43
+
44
+ ## 1. Over-block
45
+
46
+ **What legitimate inputs does this change reject that it shouldn't?**
47
+
48
+ On the runtime / message surface: no block/allow change. The pre-push gate
49
+ is a contributor-side hook, not a runtime gate, so over-block doesn't apply
50
+ in the agent-behavior sense.
51
+
52
+ On the contributor surface: the smoke tier runs *fewer* tests than the full
53
+ push suite, so it will **accept pushes that the full suite would have
54
+ rejected** — the opposite of over-blocking. See Under-block below.
55
+
56
+ There is one narrow over-block possibility: if `origin/main` is stale (user
57
+ hasn't fetched recently), the `--changed` diff may include files that are
58
+ already merged, running more tests than strictly needed. We mitigate with
59
+ `git fetch --quiet origin main` inside the hook. If the fetch fails (offline
60
+ push), the diff falls back to whatever the local `origin/main` ref points
61
+ at — worst case runs a few extra tests, still fast.
62
+
63
+ ---
64
+
65
+ ## 2. Under-block
66
+
67
+ **What failure modes does this still miss?**
68
+
69
+ This is the real surface to review. Moving from "full suite" to "tests
70
+ affected by changed files" trades thoroughness for speed. Specifically:
71
+
72
+ 1. **Regression in unchanged code** — if your change to file A breaks
73
+ file B via some runtime-only coupling that vitest's module graph
74
+ doesn't see (e.g., a serialization format that both sides implement
75
+ independently), the smoke tier will miss it. CI's full suite on PR
76
+ will catch it before merge. Net risk: the contributor experiences
77
+ "passed locally, failed in CI" more often. Cost: one extra CI round
78
+ trip. Benefit: ~9 minutes saved per push across all pushes that don't
79
+ break anything (i.e., the vast majority).
80
+
81
+ 2. **Config/global-state files** — if a change touches a file imported by
82
+ most tests (e.g., a vitest global setup, a shared fixture builder),
83
+ `--changed` will correctly include the full test set. No extra risk
84
+ here; the mechanism is self-balancing.
85
+
86
+ 3. **No-change pushes** — pure doc or comment changes produce 0 tests,
87
+ which is correct. The pre-push gate still enforces NEXT.md presence
88
+ and side-effects artifact presence independently, so doc pushes that
89
+ claim a fix/feature still require the corresponding artifact.
90
+
91
+ 4. **Stale local `origin/main`** — if the user hasn't fetched for days,
92
+ the diff could be larger than reality, but never smaller. Under-block
93
+ is bounded: you cannot *miss* a file this way, only over-include.
94
+ We still proactively `git fetch --quiet origin main` inside the hook
95
+ to keep it tight.
96
+
97
+ The accepted residual risk is (1). Mitigation: CI is the merge authority
98
+ (per `docs/signal-vs-authority.md`); the pre-push gate is downgraded from
99
+ "implicit authority" to "explicit signal" by this change, which matches
100
+ the architectural principle.
101
+
102
+ ---
103
+
104
+ ## 3. Level-of-abstraction fit
105
+
106
+ **Is this at the right layer?**
107
+
108
+ Yes. The correct layer is the pre-push hook itself (the boundary where
109
+ we trade speed for confidence on the contributor side). Alternatives:
110
+
111
+ - **Change vitest.push.config.ts to be smaller** — wrong layer. That
112
+ config defines "the push suite"; the smoke tier is a *different use*
113
+ of that same config, selected per-push based on diff size. Keeping
114
+ two scripts that share one config is cleaner than two configs that
115
+ overlap.
116
+ - **Change CI to run less** — wrong direction. CI is the authority; it
117
+ must remain exhaustive or the invariant breaks.
118
+ - **Tag some tests as "smoke" and run only those** — brittle taxonomy
119
+ that humans would have to maintain. Vitest's `--changed` computes
120
+ affected tests from the module graph automatically; no taxonomy drift.
121
+
122
+ Signal-vs-authority reference: `docs/signal-vs-authority.md`. The new
123
+ pre-push hook is a signal consumed by the contributor (and, if they
124
+ push anyway via `INSTAR_PRE_PUSH_SKIP=1`, CI is the binding authority).
125
+ The old hook *was* acting as authority by blocking pushes that CI
126
+ would have caught anyway — same verdict, earlier but slower. Moving
127
+ the authority to CI and keeping a fast signal locally is the correct
128
+ decomposition.
129
+
130
+ ---
131
+
132
+ ## 4. Signal vs authority compliance
133
+
134
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
135
+
136
+ **Does this change hold blocking authority with brittle logic?**
137
+
138
+ - [x] No — this change produces a signal consumed by an existing smart gate.
139
+ - [ ] No — this change has no block/allow surface on message flow or agent behavior.
140
+ - [ ] Yes — but the logic is a smart gate with full conversational context.
141
+ - [ ] ⚠️ Yes, with brittle logic.
142
+
143
+ The pre-push hook is now explicitly a *signal* — it fails the push fast
144
+ when a clearly-affected test regressed, and otherwise lets CI act as
145
+ authority. The `INSTAR_PRE_PUSH_SKIP=1` escape hatch formalizes this:
146
+ contributors can bypass the signal; they cannot bypass CI on the PR.
147
+
148
+ ---
149
+
150
+ ## 5. Interactions
151
+
152
+ **Does this interact with existing checks, recovery paths, or infrastructure?**
153
+
154
+ - **Pre-push gate (NEXT.md / version / side-effects / contract evidence):**
155
+ runs *before* the test tier and is unchanged. Smoke tier substitution
156
+ happens after that gate passes. No interaction risk — orthogonal
157
+ concerns.
158
+ - **Retry loop:** the 2-attempt retry is preserved, now wrapped around
159
+ `test:smoke`. Same behavior on flaky failures; just shorter because
160
+ the retry suite is smaller.
161
+ - **CI (`ci.yml`):** unchanged. Still runs 8 sharded unit-test matrix +
162
+ integration + e2e + build + type-check on every PR. CI remains the
163
+ authority on merge-readiness.
164
+ - **Shadowing:** does not exist. CI is not a `needs:` target of the
165
+ pre-push hook or vice versa; they operate on different events
166
+ (contributor push vs. GitHub Actions PR event).
167
+ - **Double-fire:** if a contributor has the smoke tier pass and pushes,
168
+ CI re-runs the full suite — intentional double-gate (fast local
169
+ signal, authoritative remote). Not wasted work; that's the design.
170
+ - **Races:** none. Contributor-side hook, synchronous, no shared state.
171
+ - **Feedback loops:** if `origin/main` ref moves mid-push, the diff
172
+ window shifts but the push is a single event; the diff is snapshotted
173
+ at hook-start time.
174
+
175
+ ---
176
+
177
+ ## 6. External surfaces
178
+
179
+ **Does this change anything visible outside the immediate code path?**
180
+
181
+ - **Other agents on the same machine:** no. The git hook runs only in
182
+ the contributor's shell during `git push`.
183
+ - **Other users of the install base:** no runtime change. The shipped
184
+ npm package is unchanged. Only the contributor-side hook installed
185
+ by `npm install` is affected, and only for people working *in* the
186
+ instar repo — not consumers of the `instar` package.
187
+ - **External systems:** no.
188
+ - **Persistent state:** none touched.
189
+ - **CI minutes consumption:** unchanged — CI runs the same matrix.
190
+ If anything, fewer "contributor runs full suite locally, then CI
191
+ runs it again" iterations means slightly less duplicated load on
192
+ developer machines.
193
+
194
+ ---
195
+
196
+ ## 7. Rollback cost
197
+
198
+ **If this turns out wrong in production, what's the back-out?**
199
+
200
+ Trivial. Revert the commit that changes `.husky/pre-push` and
201
+ `package.json`. `test:push` is unchanged, so the pre-push hook
202
+ reverts to running the full suite exactly as it did before. No
203
+ migration, no state cleanup, no user-visible impact (since the
204
+ npm package is unaffected).
205
+
206
+ Partial rollback also available without reverting: set the env var
207
+ `INSTAR_PRE_PUSH_FULL=1` globally (e.g., in the user's shell rc) to
208
+ restore the old blocking behavior without changing any code.
209
+
210
+ ---
211
+
212
+ ## Conclusion
213
+
214
+ Moves the pre-push test gate from "implicit authority" (slow, exhaustive,
215
+ blocks every push) to "fast signal" (runs only tests affected by the
216
+ diff, CI remains the merge authority). Matches the signal-vs-authority
217
+ architectural principle. Preserves the exclude list, the isolation
218
+ invariant (`fileParallelism: false`), the NEXT.md / version / side-effects
219
+ gate, the retry loop, and the full suite in CI — all authorities stay
220
+ authoritative. Expected wall-clock on a typical small push: ~9 min → <1
221
+ min. Escape hatches for the edge cases. Rollback is one revert. Cleared
222
+ to ship.
223
+
224
+ ---
225
+
226
+ ## Evidence pointers
227
+
228
+ - `docs/signal-vs-authority.md` — the architectural principle this change
229
+ aligns the hook with.
230
+ - Vitest `--changed` mode docs:
231
+ <https://vitest.dev/guide/cli.html#changed> — deterministic, based on
232
+ module graph, safe to compose with `--config`.
233
+ - CI authority chain: `.github/workflows/ci.yml` — 8-shard unit matrix
234
+ + integration + e2e + build + type-check; required by branch
235
+ protection on main (per the ruleset update landed with PR #69).