superpowers-manager 0.5.2 → 0.6.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.
@@ -61,24 +61,9 @@ export async function readCodexAssessment(root) {
61
61
  });
62
62
  if (errors.length)
63
63
  throw new Error("generated resources");
64
- const selection = {
64
+ const compatibility = await assessCodexCompatibility(root, {
65
65
  effectiveSource: receipt.source,
66
- desiredCommit: receipt.commit,
67
- requestedRef: provenance.requested_ref,
68
- resolvedRef: provenance.resolved_ref,
69
- resolutionKind: "raw-commit",
70
- selectionOrigin: "package-default",
71
- selectionMode: "default",
72
- upstreamSourceOrigin: "package-default",
73
- saved: {
74
- saved_mode: "none",
75
- saved_source: "",
76
- saved_requested_ref: "",
77
- saved_resolved_ref: "",
78
- saved_commit: "",
79
- },
80
- };
81
- const compatibility = await assessCodexCompatibility(root, selection);
66
+ });
82
67
  return {
83
68
  root,
84
69
  commit: receipt.commit,
@@ -1,9 +1,7 @@
1
- import { failureResult, hasTerminalControl, successResult, } from "../../adapter-result.js";
2
- import { codexInspect, codexInstall, } from "./adapter.js";
1
+ import { failureResult, successResult, } from "../../adapter-result.js";
2
+ import { codexInspectControl, codexInspectOwnership, codexInstall, } from "./adapter.js";
3
3
  import { codexPreparationLocation, inspectCodexPrepared, prepareCodexCandidate, readCodexPrepared, validateCodexPreparationBeforeFetch, } from "./prepare.js";
4
- import { codexInstallReceipt, codexPresentation } from "./presentation.js";
5
- import { reportLegacyState, requireManagedUpdateControl, requireNoLegacyState, verifyUninstalledResources, } from "./lifecycle.js";
6
- import { commitMatches } from "../../status.js";
4
+ import { codexPresentation } from "./presentation.js";
7
5
  import { codexHome, codexPaths } from "./paths.js";
8
6
  import { installCodexMarketplace, removeCodexMarketplace, } from "./publication.js";
9
7
  import { readCodexRecovery } from "./recovery.js";
@@ -14,257 +12,11 @@ function preserveFailure(result) {
14
12
  }
15
13
  return { status: result.status, outcome: result.outcome };
16
14
  }
