audit-tools 0.32.17 → 0.32.18

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 (72) hide show
  1. package/dist/audit/decompose/buildStructureDecomposition.d.ts +14 -0
  2. package/dist/audit/decompose/buildStructureDecomposition.d.ts.map +1 -0
  3. package/dist/audit/decompose/buildStructureDecomposition.js +107 -0
  4. package/dist/audit/decompose/buildStructureDecomposition.js.map +1 -0
  5. package/dist/audit/decompose/findings.d.ts +23 -0
  6. package/dist/audit/decompose/findings.d.ts.map +1 -0
  7. package/dist/audit/decompose/findings.js +121 -0
  8. package/dist/audit/decompose/findings.js.map +1 -0
  9. package/dist/audit/decompose/sources.d.ts +21 -0
  10. package/dist/audit/decompose/sources.d.ts.map +1 -0
  11. package/dist/audit/decompose/sources.js +168 -0
  12. package/dist/audit/decompose/sources.js.map +1 -0
  13. package/dist/audit/extractors/commentDecomposition.d.ts +52 -0
  14. package/dist/audit/extractors/commentDecomposition.d.ts.map +1 -0
  15. package/dist/audit/extractors/commentDecomposition.js +298 -0
  16. package/dist/audit/extractors/commentDecomposition.js.map +1 -0
  17. package/dist/audit/extractors/dataStateCoupling.d.ts +26 -0
  18. package/dist/audit/extractors/dataStateCoupling.d.ts.map +1 -0
  19. package/dist/audit/extractors/dataStateCoupling.js +71 -0
  20. package/dist/audit/extractors/dataStateCoupling.js.map +1 -0
  21. package/dist/audit/io/artifacts.d.ts +3 -0
  22. package/dist/audit/io/artifacts.d.ts.map +1 -1
  23. package/dist/audit/io/artifacts.js +1 -0
  24. package/dist/audit/io/artifacts.js.map +1 -1
  25. package/dist/audit/orchestrator/dependencyMap.d.ts +1 -0
  26. package/dist/audit/orchestrator/dependencyMap.d.ts.map +1 -1
  27. package/dist/audit/orchestrator/dependencyMap.js +11 -0
  28. package/dist/audit/orchestrator/dependencyMap.js.map +1 -1
  29. package/dist/audit/orchestrator/executorRunners.d.ts.map +1 -1
  30. package/dist/audit/orchestrator/executorRunners.js +2 -1
  31. package/dist/audit/orchestrator/executorRunners.js.map +1 -1
  32. package/dist/audit/orchestrator/executors.d.ts.map +1 -1
  33. package/dist/audit/orchestrator/executors.js +5 -0
  34. package/dist/audit/orchestrator/executors.js.map +1 -1
  35. package/dist/audit/orchestrator/nextStep.d.ts.map +1 -1
  36. package/dist/audit/orchestrator/nextStep.js +1 -0
  37. package/dist/audit/orchestrator/nextStep.js.map +1 -1
  38. package/dist/audit/orchestrator/state.d.ts.map +1 -1
  39. package/dist/audit/orchestrator/state.js +4 -0
  40. package/dist/audit/orchestrator/state.js.map +1 -1
  41. package/dist/audit/orchestrator/structureExecutors.d.ts +8 -0
  42. package/dist/audit/orchestrator/structureExecutors.d.ts.map +1 -1
  43. package/dist/audit/orchestrator/structureExecutors.js +30 -0
  44. package/dist/audit/orchestrator/structureExecutors.js.map +1 -1
  45. package/dist/audit/orchestrator/synthesisExecutors.d.ts.map +1 -1
  46. package/dist/audit/orchestrator/synthesisExecutors.js +1 -0
  47. package/dist/audit/orchestrator/synthesisExecutors.js.map +1 -1
  48. package/dist/audit/reporting/mergeFindings.d.ts +2 -1
  49. package/dist/audit/reporting/mergeFindings.d.ts.map +1 -1
  50. package/dist/audit/reporting/mergeFindings.js +3 -1
  51. package/dist/audit/reporting/mergeFindings.js.map +1 -1
  52. package/dist/audit/reporting/synthesis.d.ts +2 -0
  53. package/dist/audit/reporting/synthesis.d.ts.map +1 -1
  54. package/dist/audit/reporting/synthesis.js +1 -1
  55. package/dist/audit/reporting/synthesis.js.map +1 -1
  56. package/dist/audit/types/structureDecomposition.d.ts +27 -0
  57. package/dist/audit/types/structureDecomposition.d.ts.map +1 -0
  58. package/dist/audit/types/structureDecomposition.js +2 -0
  59. package/dist/audit/types/structureDecomposition.js.map +1 -0
  60. package/dist/shared/decompose/consensus.d.ts +66 -0
  61. package/dist/shared/decompose/consensus.d.ts.map +1 -0
  62. package/dist/shared/decompose/consensus.js +213 -0
  63. package/dist/shared/decompose/consensus.js.map +1 -0
  64. package/dist/shared/decompose/modularity.d.ts +44 -0
  65. package/dist/shared/decompose/modularity.d.ts.map +1 -0
  66. package/dist/shared/decompose/modularity.js +249 -0
  67. package/dist/shared/decompose/modularity.js.map +1 -0
  68. package/dist/shared/index.d.ts +4 -0
  69. package/dist/shared/index.d.ts.map +1 -1
  70. package/dist/shared/index.js +2 -0
  71. package/dist/shared/index.js.map +1 -1
  72. package/package.json +1 -1
