Skip to content

Commit d28506b

Browse files
Gate 10 close Read-Need comprehension
Adds the typed bitcode.read.request record inside Read-Needs, preserves feedback and previous-Need lineage during resynthesis, and exposes ReadNeedComprehensionSynthesis contract traces through route telemetry. Updates Terminal gating so accepted Read-Need truth is recorded with Finding Fits admission evidence, adds focused package and route coverage, and wires the Gate 10 readiness check into V28 gate quality.
1 parent 8b1651a commit d28506b

15 files changed

Lines changed: 423 additions & 15 deletions

.github/workflows/bitcode-gate-quality.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ jobs:
4646
node scripts/check-bitcode-canonical-inputs.mjs --current-target V27
4747
node scripts/check-v28-metadevelopment-readiness.mjs --skip-branch-check
4848
node scripts/check-v28-gate9-depository-evidence.mjs
49+
node scripts/check-v28-gate10-read-need-comprehension.mjs
4950
node scripts/promote-bitcode-canon.mjs --version V28 --commit HEAD --dry-run
5051
5152
- name: Validate source casing and imports

BITCODE_SPEC_V28.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,24 @@ reviewable Need through `ReadNeedComprehensionSynthesis`, allow feedback and
826826
resynthesis, persist accepted Need truth, and block Finding Fits until the Need
827827
is reviewed.
828828

829+
Gate 10 acceptance requires:
830+
831+
- a typed `bitcode.read.request` record embedded in every `bitcode.read.need`
832+
so the raw request remains auditable as request truth rather than fit truth;
833+
- `ReadNeedComprehensionSynthesis` contract trace exposure for every phase,
834+
PTRR agent, PTRR step, and ThricifiedGeneration identifier used by route
835+
telemetry and Terminal stream/history surfaces;
836+
- resynthesis lineage: each resynthesized Need records the prior Need id and
837+
appends operator feedback without losing earlier feedback turns;
838+
- accepted Need truth: accepting a Need computes an acceptance root, records
839+
review state `accepted`, and emits Finding Fits admission evidence;
840+
- strict admission: `ReadFindingFitsSynthesis` receives only an accepted
841+
`bitcode.read.need` when `requireAcceptedReadNeed=true`; raw Read requests
842+
and unaccepted Needs produce blocked readiness;
843+
- Terminal exposes the first two enterprise Reading steps without raw JSON:
844+
synthesize Need, review measurements/proof expectations/feedback, request
845+
resynthesis, or accept the Need before the Fit button becomes ready.
846+
829847
### Gate 11: Finding Fits And Source-Safe AssetPack Preview
830848

831849
Gate 11 owns `ReadFindingFitsSynthesis`: depository search, candidate fit
@@ -1171,8 +1189,9 @@ Gate 4 implementation state:
11711189

11721190
- `/api/read-review` exposes server-side Read-Need synthesis, resynthesis, and
11731191
acceptance actions while preserving the prior Read review boundary. The
1174-
response stores prompt input, interpolated source context, parsed Need,
1175-
measurement root, review state, feedback, and synthesis telemetry.
1192+
response stores prompt input, interpolated source context, typed
1193+
`bitcode.read.request`, parsed Need, measurement root, review state,
1194+
feedback, resynthesis lineage, contract trace, and synthesis telemetry.
11761195
- Read-Need test coverage must stay implementation-guiding rather than
11771196
snapshot-only: it must assert typed Need fields, measurement roots,
11781197
source-disclosure constraints, pricing measurement vectors, acceptance

BITCODE_SPEC_V28_DELTA.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,14 @@ harness deposit reference. The vector document policy is explicit:
198198
`text-embedding-3-small`, 1536 dimensions, `deliverable_vectors`, and
199199
`match_deliverable_vectors`.
200200

201+
Gate 10 introduces the first two enterprise Reading steps as typed protocol
202+
state. `/api/read-review` can synthesize, resynthesize, and accept
203+
Read-Needs; every Need embeds its `bitcode.read.request`, feedback lineage, and
204+
prior Need id when resynthesized. Route telemetry exposes the
205+
ReadNeedComprehensionSynthesis contract trace across phases, PTRR agents,
206+
PTRR steps, and ThricifiedGeneration identifiers. Terminal cannot request
207+
Finding Fits from this path until an accepted `bitcode.read.need` is present.
208+
201209
## Later Version Handoff
202210

