@vibecompass/vibecompass 0.9.3 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,10 +1,21 @@
1
1
  # Changelog
2
2
 
3
- ## 0.9.3 - 2026-06-14
3
+ ## 0.10.1 - 2026-06-14
4
+
5
+ - Add completeness-inventory guidance to docs-review so coverage plans account for accepted, deferred, and missing subsystems instead of silently omitting large areas.
6
+ - Score accepted coverage against the optional completeness inventory denominator when present, with a backwards-compatible fallback for older coverage plans.
7
+ - Add soft parser warnings for modern coverage plans that omit completeness inventory or under-explain accepted/deferred/missing inventory items.
8
+ - Add prose-only surface matrix guidance for feature docs while keeping `systems[]` as derived overview metadata rather than a second projected graph.
9
+
10
+ ## 0.10.0 - 2026-06-14
4
11
 
5
12
  - Make docs-review prompts mode-aware: interactive AI sessions stop at the coverage-plan approval gate, while `--run-local` and hosted single-turn runs emit fenced proposal material for later apply/proposal review.
6
13
  - Clarify that pre-approval coverage plans are proposed scope, not accepted output, so agents should not label proposed areas `accepted` before user approval.
7
- - Update generated docs-review workflow guidance and prompt regression coverage for the interactive vs single-turn split.
14
+ - Add docs-review Stage 0 quality guidance: topology-aware taxonomy rules, re-review anchoring, prior-doc reuse/update/split/merge/defer/replace classifications, and concrete evidence requirements.
15
+ - Preserve optional topology, taxonomy, and anchor classification fields in accepted coverage-plan projections.
16
+ - Surface quality warnings for architecture docs that keep generic evidence metadata instead of concrete repo:path references.
17
+ - Add Stage 1 backbone guidance for topology, core feature inventory, user journey map, project systems map, coverage/quality summary, and minimal optional derived project-map `systems[]` metadata.
18
+ - Update generated docs-review workflow guidance and prompt regression coverage for the interactive vs single-turn split, Stage 0 anchoring contract, and Stage 1 backbone outputs.
8
19
 
9
20
  ## 0.9.2 - 2026-06-14
10
21
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibecompass/vibecompass",
3
- "version": "0.9.3",
3
+ "version": "0.10.1",
4
4
  "description": "Local-first project-memory core for VibeCompass",
5
5
  "type": "module",
6
6
  "bin": {
@@ -4,16 +4,19 @@ import { createInterface } from 'node:readline/promises';
4
4
  import { parseFrontmatter } from './frontmatter.js';
5
5
  import { sha256Text } from './hash.js';
6
6
  import { writeStateManifest } from './manifest.js';
7
+ import { scanProjectMemory } from './project-memory.js';
7
8
  import { parseSimpleYaml } from './simple-yaml.js';
8
9
  import { readSyncCursor, resolveSyncBinding } from './sync-binding.js';
9
10
  import { PACKAGE_VERSION } from './version.js';
10
11
 
11
- const DOCS_REVIEW_PROMPT_VERSION = 'VibeCompass Docs Review Prompt v3';
12
+ const DOCS_REVIEW_PROMPT_VERSION = 'VibeCompass Docs Review Prompt v6';
12
13
  const ARCHITECTURE_DOC_SOFT_SIZE_LIMIT_BYTES = 12000;
13
14
  const COVERAGE_PLAN_OUTPUT_VERSION = 1;
14
15
  const COVERAGE_PLAN_STATUSES = new Set(['accepted', 'deferred', 'missing']);
15
16
  const COVERAGE_LEVELS = new Set(['comprehensive', 'partial', 'initial', 'missing']);
17
+ const ANCHOR_ACTIONS = new Set(['reuse', 'update', 'split', 'merge', 'defer', 'replace', 'new']);
16
18
  const REBUILD_STALE_POLICIES = new Set(['keep', 'archive']);
19
+ const PROJECT_MAP_PATH = 'architecture/overview/project-shape.md';
17
20
 
18
21
  export async function preflightDocsReview(options = {}, environment = {}) {
19
22
  validateDocsReviewMode(options);
@@ -96,6 +99,7 @@ export async function preflightDocsReview(options = {}, environment = {}) {
96
99
  llm: reviewConfig.llm,
97
100
  model: reviewConfig.model,
98
101
  executionMode: promptExecutionMode,
102
+ anchors: await buildDocsReviewAnchorContext(rootDir),
99
103
  });
100
104
 
101
105
  const status = options.submitHosted
@@ -242,8 +246,10 @@ async function applyDocsReviewOutput(options) {
242
246
  outputPath,
243
247
  outputHash: sha256Text(source),
244
248
  });
249
+ const anchorContext = coverageProjection ? await buildDocsReviewAnchorContext(options.rootDir) : null;
245
250
  const decisionRecommendations = validateDecisionRecommendationBlocks(decisionRecommendationBlocks);
246
251
  const warnings = [
252
+ ...createCoveragePlanQualityWarnings(coverageProjection, anchorContext),
247
253
  ...createArchitectureDocSizeWarnings(blocks),
248
254
  ...createArchitectureDocQualityWarnings(blocks),
249
255
  ];
@@ -448,12 +454,13 @@ async function rebuildDocsReview(options) {
448
454
  rebuild: {
449
455
  scope_path: scopePath,
450
456
  stale_policy: stalePolicy,
457
+ anchor_reconcile_required: true,
451
458
  architecture_docs: rebuildEntries,
452
459
  architecture_doc_count: rebuildEntries.length,
453
460
  archive_root: archiveRoot,
454
461
  },
455
462
  warnings: [],
456
- message: 'Docs-review rebuild preview complete. Re-run with --apply to record the rebuild marker and perform the selected stale-doc action.',
463
+ message: 'Docs-review rebuild preview complete. Re-run with --apply to record the rebuild marker and perform the selected stale-doc action. The next docs-review run must anchor to the prior tree and reconcile taxonomy changes instead of cold-regenerating from a blank slate.',
457
464
  };
458
465
  }
459
466
 
