@jevitate/cli 0.8.0 → 0.9.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 (122) hide show
  1. package/dist/approval-provenance.d.ts +16 -0
  2. package/dist/approval-provenance.js +18 -1
  3. package/dist/approval-provenance.js.map +1 -1
  4. package/dist/bin.js +17011 -13887
  5. package/dist/browser-cli.d.ts +10 -0
  6. package/dist/browser-cli.js +50 -0
  7. package/dist/browser-cli.js.map +1 -0
  8. package/dist/build-info.generated.js +2 -2
  9. package/dist/change-context.d.ts +44 -0
  10. package/dist/change-context.js +203 -0
  11. package/dist/change-context.js.map +1 -0
  12. package/dist/check-api.d.ts +1 -1
  13. package/dist/check-api.js +44 -7
  14. package/dist/check-api.js.map +1 -1
  15. package/dist/check-cli.d.ts +2 -1
  16. package/dist/check-cli.js +47 -7
  17. package/dist/check-cli.js.map +1 -1
  18. package/dist/check-execute.d.ts +16 -1
  19. package/dist/check-execute.js +58 -27
  20. package/dist/check-execute.js.map +1 -1
  21. package/dist/check-plan.d.ts +14 -0
  22. package/dist/check-plan.js +24 -1
  23. package/dist/check-plan.js.map +1 -1
  24. package/dist/check-types.d.ts +39 -5
  25. package/dist/check-types.js +8 -0
  26. package/dist/check-types.js.map +1 -1
  27. package/dist/cli-output.js +3 -1
  28. package/dist/cli-output.js.map +1 -1
  29. package/dist/exit-codes.d.ts +6 -3
  30. package/dist/exit-codes.js +9 -3
  31. package/dist/exit-codes.js.map +1 -1
  32. package/dist/explore-adversarial.js +2 -2
  33. package/dist/explore-adversarial.js.map +1 -1
  34. package/dist/explore-cli.js +3 -2
  35. package/dist/explore-cli.js.map +1 -1
  36. package/dist/explore-coverage.js +3 -3
  37. package/dist/explore-coverage.js.map +1 -1
  38. package/dist/explore-feature.js +3 -3
  39. package/dist/explore-feature.js.map +1 -1
  40. package/dist/explore-goal.js +11 -5
  41. package/dist/explore-goal.js.map +1 -1
  42. package/dist/findings-filing.d.ts +6 -0
  43. package/dist/findings-filing.js +15 -1
  44. package/dist/findings-filing.js.map +1 -1
  45. package/dist/heal-probe-guard.d.ts +9 -0
  46. package/dist/heal-probe-guard.js +113 -0
  47. package/dist/heal-probe-guard.js.map +1 -0
  48. package/dist/host-health-run.d.ts +32 -1
  49. package/dist/host-health-run.js +38 -1
  50. package/dist/host-health-run.js.map +1 -1
  51. package/dist/journey-api.d.ts +42 -3
  52. package/dist/journey-api.js +190 -12
  53. package/dist/journey-api.js.map +1 -1
  54. package/dist/journey-cli.js +105 -15
  55. package/dist/journey-cli.js.map +1 -1
  56. package/dist/journey-heal.d.ts +74 -0
  57. package/dist/journey-heal.js +180 -0
  58. package/dist/journey-heal.js.map +1 -0
  59. package/dist/journey-network-checks.d.ts +5 -1
  60. package/dist/journey-network-checks.js +15 -4
  61. package/dist/journey-network-checks.js.map +1 -1
  62. package/dist/journey-prefix.js +2 -2
  63. package/dist/journey-prefix.js.map +1 -1
  64. package/dist/journey-proposal-store.d.ts +100 -0
  65. package/dist/journey-proposal-store.js +342 -0
  66. package/dist/journey-proposal-store.js.map +1 -0
  67. package/dist/journey-result-record.d.ts +40 -0
  68. package/dist/journey-result-record.js +28 -0
  69. package/dist/journey-result-record.js.map +1 -0
  70. package/dist/journey-review-api.js +7 -0
  71. package/dist/journey-review-api.js.map +1 -1
  72. package/dist/journey-review.d.ts +7 -0
  73. package/dist/journey-review.js +39 -0
  74. package/dist/journey-review.js.map +1 -1
  75. package/dist/launch-armed.js +5 -1
  76. package/dist/launch-armed.js.map +1 -1
  77. package/dist/login-cli.d.ts +2 -0
  78. package/dist/login-cli.js +56 -11
  79. package/dist/login-cli.js.map +1 -1
  80. package/dist/mcp-api.d.ts +5 -1
  81. package/dist/mcp-api.js +53 -8
  82. package/dist/mcp-api.js.map +1 -1
  83. package/dist/mcp-cli-tools.js +25 -5
  84. package/dist/mcp-cli-tools.js.map +1 -1
  85. package/dist/multi-run.d.ts +7 -6
  86. package/dist/multi-run.js +5 -4
  87. package/dist/multi-run.js.map +1 -1
  88. package/dist/persona-login.d.ts +60 -3
  89. package/dist/persona-login.js +341 -31
  90. package/dist/persona-login.js.map +1 -1
  91. package/dist/program.js +2 -0
  92. package/dist/program.js.map +1 -1
  93. package/dist/report-api.d.ts +1 -0
  94. package/dist/report-api.js +1 -0
  95. package/dist/report-api.js.map +1 -1
  96. package/dist/resource-preflight.js +5 -3
  97. package/dist/resource-preflight.js.map +1 -1
  98. package/dist/run-metadata.d.ts +5 -2
  99. package/dist/run-metadata.js +10 -3
  100. package/dist/run-metadata.js.map +1 -1
  101. package/dist/run-tags-cli.d.ts +2 -0
  102. package/dist/run-tags-cli.js +8 -2
  103. package/dist/run-tags-cli.js.map +1 -1
  104. package/dist/self-heal-adapter.d.ts +43 -27
  105. package/dist/self-heal-adapter.js +196 -62
  106. package/dist/self-heal-adapter.js.map +1 -1
  107. package/dist/source-cli.js +5 -3
  108. package/dist/source-cli.js.map +1 -1
  109. package/dist/suite-explore-options.js +1 -0
  110. package/dist/suite-explore-options.js.map +1 -1
  111. package/dist/sweep-api.d.ts +23 -0
  112. package/dist/sweep-api.js +39 -8
  113. package/dist/sweep-api.js.map +1 -1
  114. package/dist/sweep-cli.js +3 -1
  115. package/dist/sweep-cli.js.map +1 -1
  116. package/dist/ux-usability.js +3 -2
  117. package/dist/ux-usability.js.map +1 -1
  118. package/package.json +10 -10
  119. package/skills/jevitate-ci-check/SKILL.md +9 -1
  120. package/skills/jevitate-getting-started/SKILL.md +2 -1
  121. package/skills/jevitate-mission-scope/SKILL.md +2 -2
  122. package/skills/jevitate-run-journey/SKILL.md +25 -5
