audit-tools 0.41.1 → 0.42.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/dist/audit/cli/args.d.ts +0 -1
- package/dist/audit/cli/args.d.ts.map +1 -1
- package/dist/audit/cli/args.js +0 -7
- package/dist/audit/cli/args.js.map +1 -1
- package/dist/audit/cli/auditStep.d.ts +18 -0
- package/dist/audit/cli/auditStep.d.ts.map +1 -1
- package/dist/audit/cli/auditStep.js +35 -1
- package/dist/audit/cli/auditStep.js.map +1 -1
- package/dist/audit/cli/cleanup.d.ts +1 -0
- package/dist/audit/cli/cleanup.d.ts.map +1 -1
- package/dist/audit/cli/cleanup.js +22 -7
- package/dist/audit/cli/cleanup.js.map +1 -1
- package/dist/audit/cli/confirmIntentStep.d.ts.map +1 -1
- package/dist/audit/cli/confirmIntentStep.js +11 -0
- package/dist/audit/cli/confirmIntentStep.js.map +1 -1
- package/dist/audit/cli/dispatch/hostHandoff.d.ts.map +1 -1
- package/dist/audit/cli/dispatch/hostHandoff.js +19 -1
- package/dist/audit/cli/dispatch/hostHandoff.js.map +1 -1
- package/dist/audit/cli/envelope.d.ts +0 -24
- package/dist/audit/cli/envelope.d.ts.map +1 -1
- package/dist/audit/cli/envelope.js +0 -18
- package/dist/audit/cli/envelope.js.map +1 -1
- package/dist/audit/cli/fanoutLanes.d.ts +24 -0
- package/dist/audit/cli/fanoutLanes.d.ts.map +1 -1
- package/dist/audit/cli/fanoutLanes.js +46 -2
- package/dist/audit/cli/fanoutLanes.js.map +1 -1
- package/dist/audit/cli/nextStepCommand.d.ts.map +1 -1
- package/dist/audit/cli/nextStepCommand.js +19 -24
- package/dist/audit/cli/nextStepCommand.js.map +1 -1
- package/dist/audit/cli/nextStepHelpers.d.ts.map +1 -1
- package/dist/audit/cli/nextStepHelpers.js +20 -6
- package/dist/audit/cli/nextStepHelpers.js.map +1 -1
- package/dist/audit/cli/workerResult.d.ts +0 -13
- package/dist/audit/cli/workerResult.d.ts.map +1 -1
- package/dist/audit/cli/workerResult.js +0 -15
- package/dist/audit/cli/workerResult.js.map +1 -1
- package/dist/audit/contracts/workerSchemas.d.ts +13 -83
- package/dist/audit/contracts/workerSchemas.d.ts.map +1 -1
- package/dist/audit/contracts/workerSchemas.js +10 -4
- package/dist/audit/contracts/workerSchemas.js.map +1 -1
- package/dist/audit/orchestrator/advance.d.ts +30 -0
- package/dist/audit/orchestrator/advance.d.ts.map +1 -1
- package/dist/audit/orchestrator/advance.js +45 -2
- package/dist/audit/orchestrator/advance.js.map +1 -1
- package/dist/audit/orchestrator/designReviewPrompt.d.ts.map +1 -1
- package/dist/audit/orchestrator/designReviewPrompt.js +6 -4
- package/dist/audit/orchestrator/designReviewPrompt.js.map +1 -1
- package/dist/audit/orchestrator/intentCheckpointExecutor.d.ts +8 -0
- package/dist/audit/orchestrator/intentCheckpointExecutor.d.ts.map +1 -1
- package/dist/audit/orchestrator/intentCheckpointExecutor.js +2 -0
- package/dist/audit/orchestrator/intentCheckpointExecutor.js.map +1 -1
- package/dist/audit/orchestrator/partitionTaskGraph.d.ts.map +1 -1
- package/dist/audit/orchestrator/partitionTaskGraph.js +2 -2
- package/dist/audit/orchestrator/partitionTaskGraph.js.map +1 -1
- package/dist/audit/orchestrator/reviewPacketSizing.d.ts +0 -1
- package/dist/audit/orchestrator/reviewPacketSizing.d.ts.map +1 -1
- package/dist/audit/orchestrator/reviewPacketSizing.js +0 -5
- package/dist/audit/orchestrator/reviewPacketSizing.js.map +1 -1
- package/dist/audit/orchestrator/reviewPackets.d.ts +8 -2
- package/dist/audit/orchestrator/reviewPackets.d.ts.map +1 -1
- package/dist/audit/orchestrator/reviewPackets.js +7 -8
- package/dist/audit/orchestrator/reviewPackets.js.map +1 -1
- package/dist/audit/orchestrator/taskAffinityGraph.d.ts +0 -6
- package/dist/audit/orchestrator/taskAffinityGraph.d.ts.map +1 -1
- package/dist/audit/orchestrator/taskAffinityGraph.js +0 -16
- package/dist/audit/orchestrator/taskAffinityGraph.js.map +1 -1
- package/dist/audit/orchestrator/taskBuilder.d.ts.map +1 -1
- package/dist/audit/orchestrator/taskBuilder.js +1 -2
- package/dist/audit/orchestrator/taskBuilder.js.map +1 -1
- package/dist/audit/reporting/synthesis.d.ts.map +1 -1
- package/dist/audit/reporting/synthesis.js +2 -3
- package/dist/audit/reporting/synthesis.js.map +1 -1
- package/dist/audit/reporting/workBlocks.d.ts +0 -1
- package/dist/audit/reporting/workBlocks.d.ts.map +1 -1
- package/dist/audit/reporting/workBlocks.js +4 -41
- package/dist/audit/reporting/workBlocks.js.map +1 -1
- package/dist/audit/systemic/systemicChallengeLoop.d.ts +0 -8
- package/dist/audit/systemic/systemicChallengeLoop.d.ts.map +1 -1
- package/dist/audit/systemic/systemicChallengeLoop.js +0 -9
- package/dist/audit/systemic/systemicChallengeLoop.js.map +1 -1
- package/dist/audit/types.d.ts +4 -4
- package/dist/remediate/contractPipeline/artifactStore.d.ts +19 -23
- package/dist/remediate/contractPipeline/artifactStore.d.ts.map +1 -1
- package/dist/remediate/contractPipeline/artifactStore.js +22 -29
- package/dist/remediate/contractPipeline/artifactStore.js.map +1 -1
- package/dist/remediate/contractPipeline/changeClassification.d.ts +0 -12
- package/dist/remediate/contractPipeline/changeClassification.d.ts.map +1 -1
- package/dist/remediate/contractPipeline/changeClassification.js +0 -101
- package/dist/remediate/contractPipeline/changeClassification.js.map +1 -1
- package/dist/remediate/contractPipeline/derive.d.ts +0 -42
- package/dist/remediate/contractPipeline/derive.d.ts.map +1 -1
- package/dist/remediate/contractPipeline/derive.js +0 -108
- package/dist/remediate/contractPipeline/derive.js.map +1 -1
- package/dist/remediate/index.d.ts +0 -1
- package/dist/remediate/index.d.ts.map +1 -1
- package/dist/remediate/index.js +5 -132
- package/dist/remediate/index.js.map +1 -1
- package/dist/remediate/intake.d.ts +0 -55
- package/dist/remediate/intake.d.ts.map +1 -1
- package/dist/remediate/intake.js +1 -81
- package/dist/remediate/intake.js.map +1 -1
- package/dist/remediate/phases/plan.d.ts +0 -1
- package/dist/remediate/phases/plan.d.ts.map +1 -1
- package/dist/remediate/phases/plan.js +1 -207
- package/dist/remediate/phases/plan.js.map +1 -1
- package/dist/remediate/state/types.d.ts +4 -4
- package/dist/remediate/steps/contractPipeline.d.ts.map +1 -1
- package/dist/remediate/steps/contractPipeline.js +15 -12
- package/dist/remediate/steps/contractPipeline.js.map +1 -1
- package/dist/remediate/steps/contractPipelinePrompts.d.ts +9 -9
- package/dist/remediate/steps/contractPipelinePrompts.d.ts.map +1 -1
- package/dist/remediate/steps/contractPipelinePrompts.js +26 -18
- package/dist/remediate/steps/contractPipelinePrompts.js.map +1 -1
- package/dist/remediate/steps/dispatch/hostHandoff.d.ts +0 -1
- package/dist/remediate/steps/dispatch/hostHandoff.d.ts.map +1 -1
- package/dist/remediate/steps/dispatch/hostHandoff.js +3 -2
- package/dist/remediate/steps/dispatch/hostHandoff.js.map +1 -1
- package/dist/remediate/steps/dispatch.d.ts +0 -8
- package/dist/remediate/steps/dispatch.d.ts.map +1 -1
- package/dist/remediate/steps/dispatch.js +3 -9
- package/dist/remediate/steps/dispatch.js.map +1 -1
- package/dist/remediate/steps/nextStep.d.ts +0 -1
- package/dist/remediate/steps/nextStep.d.ts.map +1 -1
- package/dist/remediate/steps/nextStep.js +0 -1
- package/dist/remediate/steps/nextStep.js.map +1 -1
- package/dist/remediate/utils/fileIntegrity.d.ts +0 -8
- package/dist/remediate/utils/fileIntegrity.d.ts.map +1 -1
- package/dist/remediate/utils/fileIntegrity.js +5 -65
- package/dist/remediate/utils/fileIntegrity.js.map +1 -1
- package/dist/remediate/validation/contractPipelineGates.d.ts.map +1 -1
- package/dist/remediate/validation/contractPipelineGates.js +27 -19
- package/dist/remediate/validation/contractPipelineGates.js.map +1 -1
- package/dist/shared/analyzers/candidates.d.ts +6 -6
- package/dist/shared/analyzers/candidates.d.ts.map +1 -1
- package/dist/shared/analyzers/candidates.js +6 -6
- package/dist/shared/analyzers/candidates.js.map +1 -1
- package/dist/shared/chunkByBudget.d.ts +8 -8
- package/dist/shared/decompose/contentCoherence.d.ts +43 -273
- package/dist/shared/decompose/contentCoherence.d.ts.map +1 -1
- package/dist/shared/decompose/contentCoherence.js +120 -63
- package/dist/shared/decompose/contentCoherence.js.map +1 -1
- package/dist/shared/decompose/modularity.d.ts +17 -0
- package/dist/shared/decompose/modularity.d.ts.map +1 -1
- package/dist/shared/decompose/modularity.js +78 -3
- package/dist/shared/decompose/modularity.js.map +1 -1
- package/dist/shared/decompose/systemicChallenge.d.ts +4 -4
- package/dist/shared/decompose/workBlockSeams.d.ts +45 -0
- package/dist/shared/decompose/workBlockSeams.d.ts.map +1 -0
- package/dist/shared/decompose/workBlockSeams.js +82 -0
- package/dist/shared/decompose/workBlockSeams.js.map +1 -0
- package/dist/shared/index.d.ts +6 -5
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +6 -4
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/io/auditToolsPaths.d.ts +0 -20
- package/dist/shared/io/auditToolsPaths.d.ts.map +1 -1
- package/dist/shared/io/auditToolsPaths.js +0 -24
- package/dist/shared/io/auditToolsPaths.js.map +1 -1
- package/dist/shared/loopCorePaths.d.ts.map +1 -1
- package/dist/shared/loopCorePaths.js +15 -9
- package/dist/shared/loopCorePaths.js.map +1 -1
- package/dist/shared/reporting/auditDeliverable.d.ts.map +1 -1
- package/dist/shared/reporting/auditDeliverable.js +8 -3
- package/dist/shared/reporting/auditDeliverable.js.map +1 -1
- package/dist/shared/types/finding.d.ts +119 -317
- package/dist/shared/types/finding.d.ts.map +1 -1
- package/dist/shared/types/finding.js +38 -11
- package/dist/shared/types/finding.js.map +1 -1
- package/dist/shared/validation/findingGrounding.d.ts +11 -0
- package/dist/shared/validation/findingGrounding.d.ts.map +1 -1
- package/dist/shared/validation/findingGrounding.js +21 -0
- package/dist/shared/validation/findingGrounding.js.map +1 -1
- package/dist/shared/validation/findingsReport.d.ts.map +1 -1
- package/dist/shared/validation/findingsReport.js +19 -7
- package/dist/shared/validation/findingsReport.js.map +1 -1
- package/docs/audit-pkg/contracts.md +2 -2
- package/docs/audit-pkg/development.md +1 -1
- package/docs/audit-pkg/release.md +20 -114
- package/package.json +6 -2
- package/schemas/audit_result.schema.json +0 -20
- package/schemas/audit_results.schema.json +0 -20
- package/schemas/finding.schema.json +0 -20
- package/skills/audit-code/SKILL.md +3 -7
- package/skills/audit-code/audit-code.prompt.md +5 -9
- package/skills/remediate-code/SKILL.md +0 -1
- package/dist/remediate/steps/dispatch/common.d.ts +0 -126
- package/dist/remediate/steps/dispatch/common.d.ts.map +0 -1
- package/dist/remediate/steps/dispatch/common.js +0 -204
- package/dist/remediate/steps/dispatch/common.js.map +0 -1
- package/dist/remediate/steps/dispatch/dagNodeFields.d.ts +0 -25
- package/dist/remediate/steps/dispatch/dagNodeFields.d.ts.map +0 -1
- package/dist/remediate/steps/dispatch/dagNodeFields.js +0 -5
- package/dist/remediate/steps/dispatch/dagNodeFields.js.map +0 -1
- package/dist/remediate/steps/dispatch/implementPrompt.d.ts +0 -47
- package/dist/remediate/steps/dispatch/implementPrompt.d.ts.map +0 -1
- package/dist/remediate/steps/dispatch/implementPrompt.js +0 -165
- package/dist/remediate/steps/dispatch/implementPrompt.js.map +0 -1
- package/dist/remediate/steps/dispatch/verifyCommands.d.ts +0 -144
- package/dist/remediate/steps/dispatch/verifyCommands.d.ts.map +0 -1
- package/dist/remediate/steps/dispatch/verifyCommands.js +0 -331
- package/dist/remediate/steps/dispatch/verifyCommands.js.map +0 -1
- package/dist/remediate/steps/dispatch/writeScope.d.ts +0 -43
- package/dist/remediate/steps/dispatch/writeScope.d.ts.map +0 -1
- package/dist/remediate/steps/dispatch/writeScope.js +0 -81
- package/dist/remediate/steps/dispatch/writeScope.js.map +0 -1
- package/dist/remediate/utils/hostAssets.d.ts +0 -21
- package/dist/remediate/utils/hostAssets.d.ts.map +0 -1
- package/dist/remediate/utils/hostAssets.js +0 -34
- package/dist/remediate/utils/hostAssets.js.map +0 -1
|
@@ -15,6 +15,39 @@ export declare const CONTENT_COHERENCE_SCORES: Readonly<{
|
|
|
15
15
|
readonly shared_unit: 80;
|
|
16
16
|
}>;
|
|
17
17
|
export declare const CONTENT_COHERENCE_THRESHOLD = 60;
|
|
18
|
+
/**
|
|
19
|
+
* Which pairs may join at all — the ONE policy axis of the shared core, never a
|
|
20
|
+
* fork. Both draws run the identical scan, union-find, and canonical ordering;
|
|
21
|
+
* they differ only in this predicate and in {@link ContentCoherencePolicy.refineAtModularityPeak}.
|
|
22
|
+
*
|
|
23
|
+
* - `weighted_score_threshold` — a pair joins when its summed evidence weight
|
|
24
|
+
* reaches {@link CONTENT_COHERENCE_THRESHOLD}. Disjunctive: four of the six
|
|
25
|
+
* classes clear 60 alone.
|
|
26
|
+
* - `shared_file_and_same_lens` — a pair joins only when it shares a FILE **and**
|
|
27
|
+
* a lens. Measured on the promoted 2026-08-18 run (3,230 findings): the
|
|
28
|
+
* threshold rule left 99.97% of findings in a single component, and no
|
|
29
|
+
* class-COUNT variant fixed it, because `shared_unit`/`same_lens`/`same_directory`
|
|
30
|
+
* are near-vacuous partitions at audit scale. The conjunction bounds by
|
|
31
|
+
* STRUCTURE, not density — `same_lens` is a hard partition no edge may cross.
|
|
32
|
+
*/
|
|
33
|
+
export type ContentCoherenceEligibility = "weighted_score_threshold" | "shared_file_and_same_lens";
|
|
34
|
+
/** The per-draw policy. Every difference between the draws lands here. */
|
|
35
|
+
export interface ContentCoherencePolicy {
|
|
36
|
+
readonly eligibility: ContentCoherenceEligibility;
|
|
37
|
+
/**
|
|
38
|
+
* Refine each eligible-edge component at its modularity peak (below), so a
|
|
39
|
+
* loose component splits at its own data-derived thin seam while a true clique
|
|
40
|
+
* survives whole. No budget, ceiling, or size target participates.
|
|
41
|
+
*/
|
|
42
|
+
readonly refineAtModularityPeak: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The audit TASK draw. Unmeasured for collapse — its eligibility is deliberately
|
|
46
|
+
* left as-is until it is measured on its own lap (`docs/backlog/open-bugs.md`).
|
|
47
|
+
*/
|
|
48
|
+
export declare const TASK_DRAW_COHERENCE_POLICY: ContentCoherencePolicy;
|
|
49
|
+
/** The FINDINGS draw (work blocks + the findings deliverable). Owner cut, 2026-08-19. */
|
|
50
|
+
export declare const FINDINGS_DRAW_COHERENCE_POLICY: ContentCoherencePolicy;
|
|
18
51
|
export type ContentCoherenceEvidence = {
|
|
19
52
|
-readonly [Kind in keyof typeof CONTENT_COHERENCE_SCORES]: boolean;
|
|
20
53
|
};
|
|
@@ -43,52 +76,10 @@ export interface NormalizedContentCoherenceItem {
|
|
|
43
76
|
tags: string[];
|
|
44
77
|
critical_flow_ids?: string[];
|
|
45
78
|
}
|
|
46
|
-
export interface ContentCoherencePairScore {
|
|
47
|
-
left: string;
|
|
48
|
-
right: string;
|
|
49
|
-
evidence: ContentCoherenceEvidence;
|
|
50
|
-
score: number;
|
|
51
|
-
eligible: boolean;
|
|
52
|
-
}
|
|
53
|
-
export interface ContentCoherenceCandidate {
|
|
54
|
-
left: string;
|
|
55
|
-
right: string;
|
|
56
|
-
score: number;
|
|
57
|
-
}
|
|
58
|
-
export interface ContentCoherenceMergeTraceEntry extends ContentCoherenceCandidate {
|
|
59
|
-
decision: "merge" | "already_connected";
|
|
60
|
-
root: string;
|
|
61
|
-
}
|
|
62
79
|
export interface ContentCoherenceTrace {
|
|
63
80
|
normalized_items: NormalizedContentCoherenceItem[];
|
|
64
|
-
pair_scores: ContentCoherencePairScore[];
|
|
65
|
-
eligible_candidates: ContentCoherenceCandidate[];
|
|
66
|
-
merge_trace: ContentCoherenceMergeTraceEntry[];
|
|
67
|
-
merge_decisions: Array<ContentCoherenceMergeTraceEntry["decision"]>;
|
|
68
81
|
components: string[][];
|
|
69
82
|
}
|
|
70
|
-
export declare const ContentCoherenceEvidenceSchema: z.ZodObject<{
|
|
71
|
-
call_import_reference_adjacency: z.ZodBoolean;
|
|
72
|
-
same_directory: z.ZodBoolean;
|
|
73
|
-
shared_critical_flow: z.ZodBoolean;
|
|
74
|
-
shared_file: z.ZodBoolean;
|
|
75
|
-
shared_semantic_tag_or_same_lens: z.ZodBoolean;
|
|
76
|
-
shared_unit: z.ZodBoolean;
|
|
77
|
-
}, "strict", z.ZodTypeAny, {
|
|
78
|
-
call_import_reference_adjacency: boolean;
|
|
79
|
-
same_directory: boolean;
|
|
80
|
-
shared_critical_flow: boolean;
|
|
81
|
-
shared_file: boolean;
|
|
82
|
-
shared_semantic_tag_or_same_lens: boolean;
|
|
83
|
-
shared_unit: boolean;
|
|
84
|
-
}, {
|
|
85
|
-
call_import_reference_adjacency: boolean;
|
|
86
|
-
same_directory: boolean;
|
|
87
|
-
shared_critical_flow: boolean;
|
|
88
|
-
shared_file: boolean;
|
|
89
|
-
shared_semantic_tag_or_same_lens: boolean;
|
|
90
|
-
shared_unit: boolean;
|
|
91
|
-
}>;
|
|
92
83
|
export declare const NormalizedContentCoherenceItemSchema: z.ZodObject<{
|
|
93
84
|
id: z.ZodString;
|
|
94
85
|
file_paths: z.ZodArray<z.ZodString, "many">;
|
|
@@ -108,93 +99,6 @@ export declare const NormalizedContentCoherenceItemSchema: z.ZodObject<{
|
|
|
108
99
|
tags: string[];
|
|
109
100
|
critical_flow_ids?: string[] | undefined;
|
|
110
101
|
}>;
|
|
111
|
-
export declare const ContentCoherencePairScoreSchema: z.ZodObject<{
|
|
112
|
-
left: z.ZodString;
|
|
113
|
-
right: z.ZodString;
|
|
114
|
-
evidence: z.ZodObject<{
|
|
115
|
-
call_import_reference_adjacency: z.ZodBoolean;
|
|
116
|
-
same_directory: z.ZodBoolean;
|
|
117
|
-
shared_critical_flow: z.ZodBoolean;
|
|
118
|
-
shared_file: z.ZodBoolean;
|
|
119
|
-
shared_semantic_tag_or_same_lens: z.ZodBoolean;
|
|
120
|
-
shared_unit: z.ZodBoolean;
|
|
121
|
-
}, "strict", z.ZodTypeAny, {
|
|
122
|
-
call_import_reference_adjacency: boolean;
|
|
123
|
-
same_directory: boolean;
|
|
124
|
-
shared_critical_flow: boolean;
|
|
125
|
-
shared_file: boolean;
|
|
126
|
-
shared_semantic_tag_or_same_lens: boolean;
|
|
127
|
-
shared_unit: boolean;
|
|
128
|
-
}, {
|
|
129
|
-
call_import_reference_adjacency: boolean;
|
|
130
|
-
same_directory: boolean;
|
|
131
|
-
shared_critical_flow: boolean;
|
|
132
|
-
shared_file: boolean;
|
|
133
|
-
shared_semantic_tag_or_same_lens: boolean;
|
|
134
|
-
shared_unit: boolean;
|
|
135
|
-
}>;
|
|
136
|
-
score: z.ZodNumber;
|
|
137
|
-
eligible: z.ZodBoolean;
|
|
138
|
-
}, "strict", z.ZodTypeAny, {
|
|
139
|
-
left: string;
|
|
140
|
-
right: string;
|
|
141
|
-
evidence: {
|
|
142
|
-
call_import_reference_adjacency: boolean;
|
|
143
|
-
same_directory: boolean;
|
|
144
|
-
shared_critical_flow: boolean;
|
|
145
|
-
shared_file: boolean;
|
|
146
|
-
shared_semantic_tag_or_same_lens: boolean;
|
|
147
|
-
shared_unit: boolean;
|
|
148
|
-
};
|
|
149
|
-
score: number;
|
|
150
|
-
eligible: boolean;
|
|
151
|
-
}, {
|
|
152
|
-
left: string;
|
|
153
|
-
right: string;
|
|
154
|
-
evidence: {
|
|
155
|
-
call_import_reference_adjacency: boolean;
|
|
156
|
-
same_directory: boolean;
|
|
157
|
-
shared_critical_flow: boolean;
|
|
158
|
-
shared_file: boolean;
|
|
159
|
-
shared_semantic_tag_or_same_lens: boolean;
|
|
160
|
-
shared_unit: boolean;
|
|
161
|
-
};
|
|
162
|
-
score: number;
|
|
163
|
-
eligible: boolean;
|
|
164
|
-
}>;
|
|
165
|
-
export declare const ContentCoherenceCandidateSchema: z.ZodObject<{
|
|
166
|
-
left: z.ZodString;
|
|
167
|
-
right: z.ZodString;
|
|
168
|
-
score: z.ZodNumber;
|
|
169
|
-
}, "strict", z.ZodTypeAny, {
|
|
170
|
-
left: string;
|
|
171
|
-
right: string;
|
|
172
|
-
score: number;
|
|
173
|
-
}, {
|
|
174
|
-
left: string;
|
|
175
|
-
right: string;
|
|
176
|
-
score: number;
|
|
177
|
-
}>;
|
|
178
|
-
export declare const ContentCoherenceMergeTraceEntrySchema: z.ZodObject<{
|
|
179
|
-
left: z.ZodString;
|
|
180
|
-
right: z.ZodString;
|
|
181
|
-
score: z.ZodNumber;
|
|
182
|
-
} & {
|
|
183
|
-
decision: z.ZodEnum<["merge", "already_connected"]>;
|
|
184
|
-
root: z.ZodString;
|
|
185
|
-
}, "strict", z.ZodTypeAny, {
|
|
186
|
-
decision: "merge" | "already_connected";
|
|
187
|
-
left: string;
|
|
188
|
-
right: string;
|
|
189
|
-
score: number;
|
|
190
|
-
root: string;
|
|
191
|
-
}, {
|
|
192
|
-
decision: "merge" | "already_connected";
|
|
193
|
-
left: string;
|
|
194
|
-
right: string;
|
|
195
|
-
score: number;
|
|
196
|
-
root: string;
|
|
197
|
-
}>;
|
|
198
102
|
export declare const ContentCoherenceTraceSchema: z.ZodObject<{
|
|
199
103
|
normalized_items: z.ZodArray<z.ZodObject<{
|
|
200
104
|
id: z.ZodString;
|
|
@@ -215,94 +119,6 @@ export declare const ContentCoherenceTraceSchema: z.ZodObject<{
|
|
|
215
119
|
tags: string[];
|
|
216
120
|
critical_flow_ids?: string[] | undefined;
|
|
217
121
|
}>, "many">;
|
|
218
|
-
pair_scores: z.ZodArray<z.ZodObject<{
|
|
219
|
-
left: z.ZodString;
|
|
220
|
-
right: z.ZodString;
|
|
221
|
-
evidence: z.ZodObject<{
|
|
222
|
-
call_import_reference_adjacency: z.ZodBoolean;
|
|
223
|
-
same_directory: z.ZodBoolean;
|
|
224
|
-
shared_critical_flow: z.ZodBoolean;
|
|
225
|
-
shared_file: z.ZodBoolean;
|
|
226
|
-
shared_semantic_tag_or_same_lens: z.ZodBoolean;
|
|
227
|
-
shared_unit: z.ZodBoolean;
|
|
228
|
-
}, "strict", z.ZodTypeAny, {
|
|
229
|
-
call_import_reference_adjacency: boolean;
|
|
230
|
-
same_directory: boolean;
|
|
231
|
-
shared_critical_flow: boolean;
|
|
232
|
-
shared_file: boolean;
|
|
233
|
-
shared_semantic_tag_or_same_lens: boolean;
|
|
234
|
-
shared_unit: boolean;
|
|
235
|
-
}, {
|
|
236
|
-
call_import_reference_adjacency: boolean;
|
|
237
|
-
same_directory: boolean;
|
|
238
|
-
shared_critical_flow: boolean;
|
|
239
|
-
shared_file: boolean;
|
|
240
|
-
shared_semantic_tag_or_same_lens: boolean;
|
|
241
|
-
shared_unit: boolean;
|
|
242
|
-
}>;
|
|
243
|
-
score: z.ZodNumber;
|
|
244
|
-
eligible: z.ZodBoolean;
|
|
245
|
-
}, "strict", z.ZodTypeAny, {
|
|
246
|
-
left: string;
|
|
247
|
-
right: string;
|
|
248
|
-
evidence: {
|
|
249
|
-
call_import_reference_adjacency: boolean;
|
|
250
|
-
same_directory: boolean;
|
|
251
|
-
shared_critical_flow: boolean;
|
|
252
|
-
shared_file: boolean;
|
|
253
|
-
shared_semantic_tag_or_same_lens: boolean;
|
|
254
|
-
shared_unit: boolean;
|
|
255
|
-
};
|
|
256
|
-
score: number;
|
|
257
|
-
eligible: boolean;
|
|
258
|
-
}, {
|
|
259
|
-
left: string;
|
|
260
|
-
right: string;
|
|
261
|
-
evidence: {
|
|
262
|
-
call_import_reference_adjacency: boolean;
|
|
263
|
-
same_directory: boolean;
|
|
264
|
-
shared_critical_flow: boolean;
|
|
265
|
-
shared_file: boolean;
|
|
266
|
-
shared_semantic_tag_or_same_lens: boolean;
|
|
267
|
-
shared_unit: boolean;
|
|
268
|
-
};
|
|
269
|
-
score: number;
|
|
270
|
-
eligible: boolean;
|
|
271
|
-
}>, "many">;
|
|
272
|
-
eligible_candidates: z.ZodArray<z.ZodObject<{
|
|
273
|
-
left: z.ZodString;
|
|
274
|
-
right: z.ZodString;
|
|
275
|
-
score: z.ZodNumber;
|
|
276
|
-
}, "strict", z.ZodTypeAny, {
|
|
277
|
-
left: string;
|
|
278
|
-
right: string;
|
|
279
|
-
score: number;
|
|
280
|
-
}, {
|
|
281
|
-
left: string;
|
|
282
|
-
right: string;
|
|
283
|
-
score: number;
|
|
284
|
-
}>, "many">;
|
|
285
|
-
merge_trace: z.ZodArray<z.ZodObject<{
|
|
286
|
-
left: z.ZodString;
|
|
287
|
-
right: z.ZodString;
|
|
288
|
-
score: z.ZodNumber;
|
|
289
|
-
} & {
|
|
290
|
-
decision: z.ZodEnum<["merge", "already_connected"]>;
|
|
291
|
-
root: z.ZodString;
|
|
292
|
-
}, "strict", z.ZodTypeAny, {
|
|
293
|
-
decision: "merge" | "already_connected";
|
|
294
|
-
left: string;
|
|
295
|
-
right: string;
|
|
296
|
-
score: number;
|
|
297
|
-
root: string;
|
|
298
|
-
}, {
|
|
299
|
-
decision: "merge" | "already_connected";
|
|
300
|
-
left: string;
|
|
301
|
-
right: string;
|
|
302
|
-
score: number;
|
|
303
|
-
root: string;
|
|
304
|
-
}>, "many">;
|
|
305
|
-
merge_decisions: z.ZodArray<z.ZodEnum<["merge", "already_connected"]>, "many">;
|
|
306
122
|
components: z.ZodArray<z.ZodArray<z.ZodString, "many">, "many">;
|
|
307
123
|
}, "strict", z.ZodTypeAny, {
|
|
308
124
|
normalized_items: {
|
|
@@ -312,33 +128,6 @@ export declare const ContentCoherenceTraceSchema: z.ZodObject<{
|
|
|
312
128
|
tags: string[];
|
|
313
129
|
critical_flow_ids?: string[] | undefined;
|
|
314
130
|
}[];
|
|
315
|
-
pair_scores: {
|
|
316
|
-
left: string;
|
|
317
|
-
right: string;
|
|
318
|
-
evidence: {
|
|
319
|
-
call_import_reference_adjacency: boolean;
|
|
320
|
-
same_directory: boolean;
|
|
321
|
-
shared_critical_flow: boolean;
|
|
322
|
-
shared_file: boolean;
|
|
323
|
-
shared_semantic_tag_or_same_lens: boolean;
|
|
324
|
-
shared_unit: boolean;
|
|
325
|
-
};
|
|
326
|
-
score: number;
|
|
327
|
-
eligible: boolean;
|
|
328
|
-
}[];
|
|
329
|
-
eligible_candidates: {
|
|
330
|
-
left: string;
|
|
331
|
-
right: string;
|
|
332
|
-
score: number;
|
|
333
|
-
}[];
|
|
334
|
-
merge_trace: {
|
|
335
|
-
decision: "merge" | "already_connected";
|
|
336
|
-
left: string;
|
|
337
|
-
right: string;
|
|
338
|
-
score: number;
|
|
339
|
-
root: string;
|
|
340
|
-
}[];
|
|
341
|
-
merge_decisions: ("merge" | "already_connected")[];
|
|
342
131
|
components: string[][];
|
|
343
132
|
}, {
|
|
344
133
|
normalized_items: {
|
|
@@ -348,35 +137,16 @@ export declare const ContentCoherenceTraceSchema: z.ZodObject<{
|
|
|
348
137
|
tags: string[];
|
|
349
138
|
critical_flow_ids?: string[] | undefined;
|
|
350
139
|
}[];
|
|
351
|
-
pair_scores: {
|
|
352
|
-
left: string;
|
|
353
|
-
right: string;
|
|
354
|
-
evidence: {
|
|
355
|
-
call_import_reference_adjacency: boolean;
|
|
356
|
-
same_directory: boolean;
|
|
357
|
-
shared_critical_flow: boolean;
|
|
358
|
-
shared_file: boolean;
|
|
359
|
-
shared_semantic_tag_or_same_lens: boolean;
|
|
360
|
-
shared_unit: boolean;
|
|
361
|
-
};
|
|
362
|
-
score: number;
|
|
363
|
-
eligible: boolean;
|
|
364
|
-
}[];
|
|
365
|
-
eligible_candidates: {
|
|
366
|
-
left: string;
|
|
367
|
-
right: string;
|
|
368
|
-
score: number;
|
|
369
|
-
}[];
|
|
370
|
-
merge_trace: {
|
|
371
|
-
decision: "merge" | "already_connected";
|
|
372
|
-
left: string;
|
|
373
|
-
right: string;
|
|
374
|
-
score: number;
|
|
375
|
-
root: string;
|
|
376
|
-
}[];
|
|
377
|
-
merge_decisions: ("merge" | "already_connected")[];
|
|
378
140
|
components: string[][];
|
|
379
141
|
}>;
|
|
380
|
-
/**
|
|
381
|
-
|
|
142
|
+
/**
|
|
143
|
+
* Build the complete canonical trace used by both audit and findings draws.
|
|
144
|
+
*
|
|
145
|
+
* `policy` is REQUIRED and has no default: a draw that forgot to declare its
|
|
146
|
+
* eligibility would otherwise silently inherit the other draw's, which is
|
|
147
|
+
* exactly the class of latent failure this project refuses to leave to a
|
|
148
|
+
* caller's memory. Pass {@link TASK_DRAW_COHERENCE_POLICY} or
|
|
149
|
+
* {@link FINDINGS_DRAW_COHERENCE_POLICY}.
|
|
150
|
+
*/
|
|
151
|
+
export declare function buildContentCoherenceTrace(input: ContentCoherenceInput | Record<string, unknown>, policy: ContentCoherencePolicy): ContentCoherenceTrace;
|
|
382
152
|
//# sourceMappingURL=contentCoherence.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contentCoherence.d.ts","sourceRoot":"","sources":["../../../src/shared/decompose/contentCoherence.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"contentCoherence.d.ts","sourceRoot":"","sources":["../../../src/shared/decompose/contentCoherence.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,wBAAwB;;;;;;;EAO1B,CAAC;AAEZ,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,2BAA2B,GACnC,0BAA0B,GAC1B,2BAA2B,CAAC;AAEhC,0EAA0E;AAC1E,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,WAAW,EAAE,2BAA2B,CAAC;IAClD;;;;OAIG;IACH,QAAQ,CAAC,sBAAsB,EAAE,OAAO,CAAC;CAC1C;AAED;;;GAGG;AACH,eAAO,MAAM,0BAA0B,EAAE,sBAGvC,CAAC;AAEH,yFAAyF;AACzF,eAAO,MAAM,8BAA8B,EAAE,sBAG3C,CAAC;AAiBH,MAAM,MAAM,wBAAwB,GAAG;IACrC,CAAC,UAAU,IAAI,IAAI,MAAM,OAAO,wBAAwB,GAAG,OAAO;CACnE,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACjD;AAED,MAAM,MAAM,gCAAgC,GACxC,aAAa,GACb,sBAAsB,GACtB,WAAW,GACX,eAAe,GACf,WAAW,GACX,WAAW,GACX,UAAU,CAAC;AAEf,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,gCAAgC,GAAG,MAAM,CAAC;CACjD;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,SAAS,oBAAoB,EAAE,CAAC;IACvC,aAAa,CAAC,EAAE,SAAS,4BAA4B,EAAE,CAAC;CACzD;AAED,MAAM,WAAW,8BAA8B;IAC7C,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAoBD,MAAM,WAAW,qBAAqB;IACpC,gBAAgB,EAAE,8BAA8B,EAAE,CAAC;IACnD,UAAU,EAAE,MAAM,EAAE,EAAE,CAAC;CACxB;AAED,eAAO,MAAM,oCAAoC;;;;;;;;;;;;;;;;;;EAQtC,CAAC;AAEZ,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAK7B,CAAC;AA4RZ;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtD,MAAM,EAAE,sBAAsB,GAC7B,qBAAqB,CAkGvB"}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* those components.
|
|
7
7
|
*/
|
|
8
8
|
import { z } from "zod";
|
|
9
|
+
import { louvain, modularityOf } from "./modularity.js";
|
|
9
10
|
export const CONTENT_COHERENCE_SCORES = Object.freeze({
|
|
10
11
|
call_import_reference_adjacency: 70,
|
|
11
12
|
same_directory: 10,
|
|
@@ -15,16 +16,32 @@ export const CONTENT_COHERENCE_SCORES = Object.freeze({
|
|
|
15
16
|
shared_unit: 80,
|
|
16
17
|
});
|
|
17
18
|
export const CONTENT_COHERENCE_THRESHOLD = 60;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
/**
|
|
20
|
+
* The audit TASK draw. Unmeasured for collapse — its eligibility is deliberately
|
|
21
|
+
* left as-is until it is measured on its own lap (`docs/backlog/open-bugs.md`).
|
|
22
|
+
*/
|
|
23
|
+
export const TASK_DRAW_COHERENCE_POLICY = Object.freeze({
|
|
24
|
+
eligibility: "weighted_score_threshold",
|
|
25
|
+
refineAtModularityPeak: false,
|
|
26
|
+
});
|
|
27
|
+
/** The FINDINGS draw (work blocks + the findings deliverable). Owner cut, 2026-08-19. */
|
|
28
|
+
export const FINDINGS_DRAW_COHERENCE_POLICY = Object.freeze({
|
|
29
|
+
eligibility: "shared_file_and_same_lens",
|
|
30
|
+
refineAtModularityPeak: true,
|
|
31
|
+
});
|
|
32
|
+
/**
|
|
33
|
+
* The canonical modularity resolution (γ). NOT a tuned knob and never to become
|
|
34
|
+
* one: γ = 1 is the literature default, at which the trivial whole-component
|
|
35
|
+
* partition scores exactly 0, so "did the split beat keeping the component
|
|
36
|
+
* whole" is answered by the data alone.
|
|
37
|
+
*/
|
|
38
|
+
const MODULARITY_RESOLUTION = 1;
|
|
39
|
+
/**
|
|
40
|
+
* Components at or below this size are handed back unrefined. A triviality
|
|
41
|
+
* guard, not a size budget: modularity over one, two, or three nodes describes
|
|
42
|
+
* noise rather than structure, and nothing downstream reads the number.
|
|
43
|
+
*/
|
|
44
|
+
const TRIVIAL_COMPONENT_SIZE = 3;
|
|
28
45
|
export const NormalizedContentCoherenceItemSchema = z
|
|
29
46
|
.object({
|
|
30
47
|
id: z.string().min(1),
|
|
@@ -34,33 +51,9 @@ export const NormalizedContentCoherenceItemSchema = z
|
|
|
34
51
|
critical_flow_ids: z.array(z.string()).optional(),
|
|
35
52
|
})
|
|
36
53
|
.strict();
|
|
37
|
-
export const ContentCoherencePairScoreSchema = z
|
|
38
|
-
.object({
|
|
39
|
-
left: z.string(),
|
|
40
|
-
right: z.string(),
|
|
41
|
-
evidence: ContentCoherenceEvidenceSchema,
|
|
42
|
-
score: z.number().int(),
|
|
43
|
-
eligible: z.boolean(),
|
|
44
|
-
})
|
|
45
|
-
.strict();
|
|
46
|
-
export const ContentCoherenceCandidateSchema = z
|
|
47
|
-
.object({
|
|
48
|
-
left: z.string(),
|
|
49
|
-
right: z.string(),
|
|
50
|
-
score: z.number().int(),
|
|
51
|
-
})
|
|
52
|
-
.strict();
|
|
53
|
-
export const ContentCoherenceMergeTraceEntrySchema = ContentCoherenceCandidateSchema.extend({
|
|
54
|
-
decision: z.enum(["merge", "already_connected"]),
|
|
55
|
-
root: z.string(),
|
|
56
|
-
}).strict();
|
|
57
54
|
export const ContentCoherenceTraceSchema = z
|
|
58
55
|
.object({
|
|
59
56
|
normalized_items: z.array(NormalizedContentCoherenceItemSchema),
|
|
60
|
-
pair_scores: z.array(ContentCoherencePairScoreSchema),
|
|
61
|
-
eligible_candidates: z.array(ContentCoherenceCandidateSchema),
|
|
62
|
-
merge_trace: z.array(ContentCoherenceMergeTraceEntrySchema),
|
|
63
|
-
merge_decisions: z.array(z.enum(["merge", "already_connected"])),
|
|
64
57
|
components: z.array(z.array(z.string())),
|
|
65
58
|
})
|
|
66
59
|
.strict();
|
|
@@ -201,6 +194,55 @@ function scoreEvidence(evidence) {
|
|
|
201
194
|
}
|
|
202
195
|
return score;
|
|
203
196
|
}
|
|
197
|
+
function isEligible(direct, score, policy) {
|
|
198
|
+
return policy.eligibility === "shared_file_and_same_lens"
|
|
199
|
+
? direct.sharedFile && direct.sameLens
|
|
200
|
+
: score >= CONTENT_COHERENCE_THRESHOLD;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Split one eligible-edge component at its modularity PEAK, or hand it back
|
|
204
|
+
* whole. The component's own eligible pairs are the weighted graph (edge weight =
|
|
205
|
+
* the pair's evidence score), Louvain proposes a partition at the canonical γ,
|
|
206
|
+
* and the proposal is accepted only when it scores strictly above keeping the
|
|
207
|
+
* component whole. A component held together by one weak bridge splits there.
|
|
208
|
+
* Nothing in the decision is a constant of any denomination.
|
|
209
|
+
*
|
|
210
|
+
* What survives whole is a UNIFORM-weight clique: with every edge equal, any cut
|
|
211
|
+
* scores below the trivial partition, so it cannot be split. A clique whose
|
|
212
|
+
* weights DIFFER can still split — six findings on one file and one lens but two
|
|
213
|
+
* units weigh 220 within a unit and 140 across it, and that contrast alone beats
|
|
214
|
+
* the whole. That is intended (the halves are genuinely less coupled), and the
|
|
215
|
+
* shared file then surfaces as a seam between them rather than disappearing into
|
|
216
|
+
* one block. "A clique stays whole" without the uniform qualifier is an
|
|
217
|
+
* overclaim; the two cases are pinned separately in
|
|
218
|
+
* `tests/shared/content-coherence.test.ts`.
|
|
219
|
+
*/
|
|
220
|
+
function refineAtModularityPeak(members, candidates) {
|
|
221
|
+
if (members.length <= TRIVIAL_COMPONENT_SIZE)
|
|
222
|
+
return [[...members]];
|
|
223
|
+
const graph = {
|
|
224
|
+
nodes: [...members],
|
|
225
|
+
edges: candidates.map((candidate) => ({
|
|
226
|
+
a: candidate.left,
|
|
227
|
+
b: candidate.right,
|
|
228
|
+
weight: candidate.score,
|
|
229
|
+
})),
|
|
230
|
+
};
|
|
231
|
+
const proposed = louvain(graph, MODULARITY_RESOLUTION);
|
|
232
|
+
const whole = new Map(members.map((id) => [id, members[0] ?? id]));
|
|
233
|
+
if (modularityOf(graph, proposed, MODULARITY_RESOLUTION) <=
|
|
234
|
+
modularityOf(graph, whole, MODULARITY_RESOLUTION)) {
|
|
235
|
+
return [[...members]];
|
|
236
|
+
}
|
|
237
|
+
const communities = new Map();
|
|
238
|
+
for (const member of members) {
|
|
239
|
+
const community = proposed.get(member) ?? member;
|
|
240
|
+
const group = communities.get(community) ?? [];
|
|
241
|
+
group.push(member);
|
|
242
|
+
communities.set(community, group);
|
|
243
|
+
}
|
|
244
|
+
return [...communities.values()];
|
|
245
|
+
}
|
|
204
246
|
function findRoot(parent, id) {
|
|
205
247
|
const lineage = [];
|
|
206
248
|
let root = id;
|
|
@@ -212,37 +254,50 @@ function findRoot(parent, id) {
|
|
|
212
254
|
parent.set(member, root);
|
|
213
255
|
return root;
|
|
214
256
|
}
|
|
215
|
-
/**
|
|
216
|
-
|
|
257
|
+
/**
|
|
258
|
+
* Build the complete canonical trace used by both audit and findings draws.
|
|
259
|
+
*
|
|
260
|
+
* `policy` is REQUIRED and has no default: a draw that forgot to declare its
|
|
261
|
+
* eligibility would otherwise silently inherit the other draw's, which is
|
|
262
|
+
* exactly the class of latent failure this project refuses to leave to a
|
|
263
|
+
* caller's memory. Pass {@link TASK_DRAW_COHERENCE_POLICY} or
|
|
264
|
+
* {@link FINDINGS_DRAW_COHERENCE_POLICY}.
|
|
265
|
+
*/
|
|
266
|
+
export function buildContentCoherenceTrace(input, policy) {
|
|
217
267
|
if (!isRecord(input)) {
|
|
218
268
|
throw new Error("Content-coherence input must be an object.");
|
|
219
269
|
}
|
|
220
270
|
const normalizedItems = normalizeItems(input.items);
|
|
221
271
|
const itemIds = new Set(normalizedItems.map((item) => item.id));
|
|
222
272
|
const relationshipEvidence = normalizeRelationshipEvidence(input.relationships, itemIds);
|
|
223
|
-
|
|
273
|
+
// Only ELIGIBLE pairs are retained. The ineligible majority (82% of pairs on
|
|
274
|
+
// the 3,194-finding dogfood run) can affect nothing downstream — union-find
|
|
275
|
+
// consumes eligibility alone — so materializing the full O(N²) pair layer was
|
|
276
|
+
// pure cost. Scoring itself is unchanged; the sort comparator below is total
|
|
277
|
+
// over distinct (left,right) pairs, so filtering during the scan yields the
|
|
278
|
+
// byte-identical candidate order the filter-then-sort form produced.
|
|
279
|
+
const eligibleCandidates = [];
|
|
224
280
|
for (let leftIndex = 0; leftIndex < normalizedItems.length; leftIndex += 1) {
|
|
225
281
|
for (let rightIndex = leftIndex + 1; rightIndex < normalizedItems.length; rightIndex += 1) {
|
|
226
282
|
const left = normalizedItems[leftIndex];
|
|
227
283
|
const right = normalizedItems[rightIndex];
|
|
228
284
|
const evidence = directEvidence(left, right);
|
|
285
|
+
// Captured BEFORE relationship evidence is folded in — the conjunctive
|
|
286
|
+
// policy must read only what the items themselves assert.
|
|
287
|
+
const direct = {
|
|
288
|
+
sharedFile: evidence.shared_file,
|
|
289
|
+
sameLens: evidence.shared_semantic_tag_or_same_lens,
|
|
290
|
+
};
|
|
229
291
|
for (const kind of relationshipEvidence.get(pairKey(left.id, right.id)) ?? []) {
|
|
230
292
|
evidence[kind] = true;
|
|
231
293
|
}
|
|
232
294
|
const score = scoreEvidence(evidence);
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
evidence,
|
|
237
|
-
score,
|
|
238
|
-
eligible: score >= CONTENT_COHERENCE_THRESHOLD,
|
|
239
|
-
});
|
|
295
|
+
if (!isEligible(direct, score, policy))
|
|
296
|
+
continue;
|
|
297
|
+
eligibleCandidates.push({ left: left.id, right: right.id, score });
|
|
240
298
|
}
|
|
241
299
|
}
|
|
242
|
-
|
|
243
|
-
.filter((pair) => pair.eligible)
|
|
244
|
-
.map(({ left, right, score }) => ({ left, right, score }))
|
|
245
|
-
.sort((left, right) => {
|
|
300
|
+
eligibleCandidates.sort((left, right) => {
|
|
246
301
|
if (left.score !== right.score)
|
|
247
302
|
return right.score - left.score;
|
|
248
303
|
const leftDelta = compareCodeUnits(left.left, right.left);
|
|
@@ -251,22 +306,14 @@ export function buildContentCoherenceTrace(input) {
|
|
|
251
306
|
: compareCodeUnits(left.right, right.right);
|
|
252
307
|
});
|
|
253
308
|
const parent = new Map(normalizedItems.map((item) => [item.id, item.id]));
|
|
254
|
-
const mergeTrace = [];
|
|
255
309
|
for (const candidate of eligibleCandidates) {
|
|
256
310
|
const leftRoot = findRoot(parent, candidate.left);
|
|
257
311
|
const rightRoot = findRoot(parent, candidate.right);
|
|
258
|
-
if (leftRoot === rightRoot)
|
|
259
|
-
mergeTrace.push({
|
|
260
|
-
...candidate,
|
|
261
|
-
decision: "already_connected",
|
|
262
|
-
root: leftRoot,
|
|
263
|
-
});
|
|
312
|
+
if (leftRoot === rightRoot)
|
|
264
313
|
continue;
|
|
265
|
-
}
|
|
266
314
|
const root = compareCodeUnits(leftRoot, rightRoot) <= 0 ? leftRoot : rightRoot;
|
|
267
315
|
const other = root === leftRoot ? rightRoot : leftRoot;
|
|
268
316
|
parent.set(other, root);
|
|
269
|
-
mergeTrace.push({ ...candidate, decision: "merge", root });
|
|
270
317
|
}
|
|
271
318
|
const groups = new Map();
|
|
272
319
|
for (const item of normalizedItems) {
|
|
@@ -275,15 +322,25 @@ export function buildContentCoherenceTrace(input) {
|
|
|
275
322
|
members.push(item.id);
|
|
276
323
|
groups.set(root, members);
|
|
277
324
|
}
|
|
278
|
-
|
|
279
|
-
|
|
325
|
+
// Bucket the eligible pairs by the component they ended up inside, so
|
|
326
|
+
// refinement reads each component's own weighted evidence graph once.
|
|
327
|
+
const candidatesByRoot = new Map();
|
|
328
|
+
if (policy.refineAtModularityPeak) {
|
|
329
|
+
for (const candidate of eligibleCandidates) {
|
|
330
|
+
const root = findRoot(parent, candidate.left);
|
|
331
|
+
const bucket = candidatesByRoot.get(root) ?? [];
|
|
332
|
+
bucket.push(candidate);
|
|
333
|
+
candidatesByRoot.set(root, bucket);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
const merged = [...groups.entries()].map(([root, members]) => [root, members.sort(compareCodeUnits)]);
|
|
337
|
+
const components = (policy.refineAtModularityPeak
|
|
338
|
+
? merged.flatMap(([root, members]) => refineAtModularityPeak(members, candidatesByRoot.get(root) ?? []))
|
|
339
|
+
: merged.map(([, members]) => members))
|
|
340
|
+
.map((members) => [...members].sort(compareCodeUnits))
|
|
280
341
|
.sort((left, right) => compareCodeUnits(left[0] ?? "", right[0] ?? ""));
|
|
281
342
|
return {
|
|
282
343
|
normalized_items: normalizedItems,
|
|
283
|
-
pair_scores: pairScores,
|
|
284
|
-
eligible_candidates: eligibleCandidates,
|
|
285
|
-
merge_trace: mergeTrace,
|
|
286
|
-
merge_decisions: mergeTrace.map((entry) => entry.decision),
|
|
287
344
|
components,
|
|
288
345
|
};
|
|
289
346
|
}
|