arkgate 4.8.19 → 4.8.21
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 +220 -0
- package/README.md +107 -117
- package/bin/ark-check-runtime.mjs +26 -4
- package/bin/ark-layer-match.mjs +236 -12
- package/bin/ark.mjs +6 -2
- package/bin/lib/analysis-engine.mjs +13 -6
- package/bin/lib/config-contract.mjs +6 -0
- package/bin/lib/diagnostic-catalog.mjs +1 -0
- package/bin/lib/doctor-advisories.mjs +22 -1
- package/bin/lib/doctor-human.mjs +11 -9
- package/bin/lib/doctor-plan.mjs +17 -14
- package/bin/lib/enforcement-honesty.mjs +30 -2
- package/bin/lib/first-run-help.mjs +7 -1
- package/bin/lib/html-report-advisories.mjs +8 -0
- package/bin/lib/html-report-depth.mjs +12 -7
- package/bin/lib/invariant-coverage.mjs +406 -54
- package/bin/lib/managed-upgrade.mjs +59 -14
- package/bin/lib/package-manager.mjs +5 -2
- package/bin/lib/physical-cohesion.mjs +68 -51
- package/bin/lib/pilot-loop.mjs +183 -37
- package/bin/lib/reshape-decisions.mjs +2 -2
- package/bin/lib/rules-under-contract.mjs +40 -4
- package/bin/lib/start-install-recovery.mjs +156 -0
- package/bin/lib/start-preview.mjs +0 -54
- package/bin/lib/upgrade-behavior-changes.mjs +83 -0
- package/bin/lib/upgrade-command.mjs +69 -31
- package/bin/lib/upgrade-outcome.mjs +359 -0
- package/bin/lib/violations.mjs +109 -0
- package/dist/{configTypes-VD0qcubY.d.ts → configTypes-CTELo87-.d.ts} +9 -0
- package/dist/{diagnosticCatalog-D2v0ASN2.d.ts → diagnosticCatalog-B7BXlet6.d.ts} +79 -5
- package/dist/eslint/index.cjs +4 -4
- package/dist/eslint/index.d.ts +35 -4
- package/dist/eslint/index.js +4 -4
- package/dist/index.cjs +25 -18
- package/dist/index.d.ts +13 -4
- package/dist/index.js +27 -20
- package/dist/nestjs/index.cjs +9 -9
- package/dist/nestjs/index.d.ts +3 -3
- package/dist/nestjs/index.js +9 -9
- package/dist/runtime/index.cjs +18 -11
- package/dist/runtime/index.d.ts +6 -6
- package/dist/runtime/index.js +18 -11
- package/dist/{types-D5GT5ZT8.d.ts → types-fQ9-KrBj.d.ts} +1 -1
- package/dist/{types-BSzRy2X1.d.ts → types-yDNEQ603.d.ts} +1 -1
- package/docs/README.md +4 -3
- package/docs/agent-guide.md +15 -7
- package/docs/brownfield-adoption.md +4 -3
- package/docs/configuration.md +46 -8
- package/docs/develop.md +1 -1
- package/docs/diagnostics.md +12 -2
- package/docs/package-surface.md +4 -3
- package/docs/product-voice.md +11 -3
- package/docs/use.md +1 -1
- package/package.json +3 -3
- package/schemas/ark.config.schema.json +14 -0
- package/server.json +2 -2
- package/templates/agent-skills/README.md +1 -1
- package/templates/agent-skills/ark-adopt/SKILL.md +18 -1
- package/templates/agent-skills/ark-architect/SKILL.md +2 -0
- package/templates/agent-skills/ark-autopilot/SKILL.md +6 -2
- package/templates/agent-skills/ark-contract/SKILL.md +2 -0
- package/templates/agent-skills/ark-coverage/SKILL.md +3 -1
- package/templates/agent-skills/ark-explain/SKILL.md +3 -1
- package/templates/agent-skills/ark-explore/SKILL.md +15 -1
- package/templates/agent-skills/ark-fix/SKILL.md +2 -0
- package/templates/agent-skills/ark-loop/SKILL.md +2 -0
- package/templates/agent-skills/ark-order/SKILL.md +2 -0
- package/templates/agent-skills/ark-place/SKILL.md +2 -0
- package/templates/agent-skills/ark-runtime/SKILL.md +2 -0
- package/templates/agent-skills/ark-think/SKILL.md +2 -0
- package/templates/agent-skills/ark-upgrade/SKILL.md +25 -10
- package/templates/skills/ark-adopt.md +18 -1
- package/templates/skills/ark-architect.md +2 -0
- package/templates/skills/ark-autopilot.md +6 -2
- package/templates/skills/ark-contract.md +2 -0
- package/templates/skills/ark-coverage.md +3 -1
- package/templates/skills/ark-explain.md +3 -1
- package/templates/skills/ark-explore.md +15 -1
- package/templates/skills/ark-fix.md +2 -0
- package/templates/skills/ark-loop.md +2 -0
- package/templates/skills/ark-order.md +2 -0
- package/templates/skills/ark-place.md +2 -0
- package/templates/skills/ark-runtime.md +2 -0
- package/templates/skills/ark-think.md +2 -0
- package/templates/skills/ark-upgrade.md +25 -10
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classified next steps when start / start --apply package install fails.
|
|
3
|
+
*
|
|
4
|
+
* Callers render `formatStartPackageInstallFailure`. Classification, Age window,
|
|
5
|
+
* pin-aware npx, and AgeExclude-must-be-in-config stay behind this module.
|
|
6
|
+
*/
|
|
7
|
+
import { spawnSync } from 'node:child_process';
|
|
8
|
+
import { arkPackageRecoveryCommand } from './package-manager.mjs';
|
|
9
|
+
|
|
10
|
+
const AGE_MARK = /ERR_PNPM_NO_MATURE_MATCHING_VERSION|minimumReleaseAge/i;
|
|
11
|
+
const AGE_EXCLUDE_LINE =
|
|
12
|
+
'To add it locally later: put arkgate on minimumReleaseAgeExclude in pnpm config or pnpm-workspace.yaml (a CLI flag is not enough), or wait until the release is old enough.';
|
|
13
|
+
const BIN_NOTE =
|
|
14
|
+
'`arkgate-check` is a command in the arkgate package — not its own npm package.';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {unknown} hostOutput
|
|
18
|
+
* @returns {string[]}
|
|
19
|
+
*/
|
|
20
|
+
function hostOutputTail(hostOutput) {
|
|
21
|
+
if (!hostOutput || !String(hostOutput).trim()) return [];
|
|
22
|
+
return String(hostOutput)
|
|
23
|
+
.split('\n')
|
|
24
|
+
.map((line) => line.trimEnd())
|
|
25
|
+
.filter((line) => line.trim())
|
|
26
|
+
.slice(-6);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Exact npm version for `npx --package=arkgate@<pin>` when Age blocked add.
|
|
31
|
+
* @param {string|undefined} packageVersion
|
|
32
|
+
* @param {string|undefined} installCommand
|
|
33
|
+
* @returns {string|null}
|
|
34
|
+
*/
|
|
35
|
+
export function resolveStartInstallPackagePin(packageVersion, installCommand) {
|
|
36
|
+
const fromArg = String(packageVersion ?? '')
|
|
37
|
+
.trim()
|
|
38
|
+
.replace(/^arkgate@/i, '')
|
|
39
|
+
.replace(/^\^/, '')
|
|
40
|
+
.replace(/^v/i, '');
|
|
41
|
+
if (/^\d+\.\d+\.\d+/.test(fromArg)) return fromArg.match(/^\d+\.\d+\.\d+/)?.[0] ?? null;
|
|
42
|
+
const fromCmd = String(installCommand ?? '').match(/arkgate@\^?v?(\d+\.\d+\.\d+)/i);
|
|
43
|
+
return fromCmd?.[1] ?? null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @typedef {{ kind: 'pnpm-age', ageWindow: string, headline: string }} PnpmAgeInstallFailure
|
|
48
|
+
* @typedef {{ kind: 'generic', hostTail: string[] }} GenericInstallFailure
|
|
49
|
+
* @typedef {PnpmAgeInstallFailure | GenericInstallFailure} ClassifiedStartInstallFailure
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Classify captured package-manager stdout+stderr. Age is one class.
|
|
54
|
+
* @param {unknown} hostOutput
|
|
55
|
+
* @returns {ClassifiedStartInstallFailure}
|
|
56
|
+
*/
|
|
57
|
+
export function classifyStartInstallFailure(hostOutput) {
|
|
58
|
+
const text = String(hostOutput ?? '');
|
|
59
|
+
if (AGE_MARK.test(text)) {
|
|
60
|
+
const days = text.match(/(\d+)\s*days?/i)?.[1];
|
|
61
|
+
const ageWindow = days ? `${days} days` : 'a cooling-off window';
|
|
62
|
+
const headline = days
|
|
63
|
+
? `This repo waits before trusting new npm packages (pnpm Age — ${days} days).`
|
|
64
|
+
: 'This repo waits before trusting new npm packages (pnpm Age).';
|
|
65
|
+
return { kind: 'pnpm-age', ageWindow, headline };
|
|
66
|
+
}
|
|
67
|
+
return { kind: 'generic', hostTail: hostOutputTail(text) };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Plain Age headline, or null. Kept for the #268 one-liner tests.
|
|
72
|
+
* @param {unknown} hostOutput
|
|
73
|
+
* @returns {string|null}
|
|
74
|
+
*/
|
|
75
|
+
export function explainPnpmMaturityBlock(hostOutput) {
|
|
76
|
+
const classified = classifyStartInstallFailure(hostOutput);
|
|
77
|
+
return classified.kind === 'pnpm-age' ? classified.headline : null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @typedef {{
|
|
82
|
+
* kind: 'pnpm-age' | 'generic',
|
|
83
|
+
* ageWindow?: string,
|
|
84
|
+
* primaryCommand: string,
|
|
85
|
+
* replayInstallCommand: string | null,
|
|
86
|
+
* lines: string[],
|
|
87
|
+
* }} StartInstallRecovery
|
|
88
|
+
*/
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Typed next-step for a failed start package install.
|
|
92
|
+
* Age never replays the doomed add; generic still names the exact command.
|
|
93
|
+
* @param {{
|
|
94
|
+
* exitStatus: number,
|
|
95
|
+
* installCommand: string,
|
|
96
|
+
* hostOutput?: string,
|
|
97
|
+
* packageVersion?: string,
|
|
98
|
+
* }} input
|
|
99
|
+
* @returns {StartInstallRecovery}
|
|
100
|
+
*/
|
|
101
|
+
export function startInstallRecovery(input) {
|
|
102
|
+
const exitStatus = input?.exitStatus ?? 1;
|
|
103
|
+
const installCommand = input?.installCommand ?? '';
|
|
104
|
+
const classified = classifyStartInstallFailure(input?.hostOutput);
|
|
105
|
+
const pin = resolveStartInstallPackagePin(input?.packageVersion, installCommand);
|
|
106
|
+
const honesty = `Package install failed (exit ${exitStatus}). Setup files are written; the local command is not installed yet.`;
|
|
107
|
+
if (classified.kind === 'pnpm-age') {
|
|
108
|
+
const doctor = arkPackageRecoveryCommand(
|
|
109
|
+
'arkgate-check',
|
|
110
|
+
'--doctor',
|
|
111
|
+
pin ? `arkgate@${pin}` : undefined
|
|
112
|
+
);
|
|
113
|
+
return {
|
|
114
|
+
kind: 'pnpm-age',
|
|
115
|
+
ageWindow: classified.ageWindow,
|
|
116
|
+
primaryCommand: doctor,
|
|
117
|
+
replayInstallCommand: null,
|
|
118
|
+
lines: [classified.headline, honesty, ` ${doctor}`, BIN_NOTE, AGE_EXCLUDE_LINE],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const doctor = arkPackageRecoveryCommand('arkgate-check', '--doctor');
|
|
122
|
+
return {
|
|
123
|
+
kind: 'generic',
|
|
124
|
+
primaryCommand: installCommand,
|
|
125
|
+
replayInstallCommand: installCommand,
|
|
126
|
+
lines: [...classified.hostTail, honesty, ` ${installCommand}`, ` ${doctor}`, BIN_NOTE],
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Red next-step when post-apply package install fails.
|
|
132
|
+
* Host files may already be written; local bins may be missing.
|
|
133
|
+
* @param {{
|
|
134
|
+
* exitStatus: number,
|
|
135
|
+
* installCommand: string,
|
|
136
|
+
* hostOutput?: string,
|
|
137
|
+
* packageVersion?: string,
|
|
138
|
+
* }} input
|
|
139
|
+
*/
|
|
140
|
+
export function formatStartPackageInstallFailure(input) {
|
|
141
|
+
return startInstallRecovery(input).lines.join('\n');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Pipe the package manager so recovery can hide maturity internals. */
|
|
145
|
+
export function runStartPackageInstall(command, commandArgs, cwd) {
|
|
146
|
+
const result = spawnSync(command, commandArgs, {
|
|
147
|
+
cwd,
|
|
148
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
149
|
+
encoding: 'utf8',
|
|
150
|
+
});
|
|
151
|
+
return {
|
|
152
|
+
status: result.status ?? 1,
|
|
153
|
+
stdout: result.stdout ?? '',
|
|
154
|
+
stderr: result.stderr ?? '',
|
|
155
|
+
};
|
|
156
|
+
}
|
|
@@ -5,7 +5,6 @@ import os from 'node:os';
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import {
|
|
7
7
|
arkCommand,
|
|
8
|
-
arkPackageRecoveryCommand,
|
|
9
8
|
buildArchitectureRecommendation,
|
|
10
9
|
START_APPLY_REFUSE_FOOTER,
|
|
11
10
|
} from '../ark-shared.mjs';
|
|
@@ -218,59 +217,6 @@ function commands(root, args, helpers) {
|
|
|
218
217
|
return result;
|
|
219
218
|
}
|
|
220
219
|
|
|
221
|
-
/**
|
|
222
|
-
* Plain one-liner for pnpm minimumReleaseAge / ERR_PNPM_NO_MATURE_MATCHING_VERSION.
|
|
223
|
-
* Host dumps stay out of the recovery — stranger needs one next step.
|
|
224
|
-
*/
|
|
225
|
-
export function explainPnpmMaturityBlock(hostOutput) {
|
|
226
|
-
const text = String(hostOutput ?? '');
|
|
227
|
-
if (!/ERR_PNPM_NO_MATURE_MATCHING_VERSION|minimumReleaseAge/i.test(text)) return null;
|
|
228
|
-
const days = text.match(/(\d+)\s*days?/i)?.[1];
|
|
229
|
-
const window = days ? `${days} days` : 'a cooling-off window';
|
|
230
|
-
return `This repo blocks packages younger than ${window} — pin an older release or exclude arkgate temporarily.`;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/** Pipe the package manager so recovery can hide maturity internals. */
|
|
234
|
-
export function runStartPackageInstall(command, commandArgs, cwd) {
|
|
235
|
-
const result = spawnSync(command, commandArgs, {
|
|
236
|
-
cwd,
|
|
237
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
238
|
-
encoding: 'utf8',
|
|
239
|
-
});
|
|
240
|
-
return {
|
|
241
|
-
status: result.status ?? 1,
|
|
242
|
-
stdout: result.stdout ?? '',
|
|
243
|
-
stderr: result.stderr ?? '',
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Red next-step when post-apply package install fails.
|
|
249
|
-
* Host files may already be written; local bins may be missing.
|
|
250
|
-
*/
|
|
251
|
-
export function formatStartPackageInstallFailure({ exitStatus, installCommand, hostOutput }) {
|
|
252
|
-
const doctor = arkPackageRecoveryCommand('arkgate-check', '--doctor');
|
|
253
|
-
const maturity = explainPnpmMaturityBlock(hostOutput);
|
|
254
|
-
const lines = [];
|
|
255
|
-
if (maturity) {
|
|
256
|
-
lines.push(maturity);
|
|
257
|
-
} else if (hostOutput && String(hostOutput).trim()) {
|
|
258
|
-
const tail = String(hostOutput)
|
|
259
|
-
.split('\n')
|
|
260
|
-
.map((line) => line.trimEnd())
|
|
261
|
-
.filter((line) => line.trim())
|
|
262
|
-
.slice(-6);
|
|
263
|
-
if (tail.length) lines.push(...tail);
|
|
264
|
-
}
|
|
265
|
-
lines.push(
|
|
266
|
-
`Package install failed (exit ${exitStatus}). Setup files are written; the local command is not installed yet.`,
|
|
267
|
-
` ${installCommand}`,
|
|
268
|
-
` ${doctor}`,
|
|
269
|
-
'`arkgate-check` is a command in the arkgate package — not its own npm package.'
|
|
270
|
-
);
|
|
271
|
-
return lines.join('\n');
|
|
272
|
-
}
|
|
273
|
-
|
|
274
220
|
/**
|
|
275
221
|
* @param {object} preview
|
|
276
222
|
* @param {{ applying?: boolean }} [options] when applying=true, do not claim “no files were changed”
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-version upgrade behavior changes.
|
|
3
|
+
*
|
|
4
|
+
* This table is the package source for migration notes. CHANGELOG.md must
|
|
5
|
+
* carry the same sentences (Unreleased / 4.8.21, including the 4.8.20
|
|
6
|
+
* coverage.symbol note). Post-upgrade verification prints these lines so an
|
|
7
|
+
* agent reads them before editing code.
|
|
8
|
+
*
|
|
9
|
+
* Shipped with the package (`bin/lib` is in the published `files` list).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** @typedef {{ version: string, note: string }} UpgradeBehaviorChange */
|
|
13
|
+
|
|
14
|
+
/** @type {readonly UpgradeBehaviorChange[]} */
|
|
15
|
+
export const UPGRADE_BEHAVIOR_CHANGES = Object.freeze([
|
|
16
|
+
Object.freeze({
|
|
17
|
+
version: '4.8.20',
|
|
18
|
+
note: 'coverage.symbol now requires a real declaration (some green repos turn red)',
|
|
19
|
+
}),
|
|
20
|
+
Object.freeze({
|
|
21
|
+
version: '4.8.21',
|
|
22
|
+
note: 'a bare mention of an invariant id no longer counts as coverage (only describe/it titles or declarations, incl. type/interface/enum)',
|
|
23
|
+
}),
|
|
24
|
+
Object.freeze({
|
|
25
|
+
version: '4.8.21',
|
|
26
|
+
note: "upgrade exit code 3 = applied but red. Scripts comparing against 1 must switch to 'non-zero'",
|
|
27
|
+
}),
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {string} version
|
|
32
|
+
* @returns {number[]}
|
|
33
|
+
*/
|
|
34
|
+
function versionParts(version) {
|
|
35
|
+
return String(version)
|
|
36
|
+
.replace(/^v/, '')
|
|
37
|
+
.split('.')
|
|
38
|
+
.map((part) => {
|
|
39
|
+
const match = /^(\d+)/.exec(part);
|
|
40
|
+
return match ? Number(match[1]) : 0;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {string} left
|
|
46
|
+
* @param {string} right
|
|
47
|
+
* @returns {-1|0|1}
|
|
48
|
+
*/
|
|
49
|
+
export function compareUpgradeVersions(left, right) {
|
|
50
|
+
const a = versionParts(left);
|
|
51
|
+
const b = versionParts(right);
|
|
52
|
+
const length = Math.max(a.length, b.length);
|
|
53
|
+
for (let index = 0; index < length; index += 1) {
|
|
54
|
+
const delta = (a[index] ?? 0) - (b[index] ?? 0);
|
|
55
|
+
if (delta !== 0) return delta < 0 ? -1 : 1;
|
|
56
|
+
}
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @param {UpgradeBehaviorChange} row
|
|
62
|
+
* @returns {string}
|
|
63
|
+
*/
|
|
64
|
+
export function formatBehaviorChange(row) {
|
|
65
|
+
return `${row.version}: ${row.note}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Lines from the shipped table.
|
|
70
|
+
* Omit `upTo` to return every note in this package (the post-upgrade default:
|
|
71
|
+
* the build that implements a note must show it, even before the version bump).
|
|
72
|
+
* Pass `upTo` to keep notes whose version is less than or equal to that release.
|
|
73
|
+
*
|
|
74
|
+
* @param {string} [upTo]
|
|
75
|
+
* @returns {string[]}
|
|
76
|
+
*/
|
|
77
|
+
export function behaviorChangeLines(upTo) {
|
|
78
|
+
const rows =
|
|
79
|
+
typeof upTo === 'string' && upTo.trim()
|
|
80
|
+
? UPGRADE_BEHAVIOR_CHANGES.filter((row) => compareUpgradeVersions(row.version, upTo) <= 0)
|
|
81
|
+
: UPGRADE_BEHAVIOR_CHANGES;
|
|
82
|
+
return rows.map((row) => formatBehaviorChange(row));
|
|
83
|
+
}
|
|
@@ -10,6 +10,13 @@ import {
|
|
|
10
10
|
formatPackageInstallDecisionHuman,
|
|
11
11
|
} from '../ark-shared.mjs';
|
|
12
12
|
import { describePackageVersionDualTruth } from './field-install.mjs';
|
|
13
|
+
import {
|
|
14
|
+
exitCodeFor,
|
|
15
|
+
formatBlockedAssets,
|
|
16
|
+
formatPostUpgradeHuman,
|
|
17
|
+
runPostUpgradeVerification,
|
|
18
|
+
upgradeOutcome,
|
|
19
|
+
} from './upgrade-outcome.mjs';
|
|
13
20
|
import { __packageRoot } from './gate-files.mjs';
|
|
14
21
|
import {
|
|
15
22
|
applyManagedUpgrade,
|
|
@@ -316,15 +323,13 @@ export function buildUpgradeNextCommand(args, planDigest) {
|
|
|
316
323
|
return arkCommand(args.root, 'arkgate', argsStr);
|
|
317
324
|
}
|
|
318
325
|
|
|
319
|
-
function
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
});
|
|
327
|
-
return { exitCode: result.status ?? 1, stderr: result.stderr?.trim() || undefined };
|
|
326
|
+
function stampBlockedNextCommands(plan, args) {
|
|
327
|
+
if ((plan?.summary?.blocked ?? 0) === 0) return plan;
|
|
328
|
+
const command = buildUpgradeNextCommand({ ...args, acceptConflicts: true }, plan.planDigest);
|
|
329
|
+
for (const asset of plan.assets ?? []) {
|
|
330
|
+
if (asset.blocked) asset.nextCommand = command;
|
|
331
|
+
}
|
|
332
|
+
return plan;
|
|
328
333
|
}
|
|
329
334
|
|
|
330
335
|
function runProjectLocalCli({ root, script, argv }) {
|
|
@@ -544,11 +549,14 @@ export function runUpgradeCommand(args, dependencies) {
|
|
|
544
549
|
}).status ?? 1;
|
|
545
550
|
}
|
|
546
551
|
|
|
547
|
-
const plan =
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
+
const plan = stampBlockedNextCommands(
|
|
553
|
+
planManagedUpgrade(root, {
|
|
554
|
+
tools: args.tools,
|
|
555
|
+
acceptConflicts: args.acceptConflicts,
|
|
556
|
+
refreshSkills: args.refreshSkills === true,
|
|
557
|
+
}),
|
|
558
|
+
args
|
|
559
|
+
);
|
|
552
560
|
if (!args.apply) {
|
|
553
561
|
const wouldWrite = plan.summary?.wouldWrite ?? 0;
|
|
554
562
|
const blocked = plan.summary?.blocked ?? 0;
|
|
@@ -577,11 +585,14 @@ export function runUpgradeCommand(args, dependencies) {
|
|
|
577
585
|
);
|
|
578
586
|
} else {
|
|
579
587
|
renderManagedUpgrade(plan, {
|
|
580
|
-
next:
|
|
581
|
-
|
|
582
|
-
?
|
|
583
|
-
:
|
|
584
|
-
|
|
588
|
+
next:
|
|
589
|
+
blocked > 0
|
|
590
|
+
? undefined
|
|
591
|
+
: needsApply
|
|
592
|
+
? args.install
|
|
593
|
+
? `Update the package and recompute this preview with: ${command}`
|
|
594
|
+
: `Apply the exact preview with: ${command}`
|
|
595
|
+
: undefined,
|
|
585
596
|
skillDrift,
|
|
586
597
|
hostSelection: hostHonesty,
|
|
587
598
|
});
|
|
@@ -600,54 +611,80 @@ export function runUpgradeCommand(args, dependencies) {
|
|
|
600
611
|
} catch (error) {
|
|
601
612
|
const message = error instanceof Error ? error.message : String(error);
|
|
602
613
|
console.error(message);
|
|
603
|
-
return
|
|
614
|
+
return exitCodeFor(upgradeOutcome({ error }));
|
|
604
615
|
}
|
|
605
616
|
if (applied.blocked) {
|
|
617
|
+
stampBlockedNextCommands(applied, args);
|
|
606
618
|
const command = buildUpgradeNextCommand(
|
|
607
619
|
{ ...args, acceptConflicts: true },
|
|
608
620
|
applied.planDigest
|
|
609
621
|
);
|
|
622
|
+
const refusal = formatBlockedAssets(applied, { refused: true }).join('\n');
|
|
623
|
+
console.error(refusal);
|
|
610
624
|
if (args.json) {
|
|
611
625
|
console.log(
|
|
612
626
|
managedUpgradeJson(applied, {
|
|
613
627
|
blocked: true,
|
|
628
|
+
outcome: 'blocked',
|
|
614
629
|
reasonCode: applied.reasonCode ?? 'managed-consent-required',
|
|
615
630
|
nextCommand: command,
|
|
616
631
|
})
|
|
617
632
|
);
|
|
618
633
|
} else {
|
|
619
|
-
renderManagedUpgrade(applied, {
|
|
620
|
-
next: 'Preview again with --accept-conflicts, then use that preview\'s exact next command.',
|
|
621
|
-
});
|
|
634
|
+
renderManagedUpgrade(applied, { refused: true });
|
|
622
635
|
}
|
|
623
|
-
return
|
|
636
|
+
return exitCodeFor(upgradeOutcome({ blocked: true }));
|
|
624
637
|
}
|
|
625
638
|
if (applied.nothingToApply && !applied.applied) {
|
|
626
|
-
|
|
639
|
+
const idle = upgradeOutcome({ nothingToApply: true, applied: false });
|
|
640
|
+
if (args.json) console.log(JSON.stringify({ ...applied, outcome: idle.kind }, null, 2));
|
|
627
641
|
else {
|
|
628
642
|
renderManagedUpgrade(applied);
|
|
629
643
|
console.log('No managed content writes pending.');
|
|
630
644
|
}
|
|
631
|
-
return
|
|
645
|
+
return exitCodeFor(idle);
|
|
632
646
|
}
|
|
633
|
-
const
|
|
634
|
-
|
|
635
|
-
:
|
|
647
|
+
const postUpgrade = runPostUpgradeVerification(root, {
|
|
648
|
+
strict: args.strict,
|
|
649
|
+
arkCheck: dependencies.arkCheck,
|
|
650
|
+
packageVersion: dependencies.cliVersion,
|
|
651
|
+
...(dependencies.checkResult ? { checkResult: dependencies.checkResult } : {}),
|
|
652
|
+
...(typeof dependencies.spawnArchitectureCheck === 'function'
|
|
653
|
+
? { checkResult: dependencies.spawnArchitectureCheck(root) }
|
|
654
|
+
: {}),
|
|
655
|
+
});
|
|
656
|
+
const outcome = upgradeOutcome({ applied: true, postUpgrade });
|
|
657
|
+
const verification = {
|
|
658
|
+
mode: postUpgrade.mode,
|
|
659
|
+
exitCode: postUpgrade.exitCode,
|
|
660
|
+
...(postUpgrade.stderr ? { stderr: postUpgrade.stderr } : {}),
|
|
661
|
+
};
|
|
636
662
|
const dualTruth = describePackageVersionDualTruth(root);
|
|
637
663
|
const skillDrift = buildSkillDriftSummary(applied);
|
|
638
664
|
const hostHonesty = buildHostSelectionHonesty(applied);
|
|
639
665
|
// FX05: post-upgrade verification block (advisory, notAScore).
|
|
640
666
|
const postUpgradeChecks = buildPostUpgradeChecks(root, {
|
|
641
667
|
cliVersion: dependencies.cliVersion,
|
|
642
|
-
verification
|
|
668
|
+
verification: {
|
|
669
|
+
...verification,
|
|
670
|
+
verdict: postUpgrade.verdict,
|
|
671
|
+
failing: postUpgrade.failing,
|
|
672
|
+
},
|
|
643
673
|
dualTruth,
|
|
644
674
|
});
|
|
675
|
+
const postUpgradePayload = {
|
|
676
|
+
verdict: postUpgrade.verdict,
|
|
677
|
+
failing: postUpgrade.failing,
|
|
678
|
+
behaviorChanges: postUpgrade.behaviorChanges,
|
|
679
|
+
};
|
|
645
680
|
if (args.json) {
|
|
646
681
|
console.log(
|
|
647
682
|
JSON.stringify(
|
|
648
683
|
{
|
|
649
684
|
...applied,
|
|
685
|
+
outcome: outcome.kind,
|
|
650
686
|
verification,
|
|
687
|
+
postUpgrade: postUpgradePayload,
|
|
651
688
|
skillDrift,
|
|
652
689
|
hostSelection: hostHonesty,
|
|
653
690
|
postUpgradeChecks,
|
|
@@ -673,6 +710,7 @@ export function runUpgradeCommand(args, dependencies) {
|
|
|
673
710
|
);
|
|
674
711
|
} else {
|
|
675
712
|
renderManagedUpgrade(applied, { skillDrift, hostSelection: hostHonesty });
|
|
713
|
+
for (const line of formatPostUpgradeHuman(postUpgrade)) console.log(line);
|
|
676
714
|
if (!args.strict) console.log('Architecture verification skipped (--no-strict).');
|
|
677
715
|
if (args.install === false || dualTruth.dualTruth) {
|
|
678
716
|
console.log(
|
|
@@ -690,5 +728,5 @@ export function runUpgradeCommand(args, dependencies) {
|
|
|
690
728
|
console.log(` [note] mcp: ${postUpgradeChecks.mcpNote}`);
|
|
691
729
|
}
|
|
692
730
|
}
|
|
693
|
-
return
|
|
731
|
+
return exitCodeFor(outcome);
|
|
694
732
|
}
|