203211
- V29: deeper Terminal workflows and transaction operation beyond V28 MVP QA.

BITCODE_SPEC_V28_NOTES.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,26 @@ distance. Gate 9 records the vector document as ready for embedding generation;
570570
later Finding Fits and live validation gates own provider execution, ranking,
571571
settlement, and protected-source disclosure boundaries.
572572

573+
## Gate 10 Read-Need comprehension closure notes
574+
575+
Gate 10 closes the first two enterprise Reading UX steps. The raw request is
576+
stored as `bitcode.read.request` and embedded in the synthesized
577+
`bitcode.read.need`. This keeps request capture auditable without allowing a
578+
raw request to drive depository discovery.
579+
580+
ReadNeedComprehensionSynthesis route telemetry now carries a contract trace for
581+
all request, comprehend, measure, and review phase PTRR agents, their four PTRR
582+
steps, and the three ThricifiedGeneration identifiers under each step. The
583+
route-level telemetry also includes prompt template, interpolated context, raw
584+
structured response posture, parsed typed output, measurement root, review
585+
state, and resynthesis lineage.
586+
587+
Resynthesis appends operator feedback and records the previous Need id. Need
588+
acceptance computes an acceptance root and returns Finding Fits admission
589+
evidence. Terminal uses that accepted Need as the only live harness input for
590+
Finding Fits while keeping the previous measured-Read history path visible for
591+
readback compatibility.
592+
573593
## Non-Goals For V28
574594

575595
- V28 does not implement or QA Exchange product behavior, broad order-book depth, external market routing, wrapper liquidity, or third-party marketplace integration.

BITCODE_SPEC_V28_PARITY_MATRIX.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,8 @@ by a later V28 gate before `version/v28` can be promoted to `main`.
173173
| Carryforward surface | Next gate | Required commercial implementation | Required proof/test coverage | Carryforward closure requirement |
174174
| --- | --- | --- | --- | --- |
175175
| Source Depositing and depository evidence | Gate 9 | Repository inventory, deposit admission, source measurement, depository asset rows, lexical/vector search documents, depositor wallet boundary, source proof roots, execution-history projection, and Terminal deposit history. | Protocol deposit evidence tests, depository-search tests, pipeline harness deposit-root propagation tests, staging-testnet SQL/readback queries, Terminal UX tests, and no demonstration runtime imports. | A user can deposit real source material and see source-bound evidence persisted, searchable, journaled, root-projected into Finding Fits, and owned by the depositor boundary. |
176-
| Read Request capture | Gate 10 | Enterprise Read Request form/state, repository target, source branch/commit context, constraints, non-goals, desired artifact types, and feedback history. | UAPI route tests, Terminal form tests, typed Read Request schema tests, prompt/context interpolation tests. | Raw requests are persisted as requests only; they do not directly trigger Finding Fits until a reviewed Need exists. |
177-
| Read-Need synthesis, review, and resynthesis | Gate 10 | `ReadNeedComprehensionSynthesis` execution over PTRR agents and ThricifiedGenerations, reviewable Need object, operator feedback, resynthesis attempts, acceptance root, and blocked-readiness reasons. | Pipeline contract tests, prompt/return audits, mocked typed-envelope tests, live local OpenAI validation, Terminal stream/accordion tests. | A Reader can accept or resynthesize the Need; accepted Need truth becomes the only input admitted to Finding Fits. |
176+
| Read Request capture | Gate 10 | Enterprise Read Request form/state, repository target, source branch/commit context, constraints, non-goals, desired artifact types, and feedback history, encoded as `bitcode.read.request` inside the Need. | UAPI route tests, Terminal form tests, typed Read Request schema tests, prompt/context interpolation tests, `check:v28-gate10`. | Raw requests are persisted as requests only; they do not directly trigger Finding Fits until a reviewed Need exists. |
177+
| Read-Need synthesis, review, and resynthesis | Gate 10 | `ReadNeedComprehensionSynthesis` execution over PTRR agents and ThricifiedGenerations, contract trace telemetry, reviewable Need object, operator feedback, resynthesis attempts, acceptance root, and blocked-readiness reasons. | Pipeline contract tests, prompt/return audits, mocked typed-envelope tests, route resynthesis tests, live local OpenAI validation, Terminal stream/accordion tests. | A Reader can accept or resynthesize the Need; accepted Need truth becomes the only input admitted to Finding Fits. |
178178
| Finding Fits over the depository | Gate 11 | `ReadFindingFitsSynthesis.discovery` searches lexical/vector deposits, ranks all threshold-passing fits, records fit deposits, query/ranking roots, blockers, and no-worthy-fit outcomes. | Depository search tests, embedding policy tests, tool telemetry tests, staging-testnet readback verifier, mock/live parity tests. | Discovery returns all fit deposits above threshold and never invents source outside the depository. |
179179
| AssetPack synthesis and preview | Gate 11 | Implementation phase uses fit deposits as context, synthesizes AssetPack measurements, source-safe preview, quality score, disclosure policy, and Share-to-Fee quote without protected source leakage. | AssetPack synthesis tests, disclosure-boundary tests, prompt telemetry tests, UI preview tests, source leakage scans. | Reader sees enough measured fit confidence to decide whether to settle, without seeing the protected AssetPack source before payment. |
180180
| BTC settlement and read-license/right transfer | Gate 12 | Fee quote/root, PSBT/finality posture, BTC payment row, BTD range/ownership/license rows, depositor/reader boundary, settlement blockers, and reconciliation. | BTD package tests, API route tests, staging-testnet ledger/database readback, reconciliation tests, wallet/signing failure tests. | Payment transfers read rights/license according to the committed policy and never grants unpaid protected-source visibility. |

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"check:spec-family": "node scripts/check-bitcode-spec-family.mjs",
4141
"check:v28-metadevelopment": "node scripts/check-v28-metadevelopment-readiness.mjs",
4242
"check:v28-gate9": "node scripts/check-v28-gate9-depository-evidence.mjs",
43+
"check:v28-gate10": "node scripts/check-v28-gate10-read-need-comprehension.mjs",
4344
"check:spec-quality": "node scripts/run-bitcode-spec-quality.mjs --mode basic",
4445
"check:spec-quality:title": "node scripts/run-bitcode-spec-quality.mjs --mode strict-from-title",
4546
"check:spec-quality:v24": "node scripts/run-bitcode-spec-quality.mjs --mode strict-version --version V24",

