vouchington-tooling 0.1.8 → 0.2.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.
Files changed (64) hide show
  1. package/README.md +22 -5
  2. package/dist/cli/commands/link-skill.d.mts +5 -0
  3. package/dist/cli/commands/link-skill.mjs +6 -0
  4. package/dist/cli/index.mjs +3 -0
  5. package/dist/cli/parse.d.mts +5 -0
  6. package/dist/cli/parse.mjs +25 -0
  7. package/dist/cli/usage.d.mts +1 -1
  8. package/dist/cli/usage.mjs +2 -0
  9. package/dist/gha-post-review/github.d.mts +2 -0
  10. package/dist/gha-post-review/github.mjs +47 -5
  11. package/dist/gha-post-review/post.mjs +6 -1
  12. package/dist/index.d.mts +2 -0
  13. package/dist/index.mjs +1 -0
  14. package/dist/pnpm-install/index.d.mts +2 -1
  15. package/dist/pnpm-install/index.mjs +2 -1
  16. package/dist/pnpm-install/install-operations.d.mts +4 -0
  17. package/dist/pnpm-install/install-operations.mjs +38 -0
  18. package/dist/pnpm-install/metadata-legacy.d.mts +4 -0
  19. package/dist/pnpm-install/metadata-legacy.mjs +81 -0
  20. package/dist/pnpm-install/metadata.d.mts +16 -3
  21. package/dist/pnpm-install/metadata.mjs +103 -36
  22. package/dist/pnpm-install/pending-builds.d.mts +17 -0
  23. package/dist/pnpm-install/pending-builds.mjs +87 -0
  24. package/dist/pnpm-install/pnpm-install-fake-pnpm.test-helpers.d.mts +1 -0
  25. package/dist/pnpm-install/pnpm-install-fake-pnpm.test-helpers.mjs +52 -0
  26. package/dist/pnpm-install/pnpm-install-fixture.test-helpers.mjs +6 -40
  27. package/dist/pnpm-install/runner.mjs +72 -56
  28. package/dist/pnpm-install/transition.d.mts +23 -0
  29. package/dist/pnpm-install/transition.mjs +36 -0
  30. package/dist/skill-discovery/index.d.mts +12 -0
  31. package/dist/skill-discovery/index.mjs +64 -0
  32. package/dist/skill-discovery/manifest.d.mts +15 -0
  33. package/dist/skill-discovery/manifest.mjs +85 -0
  34. package/dist/skill-discovery/target-directory.d.mts +10 -0
  35. package/dist/skill-discovery/target-directory.mjs +126 -0
  36. package/package.json +6 -1
  37. package/scripts/build.mjs +16 -4
  38. package/skills/agent-workflow/SKILL.md +5 -0
  39. package/skills/agent-workflow/references/evidence-sweep.md +8 -0
  40. package/skills/agent-workflow/references/implementation-and-review.md +9 -0
  41. package/skills/agent-workflow/references/implementation.md +8 -0
  42. package/skills/agent-workflow/references/review.md +7 -0
  43. package/skills/backend-vitest-test-authoring/SKILL.md +15 -0
  44. package/skills/backend-vitest-test-authoring/references/integration-boundaries.md +10 -0
  45. package/skills/dotnet-test-authoring/SKILL.md +16 -0
  46. package/skills/manifest.json +158 -0
  47. package/skills/nextjs-vitest-test-authoring/SKILL.md +15 -0
  48. package/skills/nextjs-vitest-test-authoring/references/framework-boundaries.md +9 -0
  49. package/skills/planning/SKILL.md +3 -0
  50. package/skills/planning/references/impact-discovery.md +9 -0
  51. package/skills/playwright-authoring/SKILL.md +15 -0
  52. package/skills/playwright-authoring/references/browser-reliability.md +9 -0
  53. package/skills/postgres-node-performance-tuning/SKILL.md +15 -0
  54. package/skills/postgres-node-performance-tuning/references/performance-patterns.md +14 -0
  55. package/skills/postgres-partitioning-uuid-v7/SKILL.md +16 -0
  56. package/skills/postgres-partitioning-uuid-v7/references/partition-lifecycle.md +14 -0
  57. package/skills/storybook-authoring/SKILL.md +15 -0
  58. package/skills/storybook-authoring/references/component-coverage.md +9 -0
  59. package/skills/swift-test-authoring/SKILL.md +15 -0
  60. package/skills/swift-test-authoring/references/network-test-doubles.md +9 -0
  61. package/skills/test-authoring/SKILL.md +17 -0
  62. package/skills/test-authoring/references/core-practice.md +10 -0
  63. package/skills/vitest-test-authoring/SKILL.md +15 -0
  64. package/skills/vitest-test-authoring/references/mock-boundaries.md +9 -0
