kibi-cli 0.16.1 → 0.18.1
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-json-command.d.ts.map +1 -1
- package/dist/cli-json-command.js +87 -11
- package/dist/cli-register-maintenance.d.ts.map +1 -1
- package/dist/cli-register-maintenance.js +8 -0
- package/dist/cli-register-reporting.d.ts.map +1 -1
- package/dist/cli-register-reporting.js +5 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -1
- package/dist/commands/aggregated-checks.d.ts.map +1 -1
- package/dist/commands/aggregated-checks.js +1 -0
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +1 -1
- package/dist/commands/coverage.d.ts +5 -0
- package/dist/commands/coverage.d.ts.map +1 -1
- package/dist/commands/coverage.js +5 -0
- package/dist/commands/discovery-table.d.ts.map +1 -1
- package/dist/commands/discovery-table.js +96 -5
- package/dist/commands/sync/cache.d.ts +2 -0
- package/dist/commands/sync/cache.d.ts.map +1 -1
- package/dist/commands/sync/cache.js +8 -0
- package/dist/commands/sync/manifest.d.ts +12 -1
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +22 -5
- package/dist/commands/sync/persistence.d.ts.map +1 -1
- package/dist/commands/sync/persistence.js +76 -2
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +72 -15
- package/dist/commands/usage-metrics.d.ts +66 -2
- package/dist/commands/usage-metrics.d.ts.map +1 -1
- package/dist/commands/usage-metrics.js +34 -24
- package/dist/commands/usage-remediation.d.ts +9 -0
- package/dist/commands/usage-remediation.d.ts.map +1 -0
- package/dist/commands/usage-remediation.js +52 -0
- package/dist/extractors/markdown.d.ts +17 -1
- package/dist/extractors/markdown.d.ts.map +1 -1
- package/dist/extractors/markdown.js +81 -2
- package/dist/logic/ir.d.ts +112 -0
- package/dist/logic/ir.d.ts.map +1 -0
- package/dist/logic/ir.js +729 -0
- package/dist/operations/modeling/logic-modeling.d.ts +22 -0
- package/dist/operations/modeling/logic-modeling.d.ts.map +1 -0
- package/dist/operations/modeling/logic-modeling.js +120 -0
- package/dist/operations/modeling/model-requirement.d.ts.map +1 -1
- package/dist/operations/modeling/model-requirement.js +83 -2
- package/dist/operations/modeling/predicate-applyplan.d.ts +1 -1
- package/dist/operations/modeling/predicate-applyplan.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-applyplan.js +29 -4
- package/dist/operations/modeling/predicate-inference.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-inference.js +3 -0
- package/dist/operations/modeling/predicate-loader.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-loader.js +7 -1
- package/dist/operations/modeling/predicate-ranker.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-ranker.js +40 -2
- package/dist/operations/modeling/predicate-types.d.ts +12 -1
- package/dist/operations/modeling/predicate-types.d.ts.map +1 -1
- package/dist/operations/modeling/requirement-types.d.ts +12 -0
- package/dist/operations/modeling/requirement-types.d.ts.map +1 -1
- package/dist/operations/modeling/suggest-predicates.d.ts.map +1 -1
- package/dist/operations/modeling/suggest-predicates.js +35 -11
- package/dist/operations/mutation/audit.js +1 -1
- package/dist/operations/mutation/contradictions.d.ts +1 -1
- package/dist/operations/mutation/contradictions.d.ts.map +1 -1
- package/dist/operations/mutation/contradictions.js +23 -18
- package/dist/operations/mutation/relationships.d.ts.map +1 -1
- package/dist/operations/mutation/relationships.js +8 -0
- package/dist/operations/mutation/serialization.d.ts.map +1 -1
- package/dist/operations/mutation/serialization.js +6 -0
- package/dist/operations/mutation/strict-fact.d.ts.map +1 -1
- package/dist/operations/mutation/strict-fact.js +68 -3
- package/dist/operations/mutation/upsert.d.ts +3 -1
- package/dist/operations/mutation/upsert.d.ts.map +1 -1
- package/dist/operations/mutation/upsert.js +51 -12
- package/dist/operations/mutation/validate-upsert.d.ts.map +1 -1
- package/dist/operations/mutation/validate-upsert.js +18 -2
- package/dist/operations/mutation/validation.d.ts.map +1 -1
- package/dist/operations/mutation/validation.js +8 -0
- package/dist/operations/semantic-advisor/analysis-receipt.d.ts +2 -2
- package/dist/operations/semantic-advisor/analysis-receipt.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/analysis-receipt.js +30 -16
- package/dist/operations/semantic-advisor/analyze-prose.d.ts +1 -1
- package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/analyze-prose.js +464 -27
- package/dist/operations/semantic-advisor/clauses.d.ts +1 -0
- package/dist/operations/semantic-advisor/clauses.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/clauses.js +7 -7
- package/dist/operations/semantic-advisor/ingestion-boundary.d.ts +18 -0
- package/dist/operations/semantic-advisor/ingestion-boundary.d.ts.map +1 -0
- package/dist/operations/semantic-advisor/ingestion-boundary.js +167 -0
- package/dist/operations/semantic-advisor/observation-plan.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/observation-plan.js +15 -3
- package/dist/operations/semantic-advisor/shared.d.ts +9 -0
- package/dist/operations/semantic-advisor/shared.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/shared.js +38 -4
- package/dist/operations/semantic-advisor/types.d.ts +66 -1
- package/dist/operations/semantic-advisor/types.d.ts.map +1 -1
- package/dist/prolog/codec.d.ts.map +1 -1
- package/dist/prolog/codec.js +19 -2
- package/dist/prolog.d.ts +6 -0
- package/dist/prolog.d.ts.map +1 -1
- package/dist/prolog.js +247 -63
- package/dist/public/diagnostic-usage.d.ts +17 -0
- package/dist/public/diagnostic-usage.d.ts.map +1 -0
- package/dist/public/diagnostic-usage.js +152 -0
- package/dist/public/distribution-parity.d.ts +90 -0
- package/dist/public/distribution-parity.d.ts.map +1 -0
- package/dist/public/distribution-parity.js +367 -0
- package/dist/public/impact/full-kb-quality.d.ts +2 -0
- package/dist/public/impact/full-kb-quality.d.ts.map +1 -1
- package/dist/public/impact/full-kb-quality.js +53 -1
- package/dist/public/impact-diagnostics.d.ts +2 -0
- package/dist/public/impact-diagnostics.d.ts.map +1 -1
- package/dist/public/impact-diagnostics.js +1 -0
- package/dist/public/operations/catalog.d.ts +235 -11
- package/dist/public/operations/catalog.d.ts.map +1 -1
- package/dist/public/operations/check-executor.d.ts.map +1 -1
- package/dist/public/operations/check-executor.js +4 -0
- package/dist/public/operations/discovery-executors.d.ts +6 -0
- package/dist/public/operations/discovery-executors.d.ts.map +1 -1
- package/dist/public/operations/discovery-executors.js +18 -2
- package/dist/public/operations/index.d.ts +5 -0
- package/dist/public/operations/index.d.ts.map +1 -1
- package/dist/public/operations/index.js +5 -0
- package/dist/public/operations/legacy-migration-plan.d.ts +113 -0
- package/dist/public/operations/legacy-migration-plan.d.ts.map +1 -0
- package/dist/public/operations/legacy-migration-plan.js +409 -0
- package/dist/public/operations/node-ports.d.ts.map +1 -1
- package/dist/public/operations/node-ports.js +90 -0
- package/dist/public/operations/repair-plan.d.ts +184 -0
- package/dist/public/operations/repair-plan.d.ts.map +1 -0
- package/dist/public/operations/repair-plan.js +397 -0
- package/dist/public/operations/runtime-types.d.ts +7 -0
- package/dist/public/operations/runtime-types.d.ts.map +1 -1
- package/dist/public/operations/specs/check.d.ts +3 -3
- package/dist/public/operations/specs/check.d.ts.map +1 -1
- package/dist/public/operations/specs/check.js +5 -2
- package/dist/public/operations/specs/discovery.d.ts +1 -1
- package/dist/public/operations/specs/discovery.js +1 -1
- package/dist/public/operations/specs/modeling.d.ts +35 -1
- package/dist/public/operations/specs/modeling.d.ts.map +1 -1
- package/dist/public/operations/specs/modeling.js +32 -1
- package/dist/public/operations/specs/mutation-schemas.d.ts +133 -3
- package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
- package/dist/public/operations/specs/mutation-schemas.js +67 -1
- package/dist/public/operations/specs/reporting.d.ts +42 -1
- package/dist/public/operations/specs/reporting.d.ts.map +1 -1
- package/dist/public/operations/specs/reporting.js +70 -6
- package/dist/public/operations/specs/semantic.d.ts +30 -2
- package/dist/public/operations/specs/semantic.d.ts.map +1 -1
- package/dist/public/operations/specs/semantic.js +35 -3
- package/dist/public/operations/specs/upsert.d.ts +133 -3
- package/dist/public/operations/specs/upsert.d.ts.map +1 -1
- package/dist/public/operations/workspace-snapshot.d.ts +10 -0
- package/dist/public/operations/workspace-snapshot.d.ts.map +1 -0
- package/dist/public/operations/workspace-snapshot.js +20 -0
- package/dist/public/schemas/entity.d.ts.map +1 -1
- package/dist/public/schemas/entity.js +332 -0
- package/dist/public/schemas/relationship.d.ts.map +1 -1
- package/dist/public/schemas/relationship.js +1 -0
- package/dist/public/skills/kibi-traceability/SKILL.md +7 -1
- package/dist/public/skills/kibi-usage/SKILL.md +44 -12
- package/dist/public/skills/kibi-usage/resources/fact-lanes.md +12 -2
- package/dist/public/skills/kibi-usage/resources/logic-ir.md +43 -0
- package/dist/public/skills/kibi-usage/resources/operation-access.md +19 -19
- package/dist/public/skills/kibi-usage/resources/ui-requirements.md +96 -0
- package/dist/public/skills/kibi-usage/resources/workflows.md +40 -8
- package/dist/public/symbol-granularity.d.ts +9 -0
- package/dist/public/symbol-granularity.d.ts.map +1 -1
- package/dist/public/symbol-granularity.js +16 -0
- package/dist/public/telemetry-acceptance.d.ts +89 -0
- package/dist/public/telemetry-acceptance.d.ts.map +1 -0
- package/dist/public/telemetry-acceptance.js +597 -0
- package/dist/public/telemetry-remediation.d.ts +33 -0
- package/dist/public/telemetry-remediation.d.ts.map +1 -0
- package/dist/public/telemetry-remediation.js +257 -0
- package/dist/public/verification-receipt.d.ts +75 -0
- package/dist/public/verification-receipt.d.ts.map +1 -0
- package/dist/public/verification-receipt.js +129 -0
- package/dist/schemas/entity.schema.json +401 -4
- package/dist/traceability/staged-diagnostics.d.ts +1 -1
- package/dist/traceability/staged-diagnostics.d.ts.map +1 -1
- package/dist/traceability/staged-diagnostics.js +17 -10
- package/dist/traceability/staged-impact-contract.js +1 -1
- package/dist/traceability/staged-symbols-manifest.d.ts.map +1 -1
- package/dist/traceability/staged-symbols-manifest.js +28 -10
- package/dist/traceability/temp-kb.d.ts.map +1 -1
- package/dist/traceability/temp-kb.js +49 -3
- package/dist/types/entities.d.ts +1 -0
- package/dist/types/entities.d.ts.map +1 -1
- package/dist/types/relationships.d.ts +1 -1
- package/dist/types/relationships.d.ts.map +1 -1
- package/dist/utils/rule-registry.d.ts +2 -0
- package/dist/utils/rule-registry.d.ts.map +1 -1
- package/dist/utils/rule-registry.js +18 -0
- package/dist/utils/schema-version.d.ts +1 -1
- package/dist/utils/schema-version.js +1 -1
- package/package.json +34 -2
- package/src/public/diagnostic-usage.ts +206 -0
- package/src/public/distribution-parity.ts +580 -0
- package/src/public/impact/full-kb-quality.ts +74 -1
- package/src/public/impact-diagnostics.ts +16 -0
- package/src/public/operations/check-executor.ts +4 -0
- package/src/public/operations/discovery-executors.ts +24 -2
- package/src/public/operations/index.ts +5 -0
- package/src/public/operations/legacy-migration-plan.ts +688 -0
- package/src/public/operations/node-ports.ts +109 -0
- package/src/public/operations/repair-plan.ts +562 -0
- package/src/public/operations/runtime-types.ts +8 -0
- package/src/public/operations/specs/check.ts +5 -2
- package/src/public/operations/specs/discovery.ts +1 -1
- package/src/public/operations/specs/modeling.ts +39 -1
- package/src/public/operations/specs/mutation-schemas.ts +77 -1
- package/src/public/operations/specs/reporting.ts +91 -6
- package/src/public/operations/specs/semantic.ts +42 -3
- package/src/public/operations/workspace-snapshot.ts +28 -0
- package/src/public/schemas/entity.ts +340 -1
- package/src/public/schemas/relationship.ts +1 -0
- package/src/public/skills/kibi-traceability/SKILL.md +7 -1
- package/src/public/skills/kibi-usage/SKILL.md +44 -12
- package/src/public/skills/kibi-usage/resources/fact-lanes.md +12 -2
- package/src/public/skills/kibi-usage/resources/logic-ir.md +43 -0
- package/src/public/skills/kibi-usage/resources/operation-access.md +19 -19
- package/src/public/skills/kibi-usage/resources/ui-requirements.md +96 -0
- package/src/public/skills/kibi-usage/resources/workflows.md +40 -8
- package/src/public/symbol-granularity.ts +25 -0
- package/src/public/telemetry-acceptance.ts +859 -0
- package/src/public/telemetry-remediation.ts +462 -0
- package/src/public/verification-receipt.ts +170 -0
- package/src/schemas/entity.schema.json +401 -4
- package/src/schemas/relationship.schema.json +1 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
import * as fs from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
3
5
|
import { promisify } from "node:util";
|
|
4
6
|
import fg from "fast-glob";
|
|
5
7
|
|
|
@@ -21,6 +23,7 @@ type NodeGitPort = GitPort & {
|
|
|
21
23
|
};
|
|
22
24
|
|
|
23
25
|
const execFileAsync = promisify(execFile);
|
|
26
|
+
const SNAPSHOT_EXCLUDED_PREFIXES = [".changeset/", ".kb/", "docs/"] as const;
|
|
24
27
|
const ALWAYS_IGNORED_GLOBS = [
|
|
25
28
|
"**/.git/**",
|
|
26
29
|
"**/.kb/**",
|
|
@@ -35,6 +38,111 @@ const ALWAYS_IGNORED_GLOBS = [
|
|
|
35
38
|
"**/target/**",
|
|
36
39
|
] as const;
|
|
37
40
|
|
|
41
|
+
function includedSnapshotPath(relativePath: string): boolean {
|
|
42
|
+
return !SNAPSHOT_EXCLUDED_PREFIXES.some(
|
|
43
|
+
(prefix) =>
|
|
44
|
+
relativePath === prefix.slice(0, -1) || relativePath.startsWith(prefix),
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function withoutVerificationReceiptFrontmatter(content: string): string {
|
|
49
|
+
const lines = content.match(/[^\n]*\n|[^\n]+$/g) ?? [];
|
|
50
|
+
if (lines[0]?.trim() !== "---") return content;
|
|
51
|
+
let inFrontmatter = true;
|
|
52
|
+
let skippingReceipts = false;
|
|
53
|
+
const retained: string[] = [];
|
|
54
|
+
for (const [index, line] of lines.entries()) {
|
|
55
|
+
const trimmed = line.trim();
|
|
56
|
+
if (index > 0 && inFrontmatter && trimmed === "---") {
|
|
57
|
+
inFrontmatter = false;
|
|
58
|
+
skippingReceipts = false;
|
|
59
|
+
retained.push(line);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (inFrontmatter && /^verification_receipts\s*:/.test(line)) {
|
|
63
|
+
skippingReceipts = true;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (
|
|
67
|
+
inFrontmatter &&
|
|
68
|
+
skippingReceipts &&
|
|
69
|
+
/^[A-Za-z_][A-Za-z0-9_-]*\s*:/.test(line)
|
|
70
|
+
) {
|
|
71
|
+
skippingReceipts = false;
|
|
72
|
+
}
|
|
73
|
+
if (!skippingReceipts) retained.push(line);
|
|
74
|
+
}
|
|
75
|
+
return retained.join("");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function snapshotFileContent(relativePath: string, content: Buffer): Buffer {
|
|
79
|
+
if (
|
|
80
|
+
relativePath.startsWith("documentation/") &&
|
|
81
|
+
relativePath.endsWith(".md")
|
|
82
|
+
) {
|
|
83
|
+
return Buffer.from(
|
|
84
|
+
withoutVerificationReceiptFrontmatter(content.toString("utf8")),
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
return content;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function workspaceSnapshot(workspaceRoot: string) {
|
|
91
|
+
const { stdout: listed } = await execFileAsync(
|
|
92
|
+
"git",
|
|
93
|
+
[
|
|
94
|
+
"-C",
|
|
95
|
+
workspaceRoot,
|
|
96
|
+
"ls-files",
|
|
97
|
+
"--cached",
|
|
98
|
+
"--others",
|
|
99
|
+
"--exclude-standard",
|
|
100
|
+
"-z",
|
|
101
|
+
],
|
|
102
|
+
{ encoding: "buffer", maxBuffer: 64 * 1024 * 1024 },
|
|
103
|
+
);
|
|
104
|
+
const paths = listed
|
|
105
|
+
.toString("utf8")
|
|
106
|
+
.split("\0")
|
|
107
|
+
.filter(Boolean)
|
|
108
|
+
.filter(includedSnapshotPath)
|
|
109
|
+
.sort();
|
|
110
|
+
const digest = createHash("sha256");
|
|
111
|
+
digest.update("kibi.workspace-snapshot.v1\0");
|
|
112
|
+
for (const relativePath of paths) {
|
|
113
|
+
digest.update(relativePath);
|
|
114
|
+
digest.update("\0");
|
|
115
|
+
try {
|
|
116
|
+
const content = await fs.readFile(path.join(workspaceRoot, relativePath));
|
|
117
|
+
digest.update(snapshotFileContent(relativePath, content));
|
|
118
|
+
} catch (error) {
|
|
119
|
+
const code =
|
|
120
|
+
typeof error === "object" && error !== null && "code" in error
|
|
121
|
+
? String(error.code)
|
|
122
|
+
: "unreadable";
|
|
123
|
+
digest.update(`<${code}>`);
|
|
124
|
+
}
|
|
125
|
+
digest.update("\0");
|
|
126
|
+
}
|
|
127
|
+
const { stdout: status } = await execFileAsync(
|
|
128
|
+
"git",
|
|
129
|
+
[
|
|
130
|
+
"-C",
|
|
131
|
+
workspaceRoot,
|
|
132
|
+
"status",
|
|
133
|
+
"--porcelain=v1",
|
|
134
|
+
"--untracked-files=normal",
|
|
135
|
+
],
|
|
136
|
+
{ maxBuffer: 16 * 1024 * 1024 },
|
|
137
|
+
);
|
|
138
|
+
return {
|
|
139
|
+
version: "kibi.workspace-snapshot.v1" as const,
|
|
140
|
+
hash: digest.digest("hex"),
|
|
141
|
+
dirty: status.trim().length > 0,
|
|
142
|
+
fileCount: paths.length,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
38
146
|
export const nodeFilesystem: NodeFilesystemPort = {
|
|
39
147
|
readFile: (filePath) => fs.readFile(filePath, "utf8"),
|
|
40
148
|
writeFile: async (filePath, data) => {
|
|
@@ -67,6 +175,7 @@ export const nodeGit: NodeGitPort = {
|
|
|
67
175
|
]);
|
|
68
176
|
return stdout.trim();
|
|
69
177
|
},
|
|
178
|
+
workspaceSnapshot,
|
|
70
179
|
ignoredPaths: async (workspaceRoot, paths) => {
|
|
71
180
|
const policy = createRepoIgnorePolicy(workspaceRoot);
|
|
72
181
|
return paths.filter((candidate) => policy.isIgnored(candidate));
|
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
export const REPAIR_PLAN_VERSION = "kibi.repair-plan.v1" as const;
|
|
4
|
+
|
|
5
|
+
type RepairPhaseDefinition = {
|
|
6
|
+
readonly order: number;
|
|
7
|
+
readonly objective: string;
|
|
8
|
+
readonly workflowSteps: readonly string[];
|
|
9
|
+
readonly validationRules: readonly string[];
|
|
10
|
+
readonly writePolicy:
|
|
11
|
+
| "analysis_then_sequential_upsert"
|
|
12
|
+
| "review_then_sequential_upsert"
|
|
13
|
+
| "sequential_upsert"
|
|
14
|
+
| "execute_then_append_receipt"
|
|
15
|
+
| "refresh_then_sync"
|
|
16
|
+
| "runtime_recovery";
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const REPAIR_PHASES = {
|
|
20
|
+
requirement_source: {
|
|
21
|
+
order: 5,
|
|
22
|
+
objective:
|
|
23
|
+
"Bind the requirement to its current authored source before semantic analysis.",
|
|
24
|
+
workflowSteps: ["kb_query", "kb_validate_upsert", "kb_upsert", "kb_check"],
|
|
25
|
+
validationRules: ["required-fields", "no-dangling-refs"],
|
|
26
|
+
writePolicy: "sequential_upsert",
|
|
27
|
+
},
|
|
28
|
+
semantic_inventory: {
|
|
29
|
+
order: 10,
|
|
30
|
+
objective:
|
|
31
|
+
"Produce and persist a complete source-bound proposition inventory.",
|
|
32
|
+
workflowSteps: [
|
|
33
|
+
"kb_semantic_advisor",
|
|
34
|
+
"kb_validate_upsert",
|
|
35
|
+
"kb_upsert",
|
|
36
|
+
"kb_check",
|
|
37
|
+
],
|
|
38
|
+
validationRules: ["semantic-completeness", "logic-coverage"],
|
|
39
|
+
writePolicy: "analysis_then_sequential_upsert",
|
|
40
|
+
},
|
|
41
|
+
semantic_resolution: {
|
|
42
|
+
order: 20,
|
|
43
|
+
objective:
|
|
44
|
+
"Resolve ambiguity or ontology gaps without treating unresolved prose as modeled.",
|
|
45
|
+
workflowSteps: [
|
|
46
|
+
"kb_semantic_advisor",
|
|
47
|
+
"kb_suggest_predicates",
|
|
48
|
+
"kb_model_requirement",
|
|
49
|
+
"kb_validate_upsert",
|
|
50
|
+
"kb_upsert",
|
|
51
|
+
"kb_check",
|
|
52
|
+
],
|
|
53
|
+
validationRules: [
|
|
54
|
+
"semantic-completeness",
|
|
55
|
+
"logic-coverage",
|
|
56
|
+
"predicate-verifiability",
|
|
57
|
+
"rule-safety",
|
|
58
|
+
"rule-verifiability",
|
|
59
|
+
],
|
|
60
|
+
writePolicy: "review_then_sequential_upsert",
|
|
61
|
+
},
|
|
62
|
+
ground_endpoints: {
|
|
63
|
+
order: 30,
|
|
64
|
+
objective:
|
|
65
|
+
"Create or repair exactly one validated logical endpoint for each modeled proposition.",
|
|
66
|
+
workflowSteps: [
|
|
67
|
+
"kb_suggest_predicates",
|
|
68
|
+
"kb_model_requirement",
|
|
69
|
+
"kb_validate_upsert",
|
|
70
|
+
"kb_upsert",
|
|
71
|
+
"kb_check",
|
|
72
|
+
],
|
|
73
|
+
validationRules: [
|
|
74
|
+
"strict-fact-shape",
|
|
75
|
+
"predicate-verifiability",
|
|
76
|
+
"rule-safety",
|
|
77
|
+
"rule-verifiability",
|
|
78
|
+
"logic-coverage",
|
|
79
|
+
],
|
|
80
|
+
writePolicy: "review_then_sequential_upsert",
|
|
81
|
+
},
|
|
82
|
+
manifest_links: {
|
|
83
|
+
order: 40,
|
|
84
|
+
objective:
|
|
85
|
+
"Persist the exact claim manifest and link it one-to-one to existing logical endpoints.",
|
|
86
|
+
workflowSteps: ["kb_query", "kb_validate_upsert", "kb_upsert", "kb_check"],
|
|
87
|
+
validationRules: [
|
|
88
|
+
"semantic-completeness",
|
|
89
|
+
"logic-coverage",
|
|
90
|
+
"predicate-verifiability",
|
|
91
|
+
"rule-safety",
|
|
92
|
+
"rule-verifiability",
|
|
93
|
+
"no-dangling-refs",
|
|
94
|
+
],
|
|
95
|
+
writePolicy: "sequential_upsert",
|
|
96
|
+
},
|
|
97
|
+
contradiction_resolution: {
|
|
98
|
+
order: 50,
|
|
99
|
+
objective:
|
|
100
|
+
"Inspect exact contradiction witnesses and supersede or reconcile conflicting requirements.",
|
|
101
|
+
workflowSteps: ["kb_query", "kb_validate_upsert", "kb_upsert", "kb_check"],
|
|
102
|
+
validationRules: ["domain-contradictions", "logic-coverage"],
|
|
103
|
+
writePolicy: "review_then_sequential_upsert",
|
|
104
|
+
},
|
|
105
|
+
scenario_endpoints: {
|
|
106
|
+
order: 60,
|
|
107
|
+
objective:
|
|
108
|
+
"Create a scenario endpoint and link the requirement through specified_by.",
|
|
109
|
+
workflowSteps: ["kb_query", "kb_validate_upsert", "kb_upsert", "kb_check"],
|
|
110
|
+
validationRules: ["must-priority-coverage", "no-dangling-refs"],
|
|
111
|
+
writePolicy: "sequential_upsert",
|
|
112
|
+
},
|
|
113
|
+
scenario_tests: {
|
|
114
|
+
order: 70,
|
|
115
|
+
objective:
|
|
116
|
+
"Create or select a test endpoint and link it through the requirement scenario.",
|
|
117
|
+
workflowSteps: ["kb_query", "kb_validate_upsert", "kb_upsert", "kb_check"],
|
|
118
|
+
validationRules: ["must-priority-coverage", "no-dangling-refs"],
|
|
119
|
+
writePolicy: "sequential_upsert",
|
|
120
|
+
},
|
|
121
|
+
snapshot_runtime: {
|
|
122
|
+
order: 75,
|
|
123
|
+
objective:
|
|
124
|
+
"Use a runtime that exposes a deterministic workspace verification snapshot.",
|
|
125
|
+
workflowSteps: ["kb_status", "kb_coverage"],
|
|
126
|
+
validationRules: [],
|
|
127
|
+
writePolicy: "runtime_recovery",
|
|
128
|
+
},
|
|
129
|
+
verification_evidence: {
|
|
130
|
+
order: 80,
|
|
131
|
+
objective:
|
|
132
|
+
"Run the scenario-backed E2E test and append a fresh snapshot-bound passing receipt.",
|
|
133
|
+
workflowSteps: [
|
|
134
|
+
"kb_status",
|
|
135
|
+
"execute_e2e",
|
|
136
|
+
"kb_validate_upsert",
|
|
137
|
+
"kb_upsert",
|
|
138
|
+
"kb_coverage",
|
|
139
|
+
],
|
|
140
|
+
validationRules: ["required-fields", "no-dangling-refs"],
|
|
141
|
+
writePolicy: "execute_then_append_receipt",
|
|
142
|
+
},
|
|
143
|
+
executable_symbols: {
|
|
144
|
+
order: 90,
|
|
145
|
+
objective:
|
|
146
|
+
"Link executable test symbols to every qualifying E2E test through executable_for.",
|
|
147
|
+
workflowSteps: ["kb_query", "kb_validate_upsert", "kb_upsert", "kb_check"],
|
|
148
|
+
validationRules: ["symbol-traceability", "no-dangling-refs"],
|
|
149
|
+
writePolicy: "sequential_upsert",
|
|
150
|
+
},
|
|
151
|
+
production_ownership: {
|
|
152
|
+
order: 100,
|
|
153
|
+
objective: "Link production symbols to the requirement through implements.",
|
|
154
|
+
workflowSteps: ["kb_query", "kb_validate_upsert", "kb_upsert", "kb_check"],
|
|
155
|
+
validationRules: ["symbol-traceability", "no-dangling-refs"],
|
|
156
|
+
writePolicy: "sequential_upsert",
|
|
157
|
+
},
|
|
158
|
+
production_coverage: {
|
|
159
|
+
order: 110,
|
|
160
|
+
objective:
|
|
161
|
+
"Link every implementing production symbol to a qualifying E2E test through covered_by.",
|
|
162
|
+
workflowSteps: ["kb_query", "kb_validate_upsert", "kb_upsert", "kb_check"],
|
|
163
|
+
validationRules: [
|
|
164
|
+
"symbol-coverage",
|
|
165
|
+
"symbol-traceability",
|
|
166
|
+
"no-dangling-refs",
|
|
167
|
+
],
|
|
168
|
+
writePolicy: "sequential_upsert",
|
|
169
|
+
},
|
|
170
|
+
source_coordinates: {
|
|
171
|
+
order: 120,
|
|
172
|
+
objective:
|
|
173
|
+
"Refresh symbol extraction and persist exact current source coordinates.",
|
|
174
|
+
workflowSteps: [
|
|
175
|
+
"kibi sync --refresh-symbol-coordinates",
|
|
176
|
+
"kibi sync",
|
|
177
|
+
"kb_check",
|
|
178
|
+
"kb_coverage",
|
|
179
|
+
],
|
|
180
|
+
validationRules: ["symbol-coverage", "symbol-traceability"],
|
|
181
|
+
writePolicy: "refresh_then_sync",
|
|
182
|
+
},
|
|
183
|
+
manual_review: {
|
|
184
|
+
order: 900,
|
|
185
|
+
objective:
|
|
186
|
+
"Review the unclassified proof gap and choose a conservative repair workflow.",
|
|
187
|
+
workflowSteps: ["kb_query", "kb_check", "kb_coverage"],
|
|
188
|
+
validationRules: [],
|
|
189
|
+
writePolicy: "review_then_sequential_upsert",
|
|
190
|
+
},
|
|
191
|
+
} as const satisfies Readonly<Record<string, RepairPhaseDefinition>>;
|
|
192
|
+
|
|
193
|
+
type RepairPhase = keyof typeof REPAIR_PHASES;
|
|
194
|
+
|
|
195
|
+
const GAP_PHASE: Readonly<Record<string, RepairPhase>> = {
|
|
196
|
+
missing_requirement_source: "requirement_source",
|
|
197
|
+
missing_semantic_inventory: "semantic_inventory",
|
|
198
|
+
incomplete_semantic_inventory: "semantic_inventory",
|
|
199
|
+
malformed_semantic_inventory: "semantic_inventory",
|
|
200
|
+
unresolved_semantic_proposition: "semantic_resolution",
|
|
201
|
+
missing_logic_grounding: "ground_endpoints",
|
|
202
|
+
ambiguous_logic_grounding: "ground_endpoints",
|
|
203
|
+
contradiction_check_incomplete: "ground_endpoints",
|
|
204
|
+
missing_logic_claims: "manifest_links",
|
|
205
|
+
logic_manifest_mismatch: "manifest_links",
|
|
206
|
+
blocking_contradiction: "contradiction_resolution",
|
|
207
|
+
missing_scenario: "scenario_endpoints",
|
|
208
|
+
missing_scenario_test: "scenario_tests",
|
|
209
|
+
verification_snapshot_unavailable: "snapshot_runtime",
|
|
210
|
+
missing_passing_e2e: "verification_evidence",
|
|
211
|
+
missing_verification_receipt: "verification_evidence",
|
|
212
|
+
stale_verification_receipt: "verification_evidence",
|
|
213
|
+
failed_verification_receipt: "verification_evidence",
|
|
214
|
+
invalid_verification_receipt: "verification_evidence",
|
|
215
|
+
missing_executable_test_symbol: "executable_symbols",
|
|
216
|
+
missing_production_symbol: "production_ownership",
|
|
217
|
+
missing_production_symbol_coverage: "production_coverage",
|
|
218
|
+
missing_symbol_coordinates: "source_coordinates",
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const GAP_STAGE: Readonly<Record<string, string>> = {
|
|
222
|
+
missing_requirement_source: "source_coordinates",
|
|
223
|
+
missing_semantic_inventory: "semantic_inventory",
|
|
224
|
+
incomplete_semantic_inventory: "semantic_inventory",
|
|
225
|
+
malformed_semantic_inventory: "semantic_inventory",
|
|
226
|
+
unresolved_semantic_proposition: "semantic_inventory",
|
|
227
|
+
missing_logic_claims: "logic_grounding",
|
|
228
|
+
logic_manifest_mismatch: "logic_grounding",
|
|
229
|
+
missing_logic_grounding: "logic_grounding",
|
|
230
|
+
ambiguous_logic_grounding: "logic_grounding",
|
|
231
|
+
blocking_contradiction: "contradictions",
|
|
232
|
+
contradiction_check_incomplete: "contradictions",
|
|
233
|
+
missing_scenario: "scenarios",
|
|
234
|
+
missing_scenario_test: "scenario_tests",
|
|
235
|
+
missing_passing_e2e: "passing_e2e",
|
|
236
|
+
missing_verification_receipt: "passing_e2e",
|
|
237
|
+
stale_verification_receipt: "passing_e2e",
|
|
238
|
+
failed_verification_receipt: "passing_e2e",
|
|
239
|
+
invalid_verification_receipt: "passing_e2e",
|
|
240
|
+
verification_snapshot_unavailable: "passing_e2e",
|
|
241
|
+
missing_executable_test_symbol: "executable_symbols",
|
|
242
|
+
missing_production_symbol: "production_symbols",
|
|
243
|
+
missing_production_symbol_coverage: "production_symbols",
|
|
244
|
+
missing_symbol_coordinates: "source_coordinates",
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const PROOF_STAGE_KEY: Readonly<Record<string, string>> = {
|
|
248
|
+
semantic_inventory: "semanticInventory",
|
|
249
|
+
logic_grounding: "logicGrounding",
|
|
250
|
+
contradictions: "contradictions",
|
|
251
|
+
scenarios: "scenarios",
|
|
252
|
+
scenario_tests: "scenarioTests",
|
|
253
|
+
passing_e2e: "passingE2e",
|
|
254
|
+
executable_symbols: "executableSymbols",
|
|
255
|
+
production_symbols: "productionSymbols",
|
|
256
|
+
source_coordinates: "sourceCoordinates",
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
export type RepairPlanRepair = {
|
|
260
|
+
readonly requirementId: string;
|
|
261
|
+
readonly gap: string;
|
|
262
|
+
readonly proofPriority: number;
|
|
263
|
+
readonly proofStage: string;
|
|
264
|
+
readonly action: string;
|
|
265
|
+
readonly evidence: Readonly<Record<string, unknown>>;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
export type RepairPlanBatch = {
|
|
269
|
+
readonly id: string;
|
|
270
|
+
readonly order: number;
|
|
271
|
+
readonly phase: RepairPhase;
|
|
272
|
+
readonly requirementId: string;
|
|
273
|
+
readonly state: "ready" | "blocked";
|
|
274
|
+
readonly dependsOn: readonly string[];
|
|
275
|
+
readonly objective: string;
|
|
276
|
+
readonly workflowSteps: readonly string[];
|
|
277
|
+
readonly validationRules: readonly string[];
|
|
278
|
+
readonly writePolicy: RepairPhaseDefinition["writePolicy"];
|
|
279
|
+
readonly autoApplicable: false;
|
|
280
|
+
readonly repairs: readonly RepairPlanRepair[];
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
export type RepairPlan = {
|
|
284
|
+
readonly version: typeof REPAIR_PLAN_VERSION;
|
|
285
|
+
readonly planId: string;
|
|
286
|
+
readonly readOnly: true;
|
|
287
|
+
readonly status: "no_repairs" | "ready" | "partial";
|
|
288
|
+
readonly codeSnapshot: string;
|
|
289
|
+
readonly scope: {
|
|
290
|
+
readonly by: string;
|
|
291
|
+
readonly tags: readonly string[];
|
|
292
|
+
readonly offset: number;
|
|
293
|
+
readonly limit: number;
|
|
294
|
+
readonly complete: boolean;
|
|
295
|
+
readonly actionableRequirements: number;
|
|
296
|
+
readonly returnedActionableRequirements: number;
|
|
297
|
+
readonly excludedByPagination: number;
|
|
298
|
+
};
|
|
299
|
+
readonly applicationPolicy: {
|
|
300
|
+
readonly queryBeforeMutation: true;
|
|
301
|
+
readonly createEndpointsBeforeRelationships: true;
|
|
302
|
+
readonly validateBeforeEachWrite: true;
|
|
303
|
+
readonly sequentialUpsertsOnly: true;
|
|
304
|
+
readonly recheckCoverageAfterEachBatch: true;
|
|
305
|
+
};
|
|
306
|
+
readonly summary: {
|
|
307
|
+
readonly requirementCount: number;
|
|
308
|
+
readonly repairCount: number;
|
|
309
|
+
readonly batchCount: number;
|
|
310
|
+
readonly readyBatchCount: number;
|
|
311
|
+
readonly blockedBatchCount: number;
|
|
312
|
+
};
|
|
313
|
+
readonly batches: readonly RepairPlanBatch[];
|
|
314
|
+
readonly diagnostics: readonly string[];
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
export type RepairPlanInput = {
|
|
318
|
+
readonly by?: "req" | "symbol" | "type";
|
|
319
|
+
readonly tags?: readonly string[];
|
|
320
|
+
readonly limit?: number;
|
|
321
|
+
readonly offset?: number;
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
type CoverageForRepairPlan = {
|
|
325
|
+
readonly summary?: Readonly<Record<string, number>>;
|
|
326
|
+
readonly rows?: readonly Readonly<Record<string, unknown>>[];
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
function isRecord(value: unknown): value is Readonly<Record<string, unknown>> {
|
|
330
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function stringList(value: unknown): string[] {
|
|
334
|
+
if (!Array.isArray(value)) return [];
|
|
335
|
+
return value.filter((item): item is string => typeof item === "string");
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function stableValue(value: unknown): unknown {
|
|
339
|
+
if (Array.isArray(value)) return value.map(stableValue);
|
|
340
|
+
if (!isRecord(value)) return value;
|
|
341
|
+
return Object.fromEntries(
|
|
342
|
+
Object.entries(value)
|
|
343
|
+
.filter(([key]) => key !== "checkedAt" && key !== "ageSeconds")
|
|
344
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
345
|
+
.map(([key, item]) => [key, stableValue(item)]),
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function stableStringify(value: unknown): string {
|
|
350
|
+
return JSON.stringify(stableValue(value));
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function numberField(value: unknown, fallback: number): number {
|
|
354
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function actionForRepair(
|
|
358
|
+
repair: Readonly<Record<string, unknown>> | undefined,
|
|
359
|
+
phase: RepairPhase,
|
|
360
|
+
): string {
|
|
361
|
+
return typeof repair?.action === "string"
|
|
362
|
+
? repair.action
|
|
363
|
+
: REPAIR_PHASES[phase].objective;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function stageEvidence(
|
|
367
|
+
row: Readonly<Record<string, unknown>>,
|
|
368
|
+
proofStage: string,
|
|
369
|
+
): Readonly<Record<string, unknown>> {
|
|
370
|
+
const stages = isRecord(row.proofStages) ? row.proofStages : {};
|
|
371
|
+
const key = PROOF_STAGE_KEY[proofStage];
|
|
372
|
+
const evidence = key === undefined ? undefined : stages[key];
|
|
373
|
+
const stable = stableValue(evidence);
|
|
374
|
+
return isRecord(stable) ? stable : {};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function batchId(requirementId: string, phase: RepairPhase): string {
|
|
378
|
+
const order = String(REPAIR_PHASES[phase].order).padStart(3, "0");
|
|
379
|
+
const safeRequirementId = requirementId.replace(/[^A-Za-z0-9_-]/g, "-");
|
|
380
|
+
return `repair-${order}-${safeRequirementId}-${phase}`;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function buildRequirementBatches(
|
|
384
|
+
row: Readonly<Record<string, unknown>>,
|
|
385
|
+
): RepairPlanBatch[] {
|
|
386
|
+
const requirementId =
|
|
387
|
+
typeof row.id === "string" ? row.id : "unknown-requirement";
|
|
388
|
+
const gaps = [...new Set(stringList(row.proofGaps))].sort();
|
|
389
|
+
const repairs = Array.isArray(row.proofRepairs)
|
|
390
|
+
? row.proofRepairs.filter(isRecord)
|
|
391
|
+
: [];
|
|
392
|
+
const repairByGap = new Map(
|
|
393
|
+
repairs.flatMap((repair) =>
|
|
394
|
+
typeof repair.gap === "string" ? [[repair.gap, repair] as const] : [],
|
|
395
|
+
),
|
|
396
|
+
);
|
|
397
|
+
const byPhase = new Map<RepairPhase, RepairPlanRepair[]>();
|
|
398
|
+
|
|
399
|
+
for (const gap of gaps) {
|
|
400
|
+
const phase = GAP_PHASE[gap] ?? "manual_review";
|
|
401
|
+
const sourceRepair = repairByGap.get(gap);
|
|
402
|
+
const proofStage =
|
|
403
|
+
typeof sourceRepair?.stage === "string"
|
|
404
|
+
? sourceRepair.stage
|
|
405
|
+
: (GAP_STAGE[gap] ?? "manual_review");
|
|
406
|
+
const repair: RepairPlanRepair = {
|
|
407
|
+
requirementId,
|
|
408
|
+
gap,
|
|
409
|
+
proofPriority: numberField(
|
|
410
|
+
sourceRepair?.priority,
|
|
411
|
+
REPAIR_PHASES[phase].order,
|
|
412
|
+
),
|
|
413
|
+
proofStage,
|
|
414
|
+
action: actionForRepair(sourceRepair, phase),
|
|
415
|
+
evidence: stageEvidence(row, proofStage),
|
|
416
|
+
};
|
|
417
|
+
const phaseRepairs = byPhase.get(phase) ?? [];
|
|
418
|
+
phaseRepairs.push(repair);
|
|
419
|
+
byPhase.set(phase, phaseRepairs);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const phases = [...byPhase.keys()].sort(
|
|
423
|
+
(left, right) =>
|
|
424
|
+
REPAIR_PHASES[left].order - REPAIR_PHASES[right].order ||
|
|
425
|
+
left.localeCompare(right),
|
|
426
|
+
);
|
|
427
|
+
const priorBatchIds: string[] = [];
|
|
428
|
+
return phases.map((phase) => {
|
|
429
|
+
const definition = REPAIR_PHASES[phase];
|
|
430
|
+
const id = batchId(requirementId, phase);
|
|
431
|
+
const batch: RepairPlanBatch = {
|
|
432
|
+
id,
|
|
433
|
+
order: definition.order,
|
|
434
|
+
phase,
|
|
435
|
+
requirementId,
|
|
436
|
+
state: priorBatchIds.length === 0 ? "ready" : "blocked",
|
|
437
|
+
dependsOn: [...priorBatchIds],
|
|
438
|
+
objective: definition.objective,
|
|
439
|
+
workflowSteps: definition.workflowSteps,
|
|
440
|
+
validationRules: definition.validationRules,
|
|
441
|
+
writePolicy: definition.writePolicy,
|
|
442
|
+
autoApplicable: false,
|
|
443
|
+
repairs: (byPhase.get(phase) ?? []).sort(
|
|
444
|
+
(left, right) =>
|
|
445
|
+
left.proofPriority - right.proofPriority ||
|
|
446
|
+
left.gap.localeCompare(right.gap),
|
|
447
|
+
),
|
|
448
|
+
};
|
|
449
|
+
priorBatchIds.push(id);
|
|
450
|
+
return batch;
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function actionableRequirementTotal(
|
|
455
|
+
summary: Readonly<Record<string, number>>,
|
|
456
|
+
fallback: number,
|
|
457
|
+
): number {
|
|
458
|
+
const missing = summary.proofMissing;
|
|
459
|
+
const unresolved = summary.proofUnresolved;
|
|
460
|
+
if (typeof missing === "number" && typeof unresolved === "number") {
|
|
461
|
+
return Math.max(0, missing + unresolved);
|
|
462
|
+
}
|
|
463
|
+
return fallback;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
export function buildRepairPlan(
|
|
467
|
+
payload: CoverageForRepairPlan,
|
|
468
|
+
input: RepairPlanInput,
|
|
469
|
+
codeSnapshot: string,
|
|
470
|
+
): RepairPlan | undefined {
|
|
471
|
+
const by = input.by ?? "req";
|
|
472
|
+
if (by !== "req") return undefined;
|
|
473
|
+
|
|
474
|
+
const rows = payload.rows ?? [];
|
|
475
|
+
const requirementRows = rows.filter(
|
|
476
|
+
(row) => typeof row.id === "string" && stringList(row.proofGaps).length > 0,
|
|
477
|
+
);
|
|
478
|
+
const batches = requirementRows
|
|
479
|
+
.flatMap(buildRequirementBatches)
|
|
480
|
+
.sort(
|
|
481
|
+
(left, right) =>
|
|
482
|
+
left.order - right.order ||
|
|
483
|
+
left.requirementId.localeCompare(right.requirementId) ||
|
|
484
|
+
left.id.localeCompare(right.id),
|
|
485
|
+
);
|
|
486
|
+
const requirementCount = new Set(batches.map((batch) => batch.requirementId))
|
|
487
|
+
.size;
|
|
488
|
+
const summary = payload.summary ?? {};
|
|
489
|
+
const actionableRequirements = actionableRequirementTotal(
|
|
490
|
+
summary,
|
|
491
|
+
requirementCount,
|
|
492
|
+
);
|
|
493
|
+
const offset = input.offset ?? 0;
|
|
494
|
+
const limit = input.limit ?? 100;
|
|
495
|
+
const complete = offset === 0 && requirementCount >= actionableRequirements;
|
|
496
|
+
const excludedByPagination = Math.max(
|
|
497
|
+
0,
|
|
498
|
+
actionableRequirements - requirementCount,
|
|
499
|
+
);
|
|
500
|
+
const repairCount = batches.reduce(
|
|
501
|
+
(count, batch) => count + batch.repairs.length,
|
|
502
|
+
0,
|
|
503
|
+
);
|
|
504
|
+
const readyBatchCount = batches.filter(
|
|
505
|
+
(batch) => batch.state === "ready",
|
|
506
|
+
).length;
|
|
507
|
+
const blockedBatchCount = batches.length - readyBatchCount;
|
|
508
|
+
const diagnostics = complete
|
|
509
|
+
? []
|
|
510
|
+
: [
|
|
511
|
+
`Repair plan is partial: ${excludedByPagination} actionable requirement(s) were excluded by coverage pagination; rerun with offset 0 and a larger limit.`,
|
|
512
|
+
];
|
|
513
|
+
const planCore = {
|
|
514
|
+
version: REPAIR_PLAN_VERSION,
|
|
515
|
+
codeSnapshot,
|
|
516
|
+
scope: {
|
|
517
|
+
by,
|
|
518
|
+
tags: [...(input.tags ?? [])].sort(),
|
|
519
|
+
offset,
|
|
520
|
+
limit,
|
|
521
|
+
complete,
|
|
522
|
+
actionableRequirements,
|
|
523
|
+
returnedActionableRequirements: requirementCount,
|
|
524
|
+
excludedByPagination,
|
|
525
|
+
},
|
|
526
|
+
batches,
|
|
527
|
+
};
|
|
528
|
+
const planId = `repair-plan-${createHash("sha256")
|
|
529
|
+
.update(stableStringify(planCore))
|
|
530
|
+
.digest("hex")
|
|
531
|
+
.slice(0, 24)}`;
|
|
532
|
+
|
|
533
|
+
return {
|
|
534
|
+
version: REPAIR_PLAN_VERSION,
|
|
535
|
+
planId,
|
|
536
|
+
readOnly: true,
|
|
537
|
+
status:
|
|
538
|
+
repairCount === 0 && complete
|
|
539
|
+
? "no_repairs"
|
|
540
|
+
: complete
|
|
541
|
+
? "ready"
|
|
542
|
+
: "partial",
|
|
543
|
+
codeSnapshot,
|
|
544
|
+
scope: planCore.scope,
|
|
545
|
+
applicationPolicy: {
|
|
546
|
+
queryBeforeMutation: true,
|
|
547
|
+
createEndpointsBeforeRelationships: true,
|
|
548
|
+
validateBeforeEachWrite: true,
|
|
549
|
+
sequentialUpsertsOnly: true,
|
|
550
|
+
recheckCoverageAfterEachBatch: true,
|
|
551
|
+
},
|
|
552
|
+
summary: {
|
|
553
|
+
requirementCount,
|
|
554
|
+
repairCount,
|
|
555
|
+
batchCount: batches.length,
|
|
556
|
+
readyBatchCount,
|
|
557
|
+
blockedBatchCount,
|
|
558
|
+
},
|
|
559
|
+
batches,
|
|
560
|
+
diagnostics,
|
|
561
|
+
};
|
|
562
|
+
}
|