Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ prd.md text eol=lf
docs/v0.3/*.md text eol=lf
.github/workflows/*.yml text eol=lf
extension/test/fixtures/v0.3/** text eol=lf
extension/fixtures/v0.3/** text eol=lf
10 changes: 10 additions & 0 deletions docs/BUILD_LOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

This is a concise chronological record of material implementation work and runtime evidence. It is not a substitute for Git history; it captures intent, verification, and blockers that a commit alone may not explain.

## 2026-07-31 — R3 Takeover Twin local candidate

- Implemented a controller-owned `SnapshotStore` that starts from the verified fixture target, applies the catalog mutation, creates a sanitized standalone one-commit participant repository, and re-verifies tree hashes, commit identity, ownership, remotes, reflogs, and Git alternates before materialization.
- Added an opaque Twin Manager with separate uniquely named twin/evaluation directories and explicit `preparing → ready → running → completed/failed → cleaning → cleaned` lifecycle. Cleanup resolves and checks both exact controller-owned paths before recursive removal.
- Added the immutable fixture command registry, extension-owned catalog, hash-pinned `fixture-node` resolution, bounded command evidence, tombstoned execution IDs, independent cancellation, scrubbed child environment, and full process-tree termination. Every non-fixture request, caller manifest/runtime/path, unknown command, and undeclared tree state fails before execution.
- Moved the committed fixture from the excluded test tree into packaged extension assets after a VSIX audit found it would otherwise be absent at runtime. The package now contains all three candidate states plus controller-only mutation, repair, harness, and oracle assets.
- Seven R3 tests pass locally on Windows, including a real descendant-process kill in a path with spaces. The complete extension suite passes 50/50; `npm run check`, production build, and VSIX packaging pass. The first protected Windows run exposed missing LF policy after the fixture moved from `test/` to packaged assets; `.gitattributes` now pins the new path. Protected PR #13 run `30668675359` then passed all required Linux, Windows, contract, web, and policy checks, so R3 acceptance is complete.

Evidence: `extension/src/twin/{types,catalog,snapshot,manager,commands}.ts`, `extension/fixtures/v0.3/tenant-cache-key/`, `extension/test/twin.test.ts`, local command/package output on 2026-07-31, and protected GitHub Actions run `30668675359` on PR #13.

## 2026-07-31 — R2 change evidence accepted

- Added a bounded read-only Git revision reader that verifies full base/target commit IDs, parses NUL-delimited name status plus zero-context hunks, detects renames, caps source and revision size, and keeps every emitted path workspace-relative.
Expand Down
17 changes: 9 additions & 8 deletions docs/PROJECT_STATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Last updated: 2026-07-31

## Current branch milestone — R2 change evidence complete
## Current branch milestone — R3 Takeover Twin complete

Branch `codex/shadow-cockpit-rnd` resets the product R&D thesis around **Dual-Control Development**.

Expand All @@ -19,7 +19,9 @@ Branch `codex/shadow-cockpit-rnd` resets the product R&D thesis around **Dual-Co
- The full local Windows extension suite passes 37/37 with `npm run check`; the production bundle and VSIX package also pass. Protected PR #11 run `30665384997` independently passed `extension`, `extension-windows`, `contract`, `web`, and `jules-rnd-policy`, so R1 acceptance is complete.
- R2 implements bounded Git revision diffs, zero-context changed-line extraction, TypeScript compiler-API symbol resolution, explicit fixture check linkage, Flight Recorder hash/attribution validation, and exact `ExtractionResult` / `SemanticUnit` / `CandidateSeam` outputs. It fails to `partial` or `unsupported` for missing links, syntax failures, unsupported languages, or incomplete coverage instead of inventing an invariant.
- Six R2 tests cover the real cache-key fixture, cross-run determinism and golden IDs, file rename, multi-file changes, added/deleted functions, class boundaries, Git hunk parsing, unsupported syntax/language, missing checks, unsafe paths, and revision/run drift. The full local Windows extension suite passes 43/43 with TypeScript, build, and VSIX packaging. Protected PR #12 run `30666648522` passed `extension`, `extension-windows`, `contract`, `web`, and `jules-rnd-policy`, so R2 acceptance is complete.
- The Experience Compiler, Takeover Twin lifecycle, Evidence Judge, Control Pulse runtime, readiness ledger, and v0.3 cockpit do not exist yet. `TrustedFixtureRunner` exists for the closed R0 fixture substrate.
- R3 now implements the fixture-only sanitized snapshot store, standalone one-commit participant repository, opaque Twin Manager, immutable command-registry snapshots, extension-owned trusted catalog, and `TrustedFixtureRunner`. The runner accepts only the pinned manifest, exact declared state/tree, catalog command, hash-verified standalone Node runtime, and opaque twin handle; non-fixture execution remains explicitly unsupported.
- Seven R3 tests cover production file/index/HEAD/ref/remote/worktree invariants, hidden-answer and source-history absence, cross-project ownership, exact-state execution, tamper/unknown-command rejection, concurrent execution IDs, single-run cancellation, immutable command snapshots, Windows paths with spaces, descendant-process termination, and exact cleanup. The full local Windows extension suite passes 50/50; build and VSIX packaging pass, and the runtime fixture assets are present in the VSIX. Protected PR #13 run `30668675359` passed `extension`, `extension-windows`, `contract`, `web`, and `jules-rnd-policy`, so R3 acceptance is complete.
- The Experience Compiler, Evidence Judge, Control Pulse runtime, readiness ledger, and v0.3 cockpit do not exist yet. R3 proves only the closed reviewed-fixture boundary; it does not execute arbitrary participant or workspace code.
- No skill-retention or speed metric has been measured. Values in the PRD are predeclared R&D targets.
- A new implementation audit found five R0 ambiguities: candidate-diff identity, pre-store fixture blobs, runtime identity, check IDs, and Git object format. The normative contract closes them with structured diffs, catalog-owned blobs, standalone Node `v22.17.0`, declared test IDs, and SHA-1 Git initialization; R0a/R0b now implement and verify that complete substrate.
- A guarded Jules dispatcher and PR policy are defined as a finite R0→R4 queue. They create at most one session after a successful preflight, stop after merged R4, remain inert unless dispatch is explicitly enabled, and keep plan approval on by default. Merges remain manual because the current project tests are not an independent immutable verifier. Full scheduled continuation still requires the dispatcher workflow to be reviewed into the default branch.
Expand Down Expand Up @@ -137,12 +139,11 @@ No external input blocks the repository-owned fixture R0–R4.5 mechanism in `do

## Next ordered actions

1. Implement R3 behind the accepted contract: safe Takeover Twin snapshot, lifecycle, catalog, and command boundaries.
2. Integrate R4: one compiled recovery episode and deterministic Evidence Judge.
3. Pass R4.5: one bounded, catalog-only Explain-to-Break Pulse with replay/error fail-closed tests.
4. Run the 30-patch recovery-plus-probe technical corpus audit before expanding the product surface.
5. Add the local readiness ledger and minimal cockpit only after the vertical slice is reliable.
6. Run the preregistered delayed-transfer pilot before making any skill-retention claim.
1. Integrate R4: one compiled recovery episode and deterministic Evidence Judge.
2. Pass R4.5: one bounded, catalog-only Explain-to-Break Pulse with replay/error fail-closed tests.
3. Run the 30-patch recovery-plus-probe technical corpus audit before expanding the product surface.
4. Add the local readiness ledger and minimal cockpit only after the vertical slice is reliable.
5. Run the preregistered delayed-transfer pilot before making any skill-retention claim.

## Recent milestone commits

Expand Down
5 changes: 4 additions & 1 deletion docs/v0.3/AGENT_EXECUTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,11 @@ extension/src/
readiness/ local evidence ledger and derived state
cockpit/ host-to-webview messages for v0.3 UI

extension/fixtures/v0.3/
tenant-cache-key/ packaged finite candidate states and controller assets

extension/test/
fixtures/v0.3/ tiny deterministic Git repositories or fixture builders
fixtures/v0.3/ replay transcripts and other test-only evidence
agent.test.ts
flight-recorder.test.ts
experience.test.ts
Expand Down
2 changes: 1 addition & 1 deletion extension/src/recorder/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ function assertRunEvent(value: unknown): asserts value is RunEvent {
}
}

function assertEvidenceRef(value: unknown, expectedKind: EvidenceKind): asserts value is EvidenceRef {
export function assertEvidenceRef(value: unknown, expectedKind: EvidenceKind): asserts value is EvidenceRef {
assertRecord(value, "evidence ref");
assertExactKeys(
value,
Expand Down
100 changes: 100 additions & 0 deletions extension/src/twin/catalog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { readFile, stat } from "node:fs/promises";
import { rawSha256 } from "../rnd/canonical";
import {
EXPECTED_TENANT_CACHE_KEY,
loadTenantCacheKeyManifest,
tenantCacheKeyControllerAssets,
} from "./fixture-factory";
import { fixtureManifestHash, validateFixtureManifest, type FixtureBlobRef } from "./fixture-contract";
import { openFixtureNodeRuntime, type InstalledFixtureRuntime } from "./fixture-runtime";
import type { TrustedFixtureRecord } from "./types";

export class BuiltinFixtureCatalog {
private record?: Promise<TrustedFixtureRecord>;
private runtime?: InstalledFixtureRuntime;

async open(fixtureId: string, manifestHash: string): Promise<TrustedFixtureRecord | undefined> {
if (
fixtureId !== "tenant-cache-key" ||
manifestHash !== EXPECTED_TENANT_CACHE_KEY.manifestHash
) {
return undefined;
}
return structuredClone(await (this.record ??= this.load()));
}

async openForSnapshot(sourceRevision: string, mutationId: string): Promise<TrustedFixtureRecord | undefined> {
const record = await this.open("tenant-cache-key", EXPECTED_TENANT_CACHE_KEY.manifestHash);
if (
!record ||
record.manifest.targetRevision !== sourceRevision ||
record.manifest.mutation.id !== mutationId
) {
return undefined;
}
return record;
}

async resolveNode(record: TrustedFixtureRecord): Promise<string> {
const current = await this.open(record.manifest.fixtureId, record.manifestHash);
if (!current || current.node.executableSha256 !== record.node.executableSha256) {
throw new Error("Trusted fixture runtime record changed after catalog validation");
}
const runtime = this.runtime;
if (!runtime || runtime.executableSha256 !== record.node.executableSha256) {
throw new Error("Trusted fixture runtime is not open");
}
return runtime.executablePath;
}

private async load(): Promise<TrustedFixtureRecord> {
const [manifest, runtime] = await Promise.all([
loadTenantCacheKeyManifest(),
openFixtureNodeRuntime(),
]);
validateFixtureManifest(manifest, {
handle: runtime.handle,
version: runtime.version,
executableSha256: runtime.executableSha256,
});
const manifestHash = fixtureManifestHash(manifest);
if (manifestHash !== EXPECTED_TENANT_CACHE_KEY.manifestHash) {
throw new Error("Built-in fixture manifest hash changed");
}
this.runtime = runtime;

const handles = tenantCacheKeyControllerAssets();
const record: TrustedFixtureRecord = {
manifestHash,
manifest,
node: {
handle: "fixture-node",
version: runtime.version,
executableSha256: runtime.executableSha256,
},
blobs: {
harness: { localHandle: handles.harness, ref: manifest.toolchain.harness },
oracle: { localHandle: handles.oracle, ref: manifest.toolchain.oracle },
mutation: { localHandle: handles.mutation, ref: manifest.mutation.changeRef },
repair: { localHandle: handles.repair, ref: manifest.knownRepair.changeRef },
},
};

await Promise.all([
verifyBlob(record.blobs.harness.localHandle, record.blobs.harness.ref),
verifyBlob(record.blobs.oracle.localHandle, record.blobs.oracle.ref),
verifyBlob(record.blobs.mutation.localHandle, record.blobs.mutation.ref),
verifyBlob(record.blobs.repair.localHandle, record.blobs.repair.ref),
]);
return record;
}
}

async function verifyBlob(path: string, ref: FixtureBlobRef): Promise<void> {
const info = await stat(path);
if (!info.isFile()) throw new Error(`Fixture blob is not a regular file: ${ref.id}`);
const bytes = await readFile(path);
if (bytes.byteLength !== ref.storedBytes || rawSha256(bytes) !== ref.sha256) {
throw new Error(`Fixture blob does not match its committed reference: ${ref.id}`);
}
}
Loading
Loading