17
- function malformed(result, message) {
18
- return failureResult(result.outcome.operation, "malformed-result", message, [], result.outcome.messages);
19
- }
20
- function invalidStatus(result, message) {
15
+ export function rejectSuccessfulNonzeroStatus(result, message) {
16
+ if (!result.outcome.ok || result.status === 0)
17
+ return result;
21
18
  return failureResult(result.outcome.operation, "invalid-status", message, [], result.outcome.messages);
22
19
  }
23
- function resultRecord(result) {
24
- const value = result.outcome.ok ? result.outcome.result : null;
25
- return typeof value === "object" && value !== null && !Array.isArray(value)
26
- ? value
27
- : null;
28
- }
29
- function stringField(result, key) {
30
- const raw = result[key];
31
- if (raw === null || raw === undefined)
32
- return { ok: true, value: "" };
33
- if (typeof raw === "string")
34
- return { ok: true, value: raw };
35
- return { ok: false };
36
- }
37
- function resourceFlag(result, key) {
38
- const resources = result.resources;
39
- const bag = typeof resources === "object" &&
40
- resources !== null &&
41
- !Array.isArray(resources)
42
- ? resources
43
- : {};
44
- const value = bag[key];
45
- return typeof value === "boolean" ? value : null;
46
- }
47
- function conflictDescriptions(result) {
48
- const raw = result.conflicts;
49
- if (raw === null || raw === undefined)
50
- return [];
51
- if (!Array.isArray(raw))
52
- return null;
53
- const descriptions = [];
54
- for (const item of raw) {
55
- if (typeof item !== "string" ||
56
- item.length === 0 ||
57
- hasTerminalControl(item)) {
58
- return null;
59
- }
60
- descriptions.push(item);
61
- }
62
- return descriptions;
63
- }
64
- function installDecision(legacy) {
65
- switch (legacy.kind) {
66
- case "ok":
67
- return { kind: "allowed" };
68
- case "blocked":
69
- return {
70
- kind: "blocked",
71
- output: { stdout: [], stderr: legacy.lines },
72
- };
73
- case "unknown":
74
- return {
75
- kind: "blocked",
76
- output: { stdout: [], stderr: [`error: ${legacy.message}`] },
77
- };
78
- case "report":
79
- return {
80
- kind: "blocked",
81
- output: {
82
- stdout: [],
83
- stderr: ["error: unexpected legacy report during installation"],
84
- },
85
- };
86
- }
87
- }
88
- export function normalizeCodexOwnership(result) {
89
- if (!result.outcome.ok)
90
- return preserveFailure(result);
91
- if (result.status !== 0) {
92
- return invalidStatus(result, "adapter reported a failure status for inspect --view ownership");
93
- }
94
- const record = resultRecord(result);
95
- if (record === null) {
96
- return malformed(result, "adapter returned a non-object result for inspect --view ownership");
97
- }
98
- const pluginPresent = resourceFlag(record, "plugin");
99
- if (pluginPresent === null) {
100
- return malformed(result, "expected a Boolean adapter result at resources.plugin");
101
- }
102
- const marketplacePresent = resourceFlag(record, "marketplace");
103
- if (marketplacePresent === null) {
104
- return malformed(result, "expected a Boolean adapter result at resources.marketplace");
105
- }
106
- const identity = stringField(record, "identity_state");
107
- if (!identity.ok) {
108
- return malformed(result, "adapter returned a non-string identity_state for inspect --view ownership");
109
- }
110
- const identityState = identity.value;
111
- const conflicts = conflictDescriptions(record);
112
- if (conflicts === null) {
113
- return malformed(result, "expected an array of strings at conflicts");
114
- }
115
- const legacyEligibility = identityState.length === 0
116
- ? {
117
- kind: "blocked",
118
- output: {
119
- stdout: [],
120
- stderr: ["error: probe did not report adapter identity state"],
121
- },
122
- }
123
- : installDecision(requireNoLegacyState(identityState));
124
- const installEligibility = legacyEligibility.kind === "blocked" || conflicts.length === 0
125
- ? legacyEligibility
126
- : {
127
- kind: "blocked",
128
- output: {
129
- stdout: [],
130
- stderr: [
131
- "Conflicting unmanaged Superpowers Codex resources require manual resolution:",
132
- ...conflicts.map((conflict) => `- ${conflict}`),
133
- "Remove or disable each resource manually, then retry.",
134
- ],
135
- },
136
- };
137
- const verification = verifyUninstalledResources(result);
138
- const legacyReport = reportLegacyState(identityState);
139
- let removalVerification;
140
- if (!verification.ok) {
141
- removalVerification = {
142
- kind: "blocked",
143
- output: { stdout: [], stderr: [`error: ${verification.message}`] },
144
- };
145
- }
146
- else if (legacyReport.kind === "unknown") {
147
- removalVerification = {
148
- kind: "blocked",
149
- output: { stdout: [], stderr: [`error: ${legacyReport.message}`] },
150
- };
151
- }
152
- else if (legacyReport.kind === "blocked") {
153
- removalVerification = {
154
- kind: "blocked",
155
- output: {
156
- stdout: [],
157
- stderr: ["error: unexpected legacy block after removal"],
158
- },
159
- };
160
- }
161
- else {
162
- removalVerification = { kind: "allowed" };
163
- }
164
- const postRemovalOutput = legacyReport.kind === "report"
165
- ? { stdout: legacyReport.lines, stderr: [] }
166
- : { stdout: [], stderr: [] };
167
- return successResult(result.outcome.operation, {
168
- installEligibility,
169
- removalInput: { pluginPresent, marketplacePresent },
170
- removalVerification,
171
- postRemovalOutput,
172
- presentationValue: identityState,
173
- presentationConflicts: conflicts,
174
- }, result.outcome.messages);
175
- }
176
- export function normalizeCodexControl(result) {
177
- if (!result.outcome.ok)
178
- return preserveFailure(result);
179
- if (result.status !== 0) {
180
- return invalidStatus(result, "adapter reported a failure status for inspect --view update-control");
181
- }
182
- const record = resultRecord(result);
183
- if (record === null) {
184
- return malformed(result, "adapter returned a non-object result for inspect --view update-control");
185
- }
186
- const control = stringField(record, "update_control");
187
- if (!control.ok) {
188
- return malformed(result, "adapter returned a non-string update_control for inspect --view update-control");
189
- }
190
- const updateControl = control.value;
191
- const probeEligibility = updateControl.length === 0
192
- ? {
193
- kind: "blocked",
194
- output: {
195
- stdout: [],
196
- stderr: [
197
- "error: probe did not report adapter update-control capability",
198
- ],
199
- },
200
- }
201
- : { kind: "allowed" };
202
- const managed = requireManagedUpdateControl(updateControl);
203
- const mutationEligibility = probeEligibility.kind === "blocked"
204
- ? probeEligibility
205
- : managed.ok
206
- ? { kind: "allowed" }
207
- : {
208
- kind: "blocked",
209
- output: {
210
- stdout: [],
211
- stderr: [`error: ${managed.message}`],
212
- },
213
- };
214
- return successResult(result.outcome.operation, { probeEligibility, mutationEligibility, presentationValue: updateControl }, result.outcome.messages);
215
- }
216
- export function normalizeCodexInstalled(result, desiredCommit) {
217
- if (!result.outcome.ok)
218
- return preserveFailure(result);
219
- if (result.status !== 0) {
220
- return invalidStatus(result, "adapter reported a failure status for inspect --view fingerprint");
221
- }
222
- const record = resultRecord(result);
223
- if (record === null) {
224
- return malformed(result, "adapter returned a non-object result for inspect --view fingerprint");
225
- }
226
- const fingerprint = stringField(record, "fingerprint");
227
- if (!fingerprint.ok) {
228
- return malformed(result, "adapter returned a non-string fingerprint for inspect --view fingerprint");
229
- }
230
- const observedIdentity = fingerprint.value;
231
- const installed = observedIdentity.length === 0
232
- ? { kind: "absent", observedIdentity: "" }
233
- : commitMatches(desiredCommit, observedIdentity)
234
- ? { kind: "current", observedIdentity }
235
- : { kind: "mismatch", observedIdentity };
236
- return successResult(result.outcome.operation, installed, result.outcome.messages);
237
- }
238
- function safeHint(value) {
239
- return typeof value === "string" && !hasTerminalControl(value) ? value : "";
240
- }
241
- export function normalizeCodexInstall(result) {
242
- if (!result.outcome.ok)
243
- return preserveFailure(result);
244
- if (result.status !== 0) {
245
- return invalidStatus(result, "adapter reported a failure status for install");
246
- }
247
- const record = resultRecord(result);
248
- if (record === null) {
249
- return malformed(result, "adapter returned a non-object result for install");
250
- }
251
- const hints = record.verification_hints;
252
- const bag = typeof hints === "object" && hints !== null && !Array.isArray(hints)
253
- ? hints
254
- : {};
255
- return successResult(result.outcome.operation, codexInstallReceipt(safeHint(bag.missing), safeHint(bag.mismatch)), result.outcome.messages);
256
- }
257
- // The canonical payload normalizer has no context parameter. Keep the native
258
- // call's defensive success/nonzero-status guard beside the concrete adapter,
259
- // where the exact legacy diagnostic can still name ctx.root.
260
- export function normalizeCodexInstallForContext(result, ctx) {
261
- if (!result.outcome.ok)
262
- return preserveFailure(result);
263
- if (result.status !== 0) {
264
- return invalidStatus(result, codexPresentation.callFailure("install", ctx).invalidStatus);
265
- }
266
- return normalizeCodexInstall(result);
267
- }
268
20
  function requirements(command, env) {
269
21
  if (command !== "probe" &&
270
22
  command !== "install" &&
@@ -286,9 +38,9 @@ async function mutationRoots(ctx) {
286
38
  return [codexHome(ctx.env ?? {}, process.cwd())];
287
39
  }
288
40
  async function inspectCodexUpdateControl(ctx) {
289
- const normalized = normalizeCodexControl(await codexInspect("update-control", ctx));
290
- if (!normalized.outcome.ok)
291
- return normalized;
41
+ const result = rejectSuccessfulNonzeroStatus(await codexInspectControl(ctx), "adapter reported a failure status for inspect --view update-control");
42
+ if (!result.outcome.ok)
43
+ return result;
292
44
  const paths = codexPaths(ctx.env ?? {}, process.cwd());
293
45
  let diagnostic = null;
294
46
  try {
@@ -300,17 +52,17 @@ async function inspectCodexUpdateControl(ctx) {
300
52
  diagnostic = `cannot inspect Codex recovery state at ${paths.recoveryRoot}`;
301
53
  }
302
54
  if (diagnostic === null)
303
- return normalized;
55
+ return result;
304
56
  const decision = {
305
57
  kind: "blocked",
306
58
  output: { stdout: [], stderr: [`error: ${diagnostic}`] },
307
59
  };
308
- return successResult(normalized.outcome.operation, {
60
+ return successResult(result.outcome.operation, {
309
61
  probeEligibility: decision,
310
62
  mutationEligibility: decision,
311
63
  presentationValue: diagnostic,
312
64
  recoveryState: "required",
313
- }, normalized.outcome.messages);
65
+ }, result.outcome.messages);
314
66
  }
315
67
  export const codexHarness = {
316
68
  preparationLocation: codexPreparationLocation,
@@ -319,16 +71,19 @@ export const codexHarness = {
319
71
  prepareCandidate: prepareCodexCandidate,
320
72
  inspectPrepared: inspectCodexPrepared,
321
73
  readPrepared: readCodexPrepared,
322
- inspectOwnership: async (ctx) => normalizeCodexOwnership(await codexInspect("ownership", ctx)),
74
+ inspectOwnership: codexInspectOwnership,
323
75
  inspectUpdateControl: inspectCodexUpdateControl,
324
76
  inspectInstalled: inspectCodexInstallation,
325
- install: async (artifact, ctx) => installCodexMarketplace(artifact, ctx, async (root, context) => normalizeCodexInstallForContext(await codexInstall(root, context), context)),
77
+ install: async (artifact, ctx) => installCodexMarketplace(artifact, ctx, async (root, context) => {
78
+ const result = await codexInstall(root, context);
79
+ return rejectSuccessfulNonzeroStatus(result, codexPresentation.callFailure("install", context).invalidStatus);
80
+ }),
326
81
  remove: async (input, ctx) => {
327
82
  const result = await removeCodexMarketplace(input, ctx);
328
83
  if (!result.outcome.ok)
329
84
  return preserveFailure(result);
330
85
  if (result.status !== 0) {
331
- return invalidStatus(result, codexPresentation.callFailure("remove", ctx, input).invalidStatus);
86
+ return rejectSuccessfulNonzeroStatus(result, codexPresentation.callFailure("remove", ctx, input).invalidStatus);
332
87
  }
333
88
  return successResult(result.outcome.operation, null, result.outcome.messages);
334
89
  },
@@ -52,7 +52,6 @@ export function reportLegacyState(identityState) {
52
52
  }
53
53
  return unknownState(identityState);
54
54
  }
55
- export { verifyInstalledFingerprint } from "./presentation.js";
56
55
  // Ported from
57
56
  // `git show ad56569a4c161e7b122967442e2b026eeb6395f6:scripts/core/lifecycle.sh:62-70::spw_require_managed_update_control`.
58
57
  // Both refusals reached spw_die in the shell, so both carry the `error: ` prefix
@@ -71,65 +70,122 @@ export function requireManagedUpdateControl(value) {
71
70
  message: `unknown adapter update-control capability: ${value}`,
72
71
  };
73
72
  }
74
- function readResult(adapterResult) {
75
- if (adapterResult.status !== 0)
76
- return { kind: "call-failed" };
77
- const outcome = adapterResult.outcome;
78
- if (!outcome.ok)
79
- return { kind: "call-failed" };
80
- const value = outcome.result;
81
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
82
- return { kind: "unusable" };
83
- }
84
- return { kind: "object", value: value };
85
- }
86
- // Ported from
87
- // `git show ad56569a4c161e7b122967442e2b026eeb6395f6:scripts/core/lifecycle.sh:126-141::spw_verify_uninstalled_resources`,
88
- // with the Boolean coercion of
89
- // `git show ad56569a4c161e7b122967442e2b026eeb6395f6:scripts/core/adapter.sh:58-73::spw_adapter_result_boolean`
90
- // folded in: a non-Boolean is a hard failure, never a falsy "absent".
91
- export function verifyUninstalledResources(inspectResult) {
92
- const read = readResult(inspectResult);
93
- if (read.kind !== "object") {
94
- return {
95
- ok: false,
96
- message: "cannot read the adapter ownership inspection after removal",
97
- };
98
- }
99
- const inspected = read.value;
100
- // A missing or non-object `resources` falls THROUGH to the Boolean check
101
- // rather than getting its own message.
102
- // `git show ad56569a4c161e7b122967442e2b026eeb6395f6:scripts/core/adapter.sh:70::expected`
103
- // emits "expected Boolean adapter result at resources.plugin" for input {} —
104
- // the input tests/unit/harnesses/codex/lifecycle.test.ts's "a non-object resources falls
105
- // through to the Boolean message" exercises — so a distinct "not an object"
106
- // message here would be a port-only divergence. Parity, not divergence. Spec
107
- // §6.2.3 item 3.
108
- const resources = inspected.resources;
109
- const bag = typeof resources === "object" &&
110
- resources !== null &&
111
- !Array.isArray(resources)
112
- ? resources
113
- : {};
114
- for (const key of ["plugin", "marketplace"]) {
115
- if (typeof bag[key] !== "boolean") {
73
+ function installDecision(legacy) {
74
+ switch (legacy.kind) {
75
+ case "ok":
76
+ return { kind: "allowed" };
77
+ case "blocked":
116
78
  return {
117
- ok: false,
118
- message: `expected a Boolean adapter result at resources.${key}`,
79
+ kind: "blocked",
80
+ output: { stdout: [], stderr: legacy.lines },
119
81
  };
82
+ case "unknown":
83
+ return {
84
+ kind: "blocked",
85
+ output: { stdout: [], stderr: [`error: ${legacy.message}`] },
86
+ };
87
+ case "report":
88
+ return {
89
+ kind: "blocked",
90
+ output: {
91
+ stdout: [],
92
+ stderr: ["error: unexpected legacy report during installation"],
93
+ },
94
+ };
95
+ }
96
+ }
97
+ export function codexOwnershipInspection(identityState, removalInput, conflicts) {
98
+ const { pluginPresent, marketplacePresent } = removalInput;
99
+ const legacyEligibility = identityState.length === 0
100
+ ? {
101
+ kind: "blocked",
102
+ output: {
103
+ stdout: [],
104
+ stderr: ["error: probe did not report adapter identity state"],
105
+ },
120
106
  }
107
+ : installDecision(requireNoLegacyState(identityState));
108
+ const installEligibility = legacyEligibility.kind === "blocked" || conflicts.length === 0
109
+ ? legacyEligibility
110
+ : {
111
+ kind: "blocked",
112
+ output: {
113
+ stdout: [],
114
+ stderr: [
115
+ "Conflicting unmanaged Superpowers Codex resources require manual resolution:",
116
+ ...conflicts.map((conflict) => `- ${conflict}`),
117
+ "Remove or disable each resource manually, then retry.",
118
+ ],
119
+ },
120
+ };
121
+ const legacyReport = reportLegacyState(identityState);
122
+ let removalVerification;
123
+ if (pluginPresent || marketplacePresent) {
124
+ const message = pluginPresent
125
+ ? "owned plugin resource is still installed after removal"
126
+ : "owned marketplace resource is still registered after removal";
127
+ removalVerification = {
128
+ kind: "blocked",
129
+ output: { stdout: [], stderr: ["error: " + message] },
130
+ };
121
131
  }
122
- if (bag.plugin === true) {
123
- return {
124
- ok: false,
125
- message: "owned plugin resource is still installed after removal",
132
+ else if (legacyReport.kind === "unknown") {
133
+ removalVerification = {
134
+ kind: "blocked",
135
+ output: { stdout: [], stderr: [`error: ${legacyReport.message}`] },
126
136
  };
127
137
  }
128
- if (bag.marketplace === true) {
129
- return {
130
- ok: false,
131
- message: "owned marketplace resource is still registered after removal",
138
+ else if (legacyReport.kind === "blocked") {
139
+ removalVerification = {
140
+ kind: "blocked",
141
+ output: {
142
+ stdout: [],
143
+ stderr: ["error: unexpected legacy block after removal"],
144
+ },
132
145
  };
133
146
  }
134
- return { ok: true };
147
+ else {
148
+ removalVerification = { kind: "allowed" };
149
+ }
150
+ const postRemovalOutput = legacyReport.kind === "report"
151
+ ? { stdout: legacyReport.lines, stderr: [] }
152
+ : { stdout: [], stderr: [] };
153
+ return {
154
+ installEligibility,
155
+ removalInput,
156
+ removalVerification,
157
+ postRemovalOutput,
158
+ presentationValue: identityState,
159
+ presentationConflicts: conflicts,
160
+ };
161
+ }
162
+ export function codexControlInspection(updateControl) {
163
+ const probeEligibility = updateControl.length === 0
164
+ ? {
165
+ kind: "blocked",
166
+ output: {
167
+ stdout: [],
168
+ stderr: [
169
+ "error: probe did not report adapter update-control capability",
170
+ ],
171
+ },
172
+ }
173
+ : { kind: "allowed" };
174
+ const managed = requireManagedUpdateControl(updateControl);
175
+ const mutationEligibility = probeEligibility.kind === "blocked"
176
+ ? probeEligibility
177
+ : managed.ok
178
+ ? { kind: "allowed" }
179
+ : {
180
+ kind: "blocked",
181
+ output: {
182
+ stdout: [],
183
+ stderr: [`error: ${managed.message}`],
184
+ },
185
+ };
186
+ return {
187
+ probeEligibility,
188
+ mutationEligibility,
189
+ presentationValue: updateControl,
190
+ };
135
191
  }
@@ -1,4 +1,4 @@
1
- import { escapeNonAscii, formatPythonNumber, } from "../../python-json-format.js";
1
+ import { escapeNonAscii } from "../../python-json-format.js";
2
2
  import { SafetyError } from "../../safety-error.js";
3
3
  import { isRawNumber, isRawObject, parseStrictJsonPreservingNumbers, } from "../../strict-json.js";
4
4
  const OVERLAY_PROFILE = {
@@ -9,14 +9,17 @@ const OVERLAY_PROFILE = {
9
9
  /**
10
10
  * Apply the manager's manifest overlay to a Codex plugin manifest.
11
11
  *
12
- * Byte-for-byte equivalent to the CPython implementation this replaces:
13
- * `json.dump(data, f, indent=2, allow_nan=False)` plus a trailing newline.
14
- * The byte-parity evidence lives in
15
- * `tests/baseline/harnesses/codex/manifest-overlay-parity.test.ts` (tracked as
16
- * `MANIFEST-READER-OVERLAY-01` in `docs/baseline/traceability.md`) against
17
- * the fixtures under `tests/fixtures/baseline/overlay-parity/`. Known,
18
- * deliberate divergences from strict byte parity are recorded in
12
+ * Preserves validated JSON numeric source tokens while applying the manager's
13
+ * manifest fields, with a trailing newline. The baseline contract lives in
14
+ * `tests/baseline/harnesses/codex/manifest-overlay-parity.test.ts` against the
15
+ * fixtures under `tests/fixtures/baseline/overlay-parity/`. The 5,000-digit
16
+ * integer widening is recorded in
19
17
  * `tests/fixtures/baseline/overlay-parity/divergent/README.md`.
18
+ *
19
+ * Decimal and exponent tokens are range-checked before emission. The strict
20
+ * parser rejects non-standard constants before they reach the emitter. Object
21
+ * member order, string escaping, and unknown fields remain part of this
22
+ * overlay contract.
20
23
  */
21
24
  export function applyManifestOverlay(source, version, path) {
22
25
  let parsed;
@@ -43,14 +46,10 @@ export function applyManifestOverlay(source, version, path) {
43
46
  throw rewrapNumberOutOfRange(cause, path);
44
47
  }
45
48
  }
46
- // formatPythonNumber (src/python-json-format.ts) has no notion of a manifest
47
- // path — it is a general CPython-`json.dump`-equivalent number formatter,
48
- // exercised on its own in tests/unit/python-json-format.test.js — so it
49
- // throws a bare `JSON number out of range: ${raw}`. That left the operator
50
- // with no indication of which manifest produced the diagnostic, unlike every
51
- // other overlay failure. This rewrap adds the path without touching
52
- // formatPythonNumber's own contract, and is scoped to exactly this message so
53
- // it cannot swallow or reword any other overlay throw.
49
+ // emitNumber has no manifest path, so it throws a bare
50
+ // `JSON number out of range: ${raw}`. This rewrap adds the path without
51
+ // touching the local emitter's own contract, and is scoped to exactly this
52
+ // message so it cannot swallow or reword any other overlay throw.
54
53
  const NUMBER_OUT_OF_RANGE_PREFIX = "JSON number out of range: ";
55
54
  function rewrapNumberOutOfRange(cause, path) {
56
55
  if (cause instanceof SafetyError &&
@@ -60,6 +59,12 @@ function rewrapNumberOutOfRange(cause, path) {
60
59
  }
61
60
  return cause;
62
61
  }
62
+ function emitNumber(raw) {
63
+ if (/[.eE]/.test(raw) && !Number.isFinite(Number(raw))) {
64
+ throw new SafetyError("manifest-overlay", `JSON number out of range: ${raw}`);
65
+ }
66
+ return raw;
67
+ }
63
68
  function setMember(entries, key, value) {
64
69
  const index = entries.findIndex(([existing]) => existing === key);
65
70
  if (index === -1)
@@ -105,7 +110,7 @@ function emit(value, depth) {
105
110
  // Order matters: isRawNumber before isRawObject, and both before Array —
106
111
  // the branded records are objects too.
107
112
  if (isRawNumber(value))
108
- return formatPythonNumber(value.source);
113
+ return emitNumber(value.source);
109
114
  if (isRawObject(value))
110
115
  return emitObject(value.entries, depth);
111
116
  const inner = " ".repeat(depth + 1);
@@ -1,5 +1,5 @@
1
- import { normalizeCodexInstall, normalizeCodexInstalled } from "./harness.js";
2
1
  import { displaySource } from "../../selection.js";
2
+ import { hasTerminalControl } from "../../adapter-result.js";
3
3
  // One ordered table drives both formats, preserving the public human and
4
4
  // porcelain field order exposed through the harness presentation.
5
5
  function fields(f) {
@@ -110,23 +110,15 @@ function verificationOutput(kind, hint) {
110
110
  stderr.push(`hint: ${hint}`);
111
111
  return { stdout: [], stderr };
112
112
  }
113
+ function safeHint(value) {
114
+ return typeof value === "string" && !hasTerminalControl(value) ? value : "";
115
+ }
113
116
  export function codexInstallReceipt(missingHint, mismatchHint) {
114
117
  return {
115
- missingVerificationOutput: verificationOutput("missing", missingHint),
116
- mismatchVerificationOutput: verificationOutput("mismatch", mismatchHint),
118
+ missingVerificationOutput: verificationOutput("missing", safeHint(missingHint)),
119
+ mismatchVerificationOutput: verificationOutput("mismatch", safeHint(mismatchHint)),
117
120
  };
118
121
  }
119
- // Compatibility export target for src/harnesses/codex/lifecycle.ts. Delegate to the production
120
- // normalizers and renderer so the retained verdict cannot drift from commands.
121
- export function verifyInstalledFingerprint(desiredCommit, installResult, inspectResult) {
122
- const receipt = normalizeCodexInstall(installResult);
123
- const inspection = normalizeCodexInstalled(inspectResult, desiredCommit);
124
- const output = codexPresentation.renderInstallVerification(desiredCommit, receipt, inspection);
125
- const ok = inspection.status === 0 &&
126
- inspection.outcome.ok &&
127
- inspection.outcome.result.kind === "current";
128
- return { ok, stdout: output.stdout, stderr: output.stderr };
129
- }
130
122
  function probeFacts(facts) {
131
123
  const selection = facts.selection;
132
124
  const saved = selection.saved;