vouchington-tooling 0.11.0 → 0.11.2

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.
Files changed (66) hide show
  1. package/README.md +17 -0
  2. package/dist/agent-blackboard/snapshot-cleanup-receipt.mjs +2 -0
  3. package/dist/agent-blackboard/snapshot-cleanup-resume.d.mts +1 -1
  4. package/dist/agent-blackboard/snapshot-cleanup-resume.mjs +3 -6
  5. package/dist/agent-harness-config/apply.d.mts +4 -0
  6. package/dist/agent-harness-config/apply.mjs +72 -0
  7. package/dist/agent-harness-config/cli.d.mts +10 -0
  8. package/dist/agent-harness-config/cli.mjs +100 -0
  9. package/dist/agent-harness-config/index.d.mts +4 -0
  10. package/dist/agent-harness-config/index.mjs +3 -0
  11. package/dist/agent-harness-config/merge-json.d.mts +5 -0
  12. package/dist/agent-harness-config/merge-json.mjs +62 -0
  13. package/dist/agent-harness-config/merge-toml.d.mts +6 -0
  14. package/dist/agent-harness-config/merge-toml.mjs +132 -0
  15. package/dist/agent-harness-config/paths.d.mts +3 -0
  16. package/dist/agent-harness-config/paths.mjs +117 -0
  17. package/dist/agent-harness-config/policy.d.mts +14 -0
  18. package/dist/agent-harness-config/policy.mjs +51 -0
  19. package/dist/agent-harness-config/prerequisites.d.mts +2 -0
  20. package/dist/agent-harness-config/prerequisites.mjs +67 -0
  21. package/dist/agent-harness-config/toml-value.d.mts +6 -0
  22. package/dist/agent-harness-config/toml-value.mjs +69 -0
  23. package/dist/agent-harness-config/types.d.mts +88 -0
  24. package/dist/agent-harness-config/types.mjs +1 -0
  25. package/dist/browser-session-runner/watchdog.mjs +1 -0
  26. package/dist/cli/index.mjs +9 -6
  27. package/dist/cli/parse.d.mts +3 -0
  28. package/dist/cli/parse.mjs +2 -0
  29. package/dist/cli/usage.d.mts +1 -1
  30. package/dist/cli/usage.mjs +5 -0
  31. package/dist/gha-artifacts-cleanup/classify.mjs +1 -0
  32. package/dist/gha-post-review/claude-token.mjs +2 -0
  33. package/dist/gha-post-review/github.mjs +1 -0
  34. package/dist/gha-post-review/post.mjs +7 -1
  35. package/dist/gha-pr-checkpoint/codec.mjs +1 -0
  36. package/dist/gha-review-payload/diff.mjs +1 -0
  37. package/dist/gha-review-payload/payload.mjs +2 -0
  38. package/dist/gha-workspace-policy/shared.mjs +1 -0
  39. package/dist/http-link-pagination/index.mjs +1 -0
  40. package/dist/index.d.mts +2 -0
  41. package/dist/index.mjs +1 -0
  42. package/dist/pnpm-deploy/normalize.mjs +1 -0
  43. package/dist/pnpm-install/install-operations.d.mts +2 -1
  44. package/dist/pnpm-install/install-operations.mjs +16 -4
  45. package/dist/pnpm-install/metadata.d.mts +3 -3
  46. package/dist/pnpm-install/metadata.mjs +10 -24
  47. package/dist/pnpm-install/pending-builds.d.mts +6 -14
  48. package/dist/pnpm-install/pending-builds.mjs +23 -91
  49. package/dist/pnpm-install/pnpm-install-fake-pnpm.test-helpers.mjs +15 -1
  50. package/dist/pnpm-install/runner.mjs +27 -59
  51. package/dist/pnpm-install/transition.d.mts +2 -7
  52. package/dist/pnpm-install/transition.mjs +3 -11
  53. package/dist/runner-port-policy/index.mjs +2 -0
  54. package/dist/session-friction/log.mjs +1 -0
  55. package/dist/skill-discovery/manifest.mjs +2 -0
  56. package/dist/sql-ast/constraint-do-block.mjs +1 -0
  57. package/dist/sql-ast/drop-index.mjs +2 -0
  58. package/dist/vitest-blob-manifest/reports-cli.mjs +2 -0
  59. package/docs/agent-harness-config.md +134 -0
  60. package/package.json +8 -1
  61. package/dist/cli/commands/post-review.d.mts +0 -1
  62. package/dist/cli/commands/post-review.mjs +0 -4
  63. package/dist/cli/commands/stage-review-payload.d.mts +0 -1
  64. package/dist/cli/commands/stage-review-payload.mjs +0 -4
  65. package/dist/cli/commands/swift-semantic-equal.d.mts +0 -1
  66. package/dist/cli/commands/swift-semantic-equal.mjs +0 -4
