@codyswann/lisa 2.272.0 → 2.274.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 (78) hide show
  1. package/dist/cli/doctor-readiness-blockers.d.ts +154 -0
  2. package/dist/cli/doctor-readiness-blockers.d.ts.map +1 -0
  3. package/dist/cli/doctor-readiness-blockers.js +280 -0
  4. package/dist/cli/doctor-readiness-blockers.js.map +1 -0
  5. package/dist/cli/doctor-readiness-journey-freshness.d.ts +36 -0
  6. package/dist/cli/doctor-readiness-journey-freshness.d.ts.map +1 -0
  7. package/dist/cli/doctor-readiness-journey-freshness.js +166 -0
  8. package/dist/cli/doctor-readiness-journey-freshness.js.map +1 -0
  9. package/dist/cli/doctor-readiness-journey.d.ts +110 -0
  10. package/dist/cli/doctor-readiness-journey.d.ts.map +1 -0
  11. package/dist/cli/doctor-readiness-journey.js +233 -0
  12. package/dist/cli/doctor-readiness-journey.js.map +1 -0
  13. package/dist/cli/doctor-readiness.d.ts +9 -5
  14. package/dist/cli/doctor-readiness.d.ts.map +1 -1
  15. package/dist/cli/doctor-readiness.js +40 -27
  16. package/dist/cli/doctor-readiness.js.map +1 -1
  17. package/dist/cli/doctor-worker-epoch.d.ts +5 -0
  18. package/dist/cli/doctor-worker-epoch.d.ts.map +1 -1
  19. package/dist/cli/doctor-worker-epoch.js +10 -134
  20. package/dist/cli/doctor-worker-epoch.js.map +1 -1
  21. package/dist/cli/doctor-worker-journey.d.ts +118 -0
  22. package/dist/cli/doctor-worker-journey.d.ts.map +1 -0
  23. package/dist/cli/doctor-worker-journey.js +222 -0
  24. package/dist/cli/doctor-worker-journey.js.map +1 -0
  25. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  26. package/dist/core/upstream-evidence-manifest.js +17 -0
  27. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  28. package/package.json +1 -1
  29. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  30. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  31. package/plugins/lisa-agy/plugin.json +1 -1
  32. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  34. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  35. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  41. package/plugins/lisa-expo-agy/plugin.json +1 -1
  42. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  46. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  47. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  51. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  52. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  56. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  57. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  61. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  62. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  66. package/plugins/lisa-rails-agy/plugin.json +1 -1
  67. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  71. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  72. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  76. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  77. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
