@codyswann/lisa 2.281.0 → 2.282.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.
- package/all/copy-contents/gitignore +2 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +20 -15
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/standards-proof-cmd.d.ts +7 -0
- package/dist/cli/standards-proof-cmd.d.ts.map +1 -0
- package/dist/cli/standards-proof-cmd.js +14 -0
- package/dist/cli/standards-proof-cmd.js.map +1 -0
- package/dist/cli/ui-setup-readiness-local.d.ts +2 -1
- package/dist/cli/ui-setup-readiness-local.d.ts.map +1 -1
- package/dist/cli/ui-setup-readiness-local.js +4 -2
- package/dist/cli/ui-setup-readiness-local.js.map +1 -1
- package/dist/cli/ui-setup-readiness.d.ts.map +1 -1
- package/dist/cli/ui-setup-readiness.js +3 -2
- package/dist/cli/ui-setup-readiness.js.map +1 -1
- package/dist/cli/update-check-hook.d.ts +19 -0
- package/dist/cli/update-check-hook.d.ts.map +1 -0
- package/dist/cli/update-check-hook.js +36 -0
- package/dist/cli/update-check-hook.js.map +1 -0
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +37 -6
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/standards/capture.d.ts +41 -0
- package/dist/standards/capture.d.ts.map +1 -0
- package/dist/standards/capture.js +196 -0
- package/dist/standards/capture.js.map +1 -0
- package/dist/standards/contract.d.ts +45 -0
- package/dist/standards/contract.d.ts.map +1 -0
- package/dist/standards/contract.js +175 -0
- package/dist/standards/contract.js.map +1 -0
- package/dist/standards/git-state.d.ts +30 -0
- package/dist/standards/git-state.d.ts.map +1 -0
- package/dist/standards/git-state.js +179 -0
- package/dist/standards/git-state.js.map +1 -0
- package/dist/standards/index.d.ts +8 -0
- package/dist/standards/index.d.ts.map +1 -0
- package/dist/standards/index.js +8 -0
- package/dist/standards/index.js.map +1 -0
- package/dist/standards/readiness.d.ts +11 -0
- package/dist/standards/readiness.d.ts.map +1 -0
- package/dist/standards/readiness.js +170 -0
- package/dist/standards/readiness.js.map +1 -0
- package/dist/standards/registry.d.ts +42 -0
- package/dist/standards/registry.d.ts.map +1 -0
- package/dist/standards/registry.js +330 -0
- package/dist/standards/registry.js.map +1 -0
- package/dist/standards/storage-reasons.d.ts +15 -0
- package/dist/standards/storage-reasons.d.ts.map +1 -0
- package/dist/standards/storage-reasons.js +48 -0
- package/dist/standards/storage-reasons.js.map +1 -0
- package/dist/standards/storage.d.ts +36 -0
- package/dist/standards/storage.d.ts.map +1 -0
- package/dist/standards/storage.js +330 -0
- package/dist/standards/storage.js.map +1 -0
- package/dist/standards/test-evidence.d.ts +19 -0
- package/dist/standards/test-evidence.d.ts.map +1 -0
- package/dist/standards/test-evidence.js +195 -0
- package/dist/standards/test-evidence.js.map +1 -0
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-doctor/SKILL.md +21 -3
- package/plugins/lisa/scripts/doctor-report.mjs +266 -22
- package/plugins/lisa/skills/lisa-doctor/SKILL.md +21 -3
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/doctor-report.mjs +266 -22
- package/plugins/lisa-agy/skills/lisa-doctor/SKILL.md +21 -3
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/scripts/doctor-report.mjs +266 -22
- package/plugins/lisa-copilot/skills/lisa-doctor/SKILL.md +21 -3
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/scripts/doctor-report.mjs +266 -22
- package/plugins/lisa-cursor/skills/lisa-doctor/SKILL.md +21 -3
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/scripts/doctor-report.mjs +266 -22
- package/plugins/src/base/skills/lisa-doctor/SKILL.md +21 -3
- package/rails/copy-overwrite/lefthook.yml +9 -11
- package/rails/copy-overwrite/scripts/lisa-clean-git-env.sh +13 -0
- package/typescript/copy-contents/.husky/pre-push +9 -0
- package/ui/index.html +3 -2
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* repo adds real readiness probes. Keep this file dependency-free so future
|
|
7
7
|
* doctor scripts can reuse it from plugin distributions and downstream repos.
|
|
8
8
|
*/
|
|
9
|
-
import { existsSync } from "node:fs";
|
|
9
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
10
10
|
import path from "node:path";
|
|
11
11
|
import process from "node:process";
|
|
12
12
|
|
|
@@ -121,9 +121,12 @@ export function createPluginSyncDoctorGroup(root = process.cwd()) {
|
|
|
121
121
|
/**
|
|
122
122
|
* The eight repository-readiness ownership dimensions, in fixed render order,
|
|
123
123
|
* defined once by the `readiness-rubric` rule. This group consumes that rubric;
|
|
124
|
-
* it does not redefine the vocabulary. Evidence gathering
|
|
125
|
-
*
|
|
126
|
-
*
|
|
124
|
+
* it does not redefine the vocabulary. Evidence gathering lives in the
|
|
125
|
+
* TypeScript CLI producers, which persist the authoritative per-dimension
|
|
126
|
+
* result to `.lisa/readiness.json`; this surface projects that result. When no
|
|
127
|
+
* usable report is on disk, each dimension renders `SKIP` carrying the reason
|
|
128
|
+
* it was not assessed — reported, never silently omitted, per the shipped
|
|
129
|
+
* contract.
|
|
127
130
|
* @type {readonly { id: string, question: string, skipReason: string }[]}
|
|
128
131
|
*/
|
|
129
132
|
const REPOSITORY_READINESS_DIMENSIONS = [
|
|
@@ -132,81 +135,322 @@ const REPOSITORY_READINESS_DIMENSIONS = [
|
|
|
132
135
|
question:
|
|
133
136
|
"Can an agent recover the real job from what is written down (integration-access-layer, wiki-knowledge-source, config-resolution)?",
|
|
134
137
|
skipReason:
|
|
135
|
-
"Context/routing evidence is assessed by the agent-ready wiring (RRR-4, #1856); no readiness
|
|
138
|
+
"Context/routing evidence is assessed by the agent-ready wiring (RRR-4, #1856); no usable `.lisa/readiness.json` was found, so this dimension was not assessed here. Run `lisa doctor --offline --readiness` to produce the report, then re-run doctor.",
|
|
136
139
|
},
|
|
137
140
|
{
|
|
138
141
|
id: "capabilities-tools",
|
|
139
142
|
question:
|
|
140
143
|
"Is every tool the work needs provably reachable, not merely installed (tool-access-gate)?",
|
|
141
144
|
skipReason:
|
|
142
|
-
"
|
|
145
|
+
"Tool reachability needs a live read-only probe and can never be established from an offline read: the `tool-access-gate` rule names presence — a binary on `PATH`, a dependency in a manifest, a configured MCP server — as the anti-pattern, because it proves a tool was declared, never that a request through it succeeded. This dimension is therefore a deliberate, permanent reasoned SKIP on an offline pass rather than a not-yet-wired one; only a run that actually exercises the tools can answer it.",
|
|
143
146
|
},
|
|
144
147
|
{
|
|
145
148
|
id: "domain-ownership",
|
|
146
149
|
question:
|
|
147
150
|
"Are the business rules, glossary, and danger zones owned and written down (agent-ready domain phase wiki pages)?",
|
|
148
151
|
skipReason:
|
|
149
|
-
"Domain-ownership evidence sources from agent-ready's danger-zone wiki pages, read by RRR-4 (#1856); no readiness
|
|
152
|
+
"Domain-ownership evidence sources from agent-ready's danger-zone wiki pages, read by RRR-4 (#1856); no usable `.lisa/readiness.json` was found, so this dimension was not assessed here. Run `lisa doctor --offline --readiness` to produce the report, then re-run doctor.",
|
|
150
153
|
},
|
|
151
154
|
{
|
|
152
155
|
id: "execution-proof",
|
|
153
156
|
question:
|
|
154
157
|
"Can the claimed user-visible outcome be proved by running the system (verification, empirical-inquiry, claim-evidence-mapping)?",
|
|
155
158
|
skipReason:
|
|
156
|
-
"Execution/proof consumes qualification evidence and representative journeys wired by RRR-6 (#1858); no readiness
|
|
159
|
+
"Execution/proof consumes qualification evidence and representative journeys wired by RRR-6 (#1858); no usable `.lisa/readiness.json` was found, so this dimension was not assessed here. Run `lisa doctor --offline --readiness` to produce the report, then re-run doctor.",
|
|
157
160
|
},
|
|
158
161
|
{
|
|
159
162
|
id: "feedback-guardrails",
|
|
160
163
|
question:
|
|
161
164
|
"Does a failing loop produce a named outcome and a runbook (automation-runbook-contract, observability-audit)?",
|
|
162
165
|
skipReason:
|
|
163
|
-
"Feedback/guardrails evidence is assessed by RRR-4 (#1856); no readiness
|
|
166
|
+
"Feedback/guardrails evidence is assessed by RRR-4 (#1856); no usable `.lisa/readiness.json` was found, so this dimension was not assessed here. Run `lisa doctor --offline --readiness` to produce the report, then re-run doctor.",
|
|
164
167
|
},
|
|
165
168
|
{
|
|
166
169
|
id: "dependencies-supply-chain",
|
|
167
170
|
question:
|
|
168
171
|
"Is there a confidence model for what the repo depends on (security-audit-handling)?",
|
|
169
172
|
skipReason:
|
|
170
|
-
"Dependencies/supply-chain evidence is assessed by RRR-4 (#1856); no readiness
|
|
173
|
+
"Dependencies/supply-chain evidence is assessed by RRR-4 (#1856); no usable `.lisa/readiness.json` was found, so this dimension was not assessed here. Run `lisa doctor --offline --readiness` to produce the report, then re-run doctor.",
|
|
171
174
|
},
|
|
172
175
|
{
|
|
173
176
|
id: "delivery-authority",
|
|
174
177
|
question:
|
|
175
178
|
"Does the thing that ships equal the thing that was validated, and does the shipping credential carry only the authority it needs (claim-archaeology, security-audit-handling)?",
|
|
176
179
|
skipReason:
|
|
177
|
-
"Delivery/authority blockers are populated by the blocker gate in RRR-5 (#1857); no readiness
|
|
180
|
+
"Delivery/authority blockers are populated by the blocker gate in RRR-5 (#1857); no usable `.lisa/readiness.json` was found, so this dimension was not assessed here. Run `lisa doctor --offline --readiness` to produce the report, then re-run doctor.",
|
|
178
181
|
},
|
|
179
182
|
{
|
|
180
183
|
id: "proportionality",
|
|
181
184
|
question:
|
|
182
185
|
"Is the machinery proportional to the job, or is there scaffolding to subtract (repo-scope-split, #1742 subtraction candidates)?",
|
|
183
186
|
skipReason:
|
|
184
|
-
"Proportionality reuses the scaffolding-subtraction candidates surfaced by the journey work (RRR-6, #1858); no readiness
|
|
187
|
+
"Proportionality reuses the scaffolding-subtraction candidates surfaced by the journey work (RRR-6, #1858); no usable `.lisa/readiness.json` was found, so this dimension was not assessed here. Run `lisa doctor --offline --readiness` to produce the report, then re-run doctor.",
|
|
185
188
|
},
|
|
186
189
|
];
|
|
187
190
|
|
|
191
|
+
/**
|
|
192
|
+
* `schema_version` this surface knows how to project. A report stamped with any
|
|
193
|
+
* other version is treated as unreadable rather than guessed at: the whole point
|
|
194
|
+
* of the version stamp is that a reader may not invent a mapping it was never
|
|
195
|
+
* told about.
|
|
196
|
+
*/
|
|
197
|
+
const READINESS_REPORT_SCHEMA_VERSION = 1;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Longest operator-facing summary projected out of a CLI finding. Findings can
|
|
201
|
+
* carry kilobytes of concatenated evidence (every offending workflow job, for
|
|
202
|
+
* example); the doctor line is a headline, and the full text stays available in
|
|
203
|
+
* `.lisa/readiness.json`.
|
|
204
|
+
*/
|
|
205
|
+
const READINESS_SUMMARY_MAX_LENGTH = 320;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Resolve the single location the CLI writes the readiness report to. This
|
|
209
|
+
* mirrors the CLI's `resolveReadinessReportPath` so relocating the artifact
|
|
210
|
+
* stays a two-line change across both scorers.
|
|
211
|
+
* @param {string} repoRoot
|
|
212
|
+
* @returns {string}
|
|
213
|
+
*/
|
|
214
|
+
function resolveReadinessReportPath(repoRoot) {
|
|
215
|
+
return path.join(repoRoot, ".lisa", "readiness.json");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Read the CLI-authored readiness report, or `null` when there is nothing
|
|
220
|
+
* trustworthy to project. Absence, unparseable JSON, an unknown
|
|
221
|
+
* `schema_version`, and a missing `dimensions` array are all the same answer —
|
|
222
|
+
* "the CLI readiness pass has not produced a result this surface can read" —
|
|
223
|
+
* and none of them is evidence about the repository itself.
|
|
224
|
+
* @param {string} repoRoot
|
|
225
|
+
* @returns {{ dimensions: readonly Record<string, unknown>[], blockers: readonly Record<string, unknown>[], narrowedClaim: string, provenance: string } | null}
|
|
226
|
+
*/
|
|
227
|
+
function readReadinessReport(repoRoot) {
|
|
228
|
+
const reportPath = resolveReadinessReportPath(repoRoot);
|
|
229
|
+
if (!existsSync(reportPath)) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
const parsed = JSON.parse(readFileSync(reportPath, "utf8"));
|
|
234
|
+
if (
|
|
235
|
+
parsed === null ||
|
|
236
|
+
typeof parsed !== "object" ||
|
|
237
|
+
parsed.schema_version !== READINESS_REPORT_SCHEMA_VERSION ||
|
|
238
|
+
!Array.isArray(parsed.dimensions)
|
|
239
|
+
) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
dimensions: parsed.dimensions,
|
|
244
|
+
blockers: Array.isArray(parsed.blockers) ? parsed.blockers : [],
|
|
245
|
+
// The narrowed claim is the sentence `readiness-rubric` requires whenever
|
|
246
|
+
// a blocker stands ("...IS ready for supervised, single-ticket agent
|
|
247
|
+
// work..."). Dropping it would hand the agent operator a bare NOT_READY
|
|
248
|
+
// where the CLI hands them the fallback they can actually act on.
|
|
249
|
+
narrowedClaim:
|
|
250
|
+
typeof parsed.narrowed_claim === "string" ? parsed.narrowed_claim : "",
|
|
251
|
+
// Stamped into every projected check so a months-old report cannot read
|
|
252
|
+
// as current truth: this surface reflects a file, not a live assessment.
|
|
253
|
+
provenance: describeReportProvenance(parsed),
|
|
254
|
+
};
|
|
255
|
+
} catch {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Describe when the projected report was produced and by which Lisa version.
|
|
262
|
+
* @param {Record<string, unknown>} parsed
|
|
263
|
+
* @returns {string}
|
|
264
|
+
*/
|
|
265
|
+
function describeReportProvenance(parsed) {
|
|
266
|
+
const generatedAt =
|
|
267
|
+
typeof parsed.generated_at === "string" && parsed.generated_at.length > 0
|
|
268
|
+
? parsed.generated_at
|
|
269
|
+
: "an unrecorded time";
|
|
270
|
+
const version =
|
|
271
|
+
typeof parsed.lisa_version === "string" && parsed.lisa_version.length > 0
|
|
272
|
+
? ` by Lisa ${parsed.lisa_version}`
|
|
273
|
+
: "";
|
|
274
|
+
return `generated ${generatedAt}${version}`;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Pull the operator-facing sentence out of a dimension's findings. The CLI
|
|
279
|
+
* producers write human text under `evidence` (an assessed dimension),
|
|
280
|
+
* `reason` (a deliberately-unassessed one), or `observation` (a neutral note);
|
|
281
|
+
* the rest of the finding is machine bookkeeping the blocker engine owns.
|
|
282
|
+
* Reading only two of the three would print the dimension's question under a
|
|
283
|
+
* FAIL, which reads as "nothing to report" exactly when there is most to say.
|
|
284
|
+
* @param {readonly unknown[]} findings
|
|
285
|
+
* @returns {string}
|
|
286
|
+
*/
|
|
287
|
+
function summarizeReadinessFindings(findings) {
|
|
288
|
+
const sentences = findings
|
|
289
|
+
.filter(finding => finding !== null && typeof finding === "object")
|
|
290
|
+
.flatMap(finding => [finding.evidence, finding.reason, finding.observation])
|
|
291
|
+
.filter(text => typeof text === "string" && text.trim().length > 0)
|
|
292
|
+
.map(text => text.trim());
|
|
293
|
+
if (sentences.length === 0) {
|
|
294
|
+
return "";
|
|
295
|
+
}
|
|
296
|
+
const joined = sentences.join(" ");
|
|
297
|
+
return joined.length > READINESS_SUMMARY_MAX_LENGTH
|
|
298
|
+
? `${joined.slice(0, READINESS_SUMMARY_MAX_LENGTH - 1).trimEnd()}…`
|
|
299
|
+
: joined;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Carry the CLI's not-established caveat onto every unassessed dimension. The
|
|
304
|
+
* CLI headline says it out loud — "N of 8 dimensions were never assessed, so
|
|
305
|
+
* this cannot say whether an unattended fleet may operate here" — and a reader
|
|
306
|
+
* who sees a lone `SKIP` line without it can mistake silence for a clean bill
|
|
307
|
+
* of health on that dimension.
|
|
308
|
+
* @param {DoctorCheck} check
|
|
309
|
+
* @param {readonly DoctorCheck[]} checks
|
|
310
|
+
* @returns {DoctorCheck}
|
|
311
|
+
*/
|
|
312
|
+
function applyNotEstablishedCaveat(check, checks) {
|
|
313
|
+
const unassessed = checks.filter(entry => entry.status === "SKIP").length;
|
|
314
|
+
if (check.status !== "SKIP" || unassessed === 0) {
|
|
315
|
+
return check;
|
|
316
|
+
}
|
|
317
|
+
return {
|
|
318
|
+
...check,
|
|
319
|
+
observed: `${check.observed} NOT ESTABLISHED: ${unassessed} of ${checks.length} dimensions were never assessed, so this cannot say whether an unattended fleet may operate here.`,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Decide whether a standing blocker was evidenced in this dimension.
|
|
325
|
+
*
|
|
326
|
+
* `dimension_id` is the authority: the CLI's `buildDetectedBlocker` documents it
|
|
327
|
+
* as "the dimension the evidencing finding was found in". `owning_dimensions` is
|
|
328
|
+
* the blocker's static spec and can name dimensions that did NOT evidence it —
|
|
329
|
+
* B4 is specced as `[domain-ownership, feedback-guardrails]` while only the
|
|
330
|
+
* guardrails producer ever emits the B4 finding. Escalating on the spec would
|
|
331
|
+
* stamp `FAIL` on a dimension whose own recorded evidence says it found nothing,
|
|
332
|
+
* contradicting both that evidence and the CLI's per-dimension result. The spec
|
|
333
|
+
* is used only as a fallback, for a blocker record carrying no attribution.
|
|
334
|
+
* @param {Record<string, unknown>} blocker
|
|
335
|
+
* @param {{ id: string }} dimension
|
|
336
|
+
* @returns {boolean}
|
|
337
|
+
*/
|
|
338
|
+
function blockerEvidencedIn(blocker, dimension) {
|
|
339
|
+
if (
|
|
340
|
+
typeof blocker.dimension_id === "string" &&
|
|
341
|
+
blocker.dimension_id.length > 0
|
|
342
|
+
) {
|
|
343
|
+
return blocker.dimension_id === dimension.id;
|
|
344
|
+
}
|
|
345
|
+
return (
|
|
346
|
+
Array.isArray(blocker.owning_dimensions) &&
|
|
347
|
+
blocker.owning_dimensions.includes(dimension.id)
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Collect the ids of the standing ship blockers evidenced in a dimension.
|
|
353
|
+
* Blockers with no usable id are dropped rather than stringified, so a malformed
|
|
354
|
+
* record can never render "Standing ship blocker(s): undefined" at an operator.
|
|
355
|
+
* @param {{ id: string }} dimension
|
|
356
|
+
* @param {{ blockers: readonly Record<string, unknown>[] }} report
|
|
357
|
+
* @returns {readonly string[]}
|
|
358
|
+
*/
|
|
359
|
+
function standingBlockerIds(dimension, report) {
|
|
360
|
+
return report.blockers
|
|
361
|
+
.filter(
|
|
362
|
+
blocker =>
|
|
363
|
+
blocker !== null &&
|
|
364
|
+
typeof blocker === "object" &&
|
|
365
|
+
blockerEvidencedIn(blocker, dimension)
|
|
366
|
+
)
|
|
367
|
+
.map(blocker => blocker.id)
|
|
368
|
+
.filter(id => typeof id === "string" && id.length > 0)
|
|
369
|
+
.filter((id, index, ids) => ids.indexOf(id) === index);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Project one CLI dimension record into this surface's check shape, or `null`
|
|
374
|
+
* when the record is absent or carries a status outside the shipped vocabulary
|
|
375
|
+
* (the caller then falls back to the reasoned SKIP).
|
|
376
|
+
* @param {{ id: string, question: string, skipReason: string }} dimension
|
|
377
|
+
* @param {{ dimensions: readonly Record<string, unknown>[], blockers: readonly Record<string, unknown>[], narrowedClaim: string, provenance: string }} report
|
|
378
|
+
* @returns {DoctorCheck | null}
|
|
379
|
+
*/
|
|
380
|
+
function projectReadinessDimension(dimension, report) {
|
|
381
|
+
const record = report.dimensions.find(
|
|
382
|
+
entry =>
|
|
383
|
+
entry !== null && typeof entry === "object" && entry.id === dimension.id
|
|
384
|
+
);
|
|
385
|
+
if (!record || !DOCTOR_STATUSES.includes(record.status)) {
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
const findings = Array.isArray(record.findings) ? record.findings : [];
|
|
389
|
+
const summary = summarizeReadinessFindings(findings);
|
|
390
|
+
const blockerIds = standingBlockerIds(dimension, report);
|
|
391
|
+
|
|
392
|
+
return {
|
|
393
|
+
id: dimension.id,
|
|
394
|
+
// A standing blocker outranks the recorded label. Three CLI producers (B1
|
|
395
|
+
// domain-ownership, B4 feedback-guardrails, B6 context-routing) record
|
|
396
|
+
// `WARN` while standing a blocker, each stating in-line that "the blocker
|
|
397
|
+
// engine never reads this status, so the finding flips the repository to
|
|
398
|
+
// NOT_READY exactly as a FAIL would". Projecting that `WARN` verbatim would
|
|
399
|
+
// score READY_WITH_WARNINGS on a repository the CLI calls NOT_READY — the
|
|
400
|
+
// precise disagreement this bridge exists to eliminate. This still reflects
|
|
401
|
+
// the CLI's decision rather than re-scoring: the CLI decided the blocker
|
|
402
|
+
// stands and wrote it into `blockers[]`.
|
|
403
|
+
status: blockerIds.length > 0 ? "FAIL" : record.status,
|
|
404
|
+
summary: summary.length > 0 ? summary : dimension.question,
|
|
405
|
+
observed: `${dimension.question} Projected from \`.lisa/readiness.json\` (schema_version ${READINESS_REPORT_SCHEMA_VERSION}, ${report.provenance}), the report the Lisa CLI readiness pass wrote.`,
|
|
406
|
+
...(blockerIds.length > 0
|
|
407
|
+
? {
|
|
408
|
+
remediation:
|
|
409
|
+
`Standing ship blocker(s): ${blockerIds.join(", ")}. See \`.lisa/readiness.json\` for the full evidence and the \`readiness-rubric\` rule for the blocker definitions.` +
|
|
410
|
+
(report.narrowedClaim.length > 0 ? ` ${report.narrowedClaim}` : ""),
|
|
411
|
+
}
|
|
412
|
+
: {}),
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
188
416
|
/**
|
|
189
417
|
* Build the orthogonal "Repository readiness" doctor group ("may an agent fleet
|
|
190
418
|
* operate here unattended?"), scored against the eight `readiness-rubric`
|
|
191
419
|
* ownership dimensions. It is separate from the installation-readiness groups
|
|
192
420
|
* and is appended in a fixed position by the readiness-mode caller; the eight
|
|
193
|
-
* dimension checks render in fixed order.
|
|
194
|
-
*
|
|
195
|
-
*
|
|
421
|
+
* dimension checks render in fixed order.
|
|
422
|
+
*
|
|
423
|
+
* The blocker engine and the evidence producers live in the TypeScript CLI,
|
|
424
|
+
* which is the single source of truth; this surface is a bridge, not a second
|
|
425
|
+
* implementation (#1902). It projects whatever the CLI persisted to
|
|
426
|
+
* `.lisa/readiness.json` so an operator running `/lisa:doctor` through any
|
|
427
|
+
* coding agent gets the same readiness answer the CLI gives. When no usable
|
|
428
|
+
* report exists, every unmatched dimension renders `SKIP` with its reason:
|
|
429
|
+
* absence means the readiness pass has not run, never that the repository is
|
|
430
|
+
* clean, so a pass or fail is never manufactured from it.
|
|
196
431
|
* @param {string} root
|
|
197
432
|
* @returns {DoctorGroup}
|
|
198
433
|
*/
|
|
199
434
|
export function createRepositoryReadinessDoctorGroup(root = process.cwd()) {
|
|
200
|
-
|
|
435
|
+
const report = readReadinessReport(path.resolve(root));
|
|
436
|
+
const checks = REPOSITORY_READINESS_DIMENSIONS.map(
|
|
437
|
+
dimension =>
|
|
438
|
+
(report === null
|
|
439
|
+
? null
|
|
440
|
+
: projectReadinessDimension(dimension, report)) ?? {
|
|
441
|
+
id: dimension.id,
|
|
442
|
+
status: "SKIP",
|
|
443
|
+
summary: dimension.question,
|
|
444
|
+
observed:
|
|
445
|
+
report === null
|
|
446
|
+
? dimension.skipReason
|
|
447
|
+
: `${dimension.question} \`.lisa/readiness.json\` carries no usable record for this dimension, so it was not assessed here. Re-run \`lisa doctor --offline --readiness\` to regenerate the report.`,
|
|
448
|
+
}
|
|
449
|
+
);
|
|
201
450
|
return {
|
|
202
451
|
id: "repository-readiness",
|
|
203
452
|
title: "Repository readiness",
|
|
204
|
-
checks:
|
|
205
|
-
id: dimension.id,
|
|
206
|
-
status: "SKIP",
|
|
207
|
-
summary: dimension.question,
|
|
208
|
-
observed: dimension.skipReason,
|
|
209
|
-
})),
|
|
453
|
+
checks: checks.map(check => applyNotEstablishedCaveat(check, checks)),
|
|
210
454
|
};
|
|
211
455
|
}
|
|
212
456
|
|
|
@@ -319,9 +319,27 @@ default doctor path never renders it and stays byte-identical.
|
|
|
319
319
|
contract; do not restate or fork that vocabulary here.
|
|
320
320
|
3. **`SKIP` carries a reason and is never blank.** A dimension with no applicable evidence renders
|
|
321
321
|
`SKIP` with a stated reason ("no deployment target configured, so delivery/authority was not
|
|
322
|
-
assessed"). An unassessed dimension is a known unknown, and the report says so.
|
|
323
|
-
|
|
324
|
-
|
|
322
|
+
assessed"). An unassessed dimension is a known unknown, and the report says so.
|
|
323
|
+
|
|
324
|
+
**This surface reflects the CLI; it does not re-score.** The evidence producers and the blocker
|
|
325
|
+
engine live in the Lisa CLI, which is the single source of truth. When `.lisa/readiness.json` is
|
|
326
|
+
present and readable (parses, matching `schema_version`, carries a `dimensions` array), this group
|
|
327
|
+
**projects** each recorded dimension's status and its operator-facing evidence/reason text, so an
|
|
328
|
+
operator running `/lisa:doctor` through any coding agent sees the same readiness answer the CLI
|
|
329
|
+
gives. When the report is absent, unparseable, or stamped with an unknown `schema_version` — or
|
|
330
|
+
records nothing for a given dimension — that dimension falls back to `SKIP` with the reason it was
|
|
331
|
+
not assessed. Absence means the readiness pass has not run, never that the repository is clean: a
|
|
332
|
+
pass or a fail is never manufactured from a missing report. Run
|
|
333
|
+
`lisa doctor --offline --readiness` to produce the report first.
|
|
334
|
+
|
|
335
|
+
**A standing blocker outranks the recorded status.** Some CLI producers record `WARN` while
|
|
336
|
+
standing a ship blocker — the blocker engine never reads the per-dimension status, so the finding
|
|
337
|
+
flips the repository to `NOT_READY` exactly as a `FAIL` would. A dimension that owns an entry in
|
|
338
|
+
the report's `blockers[]` therefore projects as `FAIL` regardless of its recorded label, and its
|
|
339
|
+
remediation names the blocker ids and repeats the report's `narrowed_claim` (the "IS ready for
|
|
340
|
+
supervised, single-ticket agent work" fallback `readiness-rubric` requires whenever a blocker
|
|
341
|
+
stands). Projected checks also carry the report's `generated_at`/`lisa_version` so a stale report
|
|
342
|
+
cannot read as current truth, and unassessed dimensions carry the CLI's not-established caveat.
|
|
325
343
|
4. **Reuse the shipped verdict ladder and consequence ordering.** No new verdict value and no new
|
|
326
344
|
severity: reuse `READY` / `READY_WITH_WARNINGS` / `NOT_READY`. `READY` requires *positive*
|
|
327
345
|
evidence — every readiness dimension assessed and clean, with no blocker standing. An unassessed
|