@@ -0,0 +1,100 @@
1
+ import { type ApprovalProvenance, type Journey, type JourneyProposal } from "@jevitate/journey";
2
+ import { type HealAttempt, type ProposedRevisionDraft } from "@jevitate/runtime";
3
+ import type { Recording } from "@jevitate/recording";
4
+ /**
5
+ * #453 — proposed Journey revisions, beside the journeys directory (`.proposals/`, committed with
6
+ * the PR like `.approved/`; a dot-folder, so never listed as a Journey). One PENDING proposal per
7
+ * Journey, keyed by the Journey id: `.proposals/<id>.json`; a newer one replaces it. A rejected one
8
+ * moves to `.proposals/<id>.rejected/<proposalId>.json` with who rejected it and why. A run never
9
+ * writes the stored Journey — only `journey promote --proposal` does.
10
+ *
11
+ * Nothing here can hold a secret: step values arrive through `sanitizeStep` (hidden), every string
12
+ * passes the redaction of the run's secret values, and the recording is the Journey's own. The free
13
+ * text (hypotheses, evidence, notes, rejection details, paths) is also scrubbed of credential-shaped
14
+ * values and sensitive URL parameters. It DOES hold control labels and change evidence — the files
15
+ * are committed (docs/journeys.md).
16
+ *
17
+ * #453 review: the store never follows a symlink — the `.proposals/` directories and files are
18
+ * `lstat`ed on read and write and a link is refused (`JourneyProposalInvalidError`); a write goes
19
+ * to a temporary file renamed into place (atomic).
20
+ */
21
+ /** No pending proposal with that id (or none at all). Exit 64. */
22
+ export declare class JourneyProposalNotFoundError extends Error {
23
+ readonly code = "E_JOURNEY_PROPOSAL_NOT_FOUND";
24
+ }
25
+ /** The proposal was made against a Journey that has changed since, or its proof fields were touched. Exit 64. */
26
+ export declare class JourneyProposalStaleError extends Error {
27
+ readonly code = "E_JOURNEY_PROPOSAL_STALE";
28
+ }
29
+ /** A proposal that changes more than retargets (an assertion, a wait, a request check…) — refused. */
30
+ export declare class JourneyProposalProofError extends Error {
31
+ readonly code = "E_JOURNEY_PROPOSAL_PROOF";
32
+ }
33
+ /** A proposal file that is not valid. */
34
+ export declare class JourneyProposalInvalidError extends Error {
35
+ readonly code = "E_JOURNEY_PROPOSAL_INVALID";
36
+ }
37
+ /** True for a well-formed proposal id (12 hex characters) — also what keeps `../x` out of every path. */
38
+ export declare function isProposalId(id: string): boolean;
39
+ export declare function proposalPath(journeysDir: string, journeyId: string): string;
40
+ export declare function rejectedProposalPath(journeysDir: string, journeyId: string, proposalId: string): string;
41
+ /**
42
+ * #453 review: re-runs the heal floor on every step a revision changes (on write, on read for review
43
+ * and on accept) — a proposal file edited by hand, or written by an older build, never lets
44
+ * `journey promote --proposal` store what the runner would not have healed:
45
+ * - the floor (`healFloor` with the built-in `SafetyPolicy` classification, `journeyStepRisk`) holds
46
+ * for the step before AND after: never a proof / write / risky-control step;
47
+ * - a retargeted click/fill expects only GET/HEAD/OPTIONS requests (part of the floor);
48
+ * - a retargeted navigate URL stays on the Journey's allowed origin (its recorded site).
49
+ */
50
+ export declare function proposalFloorViolation(stored: Journey, proposed: Recording): string | null;
51
+ export interface WriteJourneyProposalInput {
52
+ readonly journeyId: string;
53
+ /** The stored Journey the revision was made against. */
54
+ readonly base: Journey;
55
+ readonly draft: ProposedRevisionDraft;
56
+ readonly attempts: readonly HealAttempt[];
57
+ readonly changes: {
58
+ readonly range?: string;
59
+ readonly baseSha?: string;
60
+ readonly headSha?: string;
61
+ readonly notes?: readonly string[];
62
+ };
63
+ readonly runResultPath?: string;
64
+ /** Secret values of the run, scrubbed from every string written. */
65
+ readonly secrets?: readonly string[];
66
+ }
67
+ export interface WrittenJourneyProposal {
68
+ readonly proposalId: string;
69
+ readonly path: string;
70
+ /** The id of a pending proposal this one replaced. */
71
+ readonly supersededProposal?: string;
72
+ }
73
+ /** The Journey as it would be stored on accept: the stored one with the proposed recording. */
74
+ export declare function proposedJourney(stored: Journey, proposal: Pick<JourneyProposal, "recording">): Journey;
75
+ /**
76
+ * Writes the proposal for `journeyId`, replacing a pending one. Refused (`JourneyProposalProofError`)
77
+ * when the revision changes anything but retargets (`assertRecordingProofUntouched`).
78
+ */
79
+ export declare function writeJourneyProposal(journeysDir: string, input: WriteJourneyProposalInput): Promise<WrittenJourneyProposal>;
80
+ /** The pending proposal for a Journey, or null. An unreadable or invalid file is refused, never ignored. */
81
+ export declare function readJourneyProposal(journeysDir: string, journeyId: string): Promise<JourneyProposal | null>;
82
+ /** The pending proposal named `proposalId` for the Journey; `JourneyProposalNotFoundError` when none matches. */
83
+ export declare function requireJourneyProposal(journeysDir: string, journeyId: string, proposalId: string): Promise<JourneyProposal>;
84
+ /** True when the proposal was made against a different Journey than the stored one. */
85
+ export declare function isProposalStale(stored: Journey, proposal: JourneyProposal): boolean;
86
+ /**
87
+ * Re-checks a proposal against the stored Journey (on read for review and again on accept): stale
88
+ * when the Journey changed since, proof-touched when the file was edited to change anything but
89
+ * retargets, or when its hash no longer matches its recording.
90
+ */
91
+ export declare function checkProposal(stored: Journey, proposal: JourneyProposal): JourneyProposalStaleError | JourneyProposalProofError | null;
92
+ /** Deletes the pending proposal (after it was accepted). */
93
+ export declare function deleteJourneyProposal(journeysDir: string, journeyId: string): Promise<void>;
94
+ /** Moves the pending proposal to `.rejected/`, recording when, why and how it was rejected. */
95
+ export declare function rejectJourneyProposal(journeysDir: string, journeyId: string, proposalId: string, rejection: {
96
+ readonly reason: string;
97
+ readonly provenance: ApprovalProvenance;
98
+ }): Promise<{
99
+ path: string;
100
+ }>;
@@ -0,0 +1,342 @@
1
+ import { lstat, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
2
+ import { dirname, join, relative, sep } from "node:path";
3
+ import { contentHash, clock } from "@jevitate/domain";
4
+ import { JourneyProposalSchema, RejectedProposalSchema, } from "@jevitate/journey";
5
+ import { assertRecordingProofUntouched, flattenRecording, healFloor, sanitizeStep } from "@jevitate/runtime";
6
+ import { journeyReviewHash } from "./journey-review.js";
7
+ import { journeyStepRisk } from "./journey-heal.js";
8
+ import { redactCredentialShapes, redactText, redactUrl } from "@jevitate/ai-core";
9
+ /**
10
+ * #453 — proposed Journey revisions, beside the journeys directory (`.proposals/`, committed with
11
+ * the PR like `.approved/`; a dot-folder, so never listed as a Journey). One PENDING proposal per
12
+ * Journey, keyed by the Journey id: `.proposals/<id>.json`; a newer one replaces it. A rejected one
13
+ * moves to `.proposals/<id>.rejected/<proposalId>.json` with who rejected it and why. A run never
14
+ * writes the stored Journey — only `journey promote --proposal` does.
15
+ *
16
+ * Nothing here can hold a secret: step values arrive through `sanitizeStep` (hidden), every string
17
+ * passes the redaction of the run's secret values, and the recording is the Journey's own. The free
18
+ * text (hypotheses, evidence, notes, rejection details, paths) is also scrubbed of credential-shaped
19
+ * values and sensitive URL parameters. It DOES hold control labels and change evidence — the files
20
+ * are committed (docs/journeys.md).
21
+ *
22
+ * #453 review: the store never follows a symlink — the `.proposals/` directories and files are
23
+ * `lstat`ed on read and write and a link is refused (`JourneyProposalInvalidError`); a write goes
24
+ * to a temporary file renamed into place (atomic).
25
+ */
26
+ /** No pending proposal with that id (or none at all). Exit 64. */
27
+ export class JourneyProposalNotFoundError extends Error {
28
+ code = "E_JOURNEY_PROPOSAL_NOT_FOUND";
29
+ }
30
+ /** The proposal was made against a Journey that has changed since, or its proof fields were touched. Exit 64. */
31
+ export class JourneyProposalStaleError extends Error {
32
+ code = "E_JOURNEY_PROPOSAL_STALE";
33
+ }
34
+ /** A proposal that changes more than retargets (an assertion, a wait, a request check…) — refused. */
35
+ export class JourneyProposalProofError extends Error {
36
+ code = "E_JOURNEY_PROPOSAL_PROOF";
37
+ }
38
+ /** A proposal file that is not valid. */
39
+ export class JourneyProposalInvalidError extends Error {
40
+ code = "E_JOURNEY_PROPOSAL_INVALID";
41
+ }
42
+ const PROPOSAL_ID = /^[0-9a-f]{12}$/;
43
+ /** True for a well-formed proposal id (12 hex characters) — also what keeps `../x` out of every path. */
44
+ export function isProposalId(id) {
45
+ return PROPOSAL_ID.test(id);
46
+ }
47
+ function parts(id) {
48
+ const p = id.split("/");
49
+ return { dir: p.slice(0, -1), base: p[p.length - 1] ?? id };
50
+ }
51
+ export function proposalPath(journeysDir, journeyId) {
52
+ const { dir, base } = parts(journeyId);
53
+ return join(journeysDir, ".proposals", ...dir, `${base}.json`);
54
+ }
55
+ export function rejectedProposalPath(journeysDir, journeyId, proposalId) {
56
+ const { dir, base } = parts(journeyId);
57
+ return join(journeysDir, ".proposals", ...dir, `${base}.rejected`, `${proposalId}.json`);
58
+ }
59
+ /**
60
+ * Refuses (`JourneyProposalInvalidError`) when `.proposals/` or any directory or file on the way to
61
+ * `path` under it is a symbolic link — never followed, for a read or a write. Components that do not
62
+ * exist yet are fine (a write creates them).
63
+ */
64
+ async function assertNoSymlink(path) {
65
+ const marker = `${sep}.proposals${sep}`;
66
+ const at = path.lastIndexOf(marker);
67
+ if (at < 0)
68
+ throw new JourneyProposalInvalidError(`${path} is not under a .proposals directory`);
69
+ const root = path.slice(0, at + marker.length - 1);
70
+ const parts = relative(root, path).split(sep).filter((p) => p !== "");
71
+ let cur = root;
72
+ for (const next of [null, ...parts]) {
73
+ if (next !== null)
74
+ cur = join(cur, next);
75
+ let st;
76
+ try {
77
+ st = await lstat(cur);
78
+ }
79
+ catch (err) {
80
+ if (typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT")
81
+ return;
82
+ throw err;
83
+ }
84
+ if (st.isSymbolicLink())
85
+ throw new JourneyProposalInvalidError(`refusing to follow a symbolic link at ${cur} — proposals are never read or written through a link`);
86
+ }
87
+ }
88
+ async function readJson(path) {
89
+ await assertNoSymlink(path);
90
+ let raw;
91
+ try {
92
+ raw = await readFile(path, "utf8");
93
+ }
94
+ catch (err) {
95
+ if (typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT")
96
+ return null;
97
+ throw err;
98
+ }
99
+ try {
100
+ return JSON.parse(raw);
101
+ }
102
+ catch {
103
+ throw new JourneyProposalInvalidError(`${path} is not valid JSON`);
104
+ }
105
+ }
106
+ async function writeJson(path, value) {
107
+ await assertNoSymlink(path);
108
+ await mkdir(dirname(path), { recursive: true });
109
+ await assertNoSymlink(path);
110
+ const tmp = `${path}.${process.pid}.${clock.monotonicMs().toString(36)}.tmp`;
111
+ await writeFile(tmp, `${JSON.stringify(value, null, 2)}\n`, { flag: "wx" });
112
+ try {
113
+ await rename(tmp, path);
114
+ }
115
+ catch (err) {
116
+ await rm(tmp, { force: true });
117
+ throw err;
118
+ }
119
+ }
120
+ async function removeFile(path) {
121
+ await assertNoSymlink(path);
122
+ await rm(path, { force: true });
123
+ }
124
+ /** Deterministic JSON (keys sorted, undefined dropped): two steps are the same when these agree. */
125
+ function canonical(v) {
126
+ if (Array.isArray(v))
127
+ return `[${v.map(canonical).join(",")}]`;
128
+ if (v !== null && typeof v === "object") {
129
+ const o = v;
130
+ return `{${Object.keys(o)
131
+ .filter((k) => o[k] !== undefined)
132
+ .sort()
133
+ .map((k) => `${JSON.stringify(k)}:${canonical(o[k])}`)
134
+ .join(",")}}`;
135
+ }
136
+ return JSON.stringify(v) ?? "undefined";
137
+ }
138
+ function originOf(url) {
139
+ if (!/^[a-z][a-z0-9+.-]*:/i.test(url))
140
+ return null;
141
+ try {
142
+ return new URL(url).origin;
143
+ }
144
+ catch {
145
+ return "invalid";
146
+ }
147
+ }
148
+ /**
149
+ * #453 review: re-runs the heal floor on every step a revision changes (on write, on read for review
150
+ * and on accept) — a proposal file edited by hand, or written by an older build, never lets
151
+ * `journey promote --proposal` store what the runner would not have healed:
152
+ * - the floor (`healFloor` with the built-in `SafetyPolicy` classification, `journeyStepRisk`) holds
153
+ * for the step before AND after: never a proof / write / risky-control step;
154
+ * - a retargeted click/fill expects only GET/HEAD/OPTIONS requests (part of the floor);
155
+ * - a retargeted navigate URL stays on the Journey's allowed origin (its recorded site).
156
+ */
157
+ export function proposalFloorViolation(stored, proposed) {
158
+ const riskOf = journeyStepRisk();
159
+ const before = flattenRecording(stored.recording);
160
+ const after = flattenRecording(proposed);
161
+ let site = null;
162
+ try {
163
+ site = new URL(stored.recording.site).origin;
164
+ }
165
+ catch {
166
+ site = null;
167
+ }
168
+ for (let i = 0; i < after.length; i++) {
169
+ const b = before[i];
170
+ const a = after[i];
171
+ if (b !== undefined && canonical(b.step) === canonical(a.step))
172
+ continue;
173
+ for (const [which, rec] of [["before", b?.recorded], ["after", a.recorded]]) {
174
+ if (rec === undefined)
175
+ return `step ${i + 1} is not in the stored Journey`;
176
+ const floor = healFloor(rec, riskOf);
177
+ if (floor.floor !== "healable")
178
+ return `step ${i + 1} (${which}): ${floor.reason}`;
179
+ }
180
+ if (a.step.kind === "navigate") {
181
+ const o = originOf(a.step.url);
182
+ if (o !== null && o !== site)
183
+ return `step ${i + 1}: the retargeted URL is on ${o}, not the Journey's origin ${site ?? "(none)"}`;
184
+ }
185
+ }
186
+ return null;
187
+ }
188
+ /** The Journey as it would be stored on accept: the stored one with the proposed recording. */
189
+ export function proposedJourney(stored, proposal) {
190
+ return { ...stored, recording: proposal.recording };
191
+ }
192
+ /**
193
+ * Writes the proposal for `journeyId`, replacing a pending one. Refused (`JourneyProposalProofError`)
194
+ * when the revision changes anything but retargets (`assertRecordingProofUntouched`).
195
+ */
196
+ export async function writeJourneyProposal(journeysDir, input) {
197
+ const violation = assertRecordingProofUntouched(input.base.recording, input.draft.recording);
198
+ if (violation !== null)
199
+ throw new JourneyProposalProofError(`refusing to store a proposal that touches the Journey's proof (${violation.code}): ${violation.detail}`);
200
+ const floor = proposalFloorViolation(input.base, input.draft.recording);
201
+ if (floor !== null)
202
+ throw new JourneyProposalProofError(`refusing to store a proposal the heal floor refuses: ${floor}`);
203
+ const baseHash = journeyReviewHash(input.base);
204
+ const secrets = [...(input.secrets ?? [])];
205
+ // Every string: the run's secret values. Free text (not the recording or a step): also
206
+ // credential-shaped values and sensitive URL parameters (#453 review).
207
+ const scrub = (v, free = true) => typeof v === "string"
208
+ ? free
209
+ ? redactUrl(redactCredentialShapes(secrets.length === 0 ? v : redactText(v, secrets)))
210
+ : secrets.length === 0
211
+ ? v
212
+ : redactText(v, secrets)
213
+ : Array.isArray(v)
214
+ ? v.map((x) => scrub(x, free))
215
+ : v !== null && typeof v === "object"
216
+ ? Object.fromEntries(Object.entries(v).map(([k, x]) => [k, scrub(x, free)]))
217
+ : v;
218
+ const screenshotOf = (n) => input.attempts.find((a) => a.n === n)?.observation?.screenshot;
219
+ const steps = input.draft.steps.map((s) => {
220
+ const shot = screenshotOf(s.attempt);
221
+ const before = s.evidence[0]?.before;
222
+ const after = s.evidence[0]?.after;
223
+ return {
224
+ index: s.index,
225
+ before: sanitizeStep(s.before),
226
+ after: sanitizeStep(s.after),
227
+ justification: {
228
+ hypothesis: s.hypothesis,
229
+ evidence: [...s.evidence],
230
+ ...(s.anchorNotInChange === true ? { anchorNotInChange: true } : {}),
231
+ },
232
+ evidence: { ...(before === undefined ? {} : { before }), ...(after === undefined ? {} : { after }) },
233
+ ...(shot === undefined ? {} : { screenshots: [shot] }),
234
+ };
235
+ });
236
+ const proposalId = contentHash({ baseHash, steps: steps.map((s) => ({ index: s.index, after: s.after })) }).slice(0, 12);
237
+ const proposed = proposedJourney(input.base, { recording: input.draft.recording });
238
+ const body = {
239
+ v: 1,
240
+ kind: "journey-proposal",
241
+ proposalId,
242
+ journeyId: input.journeyId,
243
+ baseHash,
244
+ proposedHash: journeyReviewHash(proposed),
245
+ createdAt: clock.nowIso(),
246
+ recording: input.draft.recording,
247
+ steps,
248
+ changes: {
249
+ ...(input.changes.range === undefined ? {} : { range: input.changes.range }),
250
+ ...(input.changes.baseSha === undefined ? {} : { baseSha: input.changes.baseSha }),
251
+ ...(input.changes.headSha === undefined ? {} : { headSha: input.changes.headSha }),
252
+ notes: [...(input.changes.notes ?? [])],
253
+ },
254
+ attempts: input.attempts.map((a) => ({ ...a, candidate: a.candidate === null ? null : sanitizeStep(a.candidate) })),
255
+ run: input.runResultPath === undefined ? {} : { resultPath: input.runResultPath },
256
+ };
257
+ const plain = JSON.parse(JSON.stringify(body));
258
+ const strict = scrub(plain, false);
259
+ const free = scrub(plain, true);
260
+ const scrubbed = {
261
+ ...free,
262
+ ...Object.fromEntries(["v", "kind", "proposalId", "journeyId", "baseHash", "proposedHash", "createdAt", "recording"].map((k) => [k, strict[k]])),
263
+ steps: free.steps.map((st, i) => ({ ...st, before: strict.steps[i].before, after: strict.steps[i].after })),
264
+ changes: { ...free.changes, ...("baseSha" in strict.changes ? { baseSha: strict.changes.baseSha } : {}), ...("headSha" in strict.changes ? { headSha: strict.changes.headSha } : {}) },
265
+ attempts: free.attempts.map((a, i) => ({ ...a, candidate: strict.attempts[i].candidate })),
266
+ };
267
+ const parsed = JourneyProposalSchema.safeParse(scrubbed);
268
+ if (!parsed.success)
269
+ throw new JourneyProposalInvalidError(`the proposal does not satisfy its schema: ${parsed.error.issues[0]?.path.join(".") ?? ""} ${parsed.error.issues[0]?.message ?? "invalid"}`);
270
+ const path = proposalPath(journeysDir, input.journeyId);
271
+ const previous = await readJourneyProposal(journeysDir, input.journeyId).catch(() => null);
272
+ await writeJson(path, parsed.data);
273
+ return {
274
+ proposalId,
275
+ path,
276
+ ...(previous !== null && previous.proposalId !== proposalId ? { supersededProposal: previous.proposalId } : {}),
277
+ };
278
+ }
279
+ /** The pending proposal for a Journey, or null. An unreadable or invalid file is refused, never ignored. */
280
+ export async function readJourneyProposal(journeysDir, journeyId) {
281
+ const path = proposalPath(journeysDir, journeyId);
282
+ const raw = await readJson(path);
283
+ if (raw === null)
284
+ return null;
285
+ const parsed = JourneyProposalSchema.safeParse(raw);
286
+ if (!parsed.success)
287
+ throw new JourneyProposalInvalidError(`${path} is not a valid Journey proposal: ${parsed.error.issues[0]?.message ?? "invalid"}`);
288
+ return parsed.data;
289
+ }
290
+ /** The pending proposal named `proposalId` for the Journey; `JourneyProposalNotFoundError` when none matches. */
291
+ export async function requireJourneyProposal(journeysDir, journeyId, proposalId) {
292
+ const proposal = isProposalId(proposalId) ? await readJourneyProposal(journeysDir, journeyId) : null;
293
+ if (proposal === null || proposal.proposalId !== proposalId || proposal.journeyId !== journeyId) {
294
+ throw new JourneyProposalNotFoundError(`journey '${journeyId}' has no pending proposal '${proposalId}' — see: jevitate journey review ${journeyId}`);
295
+ }
296
+ return proposal;
297
+ }
298
+ /** True when the proposal was made against a different Journey than the stored one. */
299
+ export function isProposalStale(stored, proposal) {
300
+ return journeyReviewHash(stored) !== proposal.baseHash;
301
+ }
302
+ /**
303
+ * Re-checks a proposal against the stored Journey (on read for review and again on accept): stale
304
+ * when the Journey changed since, proof-touched when the file was edited to change anything but
305
+ * retargets, or when its hash no longer matches its recording.
306
+ */
307
+ export function checkProposal(stored, proposal) {
308
+ if (isProposalStale(stored, proposal)) {
309
+ return new JourneyProposalStaleError(`proposal '${proposal.proposalId}' was made against journey '${stored.metadata.id}' ${proposal.baseHash.slice(0, 8)}, which has changed since (now ${journeyReviewHash(stored).slice(0, 8)}) — re-run the self-heal: jevitate journey run ${stored.metadata.id} --self-heal`);
310
+ }
311
+ const violation = assertRecordingProofUntouched(stored.recording, proposal.recording);
312
+ if (violation !== null)
313
+ return new JourneyProposalProofError(`proposal '${proposal.proposalId}' touches the Journey's proof (${violation.code}): ${violation.detail}`);
314
+ const floor = proposalFloorViolation(stored, proposal.recording);
315
+ if (floor !== null)
316
+ return new JourneyProposalProofError(`proposal '${proposal.proposalId}' changes a step the heal floor refuses: ${floor}`);
317
+ if (journeyReviewHash(proposedJourney(stored, proposal)) !== proposal.proposedHash) {
318
+ return new JourneyProposalProofError(`proposal '${proposal.proposalId}' does not match its recorded hash — the file was edited`);
319
+ }
320
+ return null;
321
+ }
322
+ /** Deletes the pending proposal (after it was accepted). */
323
+ export async function deleteJourneyProposal(journeysDir, journeyId) {
324
+ await removeFile(proposalPath(journeysDir, journeyId));
325
+ }
326
+ /** Moves the pending proposal to `.rejected/`, recording when, why and how it was rejected. */
327
+ export async function rejectJourneyProposal(journeysDir, journeyId, proposalId, rejection) {
328
+ const proposal = await requireJourneyProposal(journeysDir, journeyId, proposalId);
329
+ const record = RejectedProposalSchema.parse({
330
+ v: 1,
331
+ kind: "journey-proposal-rejected",
332
+ rejectedAt: clock.nowIso(),
333
+ reason: rejection.reason,
334
+ provenance: rejection.provenance,
335
+ proposal,
336
+ });
337
+ const path = rejectedProposalPath(journeysDir, journeyId, proposalId);
338
+ await writeJson(path, record);
339
+ await removeFile(proposalPath(journeysDir, journeyId));
340
+ return { path };
341
+ }
342
+ //# sourceMappingURL=journey-proposal-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journey-proposal-store.js","sourceRoot":"","sources":["../src/journey-proposal-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { type MissionOutcome } from "@jevitate/domain";
2
+ import type { Journey } from "@jevitate/journey";
3
+ import type { HealReport, JourneyRunResult } from "@jevitate/runtime";
4
+ /**
5
+ * #453: the `*.result.json` record of one Journey run — what `jevitate report`, the run index and a
6
+ * `check` suite read. Shared by `runJourneyProgrammatically` (a self-heal run persists it) and,
7
+ * once it switches over, `check`. Every string passes `redactSecretParams`.
8
+ */
9
+ /** The proposal a `healed-pending-review` run wrote (a person reviews it; the stored Journey is untouched). */
10
+ export interface JourneyResultProposal {
11
+ readonly id: string;
12
+ readonly path: string;
13
+ /** The retargeted steps, 1-based, as the review prints them. */
14
+ readonly steps: readonly {
15
+ readonly number: number;
16
+ readonly before: string;
17
+ readonly after: string;
18
+ }[];
19
+ readonly reviewCommand?: string;
20
+ readonly acceptCommand?: string;
21
+ }
22
+ export interface JourneyResultContext {
23
+ /** The Journey as stored (names the secret params; locates the failing step's page). */
24
+ readonly journey: Journey;
25
+ /** The run's params (secret ones are redacted from the record). */
26
+ readonly params: Readonly<Record<string, string>>;
27
+ readonly startedAt: string;
28
+ readonly engine?: unknown;
29
+ readonly suite?: unknown;
30
+ readonly targetBuild?: unknown;
31
+ readonly proposal?: JourneyResultProposal;
32
+ /** The run's heal report when the result does not already carry it. */
33
+ readonly heal?: HealReport;
34
+ }
35
+ export interface JourneyResultRecord {
36
+ readonly missionOutcome: MissionOutcome;
37
+ readonly exitCode: number;
38
+ readonly result: Record<string, unknown>;
39
+ }
40
+ export declare function journeyResultRecord(r: JourneyRunResult, ctx: JourneyResultContext): JourneyResultRecord;
@@ -0,0 +1,28 @@
1
+ import { JOURNEY_MISSION_OUTCOME, journeyExitCode } from "@jevitate/domain";
2
+ import { redactSecretParams } from "./journey-api.js";
3
+ import { journeyStepUrl } from "./check-plan.js";
4
+ export function journeyResultRecord(r, ctx) {
5
+ const at = r.outcome === "quarantined" || r.outcome === "heal-exhausted" ? r.at : undefined;
6
+ const url = journeyStepUrl(ctx.journey, at);
7
+ const heal = r.heal ?? ctx.heal;
8
+ const record = {
9
+ missionOutcome: JOURNEY_MISSION_OUTCOME[r.outcome],
10
+ exitCode: journeyExitCode(r.outcome),
11
+ result: {
12
+ mode: "journey",
13
+ journeyId: ctx.journey.metadata.id,
14
+ outcome: r.outcome,
15
+ ...(r.outcome === "quarantined" || r.outcome === "heal-exhausted" ? { reason: r.reason, ...(r.at === undefined ? {} : { at: r.at }) } : {}),
16
+ ...(url === undefined ? {} : { url }),
17
+ ...(heal === undefined ? {} : { heal }),
18
+ ...(ctx.proposal === undefined ? {} : { proposal: ctx.proposal }),
19
+ startedAt: ctx.startedAt,
20
+ target: { seedUrl: ctx.journey.recording.site, allowlist: [new URL(ctx.journey.recording.site).origin] },
21
+ ...(ctx.engine === undefined ? {} : { engine: ctx.engine }),
22
+ ...(ctx.suite === undefined ? {} : { suite: ctx.suite }),
23
+ ...(ctx.targetBuild === undefined ? {} : { targetBuild: ctx.targetBuild }),
24
+ },
25
+ };
26
+ return redactSecretParams(record, ctx.journey, ctx.params);
27
+ }
28
+ //# sourceMappingURL=journey-result-record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journey-result-record.js","sourceRoot":"","sources":["../src/journey-result-record.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAuB,MAAM,kBAAkB,CAAC;AAGjG,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAsCjD,MAAM,UAAU,mBAAmB,CAAC,CAAmB,EAAE,GAAyB;IAChF,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,KAAK,aAAa,IAAI,CAAC,CAAC,OAAO,KAAK,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5F,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC;IAChC,MAAM,MAAM,GAAwB;QAClC,cAAc,EAAE,uBAAuB,CAAC,CAAC,CAAC,OAAO,CAAC;QAClD,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC;QACpC,MAAM,EAAE;YACN,IAAI,EAAE,SAAS;YACf,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE;YAClC,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,GAAG,CAAC,CAAC,CAAC,OAAO,KAAK,aAAa,IAAI,CAAC,CAAC,OAAO,KAAK,gBAAgB,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3I,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;YACrC,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;YACvC,GAAG,CAAC,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC;YACjE,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,MAAM,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,EAAE;YACxG,GAAG,CAAC,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;YAC3D,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC;YACxD,GAAG,CAAC,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,CAAC;SAC3E;KACF,CAAC;IACF,OAAO,kBAAkB,CAAC,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;AAC7D,CAAC"}
@@ -6,6 +6,7 @@ import { loadCatalog, resolveCatalogDir } from "./catalog-api.js";
6
6
  import { catalogJourney, journeyLinks } from "./catalog.js";
7
7
  import { preApprovalFindings } from "./pre-approval.js";
8
8
  import { jevLayerOf } from "./jev-advisor.js";
9
+ import { checkProposal, readJourneyProposal } from "./journey-proposal-store.js";
9
10
  import { loadTargetsFile, resolveTargetConfig } from "./target-config.js";
10
11
  /**
11
12
  * #432: loads one Journey and everything its review sheet needs — the targets.json safety config of
@@ -34,7 +35,13 @@ export async function reviewJourneyById(journeysDir, id, opts = {}) {
34
35
  ...(opts.readiness === true ? { readiness: true } : {}),
35
36
  ...(opts.jev === undefined ? {} : { jev: opts.jev }),
36
37
  });
38
+ // #453: a pending self-heal proposal, re-checked against the stored Journey (stale, or its proof touched).
39
+ const pending = await readJourneyProposal(journeysDir, id);
40
+ const proposalProblem = pending === null ? null : checkProposal(journey, pending);
37
41
  const review = buildJourneyReview(journey, {
42
+ ...(pending === null
43
+ ? {}
44
+ : { proposal: { proposal: pending, stale: proposalProblem?.code === "E_JOURNEY_PROPOSAL_STALE", ...(proposalProblem === null ? {} : { problem: proposalProblem.message }) } }),
38
45
  ...(safety === undefined ? {} : { safety }),
39
46
  catalog: { links, findings },
40
47
  approvedSnapshot: await readApprovedSnapshot(journeysDir, id),
@@ -1 +1 @@
1
- {"version":3,"file":"journey-review-api.js","sourceRoot":"","sources":["../src/journey-review-api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"journey-review-api.js","sourceRoot":"","sources":["../src/journey-review-api.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import type { JourneyProposal } from "@jevitate/journey";
1
2
  import { type Journey, type JourneyReview, type JourneyCatalogLinks, type Finding } from "@jevitate/journey";
2
3
  import { type SafetyConfig } from "@jevitate/explore";
3
4
  /**
@@ -28,6 +29,12 @@ export interface JourneyReviewContext {
28
29
  readonly links: JourneyCatalogLinks;
29
30
  readonly findings: readonly Finding[];
30
31
  };
32
+ /** #453: the pending self-heal proposal, with whether it is stale and why it cannot be accepted. */
33
+ readonly proposal?: {
34
+ readonly proposal: JourneyProposal;
35
+ readonly stale: boolean;
36
+ readonly problem?: string;
37
+ };
31
38
  }
32
39
  /** #432: the review sheet of one Journey — pure. */
33
40
  export declare function buildJourneyReview(journey: Journey, ctx?: JourneyReviewContext): JourneyReview;