@codyswann/lisa 2.279.2 → 2.281.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/dist/cli/doctor-readiness-audit-allowlist.d.ts +7 -0
- package/dist/cli/doctor-readiness-audit-allowlist.d.ts.map +1 -0
- package/dist/cli/doctor-readiness-audit-allowlist.js +177 -0
- package/dist/cli/doctor-readiness-audit-allowlist.js.map +1 -0
- package/dist/cli/doctor-readiness-capabilities.d.ts +33 -0
- package/dist/cli/doctor-readiness-capabilities.d.ts.map +1 -0
- package/dist/cli/doctor-readiness-capabilities.js +26 -0
- package/dist/cli/doctor-readiness-capabilities.js.map +1 -0
- package/dist/cli/doctor-readiness-context-claims.d.ts +14 -0
- package/dist/cli/doctor-readiness-context-claims.d.ts.map +1 -0
- package/dist/cli/doctor-readiness-context-claims.js +117 -0
- package/dist/cli/doctor-readiness-context-claims.js.map +1 -0
- package/dist/cli/doctor-readiness-context-mechanisms.d.ts +46 -0
- package/dist/cli/doctor-readiness-context-mechanisms.d.ts.map +1 -0
- package/dist/cli/doctor-readiness-context-mechanisms.js +116 -0
- package/dist/cli/doctor-readiness-context-mechanisms.js.map +1 -0
- package/dist/cli/doctor-readiness-context.d.ts +16 -0
- package/dist/cli/doctor-readiness-context.d.ts.map +1 -0
- package/dist/cli/doctor-readiness-context.js +244 -0
- package/dist/cli/doctor-readiness-context.js.map +1 -0
- package/dist/cli/doctor-readiness-delivery.d.ts.map +1 -1
- package/dist/cli/doctor-readiness-delivery.js +1 -10
- package/dist/cli/doctor-readiness-delivery.js.map +1 -1
- package/dist/cli/doctor-readiness-domain.d.ts +17 -0
- package/dist/cli/doctor-readiness-domain.d.ts.map +1 -0
- package/dist/cli/doctor-readiness-domain.js +197 -0
- package/dist/cli/doctor-readiness-domain.js.map +1 -0
- package/dist/cli/doctor-readiness-guardrails.d.ts +15 -0
- package/dist/cli/doctor-readiness-guardrails.d.ts.map +1 -0
- package/dist/cli/doctor-readiness-guardrails.js +219 -0
- package/dist/cli/doctor-readiness-guardrails.js.map +1 -0
- package/dist/cli/doctor-readiness-operations.d.ts +103 -0
- package/dist/cli/doctor-readiness-operations.d.ts.map +1 -0
- package/dist/cli/doctor-readiness-operations.js +207 -0
- package/dist/cli/doctor-readiness-operations.js.map +1 -0
- package/dist/cli/doctor-readiness-shared.d.ts +47 -0
- package/dist/cli/doctor-readiness-shared.d.ts.map +1 -0
- package/dist/cli/doctor-readiness-shared.js +88 -0
- package/dist/cli/doctor-readiness-shared.js.map +1 -0
- package/dist/cli/doctor-readiness-supply-chain-scan.d.ts +67 -0
- package/dist/cli/doctor-readiness-supply-chain-scan.d.ts.map +1 -0
- package/dist/cli/doctor-readiness-supply-chain-scan.js +316 -0
- package/dist/cli/doctor-readiness-supply-chain-scan.js.map +1 -0
- package/dist/cli/doctor-readiness-supply-chain.d.ts +14 -0
- package/dist/cli/doctor-readiness-supply-chain.d.ts.map +1 -0
- package/dist/cli/doctor-readiness-supply-chain.js +231 -0
- package/dist/cli/doctor-readiness-supply-chain.js.map +1 -0
- package/dist/cli/doctor-readiness-workflows.d.ts +20 -0
- package/dist/cli/doctor-readiness-workflows.d.ts.map +1 -1
- package/dist/cli/doctor-readiness-workflows.js +29 -7
- package/dist/cli/doctor-readiness-workflows.js.map +1 -1
- package/dist/cli/doctor-readiness-workspaces.d.ts +22 -0
- package/dist/cli/doctor-readiness-workspaces.d.ts.map +1 -0
- package/dist/cli/doctor-readiness-workspaces.js +98 -0
- package/dist/cli/doctor-readiness-workspaces.js.map +1 -0
- package/dist/cli/doctor-readiness.d.ts.map +1 -1
- package/dist/cli/doctor-readiness.js +23 -13
- package/dist/cli/doctor-readiness.js.map +1 -1
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +23 -0
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- 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-agy/plugin.json +1 -1
- 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-cursor/.claude-plugin/plugin.json +1 -1
- 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
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collect the undocumented audit exceptions across every allowlist file.
|
|
3
|
+
* @param root - Repository root
|
|
4
|
+
* @returns Evidence lines, one per undocumented exception
|
|
5
|
+
*/
|
|
6
|
+
export declare function auditExceptionViolations(root: string): Promise<readonly string[]>;
|
|
7
|
+
//# sourceMappingURL=doctor-readiness-audit-allowlist.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-readiness-audit-allowlist.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-readiness-audit-allowlist.ts"],"names":[],"mappings":"AAgMA;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAiB5B"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit-allowlist reading for the dependencies/supply-chain readiness producer
|
|
3
|
+
* (B5, PRD #1739, #1896).
|
|
4
|
+
*
|
|
5
|
+
* An audit allowlist is a list of vulnerabilities somebody decided not to fix.
|
|
6
|
+
* B5 asks the only question that keeps such a list honest: is the decision
|
|
7
|
+
* written down next to the exception? Two failure directions matter equally and
|
|
8
|
+
* this module is shaped by both.
|
|
9
|
+
*
|
|
10
|
+
* Reporting too much fabricates findings — audit-ci's own `.nsprc` carries
|
|
11
|
+
* `$schema` and other configuration at the same level as advisory ids, so a
|
|
12
|
+
* naive walk reports the schema pointer as an undocumented exclusion. Reporting
|
|
13
|
+
* too little is worse: an entry with no `id` field, or a bare string in the
|
|
14
|
+
* list, is still a live exception with no written decision, and dropping it
|
|
15
|
+
* turns the one finding this dimension exists for into silence. Identity
|
|
16
|
+
* therefore degrades (advisory id → package name → position) rather than
|
|
17
|
+
* disappearing.
|
|
18
|
+
* @module cli/doctor-readiness-audit-allowlist
|
|
19
|
+
*/
|
|
20
|
+
import { isRecord, readFileOrNull } from "./doctor-readiness-shared.js";
|
|
21
|
+
/** Audit allowlists whose entries must each carry a decision record. */
|
|
22
|
+
const ALLOWLIST_FILES = [
|
|
23
|
+
"audit.ignore.config.json",
|
|
24
|
+
"audit.ignore.local.json",
|
|
25
|
+
".nsprc",
|
|
26
|
+
];
|
|
27
|
+
/** Keys that count as the written decision behind an audit exception. */
|
|
28
|
+
const DECISION_KEYS = [
|
|
29
|
+
"reason",
|
|
30
|
+
"notes",
|
|
31
|
+
"justification",
|
|
32
|
+
"decision",
|
|
33
|
+
"rationale",
|
|
34
|
+
"comment",
|
|
35
|
+
];
|
|
36
|
+
/**
|
|
37
|
+
* Keys whose value holds audit exceptions. Everything outside these containers
|
|
38
|
+
* is configuration, not an exception — walking a whole document as if every
|
|
39
|
+
* top-level key were an advisory id turns audit-ci's own `.nsprc` (`$schema`,
|
|
40
|
+
* `exceptions`, …) into fabricated findings.
|
|
41
|
+
*/
|
|
42
|
+
const EXCEPTION_CONTAINERS = [
|
|
43
|
+
"exclusions",
|
|
44
|
+
"exceptions",
|
|
45
|
+
"advisories",
|
|
46
|
+
"allowlist",
|
|
47
|
+
];
|
|
48
|
+
/**
|
|
49
|
+
* Whether a top-level `.nsprc`-style key names an advisory rather than config.
|
|
50
|
+
* @param key - The key to classify
|
|
51
|
+
* @returns True when the key may name an advisory
|
|
52
|
+
*/
|
|
53
|
+
function isAdvisoryKey(key) {
|
|
54
|
+
return !key.startsWith("$") && !key.startsWith("//");
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Name one array-shaped exception for an operator.
|
|
58
|
+
*
|
|
59
|
+
* An entry with no `id` must never be dropped — a live exception with no written
|
|
60
|
+
* decision is precisely what B5 exists to surface, and a missing field is not a
|
|
61
|
+
* reason to suppress it. So the identity degrades: advisory id, then package
|
|
62
|
+
* name, then the entry's position in the list.
|
|
63
|
+
* @param entry - The entry's fields
|
|
64
|
+
* @param index - Zero-based position in the container
|
|
65
|
+
* @returns The operator-facing identity
|
|
66
|
+
*/
|
|
67
|
+
function describeException(entry, index) {
|
|
68
|
+
if (typeof entry.id === "string" && entry.id.trim() !== "") {
|
|
69
|
+
return entry.id;
|
|
70
|
+
}
|
|
71
|
+
return typeof entry.package === "string" && entry.package.trim() !== ""
|
|
72
|
+
? entry.package
|
|
73
|
+
: `entry #${index + 1}`;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Normalize one array-shaped exception container.
|
|
77
|
+
*
|
|
78
|
+
* A bare string entry IS the advisory id, and it carries no fields at all — so
|
|
79
|
+
* there is nowhere for a decision to live, which makes it undocumented by
|
|
80
|
+
* construction rather than something to skip over.
|
|
81
|
+
* @param container - The array of entries
|
|
82
|
+
* @returns The normalized exceptions
|
|
83
|
+
*/
|
|
84
|
+
function arrayEntries(container) {
|
|
85
|
+
return container.flatMap((entry, index) => {
|
|
86
|
+
if (typeof entry === "string" && entry.trim() !== "") {
|
|
87
|
+
return [{ id: entry.trim(), fields: null }];
|
|
88
|
+
}
|
|
89
|
+
return isRecord(entry)
|
|
90
|
+
? [{ id: describeException(entry, index), fields: entry }]
|
|
91
|
+
: [];
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Take the object-valued, non-config entries out of an exception map.
|
|
96
|
+
* @param container - A candidate exception map
|
|
97
|
+
* @returns The entries that are genuinely exceptions
|
|
98
|
+
*/
|
|
99
|
+
function mapEntries(container) {
|
|
100
|
+
return Object.entries(container).flatMap(([key, entry]) => isAdvisoryKey(key) && isRecord(entry) ? [{ id: key, fields: entry }] : []);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Normalize one allowlist document into exception entries.
|
|
104
|
+
*
|
|
105
|
+
* Two shapes exist in the wild: a named container holding an array or a map of
|
|
106
|
+
* entries, and audit-ci's `.nsprc`, whose top level maps advisory id to entry.
|
|
107
|
+
* In the map shape only OBJECT-valued entries are exceptions — a string, a
|
|
108
|
+
* boolean, or a `$schema` pointer at the top level is configuration.
|
|
109
|
+
* @param parsed - The parsed allowlist document
|
|
110
|
+
* @returns The exception entries
|
|
111
|
+
*/
|
|
112
|
+
function exceptionEntries(parsed) {
|
|
113
|
+
if (!isRecord(parsed)) {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
const containers = EXCEPTION_CONTAINERS.flatMap(key => parsed[key] === undefined ? [] : [parsed[key]]);
|
|
117
|
+
if (containers.length > 0) {
|
|
118
|
+
return containers.flatMap(container => Array.isArray(container)
|
|
119
|
+
? arrayEntries(container)
|
|
120
|
+
: isRecord(container)
|
|
121
|
+
? mapEntries(container)
|
|
122
|
+
: []);
|
|
123
|
+
}
|
|
124
|
+
// No recognized container: fall back to the `.nsprc` id → entry map. If
|
|
125
|
+
// nothing there is an object-valued advisory key, this document establishes
|
|
126
|
+
// nothing and reports nothing.
|
|
127
|
+
return mapEntries(parsed);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Read one audit allowlist and report the exceptions carrying no decision.
|
|
131
|
+
*
|
|
132
|
+
* An entry with `active: false` is skipped: audit-ci never applies it, so there
|
|
133
|
+
* is no live exception for anybody to justify.
|
|
134
|
+
* @param file - Repo-relative allowlist path
|
|
135
|
+
* @param parsed - The parsed allowlist document
|
|
136
|
+
* @returns Evidence lines, one per undocumented exception
|
|
137
|
+
*/
|
|
138
|
+
function undocumentedExceptions(file, parsed) {
|
|
139
|
+
return exceptionEntries(parsed).flatMap(({ id, fields }) => {
|
|
140
|
+
if (fields?.active === false) {
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
const documented = fields !== null &&
|
|
144
|
+
DECISION_KEYS.some(decisionKey => typeof fields[decisionKey] === "string" &&
|
|
145
|
+
fields[decisionKey].trim() !== "");
|
|
146
|
+
return documented
|
|
147
|
+
? []
|
|
148
|
+
: [
|
|
149
|
+
`\`${file}\` excludes advisory \`${id}\` from the dependency audit ` +
|
|
150
|
+
"with no written decision (no reason/notes/justification field), so " +
|
|
151
|
+
"nobody can tell whether the exception is still true",
|
|
152
|
+
];
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Collect the undocumented audit exceptions across every allowlist file.
|
|
157
|
+
* @param root - Repository root
|
|
158
|
+
* @returns Evidence lines, one per undocumented exception
|
|
159
|
+
*/
|
|
160
|
+
export async function auditExceptionViolations(root) {
|
|
161
|
+
const perFile = await Promise.all(ALLOWLIST_FILES.map(async (file) => {
|
|
162
|
+
const source = await readFileOrNull(root, file);
|
|
163
|
+
if (source === null) {
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
return undocumentedExceptions(file, JSON.parse(source));
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// An unparseable allowlist establishes nothing about its entries, so it
|
|
171
|
+
// is passed over rather than reported as an undocumented exception.
|
|
172
|
+
return [];
|
|
173
|
+
}
|
|
174
|
+
}));
|
|
175
|
+
return perFile.flat();
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=doctor-readiness-audit-allowlist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-readiness-audit-allowlist.js","sourceRoot":"","sources":["../../src/cli/doctor-readiness-audit-allowlist.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The capabilities/tools readiness producer (PRD #1739, #1896).
|
|
3
|
+
*
|
|
4
|
+
* Dimension 2 of the `readiness-rubric` asks whether every tool the work needs
|
|
5
|
+
* is provably *reachable*, not merely installed. The `tool-access-gate` names
|
|
6
|
+
* the anti-pattern explicitly: a binary on `PATH`, a dependency in a manifest,
|
|
7
|
+
* or a configured MCP server proves that something was declared, never that a
|
|
8
|
+
* request through it succeeds. Reachability is established by a live read-only
|
|
9
|
+
* probe against the real system, and nothing an offline file read can see is a
|
|
10
|
+
* substitute for one.
|
|
11
|
+
*
|
|
12
|
+
* So this dimension has no offline evidence to gather and no blocker to stand.
|
|
13
|
+
* What it has is an obligation to say so: a blank dimension reads as "nothing to
|
|
14
|
+
* report" when it actually means "never looked" (#1898), and a presence-based
|
|
15
|
+
* PASS would be worse still — it would report the anti-pattern as proof. The
|
|
16
|
+
* producer therefore renders one reasoned SKIP, every time, naming the live
|
|
17
|
+
* probe it did not run.
|
|
18
|
+
* @module cli/doctor-readiness-capabilities
|
|
19
|
+
*/
|
|
20
|
+
import type { ReadinessDimensionRecord } from "./doctor-readiness-types.js";
|
|
21
|
+
/** The capabilities/tools readiness dimension id (readiness-rubric, RRR-1). */
|
|
22
|
+
export declare const CAPABILITIES_TOOLS_DIMENSION_ID = "capabilities-tools";
|
|
23
|
+
/**
|
|
24
|
+
* Assess the capabilities/tools dimension. Always a reasoned SKIP: the question
|
|
25
|
+
* is not offline-answerable, so the honest answer is a stated non-answer. The
|
|
26
|
+
* record names no `blocker`, so it can never stand one up, and the `root`
|
|
27
|
+
* argument is accepted only to satisfy the shared producer signature — reading
|
|
28
|
+
* it would be the presence anti-pattern the rule forbids.
|
|
29
|
+
* @param _root - Project root (deliberately unread)
|
|
30
|
+
* @returns The capabilities/tools dimension record
|
|
31
|
+
*/
|
|
32
|
+
export declare function assessCapabilitiesToolsDimension(_root: string): Promise<ReadinessDimensionRecord>;
|
|
33
|
+
//# sourceMappingURL=doctor-readiness-capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-readiness-capabilities.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-readiness-capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAE5E,+EAA+E;AAC/E,eAAO,MAAM,+BAA+B,uBAAuB,CAAC;AAWpE;;;;;;;;GAQG;AACH,wBAAsB,gCAAgC,CACpD,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,wBAAwB,CAAC,CAMnC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** The capabilities/tools readiness dimension id (readiness-rubric, RRR-1). */
|
|
2
|
+
export const CAPABILITIES_TOOLS_DIMENSION_ID = "capabilities-tools";
|
|
3
|
+
/** The stated reason this dimension is never assessed by an offline read. */
|
|
4
|
+
const CAPABILITIES_SKIP_REASON = "Tool reachability requires a live read-only probe against the real system " +
|
|
5
|
+
"and cannot be established from an offline read. The `tool-access-gate` rule " +
|
|
6
|
+
"names presence — a binary on `PATH`, a dependency in a manifest, a " +
|
|
7
|
+
"configured MCP server — as the anti-pattern: it proves a tool was declared, " +
|
|
8
|
+
"never that a request through it succeeds. This dimension was therefore " +
|
|
9
|
+
"deliberately not assessed rather than answered from presence.";
|
|
10
|
+
/**
|
|
11
|
+
* Assess the capabilities/tools dimension. Always a reasoned SKIP: the question
|
|
12
|
+
* is not offline-answerable, so the honest answer is a stated non-answer. The
|
|
13
|
+
* record names no `blocker`, so it can never stand one up, and the `root`
|
|
14
|
+
* argument is accepted only to satisfy the shared producer signature — reading
|
|
15
|
+
* it would be the presence anti-pattern the rule forbids.
|
|
16
|
+
* @param _root - Project root (deliberately unread)
|
|
17
|
+
* @returns The capabilities/tools dimension record
|
|
18
|
+
*/
|
|
19
|
+
export async function assessCapabilitiesToolsDimension(_root) {
|
|
20
|
+
return await Promise.resolve({
|
|
21
|
+
id: CAPABILITIES_TOOLS_DIMENSION_ID,
|
|
22
|
+
status: "SKIP",
|
|
23
|
+
findings: [{ reason: CAPABILITIES_SKIP_REASON, skip: true }],
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=doctor-readiness-capabilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-readiness-capabilities.js","sourceRoot":"","sources":["../../src/cli/doctor-readiness-capabilities.ts"],"names":[],"mappings":"AAqBA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,+BAA+B,GAAG,oBAAoB,CAAC;AAEpE,6EAA6E;AAC7E,MAAM,wBAAwB,GAC5B,4EAA4E;IAC5E,8EAA8E;IAC9E,qEAAqE;IACrE,8EAA8E;IAC9E,yEAAyE;IACzE,+DAA+D,CAAC;AAElE;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,gCAAgC,CACpD,KAAa;IAEb,OAAO,MAAM,OAAO,CAAC,OAAO,CAAC;QAC3B,EAAE,EAAE,+BAA+B;QACnC,MAAM,EAAE,MAAM;QACd,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,wBAAwB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;KAC7D,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** One enforcement claim read out of an instruction surface. */
|
|
2
|
+
export interface EnforcementClaim {
|
|
3
|
+
readonly file: string;
|
|
4
|
+
readonly line: number;
|
|
5
|
+
readonly text: string;
|
|
6
|
+
readonly mechanisms: readonly string[];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Read every instruction surface a repository declares.
|
|
10
|
+
* @param root - Repository root
|
|
11
|
+
* @returns Every enforcement claim, in stable file order
|
|
12
|
+
*/
|
|
13
|
+
export declare function collectClaims(root: string): Promise<readonly EnforcementClaim[]>;
|
|
14
|
+
//# sourceMappingURL=doctor-readiness-context-claims.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-readiness-context-claims.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-readiness-context-claims.ts"],"names":[],"mappings":"AA+CA,gEAAgE;AAChE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAkED;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,SAAS,gBAAgB,EAAE,CAAC,CAWtC"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enforcement-claim extraction for the context-routing readiness producer
|
|
3
|
+
* (B6, PRD #1739, #1896).
|
|
4
|
+
*
|
|
5
|
+
* B6 compares what the repository's instruction surfaces promise against the
|
|
6
|
+
* machinery that would have to deliver it. This module does the reading half —
|
|
7
|
+
* finding the sentences that assert a guarantee and the repository paths they
|
|
8
|
+
* name — while its sibling turns those claims into the rubric-shaped dimension
|
|
9
|
+
* record. Splitting them keeps each file inside the repository's file-size
|
|
10
|
+
* budget and keeps the (deliberately conservative) claim grammar in one place.
|
|
11
|
+
*
|
|
12
|
+
* Every filter here is a false-positive guard. A false B6 tells an operator
|
|
13
|
+
* their documentation lies when it does not, so a sentence is treated as a
|
|
14
|
+
* claim only when it asserts a guarantee, is not hedged, and is not inside a
|
|
15
|
+
* fenced code block — and a token is treated as a mechanism only when it is a
|
|
16
|
+
* checkable repository path.
|
|
17
|
+
* @module cli/doctor-readiness-context-claims
|
|
18
|
+
*/
|
|
19
|
+
import { readdir } from "node:fs/promises";
|
|
20
|
+
import * as path from "node:path";
|
|
21
|
+
import { isMechanismPath } from "./doctor-readiness-context-mechanisms.js";
|
|
22
|
+
import { readFileOrNull } from "./doctor-readiness-shared.js";
|
|
23
|
+
/** The instruction surfaces whose enforcement claims are cross-checked. */
|
|
24
|
+
const CLAIM_SOURCE_FILES = ["README.md", "AGENTS.md"];
|
|
25
|
+
/** The rules directory whose every `.md` file is an instruction surface. */
|
|
26
|
+
const RULES_DIR = path.join(".claude", "rules");
|
|
27
|
+
/**
|
|
28
|
+
* Phrases that assert a guarantee something else must enforce. Deliberately
|
|
29
|
+
* narrow: a loose verb list ("uses", "checks") would sweep in ordinary prose and
|
|
30
|
+
* turn B6 into noise the operator learns to ignore.
|
|
31
|
+
*/
|
|
32
|
+
const ENFORCEMENT_CLAIM = /\b(is enforced|are enforced|enforced by|enforces|is blocked by|are blocked by|blocked by|always runs|runs on every|required check|is required|hard-fails|automatically runs|automatically loaded)\b/i;
|
|
33
|
+
/**
|
|
34
|
+
* Words that turn a sentence into an illustration, a possibility, or a plan.
|
|
35
|
+
* A hedged sentence claims nothing, so it can overstate nothing.
|
|
36
|
+
*/
|
|
37
|
+
const HEDGED_CLAIM = /\b(example|e\.g\.|i\.e\.|for instance|if you|would|could|may|might|should|todo|planned|future|suppose|imagine|placeholder|instead of|rather than|used to)\b/i;
|
|
38
|
+
/** Inline-code spans, the only place a mechanism is read from. */
|
|
39
|
+
const INLINE_CODE = /`([^`\n]+)`/g;
|
|
40
|
+
/**
|
|
41
|
+
* Extract the mechanism paths a claim line names.
|
|
42
|
+
* @param line - The raw documentation line
|
|
43
|
+
* @returns Repo-relative mechanism paths, deduplicated
|
|
44
|
+
*/
|
|
45
|
+
function namedMechanisms(line) {
|
|
46
|
+
const tokens = [...line.matchAll(INLINE_CODE)]
|
|
47
|
+
.map(match => (match[1] ?? "").trim())
|
|
48
|
+
.filter(isMechanismPath)
|
|
49
|
+
.map(token => token.replace(/\/$/, ""));
|
|
50
|
+
return [...new Set(tokens)];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Read the enforcement claims out of one instruction surface. Fenced code blocks
|
|
54
|
+
* are skipped wholesale: a sample inside a fence documents a shape, not this
|
|
55
|
+
* repository's guarantees.
|
|
56
|
+
* @param file - Repo-relative file path
|
|
57
|
+
* @param source - File contents
|
|
58
|
+
* @returns The enforcement claims the file makes
|
|
59
|
+
*/
|
|
60
|
+
function extractClaims(file, source) {
|
|
61
|
+
return proseLines(source)
|
|
62
|
+
.filter(entry => ENFORCEMENT_CLAIM.test(entry.text) && !HEDGED_CLAIM.test(entry.text))
|
|
63
|
+
.map(entry => ({
|
|
64
|
+
file,
|
|
65
|
+
line: entry.line,
|
|
66
|
+
text: entry.text.trim(),
|
|
67
|
+
mechanisms: namedMechanisms(entry.text),
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Split a document into its prose lines, dropping fenced code blocks entirely.
|
|
72
|
+
* A line is inside a fence when an odd number of fence markers precede it, which
|
|
73
|
+
* is what makes the fence state derivable per line rather than accumulated.
|
|
74
|
+
* @param source - File contents
|
|
75
|
+
* @returns The prose lines, each carrying its 1-based line number
|
|
76
|
+
*/
|
|
77
|
+
function proseLines(source) {
|
|
78
|
+
const lines = source.split("\n");
|
|
79
|
+
const fences = lines.flatMap((text, index) => text.trim().startsWith("```") ? [index] : []);
|
|
80
|
+
return lines.flatMap((text, index) => {
|
|
81
|
+
const openFences = fences.filter(fence => fence < index).length;
|
|
82
|
+
return fences.includes(index) || openFences % 2 === 1
|
|
83
|
+
? []
|
|
84
|
+
: [{ line: index + 1, text }];
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Read every instruction surface a repository declares.
|
|
89
|
+
* @param root - Repository root
|
|
90
|
+
* @returns Every enforcement claim, in stable file order
|
|
91
|
+
*/
|
|
92
|
+
export async function collectClaims(root) {
|
|
93
|
+
const ruleFiles = await listRuleFiles(root);
|
|
94
|
+
const sources = await Promise.all([...CLAIM_SOURCE_FILES, ...ruleFiles].map(async (file) => ({
|
|
95
|
+
file,
|
|
96
|
+
source: await readFileOrNull(root, file),
|
|
97
|
+
})));
|
|
98
|
+
return sources.flatMap(({ file, source }) => source === null ? [] : extractClaims(file, source));
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* List the `.claude/rules/*.md` instruction files.
|
|
102
|
+
* @param root - Repository root
|
|
103
|
+
* @returns Repo-relative rule file paths, sorted
|
|
104
|
+
*/
|
|
105
|
+
async function listRuleFiles(root) {
|
|
106
|
+
try {
|
|
107
|
+
const entries = await readdir(path.join(root, RULES_DIR));
|
|
108
|
+
return entries
|
|
109
|
+
.filter(entry => entry.endsWith(".md"))
|
|
110
|
+
.sort((a, b) => a.localeCompare(b))
|
|
111
|
+
.map(entry => `${RULES_DIR.split(path.sep).join("/")}/${entry}`);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=doctor-readiness-context-claims.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-readiness-context-claims.js","sourceRoot":"","sources":["../../src/cli/doctor-readiness-context-claims.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** What a named mechanism turned out to be. */
|
|
2
|
+
export type MechanismVerdict =
|
|
3
|
+
/** The named path exists — the documented guarantee is backed. */
|
|
4
|
+
"present"
|
|
5
|
+
/** The named path provably does not exist — this is what B6 stands on. */
|
|
6
|
+
| "missing"
|
|
7
|
+
/** Not a checkable in-repository path, so it establishes nothing. */
|
|
8
|
+
| "unmappable";
|
|
9
|
+
/** Decides whether a repository path is ignored/generated. */
|
|
10
|
+
export type IgnoreMatcher = (relativePath: string) => boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Whether an inline-code span is even shaped like an in-repository path.
|
|
13
|
+
*
|
|
14
|
+
* Every exclusion is a false-positive guard: a URL is not a repo path, a glob
|
|
15
|
+
* names a set rather than a file, an `@` marks a scoped package or a versioned
|
|
16
|
+
* action reference rather than a path, and a placeholder names nothing.
|
|
17
|
+
* @param token - The inline-code text
|
|
18
|
+
* @returns True when the token is shaped like a repository path
|
|
19
|
+
*/
|
|
20
|
+
export declare function isMechanismPath(token: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Build the ignore matcher for a repository from its `.gitignore` plus the
|
|
23
|
+
* always-generated defaults. The matcher is deliberately coarse — it compares
|
|
24
|
+
* path segments and directory prefixes rather than implementing gitignore's full
|
|
25
|
+
* grammar — because its only job is to move a doubtful path OUT of the violation
|
|
26
|
+
* set, where being over-inclusive is the safe direction.
|
|
27
|
+
* @param root - Repository root
|
|
28
|
+
* @returns A matcher that answers "is this path generated or ignored?"
|
|
29
|
+
*/
|
|
30
|
+
export declare function loadIgnoreMatcher(root: string): Promise<IgnoreMatcher>;
|
|
31
|
+
/**
|
|
32
|
+
* Classify one named mechanism.
|
|
33
|
+
*
|
|
34
|
+
* A token only becomes checkable when the repository itself vouches for the
|
|
35
|
+
* shape: it carries a known file extension, it lives under a dot-directory the
|
|
36
|
+
* repository conventionally keeps machinery in, or its first segment is a real
|
|
37
|
+
* directory in this repository. `acme/release-gate` in a repository with no
|
|
38
|
+
* `acme/` directory is an `org/repo` slug as far as anything here can tell, so
|
|
39
|
+
* it is unmappable — not missing.
|
|
40
|
+
* @param root - Repository root
|
|
41
|
+
* @param token - The named mechanism path
|
|
42
|
+
* @param isIgnored - The repository's ignore matcher
|
|
43
|
+
* @returns What the mechanism turned out to be
|
|
44
|
+
*/
|
|
45
|
+
export declare function classifyMechanism(root: string, token: string, isIgnored: IgnoreMatcher): Promise<MechanismVerdict>;
|
|
46
|
+
//# sourceMappingURL=doctor-readiness-context-mechanisms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-readiness-context-mechanisms.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-readiness-context-mechanisms.ts"],"names":[],"mappings":"AA+CA,+CAA+C;AAC/C,MAAM,MAAM,gBAAgB;AAC1B,kEAAkE;AAChE,SAAS;AACX,0EAA0E;GACxE,SAAS;AACX,qEAAqE;GACnE,YAAY,CAAC;AAEjB,8DAA8D;AAC9D,MAAM,MAAM,aAAa,GAAG,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;AAE9D;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAmBtD;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAmB5E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,aAAa,GACvB,OAAO,CAAC,gBAAgB,CAAC,CAa3B"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mechanism classification for the context-routing readiness producer (B6,
|
|
3
|
+
* PRD #1739, #1896).
|
|
4
|
+
*
|
|
5
|
+
* B6 may only be stood up on a documented guarantee whose named mechanism
|
|
6
|
+
* provably does not exist. Deciding whether a backticked token even IS a
|
|
7
|
+
* checkable repository path is therefore the whole precision budget of the
|
|
8
|
+
* blocker, and a standing blocker flips the entire repository to `NOT_READY` —
|
|
9
|
+
* so every ambiguity here resolves to `unmappable`, never to `missing`.
|
|
10
|
+
*
|
|
11
|
+
* The three false-positive classes this module exists to kill, all of which look
|
|
12
|
+
* exactly like repository paths:
|
|
13
|
+
*
|
|
14
|
+
* - **Package names.** `@codyswann/lisa` is a dependency, not a file.
|
|
15
|
+
* - **Slugs and refs.** `CodySwannGT/lisa` and `origin/main` name a repository
|
|
16
|
+
* and a git ref; neither is a path in the working tree.
|
|
17
|
+
* - **Generated artifacts.** `dist/cli.js` is absent from a clean checkout by
|
|
18
|
+
* design, so faulting it would fault every repository that builds anything.
|
|
19
|
+
* @module cli/doctor-readiness-context-mechanisms
|
|
20
|
+
*/
|
|
21
|
+
import { directoryExists, pathExists, readFileOrNull, trimSlashes, } from "./doctor-readiness-shared.js";
|
|
22
|
+
/** File extensions a named mechanism plausibly ends in. */
|
|
23
|
+
const MECHANISM_EXTENSION = /\.(ya?ml|sh|bash|zsh|ts|tsx|js|mjs|cjs|json|jsonc|md|toml|cfg|ini)$/;
|
|
24
|
+
/**
|
|
25
|
+
* Generated paths treated as ignored even when a repository ships no
|
|
26
|
+
* `.gitignore`. Their absence is a property of a clean checkout, never of
|
|
27
|
+
* documentation that lies.
|
|
28
|
+
*/
|
|
29
|
+
const ALWAYS_GENERATED = [
|
|
30
|
+
"node_modules",
|
|
31
|
+
"dist",
|
|
32
|
+
"build",
|
|
33
|
+
"coverage",
|
|
34
|
+
"out",
|
|
35
|
+
".next",
|
|
36
|
+
".turbo",
|
|
37
|
+
".cache",
|
|
38
|
+
];
|
|
39
|
+
/**
|
|
40
|
+
* Whether an inline-code span is even shaped like an in-repository path.
|
|
41
|
+
*
|
|
42
|
+
* Every exclusion is a false-positive guard: a URL is not a repo path, a glob
|
|
43
|
+
* names a set rather than a file, an `@` marks a scoped package or a versioned
|
|
44
|
+
* action reference rather than a path, and a placeholder names nothing.
|
|
45
|
+
* @param token - The inline-code text
|
|
46
|
+
* @returns True when the token is shaped like a repository path
|
|
47
|
+
*/
|
|
48
|
+
export function isMechanismPath(token) {
|
|
49
|
+
const trimmed = token.trim();
|
|
50
|
+
if (trimmed === "" || /\s/.test(trimmed)) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
if (trimmed.includes("://") ||
|
|
54
|
+
trimmed.includes("*") ||
|
|
55
|
+
trimmed.includes("<") ||
|
|
56
|
+
trimmed.includes("$") ||
|
|
57
|
+
// A scoped package (`@acme/pkg`) or a pinned action (`actions/checkout@v4`)
|
|
58
|
+
// is a dependency coordinate, not a file this repository owns.
|
|
59
|
+
trimmed.includes("@") ||
|
|
60
|
+
trimmed.startsWith("/") ||
|
|
61
|
+
trimmed.startsWith("-")) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
return trimmed.includes("/") || MECHANISM_EXTENSION.test(trimmed);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Build the ignore matcher for a repository from its `.gitignore` plus the
|
|
68
|
+
* always-generated defaults. The matcher is deliberately coarse — it compares
|
|
69
|
+
* path segments and directory prefixes rather than implementing gitignore's full
|
|
70
|
+
* grammar — because its only job is to move a doubtful path OUT of the violation
|
|
71
|
+
* set, where being over-inclusive is the safe direction.
|
|
72
|
+
* @param root - Repository root
|
|
73
|
+
* @returns A matcher that answers "is this path generated or ignored?"
|
|
74
|
+
*/
|
|
75
|
+
export async function loadIgnoreMatcher(root) {
|
|
76
|
+
const source = (await readFileOrNull(root, ".gitignore")) ?? "";
|
|
77
|
+
const declared = source
|
|
78
|
+
.split("\n")
|
|
79
|
+
.map(line => line.trim())
|
|
80
|
+
.filter(line => line !== "" && !line.startsWith("#") && !line.startsWith("!"))
|
|
81
|
+
.map(trimSlashes)
|
|
82
|
+
.filter(line => line !== "" && !line.includes("*"));
|
|
83
|
+
const patterns = new Set([...ALWAYS_GENERATED, ...declared]);
|
|
84
|
+
return relativePath => relativePath
|
|
85
|
+
.split("/")
|
|
86
|
+
.some((segment, index, segments) => patterns.has(segments.slice(0, index + 1).join("/")) ||
|
|
87
|
+
patterns.has(segment));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Classify one named mechanism.
|
|
91
|
+
*
|
|
92
|
+
* A token only becomes checkable when the repository itself vouches for the
|
|
93
|
+
* shape: it carries a known file extension, it lives under a dot-directory the
|
|
94
|
+
* repository conventionally keeps machinery in, or its first segment is a real
|
|
95
|
+
* directory in this repository. `acme/release-gate` in a repository with no
|
|
96
|
+
* `acme/` directory is an `org/repo` slug as far as anything here can tell, so
|
|
97
|
+
* it is unmappable — not missing.
|
|
98
|
+
* @param root - Repository root
|
|
99
|
+
* @param token - The named mechanism path
|
|
100
|
+
* @param isIgnored - The repository's ignore matcher
|
|
101
|
+
* @returns What the mechanism turned out to be
|
|
102
|
+
*/
|
|
103
|
+
export async function classifyMechanism(root, token, isIgnored) {
|
|
104
|
+
if (isIgnored(token)) {
|
|
105
|
+
return "unmappable";
|
|
106
|
+
}
|
|
107
|
+
if (await pathExists(root, token)) {
|
|
108
|
+
return "present";
|
|
109
|
+
}
|
|
110
|
+
const [firstSegment = ""] = token.split("/");
|
|
111
|
+
const checkable = MECHANISM_EXTENSION.test(token) ||
|
|
112
|
+
firstSegment.startsWith(".") ||
|
|
113
|
+
(token.includes("/") && (await directoryExists(root, firstSegment)));
|
|
114
|
+
return checkable ? "missing" : "unmappable";
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=doctor-readiness-context-mechanisms.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-readiness-context-mechanisms.js","sourceRoot":"","sources":["../../src/cli/doctor-readiness-context-mechanisms.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ReadinessDimensionRecord } from "./doctor-readiness-types.js";
|
|
2
|
+
/** The context-routing readiness dimension id (readiness-rubric, RRR-1). */
|
|
3
|
+
export declare const CONTEXT_ROUTING_DIMENSION_ID = "context-routing";
|
|
4
|
+
/**
|
|
5
|
+
* Assess the context-routing dimension: B6, "documentation overstates enforced
|
|
6
|
+
* guarantees". Offline by construction — it reads only the repository's own
|
|
7
|
+
* instruction surfaces and the paths they name — and high-precision by design:
|
|
8
|
+
* it stands B6 only on a named mechanism that survives {@link classifyMechanism}
|
|
9
|
+
* and still demonstrably does not exist. The record's status is `WARN` rather
|
|
10
|
+
* than `FAIL`, but that is a confidence signal to a human reader only: a
|
|
11
|
+
* standing B6 blocker flips the repository to `NOT_READY` either way.
|
|
12
|
+
* @param root - Project root to assess
|
|
13
|
+
* @returns The context-routing dimension record
|
|
14
|
+
*/
|
|
15
|
+
export declare function assessContextRoutingDimension(root: string): Promise<ReadinessDimensionRecord>;
|
|
16
|
+
//# sourceMappingURL=doctor-readiness-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-readiness-context.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-readiness-context.ts"],"names":[],"mappings":"AA2CA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAE5E,4EAA4E;AAC5E,eAAO,MAAM,4BAA4B,oBAAoB,CAAC;AAqN9D;;;;;;;;;;GAUG;AACH,wBAAsB,6BAA6B,CACjD,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,wBAAwB,CAAC,CAoCnC"}
|