@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.
Files changed (131) hide show
  1. package/dist/adapters/contribution-adapter.d.ts +18 -0
  2. package/dist/adapters/contribution-adapter.js +134 -0
  3. package/dist/adapters/corpus-adapter.d.ts +12 -0
  4. package/dist/adapters/corpus-adapter.js +212 -0
  5. package/dist/adapters/index.d.ts +17 -0
  6. package/dist/adapters/index.js +17 -0
  7. package/dist/adapters/json-map-store.d.ts +4 -0
  8. package/dist/adapters/json-map-store.js +31 -0
  9. package/dist/adapters/local-learning-adapter.d.ts +25 -0
  10. package/dist/adapters/local-learning-adapter.js +49 -0
  11. package/dist/adapters/skills-adapter.d.ts +8 -0
  12. package/dist/adapters/skills-adapter.js +55 -0
  13. package/dist/admission-policy.d.ts +38 -0
  14. package/dist/admission-policy.js +17 -0
  15. package/dist/bin/jinn-distill-mcp.d.ts +2 -0
  16. package/dist/bin/jinn-distill-mcp.js +10 -0
  17. package/dist/bin/jinn-layer.d.ts +7 -0
  18. package/dist/bin/jinn-layer.js +15 -0
  19. package/dist/bridge-fetch-evidence.d.ts +102 -0
  20. package/dist/bridge-fetch-evidence.js +677 -0
  21. package/dist/bridge-verdict-source.d.ts +47 -0
  22. package/dist/bridge-verdict-source.js +216 -0
  23. package/dist/bridge.d.ts +247 -0
  24. package/dist/bridge.js +439 -0
  25. package/dist/capture.d.ts +98 -0
  26. package/dist/capture.js +617 -0
  27. package/dist/cli.d.ts +148 -0
  28. package/dist/cli.js +2221 -0
  29. package/dist/cluster-selection.d.ts +34 -0
  30. package/dist/cluster-selection.js +135 -0
  31. package/dist/cluster.d.ts +89 -0
  32. package/dist/cluster.js +142 -0
  33. package/dist/consume.d.ts +139 -0
  34. package/dist/consume.js +280 -0
  35. package/dist/corpus-probes.d.ts +61 -0
  36. package/dist/corpus-probes.js +120 -0
  37. package/dist/corpus-store.d.ts +16 -0
  38. package/dist/corpus-store.js +101 -0
  39. package/dist/distill-captures.d.ts +56 -0
  40. package/dist/distill-captures.js +283 -0
  41. package/dist/distill-feedback.d.ts +20 -0
  42. package/dist/distill-feedback.js +30 -0
  43. package/dist/distill-llm.d.ts +138 -0
  44. package/dist/distill-llm.js +438 -0
  45. package/dist/distill-mcp-server.d.ts +30 -0
  46. package/dist/distill-mcp-server.js +281 -0
  47. package/dist/distill-mode.d.ts +62 -0
  48. package/dist/distill-mode.js +107 -0
  49. package/dist/distill-progress.d.ts +78 -0
  50. package/dist/distill-progress.js +69 -0
  51. package/dist/distill-prompt.d.ts +34 -0
  52. package/dist/distill-prompt.js +85 -0
  53. package/dist/distill-render.d.ts +123 -0
  54. package/dist/distill-render.js +303 -0
  55. package/dist/distill-runs.d.ts +28 -0
  56. package/dist/distill-runs.js +39 -0
  57. package/dist/distill-traces.d.ts +67 -0
  58. package/dist/distill-traces.js +211 -0
  59. package/dist/distill.d.ts +180 -0
  60. package/dist/distill.js +337 -0
  61. package/dist/distiller.d.ts +118 -0
  62. package/dist/distiller.js +142 -0
  63. package/dist/envelope.d.ts +9 -0
  64. package/dist/envelope.js +9 -0
  65. package/dist/eval-prep.d.ts +129 -0
  66. package/dist/eval-prep.js +523 -0
  67. package/dist/execution-publish.d.ts +62 -0
  68. package/dist/execution-publish.js +72 -0
  69. package/dist/gate.d.ts +39 -0
  70. package/dist/gate.js +106 -0
  71. package/dist/index.d.ts +49 -0
  72. package/dist/index.js +50 -0
  73. package/dist/ipfs-cid.d.ts +18 -0
  74. package/dist/ipfs-cid.js +149 -0
  75. package/dist/layer-default.d.ts +3 -0
  76. package/dist/layer-default.js +10 -0
  77. package/dist/ledger.d.ts +62 -0
  78. package/dist/ledger.js +104 -0
  79. package/dist/measurement.d.ts +90 -0
  80. package/dist/measurement.js +134 -0
  81. package/dist/path-safety.d.ts +19 -0
  82. package/dist/path-safety.js +303 -0
  83. package/dist/pipeline.d.ts +94 -0
  84. package/dist/pipeline.js +124 -0
  85. package/dist/plugin-wiring.d.ts +4 -0
  86. package/dist/plugin-wiring.js +54 -0
  87. package/dist/preview.d.ts +31 -0
  88. package/dist/preview.js +28 -0
  89. package/dist/process-contract.d.ts +303 -0
  90. package/dist/process-contract.js +118 -0
  91. package/dist/publish-skill.d.ts +86 -0
  92. package/dist/publish-skill.js +116 -0
  93. package/dist/publish.d.ts +294 -0
  94. package/dist/publish.js +1090 -0
  95. package/dist/scripts/scan-skills-registry.d.ts +24 -0
  96. package/dist/scripts/scan-skills-registry.js +207 -0
  97. package/dist/seed-import/curated-batch.d.ts +36 -0
  98. package/dist/seed-import/curated-batch.js +103 -0
  99. package/dist/seed-import/episode-execute.d.ts +50 -0
  100. package/dist/seed-import/episode-execute.js +235 -0
  101. package/dist/seed-import/episode-fetch.d.ts +119 -0
  102. package/dist/seed-import/episode-fetch.js +137 -0
  103. package/dist/seed-import/episode-plan.d.ts +11 -0
  104. package/dist/seed-import/episode-plan.js +27 -0
  105. package/dist/seed-import/episode-report.d.ts +39 -0
  106. package/dist/seed-import/episode-report.js +36 -0
  107. package/dist/seed-import/execute.d.ts +61 -0
  108. package/dist/seed-import/execute.js +245 -0
  109. package/dist/seed-import/fetch.d.ts +62 -0
  110. package/dist/seed-import/fetch.js +82 -0
  111. package/dist/seed-import/licence.d.ts +17 -0
  112. package/dist/seed-import/licence.js +36 -0
  113. package/dist/seed-import/plan.d.ts +10 -0
  114. package/dist/seed-import/plan.js +21 -0
  115. package/dist/seed-import/report.d.ts +34 -0
  116. package/dist/seed-import/report.js +31 -0
  117. package/dist/seed-import/state.d.ts +72 -0
  118. package/dist/seed-import/state.js +120 -0
  119. package/dist/signal.d.ts +36 -0
  120. package/dist/signal.js +48 -0
  121. package/dist/signing.d.ts +8 -0
  122. package/dist/signing.js +21 -0
  123. package/dist/skill-package.d.ts +70 -0
  124. package/dist/skill-package.js +141 -0
  125. package/dist/skill.d.ts +21 -0
  126. package/dist/skill.js +77 -0
  127. package/dist/snapshot-transcript.d.ts +54 -0
  128. package/dist/snapshot-transcript.js +117 -0
  129. package/dist/three-arm-run.d.ts +27 -0
  130. package/dist/three-arm-run.js +41 -0
  131. 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
+ }
@@ -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>;