# Project export: Zero Handoff

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: OpenAI Build Week
- Tagline: From one idea brief to a tested application and narrated demo, built by pairs of specialized AI agents that develop trust with each other in training + continual learning
- Devpost: https://devpost.com/software/placeholder-sd5rut
- GitHub: https://github.com/diegocp01/enterprise_build
- Video: https://www.youtube.com/embed/0LPhIIqEl2M?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 0 GitHub contributor(s) — 

## Devpost submission (written by the team)

### Inspiration

Humans learn who to trust through experience. We asked whether AI agents could do the same while turning one short product request into tested software, with no human handoffs. ZeroHandoff has 7 teams of 2 Agents each that work together to deliver enterprise products, teams are: SENSE → MODEL → COMPOSE → DECIDE → SIMULATE → EXECUTE → OBSERVE

### What it does

ZeroHandoff gets a user idea for a product as input and gives back a working app + video demo as output, coordinates 14 GPT-5.6 Sol PRE-TRAINED agents in 7 two-agent teams. Pre-Training Before delivery, each team solves ten split-clue puzzles. Success or failure updates asymmetric trust (float value), while Curators maintain nine additional relationship dimensions and memory. Training completed 70 team episodes with 63 puzzles solved, producing an immutable relationship baseline. During delivery, trust helps resolve disagreements. Teams reward accepted handoffs with 1 and rejected handoffs with 0. Updates remain pending until the prototype finishes, when one Night Curator validates and commits reduced-plasticity trust and consolidated memory.

### How we built it

Using Codex, we created typed artifact contracts, 14 agent configurations, isolated workspaces, schema-constrained GPT-5.6 Sol calls, deterministic quality gates, repair loops, artifact-lineage tracking, immutable training state, continual learning, JSON logs, browser verification, and checksummed delivery packages. The final experiment built EchoLedger, which turns customer-support calls into evidence-linked problems and assigned follow-up actions. The run completed all seven stages with 81 agent calls, 32 gates, 18 repairs, a 147-file delivery bundle, and an interactive demo. Challenges and lessons Our hardest problem was ensuring learning reflected what really happened. One failed handoff was initially lost during a resume. We preserved the invalid history, restored the last verified state, implemented atomic persistence, and reran it. EchoLedger’s final rewards—[1, 1, 1, 1, 0, 1]—retain that real rejection and repair. We learned that dependable multi-agent systems require more than roles and prompts. They need relationships, memory, contracts, lineage, repair, and evidence.

### What's next

We plan to support more coding environments, test hundreds of enterprise handoffs, and expand governance and observability. The vision: one strategic intent enters, a trusted autonomous organization builds the product, and every decision remains inspectable.

## README (from the GitHub repository)

# ZeroHandoff — Autonomous Software Delivery

**One short build request in. A working, tested application and narrated demo out.**