@@ -2,15 +2,28 @@ import { createHash } from 'node:crypto';
2
2
  import { mkdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';
3
3
  import path from 'node:path';
4
4
  import { listWorkspaces, reportGlibcVersionRuntime } from './support.mjs';
5
+ import {} from './transition.mjs';
6
+ const componentNames = [
7
+ 'lockfile',
8
+ 'npm-config',
9
+ 'pnpm',
10
+ 'pnpmfiles',
11
+ 'runtime',
12
+ 'workspace-config',
13
+ 'workspace-manifests',
14
+ ];
5
15
  function persistentMetadataStampPath() {
6
16
  return path.join(process.cwd(), 'node_modules', '.pnpm-install-metadata-health.json');
7
17
  }
8
- function fail(message) {
9
- throw new Error(message);
10
- }
11
18
  function addFingerprintInput(hash, label, value) {
12
19
  hash.update(`${label.length}:${label}${value.length}:${value}`);
13
20
  }
21
+ function componentHash(inputs) {
22
+ const hash = createHash('sha256');
23
+ for (const [label, value] of inputs)
24
+ addFingerprintInput(hash, label, value);
25
+ return hash.digest('hex');
26
+ }
14
27
  async function optionalFile(pathname) {
15
28
  try {
16
29
  return await readFile(pathname, 'utf8');
@@ -34,58 +47,112 @@ function runtimePlatformIdentity() {
34
47
  platform: process.platform,
35
48
  });
36
49
  }
37
- export async function persistentMetadataFingerprint(runCapture, installScripts) {
50
+ export async function persistentMetadataFingerprintV4(runCapture) {
38
51
  const workspaces = (await listWorkspaces(runCapture)).toSorted((left, right) => left.path.localeCompare(right.path));
39
52
  const pnpmVersion = await runCapture(['--version']);
40
53
  if (pnpmVersion.code !== 0)
41
- fail(`pnpm --version failed: ${pnpmVersion.errorOutput?.trim() || pnpmVersion.output.trim() || 'unknown error'}`);
42
- const hash = createHash('sha256');
43
- addFingerprintInput(hash, 'runtime', runtimePlatformIdentity());
44
- addFingerprintInput(hash, 'pnpm', pnpmVersion.output.trim());
45
- addFingerprintInput(hash, 'installScripts', String(installScripts));
46
- for (const filename of [
47
- 'pnpm-lock.yaml',
48
- 'pnpm-workspace.yaml',
49
- '.npmrc',
50
- '.pnpmfile.cjs',
51
- '.pnpmfile.mjs',
52
- ])
53
- addFingerprintInput(hash, filename, await optionalFile(path.join(process.cwd(), filename)));
54
- for (const workspace of workspaces)
55
- addFingerprintInput(hash, path.relative(process.cwd(), path.join(workspace.path, 'package.json')), await readFile(path.join(workspace.path, 'package.json'), 'utf8'));
56
- return hash.digest('hex');
54
+ throw new Error(`pnpm --version failed: ${pnpmVersion.errorOutput?.trim() || pnpmVersion.output.trim() || 'unknown error'}`);
55
+ const files = new Map(await Promise.all(['pnpm-lock.yaml', 'pnpm-workspace.yaml', '.npmrc', '.pnpmfile.cjs', '.pnpmfile.mjs'].map(async (filename) => [filename, await optionalFile(path.join(process.cwd(), filename))])));
56
+ const manifests = await Promise.all(workspaces.map(async (workspace) => {
57
+ const filename = path.relative(process.cwd(), path.join(workspace.path, 'package.json'));
58
+ return [filename, await readFile(path.join(workspace.path, 'package.json'), 'utf8')];
59
+ }));
60
+ return {
61
+ lockfile: componentHash([['pnpm-lock.yaml', files.get('pnpm-lock.yaml')]]),
62
+ 'npm-config': componentHash([['.npmrc', files.get('.npmrc')]]),
63
+ pnpm: componentHash([['version', pnpmVersion.output.trim()]]),
64
+ pnpmfiles: componentHash([
65
+ ['.pnpmfile.cjs', files.get('.pnpmfile.cjs')],
66
+ ['.pnpmfile.mjs', files.get('.pnpmfile.mjs')],
67
+ ]),
68
+ runtime: componentHash([['identity', runtimePlatformIdentity()]]),
69
+ 'workspace-config': componentHash([['pnpm-workspace.yaml', files.get('pnpm-workspace.yaml')]]),
70
+ 'workspace-manifests': componentHash(manifests),
71
+ };
57
72
  }
58
- export async function persistentMetadataMatches(fingerprint) {
59
- try {
60
- const stamp = JSON.parse(await readFile(persistentMetadataStampPath(), 'utf8'));
61
- return stamp.version === 3 && stamp.fingerprint === fingerprint;
62
- }
63
- catch {
73
+ function validStamp(value) {
74
+ if (typeof value !== 'object' || value === null)
64
75
  return false;
65
- }
76
+ const stamp = value;
77
+ return (stamp.version === 4 &&
78
+ typeof stamp.lastInvocationInstallScripts === 'boolean' &&
79
+ typeof stamp.scriptsEnabledInstallSucceeded === 'boolean' &&
80
+ typeof stamp.provenance === 'object' &&
81
+ stamp.provenance !== null &&
82
+ componentNames.every((name) => typeof stamp.provenance?.[name] === 'string') &&
83
+ (stamp.pendingDependencyBuilds === undefined ||
84
+ (Array.isArray(stamp.pendingDependencyBuilds) &&
85
+ stamp.pendingDependencyBuilds.every((id) => typeof id === 'string'))));
66
86
  }
67
- export async function persistentDependencyTreeIsCold() {
87
+ async function readPersistentMetadataState() {
68
88
  try {
69
- await stat(path.dirname(persistentMetadataStampPath()));
70
- return false;
89
+ const parsed = JSON.parse(await readFile(persistentMetadataStampPath(), 'utf8'));
90
+ return validStamp(parsed)
91
+ ? { kind: 'stamp', stamp: parsed }
92
+ : { kind: 'unsafe' };
71
93
  }
72
94
  catch (error) {
73
- if (error.code === 'ENOENT')
74
- return true;
75
- /* v8 ignore next -- non-ENOENT stat failures are host-specific */
76
- throw error;
95
+ return error.code === 'ENOENT'
96
+ ? { kind: 'missing' }
97
+ : { kind: 'unsafe' };
77
98
  }
78
99
  }
79
- export async function writePersistentMetadataStamp(fingerprint) {
100
+ async function readPersistentMetadataStamp() {
101
+ const state = await readPersistentMetadataState();
102
+ return state.kind === 'stamp' ? state.stamp : undefined;
103
+ }
104
+ export async function persistentMetadataStatusV4(provenance) {
105
+ const state = await readPersistentMetadataState();
106
+ if (state.kind === 'missing')
107
+ return { kind: 'absent' };
108
+ if (state.kind === 'unsafe')
109
+ return { kind: 'unsafe' };
110
+ const { stamp } = state;
111
+ const changed = componentNames.filter((name) => stamp.provenance[name] !== provenance[name]);
112
+ return changed.length > 0
113
+ ? { kind: 'changed', components: changed }
114
+ : {
115
+ kind: 'matching',
116
+ lastInvocationInstallScripts: stamp.lastInvocationInstallScripts,
117
+ pendingDependencyBuilds: stamp.pendingDependencyBuilds ?? [],
118
+ scriptsEnabledInstallSucceeded: stamp.scriptsEnabledInstallSucceeded,
119
+ };
120
+ }
121
+ export async function writePersistentMetadataStampV4(provenance, installScripts, resetScriptsEnabledCapability, pendingDependencyBuilds) {
122
+ const existing = await readPersistentMetadataStamp();
123
+ const existingMatches = existing && componentNames.every((name) => existing.provenance[name] === provenance[name]);
124
+ const stamp = {
125
+ lastInvocationInstallScripts: installScripts,
126
+ pendingDependencyBuilds: (pendingDependencyBuilds ??
127
+ (existingMatches ? existing.pendingDependencyBuilds : undefined) ??
128
+ []).toSorted(),
129
+ provenance,
130
+ scriptsEnabledInstallSucceeded: installScripts ||
131
+ (!resetScriptsEnabledCapability &&
132
+ Boolean(existingMatches && existing.scriptsEnabledInstallSucceeded)),
133
+ version: 4,
134
+ };
80
135
  const stampPath = persistentMetadataStampPath();
81
136
  const directory = path.dirname(stampPath);
82
137
  const temporary = `${stampPath}.${process.pid}.tmp`;
83
138
  await mkdir(directory, { recursive: true });
84
139
  try {
85
- await writeFile(temporary, `${JSON.stringify({ fingerprint, version: 3 })}\n`);
140
+ await writeFile(temporary, `${JSON.stringify(stamp)}\n`);
86
141
  await rename(temporary, stampPath);
87
142
  }
88
143
  finally {
89
144
  await rm(temporary, { force: true });
90
145
  }
91
146
  }
147
+ export async function persistentDependencyTreeIsCold() {
148
+ try {
149
+ await stat(path.dirname(persistentMetadataStampPath()));
150
+ return false;
151
+ }
152
+ catch (error) {
153
+ if (error.code === 'ENOENT')
154
+ return true;
155
+ /* v8 ignore next -- non-ENOENT stat failures are host-specific */
156
+ throw error;
157
+ }
158
+ }
@@ -0,0 +1,17 @@
1
+ export type PendingBuilds = {
2
+ kind: 'known';
3
+ ids: Set<string>;
4
+ } | {
5
+ kind: 'unknown';
6
+ };
7
+ export type PendingBuildDelta = {
8
+ kind: 'unknown';
9
+ } | {
10
+ kind: 'known';
11
+ dependencyIds: string[];
12
+ workspaceIds: string[];
13
+ };
14
+ export declare function pendingBuilds(): Promise<PendingBuilds>;
15
+ export declare function validDependencyBuildIds(ids: [string, ...string[]]): Promise<[string, ...string[]] | undefined>;
16
+ export declare function clearPendingDependencyBuilds(ids: string[]): Promise<boolean>;
17
+ export declare function pendingBuildDelta(before: PendingBuilds, after: PendingBuilds): Promise<PendingBuildDelta>;
@@ -0,0 +1,87 @@
1
+ import { readFile, rename, rm, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { parse, stringify } from 'yaml';
4
+ export async function pendingBuilds() {
5
+ try {
6
+ const value = parse(await readFile(path.join(process.cwd(), 'node_modules', '.modules.yaml'), 'utf8'));
7
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
8
+ return { kind: 'unknown' };
9
+ const pending = value.pendingBuilds ?? [];
10
+ if (!Array.isArray(pending) || !pending.every((id) => typeof id === 'string'))
11
+ return { kind: 'unknown' };
12
+ return { kind: 'known', ids: new Set(pending) };
13
+ }
14
+ catch {
15
+ return { kind: 'unknown' };
16
+ }
17
+ }
18
+ async function lockfileDependencyIds() {
19
+ try {
20
+ const lockfile = parse(await readFile(path.join(process.cwd(), 'pnpm-lock.yaml'), 'utf8'));
21
+ if (typeof lockfile !== 'object' || lockfile === null || !('packages' in lockfile))
22
+ return undefined;
23
+ const packages = lockfile.packages;
24
+ if (typeof packages !== 'object' || packages === null || Array.isArray(packages))
25
+ return undefined;
26
+ return new Set(Object.keys(packages));
27
+ }
28
+ catch {
29
+ return undefined;
30
+ }
31
+ }
32
+ export async function validDependencyBuildIds(ids) {
33
+ const packages = await lockfileDependencyIds();
34
+ if (!packages || !ids.every((id) => packages.has(id)))
35
+ return undefined;
36
+ const [first, ...remaining] = ids.toSorted();
37
+ return [first, ...remaining];
38
+ }
39
+ export async function clearPendingDependencyBuilds(ids) {
40
+ const filename = path.join(process.cwd(), 'node_modules', '.modules.yaml');
41
+ try {
42
+ const value = parse(await readFile(filename, 'utf8'));
43
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
44
+ return false;
45
+ const record = value;
46
+ if (!Array.isArray(record.pendingBuilds) ||
47
+ !record.pendingBuilds.every((id) => typeof id === 'string'))
48
+ return false;
49
+ const ignored = record.ignoredBuilds ?? [];
50
+ if (!Array.isArray(ignored) ||
51
+ !ignored.every((id) => typeof id === 'string') ||
52
+ ids.some((id) => ignored.includes(id)))
53
+ return false;
54
+ record.pendingBuilds = record.pendingBuilds.filter((id) => !ids.includes(id));
55
+ const temporary = `${filename}.${process.pid}.tmp`;
56
+ try {
57
+ await writeFile(temporary, stringify(record));
58
+ await rename(temporary, filename);
59
+ }
60
+ finally {
61
+ await rm(temporary, { force: true });
62
+ }
63
+ return true;
64
+ }
65
+ catch {
66
+ return false;
67
+ }
68
+ }
69
+ export async function pendingBuildDelta(before, after) {
70
+ if (before.kind !== 'known' || after.kind !== 'known')
71
+ return { kind: 'unknown' };
72
+ const packages = await lockfileDependencyIds();
73
+ if (!packages)
74
+ return { kind: 'unknown' };
75
+ const dependencyIds = [];
76
+ const workspaceIds = [];
77
+ for (const id of after.ids) {
78
+ if (before.ids.has(id))
79
+ continue;
80
+ (packages.has(id) ? dependencyIds : workspaceIds).push(id);
81
+ }
82
+ return {
83
+ kind: 'known',
84
+ dependencyIds: dependencyIds.toSorted(),
85
+ workspaceIds: workspaceIds.toSorted(),
86
+ };
87
+ }
@@ -0,0 +1 @@
1
+ export declare function fakePnpmScript(): string;
@@ -0,0 +1,52 @@
1
+ export function fakePnpmScript() {
2
+ return `#!/usr/bin/env bash
3
+ set -euo pipefail
4
+ if [ "\${1:-}" = m ]; then
5
+ if [ -n "\${PNPM_LIST_WARNING:-}" ]; then printf '%s\\n' "$PNPM_LIST_WARNING" >&2; fi
6
+ printf '%s\\n' "$PNPM_WORKSPACES_JSON"
7
+ exit 0
8
+ fi
9
+ if [ "\${1:-}" = --version ]; then
10
+ printf '%s\\n' "\${PNPM_VERSION:-11.0.0}"
11
+ exit 0
12
+ fi
13
+ printf '%s\\n' "$*" >> "$PNPM_LOG"
14
+ calls=0
15
+ if [ -f "$PNPM_CALLS" ]; then calls="$(cat "$PNPM_CALLS")"; fi
16
+ calls=$((calls + 1))
17
+ printf '%s' "$calls" > "$PNPM_CALLS"
18
+ print_release_age_violation() {
19
+ printf '%s\\n' '✗ Lockfile failed supply-chain policy check (1 entries in 0.1s)'
20
+ printf '%s\\n' '[ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION] 1 lockfile entries failed verification:'
21
+ printf '%s\\n' ' undici@8.10.0 was published at 2026-08-03T15:06:33.000Z, within the minimumReleaseAge cutoff (2026-08-02T04:48:10.357Z)'
22
+ }
23
+ if [ "\${PNPM_FAIL_CALL:-0}" = "$calls" ]; then
24
+ if [ -n "\${PNPM_FAIL_RELEASE_AGE:-}" ]; then print_release_age_violation; fi
25
+ exit "\${PNPM_FAIL_CODE:-1}"
26
+ fi
27
+ if [ "\${PNPM_FAIL_RELEASE_AGE_CALL:-0}" = "$calls" ]; then
28
+ print_release_age_violation
29
+ exit 1
30
+ fi
31
+ if [ -n "\${PNPM_SLEEP_SECONDS:-}" ]; then sleep "$PNPM_SLEEP_SECONDS"; fi
32
+ if [ -n "\${PNPM_PENDING_BUILDS:-}" ]; then
33
+ mkdir -p "$PNPM_NODE_MODULES"
34
+ printf 'pendingBuilds: [%s]\\n' "$PNPM_PENDING_BUILDS" > "$PNPM_NODE_MODULES/.modules.yaml"
35
+ fi
36
+ case " $* " in
37
+ *' rebuild '*)
38
+ if [ "\${PNPM_REBUILD_INVALID_LEDGER:-0}" = 1 ]; then
39
+ printf 'pendingBuilds: invalid\n' > "$PNPM_NODE_MODULES/.modules.yaml"
40
+ fi
41
+ if [ "\${PNPM_REBUILD_BREAK_LINK:-0}" = 1 ]; then rm -f "$PNPM_DEPENDENCY_LINK"; fi
42
+ ;;
43
+ *' --force '*)
44
+ if [ "\${PNPM_REPAIR_LINK:-0}" = 1 ]; then
45
+ mkdir -p "$(dirname "$PNPM_DEPENDENCY_LINK")"
46
+ rm -f "$PNPM_DEPENDENCY_LINK"
47
+ ln -s "$PNPM_DEPENDENCY" "$PNPM_DEPENDENCY_LINK"
48
+ fi
49
+ ;;
50
+ esac
51
+ `;
52
+ }
@@ -4,6 +4,7 @@ import { tmpdir } from 'node:os';
4
4
  import { dirname, join } from 'node:path';
5
5
  import { promisify } from 'node:util';
6
6
  import { runPnpmInstallCli } from '../cli/commands/pnpm-install.mjs';
7
+ import { fakePnpmScript } from './pnpm-install-fake-pnpm.test-helpers.mjs';
7
8
  const execFileAsync = promisify(execFile);
8
9
  async function writeJson(path, value) {
9
10
  await mkdir(dirname(path), { recursive: true });
@@ -20,6 +21,7 @@ export async function makeFixture() {
20
21
  await Promise.all([
21
22
  writeJson(join(root, 'package.json'), { name: 'fixture-root', private: true }),
22
23
  writeFile(join(root, 'pnpm-workspace.yaml'), 'packages:\n - packages/*\nminimumReleaseAge: 2880\n'),
24
+ writeFile(join(root, 'pnpm-lock.yaml'), 'lockfileVersion: 9\npackages: {}\n'),
23
25
  writeJson(join(consumer, 'package.json'), {
24
26
  name: '@fixture/consumer',
25
27
  dependencies: { '@fixture/dependency': 'workspace:^' },
@@ -29,46 +31,7 @@ export async function makeFixture() {
29
31
  mkdir(pnpmBin),
30
32
  ]);
31
33
  await symlink(dependency, dependencyLink, 'dir');
32
- await writeFile(join(pnpmBin, 'pnpm'), `#!/usr/bin/env bash
33
- set -euo pipefail
34
- if [ "\${1:-}" = m ]; then
35
- if [ -n "\${PNPM_LIST_WARNING:-}" ]; then printf '%s\\n' "$PNPM_LIST_WARNING" >&2; fi
36
- printf '%s\\n' "$PNPM_WORKSPACES_JSON"
37
- exit 0
38
- fi
39
- if [ "\${1:-}" = --version ]; then
40
- printf '%s\\n' "\${PNPM_VERSION:-11.0.0}"
41
- exit 0
42
- fi
43
- printf '%s\\n' "$*" >> "$PNPM_LOG"
44
- calls=0
45
- if [ -f "$PNPM_CALLS" ]; then calls="$(cat "$PNPM_CALLS")"; fi
46
- calls=$((calls + 1))
47
- printf '%s' "$calls" > "$PNPM_CALLS"
48
- print_release_age_violation() {
49
- printf '%s\\n' '✗ Lockfile failed supply-chain policy check (1 entries in 0.1s)'
50
- printf '%s\\n' '[ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION] 1 lockfile entries failed verification:'
51
- printf '%s\\n' ' undici@8.10.0 was published at 2026-08-03T15:06:33.000Z, within the minimumReleaseAge cutoff (2026-08-02T04:48:10.357Z)'
52
- }
53
- if [ "\${PNPM_FAIL_CALL:-0}" = "$calls" ]; then
54
- if [ -n "\${PNPM_FAIL_RELEASE_AGE:-}" ]; then print_release_age_violation; fi
55
- exit "\${PNPM_FAIL_CODE:-1}"
56
- fi
57
- if [ "\${PNPM_FAIL_RELEASE_AGE_CALL:-0}" = "$calls" ]; then
58
- print_release_age_violation
59
- exit 1
60
- fi
61
- if [ -n "\${PNPM_SLEEP_SECONDS:-}" ]; then sleep "$PNPM_SLEEP_SECONDS"; fi
62
- case " $* " in
63
- *' --force '*)
64
- if [ "\${PNPM_REPAIR_LINK:-0}" = 1 ]; then
65
- mkdir -p "$(dirname "$PNPM_DEPENDENCY_LINK")"
66
- rm -f "$PNPM_DEPENDENCY_LINK"
67
- ln -s "$PNPM_DEPENDENCY" "$PNPM_DEPENDENCY_LINK"
68
- fi
69
- ;;
70
- esac
71
- `);
34
+ await writeFile(join(pnpmBin, 'pnpm'), fakePnpmScript());
72
35
  await execFileAsync('chmod', ['+x', join(pnpmBin, 'pnpm')]);
73
36
  const workspaces = [
74
37
  { name: 'fixture-root', path: root },
@@ -83,7 +46,10 @@ esac
83
46
  PNPM_DEPENDENCY: dependency,
84
47
  PNPM_DEPENDENCY_LINK: dependencyLink,
85
48
  PNPM_LOG: pnpmLog,
49
+ PNPM_NODE_MODULES: join(root, 'node_modules'),
50
+ PNPM_PENDING_BUILDS: '',
86
51
  PNPM_REPAIR_LINK: '0',
52
+ PNPM_REBUILD_BREAK_LINK: '0',
87
53
  PNPM_WORKSPACES_JSON: JSON.stringify(workspaces),
88
54
  };
89
55
  return {
@@ -1,83 +1,99 @@
1
- import { scheduler } from 'node:timers/promises';
2
- import { persistentDependencyTreeIsCold, persistentMetadataFingerprint, persistentMetadataMatches, writePersistentMetadataStamp, } from './metadata.mjs';
3
- import { nativeBinariesMatchRuntime } from './native-health.mjs';
1
+ import { persistentDependencyTreeIsCold, persistentMetadataFingerprintV4, persistentMetadataStatusV4, writePersistentMetadataStampV4, } from './metadata.mjs';
4
2
  import { runPnpm } from './exec.mjs';
5
- import { INSTALL_TERMINATION_FAILED } from './process.mjs';
6
- import { formatReleaseAgeFailure, isReleaseAgeViolation } from './release-age.mjs';
3
+ import { nativeBinariesMatchRuntime } from './native-health.mjs';
4
+ import { clearPendingDependencyBuilds, pendingBuildDelta, pendingBuilds, validDependencyBuildIds, } from './pending-builds.mjs';
5
+ import { install, reconcileOrFail, withScriptPolicy } from './install-operations.mjs';
7
6
  import { baseInstallArgs, findWorkspaceLinkMismatches, logWorkspaceLinkMismatches, } from './support.mjs';
7
+ import { persistentInstallTransition, persistentProvenanceDiagnostic } from './transition.mjs';
8
8
  // oxfmt-ignore
9
9
  const fail = (message) => { throw new Error(message); };
10
- async function install(args, options, label) {
11
- for (let attempt = 1; attempt <= options.maxAttempts; attempt += 1) {
12
- const attemptResult = await runPnpm(args, options);
13
- if (attemptResult.code === 0)
14
- return;
15
- if (attemptResult.code === INSTALL_TERMINATION_FAILED)
16
- fail(`${label} could not terminate safely`);
17
- const combinedOutput = `${attemptResult.output}\n${attemptResult.errorOutput ?? ''}`;
18
- if (isReleaseAgeViolation(combinedOutput))
19
- fail(formatReleaseAgeFailure(label, combinedOutput));
20
- if (attempt < options.maxAttempts) {
21
- console.warn(`${label} failed (attempt ${attempt}/${options.maxAttempts}); retrying`);
22
- await scheduler.wait(5000);
23
- }
24
- }
25
- fail(`${label} failed after ${options.maxAttempts} attempt${options.maxAttempts === 1 ? '' : 's'}`);
26
- }
27
- function withScriptPolicy(args, installScripts) {
28
- return installScripts ? args : [...args, '--ignore-scripts'];
29
- }
30
- async function reconcileAndFindMismatches(options, runCapture) {
31
- const forced = ['install', '--frozen-lockfile', '--force', ...baseInstallArgs.slice(2)];
32
- // oxfmt-ignore
33
- await install([...forced, '--ignore-scripts', '--ignore-pnpmfile'], options, 'script-free reconciliation');
34
- // oxfmt-ignore
35
- await install(withScriptPolicy(forced, options.installScripts), options, 'strict persistent reconciliation');
36
- return findWorkspaceLinkMismatches(runCapture);
37
- }
38
- async function reconcileOrFail(options, runCapture) {
39
- const remaining = await reconcileAndFindMismatches(options, runCapture);
40
- if (remaining.length > 0) {
41
- logWorkspaceLinkMismatches(remaining);
42
- fail('persistent reconciliation completed with invalid workspace links');
43
- }
44
- }
45
10
  async function persistent(options) {
46
11
  if (options.ephemeralWorkspaces.trim())
47
12
  fail('ephemeral-workspaces is only valid for ephemeral runners');
48
13
  const runCapture = (args) => runPnpm(args, options, true);
49
- const fingerprint = await persistentMetadataFingerprint(runCapture, options.installScripts);
50
- const stamped = await persistentMetadataMatches(fingerprint);
14
+ const fingerprint = await persistentMetadataFingerprintV4(runCapture);
15
+ const provenance = await persistentMetadataStatusV4(fingerprint);
51
16
  const nativesMatch = await nativeBinariesMatchRuntime();
52
- const provenanceOk = stamped && nativesMatch;
53
- // An absent tree has nothing to repair, so one ordinary install below matches the
54
- // reconciled end state. Check first: an install would otherwise make the tree non-cold.
17
+ const provisionalTransition = persistentInstallTransition(provenance, options.installScripts);
18
+ let transition = nativesMatch
19
+ ? provisionalTransition
20
+ : { action: 'reconcile', reason: 'native-health-mismatch' };
21
+ if (transition.action === 'upgrade-dependencies') {
22
+ const ids = await validDependencyBuildIds(transition.pendingDependencyBuilds);
23
+ if (!ids)
24
+ transition = { action: 'upgrade-scripts', reason: 'invalid-pending-dependency-builds' };
25
+ else
26
+ transition = { ...transition, pendingDependencyBuilds: ids };
27
+ }
28
+ const provenanceOk = provenance.kind === 'matching' && transition.action !== 'reconcile' && nativesMatch;
55
29
  const cold = !provenanceOk && (await persistentDependencyTreeIsCold());
56
30
  if (!provenanceOk && !cold) {
57
- console.warn(stamped && !nativesMatch
31
+ const finalTransition = provenance.kind === 'absent'
32
+ ? { action: 'reconcile', reason: 'missing-stamp-populated-tree' }
33
+ : transition;
34
+ console.warn(persistentProvenanceDiagnostic(provenance, options.installScripts, nativesMatch, finalTransition));
35
+ console.warn(provenance.kind === 'matching' && !nativesMatch
58
36
  ? 'persistent optional native binaries do not match this runtime; reconciling'
59
37
  : 'persistent dependency metadata provenance is missing or changed; reconciling');
60
38
  await reconcileOrFail(options, runCapture);
61
- await writePersistentMetadataStamp(fingerprint);
39
+ await writePersistentMetadataStampV4(fingerprint, options.installScripts, true, []);
62
40
  return 'persistent metadata reconciled';
63
41
  }
64
- if (!stamped)
42
+ if (provenance.kind === 'absent')
65
43
  console.warn('persistent dependency tree is absent; installing cold');
66
- await install(withScriptPolicy([...baseInstallArgs], options.installScripts), options, 'ordinary persistent install');
44
+ const pendingBefore = transition.action === 'ordinary' && !options.installScripts ? await pendingBuilds() : undefined;
45
+ await install(withScriptPolicy([...baseInstallArgs], transition.action.startsWith('upgrade-') ? false : options.installScripts), options, 'ordinary persistent install');
67
46
  const stale = await findWorkspaceLinkMismatches(runCapture);
68
47
  if (stale.length === 0) {
69
- if (!stamped)
70
- await writePersistentMetadataStamp(fingerprint);
71
- return stamped ? 'persistent ordinary' : 'persistent cold';
48
+ if (transition.action.startsWith('upgrade-')) {
49
+ const pendingDependencyBuilds = transition.action === 'upgrade-dependencies'
50
+ ? transition.pendingDependencyBuilds
51
+ : undefined;
52
+ const rebuild = pendingDependencyBuilds === undefined
53
+ ? ['rebuild', '--pending', '--recursive']
54
+ : ['rebuild', '--recursive', '--', ...pendingDependencyBuilds];
55
+ await install(rebuild, options, 'pending scripts rebuild');
56
+ const rebuiltStale = await findWorkspaceLinkMismatches(runCapture);
57
+ if (rebuiltStale.length > 0) {
58
+ logWorkspaceLinkMismatches(rebuiltStale);
59
+ await reconcileOrFail(options, runCapture);
60
+ console.warn(persistentProvenanceDiagnostic(provenance, options.installScripts, nativesMatch, {
61
+ action: 'reconcile',
62
+ reason: 'workspace-links-stale-after-rebuild',
63
+ }));
64
+ await writePersistentMetadataStampV4(fingerprint, options.installScripts, true, []);
65
+ return 'persistent reconciled';
66
+ }
67
+ if (pendingDependencyBuilds !== undefined &&
68
+ !(await clearPendingDependencyBuilds(pendingDependencyBuilds)))
69
+ fail('dependency rebuild completed but pending build ledger could not be updated safely');
70
+ }
71
+ console.warn(persistentProvenanceDiagnostic(provenance, options.installScripts, nativesMatch, transition));
72
+ const pendingAfter = pendingBefore ? await pendingBuilds() : undefined;
73
+ const delta = pendingBefore && pendingAfter
74
+ ? await pendingBuildDelta(pendingBefore, pendingAfter)
75
+ : undefined;
76
+ const unsafePending = delta?.kind === 'unknown' || Boolean(delta?.workspaceIds.length);
77
+ const pendingDependencyBuilds = transition.action.startsWith('upgrade-') || unsafePending
78
+ ? []
79
+ : [
80
+ ...new Set([
81
+ ...(provenance.kind === 'matching' ? provenance.pendingDependencyBuilds : []),
82
+ ...(delta?.kind === 'known' ? delta.dependencyIds : []),
83
+ ]),
84
+ ].toSorted();
85
+ await writePersistentMetadataStampV4(fingerprint, options.installScripts, unsafePending, pendingDependencyBuilds);
86
+ return provenance.kind === 'absent' ? 'persistent cold' : 'persistent ordinary';
72
87
  }
73
88
  logWorkspaceLinkMismatches(stale);
89
+ console.warn(persistentProvenanceDiagnostic(provenance, options.installScripts, nativesMatch, {
90
+ action: 'reconcile',
91
+ reason: 'workspace-links-stale',
92
+ }));
74
93
  await reconcileOrFail(options, runCapture);
75
- await writePersistentMetadataStamp(fingerprint);
94
+ await writePersistentMetadataStampV4(fingerprint, options.installScripts, true, []);
76
95
  return 'persistent reconciled';
77
96
  }
78
- // A path selector's `...` (dependency closure) suffix is silently ignored by pnpm unless the
79
- // path is brace-wrapped, e.g. `{./web}...` — `./web...` installs only `web` itself. Reject the
80
- // unbraced form outright rather than let it resolve to a smaller-than-intended scope.
81
97
  function isUnbracedPathClosureSelector(selector) {
82
98
  return /^\.{0,2}\//.test(selector) && selector.endsWith('...') && !selector.startsWith('{');
83
99
  }
@@ -0,0 +1,23 @@
1
+ export type ProvenanceStatus = {
2
+ kind: 'absent';
3
+ } | {
4
+ kind: 'changed';
5
+ components: string[];
6
+ } | {
7
+ kind: 'matching';
8
+ lastInvocationInstallScripts: boolean;
9
+ pendingDependencyBuilds: string[];
10
+ scriptsEnabledInstallSucceeded: boolean;
11
+ } | {
12
+ kind: 'unsafe';
13
+ };
14
+ export type PersistentInstallTransition = {
15
+ action: 'ordinary' | 'reconcile' | 'upgrade-scripts';
16
+ reason: string;
17
+ } | {
18
+ action: 'upgrade-dependencies';
19
+ pendingDependencyBuilds: [string, ...string[]];
20
+ reason: string;
21
+ };
22
+ export declare function persistentInstallTransition(provenance: ProvenanceStatus, installScripts: boolean): PersistentInstallTransition;
23
+ export declare function persistentProvenanceDiagnostic(provenance: ProvenanceStatus, installScripts: boolean, nativeBinariesMatchRuntime: boolean, transition: PersistentInstallTransition): string;
@@ -0,0 +1,36 @@
1
+ // The matching rows are deliberately explicit: a script-disabled invocation can never erase
2
+ // evidence that this structural tree has already completed a scripts-enabled install.
3
+ export function persistentInstallTransition(provenance, installScripts) {
4
+ if (provenance.kind === 'matching') {
5
+ if (installScripts && !provenance.scriptsEnabledInstallSucceeded)
6
+ return { action: 'upgrade-scripts', reason: 'pending-scripts-rebuild' };
7
+ const [firstPendingDependencyBuild, ...remainingPendingDependencyBuilds] = provenance.pendingDependencyBuilds;
8
+ if (installScripts && firstPendingDependencyBuild !== undefined)
9
+ return {
10
+ action: 'upgrade-dependencies',
11
+ pendingDependencyBuilds: [firstPendingDependencyBuild, ...remainingPendingDependencyBuilds],
12
+ reason: 'pending-dependency-rebuild',
13
+ };
14
+ return { action: 'ordinary', reason: 'matching-structural-provenance' };
15
+ }
16
+ if (provenance.kind === 'absent')
17
+ return { action: 'ordinary', reason: 'missing-stamp' };
18
+ return {
19
+ action: 'reconcile',
20
+ reason: provenance.kind === 'changed' ? 'structural-provenance-changed' : 'unsafe-stamp',
21
+ };
22
+ }
23
+ export function persistentProvenanceDiagnostic(provenance, installScripts, nativeBinariesMatchRuntime, transition) {
24
+ return JSON.stringify({
25
+ action: transition.action,
26
+ changedComponents: provenance.kind === 'changed' ? provenance.components : [],
27
+ event: 'pnpm-install-persistent-provenance',
28
+ installScripts,
29
+ scriptsEnabledInstallSucceeded: provenance.kind === 'matching' ? provenance.scriptsEnabledInstallSucceeded : false,
30
+ lastInvocationInstallScripts: provenance.kind === 'matching' ? provenance.lastInvocationInstallScripts : null,
31
+ pendingDependencyBuildCount: provenance.kind === 'matching' ? provenance.pendingDependencyBuilds.length : 0,
32
+ nativeBinariesMatchRuntime,
33
+ reason: transition.reason,
34
+ state: provenance.kind,
35
+ });
36
+ }
@@ -0,0 +1,12 @@
1
+ export { readSkillManifest, type SkillManifest, type SkillManifestEntry } from './manifest.mts';
2
+ export type LinkSkillOptions = {
3
+ name: string;
4
+ sourceRoot: string;
5
+ targetRoot: string;
6
+ };
7
+ export type LinkSkillResult = {
8
+ created: boolean;
9
+ path: string;
10
+ source: string;
11
+ };
12
+ export declare function linkSkill(options: LinkSkillOptions): Promise<LinkSkillResult>;