@jinn-network/jinn-layer 0.1.0
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/adapters/contribution-adapter.d.ts +18 -0
- package/dist/adapters/contribution-adapter.js +134 -0
- package/dist/adapters/corpus-adapter.d.ts +12 -0
- package/dist/adapters/corpus-adapter.js +212 -0
- package/dist/adapters/index.d.ts +17 -0
- package/dist/adapters/index.js +17 -0
- package/dist/adapters/json-map-store.d.ts +4 -0
- package/dist/adapters/json-map-store.js +31 -0
- package/dist/adapters/local-learning-adapter.d.ts +25 -0
- package/dist/adapters/local-learning-adapter.js +49 -0
- package/dist/adapters/skills-adapter.d.ts +8 -0
- package/dist/adapters/skills-adapter.js +55 -0
- package/dist/admission-policy.d.ts +38 -0
- package/dist/admission-policy.js +17 -0
- package/dist/bin/jinn-distill-mcp.d.ts +2 -0
- package/dist/bin/jinn-distill-mcp.js +10 -0
- package/dist/bin/jinn-layer.d.ts +7 -0
- package/dist/bin/jinn-layer.js +15 -0
- package/dist/bridge-fetch-evidence.d.ts +102 -0
- package/dist/bridge-fetch-evidence.js +677 -0
- package/dist/bridge-verdict-source.d.ts +47 -0
- package/dist/bridge-verdict-source.js +216 -0
- package/dist/bridge.d.ts +247 -0
- package/dist/bridge.js +439 -0
- package/dist/capture.d.ts +98 -0
- package/dist/capture.js +617 -0
- package/dist/cli.d.ts +148 -0
- package/dist/cli.js +2221 -0
- package/dist/cluster-selection.d.ts +34 -0
- package/dist/cluster-selection.js +135 -0
- package/dist/cluster.d.ts +89 -0
- package/dist/cluster.js +142 -0
- package/dist/consume.d.ts +139 -0
- package/dist/consume.js +280 -0
- package/dist/corpus-probes.d.ts +61 -0
- package/dist/corpus-probes.js +120 -0
- package/dist/corpus-store.d.ts +16 -0
- package/dist/corpus-store.js +101 -0
- package/dist/distill-captures.d.ts +56 -0
- package/dist/distill-captures.js +283 -0
- package/dist/distill-feedback.d.ts +20 -0
- package/dist/distill-feedback.js +30 -0
- package/dist/distill-llm.d.ts +138 -0
- package/dist/distill-llm.js +438 -0
- package/dist/distill-mcp-server.d.ts +30 -0
- package/dist/distill-mcp-server.js +281 -0
- package/dist/distill-mode.d.ts +62 -0
- package/dist/distill-mode.js +107 -0
- package/dist/distill-progress.d.ts +78 -0
- package/dist/distill-progress.js +69 -0
- package/dist/distill-prompt.d.ts +34 -0
- package/dist/distill-prompt.js +85 -0
- package/dist/distill-render.d.ts +123 -0
- package/dist/distill-render.js +303 -0
- package/dist/distill-runs.d.ts +28 -0
- package/dist/distill-runs.js +39 -0
- package/dist/distill-traces.d.ts +67 -0
- package/dist/distill-traces.js +211 -0
- package/dist/distill.d.ts +180 -0
- package/dist/distill.js +337 -0
- package/dist/distiller.d.ts +118 -0
- package/dist/distiller.js +142 -0
- package/dist/envelope.d.ts +9 -0
- package/dist/envelope.js +9 -0
- package/dist/eval-prep.d.ts +129 -0
- package/dist/eval-prep.js +523 -0
- package/dist/execution-publish.d.ts +62 -0
- package/dist/execution-publish.js +72 -0
- package/dist/gate.d.ts +39 -0
- package/dist/gate.js +106 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.js +50 -0
- package/dist/ipfs-cid.d.ts +18 -0
- package/dist/ipfs-cid.js +149 -0
- package/dist/layer-default.d.ts +3 -0
- package/dist/layer-default.js +10 -0
- package/dist/ledger.d.ts +62 -0
- package/dist/ledger.js +104 -0
- package/dist/measurement.d.ts +90 -0
- package/dist/measurement.js +134 -0
- package/dist/path-safety.d.ts +19 -0
- package/dist/path-safety.js +303 -0
- package/dist/pipeline.d.ts +94 -0
- package/dist/pipeline.js +124 -0
- package/dist/plugin-wiring.d.ts +4 -0
- package/dist/plugin-wiring.js +54 -0
- package/dist/preview.d.ts +31 -0
- package/dist/preview.js +28 -0
- package/dist/process-contract.d.ts +303 -0
- package/dist/process-contract.js +118 -0
- package/dist/publish-skill.d.ts +86 -0
- package/dist/publish-skill.js +116 -0
- package/dist/publish.d.ts +294 -0
- package/dist/publish.js +1090 -0
- package/dist/scripts/scan-skills-registry.d.ts +24 -0
- package/dist/scripts/scan-skills-registry.js +207 -0
- package/dist/seed-import/curated-batch.d.ts +36 -0
- package/dist/seed-import/curated-batch.js +103 -0
- package/dist/seed-import/episode-execute.d.ts +50 -0
- package/dist/seed-import/episode-execute.js +235 -0
- package/dist/seed-import/episode-fetch.d.ts +119 -0
- package/dist/seed-import/episode-fetch.js +137 -0
- package/dist/seed-import/episode-plan.d.ts +11 -0
- package/dist/seed-import/episode-plan.js +27 -0
- package/dist/seed-import/episode-report.d.ts +39 -0
- package/dist/seed-import/episode-report.js +36 -0
- package/dist/seed-import/execute.d.ts +61 -0
- package/dist/seed-import/execute.js +245 -0
- package/dist/seed-import/fetch.d.ts +62 -0
- package/dist/seed-import/fetch.js +82 -0
- package/dist/seed-import/licence.d.ts +17 -0
- package/dist/seed-import/licence.js +36 -0
- package/dist/seed-import/plan.d.ts +10 -0
- package/dist/seed-import/plan.js +21 -0
- package/dist/seed-import/report.d.ts +34 -0
- package/dist/seed-import/report.js +31 -0
- package/dist/seed-import/state.d.ts +72 -0
- package/dist/seed-import/state.js +120 -0
- package/dist/signal.d.ts +36 -0
- package/dist/signal.js +48 -0
- package/dist/signing.d.ts +8 -0
- package/dist/signing.js +21 -0
- package/dist/skill-package.d.ts +70 -0
- package/dist/skill-package.js +141 -0
- package/dist/skill.d.ts +21 -0
- package/dist/skill.js +77 -0
- package/dist/snapshot-transcript.d.ts +54 -0
- package/dist/snapshot-transcript.js +117 -0
- package/dist/three-arm-run.d.ts +27 -0
- package/dist/three-arm-run.js +41 -0
- package/package.json +60 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bridge's verdict-row source (spec/2026-07-06-distillation-v1.md §8, D10).
|
|
3
|
+
*
|
|
4
|
+
* Reads swe-rebench-v2 verdicts from the Ponder indexer's GraphQL — BOTH
|
|
5
|
+
* polarities (D10): evaluator-confirmed passes (→ pattern-eligible) and
|
|
6
|
+
* evaluator-confirmed failures (→ lesson-eligible) — and returns them as
|
|
7
|
+
* `AttemptRef[]` for `bridgeAttempts()`.
|
|
8
|
+
*
|
|
9
|
+
* A single paged GraphQL query over `verdictEnvelopeMetas`:
|
|
10
|
+
* - filtered to swe-rebench-v2 / enriched-ok;
|
|
11
|
+
* - keep only PASS and FAIL polarities (INVALID / INDETERMINATE / UNKNOWN are
|
|
12
|
+
* noise, not lessons — dropped, as is any actualPassed/verdict disagreement);
|
|
13
|
+
* - carry each verdict row's own `manifestCid` as `verdictManifestCid` — the
|
|
14
|
+
* entry point of the VERIFIED verdict→solution join done by
|
|
15
|
+
* `bridge-fetch-evidence.ts` (verdict envelope → task doc →
|
|
16
|
+
* authoritative verdict → attempt tuple → attemptEnvelopeMeta → solution
|
|
17
|
+
* envelope patch).
|
|
18
|
+
*
|
|
19
|
+
* The predecessor's `attemptEnvelopeMeta(requestId=verdict.requestId)` join is
|
|
20
|
+
* gone: probed live (see client/scripts/distill-run-live.ts), that key does not
|
|
21
|
+
* match, so the join returned empty. The real link runs through the task doc's
|
|
22
|
+
* chain-indexed verdict/attempt tuple, resolved lazily in the evidence fetcher.
|
|
23
|
+
*
|
|
24
|
+
* I/O is injected (`fetchImpl`) so the module is unit-testable without a live
|
|
25
|
+
* indexer, mirroring `queryCaptureMeta` in ./consume.ts.
|
|
26
|
+
*/
|
|
27
|
+
import type { AttemptRef } from './bridge.js';
|
|
28
|
+
export declare class IncompleteVerdictWalkError extends Error {
|
|
29
|
+
constructor(message: string);
|
|
30
|
+
}
|
|
31
|
+
export interface VerdictSourceOptions {
|
|
32
|
+
/** Ponder GraphQL endpoint, e.g. https://my-indexer.example/graphql */
|
|
33
|
+
graphqlUrl: string;
|
|
34
|
+
/** Injectable fetch (tests). Defaults to globalThis.fetch. */
|
|
35
|
+
fetchImpl?: typeof fetch;
|
|
36
|
+
}
|
|
37
|
+
export interface VerdictSource {
|
|
38
|
+
/** List swe-rebench-v2 verdict rows (both polarities) as `AttemptRef[]`. */
|
|
39
|
+
list(args?: {
|
|
40
|
+
limit?: number;
|
|
41
|
+
}): Promise<AttemptRef[]>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Build a verdict-row source over a Ponder GraphQL endpoint. No live I/O until
|
|
45
|
+
* `list()` is called.
|
|
46
|
+
*/
|
|
47
|
+
export declare function createVerdictSource(opts: VerdictSourceOptions): VerdictSource;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bridge's verdict-row source (spec/2026-07-06-distillation-v1.md §8, D10).
|
|
3
|
+
*
|
|
4
|
+
* Reads swe-rebench-v2 verdicts from the Ponder indexer's GraphQL — BOTH
|
|
5
|
+
* polarities (D10): evaluator-confirmed passes (→ pattern-eligible) and
|
|
6
|
+
* evaluator-confirmed failures (→ lesson-eligible) — and returns them as
|
|
7
|
+
* `AttemptRef[]` for `bridgeAttempts()`.
|
|
8
|
+
*
|
|
9
|
+
* A single paged GraphQL query over `verdictEnvelopeMetas`:
|
|
10
|
+
* - filtered to swe-rebench-v2 / enriched-ok;
|
|
11
|
+
* - keep only PASS and FAIL polarities (INVALID / INDETERMINATE / UNKNOWN are
|
|
12
|
+
* noise, not lessons — dropped, as is any actualPassed/verdict disagreement);
|
|
13
|
+
* - carry each verdict row's own `manifestCid` as `verdictManifestCid` — the
|
|
14
|
+
* entry point of the VERIFIED verdict→solution join done by
|
|
15
|
+
* `bridge-fetch-evidence.ts` (verdict envelope → task doc →
|
|
16
|
+
* authoritative verdict → attempt tuple → attemptEnvelopeMeta → solution
|
|
17
|
+
* envelope patch).
|
|
18
|
+
*
|
|
19
|
+
* The predecessor's `attemptEnvelopeMeta(requestId=verdict.requestId)` join is
|
|
20
|
+
* gone: probed live (see client/scripts/distill-run-live.ts), that key does not
|
|
21
|
+
* match, so the join returned empty. The real link runs through the task doc's
|
|
22
|
+
* chain-indexed verdict/attempt tuple, resolved lazily in the evidence fetcher.
|
|
23
|
+
*
|
|
24
|
+
* I/O is injected (`fetchImpl`) so the module is unit-testable without a live
|
|
25
|
+
* indexer, mirroring `queryCaptureMeta` in ./consume.ts.
|
|
26
|
+
*/
|
|
27
|
+
// ── GraphQL query string ──────────────────────────────────────────────────────
|
|
28
|
+
/**
|
|
29
|
+
* Paged verdict rows for swe-rebench-v2. Filters mirror
|
|
30
|
+
* INSTANCE_SUCCESS_COUNTS_QUERY in http.ts, minus the `actualPassed: true`
|
|
31
|
+
* pin — we source both polarities, so polarity is decided client-side from
|
|
32
|
+
* `actualPassed` + `evaluatorVerdict`. Each row carries its own `manifestCid`
|
|
33
|
+
* (the verdict envelope CID) — the entry point of the verdict→solution join.
|
|
34
|
+
*/
|
|
35
|
+
const VERDICTS_QUERY = `
|
|
36
|
+
query BridgeVerdicts($limit: Int!, $after: String) {
|
|
37
|
+
verdictEnvelopeMetas(
|
|
38
|
+
where: {
|
|
39
|
+
solverType_starts_with: "swe-rebench-v2",
|
|
40
|
+
enrichmentStatus: "ok"
|
|
41
|
+
},
|
|
42
|
+
limit: $limit,
|
|
43
|
+
after: $after,
|
|
44
|
+
orderBy: "enrichedAtBlock",
|
|
45
|
+
orderDirection: "asc"
|
|
46
|
+
) {
|
|
47
|
+
items {
|
|
48
|
+
requestId
|
|
49
|
+
chainId
|
|
50
|
+
instanceId
|
|
51
|
+
actualPassed
|
|
52
|
+
evaluatorVerdict
|
|
53
|
+
manifestCid
|
|
54
|
+
}
|
|
55
|
+
pageInfo {
|
|
56
|
+
hasNextPage
|
|
57
|
+
endCursor
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
// ── Constants ──────────────────────────────────────────────────────────────────
|
|
63
|
+
/** Ponder caps plural-query `limit` at 1000. */
|
|
64
|
+
const PAGE_LIMIT = 1000;
|
|
65
|
+
/** Bound on pages walked — 20 × 1000 rows is far beyond any realistic slate. */
|
|
66
|
+
const MAX_PAGES = 20;
|
|
67
|
+
/**
|
|
68
|
+
* A MetadataSet publisher can project arbitrary enrichment identity. Retain a
|
|
69
|
+
* finite number of projections for one authoritative request tuple so an
|
|
70
|
+
* attacker-first row cannot suppress the genuine candidate.
|
|
71
|
+
*/
|
|
72
|
+
const MAX_IDENTITY_CANDIDATES_PER_ATTEMPT = 32;
|
|
73
|
+
export class IncompleteVerdictWalkError extends Error {
|
|
74
|
+
constructor(message) {
|
|
75
|
+
super(message);
|
|
76
|
+
this.name = 'IncompleteVerdictWalkError';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// ── Helpers ─────────────────────────────────────────────────────────────────────
|
|
80
|
+
async function postGql(url, fetchImpl, query, variables) {
|
|
81
|
+
const response = await fetchImpl(url, {
|
|
82
|
+
method: 'POST',
|
|
83
|
+
headers: { 'content-type': 'application/json' },
|
|
84
|
+
body: JSON.stringify({ query, variables }),
|
|
85
|
+
});
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
throw new Error(`verdict-source GraphQL HTTP ${response.status} ${response.statusText}`);
|
|
88
|
+
}
|
|
89
|
+
const json = (await response.json());
|
|
90
|
+
if (json.errors?.length) {
|
|
91
|
+
throw new Error(`verdict-source GraphQL error: ${json.errors.map((e) => e.message ?? 'unknown').join('; ')}`);
|
|
92
|
+
}
|
|
93
|
+
if (!json.data) {
|
|
94
|
+
throw new Error('verdict-source GraphQL response missing data field');
|
|
95
|
+
}
|
|
96
|
+
return json.data;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Map an enriched verdict row to a polarity, or `null` when it is neither a
|
|
100
|
+
* clean PASS nor a clean FAIL (INVALID / INDETERMINATE / UNKNOWN, or any
|
|
101
|
+
* disagreement between `actualPassed` and `evaluatorVerdict`) — those are noise,
|
|
102
|
+
* not lessons, and are dropped.
|
|
103
|
+
*/
|
|
104
|
+
function polarityOf(row) {
|
|
105
|
+
if (row.actualPassed === true && row.evaluatorVerdict === 'PASS')
|
|
106
|
+
return 'pass';
|
|
107
|
+
if (row.actualPassed === false && row.evaluatorVerdict === 'FAIL')
|
|
108
|
+
return 'fail';
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
function requireVerdictsPage(data) {
|
|
112
|
+
if (typeof data !== 'object' || data === null) {
|
|
113
|
+
throw new IncompleteVerdictWalkError('incomplete verdict walk: malformed GraphQL data payload');
|
|
114
|
+
}
|
|
115
|
+
const verdictEnvelopeMetas = data['verdictEnvelopeMetas'];
|
|
116
|
+
if (typeof verdictEnvelopeMetas !== 'object' || verdictEnvelopeMetas === null) {
|
|
117
|
+
throw new IncompleteVerdictWalkError('incomplete verdict walk: missing or malformed verdictEnvelopeMetas');
|
|
118
|
+
}
|
|
119
|
+
const items = verdictEnvelopeMetas['items'];
|
|
120
|
+
if (!Array.isArray(items)) {
|
|
121
|
+
throw new IncompleteVerdictWalkError('incomplete verdict walk: verdictEnvelopeMetas.items is not an array');
|
|
122
|
+
}
|
|
123
|
+
const pageInfo = verdictEnvelopeMetas['pageInfo'];
|
|
124
|
+
if (typeof pageInfo !== 'object' || pageInfo === null) {
|
|
125
|
+
throw new IncompleteVerdictWalkError('incomplete verdict walk: missing or malformed verdictEnvelopeMetas.pageInfo');
|
|
126
|
+
}
|
|
127
|
+
const hasNextPage = pageInfo['hasNextPage'];
|
|
128
|
+
const endCursor = pageInfo['endCursor'];
|
|
129
|
+
if (typeof hasNextPage !== 'boolean'
|
|
130
|
+
|| (endCursor !== null && typeof endCursor !== 'string')) {
|
|
131
|
+
throw new IncompleteVerdictWalkError('incomplete verdict walk: malformed verdictEnvelopeMetas.pageInfo fields');
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
items: items,
|
|
135
|
+
pageInfo: { hasNextPage, endCursor },
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Build a verdict-row source over a Ponder GraphQL endpoint. No live I/O until
|
|
140
|
+
* `list()` is called.
|
|
141
|
+
*/
|
|
142
|
+
export function createVerdictSource(opts) {
|
|
143
|
+
const url = opts.graphqlUrl.endsWith('/graphql') ? opts.graphqlUrl : `${opts.graphqlUrl}/graphql`;
|
|
144
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
145
|
+
if (!fetchImpl) {
|
|
146
|
+
throw new Error('No fetch implementation available; pass fetchImpl in options');
|
|
147
|
+
}
|
|
148
|
+
async function list(args = {}) {
|
|
149
|
+
if (args.limit !== undefined
|
|
150
|
+
&& (!Number.isSafeInteger(args.limit) || args.limit <= 0)) {
|
|
151
|
+
throw new RangeError('verdict-source limit must be a positive safe integer');
|
|
152
|
+
}
|
|
153
|
+
const limit = args.limit ?? Number.POSITIVE_INFINITY;
|
|
154
|
+
// Page verdict rows (both polarities), dropping non-PASS/FAIL and any row
|
|
155
|
+
// missing its verdict `manifestCid` (no join entry point → nothing to bridge).
|
|
156
|
+
const refs = [];
|
|
157
|
+
const refsByRequestPolarity = new Map();
|
|
158
|
+
let cursor = null;
|
|
159
|
+
for (let page = 0; page < MAX_PAGES; page++) {
|
|
160
|
+
const data = await postGql(url, fetchImpl, VERDICTS_QUERY, {
|
|
161
|
+
limit: PAGE_LIMIT,
|
|
162
|
+
after: cursor,
|
|
163
|
+
});
|
|
164
|
+
const verdictsPage = requireVerdictsPage(data);
|
|
165
|
+
for (const row of verdictsPage.items) {
|
|
166
|
+
const polarity = polarityOf(row);
|
|
167
|
+
if (polarity === null)
|
|
168
|
+
continue; // INVALID/INDETERMINATE/UNKNOWN — dropped.
|
|
169
|
+
if (!row.manifestCid)
|
|
170
|
+
continue; // no verdict envelope CID → no join entry point.
|
|
171
|
+
const candidateKey = `${row.chainId}:${row.requestId.toLowerCase()}:${polarity}`;
|
|
172
|
+
const discoveryCandidate = {
|
|
173
|
+
instanceId: typeof row.instanceId === 'string' ? row.instanceId : '',
|
|
174
|
+
verdictManifestCid: row.manifestCid,
|
|
175
|
+
};
|
|
176
|
+
const existing = refsByRequestPolarity.get(candidateKey);
|
|
177
|
+
if (existing) {
|
|
178
|
+
if (existing.discoveryCandidates
|
|
179
|
+
&& existing.discoveryCandidates.length < MAX_IDENTITY_CANDIDATES_PER_ATTEMPT) {
|
|
180
|
+
existing.discoveryCandidates.push(discoveryCandidate);
|
|
181
|
+
}
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
// `limit` bounds authoritative attempt tuples, not permissionless
|
|
185
|
+
// candidate rows. Continue walking the bounded result set after the
|
|
186
|
+
// tuple cap so later candidates for selected tuples survive.
|
|
187
|
+
if (refs.length >= limit)
|
|
188
|
+
continue;
|
|
189
|
+
const attempt = {
|
|
190
|
+
requestId: row.requestId,
|
|
191
|
+
chainId: row.chainId,
|
|
192
|
+
instanceId: discoveryCandidate.instanceId,
|
|
193
|
+
// The attempt-join that once supplied the model is gone; the bridge
|
|
194
|
+
// falls back to 'unknown' when model is empty.
|
|
195
|
+
model: '',
|
|
196
|
+
// Filled by the evidence fetcher's authoritative join off verdictManifestCid.
|
|
197
|
+
manifestCid: '',
|
|
198
|
+
polarity,
|
|
199
|
+
verdictManifestCid: row.manifestCid,
|
|
200
|
+
discoveryCandidates: [discoveryCandidate],
|
|
201
|
+
};
|
|
202
|
+
refsByRequestPolarity.set(candidateKey, attempt);
|
|
203
|
+
refs.push(attempt);
|
|
204
|
+
}
|
|
205
|
+
const pageInfo = verdictsPage.pageInfo;
|
|
206
|
+
if (!pageInfo.hasNextPage)
|
|
207
|
+
return refs;
|
|
208
|
+
if (!pageInfo.endCursor) {
|
|
209
|
+
throw new IncompleteVerdictWalkError('incomplete verdict walk: indexer advertised another page without an end cursor');
|
|
210
|
+
}
|
|
211
|
+
cursor = pageInfo.endCursor;
|
|
212
|
+
}
|
|
213
|
+
throw new IncompleteVerdictWalkError(`incomplete verdict walk: indexer still advertised rows after ${MAX_PAGES} pages`);
|
|
214
|
+
}
|
|
215
|
+
return { list };
|
|
216
|
+
}
|
package/dist/bridge.d.ts
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bridge — SolverNet execution ledger → layer-1 evidence
|
|
3
|
+
* (spec/2026-07-06-distillation-v1.md §8, D5/D10).
|
|
4
|
+
*
|
|
5
|
+
* Turns swe-rebench attempts into canonical `jinn.episode.v1` evidence via
|
|
6
|
+
* the existing `capture()`→`publish()` pipe, scrubbed at the **layer-2
|
|
7
|
+
* (secret-only) altitude** (D6 — a verified swe-rebench solve is public-repo
|
|
8
|
+
* work, not raw private-machine activity). It sources **both polarities** (D10):
|
|
9
|
+
* verified passes → pattern-eligible evidence; evaluator-confirmed failures →
|
|
10
|
+
* lesson-eligible evidence. Held-out `cap-v0` slate instances are excluded by
|
|
11
|
+
* `instance_id` AND repo; each `(instance_id, polarity)` is retained up to
|
|
12
|
+
* `groupCap` attempts (default 1) so the per-instance attempt group survives for
|
|
13
|
+
* group-relative distillation (#1478).
|
|
14
|
+
*
|
|
15
|
+
* This module is the bridge **core + the publish adapter**. The ledger row
|
|
16
|
+
* source (indexer GraphQL over `verdictEnvelopeMeta`, both polarities) and the
|
|
17
|
+
* IPFS/corpus evidence fetch are injected ports (`BridgeDeps`) — the production
|
|
18
|
+
* adapters are thin and wired at run time; the logic here is what carries the
|
|
19
|
+
* tests.
|
|
20
|
+
*/
|
|
21
|
+
import { type CapturedTask, type PendingEnvelope } from './capture.js';
|
|
22
|
+
import { type HarnessPublishDeps, type ManifestBatchPublishDeps, type ManifestBatchResult, type ManifestBatchSetResult } from './publish.js';
|
|
23
|
+
import { type ScrubPipeline } from '@jinn-network/core/scrub';
|
|
24
|
+
import type { TranscriptSpanInput } from '@jinn-network/core/trajectory';
|
|
25
|
+
/** A verdict row from the execution ledger, one polarity. */
|
|
26
|
+
export interface AttemptRef {
|
|
27
|
+
requestId: string;
|
|
28
|
+
chainId: number;
|
|
29
|
+
/**
|
|
30
|
+
* Enrichment-projected swe-rebench `instance_id` (owner__repo-N). This is a
|
|
31
|
+
* discovery hint, never authoritative task identity.
|
|
32
|
+
*/
|
|
33
|
+
instanceId: string;
|
|
34
|
+
/** Model the attempt ran under (from `attemptEnvelopeMeta.model`). */
|
|
35
|
+
model: string;
|
|
36
|
+
/** The attempt envelope CID — where the solution patch + task descriptor live. */
|
|
37
|
+
manifestCid: string;
|
|
38
|
+
/** `pass` = verified solve (→ pattern); `fail` = evaluator-confirmed FAIL (→ lesson). */
|
|
39
|
+
polarity: 'pass' | 'fail';
|
|
40
|
+
/**
|
|
41
|
+
* The VERDICT envelope CID — the entry point of the verified join to the
|
|
42
|
+
* solver's solution patch. The task document supplies authenticated task
|
|
43
|
+
* facts only; the chain-scoped verdict → attempt tuple supplies the solution
|
|
44
|
+
* requestId, which resolves one attemptEnvelopeMeta → solution envelope. The
|
|
45
|
+
* verdict-source populates it; callers that already carry a resolved
|
|
46
|
+
* `manifestCid` (e.g. the corpus fetcher) do not need it.
|
|
47
|
+
*/
|
|
48
|
+
verdictManifestCid?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Bounded, permissionless enrichment projections retained for one
|
|
51
|
+
* `(requestId, chainId, polarity)` tuple. The bridge tries them only to let
|
|
52
|
+
* the evidence fetcher reach signed task facts; none may drive exclusion,
|
|
53
|
+
* grouping, or publication identity.
|
|
54
|
+
*/
|
|
55
|
+
discoveryCandidates?: Array<{
|
|
56
|
+
instanceId: string;
|
|
57
|
+
verdictManifestCid: string;
|
|
58
|
+
}>;
|
|
59
|
+
/** Echo/lineage key — instances sharing a key are not independent corroboration. */
|
|
60
|
+
sourceLineageKey?: string;
|
|
61
|
+
lookupFlagged?: boolean;
|
|
62
|
+
/** Optional task provenance carried by richer verdict-row sources. */
|
|
63
|
+
repo?: string;
|
|
64
|
+
baseCommit?: string;
|
|
65
|
+
}
|
|
66
|
+
/** Evidence fetched for one attempt (the injected IPFS/corpus port). */
|
|
67
|
+
export interface BridgeEvidence {
|
|
68
|
+
/** The instance problem statement (scrubbed downstream by capture's layer-2 pipeline). */
|
|
69
|
+
taskSummary: string;
|
|
70
|
+
/** The unified-diff patch the attempt produced. */
|
|
71
|
+
patch: string;
|
|
72
|
+
/**
|
|
73
|
+
* Authenticated task tuple returned by the source resolver. These remain
|
|
74
|
+
* optional on the fetch-only transport shape, but all four are mandatory at
|
|
75
|
+
* the evaluator-verified publication boundary.
|
|
76
|
+
*/
|
|
77
|
+
repo?: string;
|
|
78
|
+
baseCommit?: string;
|
|
79
|
+
taskCreatedAt?: number;
|
|
80
|
+
instanceId?: string;
|
|
81
|
+
generatorModel?: CapturedTask['environment']['generatorModel'];
|
|
82
|
+
distributionClass?: CapturedTask['environment']['distributionClass'];
|
|
83
|
+
/** Authenticated verifier facts are all-or-nothing; partial facts are never published. */
|
|
84
|
+
verifier?: {
|
|
85
|
+
failToPass: string[];
|
|
86
|
+
passToPass: string[];
|
|
87
|
+
evalSemanticsVersion: string;
|
|
88
|
+
};
|
|
89
|
+
/** Authenticated source artifacts retained for audit and lineage. */
|
|
90
|
+
verdictEnvelopeCid?: string;
|
|
91
|
+
solutionEnvelopeCid?: string;
|
|
92
|
+
rawSnapshotRef?: string;
|
|
93
|
+
/**
|
|
94
|
+
* The solver's canonical typed decision-path spans, derived from the raw
|
|
95
|
+
* harness transcript inside the solution's `system_snapshot` artifact
|
|
96
|
+
* (`.claude-code/stdout.jsonl` / `.codex-code/stdout.jsonl` — §8, #1472;
|
|
97
|
+
* `jinn.trajectory.v1` carries no reasoning until #1473). The bridge uses the
|
|
98
|
+
* same parser implementation as the live path. When absent (Hermes,
|
|
99
|
+
* missing/corrupt snapshot), the layer-1 record is
|
|
100
|
+
* tagged `patch-only` (see `toBridgeCapturedTask`) so measurement can
|
|
101
|
+
* stratify by evidence richness. Rides a step attribute → scrubbed by the
|
|
102
|
+
* same layer-2 pipeline as every other attribute; it does not bypass scrub.
|
|
103
|
+
*/
|
|
104
|
+
trajectorySpans?: TranscriptSpanInput[];
|
|
105
|
+
}
|
|
106
|
+
export interface BridgeDeps {
|
|
107
|
+
/** The held-out `cap-v0` slate instance ids (§12). Repo exclusion is derived from these. */
|
|
108
|
+
slateInstanceIds: Set<string>;
|
|
109
|
+
/** Fetch the patch + task descriptor for an attempt (IPFS/corpus). */
|
|
110
|
+
fetchEvidence: (ref: AttemptRef) => Promise<BridgeEvidence>;
|
|
111
|
+
/** Publish a constructed CapturedTask as layer-1 evidence; returns the corpus ref. */
|
|
112
|
+
publishEvidence: (task: CapturedTask, ref: AttemptRef) => Promise<{
|
|
113
|
+
envelopeRef: string;
|
|
114
|
+
anchorTx: string | null;
|
|
115
|
+
}>;
|
|
116
|
+
/** Bulk batches opt in explicitly; contributed/retrieval records stay per-record by default. */
|
|
117
|
+
anchorMode?: 'per-record' | 'manifest';
|
|
118
|
+
/** One shared manifest publication for every surviving task (manifest mode only). */
|
|
119
|
+
publishManifestBatch?: (candidates: Array<{
|
|
120
|
+
task: CapturedTask;
|
|
121
|
+
ref: AttemptRef;
|
|
122
|
+
}>) => Promise<ManifestBatchSetResult>;
|
|
123
|
+
/** Weak-suite instance ids (discrimination fail) — excluded from bridge input. */
|
|
124
|
+
weakSuiteInstanceIds?: Set<string>;
|
|
125
|
+
/** Lookup-flagged instance ids — excluded from distillation input. */
|
|
126
|
+
lookupFlaggedInstanceIds?: Set<string>;
|
|
127
|
+
/**
|
|
128
|
+
* Attempts retained per `(instance_id, polarity)` — the per-instance attempt
|
|
129
|
+
* GROUP is the distiller's raw material for group-relative distillation
|
|
130
|
+
* (#1478). Default 1 keeps the single-exemplar behavior for standalone
|
|
131
|
+
* callers; the pipeline passes a higher cap so clustering receives the whole
|
|
132
|
+
* group. Rows beyond the cap are recorded in `BridgeResult.deduped`, and
|
|
133
|
+
* since refs arrive in a stable order the kept K are deterministic.
|
|
134
|
+
*/
|
|
135
|
+
groupCap?: number;
|
|
136
|
+
now?: () => Date;
|
|
137
|
+
}
|
|
138
|
+
export interface BridgeManifestBatch {
|
|
139
|
+
batchKey?: string;
|
|
140
|
+
manifestCid: string;
|
|
141
|
+
memberRefs: string[];
|
|
142
|
+
root: `0x${string}`;
|
|
143
|
+
anchorTx: `0x${string}` | null;
|
|
144
|
+
gasUsed: bigint | null;
|
|
145
|
+
feeWei: bigint | null;
|
|
146
|
+
control?: ManifestBatchResult['control'];
|
|
147
|
+
confirmed: boolean;
|
|
148
|
+
}
|
|
149
|
+
export interface BridgeResult {
|
|
150
|
+
bridged: Array<{
|
|
151
|
+
instanceId: string;
|
|
152
|
+
polarity: 'pass' | 'fail';
|
|
153
|
+
envelopeRef: string;
|
|
154
|
+
anchorTx: string | null;
|
|
155
|
+
}>;
|
|
156
|
+
excludedHeldOut: Array<{
|
|
157
|
+
instanceId: string;
|
|
158
|
+
reason: 'instance_id' | 'repo';
|
|
159
|
+
}>;
|
|
160
|
+
/** Attempts dropped because their `(instance_id, polarity)` group already held `groupCap` (#1478). */
|
|
161
|
+
deduped: Array<{
|
|
162
|
+
instanceId: string;
|
|
163
|
+
polarity: 'pass' | 'fail';
|
|
164
|
+
}>;
|
|
165
|
+
errors: Array<{
|
|
166
|
+
requestId: string;
|
|
167
|
+
error: string;
|
|
168
|
+
}>;
|
|
169
|
+
/**
|
|
170
|
+
* Slate `instance_id`s whose repo could NOT be derived (`repoFromInstanceId`
|
|
171
|
+
* → null). Repo-axis exclusion (§12) is incomplete for these: a same-repo
|
|
172
|
+
* sibling of such a slate id would NOT be caught by the derived-repo guard,
|
|
173
|
+
* only by an exact `instance_id` match. Surfaced (not silently dropped) so an
|
|
174
|
+
* operator sees the contamination-boundary gap; empty once cap-v0 carries
|
|
175
|
+
* repos explicitly.
|
|
176
|
+
*/
|
|
177
|
+
unresolvedSlateIds: string[];
|
|
178
|
+
/** Shared batch facts, populated only in manifest mode. */
|
|
179
|
+
manifestCid?: string;
|
|
180
|
+
/** One observation per manifest partition, including recovery-only anchors. */
|
|
181
|
+
manifestBatches?: BridgeManifestBatch[];
|
|
182
|
+
/** Uploaded member refs, retained even when post-anchor local finalization fails. */
|
|
183
|
+
manifestMemberRefs?: string[];
|
|
184
|
+
/** Durable recovery identity for the manifest batch, when journaling is active. */
|
|
185
|
+
manifestBatchKey?: string;
|
|
186
|
+
anchorTx?: string | null;
|
|
187
|
+
gasUsed?: bigint | null;
|
|
188
|
+
feeWei?: bigint | null;
|
|
189
|
+
control?: ManifestBatchResult['control'];
|
|
190
|
+
/** False means a tx was broadcast but not confirmed successful. */
|
|
191
|
+
manifestConfirmed?: boolean;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Derive `owner/repo` from a SWE-bench `instance_id` (`owner__repo-<pr#>`),
|
|
195
|
+
* or null when the id does not match the convention. cap-v0 will carry repos
|
|
196
|
+
* explicitly; until then repo exclusion (§12) is derived here.
|
|
197
|
+
*/
|
|
198
|
+
export declare function repoFromInstanceId(instanceId: string): string | null;
|
|
199
|
+
/**
|
|
200
|
+
* Construct the layer-1 `CapturedTask` for one attempt (both polarities, D10).
|
|
201
|
+
*
|
|
202
|
+
* The full patch goes into the `apply_patch` step attribute. Note: `capture()`
|
|
203
|
+
* caps step attributes at `MAX_STEP_ATTRIBUTES_BYTES` (16 KiB) and truncates the
|
|
204
|
+
* largest value with a receipted `…[truncated]` suffix, so a patch over that
|
|
205
|
+
* bound is stored truncated (inherited from the frozen pipe, D5 — not
|
|
206
|
+
* re-implemented here). Real SWE-bench patches can exceed 16 KiB; the truncation
|
|
207
|
+
* is recorded in the envelope's step receipt, not silent.
|
|
208
|
+
*
|
|
209
|
+
* Evidence-richness (§8): canonical typed transcript spans are inserted
|
|
210
|
+
* between the patch and verdict steps. When no usable spans exist, a
|
|
211
|
+
* `patch-only` tag is appended to `distributionTags`.
|
|
212
|
+
*/
|
|
213
|
+
export declare function toBridgeCapturedTask(ref: AttemptRef, ev: BridgeEvidence, now: Date): CapturedTask;
|
|
214
|
+
/**
|
|
215
|
+
* Bridge a batch of ledger verdict rows into layer-1 evidence. Permissionless
|
|
216
|
+
* enrichment projections are used only to discover authenticated evidence.
|
|
217
|
+
* Exclusion and dedup use the signed task identity returned by `fetchEvidence`.
|
|
218
|
+
*/
|
|
219
|
+
export declare function bridgeAttempts(refs: AttemptRef[], deps: BridgeDeps): Promise<BridgeResult>;
|
|
220
|
+
/**
|
|
221
|
+
* The production `publishEvidence`: run the CapturedTask through `capture()`
|
|
222
|
+
* with the **layer-2 (secret-only) pipeline** injected (D6), then `publish()` —
|
|
223
|
+
* emitting a canonical `jinn.episode.v1` artifact. Reuses the scrubbed pipe; the only
|
|
224
|
+
* departure from a native capture is the scrub altitude.
|
|
225
|
+
*/
|
|
226
|
+
export declare function buildBridgeEvidencePublisher(deps: HarnessPublishDeps, opts?: {
|
|
227
|
+
pipeline?: ScrubPipeline;
|
|
228
|
+
}): (task: CapturedTask, ref: AttemptRef) => Promise<{
|
|
229
|
+
envelopeRef: string;
|
|
230
|
+
anchorTx: string | null;
|
|
231
|
+
}>;
|
|
232
|
+
/**
|
|
233
|
+
* Production manifest publisher: capture every bridge task with the shared
|
|
234
|
+
* layer-2 scrub pipeline, then hand all pending envelopes to the one-anchor
|
|
235
|
+
* batch publisher with their discovery hints.
|
|
236
|
+
*/
|
|
237
|
+
export declare function buildBridgeManifestPublisher(deps: ManifestBatchPublishDeps, opts?: {
|
|
238
|
+
pipeline?: ScrubPipeline;
|
|
239
|
+
measurePerRecordControl?: boolean;
|
|
240
|
+
onCaptured?: (pending: PendingEnvelope, candidate: {
|
|
241
|
+
task: CapturedTask;
|
|
242
|
+
ref: AttemptRef;
|
|
243
|
+
}) => void;
|
|
244
|
+
}): (candidates: Array<{
|
|
245
|
+
task: CapturedTask;
|
|
246
|
+
ref: AttemptRef;
|
|
247
|
+
}>) => Promise<ManifestBatchSetResult>;
|