[![ZeroHandoff training, delivery, and continual-learning overview](docs/images/overview-learning-system.png)](overview.html#learning-system)

[![ZeroHandoff seven-step autonomous software-delivery pipeline](docs/images/overview-delivery-pipeline.png)](overview.html#delivery-pipeline)

ZeroHandoff is an autonomous software-delivery pipeline for the OpenAI Build Week
Developer Tools track. A person describes only what they want built, for whom,
the desired outcome, and any constraints. Codex turns that request into a
sequence of deliverables without human handoffs:

`Build Request → Opportunity Model → Outcome Model → Capability Graph → Decision Graph → Scenario Model → Autonomous Change → Evidence + Learning → Demo`

Seven two-agent lifecycle cells—SENSE, MODEL, COMPOSE, DECIDE, SIMULATE,
EXECUTE, and OBSERVE—own those seven stages, respectively. Each pair forms independent
judgments and passes one versioned artifact forward. These compact artifacts are
designed for agent-to-agent execution instead of long-form specifications,
planning ceremonies, or human handoff documents. Codex preserves project memory,
enforces quality gates, and routes failed contract checks through bounded repair
loops.

Every run produces an auditable delivery bundle:

- a runnable application with source and setup instructions;
- automated tests and quality-gate evidence;
- append-only JSON logs covering timestamps, runtime and model configuration,
  the immutable training baseline, run-start inference state, handoff rewards,
  shadow deltas, night commit, decisions, gates, repairs, artifacts,
  and the final outcome; and
- a generated narrated video demo.

## Setup and running

**Supported platforms.** The full ZeroHandoff engine supports macOS and Linux
with Python 3.11+, Node.js 22+, npm, Chrome/Chromium, FFmpeg, and Codex CLI
0.144.0+ authenticated with GPT-5.6 Sol access. The prebuilt EchoLedger sandbox
works on any desktop with a modern browser and a local static-file server.

### Fastest judge test — no install or rebuild

The repository includes the production build from the final autonomous run:

```bash
python3 -m http.server 8000 --directory submission/sandbox/echoledger/dist
```

Open `http://127.0.0.1:8000`. EchoLedger requires no account, backend, API key,
network service, or rebuild. Select **Complaint · 02:31**, redact evidence
`EV-015`, inspect the recurring signal, assign its action, export the local case
brief, and reset the fictional experience.

### Install the complete engine

```bash
python3 -m venv .venv
.venv/bin/pip install -e '.[dev]'
cd ui
npm ci
npm run build
cd ..
.venv/bin/python -m zerohandoff.cli --repo . doctor --live
```

### Run without model calls

This deterministic path exercises training, orchestration, gates, repair,
artifact handoffs, demo assembly, and bundling without spending model credits:

```bash
.venv/bin/python -m pytest -q
.venv/bin/python -m zerohandoff.cli --repo . puzzles validate
.venv/bin/python -m zerohandoff.cli --repo . train --adapter fixture --rounds 10
.venv/bin/python -m zerohandoff.cli --repo . run \
  --request tests/fixtures/build_request.json --adapter fixture
```

### Run the real Codex pipeline

The checked-in trained baseline means judges do not need to retrain. From the
cloned repository root, activate the installed environment and launch Codex:

```bash
source .venv/bin/activate
codex
```

Then, **inside the Codex chat**, enter:

```text
$run-pipeline
Build a local React application for [audience] that [desired outcome].
Constraints: [non-negotiable boundaries].
```

Do not run the internal Python delivery command manually. The `$run-pipeline`
skill collects one Build Request, validates the frozen baseline and current
inference lineage, then invokes the engine and runs all seven stages, demo
generation, and bundling with GPT-5.6 Sol. Use `$pipeline-status` inside Codex
to inspect progress. To observe delivery in the local Control Room, open a
second terminal from the repository and run:

```bash
.venv/bin/python -m zerohandoff.cli --repo . serve --port 8765
```

Then open `http://127.0.0.1:8765`.

### Sample data, outputs, and verification

- `tests/fixtures/build_request.json` is the agent-free sample request.
- `data/puzzles.jsonl` and `data/puzzle_stats.json` are the deterministic
  ten-puzzle training corpus and validation summary.
- `.zerohandoff/frozen/latest.json` is the checked-in immutable relationship
  baseline; real delivery starts from it rather than retraining.
- `submission/sandbox/echoledger/src/domain.ts` and
  `submission/sandbox/echoledger/public/audio/echoledger-el1042.wav` contain
  EchoLedger's entirely fictional local transcript/domain fixtures and synthetic
  recording. No real customer information is included.
- Each run is written to `.zerohandoff/runs/<run_id>/`, including versioned
  artifacts, `logs/*.jsonl`, the runnable app, narrated demo, checksums, and
  `delivery_bundle.nosync/`. Versioned continual-learning commits are under
  `.zerohandoff/learning/commits/`.

Verify the tracked judge package, training/run completion, trust invariants,
checksums, sandbox, and media streams with:

```bash
python3 scripts/submission_package.py verify
```

More internal commands and recovery behavior are in the [judge guide](JUDGE_GUIDE.md)
and [runbook](RUNBOOK.md).

Trust training supports the pipeline; it is not the product itself. Before
delivery, a separate ten-round, solver-validated puzzle pilot trains asymmetric
directed trust through reward prediction error and one single-prompt Night
Curator call per agent after each round. The resulting ten-dimensional vectors
form an immutable trained baseline for software-delivery inference: trust alone selects authority, while
a deterministic policy compiler turns up to three strong non-trust dimensions
into qualitative collaboration guidance without exposing raw scores. See the
[visual explainer](context/trust_moa.html) or the
[source specification](context/trust_moa.md) for the full design.

Inference copies that baseline into a separate evolving state. Trained pair
edges begin at their learned values; new cross-team edges begin at `0.0`.
Each next team gives a binary handoff reward—accept `1`, request revision `0`—but
trust stays stable during the run. One extra-high-reasoning Night Curator then
commits reduced-plasticity trust (`α=0.05`, step cap `±0.1`) and consolidated
memory once the prototype completes. The nine non-trust dimensions and the
original training JSON remain unchanged forever.

Codex was both the build environment and the hackathon reference runtime. It
accelerated the typed contracts, orchestration engine, agent configurations,
gates, repair/resume logic, browser proof, demos, and regression suite; GPT-5.6
Sol powers the fourteen specialists and Curators. The human chose the learning
rules, immutable-training/evolving-inference boundary, seven agent-native
artifacts, reduced-plasticity continual learning, zero post-authorization human
handoffs, and Codex-only hackathon scope. The deeper build story and ready-to-use
submission copy are in [`submission/`](submission/README.md).

Use `$run-pipeline`, `$pipeline-status`, and `$train-trust` from Codex chat.
Internal commands and the Control Room are documented in the [runbook](RUNBOOK.md).

ZeroHandoff is an independent original implementation conceptually inspired by the
[BMad Method](https://github.com/bmad-code-org/BMAD-METHOD). It does not copy or
redistribute BMad source, prompts, agent definitions, names, or branded assets,
and is not affiliated with or endorsed by BMad Code, LLC.

**Explore:** [visual pipeline](overview.html) · [system design](SYSTEM_DESIGN.md) ·
[execution plan](EXECUTION_PLAN.md) · [trust architecture](context/trust_moa.html) ·
[judge guide](JUDGE_GUIDE.md) · [submission package](submission/README.md) ·
[project idea](c

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 90 recognized source files, 1006 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- HTML (language) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- TypeScript (language) — detected in the code
- Node.js (technology) — claimed on Devpost, not found in the code
- AI coding agent: Codex — evidence: config files committed to the repository

## Codebase structure (from repository index)

### Files (120 of 207)

```
.agents/skills/pipeline-status/agents/openai.yaml
.agents/skills/pipeline-status/SKILL.md
.agents/skills/run-pipeline/agents/openai.yaml
.agents/skills/run-pipeline/SKILL.md
.agents/skills/train-trust/agents/openai.yaml
.agents/skills/train-trust/SKILL.md
.codex/agents/compose-aster.toml
.codex/agents/compose-juno.toml
.codex/agents/decide-flint.toml
.codex/agents/decide-sable.toml
.codex/agents/execute-ember.toml
.codex/agents/execute-rook.toml
.codex/agents/model-kestrel.toml
.codex/agents/model-rowan.toml
.codex/agents/observe-betty.toml
.codex/agents/observe-peter.toml
.codex/agents/sense-mira.toml
.codex/agents/sense-zephyr.toml
.codex/agents/simulate-niko.toml
.codex/agents/simulate-tundra.toml
.codex/config.toml
.gitignore
.zerohandoff/frozen/latest.json
.zerohandoff/learning/.commit.lock
.zerohandoff/learning/commits.jsonl
.zerohandoff/learning/commits/000001_step10_shiftpulse_live_20260715.json
.zerohandoff/learning/commits/000002_exp01_roomready_20260716.json
.zerohandoff/learning/commits/000003_exp02_stocksignal_20260716.json
.zerohandoff/learning/commits/000004_exp03_policyproof_20260716.json
.zerohandoff/learning/commits/000004_exp03_policyproof_corrected_20260717.json
.zerohandoff/learning/commits/000005_exp04_coveragecanvas_20260717.json
.zerohandoff/learning/commits/000006_step13_echoledger_live_20260718.json
.zerohandoff/learning/inference_relationships.json
.zerohandoff/learning/initial_relationships.json
.zerohandoff/learning/superseded/000001_step10_shiftpulse_live_20260715_c76e4388eb1a.json
.zerohandoff/learning/superseded/000004_exp03_policyproof_20260716_f79acddf1f7a.json
AGENTS.md
context/idea.md
context/overview.md
context/rules.md
context/trust_moa.html
context/trust_moa.md
data/puzzle_stats.json
data/puzzles.fixture.json
data/puzzles.jsonl
EXECUTION_PLAN.md
experiments/audits/exp02_stocksignal_20260716.json
experiments/audits/exp03_policyproof_20260716.json
experiments/audits/exp03_policyproof_corrected_20260717.json
experiments/audits/exp04_coveragecanvas_20260717.json
experiments/audits/step13_echoledger_live_20260718.json
experiments/campaign.json
experiments/reports/exp01_roomready_20260716/baseline_failed.json
experiments/reports/exp01_roomready_20260716/demo_plan.repair.json
experiments/reports/exp01_roomready_20260716/final_after_demo.json
experiments/reports/exp01_roomready_20260716/final.json
experiments/reports/exp02_stocksignal_20260716/final.json
experiments/requests/exp01_roomready.json
experiments/requests/exp02_stocksignal.json
experiments/requests/exp03_policyproof.json
experiments/requests/exp04_coveragecanvas.json
experiments/requests/exp05_incidentweave.json
experiments/requests/exp06_bidlens.json
experiments/requests/exp07_routeguard.json
experiments/requests/exp08_capacitylab.json
experiments/requests/exp09_changegraph.json
experiments/requests/exp10_resilienceroom.json
experiments/SCORECARD.md
gwt.py
HACKATHON_CONSTRAINTS.md
JUDGE_GUIDE.md
LICENSE
overview.html
personalities/conscientiousness.md
personalities/dominance.md
personalities/influence.md
personalities/steadiness.md
pipeline/build_request_echoledger_step13.json
pipeline/build_request_step10.json
pipeline/demo/shiftpulse_step10_narration.txt
pipeline/intake_template.md
PROJECT_PLAN.json
pyproject.toml
README.md
RUNBOOK.md
schemas/AgentInvocation.schema.json
schemas/AgentResult.schema.json
schemas/ArtifactEnvelope.schema.json
schemas/BuildRequest.schema.json
schemas/DirectedRelationship.schema.json
schemas/EpisodeRecord.schema.json
schemas/EventRecord.schema.json
schemas/FrozenRelationshipSnapshot.schema.json
schemas/GateResult.schema.json
schemas/HandoffAssessment.schema.json
schemas/InferenceLearningState.schema.json
schemas/InferenceNightOutput.schema.json
schemas/NightCuratorOutput.schema.json
schemas/RelationshipPolicy.schema.json
schemas/RelationshipVector.schema.json
schemas/RepairPacket.schema.json
schemas/RunManifest.schema.json
schemas/ShadowTrustUpdate.schema.json
scripts/assemble_submission_demo.py
scripts/audit_experiment.py
scripts/record_browser_demo.mjs
scripts/record_submission_overview.mjs
scripts/submission_package.py
settings/continual_learning.json
settings/delivery.json
settings/memory.json
settings/metaplasticity.json
settings/models.json
settings/relationship_policy.json
settings/teams.json
settings/training.json
src/zerohandoff/__init__.py
src/zerohandoff/api.py
src/zerohandoff/cli.py
src/zerohandoff/config.py
[87 more files omitted for size]
```

### Dependencies

- pyproject.toml: edge-tts@>=7,<8, fastapi@>=0.115,<1, httpx@>=0.28,<1, jsonschema@>=4.23,<5, Pillow@>=11,<13, pydantic@>=2.10,<3, pytest@>=8.3,<10, uvicorn@>=0.34,<1
- submission/sandbox/echoledger/package.json: @types/react@19.2.17, @types/react-dom@19.2.3, @vitejs/plugin-react@6.0.3, react@19.2.7, react-dom@19.2.7, typescript@7.0.2, vite@8.1.5
- ui/package.json: @types/react@latest, @types/react-dom@latest, @vitejs/plugin-react@latest, react@latest, react-dom@latest, typescript@latest, vite@latest

### Recent commits (newest first)

- 13/13
- 13/13
- 13/13
- 13/13
- 12/13
- 12/13
- 10/13
- 10/13
- 10/13
- 9/10
- 4/13

## Key source files (fetched from GitHub, selected and truncated for size)

### HACKATHON_CONSTRAINTS.md

```markdown
- Build using Codex and GPT-5.6 in one of four tracks.
- Submission deadline: July 21, 2026 at 5:00 PM PT.
- Deliver a polished, working, testable project—not merely a proof of concept.
- Optimize equally for implementation, design, impact, and originality.
- Maintain clear evidence of work completed during the submission period.
- Provide a public demo or unrestricted judge access, a repository, setup instructions, and a sub-three-minute narrated YouTube demo.
- Document how Codex and GPT-5.6 contributed, including our major human-made decisions.
- Preserve this task as the core build session so its /feedback session ID can be submitted.
- Use properly licensed assets, dependencies, APIs, and data.
- If we build a developer tool or plugin, include installation instructions, supported platforms, and an immediately testable instance or sandbox.
```

### AGENTS.md

```markdown
# Repository Instructions

## Protected `context/` folder

Treat every file inside `context/` as user-owned, read-only source material.

- Never create, edit, format, overwrite, rename, move, or delete any non-HTML
  file inside `context/`.
- HTML files inside `context/` are the only exception and may be created or
  modified when the user requests documentation or visual synchronization.
- Reading and inspecting any file inside `context/` is allowed.
- If an implementation or documentation task requires a change to a protected
  Markdown, text, JSON, or other non-HTML context file, stop and tell the user
  what needs to change. The user must make that source-file change.
- Never automatically synchronize information back into protected context
  source files. Synchronization flows from those sources into allowed HTML or
  files outside `context/`, not the other way around.

This protection is permanent and takes priority over normal documentation-sync
or cleanup behavior.

```

### pyproject.toml

```
[build-system]
requires = ["hatchling>=1.25"]
build-backend = "hatchling.build"

[project]
name = "zerohandoff"
version = "0.1.0"
description = "Chat-native autonomous software delivery with trained two-agent cells."
readme = "README.md"
requires-python = ">=3.11"
license = { text = "MIT" }
dependencies = [
  "fastapi>=0.115,<1",
  "jsonschema>=4.23,<5",
  "Pillow>=11,<13",
  "pydantic>=2.10,<3",
  "uvicorn>=0.34,<1",
]

[project.optional-dependencies]
narration = [
  "edge-tts>=7,<8",
]
dev = [
  "httpx>=0.28,<1",
  "pytest>=8.3,<10",
]

[project.scripts]
zerohandoff-internal = "zerohandoff.cli:main"

[tool.hatch.build.targets.wheel]
packages = ["src/zerohandoff"]

[tool.pytest.ini_options]
addopts = "-q"
pythonpath = ["src"]
testpaths = ["tests"]

[tool.ruff]
line-length = 100
target-version = "py311"

[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B"]

```

### ui/package.json

```
{
  "name": "zerohandoff-control-room",
  "private": true,
  "version": "0.1.0",
  "type": "module",
  "scripts": {
    "dev": "vite --host 127.0.0.1",
    "build": "tsc -b && vite build",
    "typecheck": "tsc -b --pretty false"
  },
  "dependencies": {
    "@vitejs/plugin-react": "latest",
    "vite": "latest",
    "react": "latest",
    "react-dom": "latest"
  },
  "devDependencies": {
    "@types/react": "latest",
    "@types/react-dom": "latest",
    "typescript": "latest"
  }
}

```

### submission/sandbox/echoledger/package.json

```
{
  "name": "build-echoledger-an-internal-enterprise",
  "private": true,
  "version": "0.1.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "test": "bash tests/browser.acceptance.sh",
    "typecheck": "tsc -b --pretty false"
  },
  "dependencies": {
    "react": "19.2.7",
    "react-dom": "19.2.7"
  },
  "devDependencies": {
    "@vitejs/plugin-react": "6.0.3",
    "vite": "8.1.5",
    "typescript": "7.0.2",
    "@types/react": "19.2.17",
    "@types/react-dom": "19.2.3"
  }
}

```

### ui/src/main.tsx

```typescript
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './styles.css';
import './setup.css';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
);

```

### src/zerohandoff/cli.py

```python
from __future__ import annotations

import argparse
import json
from pathlib import Path
from typing import Any

import uvicorn

from zerohandoff.api import create_app
from zerohandoff.models import BuildRequest, Stage
from zerohandoff.schemas import export_schemas
from zerohandoff.service import RunService


def _json(value: Any) -> None:
    if hasattr(value, "model_dump"):
        value = value.model_dump(mode="json")
    print(json.dumps(value, indent=2, sort_keys=True, default=str))


def _parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="zerohandoff-internal",
        description="Internal deterministic engine invoked by ZeroHandoff Codex skills.",
    )
    parser.add_argument("--repo", type=Path, default=Path.cwd())
    commands = parser.add_subparsers(dest="command", required=True)
    doctor = commands.add_parser("doctor")
    doctor.add_argument("--live", action="store_true")
    train = commands.add_parser("train")
    train.add_argument("--adapter", choices=("fixture", "codex"), default="fixture")
    train.add_argument("--rounds", type=int, default=None)
    train.add_argument("--run-id")
    run = commands.add_parser("run")
    run.add_argument("--request", required=True, type=Path)
    run.add_argument("--adapter", choices=("fixture", "codex"), default="fixture")
    run.add_argument("--frozen", type=Path)
    run.add_argument("--run-id")
    run.add_argument("--resume", action="store_true")
    run.add_argument("--fault-stage", choices=[stage.value for stage in Stage])
    repair_learning = commands.add_parser("repair-learning")
    repair_learning.add_argument("--run-id", required=True)
    repair_learning.add_argument("--reason", required=True)
    repair_demo = commands.add_parser("repair-demo")
    repair_demo.add_argument("--run-id", required=True)
    repair_demo.add_argument("--narration-file", type=Path)
    repair_demo.add_argument("--plan-file", type=Path)
    status = commands.add_parser("status")
    status.add_argument("run_id", nargs="?")
    serve = commands.add_parser("serve")
    serve.add_argument("--host", default="127.0.0.1")
    serve.add_argument("--port", type=int, default=8765)
    schemas = commands.add_parser("schemas")
    schemas.add_argument("--output", type=Path, default=Path("schemas"))
    puzzles = commands.add_parser("puzzles")
    puzzles.add_argument("action", choices=("generate", "audit", "validate"))
    puzzles.add_argument("--corpus", type=Path, default=Path("data/puzzles.jsonl"))
    puzzles.add_argument("--stats", type=Path, default=Path("data/puzzle_stats.json"))
    puzzles.add_argument("--seed", type=int, default=42)
    return parser


def main() -> int:
    args = _parser().parse_args()
    service = RunService(args.repo)
    if args.command == "doctor":
        from zerohandoff.doctor import doctor

        report = doctor(args.repo, live=args.live)
        _json(report)
        return 0 if report["ok"] else 1
    if args.command == "train":
        result = service.train(adapter=args.adapter, rounds=args.rounds, run_id=args.run_id)
        _json(
            {
                "run_id": result.run_id,
                "status": result.status,
                "store_root": result.store_root,
                "metrics": result.metrics,
                "frozen_digest": result.frozen_snapshot.content_digest
                if result.frozen_snapshot
                else None,
            }
        )
        return 0
    if args.command == "run":
        request = BuildRequest.model_validate_json(args.request.read_text())
        result = service.deliver(
            request=request,
            adapter=args.adapter,
            frozen_path=args.frozen,
            run_id=args.run_id,
            fault_stage=Stage(args.fault_stage) if args.fault_stage else None,
            resume=args.resume,
        )
        _json(
            {
                "run_id": result.run_id,
                "status": result.status,
                "store_root": result.store_root,
                "bundle": result.bundle.bundle_dir,
                "video": result.demo.video_path,
            }
        )
        return 0
    if args.command == "repair-learning":
        _json(service.invalidate_learning_commit(args.run_id, reason=args.reason))
        return 0
    if args.command == "repair-demo":
        narration_override = (
            args.narration_file.read_text() if args.narration_file else None
        )
        plan_override = (
            json.loads(args.plan_file.read_text()) if args.plan_file else None
        )
        _json(
            service.repair_demo(
                args.run_id,
                narration_override=narration_override,
                demo_plan_override=plan_override,
            )
        )
        return 0
    if args.command == "status":
        _json(service.run_summary(args.run_id) if args.run_id else service.list_runs())
        return 0
    if args.command == "schemas":
        _json({"schemas": [str(path) for path in export_schemas(args.output)]})
        return 0
    if args.command == "puzzles":
        from zerohandoff.training.corpus import (
            audit_corpus,
            generate_corpus,
            validate_corpus,
            write_corpus,
        )
        from zerohandoff.training.puzzles import PuzzleRepository

        corpus_path = (args.repo / args.corpus).resolve()
        stats_path = (args.repo / args.stats).resolve()
        if args.action == "generate":
            report = write_corpus(
                generate_corpus(args.seed),
                corpus_path,
                stats_path,
                seed=args.seed,
            )
        else:
            repository = PuzzleRepository.load(corpus_path)
            report = (
                audit_corpus(repository.puzzles)
                if args.action == "audit"
                else validate_corpus(repository.puzzles, seed=args.seed)
            )
        _json(report)
        return 0 if re
[truncated — 239 more characters]
```

### ui/src/App.tsx

```typescript
import { useCallback, useEffect, useMemo, useState } from 'react';

type View = 'setup' | 'live' | 'evidence' | 'delivery';
type RunStatus = 'created' | 'running' | 'repairing' | 'completed' | 'failed' | 'cancelled' | 'unknown';

interface RunSummary {
  run_id: string;
  status: RunStatus;
  current_stage?: string;
  completed_stages: string[];
  relationship_vector_digest?: string;
  adapter?: string;
  build_request?: {
    idea: string;
    audience: string;
    outcome: string;
    constraints: string[];
    must_have_capabilities: string[];
  };
  repair_count: number;
  gate_count: number;
  agent_call_count: number;
  event_count: number;
  failure_reason?: string;
  bundle_ready: boolean;
  preview_ready: boolean;
  video_ready: boolean;
}

interface Artifact {
  artifact_id: string;
  artifact_type: string;
  stage: string;
  version: number;
  lead: string;
  peer: string;
  gate_status: string;
  content_digest: string;
  contract_item_ids?: string[];
  requirement_ids?: string[];
}

interface Evidence {
  artifacts: Artifact[];
  gates: Array<{ stage: string; decision: string; findings?: unknown[] }>;
  repairs: Array<{ repair_id: string; stage: string; attempt: number }>;
  commands: Array<{ stage?: string; command?: string[]; exit_code?: number }>;
  agent_calls: Array<{ stage: string; actor?: string; agent?: string; status: string; duration_ms?: number }>;
  demo: Array<{ status: string; duration_seconds?: number; checksum?: string }>;
}

interface Doctor {
  ok: boolean;
  checks: Record<string, { ok?: boolean; available?: boolean; version?: string }>;
  frozen_snapshot: { ready: boolean; path?: string };
}

const stages = [
  ['SENSE', 'Opportunity Model'],
  ['MODEL', 'Outcome Model'],
  ['COMPOSE', 'Capability Graph'],
  ['DECIDE', 'Decision Graph'],
  ['SIMULATE', 'Scenario Model'],
  ['EXECUTE', 'Autonomous Change'],
  ['OBSERVE', 'Evidence + Learning'],
] as const;

const emptyEvidence: Evidence = { artifacts: [], gates: [], repairs: [], commands: [], agent_calls: [], demo: [] };

async function getJson<T>(url: string, init?: RequestInit): Promise<T> {
  const response = await fetch(url, init);
  if (!response.ok) {
    const body = await response.json().catch(() => ({}));
    throw new Error(body.detail || `Request failed (${response.status})`);
  }
  return response.json() as Promise<T>;
}

function Mark({ ok }: { ok: boolean }) {
  return <span className={`mark ${ok ? 'ok' : 'waiting'}`} aria-hidden="true">{ok ? '✓' : '·'}</span>;
}

function StatusPill({ status }: { status: string }) {
  return <span className={`status status-${status.toLowerCase()}`}><span />{status.replaceAll('_', ' ')}</span>;
}

function SetupView({ doctor, run }: { doctor?: Doctor; run?: RunSummary }) {
  const request = run?.build_request;
  return <section className="view-grid setup-grid" aria-labelledby="setup-title">
    <div className="intro-panel">
      <span className="kicker">One request · zero handoffs</span>
      <h2 id="setup-title">Start in Codex. Watch everything here.</h2>
      <p>Give Codex one short build request. Seven trained two-agent cells carry it from definition to a verified delivery bundle while this Control Room remains observational.</p>
      <div className="readiness-card">
        <div><Mark ok={Boolean(doctor?.ok)} /><span>Local runtime</span><small>{doctor?.ok ? 'Ready' : 'Checking'}</small></div>
        <div><Mark ok={Boolean(doctor?.frozen_snapshot.ready)} /><span>Trust baseline</span><small>{doctor?.frozen_snapshot.ready ? 'Verified' : 'Train first'}</small></div>
        <div><Mark ok={Boolean(doctor?.checks.codex?.available)} /><span>Codex adapter</span><small>{doctor?.checks.codex?.version || 'Checking'}</small></div>
      </div>
    </div>
    <article className="request-card">
      <div className="card-head"><div><span className="kicker">Build request</span><h3>{request ? 'Captured by the chat workflow' : 'Use the Codex chat workflow'}</h3></div><span className="step-chip">01</span></div>
      {request ? <div className="captured-request">
        <div className="captured-field"><span>Product idea</span><p>{request.idea}</p></div>
        <div className="field-row">
          <div className="captured-field"><span>Audience</span><p>{request.audience}</p></div>
          <div className="captured-field"><span>Desired outcome</span><p>{request.outcome}</p></div>
        </div>
        <div className="captured-field"><span>Must-have capabilities</span><p>{request.must_have_capabilities.join(' · ') || 'Defined autonomously from the request'}</p></div>
        <div className="captured-field"><span>Constraints</span><p>{request.constraints.join(' · ') || 'Local React/Vite delivery profile'}</p></div>
      </div> : <div className="chat-start">
        <p>In this repository’s Codex chat, invoke:</p>
        <code className="chat-command">$run-pipeline</code>
        <p className="chat-note">Codex collects the idea, audience, outcome, and constraints, verifies the trained trust baseline, then starts the run. No terminal command or UI approval is required.</p>
      </div>}
    </article>
  </section>;
}

function LiveView({ run, artifacts, onCancel }: { run?: RunSummary; artifacts: Artifact[]; onCancel: () => void }) {
  if (!run) return <Empty title="No active run" copy="Start a build request to watch the seven delivery cells work." />;
  const latestByStage = new Map<string, Artifact>();
  artifacts.forEach(artifact => {
    const current = latestByStage.get(artifact.stage);
    if (!current || artifact.version > current.version) latestByStage.set(artifact.stage, artifact);
  });
  const complete = new Set(run.completed_stages);
  return <section aria-labelledby="live-title">
    <div className="run-hero">
      <div><span className="kicker">Live orchestration</span><h2 id="live-title">The lifecycle moves. The trained baseline stays intact.</h2><p className="mono">{run.run_id}</p></div>
      <div className="hero-actions"><StatusPill status={run.status}
[truncated — 9136 more characters]
```

### submission/sandbox/echoledger/src/main.tsx

```typescript
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './styles.css';
ReactDOM.createRoot(document.getElementById('root')!).render(<React.StrictMode><App /></React.StrictMode>);

```

### submission/sandbox/echoledger/src/App.tsx

```typescript
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
import {
  DURATION, activeSegmentFor, appendActionAudit, createInitialState, evidenceRegistry, formatTime, historicalCalls, primaryCall,
  primarySegments, semanticCaseBrief, signalEvidenceIds, teams, type ActionDraft, type ActionSnapshot,
  type ActionAuditEntry, type ActionStatus, type Decision, type InvestigationState, type ReviewState, type Team,
} from './domain';

const MASK = '[REDACTED — email-like fictional value]';

function PrototypeNotice() {
  return <div className="prototype-notice" role="note">
    <div><strong>Deterministic prototype</strong><span>Transcription, sensitive candidates, classifications, recurrence, confidence values, and proposed actions are fictional fixed behavior requiring human review — not production AI.</span></div>
    <span className="local-badge"><span aria-hidden="true">●</span> Local only</span>
  </div>;
}

function StatePill({ state }: { state: string }) {
  const icon = state === 'confirmed' || state === 'Completed' ? '✓' : state === 'rejected' ? '×' : state === 'redacted' ? '◼' : state === 'restored' ? '↺' : state === 'In progress' ? '→' : state === 'Assigned' ? '◆' : '○';
  return <span className={`state-pill state-${state.toLowerCase().replaceAll(' ', '-')}`}><span aria-hidden="true">{icon}</span> {state[0].toUpperCase() + state.slice(1)}</span>;
}

function EvidenceMeta({ callId, segmentId, speaker, timestamp }: { callId: string; segmentId: string; speaker: string; timestamp: string }) {
  return <div className="evidence-meta">{callId} · {segmentId} · {speaker} · {timestamp}</div>;
}

function DecisionHistory({ title, history, empty = 'No human decisions yet' }: { title: string; history: Decision[]; empty?: string }) {
  return <details className="decision-history"><summary>{title}<span>{history.length} event{history.length === 1 ? '' : 's'}</span></summary>{history.length === 0 ? <p>{empty}</p> : <ol>{history.map(entry => <li key={`${entry.subjectId}-${entry.sequence}`}><span>#{String(entry.sequence).padStart(3, '0')}</span><div><strong>{entry.decision}</strong><small>{entry.subjectId} · {entry.reviewer} · {entry.previous} → {entry.result}</small></div></li>)}</ol>}</details>;
}

function ActionDecisionHistory({ history }: { history: ActionAuditEntry[] }) {
  return <details className="decision-history"><summary>Committed action history<span>{history.length} field change{history.length === 1 ? '' : 's'}</span></summary>{history.length === 0 ? <p>No committed field changes yet</p> : <ol>{history.map(entry => <li key={entry.sequence}><span>#{String(entry.sequence).padStart(3, '0')}</span><div><strong>{entry.field}</strong><small>{entry.subjectId} · {entry.reviewer} · {entry.previousValue} → {entry.resultingValue}</small></div></li>)}</ol>}</details>;
}

function Playback({ state, setState, onSeek }: { state: InvestigationState; setState: React.Dispatch<React.SetStateAction<InvestigationState>>; onSeek: (time: number, id: string) => void }) {
  const audioRef = useRef<HTMLAudioElement>(null);
  const [metadataValid, setMetadataValid] = useState(false);

  useLayoutEffect(() => {
    const audio = audioRef.current;
    if (audio && Number.isFinite(audio.duration) && Math.abs(audio.currentTime - state.currentTime) > .05) audio.currentTime = state.currentTime;
  }, [state.currentTime]);

  useEffect(() => {
    const audio = audioRef.current;
    if (!audio) return;
    if (state.playing) void audio.play().catch(() => setState(current => ({ ...current, playing: false })));
    else audio.pause();
  }, [setState, state.playing]);

  const syncFromNativeAudio = () => {
    const audio = audioRef.current;
    if (!audio) return;
    const currentTime = Math.min(DURATION, audio.currentTime);
    setState(current => ({ ...current, currentTime, selectedSegmentId: activeSegmentFor(currentTime).id, playing: !audio.paused && !audio.ended }));
  };

  return <section className="player-card" aria-labelledby="recording-title">
    <audio
      ref={audioRef}
      data-testid="bundled-audio"
      src={primaryCall.recording ?? undefined}
      preload="metadata"
      aria-label="Bundled 6 minute 18 second deterministic synthetic fictional call recording"
      onLoadedMetadata={event => setMetadataValid(Math.abs(event.currentTarget.duration - DURATION) < .01)}
      onTimeUpdate={syncFromNativeAudio}
      onPlay={syncFromNativeAudio}
      onPause={syncFromNativeAudio}
      onEnded={syncFromNativeAudio}
    />
    <div className="section-kicker">PRIMARY CALL</div>
    <div className="player-title-row"><div><h2 id="recording-title">Northstar Labs · EL-1042</h2><p>Fictional support call · 18 Jul 2026 · 06:18</p></div><span className="asset-badge">{metadataValid ? '✓ 06:18 bundled recording' : 'Bundled synthetic recording'}</span></div>
    <div className="player-controls">
      <button className="round-play" type="button" data-testid="play" aria-label={state.playing ? 'Pause fictional call' : 'Play fictional call'} onClick={() => setState(current => ({ ...current, playing: !current.playing }))}><span aria-hidden="true">{state.playing ? 'Ⅱ' : '▶'}</span></button>
      <div className="timeline-wrap"><label htmlFor="timeline" className="sr-only">Playback position</label><input id="timeline" data-testid="timeline" type="range" min="0" max={DURATION} step="1" value={Math.floor(state.currentTime)} aria-valuetext={`${formatTime(state.currentTime)} of ${formatTime(DURATION)}`} onChange={event => onSeek(Number(event.target.value), activeSegmentFor(Number(event.target.value)).id)} /><div className="timeline-meta"><output>{formatTime(state.currentTime)} / {formatTime(DURATION)}</output><span>Native audio position · fixed transcript boundaries</span></div></div>
    </div>
    <div className="markers" aria-label="Exact evidence markers"><button type="button" data-testid="complaint-marker" onClick={() => onSeek(151, 'SEG-14')}><span aria-hidden="true">◆</span> Complaint <strong>02:31</strong></but
[truncated — 27424 more characters]
```

[73 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]