@@ -0,0 +1,14 @@
1
+ import type { FileDisposition, GraphBundle } from "audit-tools/shared";
2
+ import type { RepoManifest } from "../types.js";
3
+ import type { StructureDecomposition } from "../types/structureDecomposition.js";
4
+ export interface BuildStructureDecompositionParams {
5
+ /** Repo root (absolute); when absent, comment/doc extraction is skipped. */
6
+ root?: string;
7
+ repoManifest: RepoManifest;
8
+ disposition: FileDisposition;
9
+ graphBundle: GraphBundle;
10
+ /** Injectable file reader (tests supply a map-backed reader). */
11
+ readFileText?: (absPath: string) => Promise<string | undefined>;
12
+ }
13
+ export declare function buildStructureDecomposition(params: BuildStructureDecompositionParams): Promise<StructureDecomposition>;
14
+ //# sourceMappingURL=buildStructureDecomposition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildStructureDecomposition.d.ts","sourceRoot":"","sources":["../../../src/audit/decompose/buildStructureDecomposition.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,eAAe,EACf,WAAW,EAEZ,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAgBjF,MAAM,WAAW,iCAAiC;IAChD,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,YAAY,CAAC;IAC3B,WAAW,EAAE,eAAe,CAAC;IAC7B,WAAW,EAAE,WAAW,CAAC;IACzB,iEAAiE;IACjE,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CACjE;AAmBD,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,iCAAiC,GACxC,OAAO,CAAC,sBAAsB,CAAC,CA+EjC"}
@@ -0,0 +1,107 @@
1
+ // Top-level Phase B orchestration: gather the structure-layer sources (behavior
2
+ // graphs + the two new intent extractors), run the overlay-and-delta operator,
3
+ // emit the non-co-localization findings, and assemble the
4
+ // structure_decomposition.json artifact. The single async entry point the
5
+ // executor calls; the pure pieces it composes are individually tested.
6
+ import { clustersFromPartitions, decompose } from "audit-tools/shared";
7
+ import { deriveCommentDecomposition, deriveDocGroups, } from "../extractors/commentDecomposition.js";
8
+ import { buildStructureSources } from "./sources.js";
9
+ import { detectNonColocalization } from "./findings.js";
10
+ const DOC_EXTENSION = /\.(md|markdown|adoc|rst|txt)$/i;
11
+ /** Threshold at which an intent source's partitions collapse into boundary groups. */
12
+ const INTENT_BOUNDARY_FRACTION = 0.5;
13
+ function toPosix(path) {
14
+ return path.replace(/\\/g, "/");
15
+ }
16
+ /** Convert a partition into its non-trivial member groups (size ≥ 2). */
17
+ function partitionGroups(partition) {
18
+ const byComm = new Map();
19
+ for (const [node, comm] of partition) {
20
+ const list = byComm.get(comm);
21
+ if (list)
22
+ list.push(node);
23
+ else
24
+ byComm.set(comm, [node]);
25
+ }
26
+ const groups = [];
27
+ for (const members of byComm.values()) {
28
+ if (members.length >= 2) {
29
+ groups.push([...members].sort((a, b) => a.localeCompare(b)));
30
+ }
31
+ }
32
+ return groups;
33
+ }
34
+ export async function buildStructureDecomposition(params) {
35
+ // In-scope code universe = disposition-included files; doc files (prose intent)
36
+ // are the doc_only status plus markdown/asciidoc/rst by extension.
37
+ const universe = [];
38
+ const docFiles = [];
39
+ for (const item of params.disposition.files) {
40
+ const path = toPosix(item.path);
41
+ if (item.status === "included")
42
+ universe.push(path);
43
+ if (item.status === "doc_only" || DOC_EXTENSION.test(path)) {
44
+ if (item.status !== "excluded" && item.status !== "binary") {
45
+ docFiles.push(path);
46
+ }
47
+ }
48
+ }
49
+ const sortedUniverse = [...new Set(universe)].sort((a, b) => a.localeCompare(b));
50
+ const sortedDocs = [...new Set(docFiles)].sort((a, b) => a.localeCompare(b));
51
+ // Async intent extraction (skipped without a root — degrades to empty).
52
+ const commentResult = params.root
53
+ ? await deriveCommentDecomposition({
54
+ root: params.root,
55
+ files: sortedUniverse,
56
+ readFileText: params.readFileText,
57
+ })
58
+ : { edges: [], scannedFiles: 0 };
59
+ const docGroups = params.root
60
+ ? await deriveDocGroups({
61
+ root: params.root,
62
+ docFiles: sortedDocs,
63
+ codeFiles: sortedUniverse,
64
+ readFileText: params.readFileText,
65
+ })
66
+ : [];
67
+ const sources = buildStructureSources({
68
+ universe: sortedUniverse,
69
+ graphBundle: params.graphBundle,
70
+ commentEdges: commentResult.edges,
71
+ docGroups,
72
+ });
73
+ const result = decompose(sources, "structure");
74
+ // Finding inputs: pooled behavior partitions, all intent-declared boundaries,
75
+ // and the stated-purpose subset (docs + comments) for the smeared-purpose side.
76
+ const behaviorPartitions = sources
77
+ .filter((s) => s.family === "behavior")
78
+ .flatMap((s) => s.partitions);
79
+ const intentBoundaries = [];
80
+ const purposeGroups = [];
81
+ for (const source of sources) {
82
+ if (source.family !== "intent")
83
+ continue;
84
+ const groups = source.partitions.length === 1
85
+ ? partitionGroups(source.partitions[0])
86
+ : clustersFromPartitions(source.partitions, INTENT_BOUNDARY_FRACTION);
87
+ intentBoundaries.push(...groups);
88
+ if (source.id === "docs" || source.id === "comments") {
89
+ purposeGroups.push(...groups);
90
+ }
91
+ }
92
+ const findings = detectNonColocalization({
93
+ behaviorPartitions,
94
+ intentBoundaries,
95
+ purposeGroups,
96
+ });
97
+ return {
98
+ generated_at: new Date().toISOString(),
99
+ target: result.target,
100
+ node_universe_size: sortedUniverse.length,
101
+ source_ids: sources.map((s) => s.id),
102
+ consensus: result.consensus,
103
+ contested: result.contested,
104
+ findings,
105
+ };
106
+ }
107
+ //# sourceMappingURL=buildStructureDecomposition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildStructureDecomposition.js","sourceRoot":"","sources":["../../../src/audit/decompose/buildStructureDecomposition.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,+EAA+E;AAC/E,0DAA0D;AAC1D,0EAA0E;AAC1E,uEAAuE;AAOvE,OAAO,EAAE,sBAAsB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAGvE,OAAO,EACL,0BAA0B,EAC1B,eAAe,GAChB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAExD,MAAM,aAAa,GAAG,gCAAgC,CAAC;AACvD,sFAAsF;AACtF,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAErC,SAAS,OAAO,CAAC,IAAY;IAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAClC,CAAC;AAYD,yEAAyE;AACzE,SAAS,eAAe,CAAC,SAAoB;IAC3C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC3C,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,IAAI;YAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;;YACrB,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAChC,CAAC;IACD,MAAM,MAAM,GAAe,EAAE,CAAC;IAC9B,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;QACtC,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YACxB,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,MAAyC;IAEzC,gFAAgF;IAChF,mEAAmE;IACnE,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU;YAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpD,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3D,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;gBAC3D,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,cAAc,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAC1D,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CACnB,CAAC;IACF,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IAE7E,wEAAwE;IACxE,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI;QAC/B,CAAC,CAAC,MAAM,0BAA0B,CAAC;YAC/B,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,KAAK,EAAE,cAAc;YACrB,YAAY,EAAE,MAAM,CAAC,YAAY;SAClC,CAAC;QACJ,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;IACnC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI;QAC3B,CAAC,CAAC,MAAM,eAAe,CAAC;YACpB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,QAAQ,EAAE,UAAU;YACpB,SAAS,EAAE,cAAc;YACzB,YAAY,EAAE,MAAM,CAAC,YAAY;SAClC,CAAC;QACJ,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,OAAO,GAAG,qBAAqB,CAAC;QACpC,QAAQ,EAAE,cAAc;QACxB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,YAAY,EAAE,aAAa,CAAC,KAAK;QACjC,SAAS;KACV,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAE/C,8EAA8E;IAC9E,gFAAgF;IAChF,MAAM,kBAAkB,GAAG,OAAO;SAC/B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC;SACtC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;IAChC,MAAM,gBAAgB,GAAe,EAAE,CAAC;IACxC,MAAM,aAAa,GAAe,EAAE,CAAC;IACrC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ;YAAE,SAAS;QACzC,MAAM,MAAM,GACV,MAAM,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC;YAC5B,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAE,CAAC;YACxC,CAAC,CAAC,sBAAsB,CAAC,MAAM,CAAC,UAAU,EAAE,wBAAwB,CAAC,CAAC;QAC1E,gBAAgB,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;QACjC,IAAI,MAAM,CAAC,EAAE,KAAK,MAAM,IAAI,MAAM,CAAC,EAAE,KAAK,UAAU,EAAE,CAAC;YACrD,aAAa,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,uBAAuB,CAAC;QACvC,kBAAkB;QAClB,gBAAgB;QAChB,aAAa;KACd,CAAC,CAAC;IAEH,OAAO;QACL,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACtC,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,kBAAkB,EAAE,cAAc,CAAC,MAAM;QACzC,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpC,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,QAAQ;KACT,CAAC;AACJ,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { Finding } from "../types.js";
2
+ import type { Partition } from "audit-tools/shared";
3
+ export interface NonColocalizationInput {
4
+ /** Pooled behavior partitions (every resolution of every behavior source). */
5
+ behaviorPartitions: Partition[];
6
+ /** All intent-declared boundaries (directory + docs + comments groups). */
7
+ intentBoundaries: string[][];
8
+ /** Explicit stated-purpose groups (docs + comments only — not directory). */
9
+ purposeGroups: string[][];
10
+ agreementThreshold?: number;
11
+ overlapThreshold?: number;
12
+ /** Minimum behavior-cluster size to report a no-purpose finding. */
13
+ minClusterSize?: number;
14
+ /** Cap on findings emitted per kind (deterministic top-by-size). */
15
+ maxPerKind?: number;
16
+ }
17
+ /**
18
+ * Emit the two non-co-localization finding kinds. Deterministic: clusters/groups
19
+ * are processed largest-first (ties by first member), and ids are a stable
20
+ * instance-scoped SD-### sequence.
21
+ */
22
+ export declare function detectNonColocalization(input: NonColocalizationInput): Finding[];
23
+ //# sourceMappingURL=findings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findings.d.ts","sourceRoot":"","sources":["../../../src/audit/decompose/findings.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AA8BpD,MAAM,WAAW,sBAAsB;IACrC,8EAA8E;IAC9E,kBAAkB,EAAE,SAAS,EAAE,CAAC;IAChC,2EAA2E;IAC3E,gBAAgB,EAAE,MAAM,EAAE,EAAE,CAAC;IAC7B,6EAA6E;IAC7E,aAAa,EAAE,MAAM,EAAE,EAAE,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAOD;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,sBAAsB,GAC5B,OAAO,EAAE,CA+EX"}
@@ -0,0 +1,121 @@
1
+ // The two non-co-localization findings (Phase B; design of record
2
+ // spec/conceptual-design-review-design.md §"The two non-co-localizations are
3
+ // first-class findings"). When structure-consensus (behavior) and the
4
+ // intent-declared boundaries FAIL to coincide:
5
+ // - behavioral cluster with no coherent purpose → accidental complexity / dead
6
+ // subsystem (a coupling cluster no declared boundary owns).
7
+ // - a purpose with no behavioral cluster → a goal smeared across the codebase,
8
+ // never modularized (often the highest-value refactor).
9
+ //
10
+ // Deterministic LEADS, not verdicts (confidence: low): they mark where the
11
+ // behavior graphs and the human-declared boundaries disagree, for the Phase C
12
+ // charter pass to confirm against real charters. blast_radius is left unset
13
+ // (Phase C populates goal-graph linkage).
14
+ import { clustersFromPartitions } from "audit-tools/shared";
15
+ /** Structural boundary-integrity findings carry the architecture lens. */
16
+ const LENS = "architecture";
17
+ function createIdGenerator() {
18
+ let n = 1;
19
+ return () => `SD-${String(n++).padStart(3, "0")}`;
20
+ }
21
+ /** |subset ∩ group| / |subset|. */
22
+ function overlapFraction(subset, group) {
23
+ if (subset.length === 0)
24
+ return 0;
25
+ let hit = 0;
26
+ for (const m of subset)
27
+ if (group.has(m))
28
+ hit += 1;
29
+ return hit / subset.length;
30
+ }
31
+ /** Best fraction of `members` contained in any single group. */
32
+ function bestContainment(members, groups) {
33
+ let best = 0;
34
+ for (const group of groups) {
35
+ const set = new Set(group);
36
+ const frac = overlapFraction(members, set);
37
+ if (frac > best)
38
+ best = frac;
39
+ }
40
+ return best;
41
+ }
42
+ const DEFAULT_AGREEMENT = 0.5;
43
+ const DEFAULT_OVERLAP = 0.6;
44
+ const DEFAULT_MIN_CLUSTER = 3;
45
+ const DEFAULT_MAX_PER_KIND = 20;
46
+ /**
47
+ * Emit the two non-co-localization finding kinds. Deterministic: clusters/groups
48
+ * are processed largest-first (ties by first member), and ids are a stable
49
+ * instance-scoped SD-### sequence.
50
+ */
51
+ export function detectNonColocalization(input) {
52
+ const agreementThreshold = input.agreementThreshold ?? DEFAULT_AGREEMENT;
53
+ const overlapThreshold = input.overlapThreshold ?? DEFAULT_OVERLAP;
54
+ const minClusterSize = input.minClusterSize ?? DEFAULT_MIN_CLUSTER;
55
+ const maxPerKind = input.maxPerKind ?? DEFAULT_MAX_PER_KIND;
56
+ const nextId = createIdGenerator();
57
+ const bySizeThenFirst = (a, b) => b.length - a.length || (a[0] ?? "").localeCompare(b[0] ?? "");
58
+ const findings = [];
59
+ // One clustering pass shared by both detectors: the full set for the
60
+ // purpose-side containment check, the size-filtered set for the no-purpose one.
61
+ const allBehaviorClusters = clustersFromPartitions(input.behaviorPartitions, agreementThreshold);
62
+ // --- Behavioral cluster with no coherent purpose ---
63
+ const behaviorClusters = allBehaviorClusters
64
+ .filter((c) => c.length >= minClusterSize)
65
+ .sort(bySizeThenFirst);
66
+ for (const cluster of behaviorClusters.slice(0, maxPerKind)) {
67
+ const best = bestContainment(cluster, input.intentBoundaries);
68
+ if (best + 1e-12 >= overlapThreshold)
69
+ continue; // aligns to a declared boundary
70
+ findings.push({
71
+ id: nextId(),
72
+ title: `Behavioral cluster spans declared boundaries: ${cluster.length} files`,
73
+ category: "non_colocalization_behavioral",
74
+ severity: "low",
75
+ confidence: "low",
76
+ lens: LENS,
77
+ summary: `These ${cluster.length} files are tightly coupled by behavior (call/import, ` +
78
+ `co-change, and/or shared state) yet no single declared boundary ` +
79
+ `(directory, doc, or comment grouping) contains most of them ` +
80
+ `(best overlap ${(best * 100).toFixed(0)}%). A coupling cluster no ` +
81
+ `declared purpose owns is accidental complexity or a dead subsystem — a ` +
82
+ `lead for the conceptual charter pass to confirm.`,
83
+ affected_files: cluster.map((path) => ({ path })),
84
+ evidence: [
85
+ `Behavioral coupling consensus across ${input.behaviorPartitions.length} resolution levels.`,
86
+ `Best containment in any declared boundary: ${(best * 100).toFixed(0)}% (threshold ${(overlapThreshold * 100).toFixed(0)}%).`,
87
+ ],
88
+ systemic: true,
89
+ });
90
+ }
91
+ // --- A purpose with no behavioral cluster (smeared) ---
92
+ const purposeGroups = [...input.purposeGroups]
93
+ .filter((g) => g.length >= 2)
94
+ .sort(bySizeThenFirst);
95
+ for (const group of purposeGroups.slice(0, maxPerKind)) {
96
+ const best = bestContainment(group, allBehaviorClusters);
97
+ if (best + 1e-12 >= overlapThreshold)
98
+ continue; // the purpose IS a behavior cluster
99
+ findings.push({
100
+ id: nextId(),
101
+ title: `Declared purpose is behaviorally smeared: ${group.length} files`,
102
+ category: "non_colocalization_purpose",
103
+ severity: "low",
104
+ confidence: "low",
105
+ lens: LENS,
106
+ summary: `A doc/comment grouping declares these ${group.length} files a unit, but ` +
107
+ `they do not form a behavioral cluster — no single coupling cluster ` +
108
+ `contains most of them (best overlap ${(best * 100).toFixed(0)}%). A ` +
109
+ `purpose smeared across the codebase and never modularized is often the ` +
110
+ `highest-value refactor — a lead for the conceptual charter pass.`,
111
+ affected_files: group.map((path) => ({ path })),
112
+ evidence: [
113
+ `Declared as one unit by an intent-declared source (doc/comment).`,
114
+ `Best containment in any behavioral cluster: ${(best * 100).toFixed(0)}% (threshold ${(overlapThreshold * 100).toFixed(0)}%).`,
115
+ ],
116
+ systemic: true,
117
+ });
118
+ }
119
+ return findings;
120
+ }
121
+ //# sourceMappingURL=findings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findings.js","sourceRoot":"","sources":["../../../src/audit/decompose/findings.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { DecompositionSource, GraphBundle } from "audit-tools/shared";
2
+ import { type CouplingEdge } from "../extractors/dataStateCoupling.js";
3
+ export interface StructureSourcesInput {
4
+ /** In-scope files (posix, sorted, unique) — the shared node universe. */
5
+ universe: string[];
6
+ graphBundle: GraphBundle;
7
+ /** Comment cross-reference edges from deriveCommentDecomposition. */
8
+ commentEdges: CouplingEdge[];
9
+ /** Doc-declared groups from deriveDocGroups. */
10
+ docGroups: string[][];
11
+ resolutions?: readonly number[];
12
+ /** Directory depths for the intent directory family (default [1,2,3]). */
13
+ directoryDepths?: number[];
14
+ }
15
+ /**
16
+ * Build the structure-layer decomposition sources. Behavior sources with no edges
17
+ * and intent sources with no non-trivial grouping are omitted so an empty source
18
+ * never dilutes the agreement mean.
19
+ */
20
+ export declare function buildStructureSources(input: StructureSourcesInput): DecompositionSource[];
21
+ //# sourceMappingURL=sources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sources.d.ts","sourceRoot":"","sources":["../../../src/audit/decompose/sources.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,mBAAmB,EACnB,WAAW,EAEZ,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAEL,KAAK,YAAY,EAClB,MAAM,oCAAoC,CAAC;AAqF5C,MAAM,WAAW,qBAAqB;IACpC,yEAAyE;IACzE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;IACzB,qEAAqE;IACrE,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,gDAAgD;IAChD,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC;IACtB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,0EAA0E;IAC1E,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAID;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,qBAAqB,GAC3B,mBAAmB,EAAE,CAmFvB"}
@@ -0,0 +1,168 @@
1
+ // Structure-layer source adapter (Phase B): turns the audit graph substrate into
2
+ // the independently-sourced views the overlay-and-delta operator consumes. PURE —
3
+ // the async extraction (comment/doc reads, git co-change) happens in the executor;
4
+ // this module only shapes already-gathered signals into partitions.
5
+ //
6
+ // Behavior-exhibited sources (call/import, git co-change, data/state) each become
7
+ // a WEIGHTED GRAPH clustered across the resolution ladder → a multi-resolution
8
+ // partition family (feeds both agreed-across-source and stable-across-scale).
9
+ // Intent-declared sources (directory depths, docs, comments) each become one or
10
+ // more PARTITIONS over the same file universe (feed agreed-across-source only).
11
+ import { resolutionSweep } from "audit-tools/shared";
12
+ import { allGraphEdges } from "../extractors/graphSignals.js";
13
+ import { GIT_CO_CHANGE_CATEGORY } from "../extractors/gitHistory.js";
14
+ import { deriveDataStateCoupling, } from "../extractors/dataStateCoupling.js";
15
+ function toPosix(path) {
16
+ return path.replace(/\\/g, "/");
17
+ }
18
+ /** Aggregate undirected edges over the universe into a WeightedGraph. */
19
+ function weightedGraph(universe, edges) {
20
+ const inScope = new Set(universe);
21
+ const byPair = new Map();
22
+ for (const edge of edges) {
23
+ const a = toPosix(edge.a);
24
+ const b = toPosix(edge.b);
25
+ if (a === b || !inScope.has(a) || !inScope.has(b))
26
+ continue;
27
+ if (!(edge.weight > 0))
28
+ continue;
29
+ const lo = a.localeCompare(b) <= 0 ? a : b;
30
+ const hi = a.localeCompare(b) <= 0 ? b : a;
31
+ const key = `${lo} ${hi}`;
32
+ byPair.set(key, (byPair.get(key) ?? 0) + edge.weight);
33
+ }
34
+ const aggregated = [];
35
+ for (const [key, weight] of byPair) {
36
+ const idx = key.indexOf(" ");
37
+ aggregated.push({ a: key.slice(0, idx), b: key.slice(idx + 1), weight });
38
+ }
39
+ return { nodes: universe, edges: aggregated };
40
+ }
41
+ /** Directed graph edges → undirected coupling edges (weight = confidence or 1). */
42
+ function edgesFromGraph(raw) {
43
+ if (!Array.isArray(raw))
44
+ return [];
45
+ const out = [];
46
+ for (const edge of raw) {
47
+ if (!edge ||
48
+ typeof edge.from !== "string" ||
49
+ typeof edge.to !== "string") {
50
+ continue;
51
+ }
52
+ const weight = typeof edge.confidence === "number" && edge.confidence > 0
53
+ ? edge.confidence
54
+ : 1;
55
+ out.push({ a: edge.from, b: edge.to, weight });
56
+ }
57
+ return out;
58
+ }
59
+ /** Build a single partition from disjoint member groups; ungrouped → singleton. */
60
+ function partitionFromGroups(universe, groups) {
61
+ const partition = new Map();
62
+ for (const node of universe)
63
+ partition.set(node, node);
64
+ for (const group of groups) {
65
+ const rep = [...group].sort((a, b) => a.localeCompare(b))[0];
66
+ if (!rep)
67
+ continue;
68
+ for (const member of group) {
69
+ const m = toPosix(member);
70
+ if (partition.has(m))
71
+ partition.set(m, rep);
72
+ }
73
+ }
74
+ return partition;
75
+ }
76
+ /** Directory-prefix partition at a given depth (community = first `depth` segs). */
77
+ function directoryPartition(universe, depth) {
78
+ const partition = new Map();
79
+ for (const node of universe) {
80
+ const segs = node.split("/");
81
+ // A file with fewer than depth+1 segments has no dir at this depth → itself.
82
+ const community = segs.length > depth ? segs.slice(0, depth).join("/") : node;
83
+ partition.set(node, community);
84
+ }
85
+ return partition;
86
+ }
87
+ const DEFAULT_DIRECTORY_DEPTHS = [1, 2, 3];
88
+ /**
89
+ * Build the structure-layer decomposition sources. Behavior sources with no edges
90
+ * and intent sources with no non-trivial grouping are omitted so an empty source
91
+ * never dilutes the agreement mean.
92
+ */
93
+ export function buildStructureSources(input) {
94
+ const universe = [...new Set(input.universe.map(toPosix))].sort((a, b) => a.localeCompare(b));
95
+ const sources = [];
96
+ // --- Behavior-exhibited sources ---
97
+ const callImport = weightedGraph(universe, allGraphEdges(input.graphBundle).map((e) => ({
98
+ a: e.from,
99
+ b: e.to,
100
+ weight: typeof e.confidence === "number" && e.confidence > 0 ? e.confidence : 1,
101
+ })));
102
+ if (callImport.edges.length > 0) {
103
+ sources.push({
104
+ id: "call_import",
105
+ family: "behavior",
106
+ partitions: resolutionSweep(callImport, input.resolutions),
107
+ });
108
+ }
109
+ const coChangeRaw = input.graphBundle.graphs[GIT_CO_CHANGE_CATEGORY];
110
+ const coChange = weightedGraph(universe, edgesFromGraph(coChangeRaw));
111
+ if (coChange.edges.length > 0) {
112
+ sources.push({
113
+ id: "co_change",
114
+ family: "behavior",
115
+ partitions: resolutionSweep(coChange, input.resolutions),
116
+ });
117
+ }
118
+ const dataState = weightedGraph(universe, deriveDataStateCoupling(input.graphBundle));
119
+ if (dataState.edges.length > 0) {
120
+ sources.push({
121
+ id: "data_state",
122
+ family: "behavior",
123
+ partitions: resolutionSweep(dataState, input.resolutions),
124
+ });
125
+ }
126
+ // --- Intent-declared sources ---
127
+ const depths = input.directoryDepths ?? DEFAULT_DIRECTORY_DEPTHS;
128
+ const directoryPartitions = depths.map((d) => directoryPartition(universe, d));
129
+ // Include the directory source only when at least one depth actually groups
130
+ // files (a flat repo with all files at the root yields only singletons).
131
+ const directoryGroups = directoryPartitions.some((p) => hasNonTrivialGroup(p));
132
+ if (directoryGroups) {
133
+ sources.push({
134
+ id: "directory",
135
+ family: "intent",
136
+ partitions: directoryPartitions,
137
+ });
138
+ }
139
+ if (input.docGroups.length > 0) {
140
+ sources.push({
141
+ id: "docs",
142
+ family: "intent",
143
+ partitions: [partitionFromGroups(universe, input.docGroups)],
144
+ });
145
+ }
146
+ const commentGraph = weightedGraph(universe, input.commentEdges);
147
+ if (commentGraph.edges.length > 0) {
148
+ // Comment cross-references are a coupling graph; cluster it at the ladder so a
149
+ // comment web forms boundaries the same way behavior graphs do.
150
+ sources.push({
151
+ id: "comments",
152
+ family: "intent",
153
+ partitions: resolutionSweep(commentGraph, input.resolutions),
154
+ });
155
+ }
156
+ return sources;
157
+ }
158
+ function hasNonTrivialGroup(partition) {
159
+ const counts = new Map();
160
+ for (const comm of partition.values()) {
161
+ counts.set(comm, (counts.get(comm) ?? 0) + 1);
162
+ }
163
+ for (const count of counts.values())
164
+ if (count >= 2)
165
+ return true;
166
+ return false;
167
+ }
168
+ //# sourceMappingURL=sources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sources.js","sourceRoot":"","sources":["../../../src/audit/decompose/sources.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import type { CouplingEdge } from "./dataStateCoupling.js";
2
+ /**
3
+ * Extract concatenated comment text from source, honoring the file's comment
4
+ * syntax and skipping string literals (single/double/backtick, backslash-escaped)
5
+ * so a comment marker inside a string is not misread. A single-pass char scanner:
6
+ * normal → string → line-comment → block-comment. Degrades gracefully (an
7
+ * unterminated block just consumes to EOF).
8
+ */
9
+ export declare function extractCommentText(source: string, path: string): string;
10
+ export interface CommentDecompositionResult {
11
+ /** Intent-declared coupling edges from comment cross-references (undirected). */
12
+ edges: CouplingEdge[];
13
+ /** Number of files whose source was read + scanned. */
14
+ scannedFiles: number;
15
+ }
16
+ export interface CommentDecompositionParams {
17
+ root: string;
18
+ /** In-scope repo-relative file paths. */
19
+ files: string[];
20
+ /**
21
+ * Injectable file reader (absolute path → text | undefined). Defaults to a
22
+ * size-capped node:fs read; tests supply a map-backed reader.
23
+ */
24
+ readFileText?: (absPath: string) => Promise<string | undefined>;
25
+ /** Skip files larger than this many bytes when reading (default 512 KiB). */
26
+ maxBytes?: number;
27
+ }
28
+ /**
29
+ * Derive intent-declared coupling edges from comment cross-references across the
30
+ * in-scope files. For each file, its comment text is scanned for the distinctive
31
+ * reference tokens of OTHER files; a match adds an undirected edge. Deterministic:
32
+ * files are processed in sorted order and edges are canonicalized + sorted.
33
+ */
34
+ export declare function deriveCommentDecomposition(params: CommentDecompositionParams): Promise<CommentDecompositionResult>;
35
+ export interface DocGroupsParams {
36
+ root: string;
37
+ /** Doc files (prose) to mine — README / ADRs / other markdown. */
38
+ docFiles: string[];
39
+ /** In-scope code files a doc may name (the grouping universe). */
40
+ codeFiles: string[];
41
+ readFileText?: (absPath: string) => Promise<string | undefined>;
42
+ maxBytes?: number;
43
+ }
44
+ /**
45
+ * Derive intent-declared groups from docs: each doc's ENTIRE text (docs are
46
+ * prose, not comment-embedded) is scanned for the reference tokens of in-scope
47
+ * code files; every code file a single doc names becomes one group (the doc
48
+ * declares a module). Returns disjoint member groups of size ≥ 2 (a doc naming
49
+ * one file declares no grouping), lexically sorted. Deterministic.
50
+ */
51
+ export declare function deriveDocGroups(params: DocGroupsParams): Promise<string[][]>;
52
+ //# sourceMappingURL=commentDecomposition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commentDecomposition.d.ts","sourceRoot":"","sources":["../../../src/audit/extractors/commentDecomposition.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAiD3D;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAoEvE;AA8CD,MAAM,WAAW,0BAA0B;IACzC,iFAAiF;IACjF,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,uDAAuD;IACvD,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAChE,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAiBD;;;;;GAKG;AACH,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,0BAA0B,GACjC,OAAO,CAAC,0BAA0B,CAAC,CA0DrC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,kEAAkE;IAClE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CA0CrB"}