packages/pipelines/asset-pack/src/__tests__/read-need.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,15 @@ describe('Read-Need synthesis and Finding Fits admission', () => {
7272
expect(need.needId).toMatch(/^need-[a-f0-9]{16}$/);
7373
expect(need.reviewState).toBe('needs_acceptance');
7474
expect(need.measurementRoot).toMatch(/^sha256:/);
75+
expect(need.request).toMatchObject({
76+
schema: 'bitcode.read.request',
77+
requestId: 'read-1',
78+
prompt: input.read.prompt,
79+
repositoryFullName: 'engineeredsoftware/ENGI',
80+
sourceBranch: 'main',
81+
sourceCommit: '31bbc0c5227b6b3aed5d107fd8507d35ec22970a',
82+
previousNeedId: null,
83+
});
7584
expect(need.sourceConstraints).toMatchObject({
7685
repositoryFullName: 'engineeredsoftware/ENGI',
7786
sourceBranch: 'main',
@@ -86,6 +95,27 @@ describe('Read-Need synthesis and Finding Fits admission', () => {
8695
);
8796
});
8897

98+
it('carries Read Request lineage and feedback through resynthesis', () => {
99+
const first = synthesizeReadNeedForPipelineInput({
100+
...input,
101+
feedback: ['Keep proof roots prominent.'],
102+
});
103+
const second = synthesizeReadNeedForPipelineInput({
104+
...input,
105+
previousReadNeed: first,
106+
feedback: ['Exclude settlement completion from Need comprehension.'],
107+
});
108+
109+
expect(second.needId).not.toBe(first.needId);
110+
expect(second.request.previousNeedId).toBe(first.needId);
111+
expect(second.feedbackHistory).toEqual([
112+
'Keep proof roots prominent.',
113+
'Exclude settlement completion from Need comprehension.',
114+
]);
115+
expect(second.request.feedbackHistory).toEqual(second.feedbackHistory);
116+
expect(second.reviewState).toBe('needs_acceptance');
117+
});
118+
89119
it('accepts a Need as the only admissible input to strict Finding Fits search', () => {
90120
const accepted = acceptReadNeed(synthesizeReadNeedForPipelineInput(input), '2026-05-18T00:00:00.000Z');
91121

packages/pipelines/asset-pack/src/__tests__/reading-pipeline-contract.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
READ_NEED_COMPREHENSION_SYNTHESIS,
55
READ_NEED_COMPREHENSION_SYNTHESIS_CONTRACT,
66
READING_PIPELINE_CONTRACTS,
7+
listReadingPipelineTelemetryTrace,
78
listReadingPipelineContractSummaries,
89
} from '../reading-pipeline-contract';
910

@@ -138,4 +139,22 @@ describe('Reading pipeline contracts', () => {
138139
}),
139140
]);
140141
});
142+
143+
it('lists every PTRR step as telemetry-ready trace entries with ThricifiedGeneration substeps', () => {
144+
const readNeedTrace = listReadingPipelineTelemetryTrace(READ_NEED_COMPREHENSION_SYNTHESIS_CONTRACT);
145+
const findingFitsTrace = listReadingPipelineTelemetryTrace(READ_FINDING_FITS_SYNTHESIS_CONTRACT);
146+
147+
expect(readNeedTrace).toHaveLength(16);
148+
expect(findingFitsTrace).toHaveLength(32);
149+
expect(readNeedTrace.map((entry) => entry.ptrrStepId)).toContain(
150+
'ReadNeedComprehensionSynthesis.comprehend.need-synthesizer.try',
151+
);
152+
153+
for (const entry of [...readNeedTrace, ...findingFitsTrace]) {
154+
expect(entry.agentId.startsWith(`${entry.pipelineName}.`)).toBe(true);
155+
expect(entry.thricifiedGenerationIds).toHaveLength(3);
156+
expect(entry.thricifiedGenerations).toHaveLength(3);
157+
expect(entry.telemetry.every((telemetry) => telemetry.startsWith(`${entry.pipelineName}.telemetry.`))).toBe(true);
158+
}
159+
});
141160
});