@@ -0,0 +1,154 @@
1
+ /**
2
+ * The seven-blocker gate and narrowed claim for repository readiness (RRR-5,
3
+ * #1857).
4
+ *
5
+ * RRR-3 (#1855) produces the eight readiness dimensions and persists the report;
6
+ * this module turns their findings into the thing that decides what the
7
+ * repository may be *claimed* to be. It is a pure mapping from dimension
8
+ * findings to the closed set of seven ship blockers (B1..B7 per the
9
+ * `readiness-rubric` rule, RRR-1 #1853), a verdict flip to `NOT_READY` when any
10
+ * blocker stands, and the required operator-language narrowed claim. Blocker 7
11
+ * ("no way to prove the claimed user-visible outcome") is expressed in PRD
12
+ * #1738's claim→evidence shape (`claim-evidence-mapping`) so there is exactly
13
+ * one definition of what establishes a claim across both PRDs.
14
+ *
15
+ * It gates a *claim*, not a *process*: per intake decision O1 nothing here
16
+ * hard-blocks. The verdict ladder is the shipped one — no new enum (F2). The
17
+ * blocker set is closed in v1 and extended only by editing this module and the
18
+ * rule (intake decision O4).
19
+ * @module cli/doctor-readiness-blockers
20
+ */
21
+ /** Verdict ladder reused from the shipped doctor surface (readiness-rubric). */
22
+ export type ReadinessVerdict = "READY" | "READY_WITH_WARNINGS" | "NOT_READY";
23
+ /**
24
+ * The closed v1 set of seven ship blockers (readiness-rubric, RRR-1 #1853).
25
+ * Extending it is a deliberate rule edit, never a configuration surface (intake
26
+ * decision O4). A standing blocker makes the claim "an unattended fleet may run
27
+ * here" false and flips the verdict to `NOT_READY`.
28
+ */
29
+ export declare const SHIP_BLOCKER_IDS: readonly ["B1", "B2", "B3", "B4", "B5", "B6", "B7"];
30
+ /** One of the seven closed-set ship blocker ids. */
31
+ export type ShipBlockerId = (typeof SHIP_BLOCKER_IDS)[number];
32
+ /**
33
+ * The reason blocker 7 renders `SKIP` when PRD #1738's claim→evidence contract
34
+ * is unavailable — a stated reason, never a guessed provability verdict
35
+ * (RRR-5 technical approach point 4).
36
+ */
37
+ export declare const CLAIM_EVIDENCE_UNAVAILABLE_REASON = "claim-evidence contract not yet available";
38
+ /**
39
+ * PRD #1738's claim→evidence shape (`claim-evidence-mapping`), consumed — never
40
+ * redefined — by blocker 7. A claim binds to one boundary and is established
41
+ * only by evidence of a kind that reaches it; a non-empty `not_established`
42
+ * means the user-visible outcome is not provable.
43
+ */
44
+ interface ClaimEvidence {
45
+ readonly claim_id?: string;
46
+ readonly boundary?: string;
47
+ readonly required_evidence_kinds?: readonly string[];
48
+ readonly not_established?: readonly string[];
49
+ readonly not_established_reviewed?: boolean;
50
+ }
51
+ /**
52
+ * A finding attached to a readiness dimension. Loosely typed because findings
53
+ * arrive from the persisted `.lisa/readiness.json` (or a fixture) as JSON. A
54
+ * finding evidences a blocker only when it names one and carries evidence.
55
+ */
56
+ interface ReadinessFinding {
57
+ readonly blocker?: string;
58
+ readonly severity?: string;
59
+ readonly blocking?: boolean;
60
+ readonly invariant_violated?: string;
61
+ readonly evidence?: string;
62
+ readonly claim_evidence?: ClaimEvidence;
63
+ readonly why_proof_missed?: string;
64
+ readonly root_correction?: string;
65
+ readonly machinery_to_remove?: readonly string[];
66
+ }
67
+ /** The minimal dimension shape blocker detection reads (record or fixture). */
68
+ export interface ReadinessDimensionInput {
69
+ readonly id: string;
70
+ readonly status: string;
71
+ readonly findings: readonly unknown[];
72
+ }
73
+ /**
74
+ * A standing ship blocker plus the finding that evidences it. A blocker is
75
+ * never emitted without an evidencing finding — the same discipline
76
+ * `convergent-review` applies to blocking findings without a failure scenario.
77
+ */
78
+ export interface DetectedBlocker {
79
+ readonly id: ShipBlockerId;
80
+ readonly label: string;
81
+ readonly owning_dimensions: readonly string[];
82
+ readonly dimension_id: string;
83
+ readonly invariant_violated: string;
84
+ readonly evidence: string;
85
+ readonly claim_evidence?: ClaimEvidence;
86
+ }
87
+ /** Options shared by the blocker-detection surfaces. */
88
+ export interface BlockerDetectionOptions {
89
+ /**
90
+ * Whether PRD #1738's claim→evidence contract is available. When `false`,
91
+ * blocker 7 degrades to `SKIP` with a stated reason instead of guessing.
92
+ * Defaults to `true` — #1738 has landed.
93
+ */
94
+ readonly claimEvidenceContractAvailable?: boolean;
95
+ }
96
+ /** The provability (blocker 7) sub-assessment for the execution/proof dimension. */
97
+ export interface ProvabilityAssessment {
98
+ readonly status: "STANDS" | "CLEAR" | "SKIP";
99
+ readonly reason?: string;
100
+ readonly finding?: ReadinessFinding;
101
+ }
102
+ /** The blocker-gate assessment `checkRepositoryReadiness` persists. */
103
+ export interface ReadinessAssessment {
104
+ readonly verdict: ReadinessVerdict;
105
+ readonly blockers: readonly DetectedBlocker[];
106
+ readonly narrowed_claim: string | null;
107
+ }
108
+ /**
109
+ * Gate the unattended-operation claim on the seven ship blockers and, when any
110
+ * stands, emit the narrowed claim. This is RRR-5's pure core: it consumes the
111
+ * dimension findings, maps them to blockers (blocker 7 via PRD #1738's evidence
112
+ * shape), flips the verdict to `NOT_READY` when a blocker stands, and produces
113
+ * the operator-language narrowed claim. It gates a *claim*, not a *process* —
114
+ * intake decision O1 keeps every Lisa surface warn-only.
115
+ * @param dimensions - Per-dimension records (from the report or a fixture)
116
+ * @param options - Blocker-detection options (claim→evidence availability)
117
+ * @returns The verdict, standing blockers, and narrowed claim
118
+ */
119
+ export declare function assessReadiness(dimensions: readonly ReadinessDimensionInput[], options?: BlockerDetectionOptions): ReadinessAssessment;
120
+ /**
121
+ * Assess blocker 7 — "no way to prove the claimed user-visible outcome" — from
122
+ * the execution/proof dimension, expressed in PRD #1738's claim→evidence shape.
123
+ * The outcome is unprovable (the blocker STANDS) when a claim's
124
+ * `not_established` list is non-empty; it is CLEAR when that list is empty. If
125
+ * #1738's contract is unavailable, or a blocker-7 finding is not expressed in
126
+ * its shape, the assessment is `SKIP` with a stated reason rather than a guess.
127
+ * @param dimension - The execution/proof dimension record, if present
128
+ * @param options - Blocker-detection options (claim→evidence availability)
129
+ * @returns The provability sub-assessment
130
+ */
131
+ export declare function assessProvabilityBlocker(dimension: ReadinessDimensionInput | undefined, options?: BlockerDetectionOptions): ProvabilityAssessment;
132
+ /**
133
+ * Detect the standing ship blockers from dimension findings — a pure mapping
134
+ * from findings to the closed set B1..B7. Blockers 1-6 stand on an evidencing
135
+ * finding that names them; blocker 7 stands only via {@link assessProvabilityBlocker}
136
+ * over PRD #1738's evidence shape. Emitted in fixed id order for a deterministic
137
+ * report; a blocker with no evidencing finding is absent.
138
+ * @param dimensions - Per-dimension records (from the report or a fixture)
139
+ * @param options - Blocker-detection options (claim→evidence availability)
140
+ * @returns The standing blockers, ordered by id
141
+ */
142
+ export declare function detectShipBlockers(dimensions: readonly ReadinessDimensionInput[], options?: BlockerDetectionOptions): readonly DetectedBlocker[];
143
+ /**
144
+ * Produce the narrowed claim: the required, operator-language statement of what
145
+ * the repository is NOT ready for AND what it IS ready for whenever a blocker
146
+ * stands. Returns `null` when no blocker stands (a `READY`/`READY_WITH_WARNINGS`
147
+ * report carries no narrowed claim). Governed by the `factory-model` rule —
148
+ * written outward for a non-technical operator at the gate.
149
+ * @param blockers - Standing ship blockers
150
+ * @returns The narrowed claim, or null when nothing stands
151
+ */
152
+ export declare function computeNarrowedClaim(blockers: readonly DetectedBlocker[]): string | null;
153
+ export {};
154
+ //# sourceMappingURL=doctor-readiness-blockers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-readiness-blockers.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-readiness-blockers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,280 @@
1
+ /**
2
+ * The seven-blocker gate and narrowed claim for repository readiness (RRR-5,
3
+ * #1857).
4
+ *
5
+ * RRR-3 (#1855) produces the eight readiness dimensions and persists the report;
6
+ * this module turns their findings into the thing that decides what the
7
+ * repository may be *claimed* to be. It is a pure mapping from dimension
8
+ * findings to the closed set of seven ship blockers (B1..B7 per the
9
+ * `readiness-rubric` rule, RRR-1 #1853), a verdict flip to `NOT_READY` when any
10
+ * blocker stands, and the required operator-language narrowed claim. Blocker 7
11
+ * ("no way to prove the claimed user-visible outcome") is expressed in PRD
12
+ * #1738's claim→evidence shape (`claim-evidence-mapping`) so there is exactly
13
+ * one definition of what establishes a claim across both PRDs.
14
+ *
15
+ * It gates a *claim*, not a *process*: per intake decision O1 nothing here
16
+ * hard-blocks. The verdict ladder is the shipped one — no new enum (F2). The
17
+ * blocker set is closed in v1 and extended only by editing this module and the
18
+ * rule (intake decision O4).
19
+ * @module cli/doctor-readiness-blockers
20
+ */
21
+ /** Dimension ids owned by one or more blockers (readiness-rubric table). */
22
+ const DIMENSION_CONTEXT_ROUTING = "context-routing";
23
+ const DIMENSION_DOMAIN_OWNERSHIP = "domain-ownership";
24
+ const DIMENSION_EXECUTION_PROOF = "execution-proof";
25
+ const DIMENSION_FEEDBACK_GUARDRAILS = "feedback-guardrails";
26
+ const DIMENSION_DEPENDENCIES_SUPPLY_CHAIN = "dependencies-supply-chain";
27
+ const DIMENSION_DELIVERY_AUTHORITY = "delivery-authority";
28
+ /**
29
+ * The closed v1 set of seven ship blockers (readiness-rubric, RRR-1 #1853).
30
+ * Extending it is a deliberate rule edit, never a configuration surface (intake
31
+ * decision O4). A standing blocker makes the claim "an unattended fleet may run
32
+ * here" false and flips the verdict to `NOT_READY`.
33
+ */
34
+ export const SHIP_BLOCKER_IDS = [
35
+ "B1",
36
+ "B2",
37
+ "B3",
38
+ "B4",
39
+ "B5",
40
+ "B6",
41
+ "B7",
42
+ ];
43
+ /** The blocker-7 id, named once so the provability path stays legible. */
44
+ const PROVABILITY_BLOCKER_ID = "B7";
45
+ /**
46
+ * The reason blocker 7 renders `SKIP` when PRD #1738's claim→evidence contract
47
+ * is unavailable — a stated reason, never a guessed provability verdict
48
+ * (RRR-5 technical approach point 4).
49
+ */
50
+ export const CLAIM_EVIDENCE_UNAVAILABLE_REASON = "claim-evidence contract not yet available";
51
+ /**
52
+ * Blocker → owning-dimension mapping, cited verbatim from the `readiness-rubric`
53
+ * seven-blocker table (RRR-1). This module consumes that table; it does not
54
+ * redefine the vocabulary.
55
+ */
56
+ const SHIP_BLOCKERS = {
57
+ B1: {
58
+ label: "A realistic path causes silent data loss",
59
+ owning_dimensions: [DIMENSION_DOMAIN_OWNERSHIP],
60
+ },
61
+ B2: {
62
+ label: "A release path bypasses the validated artifact",
63
+ owning_dimensions: [DIMENSION_DELIVERY_AUTHORITY],
64
+ },
65
+ B3: {
66
+ label: "Credentials carry material unintended authority",
67
+ owning_dimensions: [DIMENSION_DELIVERY_AUTHORITY],
68
+ },
69
+ B4: {
70
+ label: "A consequential operation has no gate and no recovery",
71
+ owning_dimensions: [
72
+ DIMENSION_DOMAIN_OWNERSHIP,
73
+ DIMENSION_FEEDBACK_GUARDRAILS,
74
+ ],
75
+ },
76
+ B5: {
77
+ label: "An owned compatibility or security surface has no confidence model",
78
+ owning_dimensions: [DIMENSION_DEPENDENCIES_SUPPLY_CHAIN],
79
+ },
80
+ B6: {
81
+ label: "Documentation overstates enforced guarantees",
82
+ owning_dimensions: [DIMENSION_CONTEXT_ROUTING],
83
+ },
84
+ B7: {
85
+ label: "There is no way to prove the claimed user-visible outcome",
86
+ owning_dimensions: [DIMENSION_EXECUTION_PROOF],
87
+ },
88
+ };
89
+ /**
90
+ * Gate the unattended-operation claim on the seven ship blockers and, when any
91
+ * stands, emit the narrowed claim. This is RRR-5's pure core: it consumes the
92
+ * dimension findings, maps them to blockers (blocker 7 via PRD #1738's evidence
93
+ * shape), flips the verdict to `NOT_READY` when a blocker stands, and produces
94
+ * the operator-language narrowed claim. It gates a *claim*, not a *process* —
95
+ * intake decision O1 keeps every Lisa surface warn-only.
96
+ * @param dimensions - Per-dimension records (from the report or a fixture)
97
+ * @param options - Blocker-detection options (claim→evidence availability)
98
+ * @returns The verdict, standing blockers, and narrowed claim
99
+ */
100
+ export function assessReadiness(dimensions, options = {}) {
101
+ const blockers = detectShipBlockers(dimensions, options);
102
+ return {
103
+ verdict: computeReadinessVerdict(dimensions, blockers),
104
+ blockers,
105
+ narrowed_claim: computeNarrowedClaim(blockers),
106
+ };
107
+ }
108
+ /**
109
+ * Reduce dimensions and standing blockers to the shipped verdict ladder. A
110
+ * standing ship blocker is the only thing that yields `NOT_READY` (the rubric's
111
+ * definition); otherwise any non-clean dimension is `READY_WITH_WARNINGS`, and
112
+ * an all-clean report is `READY`. No new verdict enum (F2).
113
+ * @param dimensions - Per-dimension records
114
+ * @param blockers - Standing ship blockers
115
+ * @returns Overall verdict
116
+ */
117
+ function computeReadinessVerdict(dimensions, blockers) {
118
+ if (blockers.length > 0) {
119
+ return "NOT_READY";
120
+ }
121
+ if (dimensions.some(dimension => dimension.status === "FAIL" || dimension.status === "WARN")) {
122
+ return "READY_WITH_WARNINGS";
123
+ }
124
+ return "READY";
125
+ }
126
+ /**
127
+ * Coerce an untyped persisted finding into the {@link ReadinessFinding} shape.
128
+ * @param value - A raw finding value from JSON
129
+ * @returns The finding as a typed object, or an empty object when not one
130
+ */
131
+ function coerceFinding(value) {
132
+ return typeof value === "object" && value !== null
133
+ ? value
134
+ : {};
135
+ }
136
+ /**
137
+ * Whether a finding carries the evidence a blocker needs to be emitted. A
138
+ * finding that names a blocker but carries no evidence is malformed and must be
139
+ * dropped, never emitted bare.
140
+ * @param finding - The finding to check
141
+ * @returns True when the finding has non-empty evidence text
142
+ */
143
+ function hasEvidence(finding) {
144
+ return typeof finding.evidence === "string" && finding.evidence.trim() !== "";
145
+ }
146
+ /**
147
+ * Locate the first finding across all dimensions that evidences a blocker id.
148
+ * @param dimensions - Per-dimension records
149
+ * @param blockerId - The ship blocker id to evidence
150
+ * @returns The evidencing finding and its dimension id, or undefined
151
+ */
152
+ function locateEvidencingFinding(dimensions, blockerId) {
153
+ for (const dimension of dimensions) {
154
+ for (const raw of dimension.findings) {
155
+ const finding = coerceFinding(raw);
156
+ if (finding.blocker === blockerId && hasEvidence(finding)) {
157
+ return { dimensionId: dimension.id, finding };
158
+ }
159
+ }
160
+ }
161
+ return undefined;
162
+ }
163
+ /**
164
+ * Assess blocker 7 — "no way to prove the claimed user-visible outcome" — from
165
+ * the execution/proof dimension, expressed in PRD #1738's claim→evidence shape.
166
+ * The outcome is unprovable (the blocker STANDS) when a claim's
167
+ * `not_established` list is non-empty; it is CLEAR when that list is empty. If
168
+ * #1738's contract is unavailable, or a blocker-7 finding is not expressed in
169
+ * its shape, the assessment is `SKIP` with a stated reason rather than a guess.
170
+ * @param dimension - The execution/proof dimension record, if present
171
+ * @param options - Blocker-detection options (claim→evidence availability)
172
+ * @returns The provability sub-assessment
173
+ */
174
+ export function assessProvabilityBlocker(dimension, options = {}) {
175
+ if (options.claimEvidenceContractAvailable === false) {
176
+ return { status: "SKIP", reason: CLAIM_EVIDENCE_UNAVAILABLE_REASON };
177
+ }
178
+ const finding = (dimension?.findings ?? [])
179
+ .map(coerceFinding)
180
+ .find(candidate => candidate.blocker === PROVABILITY_BLOCKER_ID);
181
+ if (!finding) {
182
+ return { status: "CLEAR" };
183
+ }
184
+ const claimEvidence = finding.claim_evidence;
185
+ if (!claimEvidence || !Array.isArray(claimEvidence.required_evidence_kinds)) {
186
+ // A blocker-7 finding not expressed in #1738's shape cannot be assessed:
187
+ // degrade to SKIP with a stated reason instead of inventing a stand-in.
188
+ return { status: "SKIP", reason: CLAIM_EVIDENCE_UNAVAILABLE_REASON };
189
+ }
190
+ const notEstablished = Array.isArray(claimEvidence.not_established)
191
+ ? claimEvidence.not_established
192
+ : [];
193
+ return notEstablished.length > 0
194
+ ? { status: "STANDS", finding }
195
+ : { status: "CLEAR" };
196
+ }
197
+ /**
198
+ * Build a {@link DetectedBlocker} from an evidencing finding, stamping the
199
+ * cited label and owning dimensions from the readiness-rubric table.
200
+ * @param id - The ship blocker id
201
+ * @param dimensionId - The dimension the evidencing finding was found in
202
+ * @param finding - The evidencing finding
203
+ * @returns The detected blocker
204
+ */
205
+ function buildDetectedBlocker(id, dimensionId, finding) {
206
+ const spec = SHIP_BLOCKERS[id];
207
+ return {
208
+ id,
209
+ label: spec.label,
210
+ owning_dimensions: spec.owning_dimensions,
211
+ dimension_id: dimensionId,
212
+ invariant_violated: typeof finding.invariant_violated === "string"
213
+ ? finding.invariant_violated
214
+ : "",
215
+ evidence: typeof finding.evidence === "string" ? finding.evidence : "",
216
+ ...(finding.claim_evidence
217
+ ? { claim_evidence: finding.claim_evidence }
218
+ : {}),
219
+ };
220
+ }
221
+ /**
222
+ * Resolve the standing blocker (if any) for a single blocker id. Blocker 7
223
+ * routes through {@link assessProvabilityBlocker}; blockers 1-6 stand on the
224
+ * first evidencing finding that names them.
225
+ * @param dimensions - Per-dimension records
226
+ * @param id - The ship blocker id to resolve
227
+ * @param options - Blocker-detection options (claim→evidence availability)
228
+ * @returns A single-element array with the standing blocker, or an empty array
229
+ */
230
+ function resolveBlocker(dimensions, id, options) {
231
+ if (id === PROVABILITY_BLOCKER_ID) {
232
+ const provability = assessProvabilityBlocker(dimensions.find(dimension => dimension.id === DIMENSION_EXECUTION_PROOF), options);
233
+ return provability.status === "STANDS" && provability.finding
234
+ ? [
235
+ buildDetectedBlocker(id, DIMENSION_EXECUTION_PROOF, provability.finding),
236
+ ]
237
+ : [];
238
+ }
239
+ const located = locateEvidencingFinding(dimensions, id);
240
+ return located
241
+ ? [buildDetectedBlocker(id, located.dimensionId, located.finding)]
242
+ : [];
243
+ }
244
+ /**
245
+ * Detect the standing ship blockers from dimension findings — a pure mapping
246
+ * from findings to the closed set B1..B7. Blockers 1-6 stand on an evidencing
247
+ * finding that names them; blocker 7 stands only via {@link assessProvabilityBlocker}
248
+ * over PRD #1738's evidence shape. Emitted in fixed id order for a deterministic
249
+ * report; a blocker with no evidencing finding is absent.
250
+ * @param dimensions - Per-dimension records (from the report or a fixture)
251
+ * @param options - Blocker-detection options (claim→evidence availability)
252
+ * @returns The standing blockers, ordered by id
253
+ */
254
+ export function detectShipBlockers(dimensions, options = {}) {
255
+ return SHIP_BLOCKER_IDS.flatMap(id => resolveBlocker(dimensions, id, options));
256
+ }
257
+ /**
258
+ * Produce the narrowed claim: the required, operator-language statement of what
259
+ * the repository is NOT ready for AND what it IS ready for whenever a blocker
260
+ * stands. Returns `null` when no blocker stands (a `READY`/`READY_WITH_WARNINGS`
261
+ * report carries no narrowed claim). Governed by the `factory-model` rule —
262
+ * written outward for a non-technical operator at the gate.
263
+ * @param blockers - Standing ship blockers
264
+ * @returns The narrowed claim, or null when nothing stands
265
+ */
266
+ export function computeNarrowedClaim(blockers) {
267
+ if (blockers.length === 0) {
268
+ return null;
269
+ }
270
+ const list = blockers
271
+ .map(blocker => `${blocker.id} — ${blocker.label}`)
272
+ .join("; ");
273
+ const standing = blockers.length === 1
274
+ ? "1 ship blocker stands"
275
+ : `${blockers.length} ship blockers stand`;
276
+ return (`This repository is NOT ready for unattended fleet operation because ` +
277
+ `${standing}: ${list}. It IS ready for supervised, single-ticket agent ` +
278
+ `work with a human reviewing and approving each change before it ships.`);
279
+ }
280
+ //# sourceMappingURL=doctor-readiness-blockers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-readiness-blockers.js","sourceRoot":"","sources":["../../src/cli/doctor-readiness-blockers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { WorkerJourneyEvidence } from "./doctor-worker-journey.js";
2
+ /** The per-environment mutation policy from `.lisa.config.json` `exploration`. */
3
+ export type MutationPolicy = "forbidden" | "read-only" | "full";
4
+ /**
5
+ * Resolve the per-environment mutation policy from an already-parsed config,
6
+ * mirroring `lisa-use-the-product`'s gate: default `read-only`; a production-
7
+ * named env defaults to `forbidden` and may only be raised to `full` with a
8
+ * written `prodMutationAck`.
9
+ * @param config - Parsed `.lisa.config.json` contents
10
+ * @returns The resolved mutation policy
11
+ */
12
+ export declare function resolveMutationPolicy(config: unknown): MutationPolicy;
13
+ /**
14
+ * Resolve the mutation policy for a path by reading `.lisa.config.json`.
15
+ * @param targetPath - Project path to read config from
16
+ * @returns The resolved mutation policy (read-only when unreadable)
17
+ */
18
+ export declare function resolveMutationPolicyForPath(targetPath: string): Promise<MutationPolicy>;
19
+ /**
20
+ * Determine whether recorded qualification evidence is fresh enough to reuse:
21
+ * the epoch is in sync (no drift), evidence is present, and it is newer than the
22
+ * current artifact head. When the artifact head cannot be resolved, freshness
23
+ * falls back to "no drift and evidence present" (the shipped #1742 notion).
24
+ * @param evidence - Resolved worker journey evidence
25
+ * @param targetPath - Project path (for git artifact-head resolution)
26
+ * @param artifactHeadOverride - Injected artifact head (skips git resolution)
27
+ * @returns True when the evidence may be reused without a new journey
28
+ */
29
+ export declare function isQualificationEvidenceFresh(evidence: WorkerJourneyEvidence, targetPath: string, artifactHeadOverride?: Date | null): Promise<boolean>;
30
+ /**
31
+ * Narrow unknown JSON values to plain records.
32
+ * @param value - Value to inspect
33
+ * @returns True for plain object records
34
+ */
35
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
36
+ //# sourceMappingURL=doctor-readiness-journey-freshness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-readiness-journey-freshness.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-readiness-journey-freshness.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAIxE,kFAAkF;AAClF,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,WAAW,GAAG,MAAM,CAAC;AAEhE;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,GAAG,cAAc,CAsBrE;AAED;;;;GAIG;AACH,wBAAsB,4BAA4B,CAChD,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,CAAC,CAYzB;AAED;;;;;;;;;GASG;AACH,wBAAsB,4BAA4B,CAChD,QAAQ,EAAE,qBAAqB,EAC/B,UAAU,EAAE,MAAM,EAClB,oBAAoB,CAAC,EAAE,IAAI,GAAG,IAAI,GACjC,OAAO,CAAC,OAAO,CAAC,CAclB;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE"}
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Freshness and mutation-policy helpers for the readiness journey wiring
3
+ * (RRR-6, #1858).
4
+ *
5
+ * Split out of `doctor-readiness-journey.ts` for file-size hygiene: this module
6
+ * owns the two gates that decide *whether* a representative journey runs — the
7
+ * per-environment mutation policy read from `.lisa.config.json` (mirroring
8
+ * `lisa-use-the-product`'s gate, which readiness never overrides) and the
9
+ * freshness contract that decides when recorded qualification evidence may be
10
+ * reused instead of re-running a journey (same worker signature, evidence newer
11
+ * than the current artifact head).
12
+ * @module cli/doctor-readiness-journey-freshness
13
+ */
14
+ import { existsSync } from "node:fs";
15
+ import { readFile, stat } from "node:fs/promises";
16
+ import * as path from "node:path";
17
+ import { promisify } from "node:util";
18
+ import { execFile } from "node:child_process";
19
+ const execFileAsync = promisify(execFile);
20
+ /**
21
+ * Resolve the per-environment mutation policy from an already-parsed config,
22
+ * mirroring `lisa-use-the-product`'s gate: default `read-only`; a production-
23
+ * named env defaults to `forbidden` and may only be raised to `full` with a
24
+ * written `prodMutationAck`.
25
+ * @param config - Parsed `.lisa.config.json` contents
26
+ * @returns The resolved mutation policy
27
+ */
28
+ export function resolveMutationPolicy(config) {
29
+ const exploration = isRecord(config) ? config.exploration : undefined;
30
+ if (!isRecord(exploration)) {
31
+ return "read-only";
32
+ }
33
+ const environments = isRecord(exploration.environments)
34
+ ? exploration.environments
35
+ : {};
36
+ const envName = typeof exploration.default === "string" ? exploration.default : "default";
37
+ const envConfig = isRecord(environments[envName])
38
+ ? environments[envName]
39
+ : undefined;
40
+ const level = envConfig && typeof envConfig.mutation === "string"
41
+ ? envConfig.mutation
42
+ : undefined;
43
+ const ack = envConfig && typeof envConfig.prodMutationAck === "string"
44
+ ? envConfig.prodMutationAck.trim() !== ""
45
+ : false;
46
+ return normalizeMutationLevel(envName, level, ack);
47
+ }
48
+ /**
49
+ * Resolve the mutation policy for a path by reading `.lisa.config.json`.
50
+ * @param targetPath - Project path to read config from
51
+ * @returns The resolved mutation policy (read-only when unreadable)
52
+ */
53
+ export async function resolveMutationPolicyForPath(targetPath) {
54
+ const configPath = path.join(targetPath, ".lisa.config.json");
55
+ if (!existsSync(configPath)) {
56
+ return "read-only";
57
+ }
58
+ try {
59
+ return resolveMutationPolicy(JSON.parse(await readFile(configPath, "utf8")));
60
+ }
61
+ catch {
62
+ return "read-only";
63
+ }
64
+ }
65
+ /**
66
+ * Determine whether recorded qualification evidence is fresh enough to reuse:
67
+ * the epoch is in sync (no drift), evidence is present, and it is newer than the
68
+ * current artifact head. When the artifact head cannot be resolved, freshness
69
+ * falls back to "no drift and evidence present" (the shipped #1742 notion).
70
+ * @param evidence - Resolved worker journey evidence
71
+ * @param targetPath - Project path (for git artifact-head resolution)
72
+ * @param artifactHeadOverride - Injected artifact head (skips git resolution)
73
+ * @returns True when the evidence may be reused without a new journey
74
+ */
75
+ export async function isQualificationEvidenceFresh(evidence, targetPath, artifactHeadOverride) {
76
+ if (evidence.evidence === null || evidence.drift.length > 0) {
77
+ return false;
78
+ }
79
+ const artifactHead = artifactHeadOverride ?? (await resolveArtifactHead(targetPath));
80
+ if (artifactHead === null) {
81
+ // Cannot compare to the artifact head: fall back to the #1742 notion.
82
+ return true;
83
+ }
84
+ const evidenceTime = await resolveEvidenceTimestamp(evidence);
85
+ return (evidenceTime !== null && evidenceTime.getTime() >= artifactHead.getTime());
86
+ }
87
+ /**
88
+ * Narrow unknown JSON values to plain records.
89
+ * @param value - Value to inspect
90
+ * @returns True for plain object records
91
+ */
92
+ export function isRecord(value) {
93
+ return typeof value === "object" && value !== null && !Array.isArray(value);
94
+ }
95
+ /**
96
+ * Normalize a raw mutation level for a resolved environment, applying the
97
+ * production carve-out.
98
+ * @param envName - Resolved environment name
99
+ * @param level - Raw mutation level from config
100
+ * @param hasAck - Whether a non-empty prodMutationAck is present
101
+ * @returns The normalized mutation policy
102
+ */
103
+ function normalizeMutationLevel(envName, level, hasAck) {
104
+ const isProduction = /^prod(uction)?$/iu.test(envName);
105
+ if (isProduction) {
106
+ if (level === "full" && hasAck) {
107
+ return "full";
108
+ }
109
+ return "forbidden";
110
+ }
111
+ if (level === "forbidden" || level === "full" || level === "read-only") {
112
+ return level;
113
+ }
114
+ return "read-only";
115
+ }
116
+ /**
117
+ * Resolve the artifact-head timestamp from the git HEAD commit time.
118
+ * @param targetPath - Project path to inspect
119
+ * @returns The HEAD commit time, or null when it cannot be resolved
120
+ */
121
+ async function resolveArtifactHead(targetPath) {
122
+ try {
123
+ const { stdout } = await execFileAsync("git", ["-C", targetPath, "log", "-1", "--format=%cI"], { encoding: "utf8" });
124
+ const parsed = new Date(stdout.trim());
125
+ return Number.isNaN(parsed.getTime()) ? null : parsed;
126
+ }
127
+ catch {
128
+ return null;
129
+ }
130
+ }
131
+ /**
132
+ * Resolve a timestamp for recorded qualification evidence: an explicit
133
+ * `qualifiedAt`/`recordedAt` on the worker entry, else the mtime of
134
+ * `.lisa/worker-config.json`.
135
+ * @param evidence - Resolved worker journey evidence
136
+ * @returns The evidence timestamp, or null when it cannot be resolved
137
+ */
138
+ async function resolveEvidenceTimestamp(evidence) {
139
+ const explicit = readTimestampField(evidence.matched);
140
+ if (explicit !== null) {
141
+ return explicit;
142
+ }
143
+ try {
144
+ return (await stat(evidence.recordPath)).mtime;
145
+ }
146
+ catch {
147
+ return null;
148
+ }
149
+ }
150
+ /**
151
+ * Read an explicit `qualifiedAt`/`recordedAt` timestamp from a worker entry.
152
+ * @param matched - The matched worker entry, if any
153
+ * @returns The parsed timestamp, or null when absent/unparseable
154
+ */
155
+ function readTimestampField(matched) {
156
+ if (!isRecord(matched)) {
157
+ return null;
158
+ }
159
+ const raw = matched.qualifiedAt ?? matched.recordedAt;
160
+ if (typeof raw !== "string") {
161
+ return null;
162
+ }
163
+ const parsed = new Date(raw);
164
+ return Number.isNaN(parsed.getTime()) ? null : parsed;
165
+ }
166
+ //# sourceMappingURL=doctor-readiness-journey-freshness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-readiness-journey-freshness.js","sourceRoot":"","sources":["../../src/cli/doctor-readiness-journey-freshness.ts"],"names":[],"mappings":"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"}