@@ -479,6 +486,7 @@ async function rebuildDocsReview(options) {
479
486
  rebuild: {
480
487
  scope_path: scopePath,
481
488
  stale_policy: stalePolicy,
489
+ anchor_reconcile_required: true,
482
490
  architecture_docs: rebuildEntries,
483
491
  architecture_doc_count: rebuildEntries.length,
484
492
  ...(archiveRoot ? { archive_root: path.join(options.rootDir, archiveRoot) } : {}),
@@ -501,8 +509,8 @@ async function rebuildDocsReview(options) {
501
509
  rebuild: marker.rebuild,
502
510
  warnings: [],
503
511
  message: stalePolicy === 'archive'
504
- ? 'Archived selected architecture docs and recorded a rebuild marker. Run docs-review again, then apply accepted output.'
505
- : 'Recorded a rebuild marker without moving architecture docs. Run docs-review again, then apply accepted output.',
512
+ ? 'Archived selected architecture docs and recorded a rebuild marker. Run docs-review again; the next coverage plan must reconcile against the prior tree and explain reuse/update/split/merge/defer/replace actions.'
513
+ : 'Recorded a rebuild marker without moving architecture docs. Run docs-review again; the next coverage plan must reconcile against the prior tree and explain reuse/update/split/merge/defer/replace actions.',
506
514
  };
507
515
  }
508
516
 
@@ -913,10 +921,12 @@ function buildCoverageProjection(blocks, options) {
913
921
 
914
922
  const ids = new Set();
915
923
  const areas = plan.areas.map((area, index) => normalizeCoverageArea(area, index, ids));
924
+ const areaById = new Map(areas.map((area) => [area.id, area]));
925
+ const hasCompletenessInventory = Object.prototype.hasOwnProperty.call(plan, 'completeness_inventory');
926
+ const completenessInventory = normalizeCompletenessInventory(plan.completeness_inventory, areaById);
916
927
  const statuses = countBy(areas, 'status');
917
928
  const coverage_levels = countBy(areas, 'coverage');
918
- const denominator = areas.length;
919
- const acceptedCount = statuses.accepted ?? 0;
929
+ const inventory_statuses = countBy(completenessInventory, 'status');
920
930
 
921
931
  return {
922
932
  version: COVERAGE_PLAN_OUTPUT_VERSION,
@@ -926,14 +936,99 @@ function buildCoverageProjection(blocks, options) {
926
936
  },
927
937
  projected_at: new Date().toISOString(),
928
938
  summary: normalizeOptionalString(plan.summary),
939
+ ...(normalizeOptionalString(plan.topology) ? { topology: normalizeOptionalString(plan.topology) } : {}),
940
+ ...(normalizeCoverageTaxonomy(plan.taxonomy) ? { taxonomy: normalizeCoverageTaxonomy(plan.taxonomy) } : {}),
929
941
  area_count: areas.length,
930
- coverage_score: denominator === 0 ? null : Number((acceptedCount / denominator).toFixed(4)),
942
+ ...(hasCompletenessInventory ? {
943
+ completeness_inventory: completenessInventory,
944
+ inventory_count: completenessInventory.length,
945
+ inventory_statuses,
946
+ } : {}),
947
+ coverage_score: calculateCoverageScore({ areas, statuses, completenessInventory, hasCompletenessInventory }),
931
948
  statuses,
932
949
  coverage_levels,
933
950
  areas,
934
951
  };
935
952
  }
936
953
 
954
+ function calculateCoverageScore({ areas, statuses, completenessInventory, hasCompletenessInventory }) {
955
+ if (hasCompletenessInventory) {
956
+ const denominator = completenessInventory.length;
957
+ const acceptedCount = completenessInventory.filter((item) => item.status === 'accepted').length;
958
+ return denominator === 0 ? null : Number((acceptedCount / denominator).toFixed(4));
959
+ }
960
+
961
+ const denominator = areas.length;
962
+ const acceptedCount = statuses.accepted ?? 0;
963
+ return denominator === 0 ? null : Number((acceptedCount / denominator).toFixed(4));
964
+ }
965
+
966
+ function normalizeCoverageTaxonomy(value) {
967
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
968
+ return null;
969
+ }
970
+
971
+ const primaryAxis = normalizeOptionalString(value.primary_axis);
972
+ const rationale = normalizeOptionalString(value.rationale);
973
+ if (!primaryAxis && !rationale) {
974
+ return null;
975
+ }
976
+
977
+ return {
978
+ ...(primaryAxis ? { primary_axis: primaryAxis } : {}),
979
+ ...(rationale ? { rationale } : {}),
980
+ };
981
+ }
982
+
983
+ function normalizeCompletenessInventory(value, areaById) {
984
+ if (value === undefined || value === null) {
985
+ return [];
986
+ }
987
+ if (!Array.isArray(value)) {
988
+ throw new Error('Coverage plan completeness_inventory must be an array when present.');
989
+ }
990
+
991
+ const ids = new Set();
992
+ return value.map((item, index) => normalizeCompletenessInventoryItem(item, index, ids, areaById));
993
+ }
994
+
995
+ function normalizeCompletenessInventoryItem(item, index, ids, areaById) {
996
+ if (!item || typeof item !== 'object' || Array.isArray(item)) {
997
+ throw new Error(`Coverage plan completeness_inventory item at index ${index} must be an object.`);
998
+ }
999
+
1000
+ const id = normalizeOptionalString(item.id);
1001
+ if (!id) {
1002
+ throw new Error(`Coverage plan completeness_inventory item at index ${index} requires non-empty "id".`);
1003
+ }
1004
+ if (ids.has(id)) {
1005
+ throw new Error(`Coverage plan completeness_inventory contains duplicate item id: ${id}`);
1006
+ }
1007
+ ids.add(id);
1008
+
1009
+ const status = normalizeOptionalString(item.status);
1010
+ if (!COVERAGE_PLAN_STATUSES.has(status)) {
1011
+ throw new Error(`Coverage plan completeness_inventory item "${id}" requires status to be accepted, deferred, or missing.`);
1012
+ }
1013
+
1014
+ const coverageAreaIds = normalizeStringArray(item.coverage_area_ids);
1015
+ for (const areaId of coverageAreaIds) {
1016
+ if (!areaById.has(areaId)) {
1017
+ throw new Error(`Coverage plan completeness_inventory item "${id}" references unknown coverage area id: ${areaId}`);
1018
+ }
1019
+ }
1020
+
1021
+ return {
1022
+ id,
1023
+ ...(normalizeOptionalString(item.kind) ? { kind: normalizeOptionalString(item.kind) } : {}),
1024
+ ...(normalizeOptionalString(item.label) ? { label: normalizeOptionalString(item.label) } : {}),
1025
+ status,
1026
+ ...(coverageAreaIds.length > 0 ? { coverage_area_ids: coverageAreaIds } : {}),
1027
+ evidence: normalizeStringArray(item.evidence),
1028
+ blindspots: normalizeStringArray(item.blindspots),
1029
+ };
1030
+ }
1031
+
937
1032
  function normalizeCoverageArea(area, index, ids) {
938
1033
  if (!area || typeof area !== 'object' || Array.isArray(area)) {
939
1034
  throw new Error(`Coverage plan area at index ${index} must be an object.`);
@@ -971,6 +1066,22 @@ function normalizeCoverageArea(area, index, ids) {
971
1066
  }
972
1067
  }
973
1068
 
1069
+ const anchorAction = normalizeOptionalString(area.anchor_action);
1070
+ if (anchorAction && !ANCHOR_ACTIONS.has(anchorAction)) {
1071
+ throw new Error(`Coverage plan area "${id}" has invalid anchor_action: ${anchorAction}`);
1072
+ }
1073
+ const anchorPaths = normalizeStringArray(area.anchor_paths).map(normalizeBlockPath);
1074
+ for (const anchorPath of anchorPaths) {
1075
+ if (
1076
+ !anchorPath.startsWith('architecture/') ||
1077
+ !anchorPath.endsWith('.md') ||
1078
+ path.posix.isAbsolute(anchorPath) ||
1079
+ anchorPath.split('/').includes('..')
1080
+ ) {
1081
+ throw new Error(`Coverage plan area "${id}" has invalid anchor_paths entry: ${anchorPath}`);
1082
+ }
1083
+ }
1084
+
974
1085
  return {
975
1086
  id,
976
1087
  domain: normalizeOptionalString(area.domain),
@@ -982,6 +1093,9 @@ function normalizeCoverageArea(area, index, ids) {
982
1093
  evidence: normalizeStringArray(area.evidence),
983
1094
  blindspots: normalizeStringArray(area.blindspots),
984
1095
  ...(normalizeOptionalString(area.reason) ? { reason: normalizeOptionalString(area.reason) } : {}),
1096
+ ...(anchorAction ? { anchor_action: anchorAction } : {}),
1097
+ ...(normalizeOptionalString(area.anchor_reason) ? { anchor_reason: normalizeOptionalString(area.anchor_reason) } : {}),
1098
+ ...(anchorPaths.length > 0 ? { anchor_paths: anchorPaths } : {}),
985
1099
  };
986
1100
  }
987
1101
 
@@ -1059,6 +1173,8 @@ function createArchitectureDocQualityWarnings(blocks) {
1059
1173
  }
1060
1174
  if (!/^\s*-\s*Evidence:\s+\S/im.test(body)) {
1061
1175
  warnings.push(`missing_evidence_metadata: ${block.path} is missing review metadata "Evidence".`);
1176
+ } else if (/^\s*-\s*Evidence:\s*repo:path references inspected before making implementation claims\s*$/im.test(body)) {
1177
+ warnings.push(`generic_evidence_metadata: ${block.path} uses generic evidence text; list concrete repo:path references instead.`);
1062
1178
  }
1063
1179
  if (!/^\s*-\s*Blindspots:\s+\S/im.test(body)) {
1064
1180
  warnings.push(`missing_blindspots_metadata: ${block.path} is missing review metadata "Blindspots".`);
@@ -1081,6 +1197,51 @@ function createArchitectureDocQualityWarnings(blocks) {
1081
1197
  return warnings;
1082
1198
  }
1083
1199
 
1200
+ function createCoveragePlanQualityWarnings(coverageProjection, anchors) {
1201
+ if (!coverageProjection) {
1202
+ return [];
1203
+ }
1204
+
1205
+ const warnings = [];
1206
+ const hasModernCoverageMetadata = Boolean(coverageProjection.topology || coverageProjection.taxonomy);
1207
+ const hasCompletenessInventory = Array.isArray(coverageProjection.completeness_inventory);
1208
+ if (hasModernCoverageMetadata && !hasCompletenessInventory) {
1209
+ warnings.push('missing_completeness_inventory: coverage plan should enumerate discovered subsystems so deferred or missing areas cannot be silently omitted.');
1210
+ } else if (hasCompletenessInventory) {
1211
+ for (const item of coverageProjection.completeness_inventory) {
1212
+ if (item.status === 'accepted' && (!Array.isArray(item.coverage_area_ids) || item.coverage_area_ids.length === 0)) {
1213
+ warnings.push(`accepted_inventory_without_area: completeness_inventory item "${item.id}" is accepted but does not reference coverage_area_ids.`);
1214
+ }
1215
+ if (item.status !== 'accepted' && (!Array.isArray(item.blindspots) || item.blindspots.length === 0)) {
1216
+ warnings.push(`incomplete_inventory_blindspot: completeness_inventory item "${item.id}" is ${item.status} but does not explain the gap in blindspots.`);
1217
+ }
1218
+ }
1219
+ }
1220
+
1221
+ const hasPriorAnchors =
1222
+ (anchors?.architectureDocs?.length ?? 0) > 0 ||
1223
+ (anchors?.projectMapFeatures?.length ?? 0) > 0 ||
1224
+ Boolean(anchors?.coverage);
1225
+ if (!hasPriorAnchors) {
1226
+ return warnings;
1227
+ }
1228
+
1229
+ for (const area of coverageProjection.areas) {
1230
+ if (!area.anchor_action) {
1231
+ warnings.push(`missing_anchor_action: coverage area "${area.id}" should classify prior docs or project-map identities when re-review anchors exist.`);
1232
+ continue;
1233
+ }
1234
+ if (['split', 'merge', 'replace'].includes(area.anchor_action) && !area.anchor_reason) {
1235
+ warnings.push(`missing_anchor_reason: coverage area "${area.id}" uses anchor_action "${area.anchor_action}" without an evidence-backed anchor_reason.`);
1236
+ }
1237
+ if (area.anchor_action !== 'new' && (!Array.isArray(area.anchor_paths) || area.anchor_paths.length === 0)) {
1238
+ warnings.push(`missing_anchor_paths: coverage area "${area.id}" uses anchor_action "${area.anchor_action}" without anchor_paths.`);
1239
+ }
1240
+ }
1241
+
1242
+ return warnings;
1243
+ }
1244
+
1084
1245
  function extractLevelTwoSections(markdown) {
1085
1246
  return new Set(
1086
1247
  [...String(markdown ?? '').matchAll(/^##\s+(.+)$/gm)]
@@ -1687,6 +1848,191 @@ function renderApplyOutputMessage(options) {
1687
1848
  return `Applied ${parts.length > 0 ? parts.join(', ') : 'accepted docs-review output'} and completed the docs-review marker.`;
1688
1849
  }
1689
1850
 
1851
+ export async function buildDocsReviewAnchorContext(rootDir) {
1852
+ const context = {
1853
+ architectureDocs: [],
1854
+ projectMapFeatures: [],
1855
+ projectMapRelationshipCount: 0,
1856
+ coverage: null,
1857
+ warnings: [],
1858
+ };
1859
+
1860
+ try {
1861
+ const scanned = await scanProjectMemory(rootDir);
1862
+ context.architectureDocs = scanned.documents
1863
+ .filter((document) => document.kind === 'architecture' && document.path !== PROJECT_MAP_PATH && document.extracted)
1864
+ .map((document) => ({
1865
+ path: document.path,
1866
+ domain: document.extracted.domain,
1867
+ feature: document.extracted.feature,
1868
+ component: document.extracted.component,
1869
+ status: document.extracted.status,
1870
+ repos: document.extracted.repo_ids ?? [],
1871
+ }))
1872
+ .sort((left, right) => left.path.localeCompare(right.path));
1873
+
1874
+ if (scanned.errors.length > 0) {
1875
+ context.warnings.push(`${scanned.errors.length} project-memory parse error(s) found while building anchors; inspect status before trusting prior docs.`);
1876
+ }
1877
+ } catch (error) {
1878
+ context.warnings.push(`Could not scan existing project memory for anchors: ${error instanceof Error ? error.message : String(error)}`);
1879
+ }
1880
+
1881
+ try {
1882
+ const projectMap = await readProjectMapAnchor(rootDir);
1883
+ context.projectMapFeatures = projectMap.features;
1884
+ context.projectMapRelationshipCount = projectMap.relationshipCount;
1885
+ } catch (error) {
1886
+ context.warnings.push(`Could not read existing project-map anchors: ${error instanceof Error ? error.message : String(error)}`);
1887
+ }
1888
+
1889
+ try {
1890
+ context.coverage = await readCoverageAnchor(rootDir);
1891
+ } catch (error) {
1892
+ context.warnings.push(`Could not read docs-review coverage anchors: ${error instanceof Error ? error.message : String(error)}`);
1893
+ }
1894
+
1895
+ return context;
1896
+ }
1897
+
1898
+ async function readProjectMapAnchor(rootDir) {
1899
+ const projectShapePath = path.join(rootDir, PROJECT_MAP_PATH);
1900
+ let content;
1901
+ try {
1902
+ content = await readFile(projectShapePath, 'utf8');
1903
+ } catch (error) {
1904
+ if (error?.code === 'ENOENT') {
1905
+ return { features: [], relationshipCount: 0 };
1906
+ }
1907
+ throw error;
1908
+ }
1909
+
1910
+ const match = content.match(/^```vibecompass-project-map\s+version=1\s*\n([\s\S]*?)^```[ \t]*$/m);
1911
+ if (!match) {
1912
+ return { features: [], relationshipCount: 0 };
1913
+ }
1914
+
1915
+ const parsed = JSON.parse(match[1]);
1916
+ const features = Array.isArray(parsed.features)
1917
+ ? parsed.features
1918
+ .map((feature) => ({
1919
+ domain: normalizeOptionalString(feature?.domain),
1920
+ feature: normalizeOptionalString(feature?.feature),
1921
+ isEntryPoint: Boolean(feature?.is_entry_point),
1922
+ }))
1923
+ .filter((feature) => feature.domain && feature.feature)
1924
+ : [];
1925
+
1926
+ return {
1927
+ features,
1928
+ relationshipCount: Array.isArray(parsed.relationships) ? parsed.relationships.length : 0,
1929
+ };
1930
+ }
1931
+
1932
+ async function readCoverageAnchor(rootDir) {
1933
+ const coveragePath = path.join(rootDir, 'state', 'docs-review-coverage.json');
1934
+ let content;
1935
+ try {
1936
+ content = await readFile(coveragePath, 'utf8');
1937
+ } catch (error) {
1938
+ if (error?.code === 'ENOENT') {
1939
+ return null;
1940
+ }
1941
+ throw error;
1942
+ }
1943
+
1944
+ const parsed = JSON.parse(content);
1945
+ return {
1946
+ summary: normalizeOptionalString(parsed.summary),
1947
+ areaCount: Number.isInteger(parsed.area_count) ? parsed.area_count : null,
1948
+ coverageScore: typeof parsed.coverage_score === 'number' ? parsed.coverage_score : null,
1949
+ statuses: parsed.statuses && typeof parsed.statuses === 'object' && !Array.isArray(parsed.statuses)
1950
+ ? parsed.statuses
1951
+ : null,
1952
+ areas: Array.isArray(parsed.areas)
1953
+ ? parsed.areas
1954
+ .map((area) => ({
1955
+ id: normalizeOptionalString(area?.id),
1956
+ path: normalizeOptionalString(area?.proposed_path),
1957
+ status: normalizeOptionalString(area?.status),
1958
+ coverage: normalizeOptionalString(area?.coverage),
1959
+ }))
1960
+ .filter((area) => area.id)
1961
+ : [],
1962
+ };
1963
+ }
1964
+
1965
+ function renderAnchorContext(anchors) {
1966
+ const lines = [
1967
+ '## Re-review Anchors',
1968
+ 'Use these anchors to prevent taxonomy churn. Reuse prior slugs and doc identities when current evidence still supports them.',
1969
+ 'If you propose a taxonomy change, explain the evidence and classify the prior doc/slug action before writing prose.',
1970
+ '',
1971
+ 'Required prior-doc classifications for Stage 2:',
1972
+ '- `reuse`: keep path/frontmatter identity and update only if needed',
1973
+ '- `update`: keep path/frontmatter identity but refresh content or coverage',
1974
+ '- `split`: replace one broad prior doc with multiple focused docs',
1975
+ '- `merge`: combine overlapping prior docs',
1976
+ '- `defer`: leave prior area documented but out of the accepted generation scope',
1977
+ '- `replace`: rename or move only when evidence strongly justifies it',
1978
+ '- `new`: add a new area not represented by the prior tree',
1979
+ '',
1980
+ ];
1981
+
1982
+ if (anchors?.warnings?.length > 0) {
1983
+ lines.push('Anchor warnings:');
1984
+ for (const warning of anchors.warnings) {
1985
+ lines.push(`- ${warning}`);
1986
+ }
1987
+ lines.push('');
1988
+ }
1989
+
1990
+ if (anchors?.architectureDocs?.length > 0) {
1991
+ lines.push(`Existing architecture docs (${anchors.architectureDocs.length}):`);
1992
+ for (const doc of anchors.architectureDocs.slice(0, 40)) {
1993
+ const identity = [doc.domain, doc.feature, doc.component].filter(Boolean).join(' / ');
1994
+ const repos = doc.repos.length > 0 ? ` repos=${doc.repos.join(',')}` : '';
1995
+ lines.push(`- ${doc.path} -> ${identity || 'unmapped'}; status=${doc.status ?? 'unknown'}${repos}`);
1996
+ }
1997
+ if (anchors.architectureDocs.length > 40) {
1998
+ lines.push(`- ... ${anchors.architectureDocs.length - 40} additional architecture docs omitted from prompt anchor summary; still classify broad prior taxonomy before changing it.`);
1999
+ }
2000
+ } else {
2001
+ lines.push('Existing architecture docs: none beyond the starter overview or none readable.');
2002
+ }
2003
+ lines.push('');
2004
+
2005
+ if (anchors?.projectMapFeatures?.length > 0) {
2006
+ lines.push(`Existing project-map feature identities (${anchors.projectMapFeatures.length}; relationships=${anchors.projectMapRelationshipCount}):`);
2007
+ for (const feature of anchors.projectMapFeatures.slice(0, 40)) {
2008
+ lines.push(`- ${feature.domain} / ${feature.feature}${feature.isEntryPoint ? ' (entry point)' : ''}`);
2009
+ }
2010
+ if (anchors.projectMapFeatures.length > 40) {
2011
+ lines.push(`- ... ${anchors.projectMapFeatures.length - 40} additional project-map features omitted from prompt anchor summary.`);
2012
+ }
2013
+ } else {
2014
+ lines.push('Existing project-map feature identities: none found.');
2015
+ }
2016
+ lines.push('');
2017
+
2018
+ if (anchors?.coverage) {
2019
+ const score = typeof anchors.coverage.coverageScore === 'number'
2020
+ ? `${Math.round(anchors.coverage.coverageScore * 100)}% accepted`
2021
+ : 'unscored';
2022
+ lines.push(`Existing coverage projection: ${anchors.coverage.areaCount ?? anchors.coverage.areas.length} areas, ${score}.`);
2023
+ if (anchors.coverage.summary) {
2024
+ lines.push(`Coverage summary: ${anchors.coverage.summary}`);
2025
+ }
2026
+ for (const area of anchors.coverage.areas.slice(0, 20)) {
2027
+ lines.push(`- ${area.id}: ${area.status || 'unknown'} / ${area.coverage || 'unknown'}${area.path ? ` -> ${area.path}` : ''}`);
2028
+ }
2029
+ } else {
2030
+ lines.push('Existing coverage projection: none found.');
2031
+ }
2032
+
2033
+ return lines.join('\n');
2034
+ }
2035
+
1690
2036
  function renderReviewPrompt(options) {
1691
2037
  const outputPath = path.join(options.rootDir, 'state', 'docs-review-output.md');
1692
2038
  const applyCommand = `vibecompass docs-review --root ${shellQuote(options.rootDir)} --apply-output --output ${shellQuote(outputPath)}`;
@@ -1715,6 +2061,18 @@ function renderReviewPrompt(options) {
1715
2061
  const stage4WriteLine = isSingleTurn
1716
2062
  ? `- The package will save the model response to \`${outputPath}\`; local users or hosted proposal reviewers decide what becomes canonical later.`
1717
2063
  : `- After the user accepts Stage 2, write the complete accepted fenced output verbatim to \`${outputPath}\`.`;
2064
+ const applyCommandLabel = isSingleTurn ? 'Apply command after acceptance' : 'Apply command after user acceptance';
2065
+ const stage4ApplyLines = isSingleTurn
2066
+ ? [
2067
+ '- Do not claim docs-review is applied in this response; generated output is proposal material until local apply or hosted proposal acceptance succeeds.',
2068
+ `- Local apply command after acceptance: \`${applyCommand}\`.`,
2069
+ `- If \`vibecompass\` is not installed on PATH, the local user can run \`${npxApplyCommand}\` instead.`,
2070
+ ]
2071
+ : [
2072
+ `- Then run \`${applyCommand}\` to validate and apply the accepted blocks.`,
2073
+ `- If \`vibecompass\` is not installed on PATH, run \`${npxApplyCommand}\` instead.`,
2074
+ '- Only report docs-review as applied after the apply command succeeds. Surface any parser warnings, especially oversized docs, so the user can decide whether to compact or accept the output.',
2075
+ ];
1718
2076
  const finalOutputLine = isSingleTurn
1719
2077
  ? `Only include accepted proposal-material blocks for coverage, architecture, and decision recommendations. The package or hosted worker saves the response to \`${outputPath}\`; report that generated output still requires local apply or hosted proposal acceptance before it is canonical.`
1720
2078
  : `Only include accepted coverage, architecture, and decision-recommendation blocks. After user acceptance, save the final accepted output to \`${outputPath}\`, run \`${applyCommand}\` or \`${npxApplyCommand}\`, and report applied paths plus parser warnings.`;
@@ -1735,7 +2093,7 @@ function renderReviewPrompt(options) {
1735
2093
  `- Review model/version to record: ${options.model}`,
1736
2094
  `- Execution mode: ${isSingleTurn ? 'single-turn (emit accepted proposal output now)' : 'interactive (stop at coverage-plan approval gate)'}`,
1737
2095
  `- Accepted output file: ${outputPath}`,
1738
- `- Apply command after user acceptance: ${applyCommand}`,
2096
+ `- ${applyCommandLabel}: ${applyCommand}`,
1739
2097
  `- Apply command if the CLI is not installed on PATH: ${npxApplyCommand}`,
1740
2098
  '',
1741
2099
  '## Required Inputs',
@@ -1744,38 +2102,50 @@ function renderReviewPrompt(options) {
1744
2102
  '3. Read all canonical files under `architecture/`, `decisions/`, and finalized `sessions/`.',
1745
2103
  '4. Inspect declared repositories before making implementation claims.',
1746
2104
  '',
2105
+ renderAnchorContext(options.anchors),
2106
+ '',
1747
2107
  '## Staged Review Protocol',
1748
2108
  'Stage 1 — Evidence inventory:',
1749
2109
  '- Build a compact repo inventory from file paths, manifests/config files, route/job/test directories, and existing architecture frontmatter before reading large file bodies.',
2110
+ '- Identify the project topology before choosing docs: single-repo/monolith, multi-repo, multi-surface, package/CLI, workers/services, mobile app, or mixed. State the topology in the coverage plan.',
2111
+ '- Build a completeness inventory of discovered apps/repos, routes/screens, API endpoints, tables/storage, jobs/cron/tasks, auth/session flows, external integrations, platform surfaces, and product features. Keep it subsystem-level, not a raw file dump.',
1750
2112
  '- Mine finalized session notes for decision candidates and product constraints, but cap proposed decisions to the five highest-value architecture choices.',
1751
2113
  '- Identify candidate domains, features, components, ownership boundaries, user journey entry points, feature-to-feature relationships, and system-layer connections with concrete `repo:path` evidence.',
2114
+ '- Separate product features from runtime surfaces. Web, mobile, backend/API, database/storage, jobs, external integrations, and deployment/runtime are surfaces or systems that participate in features; do not treat them as competing feature categories unless the project itself is a platform/tooling project.',
1752
2115
  '- Do not fetch or summarize broad source bodies until the coverage plan says a file is needed.',
1753
2116
  '',
1754
2117
  'Stage 2 — Coverage plan:',
1755
2118
  '- Propose the smallest useful architecture-doc set that will let future agents understand the project and retrieve targeted context.',
1756
2119
  '- The minimum useful set must cover: user journey, project/system map (frontend/backend/API/data/integration structure and connections), and summaries of the core journey-facing features.',
1757
2120
  '- Prioritize entry points, user-flow/gating relationships, data ownership, external integrations, jobs/async boundaries, auth/security boundaries, and test surfaces.',
2121
+ '- 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.',
2122
+ '- 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.',
2123
+ '- 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.',
2124
+ '- Every `split`, `merge`, or `replace` action requires an evidence-backed anchor reason before prose generation. No unexplained slug rename is allowed.',
2125
+ '- Treat the accepted Stage-2 plan as the taxonomy and feature-list freeze for this docs-review run.',
1758
2126
  '- Mark each proposed doc as `comprehensive`, `partial`, or `initial`, with the reason and blindspots.',
1759
2127
  ...coveragePlanStageLines,
1760
2128
  '',
1761
- 'Stage 3 — Bounded doc generation:',
2129
+ 'Stage 3 — Backbone output and bounded doc generation:',
1762
2130
  '- Generate only accepted docs, one complete fenced block per architecture path.',
1763
- '- Ensure `architecture/overview/project-shape.md` includes a concise system map plus one `vibecompass-project-map version=1` JSON fence that lists journey-facing features and supported relationships.',
2131
+ '- Ensure `architecture/overview/project-shape.md` includes compact backbone sections for topology, core feature inventory, user journey map, project systems map, and coverage/quality summary.',
2132
+ '- Ensure `architecture/overview/project-shape.md` includes exactly one `vibecompass-project-map version=1` JSON fence that lists journey-facing features, supported feature relationships, and minimal optional derived `systems[]` metadata.',
1764
2133
  '- Keep docs concise and retrieval-oriented: explain contracts, flows, invariants, and ownership; do not rewrite source code or produce file-by-file walkthroughs.',
1765
2134
  `- Soft size budget: keep each generated architecture doc under ${ARCHITECTURE_DOC_SOFT_SIZE_LIMIT_BYTES} bytes unless the extra detail is necessary and called out in Blindspots or Retrieval guidance.`,
1766
2135
  '- Include only the most important involved files; prefer representative entry points and owned contracts over exhaustive file lists.',
1767
2136
  '',
1768
2137
  'Stage 4 — Apply and verify:',
1769
2138
  stage4WriteLine,
1770
- `- Then run \`${applyCommand}\` to validate and apply the accepted blocks.`,
1771
- `- If \`vibecompass\` is not installed on PATH, run \`${npxApplyCommand}\` instead.`,
1772
- '- Only report docs-review as applied after the apply command succeeds. Surface any parser warnings, especially oversized docs, so the user can decide whether to compact or accept the output.',
2139
+ ...stage4ApplyLines,
1773
2140
  '',
1774
2141
  '## Architecture Doc Contract',
1775
2142
  'When creating or updating architecture docs, use the existing VibeCompass structure:',
1776
2143
  '- path: `architecture/<domain-slug>/<feature-slug>/<component-slug>.md` for component docs',
1777
2144
  '- frontmatter fields: `domain`, `feature`, `component`, `status`, plus `repo` or `repos` when implementation-scoped',
1778
2145
  '- body sections: `## Description`, `## Review metadata`, `## Details`, `## Retrieval guidance`, `## Next steps`, `## Involved files`',
2146
+ '- overview docs may add compact peer sections for Stage 1 backbone outputs when useful: `## Topology`, `## Core feature inventory`, `## User journey map`, `## Project systems map`, and `## Coverage and quality summary`',
2147
+ '- feature docs may add a compact `## Surface matrix` prose table when multiple surfaces participate. Keep it evidence-sized: include participating surfaces with concrete `repo:path` evidence and explicit N/A rows only when an absence is important to retrieval. Do not emit a new machine-readable surface block.',
2148
+ '- surface/platform ownership docs should stay inside the existing contract, typically as `architecture/platform/<surface>/<component>.md`; do not introduce a top-level `systems/` tree.',
1779
2149
  '',
1780
2150
  'Use this exact Review metadata sub-header in every generated architecture doc:',
1781
2151
  '',
@@ -1785,14 +2155,14 @@ function renderReviewPrompt(options) {
1785
2155
  `- Project mode: ${options.project.mode}`,
1786
2156
  '- Confidence: high | medium | low',
1787
2157
  '- Coverage: comprehensive | partial | initial',
1788
- '- Evidence: repo:path references inspected before making implementation claims',
2158
+ '- Evidence: concrete repo:path references inspected before making implementation claims',
1789
2159
  '- Retrieval scope: when future agents should load this doc',
1790
2160
  `- Token posture: compact unless the doc intentionally exceeds ${ARCHITECTURE_DOC_SOFT_SIZE_LIMIT_BYTES} bytes`,
1791
2161
  '- Blindspots: explicit list, or "None identified" only when evidence is comprehensive',
1792
2162
  '',
1793
2163
  '## Review Rules',
1794
2164
  '1. Do not delete `architecture/overview/project-shape.md`.',
1795
- '2. Add or update `architecture/overview/project-shape.md` so it explains the project/system map and includes the accepted `vibecompass-project-map version=1` block.',
2165
+ '2. Add or update `architecture/overview/project-shape.md` so it explains the topology, core feature inventory, user journey map, project systems map, coverage/quality summary, and accepted `vibecompass-project-map version=1` block.',
1796
2166
  '3. Add evidence-backed component docs alongside the overview for core journey-facing features and important supporting systems.',
1797
2167
  '4. Update `architecture/overview/project-shape.md` coverage and blindspot sections to summarize what has now been mapped.',
1798
2168
  '5. Keep all implementation claims source-backed with `repo:path` evidence.',
@@ -1801,6 +2171,7 @@ function renderReviewPrompt(options) {
1801
2171
  '8. Do not append real `D-NNN` decisions without explicit user acceptance. Propose candidate decisions separately.',
1802
2172
  '9. Do not edit `decisions/INDEX.md` directly unless a canonical decision file was explicitly accepted and appended.',
1803
2173
  '10. Optimize for future retrieval: project-level docs stay compact; component docs carry focused context for a specific area.',
2174
+ '11. Evidence metadata must name concrete files such as `app:src/server.ts`; do not use generic placeholders like "repo:path references inspected before making implementation claims".',
1804
2175
  '',
1805
2176
  '## Project Map Block',
1806
2177
  'Include exactly one project-map block inside `architecture/overview/project-shape.md` when the user accepts journey/system mapping output:',
@@ -1822,19 +2193,36 @@ function renderReviewPrompt(options) {
1822
2193
  ' "kind": "gates | navigates_to | depends_on | writes_to",',
1823
2194
  ' "label": "Short evidence-backed relationship label"',
1824
2195
  ' }',
2196
+ ' ],',
2197
+ ' "systems": [',
2198
+ ' {',
2199
+ ' "id": "stable-system-id",',
2200
+ ' "name": "System or runtime surface name",',
2201
+ ' "kind": "frontend | backend | mobile | worker | cli | package | database | integration | mixed",',
2202
+ ' "repos": ["repo-id"],',
2203
+ ' "summary": "Short evidence-backed system summary",',
2204
+ ' "owns_features": [{ "domain": "Domain", "feature": "Feature" }]',
2205
+ ' }',
1825
2206
  ' ]',
1826
2207
  '}',
1827
2208
  '```',
1828
2209
  '',
1829
2210
  'Only include relationships supported by evidence. Prefer `navigates_to` and `gates` for the main user journey; use `depends_on` and `writes_to` for supporting system relationships. If useful journey links cannot be inferred, state that as a coverage gap rather than inventing links.',
2211
+ '`systems[]` is optional derived overview metadata, not a new canonical taxonomy, relationship schema, or projection source. Include it only when evidence identifies runtime/deployable units or external systems; treat `owns_features` as documentation-only grouping, put user-journey links in `relationships[]`, and record uncertain systems or system-to-system connections as blindspots.',
2212
+ 'Per-feature surface matrices are derived prose. `systems[].owns_features` remains the only machine-readable system-to-feature hint in this release; do not duplicate that graph in another parsed block.',
1830
2213
  '',
1831
2214
  '## Output Contract',
1832
2215
  'First provide a concise findings summary with:',
1833
2216
  '- mapped areas',
2217
+ '- backbone outputs: topology, core feature inventory, user journey map, project systems map, and coverage/quality summary',
1834
2218
  '- user journey and project/system map coverage',
1835
2219
  '- gaps/blindspots',
1836
2220
  '- evidence inventory summary',
2221
+ '- completeness inventory summary with accepted/deferred/missing accounting',
2222
+ '- topology and chosen taxonomy axis',
1837
2223
  '- coverage plan and proposed architecture docs',
2224
+ '- prior-doc anchor classifications and reasons',
2225
+ '- coverage/quality report, including missing/generic evidence, unmapped core features, oversized-doc candidates, and unresolved blindspots',
1838
2226
  '- token-budget risks or oversized-doc candidates',
1839
2227
  '- proposed decisions, if any',
1840
2228
  '',
@@ -1843,6 +2231,22 @@ function renderReviewPrompt(options) {
1843
2231
  '```vibecompass-coverage-plan version=1',
1844
2232
  '{',
1845
2233
  ' "summary": "Short coverage plan summary",',
2234
+ ' "topology": "single-repo | monolith | multi-repo | multi-surface | package-cli | mixed",',
2235
+ ' "taxonomy": {',
2236
+ ' "primary_axis": "domain-first | platform-first | repo-first | other",',
2237
+ ' "rationale": "Why this axis matches the evidence and how it avoids mixed tiers"',
2238
+ ' },',
2239
+ ' "completeness_inventory": [',
2240
+ ' {',
2241
+ ' "id": "stable-subsystem-id",',
2242
+ ' "kind": "feature | surface | route | screen | api | data | storage | job | integration | auth | deployment | other",',
2243
+ ' "label": "Short discovered subsystem label",',
2244
+ ' "status": "accepted | deferred | missing",',
2245
+ ' "coverage_area_ids": ["stable-area-id"],',
2246
+ ' "evidence": ["repo:path"],',
2247
+ ' "blindspots": ["explicit blindspot for deferred or missing items"]',
2248
+ ' }',
2249
+ ' ],',
1846
2250
  ' "areas": [',
1847
2251
  ' {',
1848
2252
  ' "id": "stable-area-id",',
@@ -1852,6 +2256,9 @@ function renderReviewPrompt(options) {
1852
2256
  ' "status": "accepted | deferred | missing",',
1853
2257
  ' "coverage": "comprehensive | partial | initial | missing",',
1854
2258
  ' "proposed_path": "architecture/domain/feature/component.md",',
2259
+ ' "anchor_action": "reuse | update | split | merge | defer | replace | new",',
2260
+ ' "anchor_paths": ["architecture/existing/domain/component.md"],',
2261
+ ' "anchor_reason": "Evidence-backed reason for changing or reusing the prior doc identity",',
1855
2262
  ' "evidence": ["repo:path"],',
1856
2263
  ' "blindspots": ["explicit blindspot"]',
1857
2264
  ' }',
@@ -110,11 +110,14 @@ Turn starter project memory into evidence-backed architecture docs under \`archi
110
110
  1. Read \`${projectYamlPath}\`, \`${contextPath}\`, existing \`${architecturePath}\`, \`${decisionsPath}\`, and finalized \`${sessionsPath}\` notes.
111
111
  2. Inspect the declared source repos in stages:
112
112
  - inventory paths, manifests, configs, route/job/test directories, and existing architecture frontmatter first
113
+ - identify project topology before choosing taxonomy: single-repo/monolith, multi-repo, multi-surface, package/CLI, workers/services, mobile app, or mixed
114
+ - build a subsystem-level completeness inventory of apps/repos, routes/screens, APIs, tables/storage, jobs/cron/tasks, auth/session flows, external integrations, runtime surfaces, and product features
115
+ - distinguish product features from participating surfaces such as web, mobile, backend/API, data, jobs, integrations, and deployment/runtime
113
116
  - produce a coverage plan before reading broad source bodies
114
117
  - read targeted source files only when the coverage plan needs evidence
115
118
  3. Run \`vibecompass docs-review --guided --llm <tool-name> --model <model-name>\` to record the review marker and print the canonical docs-review contract. Use the current AI tool name and model when known.
116
- 4. Present the evidence inventory, coverage plan, user journey, project/system map, proposed architecture docs, blindspots, token-budget risks, and any decision candidates before generating accepted output. If you stop here for user approval, state clearly that no architecture docs have been applied yet and do not label proposed areas as accepted.
117
- 5. Draft only output the user accepts. After approval, the accepted plan must be fenced as \`vibecompass-coverage-plan version=1\`. Accepted architecture docs must be fenced as \`vibecompass-architecture-doc path=architecture/domain/feature/component.md\` blocks and must include required frontmatter: \`domain\`, \`feature\`, \`component\`, and \`status\`. The accepted \`architecture/overview/project-shape.md\` doc should include exactly one \`vibecompass-project-map version=1\` JSON block with journey-facing features, entry points, and evidence-backed relationships. Accepted local decisions may be fenced as \`vibecompass-decision-recommendation target=decisions/domain.md\` blocks without D-numbers.
119
+ 4. Present the evidence inventory, completeness inventory with accepted/deferred/missing accounting, topology, chosen taxonomy axis, prior-doc reuse/update/split/merge/defer/replace classifications, coverage plan, backbone outputs (core feature inventory, user journey map, project systems map, coverage/quality summary), proposed architecture docs, blindspots, token-budget risks, quality report, and any decision candidates before generating accepted output. If you stop here for user approval, state clearly that no architecture docs have been applied yet and do not label proposed areas as accepted.
120
+ 5. Draft only output the user accepts. After approval, the accepted plan must be fenced as \`vibecompass-coverage-plan version=1\`. Accepted architecture docs must be fenced as \`vibecompass-architecture-doc path=architecture/domain/feature/component.md\` blocks and must include required frontmatter: \`domain\`, \`feature\`, \`component\`, and \`status\`. Feature docs may include compact prose-only surface matrices; surface/platform ownership docs stay under the existing \`architecture/platform/<surface>/<component>.md\` shape rather than a new top-level systems tree. The accepted \`architecture/overview/project-shape.md\` doc should include compact backbone sections and exactly one \`vibecompass-project-map version=1\` JSON block with journey-facing features, entry points, evidence-backed relationships, and minimal optional derived \`systems[]\` metadata. Accepted local decisions may be fenced as \`vibecompass-decision-recommendation target=decisions/domain.md\` blocks without D-numbers.
118
121
  6. Keep each generated architecture doc focused and retrieval-oriented. Soft budget: under 12000 bytes per doc unless the extra detail is necessary and called out in review metadata or retrieval guidance.
119
122
  7. Write the accepted fenced blocks verbatim to \`${outputPath}\`.
120
123
  8. Run \`${applyCommand}\` to validate and apply the accepted blocks. If \`vibecompass\` is not installed on PATH, run \`${npxApplyCommand}\` instead. Only report docs-review as applied after this command succeeds. If accepted docs were written manually outside the output file, run \`vibecompass docs-review --root ${rootRelativePath} --complete\` after confirming the marker should be completed.
@@ -122,7 +125,7 @@ Turn starter project memory into evidence-backed architecture docs under \`archi
122
125
 
123
126
  ## Rebuild option
124
127
 
125
- If the user explicitly wants to rerun docs-review from a clean or narrower architecture-doc slate, preview the scope first with \`vibecompass docs-review --rebuild --path architecture/<scope>\`. Only apply cleanup after the user accepts the preview; use \`vibecompass docs-review --rebuild --apply --stale-policy archive --path architecture/<scope>\` when the user explicitly wants selected stale docs archived before the next review. Rebuild archives selected \`architecture/**/*.md\` docs under \`state/docs-review-archive/\`; it does not mutate decisions, sessions, project.yaml, workflow files, agent instruction files, \`architecture/README.md\`, or \`architecture/overview/project-shape.md\`.
128
+ If the user explicitly wants to rerun docs-review for a focused architecture-doc scope, preview the scope first with \`vibecompass docs-review --rebuild --path architecture/<scope>\`. Only apply cleanup after the user accepts the preview; use \`vibecompass docs-review --rebuild --apply --stale-policy archive --path architecture/<scope>\` when the user explicitly wants selected stale docs archived before the next review. Rebuild archives selected \`architecture/**/*.md\` docs under \`state/docs-review-archive/\`; it does not mutate decisions, sessions, project.yaml, workflow files, agent instruction files, \`architecture/README.md\`, or \`architecture/overview/project-shape.md\`. Rebuild is not permission to cold-regenerate taxonomy: the next docs-review coverage plan must anchor to the prior tree and explain reuse/update/split/merge/defer/replace actions before prose generation.
126
129
 
127
130
  ## Rules
128
131