Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
- Never explicitly version source code without direct instruction. Source code is always implicitly versioned to the active Bitcode canon and current gate; routes, file names, CSS files, constants, classes, API paths, tests, and implementation identifiers must be written in-place as the single current Bitcode system. Do not introduce names such as `api/v1`, `v27-*`, `first-gate-*`, `wip-*`, or similar version/gate/work-in-progress source constructs unless explicitly directed for a bounded compatibility artifact.
- Do not consider any legacy code. All legacy code is located under `_legacy/`.
- Highest caliber software engineering crafstmanship (maintainibility, abstractions, architectures, naming, patterns, comments, documentation, structures, algorithmic and data flow designs, UI/UX, etc.), correctness (specification and implementation precision, reliability, completeness, boundaried, scoped, encapsulated, etc.), and auditable (totalistic proofs systems from static code, build time, runtime, etc. etc. as is Bitcode, tests from unit, integration, E2E, linting and building, etc.).
- Do not push work directly to `main`. Create a version base branch for each draft target, such as `version/v28`, then create scoped gate/work branches from that version branch. Pull-request each closed gate back into the version branch. Pull-request the version branch into `main` only when all gates are closed and the version is formally promoted as canon. The default branch is protected by the `Bitcode Core Contributions` ruleset and requires pull requests plus verified signatures.
- Write quality commit messages that describe the grouped work, proof, or documentation change. Avoid generic messages such as `wip v28` unless the user explicitly asks for that exact temporary commit shape.

## The Bezalel Protocol: Sacred Craft for Coding Agents

Expand Down
19 changes: 18 additions & 1 deletion BITCODE_SPEC_V28.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ V28 requires:
- compatibility storage carriers are hidden behind Bitcode vocabulary and registry-derived read models;
- no new versioned UAPI route family is introduced;
- implementation source remains implicitly versioned to the active canon and current gate: routes, file names, CSS, constants, tests, and identifiers must not introduce explicit version/gate/work-in-progress names without a bounded compatibility instruction;
- repository contribution flow uses a V28 version branch as the draft integration
branch, with scoped work branches opened from it and pull-requested back into
it as individual gates close;
- value-bearing mainnet remains gated by explicit operational approval;
- V29 Terminal depth, V30 Protocol/BTD hardening, V31 Auxillaries depth, V32 provation/testing depth, V33 interface depth beyond the V28 MVP, V34 deployment depth, V35 telemetry/documenting depth, and V36+ Exchange/Conversations depth remain staged unless V28 requires a narrow Protocol/Terminal/MCP/ChatGPT hook.

Expand Down Expand Up @@ -782,6 +785,20 @@ Settlement cannot proceed unless the previewed AssetPack id, fee quote, BTD
range projection, wallet authorization, BTC fee transaction, ownership boundary,
journal entry, and ledger/database readback all agree.

The product pipeline now carries a typed `bitcode.read.need` object before
Need-Fit search. The Need object contains `needId`, `measurementRoot`,
requirements, closure criteria, failure modes, target artifact kinds, source
constraints, proof expectations, pricing measurement inputs, review state, and
feedback history. Strict Need-Fit execution is admitted only when
`acceptedReadNeed` is present and has `reviewState='accepted'`; otherwise the
pipeline returns `blocked_readiness` before depository candidate recall. The
Vercel Sandbox harness synthesizes and accepts a Need before invoking the
bounded source-bound AssetPack pipeline so the closed staging-testnet evidence
path remains runnable while Terminal moves to the separate user review step.
`BITCODE_PIPELINE_REQUIRE_ACCEPTED_READ_NEED=1` or request-level
`requireAcceptedReadNeed=true` activates the strict boundary for API and route
callers.

The protocol demonstration carries only the minimal deterministic witness of
this path: local Need synthesis, explicit Need acceptance, local Need-Fit
ranking over fixture deposits, source-safe preview, and deterministic fee-quote
Expand Down Expand Up @@ -936,7 +953,7 @@ Minimum V28 validation includes spec-family checks, canon-posture drift checks,

## promotion canon

Promotion requires V28 PROVEN, synchronized SPEC/DELTA/NOTES/PARITY, closed gates, route scan, test/build proof, and explicit update of `BITCODE_SPEC.txt` from `V27` to `V28`.
Promotion requires V28 PROVEN, synchronized SPEC/DELTA/NOTES/PARITY, closed gates, route scan, test/build proof, pull-requested integration from scoped gate branches into the V28 version branch, and explicit update of `BITCODE_SPEC.txt` from `V27` to `V28` when the version branch is pull-requested back into `main`.

