Skip to content

Development

The package map as it is at merge time, the tests, and linting. Back to the README.

Files

The holophyte/ package is the factory; factory.py is its entry point. Each module, one line:

  • holophyte/__init__.py — the package docstring: which module owns what.
  • holophyte/loop/__init__.py — the loop package; empty.
  • holophyte/pr/__init__.py — the pr package; empty.
  • holophyte/babysit/__init__.py — the babysit package; empty.
  • holophyte/review/__init__.py — the review package; empty.
  • holophyte/story/__init__.py — the story package; empty.
  • holophyte/tickets/__init__.py — the tickets package; empty.
  • holophyte/board/__init__.py — the board package; empty.
  • holophyte/isolation/__init__.py — the isolation package; empty.
  • holophyte/agents/__init__.py — the agents package; empty.
  • holophyte/host/__init__.py — the host package; empty.
  • holophyte/serve/__init__.py — the serve package; empty.
  • holophyte/config/__init__.py — the config package; empty.
  • holophyte/cli/__init__.py — the cli package; empty.
  • holophyte/scout/__init__.py — the scout package; empty.
  • holophyte/cli/cli_project.py — project registration, listing and admission commands.
  • holophyte/cli/cli_story.py — the --file-story SLUG, --approve-story KEY-n and --witness-pass KEY-n options and their dispatch.
  • holophyte/story/story_approval.py — approve() approves a planned story: the one-open-story and revision checks, the baseline witness run at main's tip, then the frozen plan and its children released to Ready in one store transaction.
  • holophyte/story/story_filing.py — files a validated story directory on a native board: its parent, its children in dependency order and its story rows in one store transaction, then the header lines in its files.
  • holophyte/host/registry.py — the host registry, HOLOPHYTE_HOME/host.toml: the projects a host serves and sweeps, by path, reloaded when the file changes; its one writer path is an exclusive temporary file and a rename.
  • holophyte/cli/entry.py — cli(), the entry point: parses the command line and dispatches to the mode it names, the loop itself included.
  • holophyte/cli/arguments.py — build_parser(), every flag and its help, and the checks that refuse a modifier without its mode.
  • holophyte/cli/store_verbs.py — the operator verbs that write the store: --requeue, --approve, --babysit, --repoint, --close, --abort and the rest.
  • holophyte/cli/board_verbs.py — the modes on the built board: --sweep [--act], --board-diff, --board-import, --move, --cancel, and require_board().
  • holophyte/cli/host_modes.py — the host forms of --status, --serve and --supervise, the read-only modes, and start_supervisor().
  • holophyte/cli/stale_paths.py — stale_paths(): the config path entries ([merge] ui_paths, [review] high_paths and medium_paths, [worktree] setup inputs) that match no file tracked on main, once per main commit.
  • holophyte/holo/__init__.py — the holo command's package.
  • holophyte/holo/cli.py — main(), the holo console script pyproject.toml declares: --version prints the package version and the checkout's short HEAD; a subcommand resolves its project, translates through the grammar table and runs as _legacy_cli(), a write command through run_write(), report through report_page, or answers as a read; holo project is factory.py project.
  • holophyte/holo/completion.py — holo completion bash|zsh|fish's scripts and the hidden holo __complete helper they call: candidates from the parser the grammar table builds, and ticket keys from holo board --json cached per project for 60 seconds.
  • holophyte/holo/follow.py — holo follow: polls the project's narrative run events and ledger entries from a starting time by store id, and the ledger view's schema migrations, prints each new one once, oldest first, as a line or a JSON object, and one ✗ line when a live run's or the supervisor's heartbeat goes stale, once until it recovers.
  • holophyte/holo/reads.py — the holo reads runs, run N, attention, board and ticket KEY: each calls the daemon's view in process, prints its body for --json or a compact listing of it, and maps its status to the exit.
  • holophyte/holo/resolve.py — a holo command's project from -p, HOLO_PROJECT, the current repository or client.toml's default_project, the client config reader, and the refusal when none answers.
  • holophyte/holo/grammar.py — the holo command table: one canonical row per factory.py mode, the send-back, steer, start and stop rows, the interventions actions each write row records, the ticket VERB and loop start|stop aliases, and their translation to the argv build_parser() parses; and the read rows, run show N among them.
  • holophyte/holo/mcp_server.py — holo mcp: the MCP SDK's low-level Server on stdio, listing the tool table's rows and answering a call by running its holo command; the SDK is imported only here and in the HTTP form below, and without it holo mcp exits 1 naming the package and its install command.
  • holophyte/holo/mcp_http.py — holo mcp --http: the same server as the SDK's stateless Streamable HTTP app under uvicorn, a process of its own, not the daemon; a guard before it demands the host's machine token (401 {}), refuses an Origin (403) and every method but POST (405); the writes are listed only under host.toml's [serve] actions, and it exits 0 when the checkout's HEAD moves.
  • holophyte/holo/mcp_tools.py — the holo mcp tool table: each tool's name, input schema, tier and the holo ... --json argv it runs as a subprocess with a timeout, and its result, a JSON object or text, or an error carrying the command's message.
  • holophyte/holo/results.py — a write command's one result: the verb run with its printed line and refusal captured, the interventions row it wrote read back as recorded, printed as JSON with --json or as a ✓/✗ line.
  • holophyte/holo/transport.py — a holo command run on client.toml's host over ssh: the remote command line, the ssh call, its exit code and the via line; holo follow's JSON lines rendered as each arrives and holo status --watch's status object asked for each frame; HOLO_TRANSPORT=local keeps it local. With transport = "http", the route table of the commands the host daemon serves, the bearer call to url, and the refusal of every other command.
  • holophyte/holo/render.py — the pieces every holo page shares: a clock time in the client's zone, a relative age, a short hash, and a symbol coloured only on a terminal without NO_COLOR.
  • holophyte/holo/status_page.py — holo status without --json: the --status --json object, the project or the host form, rendered as needs you, running, quiet and problems sections and a sweep and build footer; with --watch, redrawn every interval in place on a terminal, or after a separator line carrying its time when piped.
  • holophyte/holo/report_page.py — holo report: one object for a window, --since, built read-only from the ended runs, failure kinds, gap layers, human interventions, send-backs and consumed notes, printed with --json or rendered as counts first, then the window's runs and, with --notes, the notes.
  • holophyte/holo/run_page.py — holo run N without --json: the run's detail and files bodies rendered as a header, a timeline of its events and rounds, its files and the next commands its phase and outcome call for.
  • holophyte/holo/units.py — holo start, which records the launch and starts the project's loop unit through the daemon's unit_action(), or runs the loop in the terminal with --foreground; and holo stop, which holds the project and, with --now, aborts its live runs.
  • holophyte/holo/__main__.py — python3 -m holophyte.holo, the same command without an install.
  • holophyte/config/project.py — where a project's state lives (HOLOPHYTE_HOME, the <slug> directory, legacy adoption) and the Project value.
  • holophyte/config/reader.py — load_config(), config_table(), KNOWN_KEYS and the role defaults an absent [agents] table leaves in place.
  • holophyte/config/checks.py — the startup checks: unknown keys, the whole document, agent commands, the default routes and the [worktree] table.
  • holophyte/config/agent_settings.py — the [agents] readers: commands, fallbacks, the review route, the session pattern and budget_scale.
  • holophyte/config/worktree_settings.py — the [worktree] readers: setup commands and cap, carry list, branch prefix and environment allow-lists.
  • holophyte/config/serve_settings.py — the [console] and [serve] readers.
  • holophyte/config/review_settings.py — the [review] reader: the blast-radius path lists and the fan-in and packages thresholds.
  • holophyte/config/config_tables.py — the per-table readers (KO-397): the [supervisor], [loop], [board], [merge] and [report] tables with CONSOLE_KEYS and split_address(), the namedtuples and defaults they own.
  • holophyte/failure_reason.py — bounded failure reasons and their structured event facts for verify, adjudication and fix rounds.
  • holophyte/loop/gates.py — the verify gate: a ticket's command in, a red or green fail-loud report out.
  • holophyte/agents/agent_routes.py — process-owned route state and live console snapshots.
  • holophyte/agents/agent_turns.py — per-turn command labels, timing and run events.
  • holophyte/isolation/launcher.py — host/container implementer launch seam, validated isolation settings, credential boundary and container cleanup.
  • holophyte/isolation/isolation_git.py — Git metadata validation and atomic object copying.
  • holophyte/isolation/isolation_return.py — Git ref and index locks for atomic turn returns.
  • holophyte/isolation/isolation_clone.py — disposable container checkouts and sanitized, fast-forward-only commit imports with environment-file exclusions.
  • holophyte/agents/roles.py — the agent routes and the agent() call, one turn of a role.
  • holophyte/agents/probe_log.py — locked, atomic records of each route’s last probe.
  • holophyte/agents/probes.py — route probes, run once before a role dispatches.
  • holophyte/scout/answer.py — a finished scout's answer turn: the taken ask, its report labelled F1, F2 and A1 on, the lead's session resumed in the lead worktree, and the answer posted to the thread, or the failure recorded, as the scout returns to done.
  • holophyte/scout/brief.py — the short texts served for a scout whose lead wrote none: a scout title cut from its ask, a task title cut from its question, and a failed task's one-line reason read from its excerpt.
  • holophyte/scout/titler.py — a scout's short title, asked of the [questions.titles] route from its ask as its first run starts, stored on the scout and recorded as a scout_title event.
  • holophyte/scout/context.py — a scout's context pack: its subject's run, ledger, verify tail, findings, files and ticket, cut to a cap, for the lead's plan goal.
  • holophyte/scout/filing.py — POST /actions/scout-file, holo scout file and the holo mcp scout_file tool: a done scout's action item judged by the ticket checker for Backlog, filed to Backlog through the project's board (as a draft whose first note lists the checker's problems, when it has any), and its filed key recorded on the item.
  • holophyte/scout/follow_up.py — a steered scout's follow-up pass: the lead's session resumed to plan one wave on the steer, the helpers under the pass's own small budget, and the pass's report and action items appended after the earlier passes', or the failure or stop recorded, as the scout returns to done.
  • holophyte/scout/github_mcp.py — python3 -m holophyte.scout.github_mcp --project PATH --scout ID: the scout's github_get and github_tests tools served over stdio on the MCP SDK's low-level server, and the server entry a lead or read helper turn carries under [scout] github = true.
  • holophyte/scout/github_reads.py — the scout's GitHub reads: the guard holding a call to an allow-listed GET of its own repository (a path without repos/OWNER/NAME/ resolved under it; actions/jobs/ID/logs and the commits listing among the reads), the fetch through holophyte.pr.github, the trimmed and capped answer, a job log cut to its tail, a run's JUnit and Playwright report rows, and the scout_github event per call.
  • holophyte/scout/guardrails.py — a guardrails scout's findings digest: the last 30 days of review findings, guardrail follow-ups and ticket defects from the findings view, each with an id, clustered by path and ranked, for the lead's plan goal.
  • holophyte/scout/launch.py — POST /actions/scout, holo scout and the holo mcp scout tool: a scout's launch validated against [scout], recorded queued with its scout_queued event, then started by the host sweep's scout step when a host scout slot is free.
  • holophyte/scout/lead.py — the scout's lead: its route probe, a plan turn under the scout's time budget, the helper waves with a synthesis turn between them, and a report turn resuming its session.
  • holophyte/scout/prior.py — a scout's paths from its ask, links and context pack, and the newest earlier done scouts' reports that cited them, with what changed since their base, for the lead's plan goal.
  • holophyte/scout/proposals.py — a scout's proposal cards: the proposals an answer turn's ANSWER or a pass's REPORT ends with, each read as a question with two to four choices and at most MAX_PROPOSALS kept, then posted to the thread as cards answering the taken message.
  • holophyte/scout/report.py — a scout report's shape checked field by field, and each file/line evidence item resolved against the base commit.
  • holophyte/scout/run_helpers.py — a scout's run helpers: the helper's own shared clone of the project, the project's [worktree] setup in it, the run goal (the reproduce goal of a bug scout or the measure goal of another kind), the turn launched under implementer_isolation, the refs the turn changed in its clone and the stored reproduction level.
  • holophyte/scout/scout_config.py — scout_config: the [scout] table's lead and helper routes, each a harness Seat, their defaults and budget.
  • holophyte/scout/scout_contract.py — the TypedDicts of GET /scouts, GET /scouts/ID and GET /scouts/routes, emitted by holophyte/serve/contract.py.
  • holophyte/scout/scout_probes.py — --scout-probe: one probe of each scout route in a detached checkout of main, recorded in route-probes.json.
  • holophyte/scout/scout_page.py — holo scout page: a done scout's GET /scouts/ID body as Lotuspod markdown, its findings numbered as the thread's labels are, its action items with their drafts, and a "Decisions for the maintainer" table of its unfiled items, with fenced lines and table cells escaped for Lotuspod's converter.
  • holophyte/scout/scout_process.py — --scout ID: one scout in its own process, its lead worktree with pushes refused, its heartbeat and its end, an answering scout's answer turn, or a steered scout's follow-up pass.
  • holophyte/scout/scout_sweep.py — the host sweep's scout step: ends scouts whose heartbeat is stale and starts queued ones, waiting asks and waiting steers under [scout] max_parallel and a host-wide lock, putting a scout whose spawn failed back in queued for the next pass.
  • holophyte/scout/stop.py — POST /actions/scout-stop, holo scout stop and the holo mcp scout_stop tool: a stop recorded as the scout's scout_stop_requested event, then a queued scout ended at once or a running scout's process signalled to end itself.
  • holophyte/scout/swarm.py — one wave of a scout's helper turns: the helper route probe, a detached worktree per helper, the swarm and helper-total caps, and each helper's task record.
  • holophyte/scout/telemetry.py — turn_cost: a scout turn's cost, the CLI's reported one or its tokens at the route's usd_per_mtok; usage_tokens, a turn's tokens; scout_report_lines, the ended scouts' time, tokens, cost and cited helper tasks per route for --report.
  • holophyte/scout/thread_action.py — POST /actions/scout-message, holo scout ask and steer and the holo mcp scout_ask and scout_steer tools: an ask or a steer, optionally answering a card, recorded on a done scout's thread, then the host sweep's scout step started once to take it while a host scout slot is free.
  • holophyte/scout/views.py — the daemon's scout reads: GET /scouts, GET /scouts/ID with its tasks, served report, action items, events and thread, and GET /scouts/routes, the launch pickers less any route whose last probe failed; holo scouts, holo scout show and holo scout thread read them too.
  • holophyte/agents/fallback.py — outage detection and the switch to a probed fallback route.
  • holophyte/agents/review_workspace.py — review refs and the scratch checkouts a configured reviewer or the critic runs in.
  • holophyte/agents/agent_output.py — a turn's text with its route, exit status and transport failure.
  • holophyte/agents/claude_sdk.py — one critic turn over a Claude Agent SDK session: its options, credential environment, usage and stop at the timeout.
  • holophyte/agents/turn_tools.py — the holo_run tools an SDK critic turn hosts, read_ticket and report_freshness, scoped to the judged ticket.
  • holophyte/agents/claude_stream.py — a streaming Claude turn read line by line, its result lines kept, and a turn quiet for turn_quiet_sec ended as stalled.
  • holophyte/agents/turn_progress.py — a streaming Claude turn's tool calls counted and labelled, recorded as a turn_progress event at most once a minute.
  • holophyte/review/criterion_ids.py — stable criterion numbers for run prompts and replies, with positional fallback for tickets without stored criteria.
  • holophyte/review/briefs.py — the scope, criteria, tests, trim and evidence briefs a reviewer is sent, with the git scope they read.
  • holophyte/review/reply_parsing.py — reviewer output as structured findings, witness checks and a verdict.
  • holophyte/review/citations.py — near-miss witness citations: the classes that define a cited test, the dotted-module file, and whether an approved test is unchanged at the candidate.
  • holophyte/review/blast_radius.py — a review round's blast-radius tier: the built-in high path list, the diff signals, the raising sources and the implementer's BLAST RADIUS: line.
  • holophyte/review/adversary.py — the adversarial pass beside the primary review: when a round runs it, its brief by depth and scope, the reply's evidence levels and the findings that block.
  • holophyte/review/consolidate.py — one findings list for a fix turn after an adversarial pass: the exact merge, the consolidator's merges and order, the three-concern cap, the held concerns and the high-path raise.
  • holophyte/review/stale_approval.py — a review left only stale cited approvals: the stale_approval_rereview event, and the failure when the review again at that head still cites one.
  • holophyte/review/findings.py — FINDINGS.md as a bounded window over the store's rows.
  • holophyte/review/review_runner.py — exact-SHA staging and the model-neutral local reviewer boundary (see Reviewing).
  • holophyte/cli/report.py — --report: estimate vs actual per finished run (actual, estimate, ratio, rounds, outcome) with mean and median ratio, a read-only query over the store that claims no ticket, cuts no worktree and calls no one.
  • holophyte/cli/shadow_report.py — --report's shadow implementer section: per run with a shadow_result, the primary's and the shadow's first implementation (route, minutes, tokens, cost, verify, round-1 verdict and findings) side by side, with a summary line totalling both.
  • holophyte/cli/complexity_report.py — --report's complexity classifier section: per ended run with a complexity verdict, the profile it used, its first-attempt result, outcome, rounds, minutes and shadow result, with one summary line per verdict.
  • holophyte/loop/run.py — the frozen claimed run and the shared landing entry point.
  • holophyte/loop/stop.py — cooperative pause requests, operator aborts and durable continuations.
  • holophyte/loop/runs.py — the store seam: a run's progress as store rows.
  • holophyte/board/projection.py — Linear as the notice board: the ticket mirror, its pushes, --file-ticket and the escalation. Ticket status lives in the store and is projected onto a Linear workflow state by mirror_push() — one way, last write wins, never read back — so the provider's set_state is the only writer of that state, and the mapping table beside mirror_push says which state each status shows as.
  • holophyte/host/supervisor.py — the stale-run sweep and the PROJECT --supervise loop.
  • holophyte/host/sweep_host.py — --supervise [--once] with no project: the host sweep over every store in host.toml, its per-run home lock, sweep.json, the round-robin reconcile under a deadline, and one project's failure its own error.
  • holophyte/board/board_sync.py — observe_board() (KO-739): in store mode the host sweep's ask of the board's states() for every open ticket, once per board_ask_sec — the state name and column recorded, an issue seen gone twice walked abandoned or its live run paused. On every pass between those asks it delivers what is queued: the pending notes, and the queued pushes after one states() ask naming only their tickets.
  • holophyte/deadline.py — the host sweep's bound on its network calls: check() before each unit of Linear or GitHub work, and admit() before each Linear or GitHub request, which past the bound fails as an outage would; both no-ops outside the sweep.
  • holophyte/host/sweep_report.py — the sweep's report lines (KO-396): the SWEEP_HEADERS table, the per-run and restart lines, sweep_report() as --sweep's whole body, and the review-container and merge-lock sections.
  • holophyte/host/worktree_cleanup.py — retire_finished(): each host sweep pass, and --sweep --act, retires the worktree and local branch of every merged or abandoned ticket whose runs have all ended, through the guarded retire_worktree(); a refusal is kept and recorded.
  • holophyte/board/board_diff.py — --board-diff (KO-738): the board's ready listing against the store's rows for it, read-only on both, one line per difference and a summary.
  • holophyte/board/board_import.py — --board-import [--dry-run] (KO-756): board_import() upserts the Linear board's open_issues() into the store by board id in one transaction, one line per issue and a summary counting the pushes and notes pending for Linear; a dry run rolls back.
  • holophyte/board/provider.py — the Provider protocol the loop talks to a board through.
  • holophyte/board/linear_provider.py — the Linear GraphQL client: claim/fetch_task/ set_state/comment, ready-ticket and blocker resolution, issue creation.
  • holophyte/cli/patrol.py — --patrol [--json]: per project, the attention items grouped by kind, the runs in flight, stranded tickets, quiet runs and repeat failures, less [patrol] ignore, then main's checks, the check-held pull requests and the host's health; read only.
  • holophyte/pr/check_holds.py — the patrol's GitHub reads: main_checks(), main's check-run conclusion with each red run and whether main requires it, and pull_checks(), the red checks holding an open pull request as the merge fold decides.
  • holophyte/host/host_health.py — the patrol's host part: tmp_usage(), leftovers() (this user's processes in a finished witness or worktree tree, read from /proc), units() (systemctl --user is-active) and kernel_errors() (the last hour's kernel I/O errors), each readable or saying why it was skipped.
  • holophyte/cli/status.py — --status [--json] (KO-596): what the project is doing now — projects and admission, live and parked runs, the ready count, the schema version and the lock holders — read only, no network.
  • holophyte/story/story_views.py — --status's story lines: story_facts() reads each planned, approved or parked story's state, generation, child counts, latest witness verdicts at its last ledgered commit, errors, open decisions, open proposals and age; story_lines() renders them as two lines each, then one line per open proposal.
  • holophyte/cli/store_import.py — --import-store PATH --dry-run (KO-595): plan() reads another store and the project's own, both read-only, and says per table what an import would move — rows, id range, the remap offset and a sha256 of the rows in id order; render() prints it.
  • holophyte/host/supervisor_lock.py — the one-supervisor-per-project lock (KO-396): the lockfile's path, read, acquire and release, the SupervisorHeld refusal and the supervisor_running() probe.
  • holophyte/serve/server.py — --serve PORT|HOST:PORT, the HTTP daemon: it reads by default and writes only through [serve] actions and config_edit.
  • holophyte/serve/views.py — the daemon's store read views: /status, /attention, /board and /tickets/ID, with the supervisor and parked-ticket items they share.
  • holophyte/serve/serve_config.py — the daemon's /config routes (KO-394): GET's redacted read and PUT's validated, recorded and backed-up write, text and {"patch": ...} bodies alike.
  • holophyte/serve/serve_actions.py — the daemon's POST /actions/... routes (KO-395): the body parser, the two unit actions, requeue, and the interventions row each records before it acts.
  • holophyte/serve/serve_levers.py — the daemon's hold, release-hold, pause and resume actions (KO-609): the CLI's store calls behind the action token, each requiring a note, the paused attention item, and GET /holds, the project admission state and its change history.
  • holophyte/serve/serve_merge.py — the daemon's GET /runs/N/merge and POST /actions/merge: a run parked for a human's merge read for readiness, and released through the store's approve only when ready.
  • holophyte/serve/serve_ask.py — the daemon's POST /actions/ask and GET /runs/N/asks: a question on a parked run's pull request, recorded with the park's release and answered in the console with nothing posted to GitHub, and the console asks on that pull request.
  • holophyte/agents/transcripts.py — opted-in transcript location, rendering and turn event joins.
  • holophyte/agents/transcript_config.py — the daemon transcript root allow-list.
  • holophyte/serve/serve_runs.py — the daemon's run and ledger read routes (KO-395): /runs, /shipped, /ledger, /runs/N, /runs/N/ledger and /runs/N/files, their query parsers and the origin-link pair.
  • holophyte/serve/day_log.py — the GET /ledger rows the ledger table does not hold: a park row for each park waiting on the maintainer and a failure row for each failed run with no failure ledger row.
  • holophyte/serve/serve_history.py — the daemon's GET /history: every open run and every run started since a time, one row per run.
  • holophyte/serve/serve_findings.py — the daemon's GET /findings: the window's runs with their rounds, round and adjudication ledger rows and advisories, and its follow-up rows with their settled state.
  • holophyte/serve/serve_stories.py — the daemon's GET /stories/KEY: one story's children with their in-story dependency edges, its witnesses' latest verdicts and its decisions, open or answered.
  • holophyte/serve/serve_watch.py — the daemon's code-moved check and its in-flight request count (KO-648): what re-executes it between requests once the factory checkout's HEAD moves, or, on a socket the service manager handed over (adopted_socket()), what makes it drain and exit.
  • holophyte/serve/console_build.py — the host daemon's startup console build: console/dist/source-tree against HEAD:console, a rebuild into a staging directory swapped in on success, the old build kept on failure.
  • holophyte/serve/serve_host.py — --serve with no project: one daemon for every project in host.toml, each under /projects/NAME, the root's /status and /attention for the host, the machine token, one project's failure its own 503 or error, and run-sweep's host ledger.
  • holophyte/serve/contract.py — the poll and run routes' response types: one TypedDict per body /status, /attention and /peers answer, a project's and a host root's, and /runs/N, /runs/N/merge and /runs/N/asks, ROUTES naming each, and main(), which writes their JSON Schema and zod or --checks them.
  • holophyte/serve/contract_prefs.py — the console preferences document a host daemon's root answers at GET /prefs, as the contract declares it.
  • holophyte/serve/contract_pr.py — the pull request facts and the unmerged dependency a parked pull request waits on, as the contract declares them on /attention and /status.
  • holophyte/serve/contract_routes.py — a seat's and an implementer profile's route, last probes and labels, as the contract declares them on /status.
  • holophyte/serve/contract_run.py — a run's review round as /runs/N and /findings serve it, its per-round criteria judgement, and the run's witness pre-check, as the contract declares them.
  • holophyte/serve/contract_emit.py — a TypedDict turned into JSON Schema, JSON Schema into zod source, and the drift check against the committed files; it knows no route.
  • holophyte/serve/serve_board.py — the host daemon's native board writes (KO-758): the gate every one passes (the machine token alone, host [serve] actions, a native board, If-Match) and PUT /projects/NAME/tickets/ID, an edit authored console.
  • holophyte/serve/serve_prefs.py — the host daemon's root GET /prefs and PUT /prefs: the console's preferences in console-prefs.json in HOLOPHYTE_HOME, the day_log shape check, the machine-token gate and the atomic 0600 write.
  • holophyte/redact.py — secret values in a config.toml text, found by walking its TOML syntax: hidden for GET /config, put back for PUT.
  • holophyte/files.py — the files a run touched, read from git in the project's checkout under a timeout: what /runs/N/files answers.
  • holophyte/loop/pipeline.py — run_task: a claimed run through its stages in order, from the worktree to the merge or the pull request.
  • holophyte/loop/implement.py — the implement stage and the timed agent turn under its scaled budget, the run cap and the transport retry.
  • holophyte/loop/implementer_profile.py — profiled_run: resolves, probes and records a run's implementer profile, and scopes the run's project to it.
  • holophyte/loop/complexity.py — the complexity question a claimed run asks once, its complexity event and the profile a confident verdict picks.
  • holophyte/loop/escalation.py — a run failed on its merits on an implementer profile requeued once on the default implementer, its brief and the ESCALATION: choice the escalated turn declares.
  • holophyte/loop/contradiction.py — an acceptance criterion the implement or fix turn declares unmeetable as written: the brief, the CONTRADICTION: criterion CN lines, the contradiction event and the park on a question to the operator; when every declared criterion carries a PROPOSED_AMENDMENT: REPLACE CN line inside the ticket's In scope, the factory amendment and requeue (a steer and a requeue with source factory), recorded in the amendment_proposed event.
  • holophyte/loop/trim.py — the trim step between the implement turn and the first review: one turn, its commits judged by subject, file scope and verify.
  • holophyte/loop/trim_brief.py — TRIM_BRIEF, the vendored trim procedure the trim turn is sent, and the trim: pass subjects it commits under.
  • holophyte/loop/shadow.py — run_shadow: one shadow implementation of a run's ticket by [agents.implementer_shadow] in its own worktree, recorded as a shadow_result event.
  • holophyte/loop/shadow_spawn.py — start_shadow: a fresh claim's detached factory.py --shadow beside the primary, recorded as shadow_started; and that mode, one shadow a project under shadow.lock, else shadow_skipped.
  • holophyte/loop/review_round.py — the review rounds: verify, review and a fix turn per round, up to the cap the candidate's size earns.
  • holophyte/loop/adjudicate.py — the terminal adjudication once the review rounds are spent.
  • holophyte/loop/steering.py — a live run's pending holo steer notes: the prompt text its next implementer turn carries, their consumption, and the amendments it carried joined to the reviewers' ticket text.
  • holophyte/loop/steer_cap.py — a run that failed on its time or review-round cap while its ticket holds an amendment: the ticket parked on the operator with a question suggesting the steered scope split into a follow-up ticket.
  • holophyte/loop/branch_sync.py — the branch fast-forwarded to origin's copy, and the worktree held to the candidate a park recorded.
  • holophyte/agents/harness.py — harness adapters: a table-form [agents] role's turn argv, session id and resume argv, and the [harnesses] binary paths.
  • holophyte/agents/fix_session.py — review fix-session arms, resume argv and fresh retry.
  • holophyte/agents/session_arms.py — shared run-number assignment for session experiments.
  • holophyte/review/review_session.py — reviewer session-file capture and resume requests.
  • holophyte/loop/dispatch.py — the dispatcher out of the loop module (KO-412): one claim run under its crash containment and close-out, the startup sweep and the queue mirror.
  • holophyte/loop/merge_gate.py — the merge gate out of the loop module (KO-424): main merged into the branch, the pre-merge verify and the drift check under the merge lock, the park for a human's approval, the approved candidate's resumed run, and the --no-ff merge onto main.
  • holophyte/story/story_drift.py — a story child at the local merge gate after siblings merged since its claim: the story_drift event naming the files both changed, and one covering review of the refreshed candidate when there are any, parking the run for a person when it asks for changes.
  • holophyte/review/reproduce.py — a reported defect the implementer could not reproduce (KO-657): the OUTCOME: NOT_REPRODUCED declaration, the evidence check in place of round 1, and the not_reproduced park.
  • holophyte/review/skipped_tests.py — the candidate's added test ids by module, class and name, the verify commands rerun verbosely with their own selection (unittest or pytest), which added tests were skipped or never collected and why, and the fix-round brief asking for a runnable reproduction.
  • holophyte/review/witness_precheck.py — before the round 1 review, each criterion's named test file checked from git and a verbose verify rerun: it exists at the candidate, the branch adds or changes it, and the verify ran it; recorded as the witness_precheck and witness_precheck_results events.
  • holophyte/loop/witness_turns.py — the witness pre-check's gaps sent back to the implementer before the round 1 review, at most two witness_turn turns a run, and the brief of gaps still open for the round 1 reviewer.
  • holophyte/review/main_baseline.py — before each review round, each criterion with an on-main output clause and a named test file has its verify lines run in a detached tree at the review base with the candidate's copy of that file; recorded as the main_baseline event, reused while the base, the command and the file's blob match, and shown to the reviewer as the "Baseline at the merge base" block. A check that passes at the base is a round 1 witness gap.
  • holophyte/loop/merge_lock.py — the bounded wait for a live merge-lock holder, with waiter heartbeats and paired wait events (KO-496).
  • holophyte/config/locks.py — the Locks protocol a Project carries as its locks attribute, and FileLocks, whose merge() is live_merge_lock() unchanged (KO-594).
  • holophyte/cli/operator.py — the operator commands and the entry point: main (the serial pass or the pool's scheduler) and the self-merge re-exec, report, and the --requeue/--approve/--babysit/ --repoint store verbs.
  • holophyte/admission.py — project admission reads, hold/release CLI commands, and the scheduler's held-project exit after its workers finish.
  • holophyte/loop/claim.py — claiming a ticket and cutting its worktree: _claim_next over _admit_ticket's questions and _claim_run's lease and board label, then _cut_worktree/reuse_leftover, run_worktree_setup under _setup_worktree, and the mid-merge hand-off (merge_conflicts, conflict_brief, _resolve_merge_conflict).
  • holophyte/loop/task_worktree.py — a task worktree's own files and lifecycle: the .env (write_worktree_environment), the capture .gitignore, the [worktree] setup runner (run_worktree_setup) and retire_worktree for a finished checkout.
  • holophyte/loop/claim_store.py — the store-mode claim (Phase 3 stage 3): store_mode(), the one predicate; claim_from_store() takes the first store.read.claimable() row, admits it at its revision, reads that one issue back from the board and claims only at the admitted revision, admitting a moved ticket again at most READMIT_LIMIT times an ask.
  • holophyte/story/story_claim.py — a story's guards: refusal() keeps the store-mode claim off a child whose story is not approved, held_open() notes a board Done on a story parent instead of walking it merged, and walk_closed() abandons the story of a parent the board canceled.
  • holophyte/board/native_board.py — NativeBoard (KO-754), the board board_for() builds for [board] kind = "native": its reads answer from the project's store, filing, editing and the body read-back go through store.board, the lease and state writes do nothing, and comment() records a note. It never asks Linear.
  • holophyte/review/freshness.py — the claim's freshness check (KO-709): stale_reasons() asks the main ref for each file a ticket names, and park_stale() mirrors a stale ticket needs_spec, comments once and moves it to Backlog. The claim reads a main just fetched from origin. On a native board recheck_stale(), called from sync_board() on every pass, returns a ticket parked for stale landmarks (not one the critic parked) to ready (or blocked_on_deps) once none of its reasons holds, and --move KEY-n ready re-checks it the same way. The move also re-readies a critic park, running only the deterministic checks, and records a requeue intervention for either kind. A move to Ready of a ticket already in Ready is refused naming its status (KEY-n is already in ready at status ready; nothing changed). A native park keeps its column, and its note names --file-ticket --update and --move KEY-n ready. When reasons remain, recheck_stale() asks holophyte/review/moved_landmarks.py for a body naming where main renamed the missing paths, writes it through _rederive() and records a relandmark note ([loop] refresh_moved_landmarks).
  • holophyte/review/moved_landmarks.py — a stale park's renamed paths: renames_since() reads git log --first-parent -M -C --name-status from the newest first-parent commit dated at or before the current body was first recorded; map_landmarks() follows each missing path, and each missing -m unittest module's file, through those rows, refusing a deletion, a path with two destinations in one commit, or a final path not on main. A directory whose files one commit renamed under one new directory, keeping their relative paths, is mapped with every span and verify token under it (a commit deleting its last file, read with git ls-tree, or two destination directories keeps the park); rewrite() replaces exact backticked spans (or path::) and whole verify tokens, and any span or whole verify token naming a mapped directory or a path under it, leaving the TOML header alone. renamed_brief() gives a refreshed ticket's implementer brief the renamed paths (old path, new path and renaming commit) unless a revision authored other than factory followed the refresh.
  • holophyte/review/landmarks.py — what counts as a lost landmark: MainTree caches main's file list, blobs and git grep holders for one check; path_findings() and symbol_findings() split a ticket's misses into stale reasons (gone from main) and advisories (a bare basename that matches no tracked file, or a symbol its sentence pairs with one file but main holds in another). A symbol pairs with the path after in, from, of or (, or with a path leading its item before :. Advisories print at filing and become a claimed run's warning events; they never park.
  • holophyte/environment_git.py — excludes the filtered .env from Git staging and refuses candidate pushes containing it.
  • holophyte/commit_hygiene.py — removes configured attribution lines from unpublished commits before task pushes and local merges.
  • holophyte/note_privacy.py — keeps console notes (send-backs, steers, requeue, abort and human babysit notes, console asks) off GitHub: before a push it rewords unpublished commits to replace a note's text, or any eight consecutive words of it, with [maintainer note withheld], and it does the same to written pull request text and thread replies.
  • holophyte/leak_guard.py — scans unpublished commits, pull request text and ticket bodies for [merge] private_patterns and refuses a match by location and pattern index.
  • holophyte/loop/pause_notice.py — a paused run's pull request gets the holophyte:paused label and no comment; --resume removes the label and any notice comment an earlier pause recorded (KO-608).
  • holophyte/pr/github.py — [merge] mode = "pr"'s GitHub writes: the startup route check, the push, the pull request and its body, the babysitter's replies and resolves, the merge through the PR API, and the gh/API transport they ride on.
  • holophyte/pr/pr_contexts.py — page the head rollup's commit statuses and normalise them beside check runs for required-context folding (KO-485).
  • holophyte/pr/pr_head.py — bounded settling after a push and the foreign-head guard before the babysitter judges or merges a candidate.
  • holophyte/pr/merge_ready.py — whether a parked run may merge now: its park, [merge] approve, and GitHub's review decision, checks, mergeable, threads and head against origin, as ordered facts and the first failing fact's reason.
  • holophyte/pr/pr_activity.py — authored PR activity and event-backed wake guards.
  • holophyte/pr/pr_ready.py — under [merge] pr_draft, marking the factory's draft pull request ready for review once at the babysitter's ready point, or parking a draft it may not mark; under pr_draft = "ui", holding the draft for the maintainer to mark ready.
  • holophyte/host/ci_wake.py — when the reconcile sends a run parked ci back to the babysitter (its checks finished, or it is green and quiet for [merge] pr_quiet_sec), or re-parks it for a human once its checks stay pending past [merge] check_wait_sec.
  • holophyte/pr/pr_status.py — reading a pull request's state (KO-426), out of holophyte/pr/github.py: pull_status() for the parked-run reconcile, pr_state() and fold_checks() for the babysitter, parse_pr_url(). When main's rules name any required check the fold blocks only on the required checks; a red optional check lands in optional_failures.
  • holophyte/media_store.py — standard-library SigV4 PUTs for evidence in an S3-compatible bucket. Operator setup lives in the bucket configuration reference.
  • holophyte/pr/pr_media.py — user-facing diff matching, timed capture, bounded media publishing and evidence shared by PR descriptions and review prompts. See merge configuration for capture commands, destinations and size limits.
  • holophyte/pr/capture_files.py — under [merge] ui_capture_local = true, the capture specs and capture output a candidate adds: the factory commit that untracks them before each push, keeping the working copies and excluding them, and the list the merge refuses a head by.
  • holophyte/pr/evidence_refresh.py — before the covering review of a babysit fix, writing the capture at the fix head into the pull request's Evidence section, or marking its earlier images stale when that capture failed, and the ledger note recording which.
  • holophyte/pr/evidence_fetch.py — before a review, fetching each Evidence section image under [merge.media_bucket] public_base with the mock-up download and naming each the factory could not fetch, with why, for the reviewer's brief.
  • holophyte/pr/image_fetch.py — after a pull request body write that embeds an image, comparing each image GitHub's image proxy serves with the image at its own URL, and linking a short one again with a ?v=N query, at most twice.
  • holophyte/pr/mockup.py — a ticket's ## Mock-up media links: the filing checks against [merge.media_bucket] public_base, the download of each .png snapshot and .zip bundle into the task worktree's ignored .holophyte/mockup/ with its manifest.json, and the implementer's brief that lists them. review_carry names that directory and .holophyte/captured/ (the PNGs of the last UI capture) for every review workspace to copy read-only, and reviewer_brief lists both with the reviewer's design-match check.
  • holophyte/capture_playwright.py — a standalone Playwright capture runner a project can name as [merge] ui_capture: it runs the ticket's spec from a capture directory through a generated config that imports the project's own. Standard library only, so it runs by path from a project's worktree.
  • holophyte/pr/pullrequest.py — the pull-request stage of the loop: the PR open or adopt, every park on the PR, the merge through the PR API and its ledger line, and the resume of a run parked on its PR.
  • holophyte/pr/merge_queue.py — landing a pull request through main's merge queue (KO-712): the rules read for a merge_queue rule, the enqueuePullRequest mutation, and the bounded wait for the queue's merge commit, a removal, or [merge] check_wait_sec.
  • holophyte/babysit/maintainer_notes.py — private maintainer instructions as pre-approved babysitter threads, contract amendments, and commit event references.
  • store/steer_notes.py — holo steer's notes: a ticket's amendment or hint recorded with its interventions row, or a parked run's send-back linked to its event; a ticket's amendments and pending hints, their consumption, and --withdraw's marking of every standing amendment withdrawn.
  • store/operator_notes.py — atomic send-back and append-only note consumption evidence across attempts on the same pull request.
  • store/console_asks.py — a console ask recorded and its park released in one transaction, the unanswered asks on a ticket's pull request, and each answer, recorded in the console with nothing posted to GitHub.
  • holophyte/babysit/bot_threads.py — advisory bot findings and human-reply escalation.
  • holophyte/babysit/conversation_comments.py — paged human conversation instructions and recognition of quoted factory replies.
  • holophyte/babysit/devin_notes.py — a Devin review comment marked analysis, told apart by the factory's triage reply: a note stays fix-turn context, a finding is an open thread, and one with no reply is untriaged.
  • holophyte/babysit/note_triage.py — one read-only adjudicate triage of the untriaged Devin analysis comments as FINDING or NOTE, each answered on its thread; a FINDING joins the open threads.
  • holophyte/babysit/review_body.py — bot review-body outside-diff findings, their fingerprints and answer markers.
  • holophyte/babysit/thread_answers.py — read-only mention answers and thread replies.
  • holophyte/babysit/thread_mentions.py — mention markers and typed intent triage.
  • holophyte/questions.py — typed choice requests, the seats' settings for choices and for text answers, and safe service failures.
  • holophyte/question_cli.py — typed questions through the claude or codex CLI: prompt, schema, parsing, usage, and the probe with its fallback.
  • holophyte/loop/follow_ups.py — a fix turn's FOLLOW_UP lines parsed from its new commits and stored pending, and the merged run's settle step, one project's at a time under a file lock: a feature filed as a Backlog draft or matched to an open one by its fingerprint, a guardrail kept as a findings-ledger row. A feature raised by a child of an open story is first matched against the story's children and proposals, then put to the adjudicator: in_story writes a story proposal, standalone files the draft with a related-story line.
  • holophyte/story/story_scope.py — a story child's feature follow-up put to the adjudicator seat: scope_goal() builds the goal from the story's title, Summary, Goal and children, read_scope() reads the last SCOPE: in_story|standalone: REASON line, and judge() runs the turn on the adjudicator seat as terminal adjudication does, defaults to standalone on a failed, timed-out, outage or unreadable reply, and records its follow_up_scope event.
  • holophyte/loop/failure_triage.py — a failed run's cause question (infra, code or spec) after close-out, its failure_triage event, and the one factory requeue a confident infra answer earns.
  • holophyte/babysit/thread_findings.py — structured PR findings, bounded originals, and read-only normalization of legacy thread rows.
  • holophyte/babysit/babysit_steps.py — records PR babysitting step changes without repeating unchanged steps during polling.
  • holophyte/babysit/babysitter.py — the babysit pass over a pull request: thread answering and fixing, the merge of main, and the waits that drive them.
  • holophyte/babysit/adjudicator_outage.py — a failed thread-pass adjudicator turn, or one with no verdict for any thread, parked as a route outage that the host retries after [merge] route_retry_sec.
  • holophyte/babysit/thread_text.py — the babysitter's thread text: the conventions it quotes, the adjudicator's brief over its threads, the ADDRESS/DECLINE/HUMAN verdict parser, the fix brief and summary parser, the ---- Comment by MODEL ---- replies, the round text, and the parked question.
  • holophyte/babysit/plain_text.py — a review comment as plain text for a parked question (KO-717): HTML reduced to its text, an img to its alt, details blocks and quote markers dropped, and a 600-character cap.
  • holophyte/pr/missing_checks.py — required checks that never reported on a pull request's head: the one empty-commit retrigger per candidate under [merge] retrigger_missing_checks, and the park naming them (KO-652).
  • holophyte/babysit/check_fix.py — a red check's rerun and its check fix rounds per babysit, up to [merge] check_fix_rounds: the brief with each failed Actions job's failure lines and log tail, the round's event and ledger line, and the park on a repeated failure or spent rounds.
  • holophyte/babysit/capture_fix.py — the capture fix round before the covering review of a babysit fix whose capture from a local spec failed, up to [merge] capture_fix_rounds: the brief with the missing views, failing lines and capture log tail, the round's event and ledger line, the capture again at the round's head, and the park when it still fails.
  • holophyte/babysit/check_failures.py — a job log's failure lines (test failure headers and error lines), their normalized signature, and the test whether a red read repeats a failure an earlier round was handed.
  • holophyte/babysit/covering_fix.py — what follows a covering review of a babysit fix that asks for changes: the operator note's fix, an automatic fix round with the review's findings as the note, or the park when the findings repeat or [merge] covering_fix_rounds is spent.
  • holophyte/babysit/description_review.py — a covering review whose every blocker is tagged path: "PR description": the description rewritten with those findings as input before the same commit is reviewed once more.
  • holophyte/babysit/main_checkout.py — the detached main checkout the babysit pass verifies main in, given the task worktree's [worktree] carry directories, or its [worktree] setup, first.
  • holophyte/babysit/main_side.py — the babysit pass's main-side verify after a merge of main turns the branch red: main runs without what only the branch adds, and is not compared when the failing command was left out.
  • holophyte/babysit/pass_ends.py — where a babysit pass ends outside its loop: the verified merge or the merge queue's check fix, a pull request a person merged while a covering review ran, and the park at [merge] pr_rounds or pr_main_refreshes.
  • holophyte/story/witness.py — run_witnesses() runs a story's witness commands at one commit of main, in the warm tree for a loop pass and otherwise in a scratch worktree, within [story] witness_sec, and appends each verdict to the ledger; main_tip() reads the commit a pass runs at and red_kind() reads a red's kind from its output; witness_pass() runs an open story's witnesses at main's tip unless the ledger already holds it or the project is held, reruns once a red that was green at an earlier commit, and notes each changed verdict on the parent. A loop pass runs the whole witness set only when it is due, and otherwise carries the last real verdicts to the new commit.
  • holophyte/story/witness_running.py — witness-running.json beside the store names the witness set the loop's witness step is running: mark_running() writes it, clear_running() removes it and running_set() reads it for /status's witnesses, ignoring a record whose process has exited. witness-restarts.json beside it counts each story's cancelled witness runs since its last completed pass.
  • holophyte/story/witness_trigger.py — set_due() answers whether a loop pass runs a story's witness set at a commit: when the story has no real run of every witness since the story's latest approval, when a child's merged run's mergeSha is an ancestor of the commit and not of the set's last real run (the oldest of its witnesses' latest real verdicts, so a pass cut short still owes the rest), when the story's ## Witness paths changed since that run, when its newest real verdict is older than the most recent [story] witness_nightly_hour, or when a git command fails; carry() records each witness's latest real verdict again at the commit, carriedFrom naming the commit it came from.
  • holophyte/story/witness_tree.py — the warm tree a project's loop witness passes reuse: witness-tree/ under the project's state directory, taken under the non-blocking witness-tree.lock, checked out detached at the commit and cleaned keeping each [worktree] setup table step's keep paths; witness-tree.json stamps each table step's key over its run and its inputs at the commit, so a step whose key matches and whose keep paths exist is reused, until an earlier table step runs or a setup fails.
  • holophyte/story/witness_reap.py — stop() ends a witness command's process group and every process of this user whose working directory or an argument is inside the command's tree, SIGTERM first and SIGKILL after STOP_GRACE, once the command ends, times out or the runner is signalled; reap_stale() reaps each witness-* scratch directory an earlier pass left behind, its owner pid gone or, with no owner, older than [story] witness_sec, and a loop pass stops whatever still runs in the warm tree before it prepares it.
  • holophyte/story/story_close.py — settle_story() ends each witness pass under the merge lock: at main's tip it closes the story when every witness is green with its approved file and no child is in Ready or has an active run, queueing the parent's Done; a story with a child in Ready or with an active run does not close, and closes on the first pass after that child merges, is canceled or moves to Backlog. It otherwise parks it on one unmet or regressed decision per witness; settle_owed() keeps a story the lock kept from settling pending for the next pass at the same tip, and rerun_owed() keeps a red whose rerun did not run from parking regressed until the next pass runs it again. Its close note names each open proposal.
  • holophyte/story/proposal_decisions.py — --decide KEY-n pN answers a story's proposed child: accepting files it in Backlog through the board, depending on the child that raised it with a Role: scaffolding line (on Linear a sub-issue of the parent carrying the board's label, mirrored into the store), then accept_proposal() adds it to the story and its approved plan; rejecting records the answer and files nothing.
  • holophyte/host/reconcile.py — the startup reconciles and the GitHub read budget: parked pull requests asked about on GitHub, mirrored tickets Linear closed walked to their terminal status, and the GraphQL budget the reads spend from.
  • holophyte/host/startup.py — eager package imports, build identity and live-worker checkout guard.
  • holophyte/loop/pool_handoff.py — schema-aware restart and child ownership across exec.
  • holophyte/loop/gate_build.py — before the merge gate, a pool worker whose factory checkout moved since it loaded re-executes into the merge gate when the schema at the checkout's HEAD is unchanged: it records a gate_reexec event and execvs itself in the same pid with HOLOPHYTE_GATE_RUN naming the run, which the new image resumes straight at the merge stage. Otherwise it hands the approved run off to the next build: the run pauses at merge_gate with its candidate and the worker exits WORKER_HANDOFF.
  • holophyte/loop/pool.py — the worker pool and its scheduler (KO-343): scheduler() over factory.py --worker children under [loop] workers > 1, worker() as one child, the spawn and wait seams, the claimable count and the [holo2 wN] line prefixing.
  • holophyte/loop/reexec.py — reexec_self, the shared self re-exec the loop and the supervisor both restart themselves through, and start_transient_unit, the systemd-run --user --collect start a scout and a shadow each run as their own transient user unit through.

The store is its own package:

  • store/__init__.py — the v2 durable state store, one WAL-mode SQLite file: claims and leases, run-phase transitions, review rounds and the ledger.
  • store/operate.py — the operator API: release()/resume()/ requeue()/repoint()/approve()/babysit(), record_intervention(), record_project_intervention() and the runEvents writers, re-exported from the package.
  • store/repair.py — repair_references(): a dry run, then a recorded rewrite of foreign keys that name a dropped table.
  • store/criteria.py — append-only criterion versions, stable ticket-local numbers and open-ticket migration backfill.
  • store/failure_kinds.py — prefix-only backfill for historical run failures.
  • store/enums.py — canonical store vocabularies and generated SQL CHECK clauses.
  • store/schema.py — the schema, its migration ladder and the connection (open/init/transaction), re-exported from the package.
  • store/ddl.py — the table and index DDL the schema module creates.
  • store/project_paths.py — canonical project identity reads that require operator repair for ambiguous legacy relative paths.
  • store/tickets.py — the ticket state machine: ensure_project, the §3 TICKET_TRANSITIONS table and transition()/walk_ticket(), mirror_ticket, §2's pickable()/pickable_tickets() and the Mermaid state-graph renderer, re-exported from the package.
  • store/board.py — file_ticket() and edit_ticket(): a native board's ticket numbered from projects.ticketSeq, validated by ticket_problems() as --file-ticket validates, and an edit refused at a stale revision (KO-750).
  • store/revisions.py — record_board_fields(): one ticketRevisions row per change of a ticket's board-owned fields, healing a row an older build changed first (KO-736).
  • store/notes.py — record_note(): one ticketNotes row per key on a ticket, joining the caller's transaction so a ledger entry and its board note land together (KO-742); mark_note_posted() and mark_note_failed() record the host sweep's post of it (KO-747).
  • store/gap_layers.py — record_gap_layer() appends the correction layer a gap's lesson landed in, and gap_layer_counts() counts each gap's latest layer in ladder order.
  • store/follow_ups.py — record_follow_up() stores a pending followUps row once per run and fingerprint, pending_follow_ups() reads a run's pending rows with those of the parked runs whose candidate it carried, filed_drafts() the drafts filed for a fingerprint, and the four settle_*() writers settle a row as filed, duplicate, ledger or unfiled, each with its run event.
  • store/story_proposals.py — record_proposal() writes a story child's follow-up as a proposed storyProposals row and settles the follow-up, open_proposals() reads a story's open proposals, story_duplicate() finds the child or proposal a follow-up repeats, and accept_proposal() and reject_proposal() answer one with its decide intervention.
  • store/scouts.py — a scout run's record: create_scout(), move_scout(), beat_scout() and end_scout() along its phase edges, ScoutRefused for a move they do not allow, a pass's report, its helper tasks and action items, its runEvents rows, and scout()/scouts().
  • store/scout_thread.py — a scout's thread in threadMessages: post_message() records an ask, steer or answer, take_message() takes a waiting ask or steer and moves its done scout to answering or planning with its event, messages() and waiting_messages() read them, and ThreadRefused names a write they refuse.
  • store/stories.py — file_story() writes a story's rows for a parent and its children, story() reads one back from either, approve_story() freezes its plan with one open story per project, and abandon_story() gives it up and moves its unclaimed children to Backlog.
  • store/instructions.py — persist replies on recorded thread instructions.
  • store/agent_routes.py — atomic fallback intervention and event records.
  • store/launch_backoff.py — persistent route outages, retry deadlines and project-owned startup evidence before a first claim (KO-466).
  • store/working.py — persisted work intervals, finally-safe settlement and non-mutating effective-work reads; excludes orchestration waits.
  • store/writes.py — named transactional writes for board state, questions, pull request metadata, outcome reasons and board ask timestamps.
  • store/read.py — typed read views over the store: one query, one row type, no SQL elsewhere.
  • store/run_reads.py — the typed reads of runs, re-exported by store/read.py.

The ticket checker, in holophyte/tickets/:

  • holophyte/tickets/ticket_criteria.py — explicit criterion ids, test and verify witnesses, and blocking post-merge or operator witness checks for native filings.
  • holophyte/tickets/ticket_template.py — parser/validator for the ticket shape; python3 ticket_template.py TICKET.md [...] (or holo ticket check TICKET.md [...]) exits 0 iff the ticket is pickable-ready.
  • holophyte/tickets/ticket_advisories.py — the checker's verify-block and criterion advisories: a whole-suite discover, a hard-coded test count, a verify wider than In scope, a repository-wide ruff check that fails on main, and a criterion asking for a run's output the factory does not make.
  • holophyte/tickets/ticket_header.py — finds, parses and checks the +++ TOML header a ticket can open with, setting the same Ticket fields as the magic lines.
  • holophyte/tickets/ticket_depends.py — finds a ticket body's estimate line and rewrites its Depends on: list, for story filing and a cancel's rewire.
  • holophyte/tickets/story_template.py — validator for a story directory's body, witness files and children; python3 ticket_template.py --repo PATH --story DIR exits 0 iff the story is valid.
  • holophyte/tickets/templates/ticketTemplate.md — the ticket shape. Its typed fields (ui_change, estimate, depends_on, [mockup], [[evidence]] and the rest) sit in the +++ TOML header above the H1; a body without one keeps the magic-line form. Verify commands go in the "Verify command(s)" section (exit 0 = pass, relative paths only); estimate is the budget in minutes. The optional "Contract checks" section declares relative/path: exact literal lines the gate asserts verbatim, so a required value (a port, a URL) cannot drift while the commands still pass.
  • holophyte/tickets/templates/storyTemplate.md — the story shape: the story body, its witness files and its children.

At the root:

  • factory.py — the entry point: imports cli from the package and calls it. Holds no def or class of its own.
  • ticket_template.py — runs holophyte/tickets/ticket_template.py's main(), so python3 ticket_template.py checks tickets as before.
  • story_template.py — runs holophyte/tickets/story_template.py's main().
  • docker/reviewer.Dockerfile — pinned minimal reviewer image.
  • landing/ — the static landing page at https://holophyte.ai, served by its own Worker from landing/public/ (design note 6); the root wrangler.jsonc serves this manual at https://docs.holophyte.ai. Its screenshots and video are not in the repository: they live in the R2 bucket holophyte-media under demo/, served at https://media.holophyte.ai/demo/. After a new capture, scripts/landing_media.sh CAPTURE_DIR uploads every file the page references from the capture output directory, with its content type.
  • FINDINGS.md (generated, not kept in this repository) — in a project with [report] findings = "repo", a rendered window over the store, not a log: the factory regenerates it at each close-out from runs/reviewRounds as the newest 25 entries below a <!-- store-rendered below --> marker, with everything older counted in one archive line and kept in the store. Text above the marker is frozen pre-store history and is never rewritten; Linear ticket comments stay the full per-ticket archive.
  • tests/ — the stdlib unittest suite, one test_*.py per surface, with tests/fake_agent.py, tests/procs.py and tests/waiting.py as shared helpers. Run it with HOLOPHYTE_HOME=$(mktemp -d) python3 -m unittest discover -s tests.

Linting

ruff check . from the repo root; it exits 0 when the tree is clean. Run it alongside the tests — the developer verify path is:

ruff check .
python3 -m unittest discover -s tests

The configuration lives in ruff.toml: line length 88, Python version py311, and rule sets E, F, W, I, C90 (pycodestyle errors and warnings, pyflakes, import ordering, McCabe complexity). Nothing is formatted, only checked. Every enabled rule is a promise the factory keeps forever, so the selection stays small, and a violation that has to stand is suppressed with a per-line # noqa: <CODE> rather than a file-level or blanket ignore.

Cyclomatic complexity above 12 is a lint failure (ruff C901); an exemption is a per-function noqa: C901 that names its reason in a few words, and its COMPLEXITY_EXEMPT entry in tests/test_comment_budget.py names the ticket that retires it, or None while none is filed.

Module size is a ratchet on the same terms: no source module over 1000 lines, no test module over 1500. tests/test_file_sizes.py holds a an OVER table of unpinned modules over their ceiling, each entry its count as wc -l measures it, and walks every tracked Python file against it — a module that grows past its entry, a module with no entry that passes its ceiling, and an entry whose module is back under the ceiling all fail the suite, and a second check holds every entry to wc -l's exact count. A slice that changes a listed unpinned module's size rewrites its entry in the same commit; a module back under its ceiling leaves the table. Pins are upper bounds; more than 150 lines of slack requires lowering the pin.

Comments are a ratchet too. tests/test_comment_budget.py pins, for each module under holophyte/ and store/ and each top-level module, its count of comment and docstring lines (PINNED) and of lines citing a ticket id (CITED). Neither count may grow, and a lowered count lowers its entry in the same commit; a new module holds at most one such line per 20 lines and cites no ticket. A comment states only a constraint the code cannot express; history belongs in the commit message and the store. The suite reports the in-scope ratio against its 5% goal as a skipped test until the trim reaches it, and code_changes() witnesses that a trim removed only comments and docstrings.

ruff is a developer tool, not a dependency: install it on the host with pip install --user ruff (or uv tool install ruff). It is never vendored.

Console

The console is the browser page the daemon serves at /. It lives in console/ as its own package: Bun is the runtime, test runner and bundler; the page is React 19 with TypeScript, styled with Tailwind v4. The three commands, run from the repo root:

bun --cwd=console install --frozen-lockfile
bun --cwd=console test
bun --cwd=console run build

--cwd=console is the equals form of Bun's global flag and the only form that works here: Bun 1.4 reads the space-separated bun --cwd console X as bun run X inside the directory, so install and test would resolve to package scripts and run build would print usage without building.

install reads the committed console/bun.lock and refuses to drift from it; it never builds — there is no install lifecycle script, and run build is the one path to console/dist/. test runs bun test with console/tests/setup.ts preloaded and discovery rooted at console/tests/ (see console/bunfig.toml, so the nested electron package's tests stay its own), which registers happy-dom so component tests render with @testing-library/react and no browser; console/src/lib/ tests stay free of the DOM. build runs console/build.ts, which hands the one page, console/index.html, to a Bun.build call with bun-plugin-tailwind and writes the static bundle to console/dist/ — index.html keeps its name there, next to its hashed script, and is the stable entry the daemon serves at /. console/dist/ and console/node_modules/ are git-ignored.

The daemon owns the types of the routes the console polls. python3 -m holophyte.serve.contract renders the TypedDicts in holophyte/serve/contract.py into one JSON Schema per route in console/contract/ and the zod schemas with their inferred types in console/src/lib/contract.gen.ts; both are committed and never edited by hand. --check writes nothing and exits 1 naming each file that differs from a fresh render, or that sits in console/contract/ unrendered; tests/test_serve_contract.py runs it in the unit check, and walks the pinned answers in tests/fixtures/serve/ against their types.

Captures are the screenshots a console pull request carries, taken by holophyte/capture_playwright.py with a real Chromium. The fixture python3 -m tests.console_fixture builds the console, seeds a throwaway host with the demo project, serves it from a real host daemon and runs one command with CONSOLE_URL naming it; that command is the console's capture script, playwright test from the pinned @playwright/test. seed() writes a ticket in backlog, one ready, a working run, a run in review after one changes-requested round, a question park, a pull request parked on CI, one parked ready for its merge, a stranded failure, a merge from today, an older merge and an abandoned failure, so a spec injects only the states beyond those. console/playwright.config.ts is Chromium only, one worker, no retries, a 1440×900 light viewport, baseURL from CONSOLE_URL, and no webServer, since the fixture boots the daemon. A ticket's spec is <KEY>.capture.ts in the capture directory under console/ (so it resolves the console's @playwright/test), is never committed (the push untracks it), and saves NN-slug.png files into CAPTURE_OUT; console/e2e/CAPTURE-0.capture.ts is the smoke spec, which writes the console's Tickets board at /#/tickets as 01-board.png. console/e2e/console-routes.spec.ts checks that each address opens the console, run as python3 -m tests.console_fixture --build 'bun --cwd=console run build' bun --cwd=console run capture console-routes. Neither bun test nor the typecheck reaches console/e2e/, and Playwright's console/test-results/ is git-ignored. The verify's capture command, from the repo root:

OUT=$(mktemp -d) && HOLOPHYTE_TICKET=CAPTURE-0 python3 holophyte/capture_playwright.py --dir console/e2e --config console/playwright.config.ts --boot "python3 -m tests.console_fixture --build 'bun --cwd=console run build' bun --cwd=console run capture" "$OUT"

Bun is a developer tool like ruff, never vendored. The operator step for the writer host: install it with the upstream installer (curl -fsSL https://bun.sh/install | bash) as the factory's user, so the loop's login shell sees bun on PATH. The factory's verify step inherits that PATH, so nothing in the loop changes. The writer host runs Bun 1.4.2.

The console

There is one console. It lives in console/src/app/, with its tests under console/tests/app/, and is served at /; the shared modules it reads stay in console/src/lib/ and console/src/hooks/. The daemon answers the console's old addresses, /next.html and /classic.html, with a 301 to /. Every run link opens the Run page at #/runs/PROJECT/N.

console/src/app/focus.ts derives blocked-card headlines and extracts asks. console/src/app/components/LimitedMarkdown.tsx renders asks and latest events with a restricted Markdown vocabulary and expandable line clamps.

Browser appearance choices live in console/src/app/appearance.ts; console/src/app/components/AppearanceTab.tsx renders theme and reading-font radio cards, sharing theme state with the sidebar. Its own console/src/app/theme.css supplies the Night and Dawn tokens and Archivo and IBM Plex Mono faces.

console/src/app/router.tsx routes it with TanStack Router over hash history, since the static route has no single-page fallback: #/ is Overview, #/runs Runs, #/tickets Tickets and #/findings Findings, each in the shell its root renders. Settings lives at #/settings (General) and #/settings/$tab: general, review, agents, capture, hold, toml, hosts, appearance and log; an unknown tab is a not-found page. console/src/app/settingsDraft.ts holds per-project field and raw drafts for the Settings page. console/src/app/components/TomlEditor.tsx edits and checks the whole TOML file with a line gutter. console/src/app/components/SettingRow.tsx binds the shared field rows, and console/src/app/components/UnsavedBar.tsx lists and discards pending changes. console/src/app/hosts.ts derives the unscoped daemon cards from the shared fleet poll; console/src/app/components/HostsTab.tsx renders their health, projects and last reports. console/src/app/components/RouteTable.tsx lists their agent routes, probes and recorded fallback use. console/src/app/dayLog.ts sorts each ledger entry into the Today's log groups, writes its short phrase and keeps the filter saved at the origin daemon's /prefs; console/src/app/components/DayLogTab.tsx renders that filter's Settings tab. console/src/app/checker.ts places each /tickets/check message on the New ticket field it names and words the Checker panel's summary.

console/src/app/settings.ts defines the grouped tabs and host attention count; console/src/app/pages/Settings.tsx frames the selected section's panel, and on a Settings page the sidebar draws console/src/app/components/SettingsNav.tsx: a Back row, the grouped sections and the project picker. The sidebar footer links to Settings. Any other path is a not-found page. The root route's search holds one optional project, the project's name as the sidebar shows it, so /#/runs?project=demo reproduces a scoped view; every route inherits it, the sidebar's links carry it, and scopedHosts() in console/src/app/scope.ts narrows the polled hosts to it. The root reads the fleet once with useFleet() and shares the result with the sidebar and the pages through a React context.

The console reads its data through one TanStack Query client, consoleQueryClient() in console/src/app/fleet.ts, which its entry mounts; a failed read is not retried, since the next poll is the retry. useFleet() is one query keyed by the origin: pollPeers() fans out to every daemon each POLL_INTERVAL_MS, mergeHosts() keeps each daemon's last good answer through its failures, and every view that reads it shares the one poll. Every body is parsed with the generated schemas in console/src/lib/contract.gen.ts, so a daemon whose answer changed shape fails at the fetch with a contract error naming the endpoint and field. A view that reads another route adds its own query keyed by the daemon base and the path, parsed with that route's generated schema. An action is a mutation, useAction() in console/src/app/actions.ts: when the daemon takes it, it invalidates the fleet query and every route query of that daemon base, so the view re-reads at once, not at the next poll.

The sidebar's project switcher (console/src/app/components/ProjectSwitcher.tsx) sets that scope. Its rows come from switcherRows() in console/src/app/projects.ts: "All projects", then one row per project name, pooling the hosts that serve it, with its held tag, need count and open runs, or "not answering" when every host of it failed its last poll. A find box filters them; arrow keys move, Enter picks, Esc closes. A pick rewrites project on the current route and is kept in session storage under holophyte.next.project, so opening / with an empty hash starts at Overview in that scope; a hash that names a route is shown as written.

console/src/app/pages/NewTicket.tsx presents the ticket template as text fields and files to an editable project’s Backlog from the Tickets page. console/src/app/pages/EditTicket.tsx loads a native ticket's current revision into the same fields (console/src/app/components/TicketFields.tsx), marks each changed one and saves it as the next revision with If-Match; console/src/app/ticketEdit.ts rewrites only the fields that changed. When a newer revision lands during an edit, console/src/app/sectionPicks.ts sorts each body section by which side changed it and builds the body from the section the maintainer picks for each one both sides changed.

The Runs page (console/src/app/pages/Runs.tsx) pages and sorts through GET /history: one daemon in scope serves each page itself, and with several useRunPage() in console/src/app/history.ts merges each daemon's first pages.

The Run page’s next/components/CriteriaList.tsx shows review marks and witness notes, with its verdict count supplied by criteriaClause() in next/run.ts.

The Run page’s console/src/app/components/AgentPanel.tsx renders the agent seat, state and chronological note/question thread from the page’s shared run detail and asks queries.

console/src/app/activity.ts groups a run's events into timed phases (implement, review rounds with their steps, final check, pull request and babysit passes) and the live run's Now line, for the Run page's Progress. console/src/app/components/RunActivity.tsx draws them as a folding timeline that opens only the current group, tinted: the running one, ending on the Now line's detail, or an ended run's last, ending on console/src/app/components/EndFacts.tsx. console/src/app/components/RawLog.tsx opens every Progress line, heartbeats included, in a modal side panel. Its criteriaGrid lays out the run's criteria by round for console/src/app/components/CriteriaGrid.tsx, in a sticky right rail when Progress is wide enough; an open group lists its findings as console/src/app/components/ActivityFinding.tsx rows.

The Run page’s console/src/app/components/RunDetails.tsx folds files, review rounds and timed agent turns below Progress. Its files and turns queries in next/queries.ts fetch only while the fold is open. Under each round, console/src/app/components/RoundInstructions.tsx draws the maintainer notes that drove it and the pull request comments it acted on.

The Turn page (console/src/app/pages/Turn.tsx) shows one recorded turn’s figures and links to its neighbours. console/src/app/turn.ts selects its host and run and shares turn titles with the Details fold. Run and Turn share page framing and the quiet loading placeholder, which the Scout page also shows, in console/src/app/pages/runShared.tsx. The Turn page’s console/src/app/components/TurnEntries.tsx lists transcript entries with folded prompts and tool calls, opening failed calls automatically. console/src/app/components/TurnToolbar.tsx searches and filters the Turn transcript, jumps to its first error and controls its folds. console/src/app/scouts.ts reads every scoped host's scouts and a project's scout routes; console/src/app/pages/Scouts.tsx lists the scouts newest first in one group per project, searchable by title or ask and filtered by state and kind; console/src/app/components/ListFilters.tsx is the Search field and select the Tickets and Scouts pages share, and the Search field, FilterPopover and RangeButtons of the Runs page, its third user, and console/src/app/components/ScoutLaunch.tsx is its New scout form, posting POST /actions/scout. At 1400px and wider, which useWide() in console/src/app/viewport.ts reads, the Scouts page puts the list beside console/src/app/components/ScoutPane.tsx, which draws the scout its scout search selects with the same header and body as its page, or the New scout form, each list row reading the scout's title. console/src/app/components/ScoutHead.tsx is the header the Scout page and the pane share: the state dot, the scout's title, the what and cost lines of console/src/app/components/ScoutFacts.tsx and the ask behind Ask. console/src/app/components/ScoutBody.tsx is the body they share: the Verdict, the newest report's headline over its bullets or else its summary, or a scout's state sentence while it has no report; then Action items, Findings, Progress and Thread, with Progress second while the scout is live. A failed run's page, open findings, follow-ups and ticket defect rows on Findings, and a ticket's actions each carry a Scout button (console/src/app/components/ScoutThis.tsx) that opens #/scouts with search new, about, kind and ask, which the Scouts page reads into a filled New scout form whose launch sends about as subject. The Scout page (console/src/app/pages/Scout.tsx) reads one scout's GET /scouts/N through console/src/app/scout.ts, which also groups it into Plan, waves, Synthesis and Report, then an Answer or Pass group for each answer turn and follow-up pass, whose spans and numbers scoutThread.ts gives; console/src/app/components/ScoutActivity.tsx draws them as Progress rows, each with its time and tone mark, a wave or pass folding on its task rows, each opening its question, and console/src/app/components/ScoutStop.tsx posts POST /actions/scout-stop after a confirm; a swarming scout's queued or running helper row posts it with its task, no confirm, from Stop this branch. console/src/app/components/ScoutReport.tsx draws a scout's Findings: every pass's findings with their status and citations folded, and the newest report's reproduction as one line opening its hypotheses. console/src/app/components/ScoutActions.tsx draws its Action items, each filed with POST /actions/scout-file; a checker refusal opens New ticket with the item's draft. A revise item's Revise opens its ticket's edit page with the item's draft handed in the navigation's history state, which console/src/app/pages/EditTicket.tsx loads over the current revision for the maintainer to save. console/src/app/scoutThread.ts folds a scout's thread, answer turns and passes into rows and says why its composer cannot send; console/src/app/components/ScoutThread.tsx draws them as the Thread at the end of the scout's body, with its Ask and Steer composer, posting POST /actions/scout-message, and console/src/app/components/ThreadMessage.tsx is the message item it shares with AgentPanel.tsx. console/src/app/components/ScoutCard.tsx draws a scout's proposal card in the Thread: a button per choice posts it as the card's answer, and Type an answer points the composer at the card.

Desktop wrapper

Some operators want the console in the dock with a menubar icon rather than in a browser tab. console/electron/ is a thin Electron shell around the URL the daemon serves: one window (1280×860, never narrower than the 1100px the design assumes), a tray icon built from assets/menubar-template@1x.png and its @2x sibling whose menu carries the SwiftBar drawer's summary, then "Show console", an "Open at login" checkbox that reads and sets the macOS login item, "Developer tools", which opens the window's DevTools (the platform shortcut works too), and "Quit", and nothing of its own — no renderer code, no preload, no IPC, so the app and the browser tab never drift. The window's console errors and failed loads are appended, one timestamped line each, to console.log in Electron's user-data directory (beside console.json; source paths only, never query strings), truncated once it passes 1 MB, so a blank view can be diagnosed afterwards. On macOS closing the window leaves the tray in place and the dock or tray reopens it; elsewhere closing the window quits. On macOS the window also hides its title bar and draws the traffic lights in the console's 52px title row, followed by a panel-left sidebar toggle and the lotus mark without the wordmark (options in console/electron/window.ts); ⌘\ (Ctrl+\ elsewhere) toggles the sidebar. The seed script writes holophyte.app-shell = macos to the page's storage, which is how either console view knows to inset its header and mark the drag regions.

The console URL comes from the first of three sources, and a bad value in that source is a dialog naming it, never a fall-through to the next:

  1. HOLOPHYTE_CONSOLE_URL in the environment.
  2. console.json in Electron's user-data directory, {"url": "…"}; the operator's own URL lives there, never in the repo.
  3. The default http://127.0.0.1:7710/, the first project's port on a host.

Only http and https are accepted.

The tray menu is rebuilt every ten seconds from /peers on that URL and then /status and /attention on each daemon it names (and /runs on an idle one, for its last merge), with the drawer's wording: what needs you first ("Nothing needs you" when nothing does), one line per project (holophyte · working KO-n · hb 12s, or idle · last merge KO-n · 3m), a hosts line (1 host · 3 daemons), then the three fixed entries. Clicking an attention or project line shows the console. The tray glyph is the template when every daemon is idle and the drawer's warn or bad variant for the worst state shown (something needs you; a daemon is unreachable); bun run icon renders those variants from assets/menubar-{warn,bad}.svg into console/electron/dist/, and start and package run it first.

A daemon beyond loopback wants its serve token (see docs/reference/http.md, Authentication); the tray never prompts for one. It reads them from console.json, per HOST:PORT address as /peers names it, either inline or as the path of the file the daemon's own [serve] token_file holds, the same file the drawer's [[daemon]] token_file points at:

{
  "url": "http://127.0.0.1:7710/",
  "tokens": { "writer-2:7710": "…" },
  "token_files": { "writer-3:7710": "~/.holophyte/writer-3.token" }
}

A token_files path may start with ~ or be relative to the user-data directory; tokens wins over token_files for the same address. A daemon whose token is missing or wrong shows needs token on its line until the file is fixed; one that does not answer within two seconds shows unreachable. The window seeds the page's tokens from the same console.json entries once it has loaded, under the keys the Hosts card reads, and reloads once when that changed anything, so a host the tray can see needs nothing pasted. The commands, run from the repo root:

ELECTRON_SKIP_BINARY_DOWNLOAD=1 bun --cwd=console/electron install --frozen-lockfile
bun --cwd=console/electron test
node console/electron/node_modules/electron/install.js
bun --cwd=console/electron run start

test exercises the pure URL resolver in console/electron/config.ts, the summary builder in console/electron/tray.ts, the window options in console/electron/window.ts and the poll in console/electron/poll.ts (with a fake fetch), and needs no Electron binary, which is why the verify install skips the download. start needs the binary, and a second install after the skipped one reports no changes and leaves it absent, so fetch it by running Electron's own postinstall script directly (the node …/install.js line above; it is a no-op once the binary is present). Then run start builds main.ts to console/electron/dist/ (main.cjs, CommonJS with electron left external, since the package is "type": "module") and launches it through the top-level main.cjs. console/electron/dist/ and console/electron/node_modules/ are git-ignored.

To put the console in the Dock like any app, package it:

bun --cwd=console/electron run icon
bun --cwd=console/electron run package

icon renders assets/logo.svg to console/electron/dist/icon.png at 1024×1024 through @resvg/resvg-js, so no image tool has to exist on the machine. package runs build and icon itself, then electron-builder (configured in console/electron/electron-builder.yml) writes console/electron/dist/mac-arm64/Holophyte.app and, one level up, console/electron/dist/Holophyte-<version>-arm64.dmg (the builder puts each build's artifact in the output directory and the unpacked app in its per-arch subdirectory). The app is ad-hoc signed: the config sets identity: "-" explicitly, since the builder's default is to skip signing when no certificate is in the keychain; notarisation, auto-update and other platforms are not covered. The packaged app finds the console URL exactly as the repository run does: from HOLOPHYTE_CONSOLE_URL or console.json, never from anything baked into the bundle.

console/src/app/components/HostToken.tsx manages each Hosts card’s browser token, including saving, forgetting and undo.

console/src/app/components/HoldTab.tsx shows project admission, reasoned hold and release actions, and the polled admission history.