@vibecompass/vibecompass 0.10.4 → 0.10.6
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/CHANGELOG.md +16 -0
- package/README.md +7 -2
- package/package.json +1 -1
- package/src/cli.js +101 -2
- package/src/docs-review.js +104 -6
- package/src/docs-update.js +538 -0
- package/src/index.js +1 -0
- package/src/project-memory.js +27 -0
- package/src/scaffold.js +29 -6
- package/src/session.js +134 -0
- package/src/status.js +91 -2
- package/src/workflow.js +1 -0
- package/src/workflows/registry.js +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.10.6 - 2026-06-22
|
|
6
|
+
|
|
7
|
+
- Add `docs-update` as a session-delta documentation maintenance planner that maps changed files, lane claims, session repos/features, new decisions, and package-owned generated/state surfaces to targeted architecture/decision/session follow-up.
|
|
8
|
+
- Print the same docs-update plan during `close-session` / `end-session` before the required document-maintenance checkpoint, while keeping semantic document authorship with the closing agent or human.
|
|
9
|
+
- Breaking: `close-session` / `end-session` and the `closeProjectSession` API now require document-maintenance checkpoint statuses for architecture docs, the decision log, and session handoff/scratch maintenance before a lane can be finalized.
|
|
10
|
+
- Surface shared docs-review architecture quality warnings for missing review metadata, evidence, blindspots, retrieval guidance, and repo scope in scanner/status output.
|
|
11
|
+
- Refresh `state/manifest.json.active_sessions` after session start, switch, and close commands so package-managed roots do not keep stale active-lane summaries.
|
|
12
|
+
|
|
13
|
+
## 0.10.5 - 2026-06-20
|
|
14
|
+
|
|
15
|
+
- Include compact docs-review evidence summaries in hosted review submissions, covering coverage score basis, source-inventory totals, documentation-plan totals, reconciliation deltas, producer stamps, and warning provenance.
|
|
16
|
+
- Bump docs-review to `VibeCompass Docs Review Prompt v7` and reject accepted coverage plans where `areas[].linked_inventory_ids[]` references inventory IDs missing from the same plan's `completeness_inventory[]`; hosted parsing now enforces the same contract under `hosted-docs-review-parser-v6`.
|
|
17
|
+
- Preserve optional `completeness_inventory[].repo_id` and `confidence` fields in accepted coverage-plan projections for hosted source-inventory filtering.
|
|
18
|
+
|
|
3
19
|
## 0.10.4 - 2026-06-19
|
|
4
20
|
|
|
5
21
|
- Add `vibecompass --version`, `vibecompass -v`, and `vibecompass version` CLI support.
|
package/README.md
CHANGED
|
@@ -113,6 +113,9 @@ npx -y @vibecompass/vibecompass@latest close-session \
|
|
|
113
113
|
--session feature-lane \
|
|
114
114
|
--title "Feature Lane" \
|
|
115
115
|
--completed "Shipped the slice" \
|
|
116
|
+
--architecture-docs updated \
|
|
117
|
+
--decision-log not-needed \
|
|
118
|
+
--session-maintenance updated \
|
|
116
119
|
--next-step "Review and publish"
|
|
117
120
|
```
|
|
118
121
|
|
|
@@ -127,12 +130,14 @@ npx -y @vibecompass/vibecompass@latest status --root .compass
|
|
|
127
130
|
npx -y @vibecompass/vibecompass@latest start-session --root .compass --id auth-flow --working-on "Auth flow"
|
|
128
131
|
npx -y @vibecompass/vibecompass@latest list-sessions --root .compass
|
|
129
132
|
npx -y @vibecompass/vibecompass@latest switch-session auth-flow --root .compass
|
|
130
|
-
npx -y @vibecompass/vibecompass@latest
|
|
133
|
+
npx -y @vibecompass/vibecompass@latest docs-update --root .compass --session auth-flow
|
|
134
|
+
npx -y @vibecompass/vibecompass@latest close-session --root .compass --session auth-flow --title "Auth Flow" --completed "Built auth" --architecture-docs updated --decision-log not-needed --session-maintenance updated --next-step "Review"
|
|
131
135
|
|
|
132
136
|
# Agent instruction files
|
|
133
137
|
npx -y @vibecompass/vibecompass@latest sync-agents --root .compass
|
|
134
138
|
|
|
135
|
-
#
|
|
139
|
+
# Targeted and comprehensive architecture docs maintenance
|
|
140
|
+
npx -y @vibecompass/vibecompass@latest docs-update --root .compass --session auth-flow --changed app:src/auth/login.ts
|
|
136
141
|
npx -y @vibecompass/vibecompass@latest docs-review --root .compass --guided
|
|
137
142
|
npx -y @vibecompass/vibecompass@latest docs-review --root .compass --guided --source-root app=../app
|
|
138
143
|
npx -y @vibecompass/vibecompass@latest docs-review --root .compass --apply-output
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import { realpathSync } from 'node:fs';
|
|
|
4
4
|
import { pathToFileURL } from 'node:url';
|
|
5
5
|
import { connectHostedProjectMemory, initializeProjectMemory, setDefaultSyncTarget } from './init.js';
|
|
6
6
|
import { preflightDocsReview } from './docs-review.js';
|
|
7
|
+
import { planDocsUpdate, renderDocsUpdatePlan } from './docs-update.js';
|
|
7
8
|
import { resolveConnectHostedCliOptions, resolveInitCliOptions } from './setup.js';
|
|
8
9
|
import { closeProjectSession, listProjectSessions, startProjectSession, switchProjectSession } from './session.js';
|
|
9
10
|
import { syncAgentInstructionFiles } from './generators/agent-files/index.js';
|
|
@@ -205,6 +206,20 @@ export async function runCli(argv, io = createDefaultIo(), runtime = {}) {
|
|
|
205
206
|
return 0;
|
|
206
207
|
}
|
|
207
208
|
|
|
209
|
+
if (parsed.command === 'docs-update') {
|
|
210
|
+
await writeCompatibilityPreflightWarnings(io, parsed.options, runtime);
|
|
211
|
+
const result = await planDocsUpdate({
|
|
212
|
+
...parsed.options,
|
|
213
|
+
...(runtime.cwd ? { cwd: runtime.cwd } : {}),
|
|
214
|
+
});
|
|
215
|
+
if (parsed.options.json) {
|
|
216
|
+
io.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
217
|
+
} else {
|
|
218
|
+
io.stdout.write(renderDocsUpdatePlan(result));
|
|
219
|
+
}
|
|
220
|
+
return 0;
|
|
221
|
+
}
|
|
222
|
+
|
|
208
223
|
if (parsed.command === 'close-session' || parsed.command === 'end-session') {
|
|
209
224
|
await writeCompatibilityPreflightWarnings(io, parsed.options, runtime);
|
|
210
225
|
const result = await closeProjectSession({
|
|
@@ -214,12 +229,17 @@ export async function runCli(argv, io = createDefaultIo(), runtime = {}) {
|
|
|
214
229
|
io.stdout.write(`Closed session ${result.sessionDate}-${result.sessionNumber}\n`);
|
|
215
230
|
io.stdout.write(`Wrote ${result.sessionFilePath}\n`);
|
|
216
231
|
io.stdout.write(`Updated ${result.claudePath}\n`);
|
|
232
|
+
if (result.docsUpdatePlan) {
|
|
233
|
+
io.stdout.write(renderDocsUpdatePlan(result.docsUpdatePlan));
|
|
234
|
+
}
|
|
235
|
+
writeDocumentMaintenanceCheckpoint(io, result.documentMaintenance);
|
|
217
236
|
if (result.workflowGuidance.length > 0) {
|
|
218
237
|
io.stdout.write('Workflow guidance:\n');
|
|
219
238
|
for (const item of result.workflowGuidance) {
|
|
220
239
|
io.stdout.write(`- ${item}\n`);
|
|
221
240
|
}
|
|
222
241
|
}
|
|
242
|
+
writeWarnings(io, result.warnings);
|
|
223
243
|
writeAgentFileSyncResult(io, result.agentFileSync);
|
|
224
244
|
return 0;
|
|
225
245
|
}
|
|
@@ -250,6 +270,7 @@ export async function runCli(argv, io = createDefaultIo(), runtime = {}) {
|
|
|
250
270
|
});
|
|
251
271
|
io.stdout.write(`Current session lane: ${result.current}\n`);
|
|
252
272
|
io.stdout.write(`Updated ${result.claudePath}\n`);
|
|
273
|
+
writeWarnings(io, result.warnings);
|
|
253
274
|
writeAgentFileSyncResult(io, result.agentFileSync);
|
|
254
275
|
return 0;
|
|
255
276
|
}
|
|
@@ -457,6 +478,10 @@ export function parseCliArgs(argv) {
|
|
|
457
478
|
return parseRefreshWorkflowArgs(rest);
|
|
458
479
|
}
|
|
459
480
|
|
|
481
|
+
if (command === 'docs-update') {
|
|
482
|
+
return parseDocsUpdateArgs(rest);
|
|
483
|
+
}
|
|
484
|
+
|
|
460
485
|
if (command === 'close-session' || command === 'end-session') {
|
|
461
486
|
return parseCloseSessionArgs(rest);
|
|
462
487
|
}
|
|
@@ -896,6 +921,7 @@ function parseCloseSessionArgs(argv) {
|
|
|
896
921
|
models: [],
|
|
897
922
|
blockers: [],
|
|
898
923
|
nextSteps: [],
|
|
924
|
+
documentMaintenance: {},
|
|
899
925
|
};
|
|
900
926
|
// Repeatable CLI flags stay singular for readability, but map to plural arrays in the JS API.
|
|
901
927
|
|
|
@@ -949,6 +975,15 @@ function parseCloseSessionArgs(argv) {
|
|
|
949
975
|
case '--next-session-should':
|
|
950
976
|
parsed.nextSessionShould = value;
|
|
951
977
|
break;
|
|
978
|
+
case '--architecture-docs':
|
|
979
|
+
parsed.documentMaintenance.architectureDocs = value;
|
|
980
|
+
break;
|
|
981
|
+
case '--decision-log':
|
|
982
|
+
parsed.documentMaintenance.decisionLog = value;
|
|
983
|
+
break;
|
|
984
|
+
case '--session-maintenance':
|
|
985
|
+
parsed.documentMaintenance.sessionMaintenance = value;
|
|
986
|
+
break;
|
|
952
987
|
default:
|
|
953
988
|
throw new Error(`Unknown flag "${token}".`);
|
|
954
989
|
}
|
|
@@ -960,6 +995,49 @@ function parseCloseSessionArgs(argv) {
|
|
|
960
995
|
};
|
|
961
996
|
}
|
|
962
997
|
|
|
998
|
+
function parseDocsUpdateArgs(argv) {
|
|
999
|
+
const parsed = {
|
|
1000
|
+
changedFiles: [],
|
|
1001
|
+
};
|
|
1002
|
+
|
|
1003
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
1004
|
+
const token = argv[index];
|
|
1005
|
+
if (token === '--json') {
|
|
1006
|
+
parsed.json = true;
|
|
1007
|
+
continue;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
if (!token.startsWith('--')) {
|
|
1011
|
+
throw new Error(`Unexpected argument "${token}".`);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
const value = argv[index + 1];
|
|
1015
|
+
if (value === undefined) {
|
|
1016
|
+
throw new Error(`Flag "${token}" requires a value.`);
|
|
1017
|
+
}
|
|
1018
|
+
index += 1;
|
|
1019
|
+
|
|
1020
|
+
switch (token) {
|
|
1021
|
+
case '--root':
|
|
1022
|
+
parsed.rootDir = value;
|
|
1023
|
+
break;
|
|
1024
|
+
case '--session':
|
|
1025
|
+
parsed.sessionId = value;
|
|
1026
|
+
break;
|
|
1027
|
+
case '--changed':
|
|
1028
|
+
parsed.changedFiles.push(value);
|
|
1029
|
+
break;
|
|
1030
|
+
default:
|
|
1031
|
+
throw new Error(`Unknown flag "${token}".`);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
return {
|
|
1036
|
+
command: 'docs-update',
|
|
1037
|
+
options: parsed,
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
|
|
963
1041
|
function parseListSessionsArgs(argv) {
|
|
964
1042
|
const parsed = {};
|
|
965
1043
|
|
|
@@ -1464,6 +1542,17 @@ function writeRefreshWorkflowResult(io, result) {
|
|
|
1464
1542
|
writeAgentFileSyncResult(io, result.agentFileSync);
|
|
1465
1543
|
}
|
|
1466
1544
|
|
|
1545
|
+
function writeDocumentMaintenanceCheckpoint(io, documentMaintenance) {
|
|
1546
|
+
if (!documentMaintenance) {
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
io.stdout.write('Document maintenance checkpoint:\n');
|
|
1551
|
+
io.stdout.write(`- Architecture docs: ${documentMaintenance.architectureDocs}\n`);
|
|
1552
|
+
io.stdout.write(`- Decision log: ${documentMaintenance.decisionLog}\n`);
|
|
1553
|
+
io.stdout.write(`- Session handoff/scratch: ${documentMaintenance.sessionMaintenance}\n`);
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1467
1556
|
async function writeCompatibilityPreflightWarnings(io, options = {}, runtime = {}) {
|
|
1468
1557
|
const result = await inspectProjectCompatibility({
|
|
1469
1558
|
...options,
|
|
@@ -1513,9 +1602,10 @@ function usageText() {
|
|
|
1513
1602
|
' vibecompass sync-target [<name>] [options]',
|
|
1514
1603
|
' vibecompass status [options]',
|
|
1515
1604
|
' vibecompass refresh-workflow [--dry-run|--apply] [options]',
|
|
1605
|
+
' vibecompass docs-update [--session <lane-id>] [options]',
|
|
1516
1606
|
' vibecompass start-session --id <lane-id> --working-on <text> [options]',
|
|
1517
|
-
' vibecompass close-session --title <text> --completed <text> --
|
|
1518
|
-
' vibecompass end-session --title <text> --completed <text> --
|
|
1607
|
+
' vibecompass close-session --title <text> --completed <text> --architecture-docs <status> --decision-log <status> --session-maintenance <status> [options]',
|
|
1608
|
+
' vibecompass end-session --title <text> --completed <text> --architecture-docs <status> --decision-log <status> --session-maintenance <status> [options] # alias',
|
|
1519
1609
|
' vibecompass list-sessions [options]',
|
|
1520
1610
|
' vibecompass switch-session <id> [options]',
|
|
1521
1611
|
' vibecompass sync-agents [options]',
|
|
@@ -1578,6 +1668,12 @@ function usageText() {
|
|
|
1578
1668
|
' --update-package-stamp Update an existing project.yaml package_version stamp',
|
|
1579
1669
|
' --allow-downgrade-templates Allow applying templates with a CLI older than the root stamp',
|
|
1580
1670
|
'',
|
|
1671
|
+
'Docs-update options:',
|
|
1672
|
+
' --root <path> Project-memory root. Defaults to .compass',
|
|
1673
|
+
' --session <lane-id> Active session lane to inspect; defaults to current lane',
|
|
1674
|
+
' --changed <repo:path|path> Repeatable explicit changed path; defaults to git status when omitted',
|
|
1675
|
+
' --json Print the typed docs-update plan as JSON',
|
|
1676
|
+
'',
|
|
1581
1677
|
'Start-session options:',
|
|
1582
1678
|
' --root <path> Project-memory root. Defaults to .compass',
|
|
1583
1679
|
' --tooling-root <path> Tooling root that contains CLAUDE.md. Defaults to cwd',
|
|
@@ -1602,6 +1698,9 @@ function usageText() {
|
|
|
1602
1698
|
' --model <text> Optional repeatable model contribution entry',
|
|
1603
1699
|
' --blocker <text> Repeatable blocker or open question',
|
|
1604
1700
|
' --next-step <text> Repeatable next-session step',
|
|
1701
|
+
' --architecture-docs <status> Required checkpoint: updated|not-needed|deferred',
|
|
1702
|
+
' --decision-log <status> Required checkpoint: updated|not-needed|deferred',
|
|
1703
|
+
' --session-maintenance <status> Required checkpoint: updated|not-needed|deferred',
|
|
1605
1704
|
' --last-thing-completed <text> Optional override for the CLAUDE.md completed summary',
|
|
1606
1705
|
' --next-session-should <text> Optional override for the CLAUDE.md next-session summary',
|
|
1607
1706
|
'',
|
package/src/docs-review.js
CHANGED
|
@@ -17,8 +17,9 @@ import {
|
|
|
17
17
|
import { readSyncCursor, resolveSyncBinding } from './sync-binding.js';
|
|
18
18
|
import { PACKAGE_VERSION } from './version.js';
|
|
19
19
|
|
|
20
|
-
const DOCS_REVIEW_PROMPT_VERSION = 'VibeCompass Docs Review Prompt
|
|
20
|
+
const DOCS_REVIEW_PROMPT_VERSION = 'VibeCompass Docs Review Prompt v7';
|
|
21
21
|
const DOCS_REVIEW_PARSER_VERSION = 'docs-review-parser-v1';
|
|
22
|
+
const DOCS_REVIEW_ACCEPTED_OUTPUT_CONTRACT_VERSION = 'docs-review-accepted-output-v7';
|
|
22
23
|
const DOCS_REVIEW_DOCUMENTATION_PLAN_PROJECTION_VERSION = 'docs-review-documentation-plan-v1';
|
|
23
24
|
const DOCUMENTATION_PLAN_STATE_VERSION = 1;
|
|
24
25
|
const ARCHITECTURE_DOC_SOFT_SIZE_LIMIT_BYTES = 12000;
|
|
@@ -26,6 +27,7 @@ const COVERAGE_PLAN_OUTPUT_VERSION = 1;
|
|
|
26
27
|
const COVERAGE_PLAN_STATUSES = new Set(['accepted', 'deferred', 'missing']);
|
|
27
28
|
const COVERAGE_LEVELS = new Set(['comprehensive', 'partial', 'initial', 'missing']);
|
|
28
29
|
const ANCHOR_ACTIONS = new Set(['reuse', 'update', 'split', 'merge', 'defer', 'replace', 'new']);
|
|
30
|
+
const SOURCE_CONFIDENCE_LEVELS = new Set(['high', 'medium', 'low']);
|
|
29
31
|
const DOCUMENTATION_PLAN_RUN_SCOPES = new Set(['baseline', 'deepening']);
|
|
30
32
|
const REBUILD_STALE_POLICIES = new Set(['keep', 'archive']);
|
|
31
33
|
const PROJECT_MAP_PATH = 'architecture/overview/project-shape.md';
|
|
@@ -984,6 +986,7 @@ function buildCoverageProjection(blocks, options) {
|
|
|
984
986
|
const areaById = new Map(areas.map((area) => [area.id, area]));
|
|
985
987
|
const hasCompletenessInventory = Object.prototype.hasOwnProperty.call(plan, 'completeness_inventory');
|
|
986
988
|
const completenessInventory = normalizeCompletenessInventory(plan.completeness_inventory, areaById);
|
|
989
|
+
validateCoverageAreaInventoryLinks(areas, completenessInventory);
|
|
987
990
|
const statuses = countBy(areas, 'status');
|
|
988
991
|
const coverage_levels = countBy(areas, 'coverage');
|
|
989
992
|
const inventory_statuses = countBy(completenessInventory, 'status');
|
|
@@ -994,6 +997,7 @@ function buildCoverageProjection(blocks, options) {
|
|
|
994
997
|
package_version: PACKAGE_VERSION,
|
|
995
998
|
prompt_version: DOCS_REVIEW_PROMPT_VERSION,
|
|
996
999
|
parser_version: DOCS_REVIEW_PARSER_VERSION,
|
|
1000
|
+
accepted_output_contract: DOCS_REVIEW_ACCEPTED_OUTPUT_CONTRACT_VERSION,
|
|
997
1001
|
coverage_projection_version: COVERAGE_PLAN_OUTPUT_VERSION,
|
|
998
1002
|
...(options.sourceInventory?.producer?.scanner_version
|
|
999
1003
|
? { scanner_version: options.sourceInventory.producer.scanner_version }
|
|
@@ -1061,6 +1065,7 @@ function buildDocumentationPlanProjection(coverageProjection, sourceInventory, o
|
|
|
1061
1065
|
package_version: PACKAGE_VERSION,
|
|
1062
1066
|
prompt_version: DOCS_REVIEW_PROMPT_VERSION,
|
|
1063
1067
|
parser_version: DOCS_REVIEW_PARSER_VERSION,
|
|
1068
|
+
accepted_output_contract: DOCS_REVIEW_ACCEPTED_OUTPUT_CONTRACT_VERSION,
|
|
1064
1069
|
documentation_plan_projection_version: DOCS_REVIEW_DOCUMENTATION_PLAN_PROJECTION_VERSION,
|
|
1065
1070
|
coverage_projection_version: coverageProjection.version,
|
|
1066
1071
|
...(sourceInventory?.producer?.scanner_version
|
|
@@ -1177,8 +1182,12 @@ function normalizeCompletenessInventoryItem(item, index, ids, areaById, unknownC
|
|
|
1177
1182
|
|
|
1178
1183
|
return {
|
|
1179
1184
|
id,
|
|
1185
|
+
...(normalizeOptionalString(item.repo_id) ? { repo_id: normalizeOptionalString(item.repo_id) } : {}),
|
|
1180
1186
|
...(normalizeOptionalString(item.kind) ? { kind: normalizeOptionalString(item.kind) } : {}),
|
|
1181
1187
|
...(normalizeOptionalString(item.label) ? { label: normalizeOptionalString(item.label) } : {}),
|
|
1188
|
+
...(SOURCE_CONFIDENCE_LEVELS.has(normalizeOptionalString(item.confidence) ?? '')
|
|
1189
|
+
? { confidence: normalizeOptionalString(item.confidence) }
|
|
1190
|
+
: {}),
|
|
1182
1191
|
status,
|
|
1183
1192
|
...(coverageAreaIds.length > 0 ? { coverage_area_ids: coverageAreaIds } : {}),
|
|
1184
1193
|
evidence: normalizeStringArray(item.evidence),
|
|
@@ -1193,6 +1202,28 @@ function formatUnknownCoverageAreaReferences(references) {
|
|
|
1193
1202
|
return `Coverage plan completeness_inventory references unknown coverage area ids: ${formatted}`;
|
|
1194
1203
|
}
|
|
1195
1204
|
|
|
1205
|
+
function validateCoverageAreaInventoryLinks(areas, completenessInventory) {
|
|
1206
|
+
const inventoryIds = new Set(completenessInventory.map((item) => item.id));
|
|
1207
|
+
const unknownInventoryReferences = [];
|
|
1208
|
+
for (const area of areas) {
|
|
1209
|
+
for (const inventoryId of area.linked_inventory_ids ?? []) {
|
|
1210
|
+
if (!inventoryIds.has(inventoryId)) {
|
|
1211
|
+
unknownInventoryReferences.push({ areaId: area.id, inventoryId });
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
if (unknownInventoryReferences.length > 0) {
|
|
1216
|
+
throw new Error(formatUnknownLinkedInventoryReferences(unknownInventoryReferences));
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
function formatUnknownLinkedInventoryReferences(references) {
|
|
1221
|
+
const formatted = references
|
|
1222
|
+
.map((reference) => `"${reference.areaId}" -> "${reference.inventoryId}"`)
|
|
1223
|
+
.join(', ');
|
|
1224
|
+
return `Coverage plan areas reference unknown linked_inventory_ids: ${formatted}`;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1196
1227
|
function normalizeCoverageArea(area, index, ids) {
|
|
1197
1228
|
if (!area || typeof area !== 'object' || Array.isArray(area)) {
|
|
1198
1229
|
throw new Error(`Coverage plan area at index ${index} must be an object.`);
|
|
@@ -1461,6 +1492,7 @@ async function submitHostedDocsReview(options) {
|
|
|
1461
1492
|
const baselineRemoteRevisionId = normalizeUuid(
|
|
1462
1493
|
submitCursor.last_successful_remote_revision,
|
|
1463
1494
|
);
|
|
1495
|
+
const evidenceScope = await buildHostedDocsReviewEvidenceScope(options);
|
|
1464
1496
|
const response = await options.fetch(endpoint.href, {
|
|
1465
1497
|
method: 'POST',
|
|
1466
1498
|
headers: {
|
|
@@ -1481,11 +1513,7 @@ async function submitHostedDocsReview(options) {
|
|
|
1481
1513
|
? { baseline_remote_revision_id: baselineRemoteRevisionId }
|
|
1482
1514
|
: {}),
|
|
1483
1515
|
local_root_revision: options.manifest?.manifest?.canonical?.local_root_revision,
|
|
1484
|
-
evidence_scope:
|
|
1485
|
-
manifest_hash: options.manifest?.manifest?.canonical?.manifest_hash,
|
|
1486
|
-
document_count: options.manifest?.manifest?.canonical?.document_count,
|
|
1487
|
-
warning_count: options.manifest?.manifest?.canonical?.warning_count,
|
|
1488
|
-
},
|
|
1516
|
+
evidence_scope: evidenceScope,
|
|
1489
1517
|
}),
|
|
1490
1518
|
});
|
|
1491
1519
|
|
|
@@ -1517,6 +1545,75 @@ async function submitHostedDocsReview(options) {
|
|
|
1517
1545
|
};
|
|
1518
1546
|
}
|
|
1519
1547
|
|
|
1548
|
+
async function buildHostedDocsReviewEvidenceScope(options) {
|
|
1549
|
+
const rootDir = options.rootDir;
|
|
1550
|
+
const coverage = rootDir
|
|
1551
|
+
? await readJsonOptional(path.join(rootDir, 'state', 'docs-review-coverage.json'))
|
|
1552
|
+
: null;
|
|
1553
|
+
const sourceInventory = rootDir
|
|
1554
|
+
? await readJsonOptional(docsReviewSourceInventoryPath(rootDir))
|
|
1555
|
+
: null;
|
|
1556
|
+
const documentationPlan = rootDir
|
|
1557
|
+
? await readJsonOptional(path.join(rootDir, 'state', 'docs-review-documentation-plan.json'))
|
|
1558
|
+
: null;
|
|
1559
|
+
const warnings = Array.isArray(coverage?.warnings) ? coverage.warnings : [];
|
|
1560
|
+
|
|
1561
|
+
return {
|
|
1562
|
+
manifest_hash: options.manifest?.manifest?.canonical?.manifest_hash,
|
|
1563
|
+
document_count: options.manifest?.manifest?.canonical?.document_count,
|
|
1564
|
+
warning_count: options.manifest?.manifest?.canonical?.warning_count,
|
|
1565
|
+
...(coverage?.producer ? { producer: coverage.producer } : {}),
|
|
1566
|
+
...(coverage ? { coverage_summary: summarizeHostedCoverage(coverage) } : {}),
|
|
1567
|
+
...(coverage?.source_inventory_summary
|
|
1568
|
+
? { source_inventory_summary: coverage.source_inventory_summary }
|
|
1569
|
+
: sourceInventory
|
|
1570
|
+
? { source_inventory_summary: summarizeSourceInventory(sourceInventory) }
|
|
1571
|
+
: {}),
|
|
1572
|
+
...(coverage?.documentation_plan_summary
|
|
1573
|
+
? { documentation_plan_summary: coverage.documentation_plan_summary }
|
|
1574
|
+
: documentationPlan
|
|
1575
|
+
? { documentation_plan_summary: summarizeDocumentationPlan(documentationPlan) }
|
|
1576
|
+
: {}),
|
|
1577
|
+
...(coverage?.reconciliation_summary ? { reconciliation_summary: coverage.reconciliation_summary } : {}),
|
|
1578
|
+
...(warnings.length > 0 ? { warning_provenance: summarizeWarningProvenance(warnings) } : {}),
|
|
1579
|
+
};
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
async function readJsonOptional(filePath) {
|
|
1583
|
+
try {
|
|
1584
|
+
return JSON.parse(await readFile(filePath, 'utf8'));
|
|
1585
|
+
} catch (error) {
|
|
1586
|
+
if (error?.code === 'ENOENT') {
|
|
1587
|
+
return null;
|
|
1588
|
+
}
|
|
1589
|
+
throw error;
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
function summarizeHostedCoverage(coverage) {
|
|
1594
|
+
return {
|
|
1595
|
+
path: 'state/docs-review-coverage.json',
|
|
1596
|
+
...(coverage.score_basis ? { score_basis: coverage.score_basis } : {}),
|
|
1597
|
+
...(typeof coverage.area_count === 'number' ? { area_count: coverage.area_count } : {}),
|
|
1598
|
+
...(typeof coverage.coverage_score === 'number' || coverage.coverage_score === null
|
|
1599
|
+
? { coverage_score: coverage.coverage_score }
|
|
1600
|
+
: {}),
|
|
1601
|
+
...(coverage.statuses ? { statuses: coverage.statuses } : {}),
|
|
1602
|
+
...(coverage.coverage_levels ? { coverage_levels: coverage.coverage_levels } : {}),
|
|
1603
|
+
...(typeof coverage.inventory_count === 'number' ? { inventory_count: coverage.inventory_count } : {}),
|
|
1604
|
+
...(coverage.inventory_statuses ? { inventory_statuses: coverage.inventory_statuses } : {}),
|
|
1605
|
+
...(coverage.topology ? { topology: coverage.topology } : {}),
|
|
1606
|
+
...(coverage.taxonomy?.primary_axis ? { taxonomy_primary_axis: coverage.taxonomy.primary_axis } : {}),
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
function summarizeWarningProvenance(warnings) {
|
|
1611
|
+
return {
|
|
1612
|
+
warning_count: warnings.length,
|
|
1613
|
+
codes: countBy(warnings, 'code'),
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1520
1617
|
function parseJsonObject(value) {
|
|
1521
1618
|
if (!value) return null;
|
|
1522
1619
|
try {
|
|
@@ -2352,6 +2449,7 @@ function renderReviewPrompt(options) {
|
|
|
2352
2449
|
'- Prioritize entry points, user-flow/gating relationships, data ownership, external integrations, jobs/async boundaries, auth/security boundaries, and test surfaces.',
|
|
2353
2450
|
'- Account for each completeness-inventory item in the coverage plan as accepted, deferred, or missing. Do not inflate coverage by omitting large discovered subsystems; honest deferred/missing entries are expected when the first pass cannot cover everything.',
|
|
2354
2451
|
'- Before generating prose, self-check that every `completeness_inventory[].coverage_area_ids[]` value points to an `areas[].id` in the same accepted plan. Fix all dangling ids together at the plan gate.',
|
|
2452
|
+
'- Before generating prose, self-check that every `areas[].linked_inventory_ids[]` value points to a `completeness_inventory[].id` in the same accepted plan. Fix all dangling ids together at the plan gate.',
|
|
2355
2453
|
'- Choose one primary taxonomy axis from topology evidence and keep it stable: single-repo/monolith may default domain-first; multi-repo or multi-surface projects may use platform/repo-first or domain-first with per-surface components. Do not mix taxonomy axes at the same tier unless you explicitly propose and justify a restructuring.',
|
|
2356
2454
|
'- For every prior architecture doc or project-map feature identity in Re-review Anchors, classify the plan action as `reuse`, `update`, `split`, `merge`, `defer`, or `replace`. Use `new` only for areas not represented by the prior tree.',
|
|
2357
2455
|
'- Every `split`, `merge`, or `replace` action requires an evidence-backed anchor reason before prose generation. No unexplained slug rename is allowed.',
|