@workflow-cannon/workspace-kit 0.18.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.
Files changed (140) hide show
  1. package/README.md +23 -9
  2. package/dist/cli/doctor-planning-issues.js +3 -22
  3. package/dist/cli/run-command.js +22 -38
  4. package/dist/cli.js +95 -4
  5. package/dist/contracts/command-manifest.d.ts +17 -0
  6. package/dist/contracts/command-manifest.js +1 -0
  7. package/dist/contracts/index.d.ts +1 -1
  8. package/dist/contracts/module-contract.d.ts +12 -11
  9. package/dist/core/agent-instruction-surface.d.ts +33 -0
  10. package/dist/core/agent-instruction-surface.js +46 -0
  11. package/dist/core/config-cli.js +13 -17
  12. package/dist/core/config-metadata.js +61 -2
  13. package/dist/core/index.d.ts +4 -1
  14. package/dist/core/index.js +3 -0
  15. package/dist/core/module-command-router.js +19 -1
  16. package/dist/core/module-registry-resolve.d.ts +27 -0
  17. package/dist/core/module-registry-resolve.js +91 -0
  18. package/dist/core/module-registry.d.ts +14 -0
  19. package/dist/core/module-registry.js +57 -0
  20. package/dist/core/planning/build-plan-session-file.d.ts +29 -0
  21. package/dist/core/planning/build-plan-session-file.js +58 -0
  22. package/dist/core/planning/index.d.ts +17 -0
  23. package/dist/core/planning/index.js +15 -0
  24. package/dist/core/policy.js +18 -8
  25. package/dist/core/state/unified-state-db.d.ts +21 -0
  26. package/dist/core/state/unified-state-db.js +80 -0
  27. package/dist/core/workspace-kit-config.js +8 -0
  28. package/dist/modules/agent-behavior/builtins.d.ts +3 -0
  29. package/dist/modules/agent-behavior/builtins.js +71 -0
  30. package/dist/modules/agent-behavior/explain.d.ts +6 -0
  31. package/dist/modules/agent-behavior/explain.js +46 -0
  32. package/dist/modules/agent-behavior/index.d.ts +4 -0
  33. package/dist/modules/agent-behavior/index.js +461 -0
  34. package/dist/modules/agent-behavior/interview-session-file.d.ts +9 -0
  35. package/dist/modules/agent-behavior/interview-session-file.js +43 -0
  36. package/dist/modules/agent-behavior/interview.d.ts +13 -0
  37. package/dist/modules/agent-behavior/interview.js +88 -0
  38. package/dist/modules/agent-behavior/persistence.d.ts +6 -0
  39. package/dist/modules/agent-behavior/persistence.js +89 -0
  40. package/dist/modules/agent-behavior/store.d.ts +34 -0
  41. package/dist/modules/agent-behavior/store.js +119 -0
  42. package/dist/modules/agent-behavior/types.d.ts +28 -0
  43. package/dist/modules/agent-behavior/types.js +1 -0
  44. package/dist/modules/agent-behavior/validate.d.ts +11 -0
  45. package/dist/modules/agent-behavior/validate.js +123 -0
  46. package/dist/modules/approvals/index.js +54 -51
  47. package/dist/modules/approvals/policy-sensitive-commands.d.ts +4 -0
  48. package/dist/modules/approvals/policy-sensitive-commands.js +4 -0
  49. package/dist/modules/approvals/review-runtime.js +1 -2
  50. package/dist/modules/documentation/index.js +47 -45
  51. package/dist/modules/documentation/normalizer.d.ts +3 -0
  52. package/dist/modules/documentation/normalizer.js +171 -0
  53. package/dist/modules/documentation/parser.d.ts +7 -0
  54. package/dist/modules/documentation/parser.js +39 -0
  55. package/dist/modules/documentation/policy-sensitive-commands.d.ts +5 -0
  56. package/dist/modules/documentation/policy-sensitive-commands.js +8 -0
  57. package/dist/modules/documentation/renderer.d.ts +23 -0
  58. package/dist/modules/documentation/renderer.js +105 -0
  59. package/dist/modules/documentation/runtime-batch.d.ts +10 -0
  60. package/dist/modules/documentation/runtime-batch.js +67 -0
  61. package/dist/modules/documentation/runtime-config.d.ts +11 -0
  62. package/dist/modules/documentation/runtime-config.js +54 -0
  63. package/dist/modules/documentation/runtime-render-support.d.ts +8 -0
  64. package/dist/modules/documentation/runtime-render-support.js +36 -0
  65. package/dist/modules/documentation/runtime.js +22 -510
  66. package/dist/modules/documentation/types.d.ts +182 -0
  67. package/dist/modules/documentation/validator.d.ts +8 -0
  68. package/dist/modules/documentation/validator.js +234 -0
  69. package/dist/modules/documentation/view-models.d.ts +3 -0
  70. package/dist/modules/documentation/view-models.js +124 -0
  71. package/dist/modules/improvement/generate-recommendations-runtime.js +3 -3
  72. package/dist/modules/improvement/improvement-state.d.ts +2 -2
  73. package/dist/modules/improvement/improvement-state.js +52 -23
  74. package/dist/modules/improvement/index.js +140 -138
  75. package/dist/modules/improvement/ingest.d.ts +1 -1
  76. package/dist/modules/improvement/policy-sensitive-commands.d.ts +4 -0
  77. package/dist/modules/improvement/policy-sensitive-commands.js +7 -0
  78. package/dist/modules/index.d.ts +6 -0
  79. package/dist/modules/index.js +17 -0
  80. package/dist/modules/planning/index.js +384 -50
  81. package/dist/modules/planning/question-engine.d.ts +2 -0
  82. package/dist/modules/planning/question-engine.js +8 -1
  83. package/dist/modules/task-engine/doctor-planning-persistence.js +21 -13
  84. package/dist/modules/task-engine/index.d.ts +1 -2
  85. package/dist/modules/task-engine/index.js +1 -1143
  86. package/dist/modules/task-engine/migrate-task-persistence-runtime.js +31 -4
  87. package/dist/modules/task-engine/migrate-wishlist-intake-runtime.d.ts +2 -0
  88. package/dist/modules/task-engine/migrate-wishlist-intake-runtime.js +146 -0
  89. package/dist/modules/task-engine/planning-open.d.ts +2 -9
  90. package/dist/modules/task-engine/planning-open.js +4 -15
  91. package/dist/modules/task-engine/policy-sensitive-commands.d.ts +5 -0
  92. package/dist/modules/task-engine/policy-sensitive-commands.js +5 -0
  93. package/dist/modules/task-engine/sqlite-dual-planning.d.ts +11 -2
  94. package/dist/modules/task-engine/sqlite-dual-planning.js +134 -28
  95. package/dist/modules/task-engine/strict-task-validation.js +3 -0
  96. package/dist/modules/task-engine/suggestions.js +2 -1
  97. package/dist/modules/task-engine/task-engine-internal.d.ts +2 -0
  98. package/dist/modules/task-engine/task-engine-internal.js +1304 -0
  99. package/dist/modules/task-engine/task-type-validation.js +40 -0
  100. package/dist/modules/task-engine/wishlist-intake.d.ts +22 -0
  101. package/dist/modules/task-engine/wishlist-intake.js +180 -0
  102. package/dist/modules/task-engine/wishlist-validation.d.ts +4 -0
  103. package/dist/modules/task-engine/wishlist-validation.js +19 -0
  104. package/dist/modules/workspace-config/index.js +9 -11
  105. package/package.json +2 -2
  106. package/schemas/agent-behavior-profile.schema.json +52 -0
  107. package/schemas/task-engine-run-contracts.schema.json +80 -5
  108. package/src/modules/documentation/README.md +16 -25
  109. package/src/modules/documentation/RULES.md +9 -9
  110. package/src/modules/documentation/index.ts +54 -49
  111. package/src/modules/documentation/instructions/document-project.md +6 -6
  112. package/src/modules/documentation/instructions/generate-document.md +4 -4
  113. package/src/modules/documentation/normalizer.ts +187 -0
  114. package/src/modules/documentation/parser.ts +41 -0
  115. package/src/modules/documentation/policy-sensitive-commands.ts +8 -0
  116. package/src/modules/documentation/renderer.ts +121 -0
  117. package/src/modules/documentation/runtime-batch.ts +74 -0
  118. package/src/modules/documentation/runtime-config.ts +68 -0
  119. package/src/modules/documentation/runtime-render-support.ts +39 -0
  120. package/src/modules/documentation/runtime.ts +28 -600
  121. package/src/modules/documentation/schemas/documentation-schema.md +37 -54
  122. package/src/modules/documentation/types.ts +228 -0
  123. package/src/modules/documentation/validator.ts +247 -0
  124. package/src/modules/documentation/view-models.ts +132 -0
  125. package/src/modules/documentation/views/agents.view.yaml +18 -0
  126. package/src/modules/documentation/views/architecture.view.yaml +18 -0
  127. package/src/modules/documentation/views/principles.view.yaml +18 -0
  128. package/src/modules/documentation/views/readme.view.yaml +18 -0
  129. package/src/modules/documentation/views/releasing.view.yaml +18 -0
  130. package/src/modules/documentation/views/roadmap.view.yaml +18 -0
  131. package/src/modules/documentation/views/runbooks-consumer-cadence.view.yaml +18 -0
  132. package/src/modules/documentation/views/runbooks-parity-validation-flow.view.yaml +18 -0
  133. package/src/modules/documentation/views/runbooks-release-channels.view.yaml +18 -0
  134. package/src/modules/documentation/views/security.view.yaml +18 -0
  135. package/src/modules/documentation/views/support.view.yaml +18 -0
  136. package/src/modules/documentation/views/terms.view.yaml +18 -0
  137. package/src/modules/documentation/views/workbooks-phase2-config-policy-workbook.view.yaml +18 -0
  138. package/src/modules/documentation/views/workbooks-task-engine-workbook.view.yaml +18 -0
  139. package/src/modules/documentation/views/workbooks-transcript-automation-baseline.view.yaml +18 -0
  140. 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,3 @@
1
+ import type { ViewModelDefinition } from "./types.js";
2
+ export declare function loadViewModel(workspacePath: string, viewFile: string): Promise<ViewModelDefinition>;
3
+ export declare function listViewModels(workspacePath: string): Promise<string[]>;
@@ -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 "../task-engine/planning-open.js";
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>;