## appendices and canonical supporting material

Expand Down
22 changes: 22 additions & 0 deletions BITCODE_V28_QA.md
Original file line number Diff line number Diff line change
Expand Up @@ -1363,6 +1363,28 @@ a synchronous route, raw-prompt Read, or source-leaking preview model.
| Settle and unlock | Deterministic Share-to-Fee and BTD settlement. | Price is derived from `sum(measurement_weight * measurement_volume * admitted_fit_quality)` and the staging fee schedule. Reader pays BTC fee, BTD range/ownership/license/journal/anchor rows are written and read back, then full AssetPack source/right surface is unlocked. |
| Full-profile async pipeline | Run the full PTRR profile for long-running audits. | Vercel Sandbox execution may run for dozens of minutes and must push completion artifacts to a server-side stream/socket handler or durable queue; the push is run-id correlated, authenticated, idempotent, and durable before sandbox stop. Terminal can reattach and read final state without the starter route waiting. |

2026-05-18 implementation start for the staged Reading gate:

- `@bitcode/pipeline-asset-pack` now owns a typed `bitcode.read.need`
contract with `needId`, `measurementRoot`, requirements, closure criteria,
failure modes, target artifact kinds, source constraints, proof
expectations, pricing measurement inputs, review state, and feedback
history.
- Strict Need-Fit search blocks before depository candidate recall unless an
accepted Need is present. The blocked state is explicit
`blocked_readiness` with `accepted_read_need_missing`.
- The depository search path consumes accepted Need source constraints and
measurement roots as the Fit search input, rather than raw prompt text, when
`acceptedReadNeed` is supplied.
- The Vercel Sandbox harness now lists Need stages in the manifest and
synthesizes plus accepts a Need before invoking the existing source-bound
AssetPack pipeline. This preserves the current proven staging-testnet path
while Terminal is split into user-visible Need review and Need-Fit execution
steps.
- `buildShareToFeePreview` provides the initial source-safe quote shape from
accepted Need measurement vector and admitted Fit quality:
`sum(measurement.weight * measurement.volume * admitted_fit_quality)`.

Observed staging-testnet harness evidence on 2026-05-17:

- Vercel Sandbox run `sbx_ktb5Z6VnP5A16m9k4a0FkBcJg1d3` completed all six
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,24 @@ App.
- Update source in-place to match the active canon and current draft target.
- Keep specification notes, QA ledgers, tests, and implementation synchronized.

## Contributor Workflow

The default branch is protected by the active `Bitcode Core Contributions`
ruleset. Direct pushes to `main` are not part of the normal workflow; expect
them to be rejected because changes must arrive through pull requests and
verified signatures.

Use a version branch and gate branches:

1. Create one base branch per draft target, such as `version/v28`.
2. Create scoped work branches from the version branch, for example
`v28/<gate-or-work-slice>`.
3. Group related work into clear commits with descriptive commit messages.
4. Open pull requests from scoped branches into the version branch as gates
close.
5. Open the version branch back into `main` only after all gates close and the
version is formally promoted as canon.

## Key Surfaces

- [BITCODE_SPEC.txt](BITCODE_SPEC.txt) is the canonical version pointer.
Expand Down
11 changes: 11 additions & 0 deletions packages/pipeline-hosts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ helpers used only by the harness are installed under `.bitcode/pipeline-harness`
so historical deposited source revisions do not need to carry newer harness
dependencies.

The harness manifest includes the staged Reading boundary. The active stage
sequence begins with Need synthesis, Need review, and Need-Fit search before
candidate ranking and AssetPack synthesis. The live runner synthesizes a
typed `bitcode.read.need` object from the Read request, source revision, and
Deposit context, accepts it for the current harness invocation, and passes it
to the AssetPack pipeline as `acceptedReadNeed` with
`requireAcceptedReadNeed=true`. Product routes that already have a user-reviewed
Need should pass that accepted object directly; if strict Need-Fit execution is
requested without an accepted Need, candidate recall must return
`blocked_readiness` before searching the depository.

