V1 Decision Log
This log records design choices and tradeoffs during the V1 harness/process refactor.
2026-05-10 Branch And Plan Setup#
Decision: Use branch v1 for the rewrite.
Context: User requested a new branch named v1 if available, otherwise an
alternate such as v2. Local/remote v2 already existed; v1 was free.
Alternatives:
- Use existing
v2. - Use a prefixed branch such as
codex/v1.
Why: The user explicitly asked for the v1/v2 naming scheme. v1 was
available and clearer for this rewrite.
Consequences: Branch v1 now tracks origin/v1.
2026-05-10 Planning Scope#
Decision: Treat this as a breaking architecture rewrite, not an incremental compatibility refactor.
Context: User stated no one is using the codebase and asked not to preserve old architecture just to fit existing code.
Alternatives:
- Incrementally adapt current
bootstrapandcapture. - Keep hardcoded writer/reviewer and wrap it with new names.
Why: The new architecture requires clean boundaries: operations, process manager, harness SDK, provider adapters, and simple prompt assembly.
Consequences: Implementation should delete stale architecture where it conflicts with the new model.
2026-05-09 19:47 PDT#
Decision: Add harness provider registry with placeholder adapters before porting Claude and Codex.
Context: The new operation/process layers need a stable provider-neutral registry and capability model before real provider ports are implemented.
Alternatives:
- Port Claude immediately while defining the registry.
- Reuse
src/agent/providers/*directly.
Why: Placeholder adapters let the project compile and test the new boundary
without mixing old RunAgentOptions semantics into AgentRunSpec.
Consequences: Any command using the new harness before provider ports land will fail clearly with "not implemented yet." Real Claude/Codex behavior lands in the provider adapter phase.
2026-05-09 19:58 PDT#
Decision: Background launches write a queued run record before spawning the
detached child. The child then rehydrates the spec and owns the transition to
running, done, or failed.
Context: If the parent spawned the child first and then wrote a running
record, a fast child could complete and write done before the parent wrote its
late running record, regressing the final status.
Alternatives:
- Parent writes
runningafter spawn with the child PID. - Parent writes
runningbefore spawn with PID0. - Add a larger process supervisor before the CLI job path exists.
Why: queued keeps jobs visible immediately, avoids a parent/child record write
race, and lets the foreground process manager remain the single owner of actual
harness execution.
Consequences: A just-started background job may briefly show as queued until
the child begins. PID visibility comes from the child-owned running record
rather than the parent start response.
2026-05-09 20:03 PDT#
Decision: The Claude harness adapter maps CodeAlmanac tools to both Claude
SDK tools and allowedTools.
Context: Claude SDK docs distinguish availability (tools) from auto-approval
(allowedTools). The old adapter only set allowedTools, which could be
mistaken for a strict capability boundary.
Alternatives:
- Continue setting only
allowedTools. - Use the full Claude Code preset and rely on prompts for tool discipline.
- Build a separate CodeAlmanac permission hook before the provider port.
Why: Passing the same mapped tool list to tools and allowedTools gives the
main agent a concrete available tool surface while keeping background runs
non-interactive with permissionMode: "dontAsk".
Consequences: Tool registry entries remain provider-neutral, but the Claude
adapter is now responsible for name expansion such as search to Glob/Grep
and web to WebSearch/WebFetch. MCP server configs are passed through, but
specific MCP tool names are not invented by the registry.
2026-05-09 20:06 PDT#
Decision: The Codex V1 adapter uses codex exec --json and rejects per-run
programmatic agents.
Context: The Codex CLI has official subagents and custom-agent concepts, but
the simple non-interactive exec path does not expose a Claude-equivalent
in-memory agents map with enforced per-agent tool scopes.
Alternatives:
- Inline requested agents into the prompt as a fallback.
- Generate
.codex/agents/*.tomlfiles per run. - Wait for a fuller Codex app-server/thread lifecycle integration before supporting Codex agents.
Why: The V1 provider layer should report and enforce the actual primitive it can
control. codex exec --json is enough for Build/Absorb/Garden filesystem work,
but pretending it supports Claude-style per-run agents would make the abstraction
misleading.
Consequences: Operation builders should only include agents when the selected
provider capability supports programmaticPerRun. Codex still gets the same
assembled prompt/system text and can use its own configured harness features.
Update 2026-05-10 09:55 PDT: Codex lifecycle runs now use codex app-server --listen stdio:// instead of the simple codex exec --json path. This keeps
the same provider-neutral boundary, but maps app-server thread/turn/item
notifications into richer HarnessEvent records. The app-server path supports
model override, reasoning effort, structured output schema, and token usage, but
still rejects programmatic per-run agents, MCP, skills, and max-cost fields until
those can be mapped cleanly.
Security note: app-server turns run with workspace-write filesystem access and
networkAccess: false by default. Server-initiated approval and user-input
requests are answered noninteractively with denial or empty answers so lifecycle
commands remain pipeable and do not grant extra authority mid-run. The spawned
app-server also gets --config mcp_servers={} so user-level Codex MCP servers
do not leak into CodeAlmanac runs while the provider metadata says MCP is
unsupported. Handshake JSON-RPC calls have a timeout so a stalled or incompatible
app-server becomes a recorded provider failure instead of a permanently running
job. An accepted turn also has a terminal timeout, because a provider can accept
turn/start and then fail to emit turn/completed.
2026-05-09 20:09 PDT#
Decision: Keep Cursor as future work in V1 and leave the harness adapter as an explicit placeholder.
Context: The architecture doc says Cursor is future work and not to implement it for this rewrite. Claude and Codex are enough to validate the provider boundary and process manager.
Alternatives:
- Port the old
cursor-agent --print --output-format stream-jsonadapter now. - Remove Cursor from the provider registry entirely.
Why: Keeping the placeholder preserves the intended extension point without spending implementation time on a provider that the current V1 scope explicitly defers.
Consequences: Any Cursor run fails clearly until a later Cursor adapter lands.
2026-05-09 20:09 PDT#
Decision: New init/build scaffolding gitignores .almanac/runs/ instead of
.almanac/logs/ or old root-level capture/bootstrap globs.
Context: V1 stores process records and JSONL event logs together under
.almanac/runs/. Separate .almanac/logs/ state belongs to the old capture and
bootstrap architecture.
Alternatives:
- Keep ignoring both
.almanac/logs/and.almanac/runs/. - Keep the old legacy globs for backwards compatibility.
Why: The user explicitly called out that logs and runs overlap, and V1 should
make .almanac/runs/ the single local process-state directory.
Consequences: Existing repos may still have older ignored paths, but newly
generated V1 ignore blocks only include the derived SQLite files and
.almanac/runs/.
2026-05-09 20:30 PDT#
Decision: Retire public almanac bootstrap wiring in the V1 CLI surface.
Context: bootstrap is the old write-capable AI path. It bypasses the V1
operation/process/harness layers and writes old .almanac/logs/ artifacts.
Alternatives:
- Keep
bootstrapas a deprecated alias toinit. - Leave it public until a later cleanup phase.
Why: The agreed public V1 write commands are init, capture, ingest, and
garden. Keeping a second public build path would preserve architecture drift.
Consequences: The public command was removed first, then the old
runBootstrap, old hardcoded runCapture, capture-status state reader, and
the old bootstrap/writer/reviewer prompt files were deleted in the Phase 8
cleanup. Historical slice plans still describe that path, but V1 runtime code no
longer carries it.
2026-05-09 20:30 PDT#
Decision: capture refuses to start a job when no transcript file is provided
until V1 session discovery is implemented.
Context: The first CLI wiring accepted no-arg capture and launched Absorb with only text saying no session was provided. That created a job without the source context the operation needs.
Alternatives:
- Keep launching and let the prompt infer what to do.
- Reuse the old Claude-only transcript resolver immediately.
- Implement full Claude/Codex/Cursor discovery in this review-fix pass.
Why: Failing clearly is safer than running an empty Absorb job. The old resolver is Claude-specific and would reintroduce the wrong abstraction before the provider/session discovery layer is designed.
Consequences: Explicit transcript-file capture works. No-arg/latest-session and flag-based session discovery are a documented follow-up before V1 is complete.
Update 2026-05-09 20:33 PDT: Claude latest-session and --session <id>
discovery now work in the V1 command path. Codex/Cursor session discovery and
bulk filters remain explicit follow-up work.
Update 2026-05-09 20:54 PDT: Claude --since, --limit, and --all filters
now work in the V1 command path. Codex/Cursor discovery and cross-app
--all-apps remain explicit future work and fail clearly.
2026-05-09 20:30 PDT#
Decision: Codex exec adapter rejects unsupported per-run fields instead of
advertising and silently dropping them.
Context: Codex metadata previously said reasoning effort, MCP, skills, and
context usage were supported, but the codex exec --json adapter did not map
those fields.
Alternatives:
- Keep the broader capability flags for future Codex SDK/app-server support.
- Convert unsupported fields into prompt text.
Why: Provider capabilities must describe the actual adapter primitive, not the provider ecosystem in general.
Consequences: Current Codex V1 runs support the simple exec path: prompt, cwd, model, workspace-write sandbox, output schema, JSONL events, and usage parsing. Future richer Codex transports can re-enable capabilities when implemented.
2026-05-09 20:54 PDT#
Decision: Lifecycle command provider selection uses config only when --using
is omitted.
Context: V1 initially parsed a missing --using value as Claude. That matched
the historical default but ignored user and project config, contradicting the
public command contract.
Alternatives:
- Keep hardcoded Claude as the lifecycle default.
- Require
--usingon every write-capable lifecycle command.
Why: almanac agents use, almanac agents model, and project config are the
existing provider preference surfaces. The V1 lifecycle commands should share
that configuration instead of creating a second default system.
Consequences: init, capture, ingest, and garden resolve configured
provider/model defaults through readConfig({ cwd }). Explicit --using
continues to win for that run only.
2026-05-09 21:05 PDT#
Decision: Run records are terminal-state guarded by the process manager.
Context: A running job can be cancelled from the jobs command while its child
process is still executing. The foreground manager also performs post-harness
work such as page snapshots, indexing, summaries, and final record writes.
Alternatives:
- Let the child always overwrite the record with its final status.
- Treat cancellation as advisory and rely on stale detection.
Why: .almanac/runs/<run-id>.json is the lifecycle source of truth. Once a
record is cancelled, later in-memory process state should not resurrect it as
done or failed. Separately, finalization errors should be visible as failed run
records rather than becoming stale running records.
Consequences: Foreground finalization re-reads the current record before writing terminal status. If the current record is already cancelled, it returns that record. Finalization errors are caught and recorded as failed runs where possible.
2026-05-09 21:05 PDT#
Decision: Harness provider metadata describes implemented adapter behavior, not the broader provider product.
Context: Codex and Cursor have broader product capabilities than the current V1
adapters. Codex V1 is a one-shot codex exec --json adapter, and Cursor is an
explicit not-implemented placeholder.
Alternatives:
- Advertise broader provider capabilities so future work is visible.
- Remove Cursor from the registry until implemented.
Why: Operation code will eventually branch on capability metadata. Aspirational metadata makes that boundary unsafe because callers can trust features that the adapter rejects or cannot run.
Consequences: Codex metadata no longer advertises session persistence, resume, interrupt, or subagents in V1. Cursor remains listed but advertises no executable runtime capabilities until its adapter is implemented.
Update 2026-05-09 21:09 PDT: Claude structured output is also marked false
until the adapter maps AgentRunSpec.output into Claude SDK options. Codex
keeps structured output true because its adapter maps schemaPath to
--output-schema.
2026-05-10 Prompt Base Split#
Decision: Split shared prompt guidance into purpose.md, notability.md, and
syntax.md, then concatenate those before every operation prompt.
Context: The operation prompts need shared guidance for the broader wiki philosophy: project memory, deep-research cache, codebase as anchor but not boundary, entities, external dependencies, influences, research/product/market synthesis, topics, hubs, frontmatter, source grounding, and wikilink syntax. Keeping all of that inside each operation prompt would duplicate prose and make Build/Absorb/Garden harder to keep aligned.
Alternatives:
- Keep one large
wiki-doctrine.md. - Keep only the three operation prompts and duplicate shared rules.
- Split into many tiny modules such as
topics.md,links.md,style.md, andnotability.md.
Why: Three base modules keep the distinction clear without creating prompt plumbing sprawl:
purpose.md: why the wiki exists.notability.md: what deserves pages/topics/hubs and how clusters evolve.syntax.md: how pages are written and grounded.
Consequences: src/operations/run.ts now loads base prompts before the
operation prompt. Future improvements to wiki philosophy can usually land in a
base prompt without touching operation algorithms. Helper/subagent guidance
stays in operation prompts because it is execution behavior, not wiki doctrine.