@@ -0,0 +1,51 @@
1
+ export const DEFAULT_EXTRA_WRITABLE_ROOTS = [
2
+ '~/.cargo',
3
+ '~/Library/pnpm',
4
+ '~/.local/share/pnpm',
5
+ '~/.pnpm-store',
6
+ '~/Library/Caches/no-mistakes',
7
+ '~/Library/Caches/pnpm',
8
+ '~/.pnpm-state',
9
+ '~/.cache/no-mistakes',
10
+ '~/.cache/pnpm',
11
+ '~/.local/state/pnpm',
12
+ '/var/folders',
13
+ '/private/var/folders',
14
+ ];
15
+ export const CLAUDE_DEFAULT_MODE = 'auto';
16
+ export const CODEX_APPROVAL_POLICY = 'on-request';
17
+ export const CODEX_APPROVALS_REVIEWER = 'auto_review';
18
+ export const CODEX_SANDBOX_MODE = 'workspace-write';
19
+ export const GROK_PERMISSION_MODE = 'auto';
20
+ export const GROK_SANDBOX_PROFILE = 'workspace-write';
21
+ export const GROK_AUTO_MODE_ENABLED = true;
22
+ export const GROK_DEFAULT_AUTO_MODE = true;
23
+ export const CURSOR_APPROVAL_MODE = 'auto-review';
24
+ export const CURSOR_SANDBOX_MODE = 'enabled';
25
+ export function dumpHarnessPolicy(extraWritableRoots = DEFAULT_EXTRA_WRITABLE_ROOTS) {
26
+ return {
27
+ claude: {
28
+ defaultMode: CLAUDE_DEFAULT_MODE,
29
+ sandboxEnabled: true,
30
+ sandboxFailIfUnavailable: true,
31
+ useAutoModeDuringPlan: true,
32
+ },
33
+ codex: {
34
+ approval_policy: CODEX_APPROVAL_POLICY,
35
+ approvals_reviewer: CODEX_APPROVALS_REVIEWER,
36
+ sandbox_mode: CODEX_SANDBOX_MODE,
37
+ },
38
+ cursor: { approvalMode: CURSOR_APPROVAL_MODE, sandboxMode: CURSOR_SANDBOX_MODE },
39
+ extraWritableRoots: [...extraWritableRoots],
40
+ grok: {
41
+ auto_mode_enabled: GROK_AUTO_MODE_ENABLED,
42
+ default_auto_mode: GROK_DEFAULT_AUTO_MODE,
43
+ permission_mode: GROK_PERMISSION_MODE,
44
+ sandbox_profile_defined: GROK_SANDBOX_PROFILE,
45
+ sandbox_profile_requires_cli_selection: true,
46
+ },
47
+ };
48
+ }
49
+ export function sameValue(left, right) {
50
+ return JSON.stringify(left) === JSON.stringify(right);
51
+ }
@@ -0,0 +1,2 @@
1
+ import type { HarnessConfigOptions, HarnessPrerequisite } from './types.mts';
2
+ export declare function checkHarnessPrerequisites(root: string | undefined, options?: HarnessConfigOptions): Promise<HarnessPrerequisite[]>;
@@ -0,0 +1,67 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { homedir } from 'node:os';
3
+ import { join, resolve } from 'node:path';
4
+ import { readTomlKey } from './merge-toml.mjs';
5
+ import { CURSOR_APPROVAL_MODE } from './policy.mjs';
6
+ import { HARNESS_IDS } from './types.mjs';
7
+ async function readOptional(path) {
8
+ try {
9
+ return await readFile(path, 'utf8');
10
+ }
11
+ catch (error) {
12
+ if (error.code === 'ENOENT')
13
+ return '';
14
+ throw error;
15
+ }
16
+ }
17
+ function selected(options, harness) {
18
+ return (options?.harnesses ?? HARNESS_IDS).includes(harness);
19
+ }
20
+ function homeDir(options) {
21
+ return resolve(options?.home ?? homedir());
22
+ }
23
+ async function codexTrust(root, options) {
24
+ const source = await readOptional(join(homeDir(options), '.codex', 'config.toml'));
25
+ const table = `projects.${JSON.stringify(resolve(root))}`;
26
+ return {
27
+ harness: 'codex',
28
+ key: 'trusted-project',
29
+ message: 'trust this project in Codex before relying on its .codex/config.toml',
30
+ satisfied: readTomlKey(source, table, 'trust_level') === 'trusted',
31
+ };
32
+ }
33
+ async function cursorApproval(options) {
34
+ const source = await readOptional(join(homeDir(options), '.cursor', 'cli-config.json'));
35
+ let satisfied = false;
36
+ try {
37
+ const parsed = JSON.parse(source);
38
+ satisfied = parsed.approvalMode === CURSOR_APPROVAL_MODE;
39
+ }
40
+ catch {
41
+ // A missing or invalid user config leaves the prerequisite unsatisfied.
42
+ }
43
+ return {
44
+ harness: 'cursor',
45
+ key: 'global-auto-review',
46
+ message: 'set approvalMode=auto-review in ~/.cursor/cli-config.json',
47
+ satisfied,
48
+ };
49
+ }
50
+ function grokSandbox() {
51
+ return {
52
+ harness: 'grok',
53
+ key: 'sandbox-profile-selection',
54
+ message: 'start Grok with --sandbox workspace-write to activate the defined profile',
55
+ satisfied: false,
56
+ };
57
+ }
58
+ export async function checkHarnessPrerequisites(root, options) {
59
+ const prerequisites = [];
60
+ if (selected(options, 'grok'))
61
+ prerequisites.push(grokSandbox());
62
+ if (root !== undefined && selected(options, 'codex'))
63
+ prerequisites.push(await codexTrust(root, options));
64
+ if (root !== undefined && selected(options, 'cursor'))
65
+ prerequisites.push(await cursorApproval(options));
66
+ return prerequisites;
67
+ }
@@ -0,0 +1,6 @@
1
+ import type { TomlValue } from './types.mts';
2
+ export declare function formatTomlValue(value: TomlValue): string;
3
+ export declare function parseTomlValue(text: string, start: number): {
4
+ end: number;
5
+ value: boolean | string | string[];
6
+ };
@@ -0,0 +1,69 @@
1
+ export function formatTomlValue(value) {
2
+ if (typeof value === 'boolean')
3
+ return value ? 'true' : 'false';
4
+ if (typeof value === 'string')
5
+ return JSON.stringify(value);
6
+ const items = value.map((entry) => ` ${JSON.stringify(entry)},`).join('\n');
7
+ return `[\n${items}\n]`;
8
+ }
9
+ function skipSpaceAndComments(text, start) {
10
+ let index = start;
11
+ while (index < text.length) {
12
+ const char = text[index];
13
+ if (char === ' ' || char === '\t' || char === '\n' || char === '\r' || char === ',') {
14
+ index += 1;
15
+ continue;
16
+ }
17
+ if (char === '#') {
18
+ while (index < text.length && text[index] !== '\n')
19
+ index += 1;
20
+ continue;
21
+ }
22
+ break;
23
+ }
24
+ return index;
25
+ }
26
+ function parseTomlString(text, start) {
27
+ const quote = text[start];
28
+ let index = start + 1;
29
+ let value = '';
30
+ while (index < text.length) {
31
+ const char = text[index];
32
+ if (char === quote)
33
+ return { end: index + 1, value };
34
+ if (quote === '"' && char === '\\') {
35
+ const next = text[index + 1];
36
+ if (next === undefined)
37
+ break;
38
+ value += next === 'n' ? '\n' : next === 't' ? '\t' : next;
39
+ index += 2;
40
+ continue;
41
+ }
42
+ value += char;
43
+ index += 1;
44
+ }
45
+ throw new Error('unterminated TOML string');
46
+ }
47
+ export function parseTomlValue(text, start) {
48
+ const index = skipSpaceAndComments(text, start);
49
+ if (text.startsWith('true', index))
50
+ return { end: index + 4, value: true };
51
+ if (text.startsWith('false', index))
52
+ return { end: index + 5, value: false };
53
+ if (text[index] === '"' || text[index] === "'")
54
+ return parseTomlString(text, index);
55
+ if (text[index] !== '[')
56
+ throw new Error('unsupported TOML value');
57
+ const items = [];
58
+ let cursor = index + 1;
59
+ for (;;) {
60
+ cursor = skipSpaceAndComments(text, cursor);
61
+ if (text[cursor] === ']')
62
+ return { end: cursor + 1, value: items };
63
+ if (text[cursor] !== '"' && text[cursor] !== "'")
64
+ throw new Error('unsupported TOML array value');
65
+ const parsed = parseTomlString(text, cursor);
66
+ items.push(parsed.value);
67
+ cursor = parsed.end;
68
+ }
69
+ }
@@ -0,0 +1,88 @@
1
+ export declare const HARNESS_IDS: readonly ['claude', 'codex', 'grok', 'cursor'];
2
+ export type HarnessId = (typeof HARNESS_IDS)[number];
3
+ export type ApplyTarget = {
4
+ readonly kind: 'global';
5
+ } | {
6
+ readonly kind: 'repo';
7
+ readonly root: string;
8
+ };
9
+ export interface JsonPatch {
10
+ readonly merge?: 'union';
11
+ readonly path: readonly string[];
12
+ readonly value: unknown;
13
+ }
14
+ export type TomlValue = boolean | string | readonly string[];
15
+ export interface TomlPatch {
16
+ readonly key: string;
17
+ readonly table: string;
18
+ readonly value: TomlValue;
19
+ }
20
+ export type FilePlan = {
21
+ readonly format: 'json';
22
+ readonly patches: readonly JsonPatch[];
23
+ readonly path: string;
24
+ } | {
25
+ readonly format: 'toml';
26
+ readonly patches: readonly TomlPatch[];
27
+ readonly path: string;
28
+ };
29
+ export interface KeyDrift {
30
+ readonly current: unknown;
31
+ readonly desired: unknown;
32
+ readonly key: string;
33
+ readonly path: string;
34
+ }
35
+ export interface FileResult {
36
+ readonly action: 'create' | 'update' | 'ok' | 'missing';
37
+ readonly drifts: readonly KeyDrift[];
38
+ readonly path: string;
39
+ }
40
+ export interface HarnessPlan {
41
+ readonly files: readonly FilePlan[];
42
+ readonly target: ApplyTarget;
43
+ }
44
+ export interface HarnessCheckResult {
45
+ readonly compliant: boolean;
46
+ readonly files: readonly FileResult[];
47
+ readonly prerequisites: readonly HarnessPrerequisite[];
48
+ readonly target: ApplyTarget;
49
+ }
50
+ export interface HarnessPrerequisite {
51
+ readonly harness: HarnessId;
52
+ readonly key: string;
53
+ readonly message: string;
54
+ readonly satisfied: boolean;
55
+ }
56
+ export interface HarnessApplyResult extends HarnessCheckResult {
57
+ readonly written: readonly string[];
58
+ }
59
+ export interface HarnessConfigOptions {
60
+ readonly extraWritableRoots?: readonly string[];
61
+ readonly harnesses?: readonly HarnessId[];
62
+ readonly home?: string;
63
+ }
64
+ export interface HarnessPolicyDump {
65
+ readonly claude: {
66
+ readonly defaultMode: 'auto';
67
+ readonly sandboxEnabled: true;
68
+ readonly sandboxFailIfUnavailable: true;
69
+ readonly useAutoModeDuringPlan: true;
70
+ };
71
+ readonly codex: {
72
+ readonly approval_policy: 'on-request';
73
+ readonly approvals_reviewer: 'auto_review';
74
+ readonly sandbox_mode: 'workspace-write';
75
+ };
76
+ readonly cursor: {
77
+ readonly approvalMode: 'auto-review';
78
+ readonly sandboxMode: 'enabled';
79
+ };
80
+ readonly extraWritableRoots: readonly string[];
81
+ readonly grok: {
82
+ readonly auto_mode_enabled: true;
83
+ readonly default_auto_mode: true;
84
+ readonly permission_mode: 'auto';
85
+ readonly sandbox_profile_defined: 'workspace-write';
86
+ readonly sandbox_profile_requires_cli_selection: true;
87
+ };
88
+ }
@@ -0,0 +1 @@
1
+ export const HARNESS_IDS = ['claude', 'codex', 'grok', 'cursor'];
@@ -14,6 +14,7 @@ export function createWatchdogLifecycle() {
14
14
  complete = true;
15
15
  if (!cleanup)
16
16
  return;
17
+ // oxlint-disable-next-line no-mistakes/ts-no-const-aliases -- preserve the registered cleanup before clearing mutable lifecycle state
17
18
  const next = cleanup;
18
19
  cleanup = undefined;
19
20
  run(next, onError);
@@ -2,7 +2,11 @@
2
2
  import { readFileSync, realpathSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
4
  import { pathToFileURL } from 'node:url';
5
+ import { runAgentHarnessConfigCli } from '../agent-harness-config/cli.mjs';
6
+ import { runPostReviewCli } from '../gha-post-review/cli.mjs';
7
+ import { runStageReviewPayloadCli } from '../gha-review-payload/cli.mjs';
5
8
  import { readPackageVersion } from '../package-version.mjs';
9
+ import { runSwiftSemanticEqualCli } from '../swift-semantic-equal/cli.mjs';
6
10
  import { runGhaArtifactsCleanup } from './commands/gha-artifacts-cleanup.mjs';
7
11
  import { runGhaRuntimeAudit } from './commands/gha-runtime-audit.mjs';
8
12
  import { runHttpOrigin } from './commands/http-origin.mjs';
@@ -10,9 +14,6 @@ import { runPnpmInstallCli } from './commands/pnpm-install.mjs';
10
14
  import { runRunnerPortPolicy } from './commands/runner-port-policy.mjs';
11
15
  import { runScript } from './commands/spawn-script.mjs';
12
16
  import { runNugetCentralVersionCommand } from './commands/nuget-central-version.mjs';
13
- import { runPostReviewCommand } from './commands/post-review.mjs';
14
- import { runStageReviewPayloadCommand } from './commands/stage-review-payload.mjs';
15
- import { runSwiftSemanticEqualCommand } from './commands/swift-semantic-equal.mjs';
16
17
  import { runVitestBlobManifestCommand } from './commands/vitest-blob-manifest.mjs';
17
18
  import { runVitestReportAttemptCommand } from './commands/vitest-report-attempt.mjs';
18
19
  import { runPrepareVitestReportsCommand } from './commands/prepare-vitest-reports.mjs';
@@ -89,6 +90,8 @@ export function runCli(argv = process.argv) {
89
90
  return runRunnerPortPolicy(parsed);
90
91
  case 'with-host-lock':
91
92
  return runWithHostLock(parsed.args);
93
+ case 'agent-harness-config':
94
+ return runAgentHarnessConfigCli(parsed.args);
92
95
  case 'gha-runtime-audit':
93
96
  return runGhaRuntimeAudit(parsed);
94
97
  case 'script': {
@@ -106,11 +109,11 @@ export function runCli(argv = process.argv) {
106
109
  case 'nuget-central-version':
107
110
  return runNugetCentralVersionCommand(parsed.args);
108
111
  case 'swift-semantic-equal':
109
- return runSwiftSemanticEqualCommand(parsed.args);
112
+ return runSwiftSemanticEqualCli(parsed.args);
110
113
  case 'post-review':
111
- return runPostReviewCommand();
114
+ return runPostReviewCli();
112
115
  case 'stage-review-payload':
113
- return runStageReviewPayloadCommand(parsed.args);
116
+ return runStageReviewPayloadCli(parsed.args);
114
117
  case 'http-origin':
115
118
  return runHttpOrigin(parsed.field, parsed.value);
116
119
  case 'gha-artifacts-cleanup':
@@ -14,6 +14,9 @@ export type ParsedCli = {
14
14
  } | {
15
15
  kind: 'with-host-lock';
16
16
  args: string[];
17
+ } | {
18
+ kind: 'agent-harness-config';
19
+ args: string[];
17
20
  } | {
18
21
  kind: 'script';
19
22
  command: ScriptCommand;
@@ -35,6 +35,8 @@ export function parseCli(argv) {
35
35
  return parseRunnerPortPolicy(rest);
36
36
  if (command === 'with-host-lock')
37
37
  return { kind: 'with-host-lock', args: rest };
38
+ if (command === 'agent-harness-config')
39
+ return { kind: 'agent-harness-config', args: rest };
38
40
  if (command === 'gha-runtime-audit')
39
41
  return parseGhaRuntimeAudit(rest);
40
42
  if (command === 'pnpm-install')
@@ -1,2 +1,2 @@
1
- export declare const USAGE = "Usage: vouchington <command> [options]\n\nCommands:\n runner-port-policy Print or validate a runner port policy\n with-host-lock Run a command under a host-wide lock\n gha-runtime-audit Audit successful GitHub Actions job runtimes\n require-up-to-date Require HEAD to include a fetched remote branch\n gitleaks-directory-scan Scan a directory with Gitleaks\n ast-grep-examples Run AST-grep rule examples\n ast-grep-pack Print shipped unconditional AST-grep pack paths as JSON\n gha-workspace-policy Check GitHub Actions workspace safety policy\n gha-output Write a collision-safe multiline GITHUB_OUTPUT record\n gha-needs-results Fail if required GitHub Actions job results failed\n download-with-diagnostics Download a URL and report HTTP status on failure\n download-optional-run-artifacts Download optional artifacts from the current run\n host-pressure-diagnostics Print a bounded host memory/OOM/PSI snapshot\n allocate-browser-safe-ports Allocate Fetch-safe localhost ports\n diagnose-port-collision Capture bounded localhost port diagnostics\n prepare-trivy-db Download the Trivy vulnerability database\n gha-artifacts-cleanup Delete classified GitHub Actions artifacts\n http-origin Validate an optional HTTP(S) origin\n vitest-blob-manifest Stamp a vitest-blob-manifest:v1 identity file\n vitest-report-attempt Write or read a Vitest report-attempt marker\n prepare-vitest-reports Validate and select Vitest report JSON files\n pnpm-install Install a pnpm workspace with retry and release-age fail-fast\n check-cache-size Measure a path and decide whether to save a GHA cache\n make-shard-matrix Emit a [1..N] GitHub Actions shard matrix\n load-runner-env Overlay a runner env file onto GITHUB_ENV with injection guards\n clean-workspace Reset a persistent-runner workspace with a fork-PR trust gate\n install-github-release Download a checksum-verified GitHub Release binary\n run-with-timeout Run a command with GNU timeout or a Perl fallback\n lint-links Two-pass lychee: internal links fail, external warn\n materialize-pr-context Dump PR title/body/files/diff/comments and #N crawl\n wait-for-apt-locks Wait until apt/dpkg lock files are free\n install-playwright-chromium-arm64 Install Playwright Chromium from browsers.json\n ghcr-package-retention Delete old GHCR package versions past KEEP_MIN\n harness-admission-lane Compute a GITHUB_RUN_ID admission lane for fleet fan-out\n harness-assert-gates Fail if any named HARNESS_*_ENABLED gate is enabled\n nuget-central-version Validate a Directory.Packages.props PackageVersion delta\n swift-semantic-equal Compare Swift sources ignoring comments and whitespace\n post-review Post one COMMENT review from a staged payload file\n stage-review-payload Validate a review payload file into a staging directory\n retrospective-transcript Format facts from Claude-compatible, Codex, or Grok transcripts\n link-skill Link one packaged skill into an explicit consumer directory\n retrospective-facts Gather immutable facts for a retrospective\n agent-blackboard Probe and journal an Agent Blackboard deployment\n\nOptions:\n -h, --help Show this help\n -v, --version Print the package version\n\nrunner-port-policy\n (no args) Print the shipped policy as JSON\n --file <path> Validate and print a policy file\n --reserved <port> Print true if the port is reserved\n\nwith-host-lock\n --name <family>\n [--slots <n>]\n --timeout-seconds <n>\n [--command-timeout-seconds <n>]\n [--failure-diagnostics <absolute-script>]\n [--on-acquire-timeout fail|run-unlocked]\n -- <command> [args...]\n\ngha-runtime-audit\n [--repository owner/name] Default GITHUB_REPOSITORY\n [--branch main]\n --pr-workflow <name|/regex/> Repeatable\n --push-workflow <name|/regex/> Repeatable\n\nrequire-up-to-date --remote <name> --branch <name>\ngitleaks-directory-scan --config <path> [--directory <path>]\nast-grep-examples --rules <directory> --config <path>\nast-grep-pack\ngha-workspace-policy [--root <directory>] [--workflow-directory <directory>] [--action-directory <directory>]\n\ngha-output <name>\ngha-needs-results [label]\ndownload-with-diagnostics <url> <destination> [-- curl-args...]\ndownload-optional-run-artifacts (--name <name> | --pattern <pattern>) --dir <directory>\nhost-pressure-diagnostics\nallocate-browser-safe-ports [count] [--policy path] [--forbidden-ports path]\ndiagnose-port-collision [--ports \"2200 2216\"] [--output-dir PATH]\nprepare-trivy-db\ngha-artifacts-cleanup run --run-id <id> [--keep-pattern glob] [--delete-pattern glob] [--patterns-file json]\ngha-artifacts-cleanup sweep --older-than-hours <n> [--keep-pattern glob] [--delete-pattern glob] [--patterns-file json]\nhttp-origin [--field NAME] [value]\nvitest-blob-manifest <suite> [reports-directory]\nvitest-report-attempt <write DIRECTORY SUITE|read ROOT>\nprepare-vitest-reports [primary-directory] [fallback-directory] [output-directory]\npnpm-install --runner-lifecycle persistent|ephemeral|ephemeral-full --install-scripts true|false\ncheck-cache-size <path> <max-bytes> <label>\nmake-shard-matrix <total>\nload-runner-env\nclean-workspace\ninstall-github-release --repo owner/name --version X --asset 'name-{platform}.tar.gz' --bin name [--tag-prefix PREFIX] [--expected-sha256 SHA256] [--no-checksum] [--checksums-asset NAME] [--version-flag FLAG] [--bin-dir DIR]\nrun-with-timeout <timeout-seconds> <kill-after-seconds> <command...>\nlint-links [--offline] [--config PATH] [--glob PATTERN] [files...]\nmaterialize-pr-context\nwait-for-apt-locks\ninstall-playwright-chromium-arm64 [name:archive...]\nghcr-package-retention <url-encoded-package>...\nharness-admission-lane <lanes>\nharness-assert-gates <gate>...\nnuget-central-version <trusted-props> <candidate-props> <metadata-json> <output-props>\nswift-semantic-equal <base> <head> <file.swift>\npost-review\nstage-review-payload optional|required <source> <destination>\nretrospective-transcript [--session-id ID] [--jsonl PATH] [--projects-dir PATH] [--codex-sessions-dir PATH] [--grok-sessions-dir PATH]\nlink-skill <name> --source-root <skills-dir> --target-root <consumer-skills-dir> Link a packaged or repository-local skill\nretrospective-facts (--pr NUMBER | --branch NAME | --no-pr) [--repo OWNER/NAME] [--raw]\nagent-blackboard probe\nagent-blackboard journal append --session-id UUID --agent NAME --version VERSION --file PATH [--parent-session-id UUID] [--timestamp ISO8601]\nagent-blackboard journal entries --session-id UUID\nagent-blackboard snapshot partition --snapshot PATH --checksum SHA256 --counts '{\"sessions\":N,\"entries\":N,\"records\":N,\"bytes\":N}'\nagent-blackboard snapshot cleanup [--snapshot PATH] [--partition-directory PATH --receipt JSON]\n";
1
+ export declare const USAGE = "Usage: vouchington <command> [options]\n\nCommands:\n runner-port-policy Print or validate a runner port policy\n with-host-lock Run a command under a host-wide lock\n agent-harness-config Apply classifier-auto + sandbox keys to agent harnesses\n gha-runtime-audit Audit successful GitHub Actions job runtimes\n require-up-to-date Require HEAD to include a fetched remote branch\n gitleaks-directory-scan Scan a directory with Gitleaks\n ast-grep-examples Run AST-grep rule examples\n ast-grep-pack Print shipped unconditional AST-grep pack paths as JSON\n gha-workspace-policy Check GitHub Actions workspace safety policy\n gha-output Write a collision-safe multiline GITHUB_OUTPUT record\n gha-needs-results Fail if required GitHub Actions job results failed\n download-with-diagnostics Download a URL and report HTTP status on failure\n download-optional-run-artifacts Download optional artifacts from the current run\n host-pressure-diagnostics Print a bounded host memory/OOM/PSI snapshot\n allocate-browser-safe-ports Allocate Fetch-safe localhost ports\n diagnose-port-collision Capture bounded localhost port diagnostics\n prepare-trivy-db Download the Trivy vulnerability database\n gha-artifacts-cleanup Delete classified GitHub Actions artifacts\n http-origin Validate an optional HTTP(S) origin\n vitest-blob-manifest Stamp a vitest-blob-manifest:v1 identity file\n vitest-report-attempt Write or read a Vitest report-attempt marker\n prepare-vitest-reports Validate and select Vitest report JSON files\n pnpm-install Install a pnpm workspace with retry and release-age fail-fast\n check-cache-size Measure a path and decide whether to save a GHA cache\n make-shard-matrix Emit a [1..N] GitHub Actions shard matrix\n load-runner-env Overlay a runner env file onto GITHUB_ENV with injection guards\n clean-workspace Reset a persistent-runner workspace with a fork-PR trust gate\n install-github-release Download a checksum-verified GitHub Release binary\n run-with-timeout Run a command with GNU timeout or a Perl fallback\n lint-links Two-pass lychee: internal links fail, external warn\n materialize-pr-context Dump PR title/body/files/diff/comments and #N crawl\n wait-for-apt-locks Wait until apt/dpkg lock files are free\n install-playwright-chromium-arm64 Install Playwright Chromium from browsers.json\n ghcr-package-retention Delete old GHCR package versions past KEEP_MIN\n harness-admission-lane Compute a GITHUB_RUN_ID admission lane for fleet fan-out\n harness-assert-gates Fail if any named HARNESS_*_ENABLED gate is enabled\n nuget-central-version Validate a Directory.Packages.props PackageVersion delta\n swift-semantic-equal Compare Swift sources ignoring comments and whitespace\n post-review Post one COMMENT review from a staged payload file\n stage-review-payload Validate a review payload file into a staging directory\n retrospective-transcript Format facts from Claude-compatible, Codex, or Grok transcripts\n link-skill Link one packaged skill into an explicit consumer directory\n retrospective-facts Gather immutable facts for a retrospective\n agent-blackboard Probe and journal an Agent Blackboard deployment\n\nOptions:\n -h, --help Show this help\n -v, --version Print the package version\n\nrunner-port-policy\n (no args) Print the shipped policy as JSON\n --file <path> Validate and print a policy file\n --reserved <port> Print true if the port is reserved\n\nwith-host-lock\n --name <family>\n [--slots <n>]\n --timeout-seconds <n>\n [--command-timeout-seconds <n>]\n [--failure-diagnostics <absolute-script>]\n [--on-acquire-timeout fail|run-unlocked]\n -- <command> [args...]\n\nagent-harness-config dump\nagent-harness-config check|apply [--global] [--repo PATH]... [--harness claude|codex|grok|cursor]...\n [--home PATH]\n\ngha-runtime-audit\n [--repository owner/name] Default GITHUB_REPOSITORY\n [--branch main]\n --pr-workflow <name|/regex/> Repeatable\n --push-workflow <name|/regex/> Repeatable\n\nrequire-up-to-date --remote <name> --branch <name>\ngitleaks-directory-scan --config <path> [--directory <path>]\nast-grep-examples --rules <directory> --config <path>\nast-grep-pack\ngha-workspace-policy [--root <directory>] [--workflow-directory <directory>] [--action-directory <directory>]\n\ngha-output <name>\ngha-needs-results [label]\ndownload-with-diagnostics <url> <destination> [-- curl-args...]\ndownload-optional-run-artifacts (--name <name> | --pattern <pattern>) --dir <directory>\nhost-pressure-diagnostics\nallocate-browser-safe-ports [count] [--policy path] [--forbidden-ports path]\ndiagnose-port-collision [--ports \"2200 2216\"] [--output-dir PATH]\nprepare-trivy-db\ngha-artifacts-cleanup run --run-id <id> [--keep-pattern glob] [--delete-pattern glob] [--patterns-file json]\ngha-artifacts-cleanup sweep --older-than-hours <n> [--keep-pattern glob] [--delete-pattern glob] [--patterns-file json]\nhttp-origin [--field NAME] [value]\nvitest-blob-manifest <suite> [reports-directory]\nvitest-report-attempt <write DIRECTORY SUITE|read ROOT>\nprepare-vitest-reports [primary-directory] [fallback-directory] [output-directory]\npnpm-install --runner-lifecycle persistent|ephemeral|ephemeral-full --install-scripts true|false\ncheck-cache-size <path> <max-bytes> <label>\nmake-shard-matrix <total>\nload-runner-env\nclean-workspace\ninstall-github-release --repo owner/name --version X --asset 'name-{platform}.tar.gz' --bin name [--tag-prefix PREFIX] [--expected-sha256 SHA256] [--no-checksum] [--checksums-asset NAME] [--version-flag FLAG] [--bin-dir DIR]\nrun-with-timeout <timeout-seconds> <kill-after-seconds> <command...>\nlint-links [--offline] [--config PATH] [--glob PATTERN] [files...]\nmaterialize-pr-context\nwait-for-apt-locks\ninstall-playwright-chromium-arm64 [name:archive...]\nghcr-package-retention <url-encoded-package>...\nharness-admission-lane <lanes>\nharness-assert-gates <gate>...\nnuget-central-version <trusted-props> <candidate-props> <metadata-json> <output-props>\nswift-semantic-equal <base> <head> <file.swift>\npost-review\nstage-review-payload optional|required <source> <destination>\nretrospective-transcript [--session-id ID] [--jsonl PATH] [--projects-dir PATH] [--codex-sessions-dir PATH] [--grok-sessions-dir PATH]\nlink-skill <name> --source-root <skills-dir> --target-root <consumer-skills-dir> Link a packaged or repository-local skill\nretrospective-facts (--pr NUMBER | --branch NAME | --no-pr) [--repo OWNER/NAME] [--raw]\nagent-blackboard probe\nagent-blackboard journal append --session-id UUID --agent NAME --version VERSION --file PATH [--parent-session-id UUID] [--timestamp ISO8601]\nagent-blackboard journal entries --session-id UUID\nagent-blackboard snapshot partition --snapshot PATH --checksum SHA256 --counts '{\"sessions\":N,\"entries\":N,\"records\":N,\"bytes\":N}'\nagent-blackboard snapshot cleanup [--snapshot PATH] [--partition-directory PATH --receipt JSON]\n";
2
2
  export declare function printUsage(stream?: NodeJS.WritableStream): void;
@@ -3,6 +3,7 @@ export const USAGE = `Usage: vouchington <command> [options]
3
3
  Commands:
4
4
  runner-port-policy Print or validate a runner port policy
5
5
  with-host-lock Run a command under a host-wide lock
6
+ agent-harness-config Apply classifier-auto + sandbox keys to agent harnesses
6
7
  gha-runtime-audit Audit successful GitHub Actions job runtimes
7
8
  require-up-to-date Require HEAD to include a fetched remote branch
8
9
  gitleaks-directory-scan Scan a directory with Gitleaks
@@ -63,6 +64,10 @@ with-host-lock
63
64
  [--on-acquire-timeout fail|run-unlocked]
64
65
  -- <command> [args...]
65
66
 
67
+ agent-harness-config dump
68
+ agent-harness-config check|apply [--global] [--repo PATH]... [--harness claude|codex|grok|cursor]...
69
+ [--home PATH]
70
+
66
71
  gha-runtime-audit
67
72
  [--repository owner/name] Default GITHUB_REPOSITORY
68
73
  [--branch main]
@@ -16,6 +16,7 @@ export function parseArtifactPatternsJson(value) {
16
16
  if (value === null || typeof value !== 'object' || Array.isArray(value)) {
17
17
  throw new Error('patterns file must be a JSON object with keep and delete arrays');
18
18
  }
19
+ // oxlint-disable-next-line no-mistakes/ts-no-const-aliases -- establish a record view after validating the untrusted patterns object
19
20
  const record = value;
20
21
  return {
21
22
  keepPatterns: stringArray(record.keep, 'keep'),
@@ -23,6 +23,7 @@ export function oidcTokenRequest(env) {
23
23
  function readToken(payload) {
24
24
  if (payload === null || typeof payload !== 'object')
25
25
  return '';
26
+ // oxlint-disable-next-line no-mistakes/ts-no-const-aliases -- establish a record view after validating the untrusted response object
26
27
  const record = payload;
27
28
  const token = record.token ?? record.app_token ?? record.value;
28
29
  return typeof token === 'string' ? token : '';
@@ -30,6 +31,7 @@ function readToken(payload) {
30
31
  function readErrorMessage(payload) {
31
32
  if (payload === null || typeof payload !== 'object')
32
33
  return '';
34
+ // oxlint-disable-next-line no-mistakes/ts-no-const-aliases -- establish a record view after validating the untrusted response object
33
35
  const record = payload;
34
36
  if (typeof record.message === 'string' && record.message.length > 0)
35
37
  return record.message;
@@ -22,6 +22,7 @@ export function postWithGh(repository, prNumber, payload, token, exec) {
22
22
  return { ok: true, status: 201, body: '' };
23
23
  }
24
24
  catch (error) {
25
+ // oxlint-disable-next-line no-mistakes/ts-no-const-aliases -- isolate the process-error shape assertion at the catch boundary
25
26
  const err = error;
26
27
  const text = `${err.stdout ?? ''}${err.stderr ?? ''}${err.message ?? ''}`;
27
28
  const status = Number(/HTTP\s+(\d{3})/u.exec(text)?.[1] ?? 0);
@@ -1,5 +1,11 @@
1
1
  import { ReviewPayloadError, indexReviewFiles, parseReviewPayload, remapReviewComments, } from '../gha-review-payload/index.mjs';
2
- export { MAX_REVIEW_COMMENTS as MAX_COMMENTS, MAX_REVIEW_PAYLOAD_BYTES as MAX_PAYLOAD_BYTES, ReviewPayloadError as PostReviewError, } from '../gha-review-payload/index.mjs';
2
+ export {
3
+ // oxlint-disable-next-line no-mistakes/ts-no-export-renaming -- preserve the package's established public compatibility name
4
+ MAX_REVIEW_COMMENTS as MAX_COMMENTS,
5
+ // oxlint-disable-next-line no-mistakes/ts-no-export-renaming -- preserve the package's established public compatibility name
6
+ MAX_REVIEW_PAYLOAD_BYTES as MAX_PAYLOAD_BYTES,
7
+ // oxlint-disable-next-line no-mistakes/ts-no-export-renaming -- preserve the package's established public compatibility name
8
+ ReviewPayloadError as PostReviewError, } from '../gha-review-payload/index.mjs';
3
9
  export function runPostReview(payloadPath, io) {
4
10
  try {
5
11
  const selectedHeadSha = io.getHeadSha();
@@ -38,6 +38,7 @@ export function parseCheckpoint(body, options = {}) {
38
38
  export function validateCheckpoint(value, options = {}) {
39
39
  if (!value || typeof value !== 'object')
40
40
  return undefined;
41
+ // oxlint-disable-next-line no-mistakes/ts-no-const-aliases -- establish the candidate checkpoint type before complete field validation
41
42
  const checkpoint = value;
42
43
  const marker = options.marker ?? CHECKPOINT_MARKER;
43
44
  if (checkpoint.marker !== marker ||
@@ -38,6 +38,7 @@ function asReviewFiles(parsed) {
38
38
  return items.flatMap((item) => {
39
39
  if (item === null || typeof item !== 'object')
40
40
  return [];
41
+ // oxlint-disable-next-line no-mistakes/ts-no-const-aliases -- establish a record view after validating the untrusted file entry
41
42
  const record = item;
42
43
  if (typeof record.filename !== 'string' || record.filename.length === 0)
43
44
  return [];
@@ -17,6 +17,7 @@ function isCommitId(value) {
17
17
  function sanitizeComment(raw) {
18
18
  if (raw === null || typeof raw !== 'object' || Array.isArray(raw))
19
19
  return null;
20
+ // oxlint-disable-next-line no-mistakes/ts-no-const-aliases -- establish a record view after validating the untrusted comment object
20
21
  const record = raw;
21
22
  if (typeof record.path !== 'string' || record.path.length === 0 || record.path.includes('\0')) {
22
23
  return null;
@@ -68,6 +69,7 @@ export function parseReviewPayload(bytes, commitId) {
68
69
  if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
69
70
  throw new ReviewPayloadError('Payload must be a JSON object.');
70
71
  }
72
+ // oxlint-disable-next-line no-mistakes/ts-no-const-aliases -- establish a record view after validating the untrusted payload object
71
73
  const record = parsed;
72
74
  const body = typeof record.body === 'string' ? record.body : '';
73
75
  if (record.comments !== undefined && !Array.isArray(record.comments)) {
@@ -38,6 +38,7 @@ export function visitRunSteps(document, isAction, visitor) {
38
38
  for (const [jobId, value] of Object.entries(jobs)) {
39
39
  if (!value || typeof value !== 'object')
40
40
  continue;
41
+ // oxlint-disable-next-line no-mistakes/ts-no-const-aliases -- establish a job record view after the runtime object check
41
42
  const job = value;
42
43
  if (typeof job.uses === 'string')
43
44
  continue;
@@ -117,6 +117,7 @@ function skipWhitespace(value, index) {
117
117
  return index;
118
118
  }
119
119
  function readToken(value, index) {
120
+ // oxlint-disable-next-line no-mistakes/ts-no-const-aliases -- preserve the token start while advancing the cursor
120
121
  const start = index;
121
122
  while (index < value.length && isTokenCharacter(value[index]))
122
123
  index += 1;
package/dist/index.d.mts CHANGED
@@ -13,6 +13,8 @@ export type { EphemeralListenerOptions, RunnerPortPolicy } from './runner-port-p
13
13
  export { extractAlterTableAddColumnLocations, extractCreateIndexMetadata, extractCreateTableMetadata, extractDefaultFunction, extractDropIndexMetadata, extractFuncCallArgColumnNames, extractMigrationConstraintMetadata, initSqlAst, lineOfUtf8ByteOffset, MissingSqlAstParserError, parseSql, } from './sql-ast/index.mts';
14
14
  export type { ForeignKey, SqlCreateIndexMetadata, SqlCreateTableColumn, SqlCreateTableMetadata, SqlDropIndexMetadata, SqlIndexParam, SqlMigrationConstraintMetadata, } from './sql-ast/index.mts';
15
15
  export { dollarQuoteEnd, lineOf, maskSqlQuotedText, readDollarQuoteDelimiter, readStringLiteral, splitSqlStatements, sqlFragments, stripSqlComments, } from './sql-scanner/index.mts';
16
+ export { applyHarnessConfig, checkHarnessConfig, dumpHarnessPolicy, planHarnessConfig, DEFAULT_EXTRA_WRITABLE_ROOTS, HARNESS_IDS, } from './agent-harness-config/index.mts';
17
+ export type { ApplyTarget, FilePlan, FileResult, HarnessApplyResult, HarnessCheckResult, HarnessConfigOptions, HarnessId, HarnessPlan, HarnessPolicyDump, JsonPatch, KeyDrift, TomlPatch, TomlValue, } from './agent-harness-config/index.mts';
16
18
  export { auditCiJobRuntime, parseWorkflowNameMatch } from './gha-runtime-audit/index.mts';
17
19
  export type { GhApiExecutor, RuntimeAuditOptions, RuntimeAuditResult, RuntimeAuditWorkflowFilter, RuntimeJobResult, RuntimeSample, } from './gha-runtime-audit/index.mts';
18
20
  export { checkGhaWorkspacePolicy } from './gha-workspace-policy/index.mts';
package/dist/index.mjs CHANGED
@@ -7,6 +7,7 @@ export { buildSessionFrictionReport, classifyFrictionObservation, FRICTION_LOG_M
7
7
  export { EphemeralListenerAttemptsExhaustedError, isRunnerReservedPort, listenOnRunnerUnreservedEphemeralPort, loadRunnerPortPolicy, runnerPortPolicy, validateRunnerPortPolicy, } from './runner-port-policy/index.mjs';
8
8
  export { extractAlterTableAddColumnLocations, extractCreateIndexMetadata, extractCreateTableMetadata, extractDefaultFunction, extractDropIndexMetadata, extractFuncCallArgColumnNames, extractMigrationConstraintMetadata, initSqlAst, lineOfUtf8ByteOffset, MissingSqlAstParserError, parseSql, } from './sql-ast/index.mjs';
9
9
  export { dollarQuoteEnd, lineOf, maskSqlQuotedText, readDollarQuoteDelimiter, readStringLiteral, splitSqlStatements, sqlFragments, stripSqlComments, } from './sql-scanner/index.mjs';
10
+ export { applyHarnessConfig, checkHarnessConfig, dumpHarnessPolicy, planHarnessConfig, DEFAULT_EXTRA_WRITABLE_ROOTS, HARNESS_IDS, } from './agent-harness-config/index.mjs';
10
11
  export { auditCiJobRuntime, parseWorkflowNameMatch } from './gha-runtime-audit/index.mjs';
11
12
  export { checkGhaWorkspacePolicy } from './gha-workspace-policy/index.mjs';
12
13
  export { requireUpToDate } from './require-up-to-date/index.mjs';
@@ -18,6 +18,7 @@ function pinPrunedAt(prodDir) {
18
18
  if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
19
19
  throw new Error(`${modulesPath} is not a JSON object`);
20
20
  }
21
+ // oxlint-disable-next-line no-mistakes/ts-no-const-aliases -- establish the mutable manifest shape after validating the parsed object
21
22
  const manifest = parsed;
22
23
  manifest.prunedAt = EPOCH_PRUNED_AT;
23
24
  fs.writeFileSync(modulesPath, `${JSON.stringify(manifest, null, 2)}\n`);
@@ -1,5 +1,6 @@
1
1
  import { type CommandResult, type InstallOptions } from './support.mts';
2
2
  export declare function withScriptPolicy(args: string[], installScripts: boolean): string[];
3
3
  export declare function install(args: string[], options: InstallOptions, label: string): Promise<void>;
4
- export declare function reconcileOrFail(options: InstallOptions, runCapture: (args: string[]) => Promise<CommandResult>): Promise<void>;
4
+ export declare function reconcileOrFail(options: InstallOptions): Promise<void>;
5
+ export declare function finalizePendingBuilds(options: InstallOptions, runCapture: (args: string[]) => Promise<CommandResult>, phase: string): Promise<import("./pending-builds.mts").PendingBuildState>;
5
6
  export declare function repairIsolatedNativeMismatch(options: InstallOptions, runCapture: (args: string[]) => Promise<CommandResult>, mismatchedNativePaths: string[]): Promise<boolean>;
@@ -1,7 +1,7 @@
1
1
  import { scheduler } from 'node:timers/promises';
2
2
  import { runPnpm } from './exec.mjs';
3
3
  import { nativeBinariesMatchRuntime, repairedNativeBinariesMatchRuntime } from './native-health.mjs';
4
- import { buildLedgersAllowNativeRepair } from './pending-builds.mjs';
4
+ import { buildLedgersAllowNativeRepair, pendingBuilds } from './pending-builds.mjs';
5
5
  import { INSTALL_TERMINATION_FAILED } from './process.mjs';
6
6
  import { formatReleaseAgeFailure, isReleaseAgeViolation } from './release-age.mjs';
7
7
  import { findWorkspaceLinkMismatches, forcedInstallArgs, logWorkspaceLinkMismatches, } from './support.mjs';
@@ -28,10 +28,9 @@ export async function install(args, options, label) {
28
28
  }
29
29
  fail(`${label} failed after ${options.maxAttempts} attempt${options.maxAttempts === 1 ? '' : 's'}`);
30
30
  }
31
- export async function reconcileOrFail(options, runCapture) {
31
+ export async function reconcileOrFail(options) {
32
32
  await install([...forcedInstallArgs, '--ignore-scripts', '--ignore-pnpmfile'], options, 'script-free reconciliation');
33
- await install(withScriptPolicy(forcedInstallArgs, options.installScripts), options, 'strict persistent reconciliation');
34
- await verifyInstallHealth(runCapture, 'persistent reconciliation');
33
+ await install([...forcedInstallArgs, '--ignore-scripts'], options, 'second script-free persistent reconciliation');
35
34
  }
36
35
  async function verifyInstallHealth(runCapture, phase, repairedNativePaths = []) {
37
36
  const [nativesMatch, remaining] = await Promise.all([
@@ -47,6 +46,19 @@ async function verifyInstallHealth(runCapture, phase, repairedNativePaths = [])
47
46
  fail(`${phase} completed with invalid workspace links`);
48
47
  }
49
48
  }
49
+ export async function finalizePendingBuilds(options, runCapture, phase) {
50
+ const before = await pendingBuilds();
51
+ if (options.installScripts && before.kind === 'unknown')
52
+ fail(`${phase} completed without a clear pending build ledger`);
53
+ if (options.installScripts && before.kind === 'pending') {
54
+ await install(['rebuild', '--pending', '--recursive'], options, 'pending scripts rebuild');
55
+ const after = await pendingBuilds();
56
+ if (after.kind !== 'clear')
57
+ fail(`${phase} completed without a clear pending build ledger`);
58
+ }
59
+ await verifyInstallHealth(runCapture, phase);
60
+ return options.installScripts ? { kind: 'clear' } : before;
61
+ }
50
62
  export async function repairIsolatedNativeMismatch(options, runCapture, mismatchedNativePaths) {
51
63
  if (!(await buildLedgersAllowNativeRepair()))
52
64
  return false;
@@ -3,7 +3,7 @@ import { type ProvenanceStatus } from './transition.mts';
3
3
  declare const componentNames: readonly ['lockfile', 'npm-config', 'pnpm', 'pnpmfiles', 'runtime', 'workspace-config', 'workspace-manifests'];
4
4
  type ComponentName = (typeof componentNames)[number];
5
5
  type StructuralProvenance = Record<ComponentName, string>;
6
- export declare function persistentMetadataFingerprintV4(runCapture: CaptureCommand): Promise<{
6
+ export declare function persistentMetadataFingerprintV5(runCapture: CaptureCommand): Promise<{
7
7
  lockfile: string;
8
8
  'npm-config': string;
9
9
  pnpm: string;
@@ -12,7 +12,7 @@ export declare function persistentMetadataFingerprintV4(runCapture: CaptureComma
12
12
  'workspace-config': string;
13
13
  'workspace-manifests': string;
14
14
  }>;
15
- export declare function persistentMetadataStatusV4(provenance: StructuralProvenance): Promise<ProvenanceStatus>;
16
- export declare function writePersistentMetadataStampV4(provenance: StructuralProvenance, installScripts: boolean, resetScriptsEnabledCapability: boolean, pendingDependencyBuilds?: string[]): Promise<void>;
15
+ export declare function persistentMetadataStatusV5(provenance: StructuralProvenance): Promise<ProvenanceStatus>;
16
+ export declare function writePersistentMetadataStampV5(provenance: StructuralProvenance, installScripts: boolean, scriptsEnabledInstallVerified: boolean): Promise<void>;
17
17
  export declare function persistentDependencyTreeIsCold(): Promise<boolean>;
18
18
  export {};