Structured database telemetry is part of the harness contract. A real Read/Fit
pipeline run must write the deliverable hierarchy:
`deliverable_pipeline_runs`, `deliverable_pipeline_events`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ describe('asset-pack sandbox harness plan', () => {

expect(diagnostics.filter((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error)).toEqual([]);
expect(source).toContain('pipeline-stream-event');
expect(source).toContain('synthesizeReadNeedForPipelineInput');
expect(source).toContain('acceptedReadNeed: readNeed');
expect(source).toContain('requireAcceptedReadNeed');
expect(source).toContain('artifact-streaming-enabled');
expect(source).toContain('execution: execution ? summarizeExecution(execution) : null');
expect(source).toContain('PipelineHarnessTimeoutError');
Expand Down
4 changes: 4 additions & 0 deletions packages/pipeline-hosts/src/__tests__/manifest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ describe('pipeline harness manifest', () => {
expect(manifest.host.isolationBoundary).toBe('firecracker-microvm');
expect(manifest.host.defaultWorkingDirectory).toBe('/vercel/sandbox');
expect(manifest.stages).toEqual(ASSET_PACK_HARNESS_STAGES);
expect(manifest.stages).toEqual(
expect.arrayContaining(['need-synthesis', 'need-review', 'need-fit-search'])
);
expect(manifest.requireAcceptedReadNeed).toBe(true);
expect(manifest.expectedEvidenceTables).toEqual(ASSET_PACK_HARNESS_EVIDENCE_TABLES);
expect(manifest.resultStates).toEqual([
'worthy_fit',
Expand Down
25 changes: 24 additions & 1 deletion packages/pipeline-hosts/src/asset-pack-harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const SANDBOX_PNPM_VERSION = '10.33.0';
export interface BuildAssetPackSandboxHarnessOptions {
mode?: PipelineHarnessMode;
read: PipelineReadRequest;
readNeed?: unknown;
deposit: PipelineDepositReference;
sourceRevision: PipelineSourceRevision;
source?: PipelineSandboxSource;
Expand Down Expand Up @@ -80,6 +81,8 @@ export function buildAssetPackSandboxHarness(
const manifest = buildAssetPackPipelineHarnessManifest({
mode,
read: options.read,
readNeed: options.readNeed,
requireAcceptedReadNeed: true,
deposit,
sourceRevision: options.sourceRevision,
sourceOverlay,
Expand Down Expand Up @@ -1496,7 +1499,7 @@ try {
manifest = JSON.parse(await readFile(manifestPath, 'utf8'));
manifestRoot = createHash('sha256').update(JSON.stringify(manifest)).digest('hex');
userId = process.env.BITCODE_PIPELINE_USER_ID || manifest.deposit?.userId || DEFAULT_USER_ID;
const [{ assetPackPipeline }, { enablePipelineStreaming, factoryPipelineExecution }] = await Promise.all([
const [{ assetPackPipeline, acceptReadNeed, isAcceptedReadNeed, synthesizeReadNeedForPipelineInput }, { enablePipelineStreaming, factoryPipelineExecution }] = await Promise.all([
import('../../packages/pipelines/asset-pack/src/index'),
import('../../packages/pipelines-generics/src/index'),
]);
Expand Down Expand Up @@ -1540,9 +1543,29 @@ try {
record({ type: 'artifact-streaming-enabled', stage: 'telemetry-readback' });
}

const readNeed = isAcceptedReadNeed(manifest.readNeed)
? manifest.readNeed
: acceptReadNeed(synthesizeReadNeedForPipelineInput({
read: manifest.read,
readRequest: manifest.read,
sourceRevision: manifest.sourceRevision,
repository: {
fullName: manifest.sourceRevision.repositoryFullName,
branch: manifest.sourceRevision.branch,
commit: manifest.sourceRevision.commit,
},
}));
execution.store('read/need', 'accepted', readNeed);
execution.store('read/need', 'needId', readNeed.needId);
execution.store('read/need', 'measurementRoot', readNeed.measurementRoot);
execution.store('read/need', 'reviewState', readNeed.reviewState);

const input = {
read: manifest.read.prompt,
definitionOfRead: manifest.read.prompt,
readNeed,
acceptedReadNeed: readNeed,
requireAcceptedReadNeed: manifest.requireAcceptedReadNeed !== false,
repository: {
fullName: manifest.sourceRevision.repositoryFullName,
branch: manifest.sourceRevision.branch,
Expand Down
8 changes: 8 additions & 0 deletions packages/pipeline-hosts/src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,14 @@ export const VERCEL_SANDBOX_HOST_CAPABILITIES: PipelineHostCapabilities = {
};

export const ASSET_PACK_HARNESS_STAGES: readonly PipelineHarnessStage[] = [
'need-synthesis',
'need-review',
'need-fit-search',
'deposit-search',
'candidate-ranking',
'read-comprehension',
'asset-pack-synthesis',
'source-safe-preview',
'validation',
'finish',
'telemetry-readback',
Expand Down Expand Up @@ -106,6 +110,8 @@ export function buildAssetPackPipelineHarnessManifest(input: {
sourceRevision: PipelineSourceRevision;
sourceOverlay?: PipelineHarnessSourceOverlay;
commandEnvironment?: Record<string, string | undefined>;
readNeed?: unknown;
requireAcceptedReadNeed?: boolean;
createdAt?: string;
}): PipelineHarnessManifest {
assertNonEmpty(input.read.id, 'read.id');
Expand All @@ -119,6 +125,8 @@ export function buildAssetPackPipelineHarnessManifest(input: {
pipelineName: 'asset-pack-read-fit',
harnessMode: input.mode,
read: input.read,
requireAcceptedReadNeed: input.requireAcceptedReadNeed ?? true,
readNeed: input.readNeed,
deposit: input.deposit,
sourceRevision: input.sourceRevision,
sourceOverlay: input.sourceOverlay,
Expand Down
6 changes: 6 additions & 0 deletions packages/pipeline-hosts/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ export type BitcodePipelineResultState =
| 'blocked_readiness';

export type PipelineHarnessStage =
| 'need-synthesis'
| 'need-review'
| 'need-fit-search'
| 'deposit-search'
| 'candidate-ranking'
| 'read-comprehension'
| 'asset-pack-synthesis'
| 'source-safe-preview'
| 'validation'
| 'finish'
| 'telemetry-readback';
Expand Down Expand Up @@ -93,6 +97,8 @@ export interface PipelineHarnessManifest {
pipelineName: 'asset-pack-read-fit';
harnessMode: PipelineHarnessMode;
read: PipelineReadRequest;
requireAcceptedReadNeed?: boolean;
readNeed?: unknown;
deposit: PipelineDepositReference;
sourceRevision: PipelineSourceRevision;
sourceOverlay?: PipelineHarnessSourceOverlay;
Expand Down
46 changes: 44 additions & 2 deletions packages/pipelines/asset-pack/src/__tests__/metrics-output.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,57 @@ describe('AssetPack pipeline bring-up (setup + PTRR plan: prepare→reason)', ()
try {
const exec = new Execution('asset-pack:test');
const inserts: any[] = [];
const selectedRows = (table: string, filters: Array<[string, any]>) => {
return inserts
.filter((insert) => insert.table === table)
.map((insert) => insert.row)
.filter((row) => filters.every(([column, value]) => row?.[column] === value));
};
const selectBuilder = (table: string) => {
const filters: Array<[string, any]> = [];
const builder: any = {
eq: (column: string, value: any) => {
filters.push([column, value]);
return builder;
},
order: () => builder,
limit: () => builder,
maybeSingle: async () => ({ data: selectedRows(table, filters).at(-1) || null }),
then: (resolve: any) => Promise.resolve({ data: selectedRows(table, filters), error: null }).then(resolve),
};
return builder;
};
const updateBuilder = (table: string, patch: any) => {
const filters: Array<(row: any) => boolean> = [];
const apply = async () => {
const rows = inserts.filter((insert) => insert.table === table).map((insert) => insert.row);
for (const row of rows) {
if (filters.every((filter) => filter(row))) Object.assign(row, patch);
}
return { data: rows.filter((row) => filters.every((filter) => filter(row))), error: null };
};
const builder: any = {
eq: (column: string, value: any) => {
filters.push((row) => row?.[column] === value);
return builder;
},
in: (column: string, values: any[]) => {
filters.push((row) => values.includes(row?.[column]));
return builder;
},
then: (resolve: any) => apply().then(resolve),
};
return builder;
};
const supabaseStub: any = {
from(table: string) {
return {
insert: (row: any) => {
inserts.push({ table, row });
return { select: (_?: any) => ({ single: () => Promise.resolve({ data: { id: 'id-' + inserts.length } }) }) } as any;
},
update: (_: any) => ({ eq: () => Promise.resolve({}) }),
select: () => ({ eq: () => ({ order: () => ({ limit: () => ({ maybeSingle: async () => ({ data: { id: 'id-last' } } as any) }) }) }) })
update: (patch: any) => updateBuilder(table, patch),
select: () => selectBuilder(table),
} as any;
}
};
Expand Down
Loading
Loading