@work-graph/cli 0.2.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 (194) hide show
  1. package/README.md +31 -0
  2. package/bin/work-graph.mjs +238 -0
  3. package/package.json +38 -0
  4. package/vendor/packages/design-tokens/generated/gripe-dark-default.css +67 -0
  5. package/vendor/packages/design-tokens/generated/marketplace-default.css +67 -0
  6. package/vendor/packages/design-tokens/generated/workgraph-dark.css +67 -0
  7. package/vendor/packages/workgraph-mcp/README.md +28 -0
  8. package/vendor/packages/workgraph-mcp/bin/workgraph-mcp.mjs +21 -0
  9. package/vendor/packages/workgraph-mcp/package.json +37 -0
  10. package/vendor/packages/workgraph-mcp/src/handlers.mjs +761 -0
  11. package/vendor/packages/workgraph-mcp/src/index.mjs +638 -0
  12. package/vendor/packages/workgraph-mcp/src/prompts.mjs +162 -0
  13. package/vendor/public/assets/workgraph-logo.svg +11 -0
  14. package/vendor/public/fonts/GraphikLCG/GraphikLCG-Medium.woff2 +0 -0
  15. package/vendor/public/fonts/GraphikLCG/GraphikLCG-Regular.woff2 +0 -0
  16. package/vendor/public/fonts/GraphikLCG/GraphikLCG-Semibold.woff2 +0 -0
  17. package/vendor/public/fonts/GraphikLCG/stylesheet.css +25 -0
  18. package/vendor/public/graph-canvas-lit-flow.css +154 -0
  19. package/vendor/public/graph-canvas-lit-flow.css.map +7 -0
  20. package/vendor/public/graph-canvas-lit-flow.js +8530 -0
  21. package/vendor/public/graph-canvas-lit-flow.js.map +7 -0
  22. package/vendor/src/agentBehaviorRulesAudit.mjs +168 -0
  23. package/vendor/src/agentBehaviorRulesBundle.mjs +144 -0
  24. package/vendor/src/agentRunApi.mjs +136 -0
  25. package/vendor/src/agentToolLoopGuard.mjs +88 -0
  26. package/vendor/src/agentWorkerClaudeProvider.mjs +288 -0
  27. package/vendor/src/agentWorkerCursorSdkProvider.mjs +156 -0
  28. package/vendor/src/agentWorkerLiveLoop.mjs +455 -0
  29. package/vendor/src/agentWorkerLocalCliProvider.mjs +217 -0
  30. package/vendor/src/agentWorkerLocalRunner.mjs +246 -0
  31. package/vendor/src/agentWorkerOpenAiProvider.mjs +459 -0
  32. package/vendor/src/analyticsPanelProjection.mjs +212 -0
  33. package/vendor/src/analyticsRecordStore.mjs +165 -0
  34. package/vendor/src/analyticsRecordWorkItems.mjs +104 -0
  35. package/vendor/src/architectureL1Canon.mjs +419 -0
  36. package/vendor/src/architectureLayout.mjs +229 -0
  37. package/vendor/src/architectureSnapshot.mjs +490 -0
  38. package/vendor/src/architectureViewsProjection.mjs +116 -0
  39. package/vendor/src/atomInspector.mjs +253 -0
  40. package/vendor/src/atomInspectorApi.mjs +130 -0
  41. package/vendor/src/auditGapMatrixRefresh.mjs +121 -0
  42. package/vendor/src/backlogSchemaLint.mjs +176 -0
  43. package/vendor/src/blockedOnebaseGoPreflightEval.mjs +100 -0
  44. package/vendor/src/bracketIrTraceSignal.mjs +93 -0
  45. package/vendor/src/bvcAtomParser.mjs +210 -0
  46. package/vendor/src/bvcDialectRegistry.mjs +86 -0
  47. package/vendor/src/bvcFileFormat.mjs +218 -0
  48. package/vendor/src/bvcFormatCli.mjs +55 -0
  49. package/vendor/src/bvcLintCli.mjs +48 -0
  50. package/vendor/src/bvcNewWritePolicy.mjs +70 -0
  51. package/vendor/src/charterPreflightPromoteGate.mjs +194 -0
  52. package/vendor/src/claimNoEligibleEval.mjs +205 -0
  53. package/vendor/src/closingAnalysisSuggest.mjs +59 -0
  54. package/vendor/src/codeGapAnalyzer.mjs +308 -0
  55. package/vendor/src/codeGapBacklogFeeder.mjs +82 -0
  56. package/vendor/src/codeGapDraftIntakeApi.mjs +307 -0
  57. package/vendor/src/codeGapOperatorProjection.mjs +60 -0
  58. package/vendor/src/codeSyntaxHighlight.mjs +123 -0
  59. package/vendor/src/codegenEvidence.mjs +187 -0
  60. package/vendor/src/compilerRoundTripCli.mjs +164 -0
  61. package/vendor/src/dagreGraphLayout.mjs +78 -0
  62. package/vendor/src/draftIntakePromotionRules.mjs +205 -0
  63. package/vendor/src/epicWorkScope.mjs +85 -0
  64. package/vendor/src/evalLiveLlmEnv.mjs +63 -0
  65. package/vendor/src/evidenceReadModel.mjs +167 -0
  66. package/vendor/src/gfsOverlayProjectPassport.mjs +235 -0
  67. package/vendor/src/globalStepPathToBvcReferences.mjs +196 -0
  68. package/vendor/src/goldenPath.mjs +69 -0
  69. package/vendor/src/graphCanvasLayout.mjs +464 -0
  70. package/vendor/src/graphCanvasLitFlow/client/graphCanvasMinimap.ts +261 -0
  71. package/vendor/src/graphCanvasLitFlow/client/graphCanvasSvgEdges.ts +259 -0
  72. package/vendor/src/graphCanvasLitFlow/client/graphCanvasTheme.css +152 -0
  73. package/vendor/src/graphCanvasLitFlow/client/graphCardNode.ts +328 -0
  74. package/vendor/src/graphCanvasLitFlow/client/mountGraphCanvasLitFlow.ts +322 -0
  75. package/vendor/src/graphCanvasLitFlow/graphCanvasEdgeLabels.mjs +58 -0
  76. package/vendor/src/graphCanvasLitFlow/graphCanvasEdgeRouter.mjs +142 -0
  77. package/vendor/src/graphCanvasLitFlow/graphCanvasLayoutProfile.mjs +32 -0
  78. package/vendor/src/graphCanvasLitFlow/graphCanvasNodeMetrics.mjs +45 -0
  79. package/vendor/src/graphCanvasLitFlow/graphCanvasProjection.mjs +115 -0
  80. package/vendor/src/graphCanvasLitFlow/graphCanvasProjectionToFlow.mjs +133 -0
  81. package/vendor/src/graphCanvasLitFlow/graphCanvasTraversal.mjs +77 -0
  82. package/vendor/src/graphCanvasLitFlow/layoutIntentRoadmapWorkStack.mjs +73 -0
  83. package/vendor/src/graphCanvasLitFlow/resolveGraphCanvasOverlaps.mjs +77 -0
  84. package/vendor/src/graphRagContextSlice.mjs +461 -0
  85. package/vendor/src/gvmVerifyWorkerGate.mjs +95 -0
  86. package/vendor/src/homeSnapshotApi.mjs +131 -0
  87. package/vendor/src/homeSnapshotProjection.mjs +275 -0
  88. package/vendor/src/inboxEventStream.mjs +140 -0
  89. package/vendor/src/intentComposerApi.mjs +245 -0
  90. package/vendor/src/intentGraphGbcSliceBoundary.mjs +258 -0
  91. package/vendor/src/intentGraphProjection.mjs +208 -0
  92. package/vendor/src/intentHierarchy.mjs +241 -0
  93. package/vendor/src/intentNodeLint.mjs +107 -0
  94. package/vendor/src/intentNodeRuntime.mjs +185 -0
  95. package/vendor/src/intentRoadmapCanvas.mjs +393 -0
  96. package/vendor/src/intentRoadmapEpicProjection.mjs +122 -0
  97. package/vendor/src/intentRoadmapMermaid.mjs +165 -0
  98. package/vendor/src/intentRoadmapProjection.mjs +85 -0
  99. package/vendor/src/intentTreeLint.mjs +114 -0
  100. package/vendor/src/intentTreeMigration.mjs +150 -0
  101. package/vendor/src/intentTreeWorkItems.mjs +227 -0
  102. package/vendor/src/kanbanBoardProjection.mjs +58 -0
  103. package/vendor/src/languageAdapterRegistry.mjs +180 -0
  104. package/vendor/src/languageAdapters/goAdapter.mjs +62 -0
  105. package/vendor/src/languageAdapters/jsTsAdapter.mjs +60 -0
  106. package/vendor/src/languageAdapters/jsonYamlAdapter.mjs +103 -0
  107. package/vendor/src/languageAdapters/onebaseOsAdapter.mjs +55 -0
  108. package/vendor/src/languageAdapters/plaintextAdapter.mjs +36 -0
  109. package/vendor/src/languageAdapters/shared.mjs +68 -0
  110. package/vendor/src/languageAdapters/stepAdapter.mjs +81 -0
  111. package/vendor/src/lintPlanWorkAlignment.mjs +136 -0
  112. package/vendor/src/loopHintRepeatToolEval.mjs +153 -0
  113. package/vendor/src/lowcodeScaffoldCli.mjs +386 -0
  114. package/vendor/src/markdownDocumentRender.mjs +208 -0
  115. package/vendor/src/memoryPanelProjection.mjs +116 -0
  116. package/vendor/src/memoryRecordWriter.mjs +243 -0
  117. package/vendor/src/memoryWorkerSlice.mjs +238 -0
  118. package/vendor/src/migrateStepToBvc.mjs +133 -0
  119. package/vendor/src/missionControlServerHandlers.mjs +195 -0
  120. package/vendor/src/missionControlUiClient.mjs +278 -0
  121. package/vendor/src/onebaseCliCapabilityProbe.mjs +107 -0
  122. package/vendor/src/onebaseCliRunner.mjs +145 -0
  123. package/vendor/src/onebaseGrossProfitStaticVerify.mjs +98 -0
  124. package/vendor/src/onebaseParityEvidenceSync.mjs +88 -0
  125. package/vendor/src/onebasePvrgGraphNodes.mjs +257 -0
  126. package/vendor/src/onebaseRestEvidenceAdapter.mjs +216 -0
  127. package/vendor/src/onebaseVectorDslCodegenReadiness.mjs +137 -0
  128. package/vendor/src/onebaseWorkItemTemplate.mjs +154 -0
  129. package/vendor/src/onebaseWorkerTools.mjs +586 -0
  130. package/vendor/src/operatorShellProjection.mjs +102 -0
  131. package/vendor/src/pipelineProseRender.mjs +180 -0
  132. package/vendor/src/pipelineStageLint.mjs +118 -0
  133. package/vendor/src/promptRulesEditorApi.mjs +174 -0
  134. package/vendor/src/promptRulesProjection.mjs +134 -0
  135. package/vendor/src/pvrg/bladeAdapter.mjs +40 -0
  136. package/vendor/src/pvrgTaskScope.mjs +152 -0
  137. package/vendor/src/releaseGateMatrix.mjs +188 -0
  138. package/vendor/src/schematicView.mjs +305 -0
  139. package/vendor/src/seedAnalyticsRecord.mjs +217 -0
  140. package/vendor/src/semanticSearchBm25.mjs +103 -0
  141. package/vendor/src/semanticSearchExcerpts.mjs +68 -0
  142. package/vendor/src/semanticSearchTfidfVector.mjs +86 -0
  143. package/vendor/src/semanticSearchWorkflow.mjs +366 -0
  144. package/vendor/src/stepAtomFormatter.mjs +413 -0
  145. package/vendor/src/stepGraphSlice.mjs +318 -0
  146. package/vendor/src/ui/atoms/badge.mjs +40 -0
  147. package/vendor/src/ui/atoms/badgeClient.mjs +32 -0
  148. package/vendor/src/ui/atoms/button.mjs +114 -0
  149. package/vendor/src/ui/atoms/buttonClient.mjs +49 -0
  150. package/vendor/src/ui/atoms/icon.mjs +23 -0
  151. package/vendor/src/ui/atoms/input.mjs +38 -0
  152. package/vendor/src/ui/atoms/modal.mjs +44 -0
  153. package/vendor/src/ui/atoms/select.mjs +98 -0
  154. package/vendor/src/ui/backlogShellButtons.mjs +238 -0
  155. package/vendor/src/ui/htmlEscape.mjs +11 -0
  156. package/vendor/src/ui/molecules/rating.mjs +48 -0
  157. package/vendor/src/ui/molecules/tabs.mjs +70 -0
  158. package/vendor/src/ui/organisms/modal.mjs +1 -0
  159. package/vendor/src/ui/pages/uiKitPage.mjs +147 -0
  160. package/vendor/src/ui/workItemStatusTone.mjs +36 -0
  161. package/vendor/src/unifiedLinkageProjection.mjs +264 -0
  162. package/vendor/src/verificationLoop.mjs +206 -0
  163. package/vendor/src/workGraphBacklogPersist.mjs +234 -0
  164. package/vendor/src/workGraphBacklogUiServer.mjs +9192 -0
  165. package/vendor/src/workGraphBoundedTargetFileRead.mjs +178 -0
  166. package/vendor/src/workGraphCycleSlice.mjs +184 -0
  167. package/vendor/src/workGraphDaemonTick.mjs +307 -0
  168. package/vendor/src/workGraphDaemonWatch.mjs +157 -0
  169. package/vendor/src/workGraphEngineRoot.mjs +136 -0
  170. package/vendor/src/workGraphInstallLayout.mjs +65 -0
  171. package/vendor/src/workGraphLlmUsefulnessEval.mjs +611 -0
  172. package/vendor/src/workGraphPhasePromoteReadyQueue.mjs +159 -0
  173. package/vendor/src/workGraphProjectHost.mjs +149 -0
  174. package/vendor/src/workGraphProjectInit.mjs +392 -0
  175. package/vendor/src/workGraphPromoteReadyApi.mjs +115 -0
  176. package/vendor/src/workGraphRecoveryPolicy.mjs +124 -0
  177. package/vendor/src/workGraphRunnerQueueProjection.mjs +187 -0
  178. package/vendor/src/workGraphRuntime.mjs +1008 -0
  179. package/vendor/src/workGraphToolSurfaceAudit.mjs +372 -0
  180. package/vendor/src/workGraphToolTransportRuntime.mjs +195 -0
  181. package/vendor/src/workGraphWorkerProvider.mjs +600 -0
  182. package/vendor/src/workItemBvcQuality.mjs +262 -0
  183. package/vendor/src/workItemCreateAnalysis.mjs +157 -0
  184. package/vendor/src/workItemDecisionPipeline.mjs +278 -0
  185. package/vendor/src/workItemEpicCascade.mjs +176 -0
  186. package/vendor/src/workItemExecutionGate.mjs +78 -0
  187. package/vendor/src/workItemHierarchy.mjs +226 -0
  188. package/vendor/src/workItemProseLint.mjs +133 -0
  189. package/vendor/src/workItemTextRusify.mjs +794 -0
  190. package/vendor/src/workItemTraceEnvelope.mjs +158 -0
  191. package/vendor/src/workItemUiReferences.mjs +272 -0
  192. package/vendor/src/workflowEpicGrouping.mjs +67 -0
  193. package/vendor/src/workflowTreeProjection.mjs +53 -0
  194. package/vendor/src/workspaceRegistry.mjs +150 -0
