instar 1.3.29 → 1.3.31
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/.claude/skills/autonomous/hooks/autonomous-stop-hook.sh +32 -0
- package/dashboard/index.html +112 -0
- package/dashboard/process-health.js +504 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +22 -3
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/monitoring/FailureLedger.d.ts +11 -3
- package/dist/monitoring/FailureLedger.d.ts.map +1 -1
- package/dist/monitoring/FailureLedger.js +22 -5
- package/dist/monitoring/FailureLedger.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +6 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +48 -4
- package/dist/server/routes.js.map +1 -1
- package/package.json +2 -1
- package/scripts/analyze-release.js +21 -7
- package/src/data/builtin-manifest.json +62 -62
- package/src/scaffold/templates.ts +6 -0
- package/upgrades/1.3.30.md +35 -0
- package/upgrades/1.3.31.md +29 -0
- package/upgrades/side-effects/notify-on-stop-layer-a.md +31 -0
- package/upgrades/side-effects/process-health-dashboard-tab.md +59 -0
- package/upgrades/side-effects/release-readiness-visibility-pr1.md +26 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- bump: minor -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
Adds the **Process Health dashboard tab** — a calm, human-readable window into the Failure-Learning Loop (spec `docs/specs/PROCESS-HEALTH-DASHBOARD-TAB-SPEC.md`, converged v4, approved). Until now the loop's findings were API-only (reachable via `/failures*` curl); this tab surfaces them in the dashboard you actually look at.
|
|
8
|
+
|
|
9
|
+
The tab opens with one plain-English headline ("Watching — N issues recorded"), then surfaced patterns as readable cards (awareness-only — never auto-acted-on), recent captures as plain sentences, and a maturation track with a "← you're here" marker. The dense aggregate counts live in a collapsed "Detail" drawer at the bottom. When the Failure-Learning Loop is off, the tab shows a friendly "not turned on yet" message rather than an error.
|
|
10
|
+
|
|
11
|
+
UX is the centerpiece, not an afterthought: measurable type bars (headline ≥24px, section headers ≥20px, body ≥17px, line-height 1.5–1.6, zero monospace) are enforced by tests, and a glance check asserts the visible text never reads like a debug log. Every dynamic value (commit messages, agent diagnoses, the classifier's output) flows through a single sanitize-and-cap helper before the DOM — textContent-only writes, NFKC fold, control/bidi/chrome-glyph strip, grapheme-safe length caps, and a same-origin `safeUrl` — so untrusted upstream text can't inject markup, fake-authority glyphs, or invisible/right-to-left trickery. The redaction contract (`detail.full` never crosses the wire) is verified end-to-end against a live HTTP server.
|
|
12
|
+
|
|
13
|
+
Server-side this adds ETag/304 diff-aware polling on `/failures*`, a `before=` keyset-pagination parameter (rejects a non-ISO value with 400), and a `rollout` block on `/failures/analysis` (the maturation stage the tab draws). No behavioral change when the loop is disabled — every route still 503s and the tab renders its disabled copy.
|
|
14
|
+
|
|
15
|
+
## What to Tell Your User
|
|
16
|
+
|
|
17
|
+
- There's a new **Process Health** tab in your dashboard. It's a calm, plain-English view of what the failure-watching system is noticing — big readable type, short sentences, no wall of logs.
|
|
18
|
+
- Think of it like a dashboard light in a car, not the mechanic's diagnostic printout: one line tells you whether anything needs a look; the raw numbers are tucked away in a drawer you only open if you want them.
|
|
19
|
+
- If the failure-watching system isn't switched on for an agent, the tab just says so politely instead of looking broken.
|
|
20
|
+
- Nothing to run and nothing to babysit — it refreshes itself quietly while the tab is open and pauses when it isn't.
|
|
21
|
+
|
|
22
|
+
## Summary of New Capabilities
|
|
23
|
+
|
|
24
|
+
| Capability | How to Use |
|
|
25
|
+
|-----------|-----------|
|
|
26
|
+
| Process Health dashboard tab | Open the "Process Health" tab in the dashboard (give the user the dashboard URL + PIN) |
|
|
27
|
+
| Diff-aware polling (ETag/304) | Automatic — `GET /failures*` now return an `ETag`; a matching `If-None-Match` gets a 304 |
|
|
28
|
+
| Keyset pagination | `GET /failures?before=<ISO-ts>&limit=N` and `GET /failures/insights?before=<ISO-ts>&limit=N` |
|
|
29
|
+
| Rollout-stage readout | `GET /failures/analysis` now includes `rollout: { stage, enabled, insightTelegramEscalation }` |
|
|
30
|
+
|
|
31
|
+
## Evidence
|
|
32
|
+
|
|
33
|
+
- **3-tier + jsdom tests, all green:** unit (sanitize/safeUrl/wording + renderers against a real jsdom DOM + ledger pagination), integration (the polling controller — XSS negative, layout-bomb, out-of-order race, visibility-gating, staleness hard-fail AND the 304-pinned-headline case, 304 backoff/recovery, diff-aware zero-mutation; plus the route ETag/304/`before=`/rollout behaviors), and an E2E lifecycle test that boots a real HTTP server and drives the shipped controller through a jsdom-mounted tab with the feature ON and OFF.
|
|
34
|
+
- **Safety verified, not asserted:** the XSS negative fixture feeds `<img onerror>`/`<script>`/`<svg onload>` through the full controller path and asserts zero live elements + no canary fired; the E2E seeds a failure whose `detail.full` is a secret path and confirms it never appears in the rendered DOM.
|
|
35
|
+
- **Migration parity:** the CLAUDE.md "Process Health (Dashboard Tab)" section ships to existing agents via `PostUpdateMigrator.migrateClaudeMd`, and a shadow-capability marker carries it to Codex/Gemini agents — both enforced by the Feature Delivery Completeness test.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
Notify-on-stop, Layer A: when an autonomous run reaches a terminal exit — it completed, ran out of its time budget, or was emergency-stopped — the agent now sends one plain-English Telegram to the run's topic explaining why it stopped. Previously these reasons were only written to stderr (the terminal the user can't see), so an autonomous run could end in silence unless the agent remembered to send a wrap-up. This bakes the "a session either keeps going OR tells you why it stopped" guarantee into the stop hook itself.
|
|
8
|
+
|
|
9
|
+
The notice is best-effort and non-blocking (a delivery hiccup never blocks the exit), fires at most once per run, and reuses the existing Telegram reply transport + the run's existing topic (no new topic is ever created).
|
|
10
|
+
|
|
11
|
+
Existing agents receive the notify-enabled hook automatically: the autonomous-stop-hook capability marker is bumped, so the marker+fingerprint migration re-deploys the updated hook to stock installs on update (customized hooks are left untouched).
|
|
12
|
+
|
|
13
|
+
## What to Tell Your User
|
|
14
|
+
|
|
15
|
+
- When one of my autonomous runs finishes (or times out, or is stopped), you now get a short heads-up explaining why — no more silent endings.
|
|
16
|
+
|
|
17
|
+
## Summary of New Capabilities
|
|
18
|
+
|
|
19
|
+
| Capability | How to Use |
|
|
20
|
+
|-----------|-----------|
|
|
21
|
+
| Autonomous-run terminal exits send a Telegram explaining why | Automatic; one message per run to the run's topic |
|
|
22
|
+
|
|
23
|
+
## Evidence
|
|
24
|
+
|
|
25
|
+
- Hook: `.claude/skills/autonomous/hooks/autonomous-stop-hook.sh` (`notify_terminal_stop` at all 6 terminal exits).
|
|
26
|
+
- Migration: `src/core/PostUpdateMigrator.ts` (capability marker → `notify_terminal_stop`).
|
|
27
|
+
- Tests: `tests/unit/autonomous-stop-hook-notify.test.ts` (9 — static wiring, functional delivery via the real extracted helper, migration).
|
|
28
|
+
- Spec: `docs/specs/NOTIFY-ON-STOP-SPEC.md` (approved) + `.eli16.md`. Side-effects: `upgrades/side-effects/notify-on-stop-layer-a.md`.
|
|
29
|
+
- Layer B (watchdog-caught mid-task stalls) ships separately under the same spec.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Side-Effects Review — Notify-on-Stop, Layer A (autonomous-run-ended notice)
|
|
2
|
+
|
|
3
|
+
**Spec:** docs/specs/NOTIFY-ON-STOP-SPEC.md (approved: true) — Layer A of two. Layer B (gate-fed unjustified-stop notice) ships as a separate PR.
|
|
4
|
+
|
|
5
|
+
Closes the primary silent-stall scenario: an autonomous run reaching a terminal exit (completion / duration-expiry / emergency-stop) previously only echoed the reason to stderr — the terminal the user can't see — so the run could end in silence unless the agent *remembered* to send a final report (willpower, not structure). Justin's standing requirement: a session either keeps going OR tells you why it stopped.
|
|
6
|
+
|
|
7
|
+
## What changed
|
|
8
|
+
- `.claude/skills/autonomous/hooks/autonomous-stop-hook.sh` — added two helpers (`goal_snippet`, `notify_terminal_stop`) and a `notify_terminal_stop "<plain message>"` call before EACH of the six terminal exits (native-mode emergency + duration; legacy duration, emergency, completion-condition, completion-promise). Each sends ONE plain-English Telegram to the run's `report_topic` via `telegram-reply.sh` — the same transport the existing restart-resume recovery note uses.
|
|
9
|
+
- `src/core/PostUpdateMigrator.ts` — bumped the autonomous-stop-hook capability marker from `Native /goal delegation` to `notify_terminal_stop` so existing agents on a prior stock hook receive the notify-enabled version on update (the marker+fingerprint mechanism; customized hooks left untouched).
|
|
10
|
+
- `tests/unit/autonomous-stop-hook-notify.test.ts` — static wiring (helper defined + called at all 6 exits), functional delivery (runs the REAL extracted helper against a stub telegram-reply.sh; both no-op gates), and migration (a stock pre-notify hook gains `notify_terminal_stop`).
|
|
11
|
+
|
|
12
|
+
## Behavior / safety
|
|
13
|
+
- **Best-effort, non-blocking:** `notify_terminal_stop` ends every path with `|| true` and returns early if there's no report topic or the channel isn't telegram. A delivery failure NEVER changes the hook's exit code or blocks the terminal exit.
|
|
14
|
+
- **Fires at most once per run:** every terminal branch removes the state file immediately after, so the hook can't re-enter and re-notify.
|
|
15
|
+
- **No new transport / topic:** reuses `telegram-reply.sh` to the run's existing report topic. No new Telegram topic is ever created.
|
|
16
|
+
|
|
17
|
+
## Over/under-notify
|
|
18
|
+
- OVER: at completion, the agent's own final report (if it sent one) plus Layer A's brief "run finished" backstop = a possible second short message. Accepted — the structural guarantee that *something* is sent outweighs minor redundancy; the backstop is one line.
|
|
19
|
+
- UNDER: a heavily-customized hook (no stock fingerprint) won't receive the migration — same accepted limit as every autonomous-hook migration. Channel != telegram → no send (Layer A is telegram-scoped; other channels await Channel Parity).
|
|
20
|
+
|
|
21
|
+
## Near-silent compliance
|
|
22
|
+
An autonomous run ending is an action-relevant, once-per-run event — exactly the kind the user asked to be told about. Not routine churn. No throttling needed (one message per terminal exit, max once per run).
|
|
23
|
+
|
|
24
|
+
## Migration parity
|
|
25
|
+
`migrateAutonomousStopHookTopicKeyed` (marker bump) ships the updated hook to existing agents; new agents get it via the bundled file at init. Verified by the migration test.
|
|
26
|
+
|
|
27
|
+
## Rollback
|
|
28
|
+
Revert the hook edits + the marker bump (2 files) + the test. Worst case: terminal exits go back to stderr-only.
|
|
29
|
+
|
|
30
|
+
## NOT in this PR (tracked)
|
|
31
|
+
- Layer B: gate-fed unjustified-mid-task-stop notice via the stop-gate evaluate route + a StopNotifier reusing SentinelNotifier's coalescing sink. Separate PR under the same spec.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Side-Effects Review — Process Health dashboard tab
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `process-health-dashboard-tab`
|
|
4
|
+
**Date:** 2026-05-27
|
|
5
|
+
**Author:** echo
|
|
6
|
+
**Spec:** `docs/specs/PROCESS-HEALTH-DASHBOARD-TAB-SPEC.md` (v4, converged, approved: true)
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Adds a read-only dashboard tab that renders the Failure-Learning Loop's data (captures, surfaced patterns, rollout stage) as a calm, plain-English surface. Until now those data were API-only (`/failures*`); this makes them visible in the dashboard. The feature ships with the loop's existing on/off flag — when the loop is disabled every `/failures*` route 503s and the tab renders a friendly disabled state.
|
|
11
|
+
|
|
12
|
+
**Files changed (source):**
|
|
13
|
+
- `dashboard/process-health.js` (NEW, ~504 lines) — browser-native ESM module: pure renderers + the polling controller, both exported for tests. The load-bearing safety contract lives here (`sanitizeForDisplay`, `safeUrl`, `isMixedScript`, friendly-wording maps).
|
|
14
|
+
- `dashboard/index.html` — the tab button, panel markup, calm CSS (measurable type bars, no monospace), tab registration, and the lazy-import lifecycle wiring (start on activate / stop on hide).
|
|
15
|
+
- `src/server/routes.ts` — ETag/304 on `/failures` + `/failures/insights`, `before=` ISO-validation (400 on garbage), and the `rollout` block assembled in `/failures/analysis` (stage derived from the two `failureLearning` flags).
|
|
16
|
+
- `src/monitoring/FailureLedger.ts` — `ListFilter.beforeMs`/`limit`, a new `InsightListFilter` (50-default / 1000-max), a `listInsights` query-builder rewrite (where[]+LIMIT), and a `discovered_at` index for keyset pagination.
|
|
17
|
+
- `src/scaffold/templates.ts` — the "Process Health (Dashboard Tab)" CLAUDE.md section for new agents (Agent Awareness Standard).
|
|
18
|
+
- `src/core/PostUpdateMigrator.ts` — `migrateClaudeMd` backfill of that section for existing agents + a `migrateFrameworkShadowCapabilities` marker so Codex/Gemini agents learn it (Migration Parity Standard).
|
|
19
|
+
|
|
20
|
+
**Files changed (tests):**
|
|
21
|
+
- `tests/unit/process-health-render.test.ts` (NEW) — sanitize/safeUrl/wording + renderers against jsdom + CSS type-bar assertions.
|
|
22
|
+
- `tests/integration/process-health-tab.test.ts` (NEW) — the polling controller's full §4.3 surface (XSS, layout-bomb, race, visibility-gating, staleness hard-fail + 304-pinned, backoff, diff-aware) + the glance no-debug-vocabulary check.
|
|
23
|
+
- `tests/e2e/process-health-tab-lifecycle.test.ts` (NEW) — real HTTP server + jsdom-mounted tab, feature ON and OFF; detail.full-never-leaks verified end-to-end.
|
|
24
|
+
- `tests/integration/failure-routes.test.ts` — ETag/304, `before=` 400, rollout-stage derivation.
|
|
25
|
+
- `tests/unit/FailureLedger.test.ts` — before=/limit pagination + insight clamp.
|
|
26
|
+
- `tests/unit/PostUpdateMigrator-processHealth.test.ts` (NEW) — runtime migration + idempotency.
|
|
27
|
+
- `tests/unit/feature-delivery-completeness.test.ts` — registered Process Health in `featureSections`; incidentally fixed a pre-existing untracked section (`Cross-Machine Seamlessness`).
|
|
28
|
+
|
|
29
|
+
**Files changed (dependency):** `package.json` / `package-lock.json` — added `jsdom` as a devDependency (the spec's chosen DOM test harness; no runtime dependency added).
|
|
30
|
+
|
|
31
|
+
**Files changed (release):** `upgrades/NEXT.md` (NEW) — the upgrade-guide leg, `bump: minor`.
|
|
32
|
+
|
|
33
|
+
## Decision-point inventory
|
|
34
|
+
|
|
35
|
+
- **New runtime dependency?** No. `jsdom` is devDependencies only; the tab ships as browser-native ESM with no build step and no new server-side package.
|
|
36
|
+
- **New auth surface?** No. The tab is served behind the existing dashboard PIN; it adds no endpoint that wasn't already auth-gated.
|
|
37
|
+
- **Untrusted-data rendering** (commit messages, agent diagnoses, classifier output): **render, but only via the sanitize-and-cap helper** — textContent-only DOM writes, NFKC fold, control/bidi/chrome-glyph strip, grapheme-safe caps, same-origin `safeUrl`. Patterns cards are awareness-only with renderer-owned framing and NO action authority.
|
|
38
|
+
- **Behavior when loop disabled:** unchanged — routes still 503, tab renders pinned disabled copy (no config-key string, no monospace), never an error.
|
|
39
|
+
|
|
40
|
+
## Side-effects analysis
|
|
41
|
+
|
|
42
|
+
- **Behavioral:** Additive. The new route behaviors (ETag/304, `before=`) are backward-compatible — a client that ignores ETags gets a normal 200; omitting `before=` is unchanged. The `rollout` block is a new field on an existing response, not a breaking change.
|
|
43
|
+
- **Architectural:** The tab is one self-contained module, not a refactor of the dashboard SPA (explicitly out of scope per spec §10). `listInsights` moved from a ternary to the same where[]+LIMIT builder `list()` already uses — same shape, lower divergence.
|
|
44
|
+
- **Security:** The primary surface is rendering untrusted upstream prose on a read tab. Mitigated by the single sanitize path + textContent-only + element-ban (verified by the XSS negative fixture firing no canary and producing zero live elements). The `detail.full` redaction contract is depended upon and verified end-to-end (a seeded secret path never appears in the DOM). Accepted residual: a low-severity social-engineering surface inherent to showing untrusted text — any future *actionable* treatment is gated behind the deferred `InsightRecord.provenance` contract (spec §10).
|
|
45
|
+
- **Migration parity:** Covered all three legs — templates.ts (new agents), PostUpdateMigrator (existing agents + shadow markers for Codex/Gemini), and NEXT.md (upgrade guide). Enforced by the Feature Delivery Completeness test.
|
|
46
|
+
- **Performance:** ETag/304 + diff-aware rendering means identical ticks do zero DOM mutations and 304s skip body transfer; 304-backoff widens the poll to 5 min after 5 idle ticks. The new `discovered_at` index supports keyset pagination without a full scan.
|
|
47
|
+
- **Reversibility:** Fully reversible by reverting the commit. The feature is gated OFF by default via the existing `failureLearning.enabled` flag — no agent sees the tab change behavior until the loop is enabled, and even then it is a read surface.
|
|
48
|
+
|
|
49
|
+
## Deferred (tracked, not silently dropped)
|
|
50
|
+
|
|
51
|
+
- **LLM text-smoke (spec §6.4b):** implemented the *deterministic* glance gate (no-debug-vocabulary + plain-state assertion across all 3 fixtures), which is structurally stronger than an LLM judgment for this property and runs in CI. The soft LLM-judgment variant (a Haiku call over the text projection) is deferred as a manual reviewer-attested pre-flight rather than a forever-skipped CI harness — consistent with §6.4a's "honestly labeled soft" framing.
|
|
52
|
+
- **Visual dashboard polish / extra ingestion sources:** already tracked as fast-follows on the initiative board (spec §10).
|
|
53
|
+
|
|
54
|
+
## Evidence pointers
|
|
55
|
+
|
|
56
|
+
- Typecheck: `tsc --noEmit` — 0 errors.
|
|
57
|
+
- CI-mirror smoke suite (`npm run test:smoke`): **149 files, 3553 tests passed, 0 failed**.
|
|
58
|
+
- New tests run green in isolation: unit render (27), controller integration (12), failure-routes (11), FailureLedger (11), migrator (2), E2E lifecycle (4).
|
|
59
|
+
- Safety verified not asserted: XSS fixture fires no canary + 0 live elements; E2E confirms a seeded `detail.full` secret path is absent from the rendered DOM.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Side-Effects Review — Release-Readiness Visibility, PR-1 (`analyze-release.js --ref`)
|
|
2
|
+
|
|
3
|
+
**Spec:** docs/specs/RELEASE-READINESS-VISIBILITY-SPEC.md (converged + approved 2026-05-27)
|
|
4
|
+
**Scope of this PR:** the prerequisite-only change — add a `--ref=<rev>` flag to `scripts/analyze-release.js` and thread it through the git-range queries. No sentinel, no job, no routes (those are PR-2/PR-3). Plus the approved spec docs + convergence report + a unit test.
|
|
5
|
+
|
|
6
|
+
## What changed
|
|
7
|
+
|
|
8
|
+
- `scripts/analyze-release.js`: new `REF` constant parsed from `--ref=<rev>` / `--ref <rev>` (default `HEAD`). Threaded into `getLastReleaseTag`, `getCommitsSinceTag`, `getDiffStat`, `getChangedFiles`, `getFileDiff` — every place that previously hardcoded `HEAD`.
|
|
9
|
+
- `tests/unit/analyze-release-ref-flag.test.ts`: 4 subprocess tests against a deterministic git fixture (default ≡ HEAD; `--ref=HEAD` ≡ default; `--ref=<earlier commit>` changes the analyzed tip — different nearest tag + commit count; space form ≡ equals form).
|
|
10
|
+
- Spec + ELI16 + convergence report (docs only).
|
|
11
|
+
|
|
12
|
+
## Side-effects analysis
|
|
13
|
+
|
|
14
|
+
**Over/under-reach.** The change is additive and opt-in. `REF` defaults to `'HEAD'`, so every existing caller — most importantly the `prepublishOnly` chain (`npm run check:release` → `analyze-release.js`, which never passes `--ref`) — produces byte-identical behavior to before. There is no path where omitting the flag changes output. Verified by the `--ref=HEAD ≡ default` test.
|
|
15
|
+
|
|
16
|
+
**Level-of-abstraction fit.** The ref is a module-level constant rather than a threaded parameter, matching the script's existing shape (`getLastReleaseTag()` already took no args and implicitly used HEAD). This keeps the diff minimal and the helpers' signatures unchanged for the four that already took `tag`.
|
|
17
|
+
|
|
18
|
+
**Signal vs authority.** N/A for this PR — `analyze-release.js` is a read-only analyzer (it only reads git and prints a report). It gates nothing. The flag does not grant or remove any blocking authority. The downstream consumer that WILL use `--ref=FETCH_HEAD` (Layer B's readiness sentinel) is signal-only and ships in PR-2.
|
|
19
|
+
|
|
20
|
+
**Interactions.** The only consumer touched today is the prepublish chain, which is unaffected (default HEAD). `check-upgrade-guide.js` is not touched. The `--ref` flag is forward-looking: PR-2's readiness job will invoke `--ref=FETCH_HEAD` after a bounded fetch of canonical main. Per §10 sequencing, PR-2 does not merge until this PR is on main, enforced by a required-status CI check that asserts `--ref=<known SHA>` differs from `--ref=HEAD` (i.e. the flag is genuinely threaded, not silently ignored).
|
|
21
|
+
|
|
22
|
+
**Input safety.** `REF` flows only into `git` argv arrays via the existing `gitRead` helper (`execFileSync`, no shell) — no shell-injection surface. A bogus ref makes the underlying `git` call fail and the existing `try/catch` returns the empty/initial-commit fallback, exactly as today for a tagless repo.
|
|
23
|
+
|
|
24
|
+
**Rollback cost.** Trivial — reverting this commit restores the hardcoded `HEAD`. No state, no config, no migration introduced by PR-1. The unit test reverts with it.
|
|
25
|
+
|
|
26
|
+
**Testing.** Unit tier covered (4 tests, green). Integration/E2E tiers are not applicable to PR-1 in isolation (no routes/jobs yet) and land with PR-2, which adds the `GET /release-readiness` route + the "feature is alive" E2E that exercises the full readiness pipeline including the `--ref` path.
|