packages/pipelines/asset-pack/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,9 +288,11 @@ export {
288288
resolveReadNeedFromPipelineInput,
289289
shouldRequireAcceptedReadNeed,
290290
synthesizeReadNeedForPipelineInput,
291+
synthesizeReadNeedForPipelineInputWithInference,
291292
type AssetPackReadRightState,
292293
type AssetPackSourceSafePreview,
293294
type ReadNeed,
295+
type ReadNeedRequest,
294296
type ReadFindingFitsAdmission,
295297
type ReadNeedMeasurementDimension,
296298
type ReadNeedReviewState,

packages/pipelines/asset-pack/src/read-need.ts

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,26 @@ export interface ReadNeedMeasurementDimension {
1818
volume: number;
1919
}
2020

21+
export interface ReadNeedRequest {
22+
schema: 'bitcode.read.request';
23+
requestId: string;
24+
prompt: string;
25+
repositoryFullName?: string | null;
26+
sourceBranch?: string | null;
27+
sourceCommit?: string | null;
28+
targetArtifactKinds: string[];
29+
closureCriteria: string[];
30+
failureModes: string[];
31+
feedbackHistory: string[];
32+
previousNeedId?: string | null;
33+
}
34+
2135
export interface ReadNeed {
2236
schema: 'bitcode.read.need';
2337
needId: string;
2438
reviewState: ReadNeedReviewState;
2539
measurementRoot: string;
40+
request: ReadNeedRequest;
2641
read: {
2742
id?: string | null;
2843
prompt: string;
@@ -172,6 +187,8 @@ type ReadNeedSourceInput = {
172187
closureCriteria?: unknown;
173188
failureModes?: unknown;
174189
feedback?: unknown;
190+
readNeed?: unknown;
191+
previousReadNeed?: unknown;
175192
};
176193

177194
const inferredStringListSchema = z.preprocess(
@@ -232,6 +249,10 @@ function stringArray(value: unknown): string[] {
232249
.filter(Boolean);
233250
}
234251

252+
function uniqueStrings(values: string[]): string[] {
253+
return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
254+
}
255+
235256
function getPath(value: unknown, path: string[]): unknown {
236257
let cursor = value;
237258
for (const part of path) {
@@ -245,6 +266,13 @@ function normalizeArtifactKind(value: string): string {
245266
return value.toLowerCase().replace(/[_\s]+/g, '-');
246267
}
247268

269+
function priorReadNeed(input: ReadNeedSourceInput): Record<string, unknown> | null {
270+
const direct = recordValue(input.readNeed);
271+
if (direct?.schema === 'bitcode.read.need') return direct;
272+
const previous = recordValue(input.previousReadNeed);
273+
return previous?.schema === 'bitcode.read.need' ? previous : null;
274+
}
275+
248276
function normalizeSource(input: ReadNeedSourceInput) {
249277
const readRecord = recordValue(input.read);
250278
const requestRecord = recordValue(input.readRequest);
@@ -373,28 +401,47 @@ function clampQuality(value: number): number {
373401
export function synthesizeReadNeedForPipelineInput(input: ReadNeedSourceInput): ReadNeed {
374402
const read = normalizeSource(input);
375403
const targetArtifactKinds = normalizeTargetArtifactKinds(input);
404+
const previousNeed = priorReadNeed(input);
376405
const explicitClosureCriteria = [
377406
...stringArray(input.closureCriteria),
378407
...stringArray(recordValue(input.read)?.closureCriteria),
379408
...stringArray(recordValue(input.readRequest)?.closureCriteria),
380409
...stringArray(recordValue(input.readMeasurement)?.closureCriteria),
381410
];
382411
const closureCriteria = explicitClosureCriteria.length
383-
? [...new Set(explicitClosureCriteria)]
412+
? uniqueStrings(explicitClosureCriteria)
384413
: defaultClosureCriteria(read);
385414
const failureModes = [
386415
...defaultFailureModes(),
387416
...stringArray(input.failureModes),
388417
...stringArray(recordValue(input.read)?.failureModes),
389418
...stringArray(recordValue(input.readRequest)?.failureModes),
390419
];
391-
const feedbackHistory = stringArray(input.feedback);
420+
const feedbackHistory = uniqueStrings([
421+
...stringArray(previousNeed?.feedbackHistory),
422+
...stringArray(recordValue(previousNeed?.request)?.feedbackHistory),
423+
...stringArray(recordValue(input.readRequest)?.feedback),
424+
...stringArray(input.feedback),
425+
]);
392426
const measurementVector = measurementVectorForNeed(read, targetArtifactKinds, closureCriteria);
427+
const readRequest: ReadNeedRequest = {
428+
schema: 'bitcode.read.request',
429+
requestId: read.id || `request-${sha256(stableStringify({ read, targetArtifactKinds })).slice(0, 16)}`,
430+
prompt: read.prompt,
431+
repositoryFullName: read.repositoryFullName,
432+
sourceBranch: read.sourceBranch,
433+
sourceCommit: read.sourceCommit,
434+
targetArtifactKinds,
435+
closureCriteria,
436+
failureModes: uniqueStrings(failureModes),
437+
feedbackHistory,
438+
previousNeedId: firstString(previousNeed?.needId),
439+
};
393440
const seed = {
441+
request: readRequest,
394442
read,
395443
targetArtifactKinds,
396444
closureCriteria,
397-
failureModes,
398445
feedbackHistory,
399446
measurementVector,
400447
};
@@ -410,10 +457,11 @@ export function synthesizeReadNeedForPipelineInput(input: ReadNeedSourceInput):
410457
needId: `need-${sha256(stableStringify(seed)).slice(0, 16)}`,
411458
reviewState: 'needs_acceptance',
412459
measurementRoot,
460+
request: readRequest,
413461
read,
414462
requirements: defaultRequirements(read),
415463
closureCriteria,
416-
failureModes: [...new Set(failureModes)],
464+
failureModes: readRequest.failureModes,
417465
targetArtifactKinds,
418466
sourceConstraints: {
419467
repositoryFullName: read.repositoryFullName,
@@ -475,7 +523,8 @@ export async function synthesizeReadNeedForPipelineInputWithInference(
475523
targetArtifactKinds,
476524
closureCriteria: explicitClosureCriteria,
477525
failureModes: stringArray(input.failureModes),
478-
feedbackHistory: stringArray(input.feedback),
526+
feedbackHistory: fallbackNeed.feedbackHistory,
527+
previousNeedId: fallbackNeed.request.previousNeedId,
479528
requiredOutput:
480529
'requirements, closureCriteria, failureModes, targetArtifactKinds, proofExpectations',
481530
}),

0 commit comments

Comments
 (0)