@dzhechkov/harness-core 0.8.5 → 0.8.10
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/.dz-manifest.json +286 -82
- package/README.md +54 -2
- package/dist/amendment-trace.d.ts.map +1 -1
- package/dist/amendment-trace.js +12 -1
- package/dist/amendment-trace.js.map +1 -1
- package/dist/compounding.d.ts +54 -0
- package/dist/compounding.d.ts.map +1 -1
- package/dist/compounding.js +221 -1
- package/dist/compounding.js.map +1 -1
- package/dist/eta.d.ts +92 -0
- package/dist/eta.d.ts.map +1 -0
- package/dist/eta.js +488 -0
- package/dist/eta.js.map +1 -0
- package/dist/feature-adr-checkpoints.d.ts +11 -2
- package/dist/feature-adr-checkpoints.d.ts.map +1 -1
- package/dist/feature-adr-checkpoints.js +38 -3
- package/dist/feature-adr-checkpoints.js.map +1 -1
- package/dist/feature-adr-decision-recall.d.ts +167 -0
- package/dist/feature-adr-decision-recall.d.ts.map +1 -0
- package/dist/feature-adr-decision-recall.js +519 -0
- package/dist/feature-adr-decision-recall.js.map +1 -0
- package/dist/feature-adr-landing.d.ts +37 -0
- package/dist/feature-adr-landing.d.ts.map +1 -0
- package/dist/feature-adr-landing.js +59 -0
- package/dist/feature-adr-landing.js.map +1 -0
- package/dist/feature-adr-routing.d.ts +58 -23
- package/dist/feature-adr-routing.d.ts.map +1 -1
- package/dist/feature-adr-routing.js +194 -69
- package/dist/feature-adr-routing.js.map +1 -1
- package/dist/guard-promotion.d.ts +41 -0
- package/dist/guard-promotion.d.ts.map +1 -1
- package/dist/guard-promotion.js +218 -4
- package/dist/guard-promotion.js.map +1 -1
- package/dist/guard-volume.d.ts +108 -0
- package/dist/guard-volume.d.ts.map +1 -0
- package/dist/guard-volume.js +536 -0
- package/dist/guard-volume.js.map +1 -0
- package/dist/guard.d.ts +34 -0
- package/dist/guard.d.ts.map +1 -1
- package/dist/guard.js +113 -4
- package/dist/guard.js.map +1 -1
- package/dist/index.d.ts +18 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/dist/integration-apply.d.ts +25 -0
- package/dist/integration-apply.d.ts.map +1 -0
- package/dist/integration-apply.js +299 -0
- package/dist/integration-apply.js.map +1 -0
- package/dist/integration-evidence.d.ts +46 -0
- package/dist/integration-evidence.d.ts.map +1 -0
- package/dist/integration-evidence.js +44 -0
- package/dist/integration-evidence.js.map +1 -0
- package/dist/integration-probe-worker.d.ts +22 -0
- package/dist/integration-probe-worker.d.ts.map +1 -0
- package/dist/integration-probe-worker.js +334 -0
- package/dist/integration-probe-worker.js.map +1 -0
- package/dist/integrations-verify.d.ts +60 -0
- package/dist/integrations-verify.d.ts.map +1 -0
- package/dist/integrations-verify.js +194 -0
- package/dist/integrations-verify.js.map +1 -0
- package/dist/lesson-generalization.d.ts +29 -0
- package/dist/lesson-generalization.d.ts.map +1 -0
- package/dist/lesson-generalization.js +84 -0
- package/dist/lesson-generalization.js.map +1 -0
- package/dist/loop-blobs.generated.js +8 -8
- package/dist/loop-blobs.generated.js.map +1 -1
- package/dist/loop-plan.d.ts +13 -1
- package/dist/loop-plan.d.ts.map +1 -1
- package/dist/loop-plan.js +15 -1
- package/dist/loop-plan.js.map +1 -1
- package/dist/loop-render.d.ts.map +1 -1
- package/dist/loop-render.js +51 -6
- package/dist/loop-render.js.map +1 -1
- package/dist/loop-trace.d.ts +20 -1
- package/dist/loop-trace.d.ts.map +1 -1
- package/dist/loop-trace.js +83 -1
- package/dist/loop-trace.js.map +1 -1
- package/dist/model-recommender.d.ts +8 -0
- package/dist/model-recommender.d.ts.map +1 -1
- package/dist/model-recommender.js +31 -4
- package/dist/model-recommender.js.map +1 -1
- package/dist/operations.d.ts +27 -0
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +178 -2
- package/dist/operations.js.map +1 -1
- package/dist/patterns.d.ts +27 -1
- package/dist/patterns.d.ts.map +1 -1
- package/dist/patterns.js +211 -45
- package/dist/patterns.js.map +1 -1
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +20 -17
- package/dist/setup.js.map +1 -1
- package/dist/target-integrations.d.ts +65 -0
- package/dist/target-integrations.d.ts.map +1 -0
- package/dist/target-integrations.js +152 -0
- package/dist/target-integrations.js.map +1 -0
- package/dist/vector-tier.d.ts +6 -1
- package/dist/vector-tier.d.ts.map +1 -1
- package/dist/vector-tier.js +32 -7
- package/dist/vector-tier.js.map +1 -1
- package/dist/workflow-run.d.ts.map +1 -1
- package/dist/workflow-run.js +18 -12
- package/dist/workflow-run.js.map +1 -1
- package/package.json +19 -15
- package/sbom.json +591 -81
- package/src/amendment-trace.ts +12 -1
- package/src/compounding.ts +300 -1
- package/src/eta.ts +590 -0
- package/src/feature-adr-checkpoints.ts +39 -3
- package/src/feature-adr-decision-recall.ts +652 -0
- package/src/feature-adr-landing.ts +109 -0
- package/src/feature-adr-routing.ts +224 -65
- package/src/guard-promotion.ts +245 -4
- package/src/guard-volume.ts +752 -0
- package/src/guard.ts +148 -4
- package/src/index.ts +65 -3
- package/src/integration-apply.ts +332 -0
- package/src/integration-evidence.ts +89 -0
- package/src/integration-probe-worker.ts +310 -0
- package/src/integration-receipts/claude-code/mcp/2.1.235.json +35 -0
- package/src/integrations-verify.ts +258 -0
- package/src/lesson-generalization.ts +115 -0
- package/src/loop-blobs.generated.ts +8 -8
- package/src/loop-plan.ts +36 -3
- package/src/loop-render.ts +50 -6
- package/src/loop-trace.ts +91 -2
- package/src/model-recommender.ts +35 -4
- package/src/operations.ts +199 -2
- package/src/patterns.ts +252 -43
- package/src/setup.ts +20 -17
- package/src/target-integrations.ts +225 -0
- package/src/vector-tier.ts +44 -14
- package/src/workflow-run.ts +18 -11
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/** Exhaustive target companion policy and once-per-install manifest aggregation. */
|
|
2
|
+
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
import { claudeIntegrationAdapter } from '@dzhechkov/adapter-claude';
|
|
7
|
+
import {
|
|
8
|
+
INTEGRATION_MANIFEST_MAX_BYTES,
|
|
9
|
+
INTEGRATION_REGISTRATION_MAX_COUNT,
|
|
10
|
+
canonicalIntegrationJson,
|
|
11
|
+
integrationManifestDigest,
|
|
12
|
+
parseHarnessIntegrationManifestJson,
|
|
13
|
+
type HarnessIntegrationManifestV1,
|
|
14
|
+
type IntegrationComponent,
|
|
15
|
+
type IntegrationPlan,
|
|
16
|
+
type IntegrationReasonCode,
|
|
17
|
+
type IntegrationStatus,
|
|
18
|
+
type TargetIntegrationAdapter,
|
|
19
|
+
} from '@dzhechkov/core';
|
|
20
|
+
|
|
21
|
+
import { TARGET_NAMES, type TargetName } from './targets.js';
|
|
22
|
+
|
|
23
|
+
export interface RegistrationObservation {
|
|
24
|
+
readonly id: string;
|
|
25
|
+
readonly scope: 'project' | 'user' | 'plugin';
|
|
26
|
+
readonly registered: boolean;
|
|
27
|
+
readonly approval?: 'pending' | 'approved' | 'unknown';
|
|
28
|
+
readonly ready?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface IntegrationOutcome {
|
|
32
|
+
readonly target: TargetName;
|
|
33
|
+
readonly component: IntegrationComponent;
|
|
34
|
+
readonly status: IntegrationStatus;
|
|
35
|
+
readonly registrations: readonly RegistrationObservation[];
|
|
36
|
+
readonly carrier?: { readonly scope: 'project' | 'user' | 'plugin'; readonly path: string };
|
|
37
|
+
readonly runtimeVersion?: string;
|
|
38
|
+
readonly evidenceVersion?: string;
|
|
39
|
+
readonly reasonCode?: IntegrationReasonCode;
|
|
40
|
+
readonly remediation?: string;
|
|
41
|
+
/** True when carrier bytes may precede a failed live check or ownership-journal commit. */
|
|
42
|
+
readonly applied?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type IntegrationPolicyCell =
|
|
46
|
+
| { readonly disposition: 'receipt'; readonly reasonCode?: never; readonly receiptVersion: string }
|
|
47
|
+
| { readonly disposition: 'legacy-post-write'; readonly reasonCode: 'CURRENT_LIVE_CHECK_FAILED' }
|
|
48
|
+
| { readonly disposition: 'refused'; readonly reasonCode: IntegrationReasonCode };
|
|
49
|
+
|
|
50
|
+
export interface TargetIntegrationPolicy {
|
|
51
|
+
readonly mcp: IntegrationPolicyCell;
|
|
52
|
+
readonly hooks: IntegrationPolicyCell;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const refusedAdapter = (target: TargetName): TargetIntegrationAdapter => ({
|
|
56
|
+
target,
|
|
57
|
+
plan(manifest): IntegrationPlan {
|
|
58
|
+
const refusals: IntegrationPlan['refusals'][number][] = [];
|
|
59
|
+
if (Object.keys(manifest.mcpServers ?? {}).length > 0) {
|
|
60
|
+
refusals.push({ component: 'mcp', reasonCode: TARGET_INTEGRATION_POLICY[target].mcp.reasonCode ?? 'NO_QUALIFYING_LIVE_RECEIPT', remediation: 'run dz integrations-verify for this exact target product and version' });
|
|
61
|
+
}
|
|
62
|
+
if ((manifest.hooks?.length ?? 0) > 0) {
|
|
63
|
+
refusals.push({ component: 'hooks', reasonCode: TARGET_INTEGRATION_POLICY[target].hooks.reasonCode ?? 'NO_QUALIFYING_LIVE_RECEIPT', remediation: 'run dz integrations-verify with a hook canary and negative control' });
|
|
64
|
+
}
|
|
65
|
+
return { fragments: [], refusals };
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
/** Closed 10×2 policy. No fallback/default is permitted. */
|
|
70
|
+
export const TARGET_INTEGRATION_POLICY: Record<TargetName, TargetIntegrationPolicy> = {
|
|
71
|
+
'claude-code': {
|
|
72
|
+
mcp: { disposition: 'receipt', receiptVersion: '2.1.235' },
|
|
73
|
+
hooks: { disposition: 'refused', reasonCode: 'NO_ACTIVATION_RECEIPT' },
|
|
74
|
+
},
|
|
75
|
+
codex: {
|
|
76
|
+
mcp: { disposition: 'refused', reasonCode: 'PROJECT_CARRIER_NOT_OBSERVED' },
|
|
77
|
+
hooks: { disposition: 'legacy-post-write', reasonCode: 'CURRENT_LIVE_CHECK_FAILED' },
|
|
78
|
+
},
|
|
79
|
+
cursor: {
|
|
80
|
+
mcp: { disposition: 'refused', reasonCode: 'TARGET_BINARY_UNAVAILABLE' },
|
|
81
|
+
hooks: { disposition: 'refused', reasonCode: 'TARGET_BINARY_UNAVAILABLE' },
|
|
82
|
+
},
|
|
83
|
+
copilot: {
|
|
84
|
+
mcp: { disposition: 'refused', reasonCode: 'TARGET_BINARY_UNAVAILABLE' },
|
|
85
|
+
hooks: { disposition: 'refused', reasonCode: 'TARGET_BINARY_UNAVAILABLE' },
|
|
86
|
+
},
|
|
87
|
+
windsurf: {
|
|
88
|
+
mcp: { disposition: 'refused', reasonCode: 'PRODUCT_SURFACE_AMBIGUOUS' },
|
|
89
|
+
hooks: { disposition: 'refused', reasonCode: 'TARGET_BINARY_UNAVAILABLE' },
|
|
90
|
+
},
|
|
91
|
+
gemini: {
|
|
92
|
+
mcp: { disposition: 'refused', reasonCode: 'NO_QUALIFYING_LIVE_RECEIPT' },
|
|
93
|
+
hooks: { disposition: 'refused', reasonCode: 'NO_QUALIFYING_LIVE_RECEIPT' },
|
|
94
|
+
},
|
|
95
|
+
opencode: {
|
|
96
|
+
mcp: { disposition: 'refused', reasonCode: 'TARGET_BINARY_UNAVAILABLE' },
|
|
97
|
+
hooks: { disposition: 'refused', reasonCode: 'TARGET_BINARY_UNAVAILABLE' },
|
|
98
|
+
},
|
|
99
|
+
openclaude: {
|
|
100
|
+
mcp: { disposition: 'refused', reasonCode: 'PROJECT_CARRIER_NOT_DOCUMENTED' },
|
|
101
|
+
hooks: { disposition: 'refused', reasonCode: 'TARGET_BINARY_UNAVAILABLE' },
|
|
102
|
+
},
|
|
103
|
+
hermes: {
|
|
104
|
+
mcp: { disposition: 'refused', reasonCode: 'LIVE_PROBE_TIMEOUT' },
|
|
105
|
+
hooks: { disposition: 'refused', reasonCode: 'NO_ACTIVATION_RECEIPT' },
|
|
106
|
+
},
|
|
107
|
+
'agents-md': {
|
|
108
|
+
mcp: { disposition: 'refused', reasonCode: 'NO_RUNTIME_SURFACE' },
|
|
109
|
+
hooks: { disposition: 'refused', reasonCode: 'NO_RUNTIME_SURFACE' },
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const TARGET_INTEGRATIONS: Record<TargetName, TargetIntegrationAdapter> = Object.fromEntries(
|
|
114
|
+
TARGET_NAMES.map((target) => [target, target === 'claude-code' ? claudeIntegrationAdapter : refusedAdapter(target)]),
|
|
115
|
+
) as Record<TargetName, TargetIntegrationAdapter>;
|
|
116
|
+
|
|
117
|
+
export interface IntegrationManifestSource {
|
|
118
|
+
readonly skillId: string;
|
|
119
|
+
readonly skillDir: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface IntegrationManifestAggregate {
|
|
123
|
+
readonly manifest: HarnessIntegrationManifestV1 | undefined;
|
|
124
|
+
readonly digest: string | undefined;
|
|
125
|
+
readonly sourcePaths: readonly string[];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export class IntegrationManifestError extends Error {
|
|
129
|
+
readonly code = 'MANIFEST_INVALID';
|
|
130
|
+
constructor(readonly manifestPath: string, message: string) {
|
|
131
|
+
super(`${manifestPath}: ${message}`);
|
|
132
|
+
this.name = 'IntegrationManifestError';
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Load, validate and aggregate every adjacent manifest before target effects. */
|
|
137
|
+
export function aggregateIntegrationManifests(
|
|
138
|
+
sources: readonly IntegrationManifestSource[],
|
|
139
|
+
): IntegrationManifestAggregate {
|
|
140
|
+
const manifests: { path: string; value: HarnessIntegrationManifestV1 }[] = [];
|
|
141
|
+
for (const source of sources) {
|
|
142
|
+
const path = join(source.skillDir, source.skillId, 'INTEGRATIONS.json');
|
|
143
|
+
if (!existsSync(path)) continue;
|
|
144
|
+
try {
|
|
145
|
+
manifests.push({ path, value: parseHarnessIntegrationManifestJson(readFileSync(path, 'utf8')) });
|
|
146
|
+
} catch (error) {
|
|
147
|
+
throw new IntegrationManifestError(path, error instanceof Error ? error.message : String(error));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (manifests.length === 0) return { manifest: undefined, digest: undefined, sourcePaths: [] };
|
|
151
|
+
|
|
152
|
+
const mcp = Object.create(null) as Record<string, NonNullable<HarnessIntegrationManifestV1['mcpServers']>[string]>;
|
|
153
|
+
const hooks = new Map<string, NonNullable<HarnessIntegrationManifestV1['hooks']>[number]>();
|
|
154
|
+
for (const source of manifests) {
|
|
155
|
+
for (const [id, intent] of Object.entries(source.value.mcpServers ?? {})) {
|
|
156
|
+
const previous = mcp[id];
|
|
157
|
+
if (previous !== undefined && canonicalIntegrationJson(previous) !== canonicalIntegrationJson(intent)) {
|
|
158
|
+
throw new IntegrationManifestError(source.path, `conflicting MCP registration id ${JSON.stringify(id)}`);
|
|
159
|
+
}
|
|
160
|
+
mcp[id] = intent;
|
|
161
|
+
}
|
|
162
|
+
for (const hook of source.value.hooks ?? []) {
|
|
163
|
+
const previous = hooks.get(hook.id);
|
|
164
|
+
if (previous !== undefined && canonicalIntegrationJson(previous) !== canonicalIntegrationJson(hook)) {
|
|
165
|
+
throw new IntegrationManifestError(source.path, `conflicting hook registration id ${JSON.stringify(hook.id)}`);
|
|
166
|
+
}
|
|
167
|
+
hooks.set(hook.id, hook);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const aggregate = {
|
|
171
|
+
version: 1 as const,
|
|
172
|
+
...(Object.keys(mcp).length > 0 ? { mcpServers: Object.fromEntries(Object.entries(mcp).sort(([a], [b]) => a.localeCompare(b))) } : {}),
|
|
173
|
+
...(hooks.size > 0 ? { hooks: [...hooks.values()].sort((a, b) => a.id.localeCompare(b.id)) } : {}),
|
|
174
|
+
} satisfies HarnessIntegrationManifestV1;
|
|
175
|
+
const count = Object.keys(aggregate.mcpServers ?? {}).length + (aggregate.hooks?.length ?? 0);
|
|
176
|
+
if (count > INTEGRATION_REGISTRATION_MAX_COUNT) {
|
|
177
|
+
throw new IntegrationManifestError('<aggregate>', `final aggregate has ${count} registrations; maximum is ${INTEGRATION_REGISTRATION_MAX_COUNT}`);
|
|
178
|
+
}
|
|
179
|
+
const aggregateBytes = Buffer.byteLength(canonicalIntegrationJson(aggregate), 'utf8');
|
|
180
|
+
if (aggregateBytes > INTEGRATION_MANIFEST_MAX_BYTES) {
|
|
181
|
+
throw new IntegrationManifestError('<aggregate>', `final aggregate exceeds ${INTEGRATION_MANIFEST_MAX_BYTES} UTF-8 bytes`);
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
manifest: aggregate,
|
|
185
|
+
digest: integrationManifestDigest([aggregate]),
|
|
186
|
+
sourcePaths: manifests.map((source) => source.path),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function notRequestedOutcomes(target: TargetName): readonly [IntegrationOutcome, IntegrationOutcome] {
|
|
191
|
+
return [
|
|
192
|
+
{ target, component: 'mcp', status: 'not-requested', registrations: [] },
|
|
193
|
+
{ target, component: 'hooks', status: 'not-requested', registrations: [] },
|
|
194
|
+
];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function refusedOutcome(
|
|
198
|
+
target: TargetName,
|
|
199
|
+
component: IntegrationComponent,
|
|
200
|
+
reasonCode: IntegrationReasonCode,
|
|
201
|
+
remediation: string,
|
|
202
|
+
): IntegrationOutcome {
|
|
203
|
+
return { target, component, status: 'refused', registrations: [], reasonCode, remediation };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Static synchronous outcomes for Gemini/agents-md and other refusal-only seams. */
|
|
207
|
+
export function staticPolicyOutcomes(
|
|
208
|
+
target: TargetName,
|
|
209
|
+
manifest: HarnessIntegrationManifestV1 | undefined,
|
|
210
|
+
noHooks = false,
|
|
211
|
+
): readonly [IntegrationOutcome, IntegrationOutcome] {
|
|
212
|
+
if (manifest === undefined) return notRequestedOutcomes(target);
|
|
213
|
+
const mcpRequested = Object.keys(manifest.mcpServers ?? {}).length > 0;
|
|
214
|
+
const hooksRequested = (manifest.hooks?.length ?? 0) > 0 && !noHooks;
|
|
215
|
+
const mcpCell = TARGET_INTEGRATION_POLICY[target].mcp;
|
|
216
|
+
const hookCell = TARGET_INTEGRATION_POLICY[target].hooks;
|
|
217
|
+
return [
|
|
218
|
+
mcpRequested
|
|
219
|
+
? refusedOutcome(target, 'mcp', mcpCell.reasonCode ?? 'NO_QUALIFYING_LIVE_RECEIPT', 'run dz integrations-verify for an exact-version receipt')
|
|
220
|
+
: { target, component: 'mcp', status: 'not-requested', registrations: [] },
|
|
221
|
+
hooksRequested
|
|
222
|
+
? refusedOutcome(target, 'hooks', hookCell.reasonCode ?? 'NO_ACTIVATION_RECEIPT', 'run dz integrations-verify with a hook canary and negative control')
|
|
223
|
+
: { target, component: 'hooks', status: 'not-requested', registrations: [] },
|
|
224
|
+
];
|
|
225
|
+
}
|
package/src/vector-tier.ts
CHANGED
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
updateReinforcementState,
|
|
57
57
|
type PatternRecord,
|
|
58
58
|
type RecallHit,
|
|
59
|
+
type RecallPatternsOptions,
|
|
59
60
|
} from './patterns.js';
|
|
60
61
|
import {
|
|
61
62
|
indexPatternsToAgentdb,
|
|
@@ -202,6 +203,8 @@ export interface HybridHit {
|
|
|
202
203
|
readonly score: number;
|
|
203
204
|
/** lesson-quarantine: set only for a quarantined hit — display marks ⚠q, ranking was damped. */
|
|
204
205
|
readonly quarantined?: boolean;
|
|
206
|
+
/** Form provenance from the lexical pair merge; semantic ranking never invents it. */
|
|
207
|
+
readonly matchedForm?: NonNullable<RecallHit['matchedForm']>;
|
|
205
208
|
/**
|
|
206
209
|
* Raw closeness from the semantic leg, when the engine reports a genuine cosine. ABSENT for a
|
|
207
210
|
* lexical-only hit and for an engine whose score is not a cosine — the honest display there is a
|
|
@@ -491,7 +494,7 @@ export function isVectorNoise(text: string): boolean {
|
|
|
491
494
|
* ingest gate — I-6). Score is the record's REAL reward, never a fabricated 1.0.
|
|
492
495
|
*/
|
|
493
496
|
export function patternVectorEntry(p: PatternRecord, source = 'dz-teach', opts: { quarantined?: boolean } = {}): VectorEntry | undefined {
|
|
494
|
-
if (isVectorNoise(p.pattern)) return undefined;
|
|
497
|
+
if (p.lessonForm === 'class' || isVectorNoise(p.pattern)) return undefined;
|
|
495
498
|
const dzId = patternRecordId(p);
|
|
496
499
|
return {
|
|
497
500
|
dzId,
|
|
@@ -501,7 +504,13 @@ export function patternVectorEntry(p: PatternRecord, source = 'dz-teach', opts:
|
|
|
501
504
|
tags: ['dz-teach', p.type],
|
|
502
505
|
// FR-8: quarantine rides into the mirror so the HOOK DAEMON (which reads only the mirror's
|
|
503
506
|
// sqlite metadata) can exclude unproven lessons from auto-injection.
|
|
504
|
-
metadata: {
|
|
507
|
+
metadata: {
|
|
508
|
+
dzId, source, ts: p.ts, domain: p.domain,
|
|
509
|
+
...(p.lessonForm !== undefined && p.lessonPairId !== undefined
|
|
510
|
+
? { lessonForm: p.lessonForm, lessonPairId: p.lessonPairId }
|
|
511
|
+
: {}),
|
|
512
|
+
...(opts.quarantined === true ? { qStatus: 'quarantined' } : {}),
|
|
513
|
+
},
|
|
505
514
|
};
|
|
506
515
|
}
|
|
507
516
|
|
|
@@ -525,7 +534,7 @@ export function dreamVectorEntry(d: DreamPattern): VectorEntry | undefined {
|
|
|
525
534
|
|
|
526
535
|
/** ACL: stored {@link MemoryRecord} → {@link VectorEntry} (the consolidate-backfill mapper). */
|
|
527
536
|
export function memoryRecordVectorEntry(r: MemoryRecord): VectorEntry | undefined {
|
|
528
|
-
if (isVectorNoise(r.text)) return undefined;
|
|
537
|
+
if (r.metadata?.['lessonForm'] === 'class' || isVectorNoise(r.text)) return undefined;
|
|
529
538
|
const state = readReinforcementState(r);
|
|
530
539
|
return {
|
|
531
540
|
dzId: r.id,
|
|
@@ -533,7 +542,12 @@ export function memoryRecordVectorEntry(r: MemoryRecord): VectorEntry | undefine
|
|
|
533
542
|
score: r.score,
|
|
534
543
|
taskType: r.id.startsWith('dream:') ? 'dz-learning' : 'dz-teach',
|
|
535
544
|
tags: ['dz-backfill', r.outcome],
|
|
536
|
-
metadata: {
|
|
545
|
+
metadata: {
|
|
546
|
+
dzId: r.id, source: r.metadata?.['source'] ?? 'dz-backfill', ts: r.timestamp, skillId: r.skillId,
|
|
547
|
+
...(r.metadata?.['lessonForm'] === 'specific' && typeof r.metadata?.['lessonPairId'] === 'string'
|
|
548
|
+
? { lessonForm: 'specific', lessonPairId: r.metadata['lessonPairId'] }
|
|
549
|
+
: {}),
|
|
550
|
+
},
|
|
537
551
|
uses: state.uses,
|
|
538
552
|
avgReward: state.avgReward,
|
|
539
553
|
};
|
|
@@ -939,6 +953,7 @@ export interface RankedPattern {
|
|
|
939
953
|
readonly id: string;
|
|
940
954
|
readonly pattern: PatternRecord;
|
|
941
955
|
readonly backend: RecallHit['backend'];
|
|
956
|
+
readonly matchedForm?: NonNullable<RecallHit['matchedForm']>;
|
|
942
957
|
/**
|
|
943
958
|
* The semantic leg's raw closeness, when the engine reports a real cosine. Rides ALONGSIDE the RRF
|
|
944
959
|
* score and never enters the ranking maths — four things depend on RRF magnitude (the reinforce
|
|
@@ -967,11 +982,12 @@ export function mergeHybridHits(
|
|
|
967
982
|
opts: { readonly limit: number; readonly semanticWeight?: number | undefined },
|
|
968
983
|
): HybridHit[] {
|
|
969
984
|
const weight = opts.semanticWeight ?? 1;
|
|
970
|
-
interface Acc { pattern: PatternRecord; lex?: RecallHit['backend']; sem: boolean; score: number; similarity?: number }
|
|
985
|
+
interface Acc { pattern: PatternRecord; lex?: RecallHit['backend']; sem: boolean; score: number; similarity?: number; matchedForm?: NonNullable<RecallHit['matchedForm']> }
|
|
971
986
|
const acc = new Map<string, Acc>();
|
|
972
987
|
lexical.forEach((h, rank) => {
|
|
973
988
|
const cur = acc.get(h.id) ?? { pattern: h.pattern, sem: false, score: 0 };
|
|
974
989
|
cur.lex = h.backend;
|
|
990
|
+
if (h.matchedForm !== undefined) cur.matchedForm = h.matchedForm;
|
|
975
991
|
cur.score += 1 / (RRF_K + rank + 1);
|
|
976
992
|
acc.set(h.id, cur);
|
|
977
993
|
});
|
|
@@ -997,6 +1013,7 @@ export function mergeHybridHits(
|
|
|
997
1013
|
pattern: v.pattern,
|
|
998
1014
|
backend: v.lex !== undefined && v.sem ? ('both' as const) : v.lex ?? ('vector' as const),
|
|
999
1015
|
score: v.score,
|
|
1016
|
+
...(v.matchedForm === undefined ? {} : { matchedForm: v.matchedForm }),
|
|
1000
1017
|
...(v.similarity === undefined ? {} : { similarity: v.similarity }),
|
|
1001
1018
|
});
|
|
1002
1019
|
// `slice(0, -1)` drops the LAST element instead of returning nothing, so a negative limit used to
|
|
@@ -1105,6 +1122,8 @@ export async function recallHybrid(
|
|
|
1105
1122
|
* sitting at `pulls === 0` forever. Absent ⇒ `general`; it changes nothing while disarmed.
|
|
1106
1123
|
*/
|
|
1107
1124
|
readonly domain?: string | undefined;
|
|
1125
|
+
readonly onClassDegraded?: RecallPatternsOptions['onClassDegraded'];
|
|
1126
|
+
readonly classMatcher?: RecallPatternsOptions['classMatcher'];
|
|
1108
1127
|
} = {},
|
|
1109
1128
|
): Promise<HybridRecall> {
|
|
1110
1129
|
// Config-surface note (QE P3, benign by design): recall resolves the engine directly, while teach
|
|
@@ -1116,7 +1135,10 @@ export async function recallHybrid(
|
|
|
1116
1135
|
// write-side backend flag.
|
|
1117
1136
|
const limit = opts.limit ?? 10;
|
|
1118
1137
|
const mode = opts.mode ?? 'hybrid';
|
|
1119
|
-
const lexical = recallPatterns(projectRoot, query, limit
|
|
1138
|
+
const lexical = recallPatterns(projectRoot, query, limit, {
|
|
1139
|
+
...(opts.onClassDegraded === undefined ? {} : { onClassDegraded: opts.onClassDegraded }),
|
|
1140
|
+
...(opts.classMatcher === undefined ? {} : { classMatcher: opts.classMatcher }),
|
|
1141
|
+
});
|
|
1120
1142
|
const lexicalBackend: 'sqlite' | 'json' = lexical[0]?.backend === 'sqlite' ? 'sqlite' : 'json';
|
|
1121
1143
|
const records = loadStoreRecords(projectRoot);
|
|
1122
1144
|
const idToRecord = new Map<string, MemoryRecord>();
|
|
@@ -1223,7 +1245,12 @@ export async function recallHybrid(
|
|
|
1223
1245
|
};
|
|
1224
1246
|
const lexicalOnly = (extra: Partial<Pick<HybridRecall, 'vectorEngine' | 'vectorReason' | 'vectorError'>>): HybridRecall => {
|
|
1225
1247
|
// `enhance` FIRST: it is what populates `banditReport` (the key is absent while disarmed).
|
|
1226
|
-
const hits = enhance(lexical.map((h, rank) => ({
|
|
1248
|
+
const hits = enhance(lexical.map((h, rank) => ({
|
|
1249
|
+
pattern: h.pattern,
|
|
1250
|
+
backend: h.backend,
|
|
1251
|
+
score: 1 / (RRF_K + rank + 1),
|
|
1252
|
+
...(h.matchedForm === undefined ? {} : { matchedForm: h.matchedForm }),
|
|
1253
|
+
})));
|
|
1227
1254
|
return {
|
|
1228
1255
|
hits,
|
|
1229
1256
|
lexicalBackend,
|
|
@@ -1333,6 +1360,7 @@ export async function recallHybrid(
|
|
|
1333
1360
|
id: identityToId.get(patternIdentityOf(h.pattern)) ?? patternRecordId(h.pattern),
|
|
1334
1361
|
pattern: h.pattern,
|
|
1335
1362
|
backend: h.backend,
|
|
1363
|
+
...(h.matchedForm === undefined ? {} : { matchedForm: h.matchedForm }),
|
|
1336
1364
|
}));
|
|
1337
1365
|
const hits = enhance(mergeHybridHits(lex, semantic, { limit, semanticWeight: mode === 'semantic' ? 2 : 1 }));
|
|
1338
1366
|
markRecallHits(projectRoot, learning, hits, idOf, banditEmission());
|
|
@@ -1688,13 +1716,15 @@ export async function harmonizeVectorStore(projectRoot: string, opts: HarmonizeO
|
|
|
1688
1716
|
} catch {
|
|
1689
1717
|
records = [];
|
|
1690
1718
|
}
|
|
1691
|
-
const items: HarmonizeItem[] = records
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1719
|
+
const items: HarmonizeItem[] = records
|
|
1720
|
+
.filter((r) => r.metadata?.['lessonForm'] !== 'class')
|
|
1721
|
+
.map((r) => ({
|
|
1722
|
+
dzId: r.id,
|
|
1723
|
+
text: r.text,
|
|
1724
|
+
reward: r.score,
|
|
1725
|
+
ts: r.timestamp,
|
|
1726
|
+
taskType: r.id.startsWith('dream:') ? 'dz-learning' : 'dz-teach',
|
|
1727
|
+
}));
|
|
1698
1728
|
|
|
1699
1729
|
// 2. GATE: an embedder ⇒ SEMANTIC clustering; absence/failure ⇒ EXACT-text fallback (D4).
|
|
1700
1730
|
let embed: ((text: string) => Promise<Float32Array>) | undefined;
|
package/src/workflow-run.ts
CHANGED
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
traceInit,
|
|
34
34
|
traceLedgerLine,
|
|
35
35
|
traceOnDispatch,
|
|
36
|
+
traceOnFanoutTruncated,
|
|
36
37
|
traceOnSettle,
|
|
37
38
|
traceShellQuote,
|
|
38
39
|
TRACE_KEY_RE,
|
|
@@ -336,7 +337,7 @@ export function computeBoundaryReservations(
|
|
|
336
337
|
if (b.kind === 'region') {
|
|
337
338
|
const r = b.region;
|
|
338
339
|
if (r === undefined) continue;
|
|
339
|
-
const members = activatedMembers(r.registry, r.dedup, r.maxFanout).length;
|
|
340
|
+
const members = activatedMembers(r.registry, r.dedup, r.maxFanout, r.overflow).length;
|
|
340
341
|
const perItem = r.chain.reduce((n, s) => n + worstCaseInvocations(s), 0);
|
|
341
342
|
const perItemWall = r.chain.reduce((n, s) => n + worstCaseInvocations(s) * timeoutMsFor(s), 0);
|
|
342
343
|
out.push({
|
|
@@ -366,17 +367,14 @@ export function computeBoundaryReservations(
|
|
|
366
367
|
/**
|
|
367
368
|
* The members a fanout actually ACTIVATES.
|
|
368
369
|
*
|
|
369
|
-
* `maxFanout` caps
|
|
370
|
-
* `
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
* where the Claude host dispatched three, and the two hosts' traces would disagree on dispatch
|
|
374
|
-
* multiplicity for the same plan. (MEASURED: that is exactly what the F5 equivalence leg caught
|
|
375
|
-
* against the committed `pkg-audit-1` run — 6 lanes here, 3 there.)
|
|
370
|
+
* `maxFanout` caps CONCURRENCY under the default/window policy. Only the explicitly declared
|
|
371
|
+
* `overflow:'truncate'` escape hatch caps activation, and that path owes stderr + trace receipts.
|
|
372
|
+
* This function is shared by reservation arithmetic and enactment so neither can silently retain
|
|
373
|
+
* the old prefix-only interpretation.
|
|
376
374
|
*/
|
|
377
|
-
function activatedMembers(registry: string[], dedup: boolean, maxFanout: number): string[] {
|
|
375
|
+
function activatedMembers(registry: string[], dedup: boolean, maxFanout: number, overflow: 'window' | 'truncate'): string[] {
|
|
378
376
|
const base = dedup ? [...new Set(registry)] : [...registry];
|
|
379
|
-
return maxFanout > 0 ? base.slice(0, maxFanout) : base;
|
|
377
|
+
return overflow === 'truncate' && maxFanout > 0 ? base.slice(0, maxFanout) : base;
|
|
380
378
|
}
|
|
381
379
|
|
|
382
380
|
/**
|
|
@@ -1605,7 +1603,16 @@ async function runStage(ctx: RunCtx, b: RunBoundary): Promise<string | null> {
|
|
|
1605
1603
|
async function runRegion(ctx: RunCtx, b: RunBoundary): Promise<void> {
|
|
1606
1604
|
const r = b.region;
|
|
1607
1605
|
if (r === undefined) return;
|
|
1608
|
-
const members = activatedMembers(r.registry, r.dedup, r.maxFanout);
|
|
1606
|
+
const members = activatedMembers(r.registry, r.dedup, r.maxFanout, r.overflow);
|
|
1607
|
+
if (r.overflow === 'truncate') {
|
|
1608
|
+
traceOnFanoutTruncated(ctx.trace, {
|
|
1609
|
+
stage: r.fanout,
|
|
1610
|
+
registrySize: r.registry.length,
|
|
1611
|
+
dispatched: members.length,
|
|
1612
|
+
reason: r.truncateReason ?? '',
|
|
1613
|
+
});
|
|
1614
|
+
flush(ctx);
|
|
1615
|
+
}
|
|
1609
1616
|
for (const k of members) {
|
|
1610
1617
|
if (!safeItemKey(k)) {
|
|
1611
1618
|
throw new RunFailure(
|