@workflow-cannon/workspace-kit 0.17.0 → 0.24.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/README.md +23 -9
- package/dist/cli/doctor-planning-issues.js +3 -22
- package/dist/cli/run-command.js +22 -38
- package/dist/cli.js +95 -4
- package/dist/contracts/command-manifest.d.ts +17 -0
- package/dist/contracts/command-manifest.js +1 -0
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/module-contract.d.ts +12 -11
- package/dist/core/agent-instruction-surface.d.ts +33 -0
- package/dist/core/agent-instruction-surface.js +46 -0
- package/dist/core/config-cli.js +13 -17
- package/dist/core/config-metadata.js +101 -2
- package/dist/core/index.d.ts +4 -1
- package/dist/core/index.js +3 -0
- package/dist/core/module-command-router.js +19 -1
- package/dist/core/module-registry-resolve.d.ts +27 -0
- package/dist/core/module-registry-resolve.js +91 -0
- package/dist/core/module-registry.d.ts +14 -0
- package/dist/core/module-registry.js +57 -0
- package/dist/core/planning/build-plan-session-file.d.ts +29 -0
- package/dist/core/planning/build-plan-session-file.js +58 -0
- package/dist/core/planning/index.d.ts +17 -0
- package/dist/core/planning/index.js +15 -0
- package/dist/core/policy.js +18 -8
- package/dist/core/state/unified-state-db.d.ts +21 -0
- package/dist/core/state/unified-state-db.js +80 -0
- package/dist/core/workspace-kit-config.js +13 -1
- package/dist/modules/agent-behavior/builtins.d.ts +3 -0
- package/dist/modules/agent-behavior/builtins.js +71 -0
- package/dist/modules/agent-behavior/explain.d.ts +6 -0
- package/dist/modules/agent-behavior/explain.js +46 -0
- package/dist/modules/agent-behavior/index.d.ts +4 -0
- package/dist/modules/agent-behavior/index.js +461 -0
- package/dist/modules/agent-behavior/interview-session-file.d.ts +9 -0
- package/dist/modules/agent-behavior/interview-session-file.js +43 -0
- package/dist/modules/agent-behavior/interview.d.ts +13 -0
- package/dist/modules/agent-behavior/interview.js +88 -0
- package/dist/modules/agent-behavior/persistence.d.ts +6 -0
- package/dist/modules/agent-behavior/persistence.js +89 -0
- package/dist/modules/agent-behavior/store.d.ts +34 -0
- package/dist/modules/agent-behavior/store.js +119 -0
- package/dist/modules/agent-behavior/types.d.ts +28 -0
- package/dist/modules/agent-behavior/types.js +1 -0
- package/dist/modules/agent-behavior/validate.d.ts +11 -0
- package/dist/modules/agent-behavior/validate.js +123 -0
- package/dist/modules/approvals/index.js +54 -51
- package/dist/modules/approvals/policy-sensitive-commands.d.ts +4 -0
- package/dist/modules/approvals/policy-sensitive-commands.js +4 -0
- package/dist/modules/approvals/review-runtime.js +1 -2
- package/dist/modules/documentation/index.js +47 -45
- package/dist/modules/documentation/normalizer.d.ts +3 -0
- package/dist/modules/documentation/normalizer.js +171 -0
- package/dist/modules/documentation/parser.d.ts +7 -0
- package/dist/modules/documentation/parser.js +39 -0
- package/dist/modules/documentation/policy-sensitive-commands.d.ts +5 -0
- package/dist/modules/documentation/policy-sensitive-commands.js +8 -0
- package/dist/modules/documentation/renderer.d.ts +23 -0
- package/dist/modules/documentation/renderer.js +105 -0
- package/dist/modules/documentation/runtime-batch.d.ts +10 -0
- package/dist/modules/documentation/runtime-batch.js +67 -0
- package/dist/modules/documentation/runtime-config.d.ts +11 -0
- package/dist/modules/documentation/runtime-config.js +54 -0
- package/dist/modules/documentation/runtime-render-support.d.ts +8 -0
- package/dist/modules/documentation/runtime-render-support.js +36 -0
- package/dist/modules/documentation/runtime.js +22 -510
- package/dist/modules/documentation/types.d.ts +182 -0
- package/dist/modules/documentation/validator.d.ts +8 -0
- package/dist/modules/documentation/validator.js +234 -0
- package/dist/modules/documentation/view-models.d.ts +3 -0
- package/dist/modules/documentation/view-models.js +124 -0
- package/dist/modules/improvement/generate-recommendations-runtime.js +3 -3
- package/dist/modules/improvement/improvement-state.d.ts +2 -2
- package/dist/modules/improvement/improvement-state.js +52 -23
- package/dist/modules/improvement/index.js +140 -138
- package/dist/modules/improvement/ingest.d.ts +1 -1
- package/dist/modules/improvement/policy-sensitive-commands.d.ts +4 -0
- package/dist/modules/improvement/policy-sensitive-commands.js +7 -0
- package/dist/modules/index.d.ts +6 -0
- package/dist/modules/index.js +17 -0
- package/dist/modules/planning/artifact.d.ts +19 -0
- package/dist/modules/planning/artifact.js +72 -0
- package/dist/modules/planning/index.js +605 -6
- package/dist/modules/planning/question-engine.d.ts +25 -0
- package/dist/modules/planning/question-engine.js +284 -0
- package/dist/modules/planning/types.d.ts +9 -0
- package/dist/modules/planning/types.js +39 -0
- package/dist/modules/task-engine/doctor-planning-persistence.js +21 -13
- package/dist/modules/task-engine/index.d.ts +1 -2
- package/dist/modules/task-engine/index.js +1 -1143
- package/dist/modules/task-engine/migrate-task-persistence-runtime.js +31 -4
- package/dist/modules/task-engine/migrate-wishlist-intake-runtime.d.ts +2 -0
- package/dist/modules/task-engine/migrate-wishlist-intake-runtime.js +146 -0
- package/dist/modules/task-engine/planning-open.d.ts +2 -9
- package/dist/modules/task-engine/planning-open.js +4 -15
- package/dist/modules/task-engine/policy-sensitive-commands.d.ts +5 -0
- package/dist/modules/task-engine/policy-sensitive-commands.js +5 -0
- package/dist/modules/task-engine/sqlite-dual-planning.d.ts +11 -2
- package/dist/modules/task-engine/sqlite-dual-planning.js +134 -28
- package/dist/modules/task-engine/strict-task-validation.js +3 -0
- package/dist/modules/task-engine/suggestions.js +2 -1
- package/dist/modules/task-engine/task-engine-internal.d.ts +2 -0
- package/dist/modules/task-engine/task-engine-internal.js +1304 -0
- package/dist/modules/task-engine/task-type-validation.js +40 -0
- package/dist/modules/task-engine/wishlist-intake.d.ts +22 -0
- package/dist/modules/task-engine/wishlist-intake.js +180 -0
- package/dist/modules/task-engine/wishlist-validation.d.ts +4 -0
- package/dist/modules/task-engine/wishlist-validation.js +19 -0
- package/dist/modules/workspace-config/index.js +9 -11
- package/package.json +2 -2
- package/schemas/agent-behavior-profile.schema.json +52 -0
- package/schemas/task-engine-run-contracts.schema.json +80 -5
- package/src/modules/documentation/README.md +16 -25
- package/src/modules/documentation/RULES.md +9 -9
- package/src/modules/documentation/index.ts +54 -49
- package/src/modules/documentation/instructions/document-project.md +6 -6
- package/src/modules/documentation/instructions/generate-document.md +4 -4
- package/src/modules/documentation/normalizer.ts +187 -0
- package/src/modules/documentation/parser.ts +41 -0
- package/src/modules/documentation/policy-sensitive-commands.ts +8 -0
- package/src/modules/documentation/renderer.ts +121 -0
- package/src/modules/documentation/runtime-batch.ts +74 -0
- package/src/modules/documentation/runtime-config.ts +68 -0
- package/src/modules/documentation/runtime-render-support.ts +39 -0
- package/src/modules/documentation/runtime.ts +28 -600
- package/src/modules/documentation/schemas/documentation-schema.md +37 -54
- package/src/modules/documentation/types.ts +228 -0
- package/src/modules/documentation/validator.ts +247 -0
- package/src/modules/documentation/view-models.ts +132 -0
- package/src/modules/documentation/views/agents.view.yaml +18 -0
- package/src/modules/documentation/views/architecture.view.yaml +18 -0
- package/src/modules/documentation/views/principles.view.yaml +18 -0
- package/src/modules/documentation/views/readme.view.yaml +18 -0
- package/src/modules/documentation/views/releasing.view.yaml +18 -0
- package/src/modules/documentation/views/roadmap.view.yaml +18 -0
- package/src/modules/documentation/views/runbooks-consumer-cadence.view.yaml +18 -0
- package/src/modules/documentation/views/runbooks-parity-validation-flow.view.yaml +18 -0
- package/src/modules/documentation/views/runbooks-release-channels.view.yaml +18 -0
- package/src/modules/documentation/views/security.view.yaml +18 -0
- package/src/modules/documentation/views/support.view.yaml +18 -0
- package/src/modules/documentation/views/terms.view.yaml +18 -0
- package/src/modules/documentation/views/workbooks-phase2-config-policy-workbook.view.yaml +18 -0
- package/src/modules/documentation/views/workbooks-task-engine-workbook.view.yaml +18 -0
- package/src/modules/documentation/views/workbooks-transcript-automation-baseline.view.yaml +18 -0
- package/src/modules/documentation/state.md +0 -8
|
@@ -44,3 +44,185 @@ export type DocumentationBatchResult = {
|
|
|
44
44
|
timestamp: string;
|
|
45
45
|
};
|
|
46
46
|
};
|
|
47
|
+
export type NormalizedRecordStatus = "active" | "deprecated" | "draft" | "observed" | "planned";
|
|
48
|
+
export type NormalizedBaseRecord = {
|
|
49
|
+
id?: string;
|
|
50
|
+
status?: NormalizedRecordStatus;
|
|
51
|
+
refs?: string[];
|
|
52
|
+
};
|
|
53
|
+
export type NormalizedMeta = NormalizedBaseRecord & {
|
|
54
|
+
schema: "base.v2";
|
|
55
|
+
doc: string;
|
|
56
|
+
truth: "canonical" | "observed" | "planned";
|
|
57
|
+
profile?: "core" | "runbook" | "workbook";
|
|
58
|
+
title?: string;
|
|
59
|
+
owner?: string;
|
|
60
|
+
tags?: string[];
|
|
61
|
+
};
|
|
62
|
+
export type NormalizedRef = NormalizedBaseRecord & {
|
|
63
|
+
id: string;
|
|
64
|
+
type: "adr" | "file" | "code" | "doc" | "issue" | "pr" | "test" | "external";
|
|
65
|
+
target: string;
|
|
66
|
+
anchor?: string;
|
|
67
|
+
label?: string;
|
|
68
|
+
note?: string;
|
|
69
|
+
};
|
|
70
|
+
export type NormalizedRule = NormalizedBaseRecord & {
|
|
71
|
+
id: string;
|
|
72
|
+
level: "must" | "must_not" | "should" | "may";
|
|
73
|
+
scope: string;
|
|
74
|
+
scope_kind?: string;
|
|
75
|
+
kind?: string;
|
|
76
|
+
directive: string;
|
|
77
|
+
why: string;
|
|
78
|
+
unless?: string;
|
|
79
|
+
also?: string[];
|
|
80
|
+
risk?: "low" | "medium" | "high" | "critical";
|
|
81
|
+
approval?: "none" | "prompt" | "required";
|
|
82
|
+
override?: "auto" | "warn" | "prompt" | "stop";
|
|
83
|
+
};
|
|
84
|
+
export type NormalizedCheck = NormalizedBaseRecord & {
|
|
85
|
+
id: string;
|
|
86
|
+
scope: string;
|
|
87
|
+
assertion: string;
|
|
88
|
+
when?: string;
|
|
89
|
+
onFail?: "auto" | "warn" | "prompt" | "stop";
|
|
90
|
+
};
|
|
91
|
+
export type NormalizedDecision = NormalizedBaseRecord & {
|
|
92
|
+
id: string;
|
|
93
|
+
topic: string;
|
|
94
|
+
choice: string;
|
|
95
|
+
why: string;
|
|
96
|
+
consequence?: string;
|
|
97
|
+
};
|
|
98
|
+
export type NormalizedExample = NormalizedBaseRecord & {
|
|
99
|
+
id: string;
|
|
100
|
+
for: string;
|
|
101
|
+
kind: "good" | "bad" | "edge";
|
|
102
|
+
text: string;
|
|
103
|
+
};
|
|
104
|
+
export type NormalizedTerm = NormalizedBaseRecord & {
|
|
105
|
+
name: string;
|
|
106
|
+
definition: string;
|
|
107
|
+
};
|
|
108
|
+
export type NormalizedCommand = NormalizedBaseRecord & {
|
|
109
|
+
id: string;
|
|
110
|
+
name: string;
|
|
111
|
+
use: string;
|
|
112
|
+
scope: string;
|
|
113
|
+
expectation: string;
|
|
114
|
+
risk?: "low" | "medium" | "high" | "critical";
|
|
115
|
+
sensitivity?: "non_sensitive" | "policy_sensitive";
|
|
116
|
+
};
|
|
117
|
+
export type NormalizedWorkflow = NormalizedBaseRecord & {
|
|
118
|
+
id: string;
|
|
119
|
+
name: string;
|
|
120
|
+
when: string;
|
|
121
|
+
steps: string[];
|
|
122
|
+
done: string[];
|
|
123
|
+
forbid?: string[];
|
|
124
|
+
askIf?: string;
|
|
125
|
+
haltIf?: string;
|
|
126
|
+
approval?: "none" | "prompt" | "required";
|
|
127
|
+
risk?: "low" | "medium" | "high" | "critical";
|
|
128
|
+
};
|
|
129
|
+
export type NormalizedRunbook = NormalizedBaseRecord & {
|
|
130
|
+
name: string;
|
|
131
|
+
scope: string;
|
|
132
|
+
owner: string;
|
|
133
|
+
};
|
|
134
|
+
export type NormalizedWorkbook = NormalizedBaseRecord & {
|
|
135
|
+
name: string;
|
|
136
|
+
phase: string;
|
|
137
|
+
state: string;
|
|
138
|
+
};
|
|
139
|
+
export type NormalizedChain = NormalizedBaseRecord & {
|
|
140
|
+
step: string;
|
|
141
|
+
command: string;
|
|
142
|
+
expectExit: number;
|
|
143
|
+
};
|
|
144
|
+
export type NormalizedState = NormalizedBaseRecord & {
|
|
145
|
+
name: string;
|
|
146
|
+
distTag: string;
|
|
147
|
+
intent: string;
|
|
148
|
+
};
|
|
149
|
+
export type NormalizedTransition = NormalizedBaseRecord & {
|
|
150
|
+
from: string;
|
|
151
|
+
to: string;
|
|
152
|
+
requires: string[];
|
|
153
|
+
};
|
|
154
|
+
export type NormalizedPromotion = NormalizedBaseRecord & {
|
|
155
|
+
from: string;
|
|
156
|
+
to: string;
|
|
157
|
+
requires: string[];
|
|
158
|
+
};
|
|
159
|
+
export type NormalizedRollback = NormalizedBaseRecord & {
|
|
160
|
+
strategy: string;
|
|
161
|
+
note: string;
|
|
162
|
+
};
|
|
163
|
+
export type NormalizedArtifact = NormalizedBaseRecord & {
|
|
164
|
+
path: string;
|
|
165
|
+
schema: string;
|
|
166
|
+
};
|
|
167
|
+
export type NormalizedConfig = NormalizedBaseRecord & {
|
|
168
|
+
key: string;
|
|
169
|
+
default: string;
|
|
170
|
+
};
|
|
171
|
+
export type NormalizedCadence = NormalizedBaseRecord & {
|
|
172
|
+
rule: string;
|
|
173
|
+
};
|
|
174
|
+
export type NormalizedGuardrail = NormalizedBaseRecord & {
|
|
175
|
+
id: string;
|
|
176
|
+
level: "must" | "must_not" | "should" | "may";
|
|
177
|
+
directive: string;
|
|
178
|
+
why: string;
|
|
179
|
+
};
|
|
180
|
+
export type NormalizedDocument = {
|
|
181
|
+
meta: NormalizedMeta | null;
|
|
182
|
+
refs: NormalizedRef[];
|
|
183
|
+
rules: NormalizedRule[];
|
|
184
|
+
checks: NormalizedCheck[];
|
|
185
|
+
decisions: NormalizedDecision[];
|
|
186
|
+
examples: NormalizedExample[];
|
|
187
|
+
terms: NormalizedTerm[];
|
|
188
|
+
commands: NormalizedCommand[];
|
|
189
|
+
workflows: NormalizedWorkflow[];
|
|
190
|
+
runbooks: NormalizedRunbook[];
|
|
191
|
+
workbooks: NormalizedWorkbook[];
|
|
192
|
+
chains: NormalizedChain[];
|
|
193
|
+
states: NormalizedState[];
|
|
194
|
+
transitions: NormalizedTransition[];
|
|
195
|
+
promotions: NormalizedPromotion[];
|
|
196
|
+
rollbacks: NormalizedRollback[];
|
|
197
|
+
artifacts: NormalizedArtifact[];
|
|
198
|
+
configs: NormalizedConfig[];
|
|
199
|
+
cadences: NormalizedCadence[];
|
|
200
|
+
guardrails: NormalizedGuardrail[];
|
|
201
|
+
refsById: Map<string, NormalizedRef>;
|
|
202
|
+
examplesByParent: Map<string, NormalizedExample[]>;
|
|
203
|
+
profileRecords: Map<"core" | "runbook" | "workbook", Array<NormalizedBaseRecord>>;
|
|
204
|
+
};
|
|
205
|
+
export type ViewModelSection = {
|
|
206
|
+
id: string;
|
|
207
|
+
title?: string;
|
|
208
|
+
description?: string;
|
|
209
|
+
renderer: string;
|
|
210
|
+
source: "meta" | "refs" | "rules" | "checks" | "decisions" | "examples" | "terms" | "commands" | "workflows" | "runbooks" | "workbooks" | "chains" | "states" | "transitions" | "promotions" | "rollbacks" | "artifacts" | "configs" | "cadences" | "guardrails";
|
|
211
|
+
where?: Record<string, string | number | boolean>;
|
|
212
|
+
sortBy?: string[];
|
|
213
|
+
template?: string;
|
|
214
|
+
};
|
|
215
|
+
export type ViewRenderPolicy = {
|
|
216
|
+
id: string;
|
|
217
|
+
mode: "append" | "replace" | "fallback";
|
|
218
|
+
when?: string;
|
|
219
|
+
};
|
|
220
|
+
export type ViewModelDefinition = {
|
|
221
|
+
id: string;
|
|
222
|
+
version: number;
|
|
223
|
+
docType: string;
|
|
224
|
+
target: string;
|
|
225
|
+
profile?: "core" | "runbook" | "workbook";
|
|
226
|
+
sections: ViewModelSection[];
|
|
227
|
+
renderPolicies?: ViewRenderPolicy[];
|
|
228
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DocumentationValidationIssue } from "./types.js";
|
|
2
|
+
export type AiValidationContext = {
|
|
3
|
+
strict: boolean;
|
|
4
|
+
workspacePath: string;
|
|
5
|
+
expectedDoc?: "rules" | "runbook" | "workbook";
|
|
6
|
+
};
|
|
7
|
+
export declare function validateAiSchema(aiOutput: string, ctx: AiValidationContext): DocumentationValidationIssue[];
|
|
8
|
+
export declare function autoResolveAiSchema(aiOutput: string): string;
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { parseAiDocument } from "./parser.js";
|
|
4
|
+
const REF_TYPES = new Set(["adr", "file", "code", "doc", "issue", "pr", "test", "external"]);
|
|
5
|
+
const REQUIRED_BY_PROFILE = {
|
|
6
|
+
core: ["meta", "ref", "rule", "check", "decision", "example", "term", "command", "workflow"],
|
|
7
|
+
runbook: [
|
|
8
|
+
"meta",
|
|
9
|
+
"ref",
|
|
10
|
+
"rule",
|
|
11
|
+
"check",
|
|
12
|
+
"decision",
|
|
13
|
+
"example",
|
|
14
|
+
"term",
|
|
15
|
+
"command",
|
|
16
|
+
"workflow",
|
|
17
|
+
"runbook",
|
|
18
|
+
"chain",
|
|
19
|
+
"artifact",
|
|
20
|
+
"state",
|
|
21
|
+
"transition",
|
|
22
|
+
"promotion",
|
|
23
|
+
"rollback",
|
|
24
|
+
"config",
|
|
25
|
+
"cadence",
|
|
26
|
+
"guardrail"
|
|
27
|
+
],
|
|
28
|
+
workbook: [
|
|
29
|
+
"meta",
|
|
30
|
+
"ref",
|
|
31
|
+
"rule",
|
|
32
|
+
"check",
|
|
33
|
+
"decision",
|
|
34
|
+
"example",
|
|
35
|
+
"term",
|
|
36
|
+
"command",
|
|
37
|
+
"workflow",
|
|
38
|
+
"workbook",
|
|
39
|
+
"state",
|
|
40
|
+
"transition",
|
|
41
|
+
"artifact",
|
|
42
|
+
"guardrail"
|
|
43
|
+
]
|
|
44
|
+
};
|
|
45
|
+
function isAllowedMetaDoc(doc) {
|
|
46
|
+
return (doc === "rules" ||
|
|
47
|
+
doc === "runbook" ||
|
|
48
|
+
doc === "workbook" ||
|
|
49
|
+
doc === "generator" ||
|
|
50
|
+
doc === "map" ||
|
|
51
|
+
doc === "workflows" ||
|
|
52
|
+
doc === "commands" ||
|
|
53
|
+
doc === "decisions" ||
|
|
54
|
+
doc === "glossary" ||
|
|
55
|
+
doc === "observed" ||
|
|
56
|
+
doc === "planned" ||
|
|
57
|
+
doc === "checks" ||
|
|
58
|
+
doc === "manifest");
|
|
59
|
+
}
|
|
60
|
+
function profileForDoc(doc, explicitProfile) {
|
|
61
|
+
if (explicitProfile === "core" || explicitProfile === "runbook" || explicitProfile === "workbook") {
|
|
62
|
+
return explicitProfile;
|
|
63
|
+
}
|
|
64
|
+
if (doc === "runbook")
|
|
65
|
+
return "runbook";
|
|
66
|
+
if (doc === "workbook")
|
|
67
|
+
return "workbook";
|
|
68
|
+
return "core";
|
|
69
|
+
}
|
|
70
|
+
export function validateAiSchema(aiOutput, ctx) {
|
|
71
|
+
const issues = [];
|
|
72
|
+
const records = parseAiDocument(aiOutput);
|
|
73
|
+
if (records.length === 0) {
|
|
74
|
+
return [{ check: "schema", message: "AI output is empty", resolved: false }];
|
|
75
|
+
}
|
|
76
|
+
const meta = records[0];
|
|
77
|
+
if (!meta || meta.type !== "meta") {
|
|
78
|
+
return [{ check: "schema", message: "AI output must start with a meta record", resolved: false }];
|
|
79
|
+
}
|
|
80
|
+
const schema = meta.kv["schema"];
|
|
81
|
+
const doc = meta.kv["doc"];
|
|
82
|
+
const truth = meta.kv["truth"];
|
|
83
|
+
const status = meta.kv["status"] ?? meta.kv["st"];
|
|
84
|
+
const profile = profileForDoc(doc, meta.kv["profile"]);
|
|
85
|
+
const isLegacySchema = schema !== "base.v2";
|
|
86
|
+
if (schema !== "base.v2") {
|
|
87
|
+
issues.push({
|
|
88
|
+
check: "schema",
|
|
89
|
+
message: `AI meta.schema must be 'base.v2' (found '${schema ?? ""}')`,
|
|
90
|
+
resolved: true
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (!doc || !isAllowedMetaDoc(doc)) {
|
|
94
|
+
issues.push({ check: "schema", message: `Unsupported meta.doc '${doc ?? ""}'`, resolved: false });
|
|
95
|
+
}
|
|
96
|
+
if (!truth)
|
|
97
|
+
issues.push({ check: "schema", message: "AI meta.truth is required", resolved: false });
|
|
98
|
+
if (!status)
|
|
99
|
+
issues.push({ check: "schema", message: "AI meta.status is required", resolved: false });
|
|
100
|
+
if (ctx.expectedDoc && doc && ctx.expectedDoc !== doc) {
|
|
101
|
+
issues.push({
|
|
102
|
+
check: "schema",
|
|
103
|
+
message: `meta.doc '${doc}' does not match expected doc family for '${ctx.expectedDoc}'`,
|
|
104
|
+
resolved: !ctx.strict
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
const presentByType = { meta: true };
|
|
108
|
+
const knownIds = new Set();
|
|
109
|
+
for (const rec of records.slice(1)) {
|
|
110
|
+
presentByType[rec.type] = true;
|
|
111
|
+
const id = rec.kv["id"];
|
|
112
|
+
if (id)
|
|
113
|
+
knownIds.add(id);
|
|
114
|
+
if (rec.kv["name"])
|
|
115
|
+
knownIds.add(rec.kv["name"]);
|
|
116
|
+
}
|
|
117
|
+
for (const rec of records.slice(1)) {
|
|
118
|
+
if (rec.type === "rule" && !rec.kv["why"]) {
|
|
119
|
+
issues.push({
|
|
120
|
+
check: "schema",
|
|
121
|
+
message: "rule records require why",
|
|
122
|
+
resolved: isLegacySchema || !ctx.strict
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
if (rec.type === "ref") {
|
|
126
|
+
const type = rec.kv["type"];
|
|
127
|
+
const target = rec.kv["target"] ?? rec.kv["path"];
|
|
128
|
+
const legacyPath = rec.kv["path"];
|
|
129
|
+
if (!rec.kv["id"] || !type || !target) {
|
|
130
|
+
issues.push({
|
|
131
|
+
check: "schema",
|
|
132
|
+
message: "ref records require id, type, target",
|
|
133
|
+
resolved: isLegacySchema || !ctx.strict
|
|
134
|
+
});
|
|
135
|
+
if (legacyPath && !existsSync(resolve(ctx.workspacePath, legacyPath))) {
|
|
136
|
+
issues.push({
|
|
137
|
+
check: "schema",
|
|
138
|
+
message: `ref.path does not exist: '${legacyPath}'`,
|
|
139
|
+
resolved: !ctx.strict
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else if (!REF_TYPES.has(type)) {
|
|
144
|
+
issues.push({
|
|
145
|
+
check: "schema",
|
|
146
|
+
message: `ref.type '${type}' must be one of ${[...REF_TYPES].join(", ")}`,
|
|
147
|
+
resolved: isLegacySchema || !ctx.strict
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
else if ((type === "file" || type === "code" || type === "doc") && !existsSync(resolve(ctx.workspacePath, target))) {
|
|
151
|
+
issues.push({
|
|
152
|
+
check: "schema",
|
|
153
|
+
message: `ref.target does not exist: '${target}'`,
|
|
154
|
+
resolved: !ctx.strict
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (rec.type === "example") {
|
|
159
|
+
const parent = rec.kv["for"];
|
|
160
|
+
const kind = rec.kv["kind"];
|
|
161
|
+
if (!parent || !knownIds.has(parent)) {
|
|
162
|
+
issues.push({
|
|
163
|
+
check: "schema",
|
|
164
|
+
message: `example.for must reference an existing record id/name (found '${parent ?? ""}')`,
|
|
165
|
+
resolved: isLegacySchema || !ctx.strict
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (!kind || !["good", "bad", "edge"].includes(kind)) {
|
|
169
|
+
issues.push({
|
|
170
|
+
check: "schema",
|
|
171
|
+
message: "example.kind must be one of good|bad|edge",
|
|
172
|
+
resolved: isLegacySchema || !ctx.strict
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const isActive = status === "active";
|
|
178
|
+
if (isActive) {
|
|
179
|
+
// Preserve strict runbook/workbook/rules baseline behavior during v2 migration.
|
|
180
|
+
if (doc === "runbook" && !presentByType["rule"] && !presentByType["chain"]) {
|
|
181
|
+
issues.push({
|
|
182
|
+
check: "schema",
|
|
183
|
+
message: "Missing required AI records for doc family 'runbook': at least one rule| or chain| record",
|
|
184
|
+
resolved: !ctx.strict
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
if (doc === "workbook" && !presentByType["command"]) {
|
|
188
|
+
issues.push({
|
|
189
|
+
check: "schema",
|
|
190
|
+
message: "Missing required AI records for doc family 'workbook': at least one command| record",
|
|
191
|
+
resolved: !ctx.strict
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (doc === "workbook" && !presentByType["config"]) {
|
|
195
|
+
issues.push({
|
|
196
|
+
check: "schema",
|
|
197
|
+
message: "Missing required AI records for doc family 'workbook': at least one config| record",
|
|
198
|
+
resolved: !ctx.strict
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
if (doc === "rules" && !presentByType["rule"] && !presentByType["check"]) {
|
|
202
|
+
issues.push({
|
|
203
|
+
check: "schema",
|
|
204
|
+
message: "Missing required AI records for doc family 'rules': at least one rule| or check| record",
|
|
205
|
+
resolved: !ctx.strict
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
const missing = REQUIRED_BY_PROFILE[profile]
|
|
209
|
+
.filter((type) => {
|
|
210
|
+
if (type === "workflow")
|
|
211
|
+
return !presentByType["workflow"] && !presentByType["wf"];
|
|
212
|
+
if (type === "command")
|
|
213
|
+
return !presentByType["command"] && !presentByType["cmd"];
|
|
214
|
+
if (type === "example")
|
|
215
|
+
return !presentByType["example"];
|
|
216
|
+
return !presentByType[type];
|
|
217
|
+
})
|
|
218
|
+
.map((t) => `${t}| record`);
|
|
219
|
+
if (missing.length > 0) {
|
|
220
|
+
issues.push({
|
|
221
|
+
check: "schema",
|
|
222
|
+
message: `Missing required AI records for profile '${profile}': ${missing.join(", ")}`,
|
|
223
|
+
resolved: isLegacySchema || !ctx.strict
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return issues;
|
|
228
|
+
}
|
|
229
|
+
export function autoResolveAiSchema(aiOutput) {
|
|
230
|
+
if (aiOutput.startsWith("meta|schema=") || aiOutput.startsWith("meta|v=")) {
|
|
231
|
+
return aiOutput;
|
|
232
|
+
}
|
|
233
|
+
return `meta|schema=base.v2|doc=rules|truth=canonical|status=draft|profile=core\n\n${aiOutput}`;
|
|
234
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { dirname } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
function parseScalar(raw) {
|
|
7
|
+
const v = raw.trim();
|
|
8
|
+
if (v === "true")
|
|
9
|
+
return true;
|
|
10
|
+
if (v === "false")
|
|
11
|
+
return false;
|
|
12
|
+
if (/^-?\d+$/.test(v))
|
|
13
|
+
return Number.parseInt(v, 10);
|
|
14
|
+
return v.replace(/^"(.*)"$/, "$1");
|
|
15
|
+
}
|
|
16
|
+
function parseBlock(lines, start, indent) {
|
|
17
|
+
const items = [];
|
|
18
|
+
let idx = start;
|
|
19
|
+
while (idx < lines.length) {
|
|
20
|
+
const line = lines[idx];
|
|
21
|
+
const currentIndent = line.search(/\S|$/);
|
|
22
|
+
if (currentIndent < indent)
|
|
23
|
+
break;
|
|
24
|
+
const trimmed = line.trim();
|
|
25
|
+
if (!trimmed.startsWith("- ")) {
|
|
26
|
+
idx += 1;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const item = {};
|
|
30
|
+
const first = trimmed.slice(2);
|
|
31
|
+
if (first.includes(":")) {
|
|
32
|
+
const split = first.indexOf(":");
|
|
33
|
+
const k = first.slice(0, split).trim();
|
|
34
|
+
const v = first.slice(split + 1).trim();
|
|
35
|
+
item[k] = parseScalar(v);
|
|
36
|
+
}
|
|
37
|
+
idx += 1;
|
|
38
|
+
while (idx < lines.length) {
|
|
39
|
+
const next = lines[idx];
|
|
40
|
+
const nextIndent = next.search(/\S|$/);
|
|
41
|
+
if (nextIndent <= currentIndent)
|
|
42
|
+
break;
|
|
43
|
+
const t = next.trim();
|
|
44
|
+
const split = t.indexOf(":");
|
|
45
|
+
if (split > 0) {
|
|
46
|
+
const k = t.slice(0, split).trim();
|
|
47
|
+
const v = t.slice(split + 1).trim();
|
|
48
|
+
item[k] = parseScalar(v);
|
|
49
|
+
}
|
|
50
|
+
idx += 1;
|
|
51
|
+
}
|
|
52
|
+
items.push(item);
|
|
53
|
+
}
|
|
54
|
+
return { end: idx, items };
|
|
55
|
+
}
|
|
56
|
+
function parseViewModelYaml(text) {
|
|
57
|
+
const lines = text
|
|
58
|
+
.split("\n")
|
|
59
|
+
.map((l) => l.replace(/\t/g, " "))
|
|
60
|
+
.filter((l) => l.trim().length > 0 && !l.trim().startsWith("#"));
|
|
61
|
+
const root = {};
|
|
62
|
+
let idx = 0;
|
|
63
|
+
while (idx < lines.length) {
|
|
64
|
+
const line = lines[idx];
|
|
65
|
+
const trimmed = line.trim();
|
|
66
|
+
const split = trimmed.indexOf(":");
|
|
67
|
+
if (split <= 0) {
|
|
68
|
+
idx += 1;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const key = trimmed.slice(0, split).trim();
|
|
72
|
+
const value = trimmed.slice(split + 1).trim();
|
|
73
|
+
if (!value) {
|
|
74
|
+
const parsed = parseBlock(lines, idx + 1, line.search(/\S|$/) + 2);
|
|
75
|
+
root[key] = parsed.items;
|
|
76
|
+
idx = parsed.end;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
root[key] = parseScalar(value);
|
|
80
|
+
idx += 1;
|
|
81
|
+
}
|
|
82
|
+
const sections = root["sections"] ?? [];
|
|
83
|
+
const renderPolicies = root["render_policies"] ?? [];
|
|
84
|
+
return {
|
|
85
|
+
id: String(root["id"] ?? ""),
|
|
86
|
+
version: Number(root["version"] ?? 1),
|
|
87
|
+
docType: String(root["doc_type"] ?? ""),
|
|
88
|
+
target: String(root["target"] ?? ""),
|
|
89
|
+
profile: root["source_profile"],
|
|
90
|
+
sections: sections.map((s) => ({
|
|
91
|
+
id: String(s["id"] ?? ""),
|
|
92
|
+
title: s["title"] ? String(s["title"]) : undefined,
|
|
93
|
+
description: s["description"] ? String(s["description"]) : undefined,
|
|
94
|
+
renderer: String(s["renderer"] ?? ""),
|
|
95
|
+
source: String(s["source"] ?? "meta"),
|
|
96
|
+
template: s["template"] ? String(s["template"]) : undefined
|
|
97
|
+
})),
|
|
98
|
+
renderPolicies: renderPolicies.map((p) => ({
|
|
99
|
+
id: String(p["id"] ?? ""),
|
|
100
|
+
mode: String(p["mode"] ?? "append"),
|
|
101
|
+
when: p["when"] ? String(p["when"]) : undefined
|
|
102
|
+
}))
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
export async function loadViewModel(workspacePath, viewFile) {
|
|
106
|
+
const fullPath = resolve(resolveViewsRoot(workspacePath), viewFile);
|
|
107
|
+
const content = await readFile(fullPath, "utf8");
|
|
108
|
+
return parseViewModelYaml(content);
|
|
109
|
+
}
|
|
110
|
+
export async function listViewModels(workspacePath) {
|
|
111
|
+
const viewsPath = resolveViewsRoot(workspacePath);
|
|
112
|
+
const entries = await readdir(viewsPath, { withFileTypes: true });
|
|
113
|
+
return entries
|
|
114
|
+
.filter((e) => e.isFile() && e.name.endsWith(".view.yaml"))
|
|
115
|
+
.map((e) => e.name)
|
|
116
|
+
.sort();
|
|
117
|
+
}
|
|
118
|
+
function resolveViewsRoot(workspacePath) {
|
|
119
|
+
const local = resolve(workspacePath, "src/modules/documentation/views");
|
|
120
|
+
if (existsSync(local))
|
|
121
|
+
return local;
|
|
122
|
+
const runtimeSourceRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
|
|
123
|
+
return resolve(runtimeSourceRoot, "src/modules/documentation/views");
|
|
124
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { openPlanningStores } from "
|
|
2
|
+
import { openPlanningStores } from "../../core/planning/index.js";
|
|
3
3
|
import { appendLineageEvent } from "../../core/lineage-store.js";
|
|
4
4
|
import { loadImprovementState, saveImprovementState } from "./improvement-state.js";
|
|
5
5
|
import { ingestAgentTranscripts, ingestConfigMutations, ingestGitDiffBetweenTags, ingestPolicyDenials, ingestTaskTransitionFriction, taskIdForEvidenceKey } from "./ingest.js";
|
|
@@ -42,7 +42,7 @@ export async function runGenerateRecommendations(ctx, args) {
|
|
|
42
42
|
const runId = randomUUID();
|
|
43
43
|
const planning = await openPlanningStores(ctx);
|
|
44
44
|
const store = planning.taskStore;
|
|
45
|
-
const state = await loadImprovementState(ctx.workspacePath);
|
|
45
|
+
const state = await loadImprovementState(ctx.workspacePath, ctx.effectiveConfig);
|
|
46
46
|
const transcriptsRoot = resolveTranscriptArchivePath(ctx, args);
|
|
47
47
|
const fromTag = typeof args.fromTag === "string" ? args.fromTag.trim() : undefined;
|
|
48
48
|
const toTag = typeof args.toTag === "string" ? args.toTag.trim() : undefined;
|
|
@@ -112,7 +112,7 @@ export async function runGenerateRecommendations(ctx, args) {
|
|
|
112
112
|
});
|
|
113
113
|
}
|
|
114
114
|
await store.save();
|
|
115
|
-
await saveImprovementState(ctx.workspacePath, state);
|
|
115
|
+
await saveImprovementState(ctx.workspacePath, state, ctx.effectiveConfig);
|
|
116
116
|
const skipped = skippedDuplicateEvidenceKey + skippedExistingTaskId;
|
|
117
117
|
return {
|
|
118
118
|
runId,
|
|
@@ -18,5 +18,5 @@ export type ImprovementStateDocument = {
|
|
|
18
18
|
transcriptRetryQueue: TranscriptRetryEntry[];
|
|
19
19
|
};
|
|
20
20
|
export declare function emptyImprovementState(): ImprovementStateDocument;
|
|
21
|
-
export declare function loadImprovementState(workspacePath: string): Promise<ImprovementStateDocument>;
|
|
22
|
-
export declare function saveImprovementState(workspacePath: string, doc: ImprovementStateDocument): Promise<void>;
|
|
21
|
+
export declare function loadImprovementState(workspacePath: string, effectiveConfig?: Record<string, unknown>): Promise<ImprovementStateDocument>;
|
|
22
|
+
export declare function saveImprovementState(workspacePath: string, doc: ImprovementStateDocument, effectiveConfig?: Record<string, unknown>): Promise<void>;
|