@@ -0,0 +1,253 @@
1
+ import {
2
+ findWorkItemAtomSpan,
3
+ writeBacklogTextAtomically,
4
+ } from './workGraphBacklogPersist.mjs';
5
+ import { readWorkItemAtomFromRepo } from './intentTreeWorkItems.mjs';
6
+ import { buildClosingAnalysisSuggestion } from './closingAnalysisSuggest.mjs';
7
+ import { parseWorkItems } from './workGraphRuntime.mjs';
8
+ import {
9
+ formatStepAtomDraft,
10
+ parseStepAtomDrafts,
11
+ validateStepAtomDraft,
12
+ } from './stepAtomFormatter.mjs';
13
+
14
+ export const ATOM_INSPECTOR_PROPOSAL_SCHEMA = 'atom-inspector.proposal.v1';
15
+ export const ATOM_INSPECTOR_APPLY_SCHEMA = 'atom-inspector.apply.response.v1';
16
+
17
+ const WORK_ITEM_STATUS_OPTIONS = [
18
+ 'backlog',
19
+ 'ready',
20
+ 'claimed',
21
+ 'doing',
22
+ 'verify',
23
+ 'done',
24
+ 'blocked',
25
+ ];
26
+
27
+ export function importStepAtomDraftForWorkItem(backlogText, workId) {
28
+ const span = findWorkItemAtomSpan(backlogText, workId);
29
+ if (!span) {
30
+ throw new Error(`work item atom not found: ${workId}`);
31
+ }
32
+
33
+ const [record] = parseStepAtomDrafts(span.fullMatch);
34
+ if (!record) {
35
+ throw new Error(`failed to parse work item atom: ${workId}`);
36
+ }
37
+
38
+ return {
39
+ workId,
40
+ atomName: span.atomName,
41
+ draft: record.draft,
42
+ warnings: record.warnings,
43
+ validationErrors: record.errors,
44
+ sourceBlock: span.fullMatch,
45
+ };
46
+ }
47
+
48
+ export function normalizeAtomInspectorDraftInput(rawDraft) {
49
+ if (!rawDraft || typeof rawDraft !== 'object' || Array.isArray(rawDraft)) {
50
+ throw new TypeError('draft must be an object');
51
+ }
52
+
53
+ const draft = {
54
+ profile: String(rawDraft.profile ?? 'work_item').trim(),
55
+ name: String(rawDraft.name ?? '').trim(),
56
+ basis: normalizeTextArray(rawDraft.basis),
57
+ vector: normalizeTextArray(rawDraft.vector),
58
+ goal: normalizeTextArray(rawDraft.goal),
59
+ labels: normalizeLabels(rawDraft.labels),
60
+ };
61
+
62
+ if (rawDraft.checks !== undefined) {
63
+ draft.checks = normalizeTextArray(rawDraft.checks, { allowEmpty: true });
64
+ }
65
+
66
+ if (rawDraft.evidence !== undefined) {
67
+ draft.evidence = normalizeTextArray(rawDraft.evidence, { allowEmpty: true });
68
+ }
69
+
70
+ if (rawDraft.analysis !== undefined) {
71
+ draft.analysis = normalizeTextArray(rawDraft.analysis, { allowEmpty: true });
72
+ }
73
+
74
+ if (rawDraft.decision !== undefined) {
75
+ draft.decision = normalizeTextArray(rawDraft.decision, { allowEmpty: true });
76
+ }
77
+
78
+ if (rawDraft.uiRefs !== undefined) {
79
+ draft.uiRefs = normalizeTextArray(rawDraft.uiRefs, { allowEmpty: true });
80
+ }
81
+
82
+ if (draft.labels['atom.profile'] === undefined || draft.labels['atom.profile'] === '') {
83
+ draft.labels['atom.profile'] = draft.profile;
84
+ }
85
+
86
+ return draft;
87
+ }
88
+
89
+ export function buildAtomInspectorProposal(draft, options = {}) {
90
+ const normalizedDraft = normalizeAtomInspectorDraftInput(draft);
91
+ const validationErrors = validateStepAtomDraft(normalizedDraft);
92
+
93
+ if (validationErrors.length > 0) {
94
+ return {
95
+ schema: ATOM_INSPECTOR_PROPOSAL_SCHEMA,
96
+ ok: false,
97
+ workId: normalizedDraft.labels?.['work.id'] ?? options.workId ?? null,
98
+ targetFile: options.targetFile ?? 'work/backlog.bvc',
99
+ draft: normalizedDraft,
100
+ generatedStep: null,
101
+ warnings: [],
102
+ validationErrors,
103
+ };
104
+ }
105
+
106
+ const generatedStep = formatStepAtomDraft(normalizedDraft);
107
+ const reparsed = parseStepAtomDrafts(generatedStep)[0];
108
+
109
+ return {
110
+ schema: ATOM_INSPECTOR_PROPOSAL_SCHEMA,
111
+ ok: true,
112
+ workId: normalizedDraft.labels['work.id'] ?? options.workId ?? null,
113
+ targetFile: options.targetFile ?? 'work/backlog.bvc',
114
+ draft: normalizedDraft,
115
+ generatedStep,
116
+ warnings: reparsed?.warnings ?? [],
117
+ validationErrors: reparsed?.errors ?? [],
118
+ };
119
+ }
120
+
121
+ export function replaceWorkItemAtomInBacklogText(backlogText, workId, generatedStep) {
122
+ const span = findWorkItemAtomSpan(backlogText, workId);
123
+ if (!span) {
124
+ throw new Error(`work item atom not found: ${workId}`);
125
+ }
126
+
127
+ const atomText = String(generatedStep ?? '').trimEnd();
128
+ if (!atomText.startsWith('#')) {
129
+ throw new Error('generatedStep must be a formatted step atom block');
130
+ }
131
+
132
+ const suffix = backlogText.slice(span.end);
133
+ const separator = suffix.startsWith('\n') ? '' : '\n';
134
+ return `${backlogText.slice(0, span.start)}${atomText}${separator}${suffix}`;
135
+ }
136
+
137
+ export async function applyAtomInspectorProposalToBacklogFile(options = {}) {
138
+ const workId = String(options.workId ?? '').trim();
139
+ if (workId === '') {
140
+ throw new TypeError('workId is required');
141
+ }
142
+
143
+ const proposal = buildAtomInspectorProposal(options.draft, {
144
+ workId,
145
+ targetFile: options.targetFile,
146
+ });
147
+
148
+ if (!proposal.ok || proposal.validationErrors.length > 0) {
149
+ return {
150
+ schema: ATOM_INSPECTOR_APPLY_SCHEMA,
151
+ ok: false,
152
+ error: 'validation_failed',
153
+ workId,
154
+ proposal,
155
+ persistedBacklog: false,
156
+ };
157
+ }
158
+
159
+ if (proposal.workId !== workId) {
160
+ return {
161
+ schema: ATOM_INSPECTOR_APPLY_SCHEMA,
162
+ ok: false,
163
+ error: 'work_id_mismatch',
164
+ workId,
165
+ proposal,
166
+ persistedBacklog: false,
167
+ };
168
+ }
169
+
170
+ const source = options.backlogText !== undefined || options.backlogPath
171
+ ? {
172
+ path: resolveBacklogPath(options),
173
+ text: options.backlogText ?? await readBacklog(resolveBacklogPath(options)),
174
+ mode: 'backlog',
175
+ }
176
+ : await readWorkItemAtomFromRepo(workId, options);
177
+ const previousItem = parseWorkItems(source.text).find((entry) => entry.id === workId) ?? null;
178
+ const newText = replaceWorkItemAtomInBacklogText(source.text, workId, proposal.generatedStep);
179
+ const nextItem = parseWorkItems(newText).find((entry) => entry.id === workId) ?? null;
180
+ const closingAnalysisSuggestion = buildClosingAnalysisSuggestion(previousItem, nextItem);
181
+
182
+ if (options.persistBacklog !== false) {
183
+ await writeBacklogTextAtomically(source.path, newText);
184
+ }
185
+
186
+ return {
187
+ schema: ATOM_INSPECTOR_APPLY_SCHEMA,
188
+ ok: true,
189
+ error: null,
190
+ workId,
191
+ proposal,
192
+ persistedBacklog: options.persistBacklog !== false,
193
+ path: source.relativePath ?? source.path,
194
+ mode: source.mode,
195
+ ...(closingAnalysisSuggestion ? { closingAnalysisSuggestion } : {}),
196
+ };
197
+ }
198
+
199
+ export function workItemStatusOptions() {
200
+ return [...WORK_ITEM_STATUS_OPTIONS];
201
+ }
202
+
203
+ function normalizeTextArray(value, options = {}) {
204
+ if (Array.isArray(value)) {
205
+ return value
206
+ .map((entry) => String(entry ?? '').trim())
207
+ .filter((entry) => entry !== '');
208
+ }
209
+
210
+ if (typeof value === 'string') {
211
+ const lines = value
212
+ .split(/\r?\n/u)
213
+ .map((line) => line.replace(/^-\s*/u, '').trim())
214
+ .filter(Boolean);
215
+ return lines;
216
+ }
217
+
218
+ if (options.allowEmpty) {
219
+ return [];
220
+ }
221
+
222
+ return [];
223
+ }
224
+
225
+ function normalizeLabels(value) {
226
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
227
+ return {};
228
+ }
229
+
230
+ const labels = {};
231
+ for (const [key, rawValue] of Object.entries(value)) {
232
+ labels[String(key).trim()] = String(rawValue ?? '').trim();
233
+ }
234
+
235
+ return labels;
236
+ }
237
+
238
+ async function readBacklog(backlogPath) {
239
+ const { readFile } = await import('node:fs/promises');
240
+ return readFile(backlogPath, 'utf8');
241
+ }
242
+
243
+ function resolveBacklogPath(options) {
244
+ if (!options.backlogPath) {
245
+ throw new TypeError('backlogPath is required for backlog compatibility writes');
246
+ }
247
+
248
+ if (/^(?:[A-Za-z]:[\\/]|\\\\|\/)/u.test(options.backlogPath)) {
249
+ return options.backlogPath;
250
+ }
251
+
252
+ return `${options.cwd ?? process.cwd()}/${options.backlogPath}`;
253
+ }
@@ -0,0 +1,130 @@
1
+ import {
2
+ applyAtomInspectorProposalToBacklogFile,
3
+ buildAtomInspectorProposal,
4
+ importStepAtomDraftForWorkItem,
5
+ } from './atomInspector.mjs';
6
+ import {
7
+ readWorkItemAtomFromRepo,
8
+ readWorkItemsFromRepo,
9
+ } from './intentTreeWorkItems.mjs';
10
+
11
+ export function parseAtomInspectorRequestBody(rawBody) {
12
+ if (rawBody === undefined || rawBody === null) {
13
+ return {};
14
+ }
15
+
16
+ if (typeof rawBody === 'string' && rawBody.trim() === '') {
17
+ return {};
18
+ }
19
+
20
+ const body = typeof rawBody === 'string' ? JSON.parse(rawBody) : rawBody;
21
+
22
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
23
+ throw new TypeError('atom inspector body must be a JSON object');
24
+ }
25
+
26
+ return body;
27
+ }
28
+
29
+ export async function readAtomInspectorDraftResponse(options = {}) {
30
+ const cwd = options.cwd ?? process.cwd();
31
+ const workId = String(options.workId ?? '').trim();
32
+
33
+ if (workId === '') {
34
+ throw new TypeError('workId is required');
35
+ }
36
+
37
+ const source = await readWorkItemAtomFromRepo(workId, { ...options, cwd });
38
+ const imported = importStepAtomDraftForWorkItem(source.atomText, workId);
39
+ const items = await readWorkItemsFromRepo({ ...options, cwd });
40
+ const item = items.find((entry) => entry.id === workId) ?? null;
41
+
42
+ return {
43
+ schema: 'atom-inspector.draft.v1',
44
+ workId,
45
+ atomName: imported.atomName,
46
+ draft: imported.draft,
47
+ warnings: imported.warnings,
48
+ validationErrors: imported.validationErrors,
49
+ snapshot: item
50
+ ? {
51
+ id: item.id,
52
+ title: item.title,
53
+ status: item.status,
54
+ ownerRole: item.ownerRole,
55
+ department: item.department,
56
+ priority: item.priority,
57
+ risk: item.risk,
58
+ dependsOn: item.dependsOn,
59
+ targetFiles: item.targetFiles,
60
+ nextAction: item.nextAction,
61
+ blocker: item.blocker,
62
+ }
63
+ : null,
64
+ };
65
+ }
66
+
67
+ export async function executeAtomInspectorProposal(options = {}) {
68
+ const body = parseAtomInspectorRequestBody(options.body ?? {});
69
+ const workId = String(body.workId ?? body.taskId ?? '').trim();
70
+ const cwd = options.cwd ?? process.cwd();
71
+
72
+ if (workId === '') {
73
+ return buildAtomInspectorProposal({}, { targetFile: options.backlogPath ?? 'intent/index.bvc' });
74
+ }
75
+
76
+ if (!body.draft) {
77
+ const draftResponse = await readAtomInspectorDraftResponse({ ...options, cwd, workId });
78
+ return buildAtomInspectorProposal(draftResponse.draft, {
79
+ workId,
80
+ targetFile: options.backlogPath ?? 'intent/index.bvc',
81
+ });
82
+ }
83
+
84
+ return buildAtomInspectorProposal(body.draft, {
85
+ workId,
86
+ targetFile: options.backlogPath ?? 'intent/index.bvc',
87
+ });
88
+ }
89
+
90
+ export async function executeAtomInspectorApply(options = {}) {
91
+ const body = parseAtomInspectorRequestBody(options.body ?? {});
92
+ const workId = String(body.workId ?? body.taskId ?? '').trim();
93
+ const cwd = options.cwd ?? process.cwd();
94
+
95
+ if (workId === '') {
96
+ return {
97
+ schema: 'atom-inspector.apply.response.v1',
98
+ ok: false,
99
+ error: 'work_id_required',
100
+ workId: null,
101
+ persistedBacklog: false,
102
+ };
103
+ }
104
+
105
+ if (!body.draft) {
106
+ return {
107
+ schema: 'atom-inspector.apply.response.v1',
108
+ ok: false,
109
+ error: 'draft_required',
110
+ workId,
111
+ persistedBacklog: false,
112
+ };
113
+ }
114
+
115
+ return applyAtomInspectorProposalToBacklogFile({
116
+ ...options,
117
+ cwd,
118
+ workId,
119
+ draft: body.draft,
120
+ targetFile: options.backlogPath ?? 'intent/index.bvc',
121
+ persistBacklog: body.persistBacklog !== false && options.persistBacklog !== false,
122
+ });
123
+ }
124
+
125
+ export {
126
+ buildCodeGapDraftProposal,
127
+ buildWorkItemDraftFromCodeGapSuggestion,
128
+ executeCodeGapDraftApply,
129
+ executeCodeGapDraftProposal,
130
+ } from './codeGapDraftIntakeApi.mjs';
@@ -0,0 +1,121 @@
1
+ import { access, readFile } from 'node:fs/promises';
2
+ import { join, resolve } from 'node:path';
3
+
4
+ export const AUDIT_GAP_MATRIX_SYNC_SCHEMA = 'workgraph.audit-gap-matrix.sync.v1';
5
+
6
+ const REQUIRED_MATRIX_STATUSES = ['implemented', 'contract-only', 'stub', 'deferred', 'replace'];
7
+ const REQUIRED_MATRIX_SECTIONS = [
8
+ '## Легенда статусов',
9
+ '## Матрица: capability → strategy → fact → follow-up',
10
+ '## Post-rollout backlog',
11
+ ];
12
+
13
+ const RECONCILE_PROCEDURE_HEADING = '## Процедура сверки при изменениях ioHasC';
14
+
15
+ /**
16
+ * @param {string} text
17
+ * @param {string[]} needles
18
+ */
19
+ function includesAll(text, needles) {
20
+ return needles.every((needle) => text.includes(needle));
21
+ }
22
+
23
+ async function pathExists(path) {
24
+ try {
25
+ await access(path);
26
+ return true;
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * @param {{ cwd?: string, matrixPath?: string, rebuildPlanPath?: string, iohascStatusPath?: string, matrixText?: string, rebuildPlanText?: string }} [options]
34
+ */
35
+ export async function evaluateAuditGapMatrixSync(options = {}) {
36
+ const cwd = resolve(options.cwd ?? process.cwd());
37
+ const matrixPath = resolve(cwd, options.matrixPath ?? 'docs/plan-iohasc-rebuild-audit-gap-matrix.md');
38
+ const rebuildPlanPath = resolve(cwd, options.rebuildPlanPath ?? 'docs/plan-iohasc-full-rebuild-backlog.md');
39
+ const iohascStatusPath = resolve(cwd, options.iohascStatusPath ?? '../project/docs/architecture-v2/IMPLEMENTATION_STATUS.md');
40
+
41
+ const checks = [];
42
+ const matrixText = options.matrixText ?? await readFile(matrixPath, 'utf8');
43
+ const rebuildPlanText = options.rebuildPlanText ?? await readFile(rebuildPlanPath, 'utf8');
44
+
45
+ checks.push({
46
+ id: 'matrix-status-legend',
47
+ label: 'Audit-gap matrix documents implemented/contract-only/stub/deferred/replace legend',
48
+ met: includesAll(matrixText, REQUIRED_MATRIX_STATUSES),
49
+ evidence: matrixPath,
50
+ });
51
+
52
+ checks.push({
53
+ id: 'matrix-required-sections',
54
+ label: 'Audit-gap matrix includes legend, capability table, and post-rollout backlog sections',
55
+ met: includesAll(matrixText, REQUIRED_MATRIX_SECTIONS),
56
+ evidence: matrixPath,
57
+ });
58
+
59
+ checks.push({
60
+ id: 'rebuild-plan-matrix-link',
61
+ label: 'Full rebuild backlog links to audit-gap matrix',
62
+ met: rebuildPlanText.includes('plan-iohasc-rebuild-audit-gap-matrix.md'),
63
+ evidence: rebuildPlanPath,
64
+ });
65
+
66
+ checks.push({
67
+ id: 'reconcile-procedure-documented',
68
+ label: 'Full rebuild backlog documents reconcile procedure heading',
69
+ met: rebuildPlanText.includes(RECONCILE_PROCEDURE_HEADING),
70
+ evidence: rebuildPlanPath,
71
+ });
72
+
73
+ checks.push({
74
+ id: 'reconcile-cli-referenced',
75
+ label: 'Full rebuild backlog references npm run check:audit-gap-matrix',
76
+ met: rebuildPlanText.includes('npm run check:audit-gap-matrix'),
77
+ evidence: rebuildPlanPath,
78
+ });
79
+
80
+ const iohascStatusExists = await pathExists(iohascStatusPath);
81
+ checks.push({
82
+ id: 'iohasc-status-source-present',
83
+ label: 'Sibling ioHasC IMPLEMENTATION_STATUS source is reachable when present',
84
+ met: !iohascStatusExists || rebuildPlanText.includes('IMPLEMENTATION_STATUS.md'),
85
+ evidence: iohascStatusExists ? iohascStatusPath : 'optional sibling ../project not checked in',
86
+ });
87
+
88
+ if (iohascStatusExists) {
89
+ const iohascStatusText = await readFile(iohascStatusPath, 'utf8');
90
+ checks.push({
91
+ id: 'iohasc-status-has-phase-headings',
92
+ label: 'ioHasC IMPLEMENTATION_STATUS contains phase headings for manual reconcile',
93
+ met: /Phase\s+\d+/u.test(iohascStatusText) || /##\s+/u.test(iohascStatusText),
94
+ evidence: iohascStatusPath,
95
+ });
96
+ }
97
+
98
+ const unmet = checks.filter((check) => !check.met);
99
+ return {
100
+ schema: AUDIT_GAP_MATRIX_SYNC_SCHEMA,
101
+ ok: unmet.length === 0,
102
+ matrixPath: join(cwd, 'docs/plan-iohasc-rebuild-audit-gap-matrix.md'),
103
+ rebuildPlanPath: join(cwd, 'docs/plan-iohasc-full-rebuild-backlog.md'),
104
+ iohascStatusPath: iohascStatusExists ? iohascStatusPath : null,
105
+ checkCount: checks.length,
106
+ unmetCount: unmet.length,
107
+ checks,
108
+ };
109
+ }
110
+
111
+ export function formatAuditGapMatrixSyncReport(report) {
112
+ const lines = [
113
+ `audit-gap matrix sync: ${report.ok ? 'ok' : 'failed'} (${report.unmetCount}/${report.checkCount} unmet)`,
114
+ ];
115
+
116
+ for (const check of report.checks) {
117
+ lines.push(`${check.met ? 'ok' : 'fail'}: [${check.id}] ${check.label}`);
118
+ }
119
+
120
+ return lines.join('\n');
121
+ }
@@ -0,0 +1,176 @@
1
+ import { resolve } from 'node:path';
2
+
3
+ import { lintWorkItemHierarchyIssues } from './workItemHierarchy.mjs';
4
+ import { lintPipelineStageIssues } from './pipelineStageLint.mjs';
5
+ import { readWorkItemsFromRepo } from './intentTreeWorkItems.mjs';
6
+ import { parseWorkItems } from './workGraphRuntime.mjs';
7
+ import { evaluateWorkItemBvcQuality } from './workItemBvcQuality.mjs';
8
+ import { evaluateWorkItemProseLint } from './workItemProseLint.mjs';
9
+
10
+ export const ALLOWED_MIGRATION_STRATEGIES = new Set(['port', 'rebuild', 'replace', 'defer']);
11
+
12
+ const ALLOWED_STATUSES = new Set(['backlog', 'ready', 'claimed', 'doing', 'in_progress', 'verify', 'done', 'verified', 'blocked']);
13
+ const DONE_STATUSES = new Set(['done', 'verified']);
14
+ const ACTIVE_STATUSES = new Set(['backlog', 'ready', 'claimed', 'doing', 'in_progress', 'verify']);
15
+
16
+ const compareText = (left, right) => String(left).localeCompare(String(right), 'en', { sensitivity: 'variant' });
17
+
18
+ /**
19
+ * @param {Array<{ id: string, status: string, dependsOn?: string[], evidence?: string[], nextAction?: string, basis?: string, vector?: string, goal?: string }>} items
20
+ * @param {{ strictBvc?: boolean }} [options]
21
+ */
22
+ export function lintBacklogItems(items, options = {}) {
23
+ if (!Array.isArray(items)) {
24
+ throw new TypeError('items must be an array');
25
+ }
26
+
27
+ const issues = [];
28
+ const seenIds = new Map();
29
+ const itemById = new Map(items.map((item) => [item.id, item]));
30
+
31
+ for (const item of items) {
32
+ if (typeof item.id !== 'string' || item.id.trim() === '') {
33
+ issues.push({
34
+ severity: 'error',
35
+ code: 'missing_work_id',
36
+ message: 'WorkItem missing work.id',
37
+ workId: item.id ?? '',
38
+ });
39
+ continue;
40
+ }
41
+
42
+ const count = (seenIds.get(item.id) ?? 0) + 1;
43
+ seenIds.set(item.id, count);
44
+ if (count > 1) {
45
+ issues.push({
46
+ severity: 'error',
47
+ code: 'duplicate_work_id',
48
+ message: `Duplicate work.id: ${item.id}`,
49
+ workId: item.id,
50
+ });
51
+ }
52
+
53
+ if (!ALLOWED_STATUSES.has(item.status)) {
54
+ issues.push({
55
+ severity: 'error',
56
+ code: 'invalid_status',
57
+ message: `Invalid work.status "${item.status}" for ${item.id}`,
58
+ workId: item.id,
59
+ status: item.status,
60
+ });
61
+ }
62
+
63
+ for (const dependencyId of item.dependsOn ?? []) {
64
+ if (dependencyId === item.id) {
65
+ issues.push({
66
+ severity: 'error',
67
+ code: 'self_dependency',
68
+ message: `WorkItem depends on itself: ${item.id}`,
69
+ workId: item.id,
70
+ dependencyId,
71
+ });
72
+ } else if (!itemById.has(dependencyId)) {
73
+ issues.push({
74
+ severity: 'error',
75
+ code: 'missing_dependency',
76
+ message: `Missing dependency "${dependencyId}" for ${item.id}`,
77
+ workId: item.id,
78
+ dependencyId,
79
+ });
80
+ }
81
+ }
82
+
83
+ if (ACTIVE_STATUSES.has(item.status) && String(item.nextAction ?? '').trim() === '') {
84
+ issues.push({
85
+ severity: 'warning',
86
+ code: 'missing_next_action',
87
+ message: `Active WorkItem ${item.id} has empty work.next_action`,
88
+ workId: item.id,
89
+ status: item.status,
90
+ });
91
+ }
92
+
93
+ if (DONE_STATUSES.has(item.status) && !(item.evidence?.length > 0)) {
94
+ issues.push({
95
+ severity: 'warning',
96
+ code: 'done_without_evidence',
97
+ message: `Done WorkItem ${item.id} has no Свидетельства section lines`,
98
+ workId: item.id,
99
+ status: item.status,
100
+ });
101
+ }
102
+
103
+ const migrationStrategy = String(item.labels?.['migration.strategy'] ?? '').trim();
104
+ if (migrationStrategy === '') {
105
+ issues.push({
106
+ severity: 'error',
107
+ code: 'missing_migration_strategy',
108
+ message: `WorkItem ${item.id} missing migration.strategy (allowed: port, rebuild, replace, defer)`,
109
+ workId: item.id,
110
+ });
111
+ } else if (!ALLOWED_MIGRATION_STRATEGIES.has(migrationStrategy)) {
112
+ issues.push({
113
+ severity: 'error',
114
+ code: 'invalid_migration_strategy',
115
+ message: `WorkItem ${item.id} has invalid migration.strategy "${migrationStrategy}"`,
116
+ workId: item.id,
117
+ migrationStrategy,
118
+ });
119
+ }
120
+
121
+ for (const bvcIssue of evaluateWorkItemBvcQuality(item)) {
122
+ issues.push({
123
+ ...bvcIssue,
124
+ severity: options.strictBvc ? 'error' : bvcIssue.severity,
125
+ });
126
+ }
127
+
128
+ for (const proseIssue of evaluateWorkItemProseLint(item)) {
129
+ issues.push(proseIssue);
130
+ }
131
+ }
132
+
133
+ for (const hierarchyIssue of lintWorkItemHierarchyIssues(items)) {
134
+ issues.push(hierarchyIssue);
135
+ }
136
+
137
+ for (const pipelineIssue of lintPipelineStageIssues(items)) {
138
+ issues.push(pipelineIssue);
139
+ }
140
+
141
+ const errors = issues.filter((issue) => issue.severity === 'error');
142
+ const warnings = issues.filter((issue) => issue.severity === 'warning');
143
+
144
+ return {
145
+ schema: 'workgraph.backlog.lint.v1',
146
+ ok: errors.length === 0,
147
+ itemCount: items.length,
148
+ errorCount: errors.length,
149
+ warningCount: warnings.length,
150
+ issues: [...errors, ...warnings].sort((left, right) => compareText(left.workId, right.workId) || compareText(left.code, right.code)),
151
+ };
152
+ }
153
+
154
+ export function formatBacklogLintReport(report) {
155
+ const lines = [
156
+ `backlog lint: ${report.ok ? 'ok' : 'failed'} (${report.errorCount} errors, ${report.warningCount} warnings, ${report.itemCount} items)`,
157
+ ];
158
+
159
+ for (const issue of report.issues) {
160
+ lines.push(`${issue.severity}: [${issue.code}] ${issue.message}`);
161
+ }
162
+
163
+ return lines.join('\n');
164
+ }
165
+
166
+ export async function lintBacklogFile(options = {}) {
167
+ const items = await readWorkItemsFromRepo({
168
+ cwd: resolve(options.cwd ?? process.cwd()),
169
+ backlogPath: options.backlogPath,
170
+ backlogText: options.backlogText,
171
+ intentIndexPath: options.intentIndexPath,
172
+ });
173
+ return lintBacklogItems(items, {
174
+ strictBvc: options.strictBvc === true,
175
+ });
176
+ }