@lannguyensi/harness 0.44.0 → 0.45.0
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/CHANGELOG.md +1213 -0
- package/dist/cli/adopt/derive.d.ts +16 -7
- package/dist/cli/adopt/derive.js +19 -2
- package/dist/cli/adopt/derive.js.map +1 -1
- package/dist/cli/adopt/index.js +18 -3
- package/dist/cli/adopt/index.js.map +1 -1
- package/dist/cli/apply/apply.d.ts +1 -0
- package/dist/cli/apply/apply.js +62 -6
- package/dist/cli/apply/apply.js.map +1 -1
- package/dist/cli/apply/generate-codex-config.js +5 -6
- package/dist/cli/apply/generate-codex-config.js.map +1 -1
- package/dist/cli/apply/generate-opencode-config.d.ts +58 -0
- package/dist/cli/apply/generate-opencode-config.js +210 -0
- package/dist/cli/apply/generate-opencode-config.js.map +1 -0
- package/dist/cli/apply/generate-settings.d.ts +90 -32
- package/dist/cli/apply/generate-settings.js +133 -10
- package/dist/cli/apply/generate-settings.js.map +1 -1
- package/dist/cli/apply/index.d.ts +2 -1
- package/dist/cli/apply/index.js +2 -1
- package/dist/cli/apply/index.js.map +1 -1
- package/dist/cli/approve/stdin-report.d.ts +15 -0
- package/dist/cli/approve/stdin-report.js +1 -1
- package/dist/cli/approve/stdin-report.js.map +1 -1
- package/dist/cli/approve/understanding.d.ts +75 -0
- package/dist/cli/approve/understanding.js +150 -2
- package/dist/cli/approve/understanding.js.map +1 -1
- package/dist/cli/audit.d.ts +1 -1
- package/dist/cli/audit.js +3 -2
- package/dist/cli/audit.js.map +1 -1
- package/dist/cli/doctor/claude-mcp.d.ts +19 -1
- package/dist/cli/doctor/claude-mcp.js +36 -10
- package/dist/cli/doctor/claude-mcp.js.map +1 -1
- package/dist/cli/doctor/codex.d.ts +3 -1
- package/dist/cli/doctor/codex.js +6 -9
- package/dist/cli/doctor/codex.js.map +1 -1
- package/dist/cli/doctor/format.js +94 -8
- package/dist/cli/doctor/format.js.map +1 -1
- package/dist/cli/doctor/index.d.ts +23 -0
- package/dist/cli/doctor/index.js +106 -2
- package/dist/cli/doctor/index.js.map +1 -1
- package/dist/cli/doctor/opencode.d.ts +24 -0
- package/dist/cli/doctor/opencode.js +107 -0
- package/dist/cli/doctor/opencode.js.map +1 -0
- package/dist/cli/doctor/target-checks.d.ts +11 -0
- package/dist/cli/doctor/target-checks.js +25 -0
- package/dist/cli/doctor/target-checks.js.map +1 -0
- package/dist/cli/doctor/toolchain-parity.d.ts +63 -0
- package/dist/cli/doctor/toolchain-parity.js +154 -0
- package/dist/cli/doctor/toolchain-parity.js.map +1 -0
- package/dist/cli/doctor/types.d.ts +85 -1
- package/dist/cli/doctor/types.js +1 -1
- package/dist/cli/doctor/types.js.map +1 -1
- package/dist/cli/doctor/understanding-mode-env.d.ts +39 -0
- package/dist/cli/doctor/understanding-mode-env.js +83 -0
- package/dist/cli/doctor/understanding-mode-env.js.map +1 -0
- package/dist/cli/dry-run.js +32 -17
- package/dist/cli/dry-run.js.map +1 -1
- package/dist/cli/explain.d.ts +1 -1
- package/dist/cli/explain.js +1 -1
- package/dist/cli/explain.js.map +1 -1
- package/dist/cli/index.js +58 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/composer.js +36 -13
- package/dist/cli/init/composer.js.map +1 -1
- package/dist/cli/init/interactive.d.ts +7 -4
- package/dist/cli/init/interactive.js +79 -27
- package/dist/cli/init/interactive.js.map +1 -1
- package/dist/cli/init/profiles.d.ts +2 -2
- package/dist/cli/init/profiles.js +32 -1
- package/dist/cli/init/profiles.js.map +1 -1
- package/dist/cli/init/templates.d.ts +2 -1
- package/dist/cli/init/templates.js +130 -20
- package/dist/cli/init/templates.js.map +1 -1
- package/dist/cli/pack/approve-escape.js +69 -8
- package/dist/cli/pack/approve-escape.js.map +1 -1
- package/dist/cli/pack/hook-branch-protection.js +1 -1
- package/dist/cli/pack/hook-branch-protection.js.map +1 -1
- package/dist/cli/pack/hook-codex-pre-tool-use.js +27 -1
- package/dist/cli/pack/hook-codex-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-post-merge-gate.js +1 -1
- package/dist/cli/pack/hook-post-merge-gate.js.map +1 -1
- package/dist/cli/pack/hook-pre-tool-use.js +91 -4
- package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-solution-acceptance.js +19 -2
- package/dist/cli/pack/hook-solution-acceptance.js.map +1 -1
- package/dist/cli/pack/understanding-report-schema-hint.d.ts +13 -0
- package/dist/cli/pack/understanding-report-schema-hint.js +52 -3
- package/dist/cli/pack/understanding-report-schema-hint.js.map +1 -1
- package/dist/cli/pause/index.js +1 -1
- package/dist/cli/pause/index.js.map +1 -1
- package/dist/cli/policy/intercept.d.ts +148 -0
- package/dist/cli/policy/intercept.js +379 -48
- package/dist/cli/policy/intercept.js.map +1 -1
- package/dist/cli/session-start/index.d.ts +12 -3
- package/dist/cli/session-start/index.js +67 -11
- package/dist/cli/session-start/index.js.map +1 -1
- package/dist/cli/session-start/stale-base-check.d.ts +71 -0
- package/dist/cli/session-start/stale-base-check.js +379 -0
- package/dist/cli/session-start/stale-base-check.js.map +1 -0
- package/dist/cli/session-start/toolchain-parity.d.ts +142 -8
- package/dist/cli/session-start/toolchain-parity.js +244 -36
- package/dist/cli/session-start/toolchain-parity.js.map +1 -1
- package/dist/cli/smoke/assertions.d.ts +9 -2
- package/dist/cli/smoke/assertions.js +9 -2
- package/dist/cli/smoke/assertions.js.map +1 -1
- package/dist/cli/uninstall/index.js +18 -2
- package/dist/cli/uninstall/index.js.map +1 -1
- package/dist/cli/validate/checks.d.ts +3 -1
- package/dist/cli/validate/checks.js +298 -3
- package/dist/cli/validate/checks.js.map +1 -1
- package/dist/io/claude-mcp.d.ts +122 -1
- package/dist/io/claude-mcp.js +214 -12
- package/dist/io/claude-mcp.js.map +1 -1
- package/dist/io/duration.js.map +1 -0
- package/dist/io/expand-home.js.map +1 -0
- package/dist/io/extract.js.map +1 -0
- package/dist/io/harness-lock.js +1 -1
- package/dist/io/harness-lock.js.map +1 -1
- package/dist/{runtime → io}/ledger-record.d.ts +1 -1
- package/dist/io/ledger-record.js.map +1 -0
- package/dist/policies/index.d.ts +2 -2
- package/dist/policies/index.js +2 -2
- package/dist/policies/index.js.map +1 -1
- package/dist/policies/ledger-client.js +10 -6
- package/dist/policies/ledger-client.js.map +1 -1
- package/dist/policies/producers.js +1 -1
- package/dist/policies/producers.js.map +1 -1
- package/dist/policies/requires.js +2 -2
- package/dist/policies/requires.js.map +1 -1
- package/dist/policy-packs/builtin/branch-protection.js +28 -3
- package/dist/policy-packs/builtin/branch-protection.js.map +1 -1
- package/dist/policy-packs/builtin/post-merge-gate.js +45 -9
- package/dist/policy-packs/builtin/post-merge-gate.js.map +1 -1
- package/dist/policy-packs/builtin/solution-acceptance-runtime.d.ts +139 -8
- package/dist/policy-packs/builtin/solution-acceptance-runtime.js +246 -12
- package/dist/policy-packs/builtin/solution-acceptance-runtime.js.map +1 -1
- package/dist/policy-packs/builtin/solution-acceptance.js +17 -3
- package/dist/policy-packs/builtin/solution-acceptance.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/active-claim.d.ts +25 -0
- package/dist/policy-packs/builtin/understanding-before-execution/active-claim.js +89 -0
- package/dist/policy-packs/builtin/understanding-before-execution/active-claim.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/index.d.ts +13 -0
- package/dist/policy-packs/builtin/understanding-before-execution/index.js +39 -0
- package/dist/policy-packs/builtin/understanding-before-execution/index.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/ledger.d.ts +25 -0
- package/dist/policy-packs/builtin/understanding-before-execution/ledger.js +56 -0
- package/dist/policy-packs/builtin/understanding-before-execution/ledger.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.d.ts +20 -0
- package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.js +79 -0
- package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/markers.d.ts +103 -0
- package/dist/policy-packs/builtin/understanding-before-execution/markers.js +203 -0
- package/dist/policy-packs/builtin/understanding-before-execution/markers.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.d.ts +177 -0
- package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js +309 -0
- package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.d.ts +106 -0
- package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.js +209 -0
- package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/task-markers.d.ts +78 -0
- package/dist/policy-packs/builtin/understanding-before-execution/task-markers.js +185 -0
- package/dist/policy-packs/builtin/understanding-before-execution/task-markers.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution-runtime.d.ts +1 -536
- package/dist/policy-packs/builtin/understanding-before-execution-runtime.js +9 -1095
- package/dist/policy-packs/builtin/understanding-before-execution-runtime.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution.d.ts +109 -1
- package/dist/policy-packs/builtin/understanding-before-execution.js +183 -15
- package/dist/policy-packs/builtin/understanding-before-execution.js.map +1 -1
- package/dist/policy-packs/registry.d.ts +11 -0
- package/dist/policy-packs/registry.js +1 -1
- package/dist/policy-packs/registry.js.map +1 -1
- package/dist/policy-packs/runtime.d.ts +1 -1
- package/dist/policy-packs/runtime.js +10 -1
- package/dist/policy-packs/runtime.js.map +1 -1
- package/dist/runtime/agent-facing.js +1 -1
- package/dist/runtime/agent-facing.js.map +1 -1
- package/dist/runtime/bash-match-facts.d.ts +9 -7
- package/dist/runtime/bash-match-facts.js +9 -7
- package/dist/runtime/bash-match-facts.js.map +1 -1
- package/dist/runtime/bash-match-registry.d.ts +75 -38
- package/dist/runtime/bash-match-registry.js +279 -232
- package/dist/runtime/bash-match-registry.js.map +1 -1
- package/dist/runtime/bash-prefix-parse.d.ts +7 -0
- package/dist/runtime/bash-prefix-parse.js +205 -17
- package/dist/runtime/bash-prefix-parse.js.map +1 -1
- package/dist/runtime/command-normalize.d.ts +112 -0
- package/dist/runtime/command-normalize.js +411 -38
- package/dist/runtime/command-normalize.js.map +1 -1
- package/dist/runtime/hermetic-spawn-guard.js +6 -0
- package/dist/runtime/hermetic-spawn-guard.js.map +1 -1
- package/dist/runtime/index.d.ts +2 -2
- package/dist/runtime/index.js +2 -2
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/intercept.d.ts +78 -3
- package/dist/runtime/intercept.js +151 -39
- package/dist/runtime/intercept.js.map +1 -1
- package/dist/runtime/ledger-add.js +1 -1
- package/dist/runtime/ledger-add.js.map +1 -1
- package/dist/runtime/read-only-bash.js +303 -24
- package/dist/runtime/read-only-bash.js.map +1 -1
- package/dist/schema/doctor.d.ts +25 -0
- package/dist/schema/doctor.js +23 -0
- package/dist/schema/doctor.js.map +1 -0
- package/dist/schema/extract.js +1 -1
- package/dist/schema/extract.js.map +1 -1
- package/dist/schema/index.d.ts +76 -0
- package/dist/schema/index.js +9 -0
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/requires.js +2 -2
- package/dist/schema/requires.js.map +1 -1
- package/dist/schema/risk.d.ts +7 -0
- package/dist/schema/risk.js +23 -0
- package/dist/schema/risk.js.map +1 -1
- package/dist/schema/stale-base-check.d.ts +18 -0
- package/dist/schema/stale-base-check.js +37 -0
- package/dist/schema/stale-base-check.js.map +1 -0
- package/dist/schema/toolchain-parity.d.ts +3 -0
- package/dist/schema/toolchain-parity.js +13 -0
- package/dist/schema/toolchain-parity.js.map +1 -1
- package/package.json +3 -2
- package/dist/policies/duration.js.map +0 -1
- package/dist/policies/extract.js.map +0 -1
- package/dist/runtime/expand-home.js.map +0 -1
- package/dist/runtime/ledger-record.js.map +0 -1
- /package/dist/{policies → io}/duration.d.ts +0 -0
- /package/dist/{policies → io}/duration.js +0 -0
- /package/dist/{runtime → io}/expand-home.d.ts +0 -0
- /package/dist/{runtime → io}/expand-home.js +0 -0
- /package/dist/{policies → io}/extract.d.ts +0 -0
- /package/dist/{policies → io}/extract.js +0 -0
- /package/dist/{runtime → io}/ledger-record.js +0 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
// `harness session-start stale-base-check` — SessionStart hook
|
|
2
|
+
// entrypoint, fourth sibling of `harness session-start preflight` /
|
|
3
|
+
// `branch-check` / `toolchain-parity`.
|
|
4
|
+
//
|
|
5
|
+
// Incident (task ea8becf5): a task branch was cut from a local `master`
|
|
6
|
+
// that had not been fetched in 4 days. The v0.42.0 release had already
|
|
7
|
+
// landed on `origin/master` 6h before the branch point. The resulting PR
|
|
8
|
+
// went straight to CONFLICTING and its CI never ran. Nothing surfaced the
|
|
9
|
+
// gap at the time it was still cheap to fix (before work landed on the
|
|
10
|
+
// branch), because:
|
|
11
|
+
// - `harness preflight` checks the working TREE, never the remote.
|
|
12
|
+
// - `harness session-start branch-check` reads `.git/HEAD` and a
|
|
13
|
+
// protected-branch list; it has no notion of "is this branch's base
|
|
14
|
+
// current".
|
|
15
|
+
// - the post-merge-gate convention only ever fires AFTER a merge.
|
|
16
|
+
// And the obvious-looking fix is itself the trap this module exists to
|
|
17
|
+
// avoid: `git merge-base HEAD origin/master` LOOKS authoritative, but
|
|
18
|
+
// `origin/master` is a LOCAL, cached ref — exactly the ref that was 4
|
|
19
|
+
// days stale in the incident. Computing "how stale" from a stale ref
|
|
20
|
+
// answers a different question than the one that matters.
|
|
21
|
+
//
|
|
22
|
+
// --- WHERE this sits (task brief step 3) ---
|
|
23
|
+
// SessionStart companion, not a `preflight`-check addition and not a
|
|
24
|
+
// PreToolUse gate:
|
|
25
|
+
// - `harness policy intercept` (the PreToolUse hot path, evaluated on
|
|
26
|
+
// EVERY Bash/Edit/Write tool call — see runtime/intercept.ts) must
|
|
27
|
+
// never do network I/O; a `git fetch` there would add real latency to
|
|
28
|
+
// every single tool call for the life of the session. This module is
|
|
29
|
+
// wired ONLY under `harness session-start ...` (this file lives in
|
|
30
|
+
// src/cli/, which src/runtime/intercept.ts is structurally forbidden
|
|
31
|
+
// from importing — enforced by .dependency-cruiser.cjs's
|
|
32
|
+
// "runtime-no-upward-imports" rule, i.e. `npm run check:boundaries`
|
|
33
|
+
// fails the build if that ever changes), so the one network call this
|
|
34
|
+
// module makes can only ever happen once per SessionStart, never on
|
|
35
|
+
// the hot path.
|
|
36
|
+
// - `agent-preflight` (the external `preflight` binary
|
|
37
|
+
// `session-start/index.ts` wraps) is a SEPARATE npm package; teaching
|
|
38
|
+
// it to do remote checks is out of this task's scope (task brief
|
|
39
|
+
// "Out of scope").
|
|
40
|
+
// - A PreToolUse gate (`requires.ledger_tag`-style, like
|
|
41
|
+
// `preflight-before-push`) was considered and REJECTED: every such
|
|
42
|
+
// gate in this codebase fails CLOSED for a SECURITY invariant (an
|
|
43
|
+
// absent/expired tag blocks the action). A stale base is a
|
|
44
|
+
// time-loss problem, not a security problem — blocking on it would
|
|
45
|
+
// violate the task's hard constraint ("Keine Aussperrung: ein
|
|
46
|
+
// veralteter Base darf die Arbeit nicht verhindern") for zero
|
|
47
|
+
// security benefit. A SessionStart producer's own contract
|
|
48
|
+
// (`blocking:false`, always exit 0) makes the non-blocking posture
|
|
49
|
+
// structural rather than a policy authors could get wrong later.
|
|
50
|
+
//
|
|
51
|
+
// --- SCHÄRFE (severity) chosen: WARNUNG, not Hinweis, not Block ---
|
|
52
|
+
// - Block is impossible by construction here (see above) and would
|
|
53
|
+
// directly violate the hard constraint anyway.
|
|
54
|
+
// - A quiet Hinweis (a single low-key note easy to scroll past) risks
|
|
55
|
+
// reproducing the founding incident's own failure mode almost
|
|
56
|
+
// exactly: "nichts fiel auf" is the whole reason this task exists.
|
|
57
|
+
// - So: a loud stderr WARNING line — same visibility tier as the two
|
|
58
|
+
// existing loud-but-non-blocking SessionStart producers
|
|
59
|
+
// (`toolchain-parity`'s drift lines, `preflight`'s not-ready
|
|
60
|
+
// diagnostic) — naming the concrete commit count, how old the missing
|
|
61
|
+
// work is, and the exact recovery command. Purely advisory: no
|
|
62
|
+
// PreToolUse gate consumes the `stale-base:` ledger fact this writes
|
|
63
|
+
// (same posture as `toolchain-parity:`, audit-trail only for
|
|
64
|
+
// `harness audit`/forensics).
|
|
65
|
+
//
|
|
66
|
+
// --- Fetch cost / why opt-in (task brief: "inkl. Fetch-Kosten") ---
|
|
67
|
+
// `stale_base_check.enabled` defaults to `false` (schema:
|
|
68
|
+
// src/schema/stale-base-check.ts). An operator who has not asked for
|
|
69
|
+
// this pays ZERO extra session-start latency or network egress — the
|
|
70
|
+
// producer returns before touching `child_process` at all when
|
|
71
|
+
// unconfigured. Once enabled, the cost is bounded by
|
|
72
|
+
// `fetch_timeout_ms` (default 8000ms) per `git` subprocess call (at most
|
|
73
|
+
// three: fetch, rev-list, log — see `realCheckStaleBase` below), and the
|
|
74
|
+
// fetch itself is normally near-instant: `git fetch <remote>
|
|
75
|
+
// <defaultBranch>` only ever needs to transfer the commits this checkout
|
|
76
|
+
// doesn't already have. It always writes `FETCH_HEAD`; on a normal clone
|
|
77
|
+
// (whose configured `remote.<remote>.fetch` refspec matches the named
|
|
78
|
+
// branch) `git` ALSO refreshes the local `refs/remotes/<remote>/
|
|
79
|
+
// <defaultBranch>` tracking ref as a side effect — a welcome bonus (it
|
|
80
|
+
// self-heals the exact staleness the incident hit) but never something
|
|
81
|
+
// this module relies on or reads back: see below.
|
|
82
|
+
//
|
|
83
|
+
// --- The ref-staleness trap, closed ---
|
|
84
|
+
// The remote's default-branch NAME (e.g. "master") is resolved from the
|
|
85
|
+
// local `refs/remotes/origin/HEAD` file via `resolveOriginHeadBase` — that
|
|
86
|
+
// is safe to trust: a repo's default branch NAME essentially never
|
|
87
|
+
// changes after initial setup. What must NEVER be trusted is that same
|
|
88
|
+
// ref's SHA as a staleness signal — that is precisely what went stale in
|
|
89
|
+
// the incident. This module never READS a local `refs/remotes/<remote>/
|
|
90
|
+
// <defaultBranch>` sha for comparison (regardless of whether the fetch
|
|
91
|
+
// above happened to refresh it); the only sha it ever compares `branch`
|
|
92
|
+
// against is `FETCH_HEAD`, populated by a live `git fetch` performed
|
|
93
|
+
// THIS run.
|
|
94
|
+
//
|
|
95
|
+
// SessionStart contract, same as the three siblings: `blocking:false`.
|
|
96
|
+
// Every failure path (not configured, not a git repo, detached HEAD,
|
|
97
|
+
// default branch unresolved, `git` not on PATH, no remote configured, no
|
|
98
|
+
// credentials, offline/timeout, unparseable output, a ledger-write
|
|
99
|
+
// failure) logs one line to stderr and exits 0 — never blocking, never
|
|
100
|
+
// throwing (except the hermetic-spawn-guard violation under vitest
|
|
101
|
+
// without an injected `runCheck`, which — like the sibling producers —
|
|
102
|
+
// must propagate rather than be swallowed; see hermetic-spawn-guard.ts).
|
|
103
|
+
import { execFile } from "node:child_process";
|
|
104
|
+
import { findGitEntry, resolveCommonDir, resolveGitContext, resolveOriginHeadBase, } from "../../runtime/git-context.js";
|
|
105
|
+
import { assertNoRealSpawnInTests } from "../../runtime/hermetic-spawn-guard.js";
|
|
106
|
+
import { resolveManifestLedgerWriter } from "../../runtime/ledger-writer.js";
|
|
107
|
+
import { resolveReadSessionId, } from "../../runtime/session-id.js";
|
|
108
|
+
import { loadManifest } from "../loader.js";
|
|
109
|
+
import { formatSnapshotAge } from "./toolchain-parity.js";
|
|
110
|
+
const FALLBACK_SESSION = "default";
|
|
111
|
+
const LEDGER_SOURCE = "harness-session-start-stale-base-check";
|
|
112
|
+
const DEFAULT_REMOTE = "origin";
|
|
113
|
+
const DEFAULT_FETCH_TIMEOUT_MS = 8_000;
|
|
114
|
+
async function readStdin(stream) {
|
|
115
|
+
return new Promise((resolve, reject) => {
|
|
116
|
+
let data = "";
|
|
117
|
+
stream.setEncoding("utf8");
|
|
118
|
+
stream.on("data", (chunk) => {
|
|
119
|
+
data += chunk;
|
|
120
|
+
});
|
|
121
|
+
stream.on("end", () => resolve(data));
|
|
122
|
+
stream.on("error", reject);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Run one `git <args>` in `cwd`, classifying the common failure shapes
|
|
127
|
+
* (missing binary / timeout / non-zero exit) the same way the sibling
|
|
128
|
+
* producers' subprocess wrappers do (`spawnPreflight` in
|
|
129
|
+
* session-start/index.ts, `realNpmGlobalsSpawn` in toolchain-parity.ts).
|
|
130
|
+
* Never rejects.
|
|
131
|
+
*/
|
|
132
|
+
function execGit(args, cwd, timeoutMs) {
|
|
133
|
+
return new Promise((resolve) => {
|
|
134
|
+
execFile("git", args, { cwd, timeout: timeoutMs, maxBuffer: 4 * 1024 * 1024, encoding: "utf8" }, (err, stdout, stderr) => {
|
|
135
|
+
const text = (stdout ?? "").toString().trim();
|
|
136
|
+
if (!err) {
|
|
137
|
+
resolve({ ok: true, stdout: text, reason: "" });
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const e = err;
|
|
141
|
+
if (e.code === "ENOENT") {
|
|
142
|
+
resolve({ ok: false, stdout: "", reason: "`git` not on PATH" });
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (e.killed) {
|
|
146
|
+
resolve({
|
|
147
|
+
ok: false,
|
|
148
|
+
stdout: "",
|
|
149
|
+
reason: `\`git ${args.join(" ")}\` timed out after ${timeoutMs}ms (remote unreachable or very slow)`,
|
|
150
|
+
});
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const stderrText = (stderr ?? "").toString().trim().split("\n")[0] ?? "";
|
|
154
|
+
const capped = stderrText.length > 200 ? `${stderrText.slice(0, 199)}…` : stderrText;
|
|
155
|
+
resolve({
|
|
156
|
+
ok: false,
|
|
157
|
+
stdout: "",
|
|
158
|
+
reason: `\`git ${args.join(" ")}\` failed${capped ? `: ${capped}` : ` (exit ${e.code ?? "?"})`}`,
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The real, network-touching implementation. Guarded by
|
|
165
|
+
* `assertNoRealSpawnInTests` (see that module's doc) so an un-injected
|
|
166
|
+
* test can never accidentally exercise a real `git fetch` — every test in
|
|
167
|
+
* this module's suite either injects `runCheck` or opts into the real
|
|
168
|
+
* path via the documented `HARNESS_ALLOW_REAL_SPAWN=1` escape hatch
|
|
169
|
+
* against a local fixture repo (never a real network host).
|
|
170
|
+
*
|
|
171
|
+
* Sequence: `git fetch --no-tags <remote> <defaultBranch>` (always writes
|
|
172
|
+
* FETCH_HEAD; may also refresh the local remote-tracking ref as a normal
|
|
173
|
+
* git side effect — see the module header), then `git rev-list
|
|
174
|
+
* --left-right --count <branch>...FETCH_HEAD` for the ahead/behind counts,
|
|
175
|
+
* then — only when behind > 0, since that is the only case the caller's
|
|
176
|
+
* message needs it — `git log -1 --format=%H%x09%cI FETCH_HEAD` for the
|
|
177
|
+
* live remote tip's sha + committer date. Comparison is always against
|
|
178
|
+
* `FETCH_HEAD` (this run's live result), never a cached tracking ref.
|
|
179
|
+
*/
|
|
180
|
+
function realCheckStaleBase(args) {
|
|
181
|
+
assertNoRealSpawnInTests("git fetch (stale-base-check)", "Inject a fake `runCheck` (SessionStartStaleBaseCheckOptions.runCheck), or opt into the " +
|
|
182
|
+
"real path against a LOCAL fixture repo via HARNESS_ALLOW_REAL_SPAWN=1 for a dedicated " +
|
|
183
|
+
"real-git plumbing test — never exercise this against a real network host in a test.");
|
|
184
|
+
const { cwd, remote, defaultBranch, branch, timeoutMs } = args;
|
|
185
|
+
return (async () => {
|
|
186
|
+
const fetch = await execGit(["fetch", "--no-tags", "--", remote, defaultBranch], cwd, timeoutMs);
|
|
187
|
+
if (!fetch.ok)
|
|
188
|
+
return { ok: false, reason: fetch.reason };
|
|
189
|
+
const counts = await execGit(["rev-list", "--left-right", "--count", `${branch}...FETCH_HEAD`], cwd, timeoutMs);
|
|
190
|
+
if (!counts.ok)
|
|
191
|
+
return { ok: false, reason: counts.reason };
|
|
192
|
+
const parts = counts.stdout.split(/\s+/).filter((p) => p.length > 0);
|
|
193
|
+
const aheadCount = Number.parseInt(parts[0] ?? "", 10);
|
|
194
|
+
const behindCount = Number.parseInt(parts[1] ?? "", 10);
|
|
195
|
+
if (!Number.isFinite(aheadCount) || !Number.isFinite(behindCount)) {
|
|
196
|
+
return { ok: false, reason: `\`git rev-list --left-right --count\` produced unparseable output: "${counts.stdout}"` };
|
|
197
|
+
}
|
|
198
|
+
if (behindCount === 0)
|
|
199
|
+
return { ok: true, aheadCount, behindCount };
|
|
200
|
+
const log = await execGit(["log", "-1", "--format=%H%x09%cI", "FETCH_HEAD"], cwd, timeoutMs);
|
|
201
|
+
if (!log.ok) {
|
|
202
|
+
// The counts themselves are still good evidence — degrade the
|
|
203
|
+
// extra detail, not the whole result.
|
|
204
|
+
return { ok: true, aheadCount, behindCount };
|
|
205
|
+
}
|
|
206
|
+
const [remoteSha, latestRemoteCommitIso] = log.stdout.split("\t");
|
|
207
|
+
return {
|
|
208
|
+
ok: true,
|
|
209
|
+
aheadCount,
|
|
210
|
+
behindCount,
|
|
211
|
+
...(remoteSha ? { remoteSha } : {}),
|
|
212
|
+
...(latestRemoteCommitIso ? { latestRemoteCommitIso } : {}),
|
|
213
|
+
};
|
|
214
|
+
})();
|
|
215
|
+
}
|
|
216
|
+
export async function runSessionStartStaleBaseCheck(opts = {}) {
|
|
217
|
+
const stdin = opts.stdin ?? process.stdin;
|
|
218
|
+
const stderr = opts.stderr ?? process.stderr;
|
|
219
|
+
const now = opts.now ?? new Date();
|
|
220
|
+
const note = (msg) => {
|
|
221
|
+
stderr.write(`harness session-start stale-base-check: ${msg}\n`);
|
|
222
|
+
};
|
|
223
|
+
const done = (wrote, repo, branch, sessionId, sessionSource, reason, behindCount) => ({
|
|
224
|
+
exitCode: 0,
|
|
225
|
+
wrote,
|
|
226
|
+
repo,
|
|
227
|
+
branch,
|
|
228
|
+
sessionId,
|
|
229
|
+
sessionSource,
|
|
230
|
+
...(behindCount !== undefined && { behindCount }),
|
|
231
|
+
...(reason !== undefined && { reason }),
|
|
232
|
+
});
|
|
233
|
+
let event;
|
|
234
|
+
try {
|
|
235
|
+
event = JSON.parse((await readStdin(stdin)).trim() || "{}");
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
const reason = `malformed event JSON: ${err.message}`;
|
|
239
|
+
note(reason);
|
|
240
|
+
return done(false, "", "", FALLBACK_SESSION, "default", reason);
|
|
241
|
+
}
|
|
242
|
+
const cwd = typeof opts.cwd === "string" && opts.cwd.length > 0
|
|
243
|
+
? opts.cwd
|
|
244
|
+
: typeof event.cwd === "string" && event.cwd.length > 0
|
|
245
|
+
? event.cwd
|
|
246
|
+
: process.cwd();
|
|
247
|
+
const explicit = typeof opts.session === "string" && opts.session.length > 0
|
|
248
|
+
? opts.session
|
|
249
|
+
: typeof event.session_id === "string" && event.session_id.length > 0
|
|
250
|
+
? event.session_id
|
|
251
|
+
: undefined;
|
|
252
|
+
const resolveSession = opts.resolveSession ?? resolveReadSessionId;
|
|
253
|
+
const sessionId = resolveSession(explicit, {});
|
|
254
|
+
const sessionSource = typeof opts.session === "string" && opts.session.length > 0
|
|
255
|
+
? "flag"
|
|
256
|
+
: typeof event.session_id === "string" && event.session_id.length > 0
|
|
257
|
+
? "stdin"
|
|
258
|
+
: sessionId === FALLBACK_SESSION
|
|
259
|
+
? "default"
|
|
260
|
+
: (typeof process.env.CLAUDE_CODE_SESSION_ID === "string" &&
|
|
261
|
+
process.env.CLAUDE_CODE_SESSION_ID === sessionId) ||
|
|
262
|
+
(typeof process.env.CLAUDE_SESSION_ID === "string" &&
|
|
263
|
+
process.env.CLAUDE_SESSION_ID === sessionId)
|
|
264
|
+
? "env"
|
|
265
|
+
: "transcript";
|
|
266
|
+
let manifest;
|
|
267
|
+
if (opts.manifest) {
|
|
268
|
+
manifest = opts.manifest;
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
try {
|
|
272
|
+
manifest = loadManifest(opts).manifest;
|
|
273
|
+
}
|
|
274
|
+
catch (err) {
|
|
275
|
+
const reason = `manifest load failed: ${err.message}`;
|
|
276
|
+
note(reason);
|
|
277
|
+
return done(false, "", "", sessionId, sessionSource, reason);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
const config = manifest.stale_base_check;
|
|
281
|
+
if (!config.enabled) {
|
|
282
|
+
const reason = "not configured (add `stale_base_check: { enabled: true }` to harness.yaml); skipping — no network touched";
|
|
283
|
+
note(reason);
|
|
284
|
+
return done(false, "", "", sessionId, sessionSource, reason);
|
|
285
|
+
}
|
|
286
|
+
const { repo, branch } = resolveGitContext(cwd);
|
|
287
|
+
if (repo === "") {
|
|
288
|
+
const reason = `cwd is not inside a git work tree (${cwd}); nothing to check`;
|
|
289
|
+
note(reason);
|
|
290
|
+
return done(false, "", "", sessionId, sessionSource, reason);
|
|
291
|
+
}
|
|
292
|
+
if (branch === "") {
|
|
293
|
+
const reason = "detached HEAD (or unreadable HEAD) — no branch to check a base for";
|
|
294
|
+
note(reason);
|
|
295
|
+
return done(false, repo, "", sessionId, sessionSource, reason);
|
|
296
|
+
}
|
|
297
|
+
const remote = config.remote ?? DEFAULT_REMOTE;
|
|
298
|
+
const timeoutMs = config.fetch_timeout_ms ?? DEFAULT_FETCH_TIMEOUT_MS;
|
|
299
|
+
let defaultBranch = config.default_branch;
|
|
300
|
+
if (!defaultBranch) {
|
|
301
|
+
// NOTE: `resolveOriginHeadBase` reads `refs/remotes/origin/HEAD`
|
|
302
|
+
// specifically (hardcoded "origin" — see runtime/git-context.ts), not
|
|
303
|
+
// `refs/remotes/<remote>/HEAD`. For the overwhelming common case
|
|
304
|
+
// (`remote` left at its "origin" default) this is exactly right. A
|
|
305
|
+
// manifest that configures a NON-"origin" `remote` without also
|
|
306
|
+
// setting `default_branch` explicitly will fail this auto-resolution
|
|
307
|
+
// and fall into the "could not be resolved" degrade path below —
|
|
308
|
+
// fail-open (skip, no fact written), never a wrong/misleading result,
|
|
309
|
+
// so this is a completeness gap, not a correctness risk.
|
|
310
|
+
const gitDir = findGitEntry(cwd)?.gitDir;
|
|
311
|
+
defaultBranch = gitDir ? (resolveOriginHeadBase(resolveCommonDir(gitDir)) ?? undefined) : undefined;
|
|
312
|
+
}
|
|
313
|
+
if (!defaultBranch) {
|
|
314
|
+
const reason = `default branch could not be resolved (checked \`stale_base_check.default_branch\` and ` +
|
|
315
|
+
`refs/remotes/${remote}/HEAD / packed-refs); skipping. Set \`stale_base_check.default_branch\` ` +
|
|
316
|
+
"explicitly to fix this.";
|
|
317
|
+
note(reason);
|
|
318
|
+
return done(false, repo, branch, sessionId, sessionSource, reason);
|
|
319
|
+
}
|
|
320
|
+
if (branch === defaultBranch) {
|
|
321
|
+
const reason = `on ${defaultBranch} itself; nothing to compare a base against`;
|
|
322
|
+
note(reason);
|
|
323
|
+
return done(false, repo, branch, sessionId, sessionSource, reason);
|
|
324
|
+
}
|
|
325
|
+
const runCheck = opts.runCheck ?? realCheckStaleBase;
|
|
326
|
+
const result = await runCheck({ cwd, remote, defaultBranch, branch, timeoutMs });
|
|
327
|
+
if (!result.ok) {
|
|
328
|
+
// Fail-open by construction (task hard constraint): every reason here
|
|
329
|
+
// — no remote configured, no credentials, offline, timeout, git
|
|
330
|
+
// missing — degrades to "no fact written", never a thrown error and
|
|
331
|
+
// never a block. A stale base costs time, not safety.
|
|
332
|
+
note(`${result.reason} — degrading cleanly (not blocking; the base-staleness check is advisory-only)`);
|
|
333
|
+
return done(false, repo, branch, sessionId, sessionSource, result.reason);
|
|
334
|
+
}
|
|
335
|
+
const remoteRef = `${remote}/${defaultBranch}`;
|
|
336
|
+
if (result.behindCount === 0) {
|
|
337
|
+
note(`base is current with ${remoteRef} (live check via \`git fetch\`)`);
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
const ageSuffix = result.latestRemoteCommitIso
|
|
341
|
+
? ` (newest missing commit landed ${formatSnapshotAge(Math.max(0, now.getTime() - Date.parse(result.latestRemoteCommitIso)))} ago, ${result.latestRemoteCommitIso})`
|
|
342
|
+
: "";
|
|
343
|
+
const aheadSuffix = result.aheadCount > 0 ? `; this branch itself is ${result.aheadCount} commit(s) ahead of that base` : "";
|
|
344
|
+
note(`WARNING: this branch's base is ${result.behindCount} commit(s) behind ${remoteRef}${ageSuffix}${aheadSuffix}. ` +
|
|
345
|
+
`This is a LIVE check (a real \`git fetch\`, not the local ${remoteRef} ref, which may itself be stale). ` +
|
|
346
|
+
`Recommended before continuing: \`git fetch ${remote} && git rebase ${remoteRef}\` (or merge) — ` +
|
|
347
|
+
"otherwise this PR risks landing CONFLICTING with CI never running (the incident this check exists to catch).");
|
|
348
|
+
}
|
|
349
|
+
const content = result.behindCount === 0
|
|
350
|
+
? `stale-base:${repo}:${branch} ok base:${remoteRef}`
|
|
351
|
+
: `stale-base:${repo}:${branch} behind:${result.behindCount} ahead:${result.aheadCount} base:${remoteRef}` +
|
|
352
|
+
(result.remoteSha ? ` remote_sha:${result.remoteSha}` : "");
|
|
353
|
+
let writeLedger = opts.writeLedger;
|
|
354
|
+
if (!writeLedger) {
|
|
355
|
+
const resolved = resolveManifestLedgerWriter(manifest, {
|
|
356
|
+
...(opts.ledgerTimeoutMs !== undefined ? { ledgerTimeoutMs: opts.ledgerTimeoutMs } : {}),
|
|
357
|
+
});
|
|
358
|
+
if (!resolved.ok) {
|
|
359
|
+
const reason = `${resolved.reason}; cannot record ${content}`;
|
|
360
|
+
note(reason);
|
|
361
|
+
return done(false, repo, branch, sessionId, sessionSource, reason, result.behindCount);
|
|
362
|
+
}
|
|
363
|
+
writeLedger = resolved.write;
|
|
364
|
+
}
|
|
365
|
+
const written = await writeLedger({ sessionId, content, source: LEDGER_SOURCE });
|
|
366
|
+
if (!written.ok) {
|
|
367
|
+
const reason = `ledger write failed: ${written.reason ?? "unknown error"}`;
|
|
368
|
+
note(reason);
|
|
369
|
+
return done(false, repo, branch, sessionId, sessionSource, reason, result.behindCount);
|
|
370
|
+
}
|
|
371
|
+
note(`recorded ${content} for session ${sessionId}`);
|
|
372
|
+
if (sessionSource === "default") {
|
|
373
|
+
note("WARNING: session resolved to the literal \"default\". This tag is audit-only (no gate consumes " +
|
|
374
|
+
"stale-base: yet), but pipe SessionStart event JSON on stdin, export $CLAUDE_SESSION_ID, or pass " +
|
|
375
|
+
"--session <id> for manual / scripted use to keep the audit trail useful.");
|
|
376
|
+
}
|
|
377
|
+
return done(true, repo, branch, sessionId, sessionSource, undefined, result.behindCount);
|
|
378
|
+
}
|
|
379
|
+
//# sourceMappingURL=stale-base-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stale-base-check.js","sourceRoot":"","sources":["../../../src/cli/session-start/stale-base-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
import { type LedgerWriteFn } from "../../runtime/ledger-writer.js";
|
|
1
2
|
import { type ResolveReadSessionOptions } from "../../runtime/session-id.js";
|
|
2
3
|
import type { Manifest } from "../../schema/index.js";
|
|
3
4
|
import { type LoaderOptions } from "../loader.js";
|
|
5
|
+
/**
|
|
6
|
+
* Profile names land in a filename; strip anything not filename-safe.
|
|
7
|
+
* Exported (task 13919613) so `harness doctor`'s toolchain-parity section
|
|
8
|
+
* can resolve the SAME own-profile filename this producer would, without
|
|
9
|
+
* re-implementing the sanitization rule. Pure export, no behaviour change
|
|
10
|
+
* to any call site in this module.
|
|
11
|
+
*/
|
|
12
|
+
export declare function sanitizeProfileName(value: string): string;
|
|
4
13
|
/**
|
|
5
14
|
* The toolchain snapshot v1 content (task brief §"Snapshot-Inhalt v1").
|
|
6
15
|
* Every collected field is optional: a collection failure (spawn missing/
|
|
@@ -58,6 +67,116 @@ export type CollectNpmGlobalsResult = {
|
|
|
58
67
|
ok: false;
|
|
59
68
|
reason: string;
|
|
60
69
|
};
|
|
70
|
+
/**
|
|
71
|
+
* Hermetic guard (mirrors `realNpmExec` in src/cli/doctor/npm-bin-path.ts):
|
|
72
|
+
* asserts BEFORE touching `child_process` that we are not running under
|
|
73
|
+
* vitest without a test having injected `runNodeVersion`. This function has
|
|
74
|
+
* no try/catch around the assertion, and its only caller,
|
|
75
|
+
* `collectLocalSnapshot`, calls it (via the `runNodeVersion` param, defaulted
|
|
76
|
+
* to this function in `runSessionStartToolchainParity`) with no surrounding
|
|
77
|
+
* try/catch either — see src/runtime/hermetic-spawn-guard.ts for why that
|
|
78
|
+
* matters (a thrown `HermeticSpawnViolationError` must propagate, not be
|
|
79
|
+
* folded into an ordinary "collection failed" outcome).
|
|
80
|
+
*
|
|
81
|
+
* PATH-resolved deliberately: `execFile("node", ...)` does a PATH lookup for
|
|
82
|
+
* the literal string "node", NOT `process.execPath` — the entire point of
|
|
83
|
+
* this collector is capturing the PATH state of the *shell* the session
|
|
84
|
+
* runs in (the PATH-shim incident this task follows up on was exactly a
|
|
85
|
+
* PATH pointing at the wrong node), which `process.version` (the node
|
|
86
|
+
* running THIS harness process) would silently paper over.
|
|
87
|
+
*
|
|
88
|
+
* Exported (task 13919613) so `harness doctor`'s toolchain-parity section
|
|
89
|
+
* can default its own `runNodeVersion` injection knob to the SAME real
|
|
90
|
+
* collector this producer uses — the whole point of the reuse is that
|
|
91
|
+
* doctor never re-implements this spawn/parse logic. No behaviour change:
|
|
92
|
+
* this is the exact function every existing call site already used.
|
|
93
|
+
*/
|
|
94
|
+
export declare function realNodeVersionSpawn(timeoutMs: number): Promise<CollectNodeVersionResult>;
|
|
95
|
+
/**
|
|
96
|
+
* Hermetic guard, same shape/rationale as {@link realNodeVersionSpawn} —
|
|
97
|
+
* see that function's doc. Tolerant of a non-zero exit the same way
|
|
98
|
+
* `spawnPreflight` (session-start/index.ts) is tolerant of `preflight run`'s
|
|
99
|
+
* exit code: `npm ls -g` can exit non-zero on an unrelated extraneous/
|
|
100
|
+
* invalid global entry while still emitting a perfectly parseable
|
|
101
|
+
* `dependencies` JSON block, so a parseable stdout wins over the exit code.
|
|
102
|
+
*
|
|
103
|
+
* Exported (task 13919613) for the same reuse reason as
|
|
104
|
+
* {@link realNodeVersionSpawn} — `harness doctor`'s toolchain-parity
|
|
105
|
+
* section defaults to this real collector rather than duplicating it.
|
|
106
|
+
*/
|
|
107
|
+
export declare function realNpmGlobalsSpawn(timeoutMs: number): Promise<CollectNpmGlobalsResult>;
|
|
108
|
+
/**
|
|
109
|
+
* Read `<workspaceRoot>/.ai/workflow/manifest.json`'s `version` field
|
|
110
|
+
* (the orchestrator-workflow kit's own version marker). A missing file is
|
|
111
|
+
* the expected common case (most repos are not OW-kit-managed) and is
|
|
112
|
+
* NOT an error — it degrades silently to `{}`. Malformed JSON or a
|
|
113
|
+
* non-string `version` field on a manifest that DOES exist is noteworthy
|
|
114
|
+
* (something is there but broken), so that degrades to `{ error }`.
|
|
115
|
+
*
|
|
116
|
+
* Exported (task 13919613) for the same reuse reason as
|
|
117
|
+
* {@link realNodeVersionSpawn}.
|
|
118
|
+
*/
|
|
119
|
+
export declare function realReadOwKitVersion(workspaceRoot: string): {
|
|
120
|
+
version?: string;
|
|
121
|
+
error?: string;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* MCP server names from Claude Code's user-scope registry — a pure FILE
|
|
125
|
+
* READ (never a spawn), reusing the exact primitive `harness doctor`'s
|
|
126
|
+
* claude-mcp check ultimately compares against
|
|
127
|
+
* (`resolveClaudeUserRegistryPath` + `readTopLevelMcpServers` in
|
|
128
|
+
* src/io/claude-mcp.ts — the same functions `adopt`/`detect` use for the
|
|
129
|
+
* same "read the effective registration from outside io/claude-mcp.ts"
|
|
130
|
+
* purpose). Deliberately NOT `claude mcp list` (a spawn): the brief calls
|
|
131
|
+
* for a file-read source for the NAMES here, and this is it.
|
|
132
|
+
*
|
|
133
|
+
* Exported (task 13919613) for the same reuse reason as
|
|
134
|
+
* {@link realNodeVersionSpawn}.
|
|
135
|
+
*/
|
|
136
|
+
export declare function realReadMcpServerNames(): {
|
|
137
|
+
names: string[];
|
|
138
|
+
error?: string;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Exported (task 13919613) so `harness doctor`'s toolchain-parity section
|
|
142
|
+
* can call {@link collectLocalSnapshot} directly instead of duplicating
|
|
143
|
+
* its orchestration of the four collectors.
|
|
144
|
+
*/
|
|
145
|
+
export interface CollectLocalSnapshotOptions {
|
|
146
|
+
profile: string;
|
|
147
|
+
now: Date;
|
|
148
|
+
workspaceRoot: string;
|
|
149
|
+
nodeTimeoutMs: number;
|
|
150
|
+
npmTimeoutMs: number;
|
|
151
|
+
runNodeVersion: (timeoutMs: number) => Promise<CollectNodeVersionResult>;
|
|
152
|
+
runNpmGlobals: (timeoutMs: number) => Promise<CollectNpmGlobalsResult>;
|
|
153
|
+
readOwKitVersion: (workspaceRoot: string) => {
|
|
154
|
+
version?: string;
|
|
155
|
+
error?: string;
|
|
156
|
+
};
|
|
157
|
+
readMcpServerNames: () => {
|
|
158
|
+
names: string[];
|
|
159
|
+
error?: string;
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Collect the local, LIVE toolchain snapshot. Never throws (every
|
|
164
|
+
* collector above degrades to `{ok:false,reason}` / `{error}` instead of
|
|
165
|
+
* rejecting/throwing) — a collection failure is reported both inline on
|
|
166
|
+
* the returned snapshot (the matching `*Error` field) AND as a `notes`
|
|
167
|
+
* line for the caller to log, but never stops the other collectors from
|
|
168
|
+
* running. node/npm run in PARALLEL (see the timeout constants' comment
|
|
169
|
+
* above for why).
|
|
170
|
+
*
|
|
171
|
+
* Exported (task 13919613): `harness doctor`'s toolchain-parity section
|
|
172
|
+
* imports this directly as its Collector core — see that module's header
|
|
173
|
+
* for the reuse rationale. No behaviour change to this function or any
|
|
174
|
+
* existing call site.
|
|
175
|
+
*/
|
|
176
|
+
export declare function collectLocalSnapshot(opts: CollectLocalSnapshotOptions): Promise<{
|
|
177
|
+
snapshot: ToolchainSnapshot;
|
|
178
|
+
notes: string[];
|
|
179
|
+
}>;
|
|
61
180
|
export type DriftKind = "node" | "ow_kit" | "npm_package_version" | "npm_package_missing_local" | "npm_package_missing_peer" | "mcp_missing_local" | "mcp_missing_peer";
|
|
62
181
|
export interface DriftItem {
|
|
63
182
|
kind: DriftKind;
|
|
@@ -104,14 +223,7 @@ export interface SessionStartToolchainParityOptions extends LoaderOptions {
|
|
|
104
223
|
/** Inject a manifest (tests). Bypasses loadManifest. */
|
|
105
224
|
manifest?: Manifest;
|
|
106
225
|
/** Inject the ledger writer (tests). */
|
|
107
|
-
writeLedger?:
|
|
108
|
-
sessionId: string;
|
|
109
|
-
content: string;
|
|
110
|
-
source: string;
|
|
111
|
-
}) => Promise<{
|
|
112
|
-
ok: boolean;
|
|
113
|
-
reason?: string;
|
|
114
|
-
}>;
|
|
226
|
+
writeLedger?: LedgerWriteFn;
|
|
115
227
|
/** Inject the read-path session resolver (env + transcript discovery). Test seam. */
|
|
116
228
|
resolveSession?: (explicit: string | undefined, opts: ResolveReadSessionOptions) => string;
|
|
117
229
|
/** Inject the `node --version` collector (tests) — see realNodeVersionSpawn's doc. */
|
|
@@ -140,10 +252,32 @@ export interface SessionStartToolchainParityResult {
|
|
|
140
252
|
peersCompared: number;
|
|
141
253
|
/** Total drift items found across every compared peer. */
|
|
142
254
|
driftCount: number;
|
|
255
|
+
/**
|
|
256
|
+
* Peer files present in the machine-state directory that failed to parse
|
|
257
|
+
* as valid JSON (readable but not JSON, or JSON that fails the minimal
|
|
258
|
+
* `profile`/`timestamp` shape check) and were therefore excluded from
|
|
259
|
+
* `driftCount`/`peersCompared`. A non-zero count here means `driftCount`
|
|
260
|
+
* reflects a PARTIAL comparison, not a full one — see the
|
|
261
|
+
* `:unparseable-peer:<n>` ledger-content suffix this drives.
|
|
262
|
+
*
|
|
263
|
+
* The contrapositive does NOT hold: peers whose file could not be READ
|
|
264
|
+
* at all (EACCES, EISDIR, ...) are skipped by the unreadable branch and
|
|
265
|
+
* are not counted here, so the absence of the suffix does not prove a
|
|
266
|
+
* complete comparison. That branch is scoped to the follow-up hardening
|
|
267
|
+
* round (task c1b5ade5).
|
|
268
|
+
*/
|
|
269
|
+
unparseablePeerCount: number;
|
|
143
270
|
/** Resolved session id. */
|
|
144
271
|
sessionId: string;
|
|
145
272
|
sessionSource: "flag" | "stdin" | "env" | "transcript" | "default";
|
|
146
273
|
/** Human-readable explanation of a non-write outcome, for diagnostics. */
|
|
147
274
|
reason?: string;
|
|
148
275
|
}
|
|
276
|
+
/**
|
|
277
|
+
* Exported (task 13919613) so `harness doctor`'s toolchain-parity section
|
|
278
|
+
* resolves the SAME default machine-state directory this producer writes
|
|
279
|
+
* to when `toolchain_parity.machine_state_dir` is omitted from the
|
|
280
|
+
* manifest — reusing the constant instead of re-deriving it.
|
|
281
|
+
*/
|
|
282
|
+
export declare function defaultMachineStateDir(): string;
|
|
149
283
|
export declare function runSessionStartToolchainParity(opts?: SessionStartToolchainParityOptions): Promise<SessionStartToolchainParityResult>;
|