CodeAlmanac Python Port Live Agreement
This file records the active agreement for the Python rewrite of CodeAlmanac. It is the constraint document for future agents.
Current Decisions#
- 2026-07-15: Anonymous product telemetry is the only remote product exception
in Python v1. Setup asks last, defaults to the recommended Yes, keeps a visible
No, and
--no-telemetry,telemetry.enabled = false,DO_NOT_TRACK, orCODEALMANAC_NO_TELEMETRYdisable all capture. A random installation UUID in local SQLite identifies events and may have a UUID-only PostHog person profile; no name or email exists until a future login explicitly links an opaque account identity. GeoIP is disabled. Allowlisted command and lifecycle outcomes may leave the machine, and real unhandled foreground/background Python exceptions may send only their type, stable structural fingerprint, and CodeAlmanac-only stack shape. Code, exception messages, paths, args, queries, repository/run identifiers, prompts, transcripts, Git data, provider session IDs, locals, environment variables, and code variables never leave the machine. - 2026-07-16: Lifecycle telemetry exports a model only when its harness/model
pair matches the central controlled catalog; unknown or incompatible values
drop the event. Durable
RunSpecremains readable independently of that changing outbound catalog so historical queued records do not become invalid. - 2026-06-29: Python v1 is a local product. Do not build hosted shipping, hosted CLI, login/connect/upload, SDK, or MCP in this rewrite.
- 2026-07-05:
e773dc0bis the fork point for the right local Python product. Laterdevandmainmoved ahead on the wrong hosted/cloud direction. Use those branches andarchive/code/as reference material only. - 2026-07-05: Happy path is the product path. Do not preserve backward compatibility for old CLI names, old wiki roots, old page layouts, hosted-product assumptions, or retired syntaxes.
- 2026-07-05: Intelligence lives in prompts, not pipelines. Auto-commit is
prompt policy given to lifecycle agents, not CodeAlmanac staging or smart Git
orchestration. The default is
auto_commit = true;setup --no-auto-commitandconfig set auto_commit falseonly change the prompt permission. - 2026-07-06: Lifecycle runs do not require a clean
almanac/before starting. Explicit runs snapshot the current Git state, allow pre-existing wiki edits, reject files changed during the run outsidealmanac/, and validate the final wiki before marking the run done. - 2026-07-06: The run/repository/local-database refactor is a breaking
architectural reset. The final code shape is what counts: no half-migrated
concepts, no compatibility aliases, no
registry.jsonmigration, noregistry.jsonfallback, no root hopping, and no parallel local state paths. Repository registration, runs, run events, sync state, and worker locks belong in~/.codealmanac/codealmanac.db. - 2026-07-06: Use the durable internal nouns
repository,run,run kind,queued run,worker,schedule,trigger, andlocal database. Publicjobsremains the user-facing control command for attach/logs/cancel, but internal models, stores, and tables userunsandrun_events. - 2026-07-06: Sync is a trigger/scanner, not agent work and not a run. Scheduled
sync reads active Claude/Codex transcript files since
sync_state.last_completed_atornow - sync_interval, reads each transcript identity, exact-matches transcript cwd to a registered repository, skips internal transcripts, groups active transcripts by repository, and queues one ingest run per repository. No quiet window, cursor ledger, line-count ledger, prefix hash, pending claim, needs-attention state, or foreground/background execution mode remains in the target design. - 2026-07-06:
initis deterministic repository setup plus abuildrun. It creates the repository row,almanac/,almanac/README.md, andalmanac/topics.yaml, materializes the packaged writing manuals underalmanac/manual/, then queues/runsbuildfor the first real agent-authored wiki.initfails with a product error such asAlreadyExistswhenalmanac/already exists at the target path. - 2026-07-06: Code quality is part of the contract. Use Pydantic request models
for shaped service inputs, enums or
Literalfor stable choices, service-owned verbs, store-owned persistence, consistent product errors, and thin CLI edges. Avoid internal-looking single-underscore functions except when they are tiny, local, and clearer than a named boundary. - 2026-06-29: The branch may contain merged
dev/origin/devwork that assumes hosted shipping. Treat that work as reference or archive material, not as product direction for this rewrite. - 2026-06-29: The old TypeScript/Node implementation is archived under
archive/code/. Use it as behavior reference, not as code to preserve. - 2026-07-01: "Behavior reference" means the archived product behavior should be ported unless a decision below explicitly drops it. The Python rewrite is allowed to improve architecture and naming, but it must not silently simplify away setup UX, lifecycle semantics, harness events, page provenance, or viewer scope.
- 2026-06-29: Public command, package, and user-state language is
codealmanac. Do not call the product Codex Almanac, and do not add publicalmanacoralmaliases for compatibility. Python identifiers may use normal class-name casing such asCodeAlmanac. - 2026-06-29: The Python rewrite targets new CodeAlmanac users. Do not keep TypeScript-era backward compatibility, legacy aliases, legacy root migrations, or old frontmatter repair paths.
- 2026-07-01: The intentionally dropped archive-era surfaces are: public
almanac/almcompatibility bins, hosted login/connect/upload/MCP/SDK surfaces, migration commands unless a migration need becomes concrete, and thereviewcommand family for now.setup,uninstall, structured pagesources:, multi-wikiserve, background jobs, and rich harness events are not intentionally dropped. - 2026-06-29: "Frontmatter rewrite" means deterministic editing of current
page metadata such as
topics:while preserving page body text. It is not a compatibility layer for old page formats. - 2026-06-29: Repo-owned wiki data lives under
almanac/only. The committed tree is browseable nested Markdown. Runtime state belongs under~/.codealmanac/. - 2026-07-01: Current-repo auto-detection is exact. Commands that need a
repository run from a registered repository root or receive
--wiki <name>. CodeAlmanac does not walk parent directories to find analmanac/tree. - 2026-06-30: Global user state belongs to
~/.codealmanac/. The repo-local folder may be namedalmanac/; user config, local database state, and scheduler logs use the product-specific hidden directory. - 2026-06-29: Follow Almanac's Python style: service symmetry, explicit request models, service-owned verbs, store-owned persistence, thin CLI edges.
- 2026-07-01: CLI render follows the parser/dispatch/render domain split.
cli/render/root.pyis a small re-export facade for dispatcher stability.cli/render/wiki.pyis a wiki-render facade only;search.pyowns search/reindex output,pages.pyowns show/page output,topics.pyowns topic output,health.pyowns health and validate output, andtagging.pyowns tag/untag output.lifecycle.pyowns lifecycle/sync/job-start output, andcli/render/admin.pyis an admin-render facade only;automation.pyowns automation output,diagnostics.pyowns doctor output,jobs.pyowns jobs output,updates.pyowns update output, andsetup.pyowns setup/uninstall output plus Rich presentation.common.pyowns shared formatting helpers. - 2026-07-01: Wiki CLI dispatch follows the same command-family split.
cli/dispatch/wiki.pyremains the wiki-command facade for search/show/health/validate/reindex/tag/untag routing.topics.pyowns topic subcommand request construction, andserve.pyowns local viewer startup. Do not move topic request construction or uvicorn startup back intodispatch/wiki.py. - 2026-07-01: Lifecycle CLI dispatch follows the same command-family split.
cli/dispatch/lifecycle.pyremains the lifecycle-command facade;build.pyowns init/build request construction,operations.pyowns ingest/garden request construction,sync.pyowns sync and sync status request construction, andworker.pyowns the hidden queue-drain entrypoint. Do not move workflow request construction, sync source parsing, or worker-drain request construction back intodispatch/lifecycle.py. - 2026-07-01: Admin CLI parser construction follows the same command-family
split.
cli/parser/admin.pyremains the admin-parser facade;setup.pyowns setup/uninstall flags,diagnostics.pyowns doctor flags,updates.pyowns update flags,jobs.pyowns jobs flags, andautomation.pyowns automation flags and task choices. Do not move command flag construction back intoparser/admin.py. - 2026-06-30: Validation belongs at product boundaries. Use Pydantic request,
settings, and domain models for shaped data; use enums or literals for finite
choices; reject invalid user/product input with explicit errors. Adapter-local
tolerance can use small built-in parsers that return
Nonefor loose outside-world state, such as reading an existing launchd plist. - 2026-06-29: Public-release readiness is now evidence-gated in
docs/python-port/public-release-readiness.md. Further work should bias toward clean-install proof, real lifecycle dogfood, prompt quality, package metadata, README accuracy, and browser verification instead of adding speculative architecture seams. - 2026-07-01: Background jobs are in scope for v1. Restore the archived machinery in Python shape: per-wiki queue, worker lock, background process owner, durable job/run spec and record files, append-only event logs, attach, cancel, stale-lock handling, and safe foreground execution when explicitly requested. Automation may schedule commands, but the implementation should call workflow/services directly rather than shelling out internally.
- 2026-06-29: Manual
updateis a foreground package-manager command and has pip/uv non-editable install dogfood. - 2026-07-06: Scheduled auto-update is an explicit local automation task, not a
sync/Garden side effect and not a hosted updater. Plain
setup --yesinstalls sync, Garden, and dailyupdateautomation. Interactive onboarding asks whether CodeAlmanac should stay up to date automatically;--yestakes the happy-path default. - 2026-07-06: Scheduled
updatetakes a global lock under~/.codealmanac/, skips when another update is running, skips when queued/running lifecycle jobs exist, skips editable/source installs, supports non-editable uv tool and pip installs, and runscodealmanac --versionpluscodealmanac doctor --jsonas post-update smoke checks. - 2026-07-06:
uninstallis full uninstall. Do not expose--target,--keep-automation,--keep-instructions, or other partial-uninstall flags. The command removes CodeAlmanac-owned instructions, automation, global state, and the installed binary when the install method supports removal. It never deletes repoalmanac/. - 2026-06-29 (superseded 2026-07-15): There was no cloud capture surface in
Python v1 before the narrowly agreed anonymous product-telemetry exception.
codealmanac updateupdates the installed CLI package only.syncscans local transcripts and runs local ingest.automationschedules localsync/garden/update. Do not add publiccapture, cloud upload, hosted connection or login commands without a new agreement. The telemetry exception adds no upload/capture command and never transmits wiki or transcript content. - 2026-06-29:
syncis a scanner, not a replay ledger. It readssync_state.last_completed_at, finds active transcript files, queues ingest runs, records the scan as complete, and relies on visible jobs for individual ingest success or failure. - 2026-06-29:
runsowns lifecycle state transitions. Run records start asqueued, workflows explicitly mark themrunning, and only terminal finish calls may move them todone,failed, orcancelled. - 2026-07-01:
jobs attachandjobs cancelare public control verbs over the run ledger. Cancellation is durable run state, not a CLI-side file edit. Terminal runs are cancel no-ops; queued or running runs append acancelledstatus event. Terminal finish calls must preserve an already-cancelled run rather than rewriting it as done or failed. - 2026-07-10: Running cancellation is execution control, not a status-only
write.
__run-workermanages the serialized queue and starts one hidden__run-executor <run-id>process per run. A running record stores a verified execution id, PID, and process birth time.jobs cancelrecords non-terminal cancellation intent, terminates and confirms the matching executor and its harness descendants, then appends the terminalcancelledevent. If process termination cannot be confirmed, the run must not claim to be cancelled. Queued cancellation remains an atomic terminal transition. Cancellation does not roll back edits or commits completed before the process stopped. - 2026-07-01: Python
jobs attachstreams through a service-owned use case.services/runs/streaming.pypollsRunStore.attach(...), emits only new log events, and stops atdone,failed, orcancelled; CLI rendering owns text and JSON-line output.jobs logsremains the snapshot command. - 2026-07-01: Run persistence is centralized in
codealmanac.db.RunStoreremains the service-facing persistence facade;events.pyowns run-event sequence writes,worker_locks.pyowns the worker lock row, andfactory.pyowns run-id and initialRunRecordconstruction. Do not regrow per-repository run files, JSONL event files, log-path construction, or file-backed queue selection. - 2026-07-01: Queue membership is "queued run with durable
spec_jsonin therunstable." User commands queue a run and spawn a worker. The hidden__run-workercommand is a process entrypoint that callsRunQueue.drain(...); it is not a public command and it does not contain lifecycle business logic. Do not add parallel foreground/background modes;jobs attach <run-id>is the public way to follow a run. - 2026-07-11: Worker shutdown uses an atomic idle handoff. The lock owner rechecks durable queue membership and deletes the global worker lock in one immediate SQLite transaction. If work exists it keeps ownership and resumes draining; do not replace this with polling, sleeps, or a permanent daemon.
- 2026-07-01:
syncqueues eligible transcript ingests and starts the same worker path as manual lifecycle commands. Scheduled automation also launches ordinary scanner/queue commands rather than a separate sync worker model. - 2026-07-01: Shared page-writing lifecycle execution belongs to
OperationRunner. Operation workflows such asingestandgardenprepare operation-specific context and prompts, then delegate running-state transition, mutation preflight, harness invocation, harness transcript/event recording, mutation validation, index refresh, terminal success, and failure recording to the page-run workflow. Do not move this harness/run plumbing back into individual operation workflows. - 2026-07-16: Durable operation failure categories come from explicit workflow
phases, never broad exception classes or traceback-module inference.
OperationRunnerowns shared readiness, provider, indexing, wiki-validation, and internal phases; operation workflows name only their preparation phases, such as ingestsource_preparation, while delegating the actual failure write to the runner. - 2026-07-16: Harness readiness and provider invocation are separate operation
stages. After readiness succeeds, adapter exceptions are
provider_executionregardless of exception class; caller event-sink errors remaininternal_error. Failure-event persistence and the authoritative terminal transition are independent best-effort effects, so one cannot skip the other. - 2026-07-01: Shared lifecycle helper responsibilities are split behind the
import-compatible
workflows/lifecycle.pyfacade.lifecycle_mutation.pyowns Git/workspace mutation preflight, reported-change validation, path-diff comparison, and Almanac-root safety checks.lifecycle_harness.pyowns harness result validation, terminal fallback events, harness-event to run-event classification, and first-line failure summaries. Do not regrow mutation mechanics or harness classification inside the facade. - 2026-06-29: Lifecycle workflows record returned harness status/output before
mutation-safety validation and harness success validation. A failed harness
run should leave an
outputevent injobs logseven when the terminal run error is a later safety failure. - 2026-07-01: The inspectable transcript surface is a CodeAlmanac-owned
normalized harness event stream, not raw provider transcript files. Port the
archived harness event model: text, tool use/results, usage, provider
session, done/failure details, and agent trace events where the provider can
expose them. Codex should use the Codex app-server harness, not
codex exec, for the main lifecycle path. Claude should use the richer SDK/event harness, not only the one-shot CLI print path, when porting archive behavior. - 2026-07-01: The first rich event-log layer is implemented in Python:
HarnessEventcan carry structured tool display, actor attribution, usage, provider session, failure metadata, agent trace, and raw JSON fields, andRunLogEventpersists an optional nestedharness_eventpayload beside the readable log row. - 2026-07-01: The service-owned harness contract is split by product meaning.
kinds.pyowns provider/status enums,actors.pyowns root/helper attribution,events.pyowns normalized transcript event payloads, andresults.pyowns readiness, transcript refs, run results, and terminal helper events.models.pyis only an import-compatible facade; do not regrow the old mixed model module. - 2026-07-01: The default Python Codex harness now uses
codex app-server --listen stdio://, notcodex exec. The adapter keeps readiness probing atcodex login status, but lifecycle execution goes through the app-server JSON-RPC client withmcp_servers={}, ephemeral threads, noninteractive approval/user-input responses, workspace-write sandboxing with network disabled, root-turn completion, helper-turn error isolation, usage parsing, and normalized text/tool/warning/error/done events. - 2026-07-01: Codex app-server event normalization is split by provider-edge
responsibility.
events.pyremains the small notification dispatcher;state.pyowns mutable run state;actors.pyowns root/helper attribution;item_events.pyowns item and output mapping;agent_events.pyowns helper lifecycle traces; andresult.pyowns provider-session, usage, turn completion, and done events. Do not regrow one Codex event monolith. - 2026-07-01: Codex app-server transport orchestration is split from provider
policy helpers.
app_server.pyowns process startup, handshake requests, JSON-RPC reads, and turn flow.responses.pyowns noninteractive server-request responses;sandbox.pyowns sandbox mode/env policy and payload construction;turn_completion.pyowns root-turn completion detection;run_result.pyownsCodexRunState/failure toHarnessRunResultprojection; andtimeouts.pyowns tolerant timeout-env parsing. - 2026-07-01: Real Codex app-server dogfood proved the installed app-server can
drive a full local Ingest lifecycle through the Python harness. The run first
exposed that real
item/agentMessage/deltanotifications may be whitespace-only; the provider edge now drops blank text/plan deltas before constructingHarnessEventbecause normalized harness events require non-empty messages. - 2026-07-01: The default Python Claude harness now uses
claude-agent-sdkfor lifecycle execution, notclaude -p --output-format json. The adapter keeps readiness probing atclaude auth statuswithANTHROPIC_API_KEYfallback when the CLI exists but is not logged in. Runs use isolated SDK options (setting_sources=[],strict_mcp_config=True,mcp_servers={},permission_mode="dontAsk") and map typed SDK messages into normalized provider-session, text, tool, usage, helper-agent, error, and done events. - 2026-07-01: Real Claude SDK dogfood proved the installed SDK and Claude CLI
auth path can drive a full local Ingest lifecycle through the Python harness.
The service-level run used
create_app(AppConfig(registry_path=<tmp>)), real HOME/Claude auth, and a temp repo; runingest-20260701163930-cf193a0efinisheddone, createdrelease-package-smoke.md, search found the page, and health was clean. Normalized harness events included provider session, text, text deltas, tool use/results, context usage, warning, and done. - 2026-07-01: The default lifecycle harness is Codex. Docs, config defaults, setup recommendations, and no-flag lifecycle behavior must agree on that.
- 2026-06-29: Sync pending claims store the run id plus claimed byte/line
cursor.
sync statusreports active linked runs separately from terminal linked runs that need reconciliation. Foregroundsyncreconciles terminal linked runs against the run ledger before deciding whether newer transcript bytes still need Ingest. - 2026-06-29: Scheduled sync is ordinary foreground
synclaunched by local automation with explicit unattended policy: a stable claim owner, pending timeout, and failed-attempt budget. Repeated failed transcript ingests stop at needs-attention instead of retrying forever. - 2026-07-01: Sync orchestration, candidate evaluation, deterministic policy,
and run execution are separate.
SyncWorkflowremains the public facade forstatus,run, andevaluate, but it does not own transcript discovery, wiki scoping, run-record lookup, candidate evaluation, or execution effects.workflows/sync/evaluation.pyowns transcript discovery, explicit--wikiscoping, run-record lookup, ledger load/save for pending reconciliation, cursor decisions, ready/skipped/attention rows, work-item construction, and summary assembly. Do not move these mechanics back intoservice.py. - 2026-07-01:
workflows/sync/policy.pyremains the facade for deterministic sync policy.decisions.pyowns cursor and pending-run decisions,entries.pyowns ledger-entry transitions,identity.pyowns workspace/session/ledger identity helpers,snapshots.pyowns transcript snapshot reading and hashes,reporting.pyowns skip/start rows, andguidance.pyowns generated cursor guidance. Policy modules must not import provider integrations or service objects. - 2026-07-01: Sync run execution effects are split from sync selection.
workflows/sync/execution.pyowns foreground Ingest execution, background queueing, worker-spawn failure handling, pending/failed/absorbed ledger writes, and started summary rows. Do not move lifecycle execution effects intoservice.pyorevaluation.py. - 2026-07-01: Topic read orchestration and topic mutation mechanics are
separate.
TopicsServiceremains the service-facing facade for list/show and mutation verbs.services/topics/mutations.pyowns topic file loading, graph invariant checks, page frontmatter topic rewrites, topic-file writes, and index refresh after topic writes. Do not move topic YAML or page frontmatter rewrite mechanics back intoservices/topics/service.py. - 2026-07-01: Diagnostics is split by check family.
DiagnosticsServiceremains the service-facingdoctorfacade.diagnostics/install.pyowns install/runtime/manual-package checks,diagnostics/wiki.pyowns selected wiki readiness checks, anddiagnostics/messages.pyowns shared doctor message formatting. Do not movedoctormechanics back intoservice.py. - 2026-07-01: Automation service orchestration is split from scheduler job
construction.
AutomationServiceremains the service-facing install, uninstall, and status facade.services/automation/selection.pyowns task defaulting and install-selection validation;definitions.pyowns static task metadata; andjobs.pyownsScheduledJobconstruction, command argv, plist path, launch PATH, interval, and working-directory resolution. Do not move scheduler command construction or selection validation back intoservice.py. - 2026-07-01: Topic service orchestration is split from topic graph mechanics.
TopicsServiceremains the service-facing use-case entrypoint for list, show, create, describe, link, unlink, rename, and delete.services/topics/graph.pyowns topic existence checks, self-parent validation, and DAG cycle traversal;services/topics/read_model.pyowns topic slug lookup through the derived index;services/topics/workspace.pyowns current-repo vs explicit---wikiworkspace selection. Do not move graph traversal, read-model lookup helpers, orSelectWorkspaceRequesthandling back intoservice.py. - 2026-07-01: Wiki topic YAML handling is split by persistence path.
services/wiki/topic_models.pyowns parsed topic models and title defaults;topic_read.pyowns forgiving PyYAML reads for index refresh;topic_file.pyowns ruamel round-trip loading, mutation helpers, and atomic writes; andtopics.pyis only the import facade. Do not mix read/index tolerance, round-trip mutation mechanics, and topic models in one module. - 2026-06-29: Source input has four local layers:
SourceAddress -> SourceRef -> SourceBrief -> SourceRuntime. Git source runtime uses the Git CLI through a source-runtime adapter. GitHub PR/issue runtime uses GitHub CLI through the same port. Transcript runtime uses local JSONL parsing through the same port. Web URL runtime uses a local HTTP and HTML/text adapter through the same port. Path file/directory runtime uses a local filesystem adapter through the same port. - 2026-07-01:
SourcesServiceis the service-facing facade for source verbs. It owns resolve/discover/inspect orchestration over request models and ports, whileservices/sources/address_resolution.pydispatches source-address syntax to source-family modules. Keep runtime adapters and transcript discovery adapters out of address resolution. - 2026-07-01: Source-address resolution is split by address family.
address_resolution.pyis the small dispatcher facade;address_git.py,address_github.py,address_web.py,address_path.py, andaddress_transcript.pyown family-specific parsing andSourceBriefconstruction.address_hints.pyowns prompt hints, andaddress_numbers.pyowns shared positive-integer parsing. Do not regrow GitHub/web/path/transcript parsing, URL validation, hashing, or prompt hints inside the facade. - 2026-07-01: Transcript source runtime is split by integration
responsibility.
integrations/sources/transcripts/runtime.pyimplements theSourceRuntimeAdapterport;models.pyowns typed transcript line and entry models;reader.pyowns JSONL file reading;entries.pyowns known-provider line-to-entry normalization;rendering.pyowns prompt-facing transcript text and tail truncation;paths.pyownsSourceRefpath resolution; anderrors.pyowns unavailable-runtime diagnostics. - 2026-06-29: The local viewer's file route is a wiki-reference route:
/api/file?path=src/foo.pyreturns pages that mention the file or folder reference. It does not read or preview repo file contents. - 2026-06-29: The local viewer renders Markdown through
markdown-it-pytoken streams. Wikilink rewriting touches text inline tokens only; inline code and fenced code stay source text, and labels remain escaped by the renderer. - 2026-06-29:
serveshould borrow UseAlmanac's visual language, not its hosted wiki information architecture. Keep CodeAlmanac's local wiki-first reading model: sidebar navigation, page/topic/search/file-reference graph movement, and a read-only local viewer over repo-owned pages. UseAlmanac's colors, shell polish, account-picker styling, and dashboard chrome are valid reference material. Its hosted wiki page list/search flow, account routes, billing/settings surfaces, and hosted wording are not the target shape for the local product. - 2026-06-29: Treat the current UseAlmanac wiki page and search experience as a non-target reference for CodeAlmanac. The desired direction is the local viewer already explored here: persistent sidebar, direct page reading, topic/file/search movement, and wiki navigation that feels like a repo-local knowledge browser. The earlier CodeAlmanac sidebar shell is the interaction target. The design layer can borrow UseAlmanac polish, but the current UseAlmanac page list and search treatment should not be copied. The issue is not the wiki content model; it is the visual/product treatment around the wiki reader.
- 2026-06-29: Bulletproof React is a frontend architecture reference for
serve, not a mandate to add React or Next.js immediately. Apply its principles as the viewer grows: feature boundaries, colocated API requests, shared UI primitives, direct imports over broad barrels, and browser-level tests. A Next.js viewer is allowed only if the static package-data viewer becomes harder to maintain than a small built frontend. - 2026-06-29: The
servefrontend can use static ES modules while it remains a small read-only viewer.app.jsis the entrypoint; nested modules underserver/assets/viewer/own API calls, hash routes, DOM components, and screen renderers. This is the current Bulletproof-inspired seam. Do not add React, Next.js, Vite, or a frontend build step until real UI complexity makes static package data harder to maintain. - 2026-06-29: Filesystem directory source runtime uses Git listing when the selected directory is inside a Git worktree, then falls back to the bounded Python/pathspec walk when Git cannot answer. This is runtime material selection, not a new source kind or source catalog.
- 2026-06-29: Git-listed filesystem directory runtime ranks changed and
untracked files before unchanged files and annotates included files as
changedorunchangedin the runtime tree. This is a prompt-material selection policy inside the filesystem adapter; it is not a durablecandidateobject. - 2026-06-29: Clean filesystem directory runtime uses semantic diversity
inside the adapter: after changed/untracked files, bounded selection
interleaves directory groups and prefers role-bearing files such as
service.py,adapter.py,app.py, andmain.py. Do not add a source pool, durable candidate object, or Ingest branching for this. - 2026-07-01: Filesystem source runtime is split by integration
responsibility.
adapter.pyimplements theSourceRuntimeAdapterport;documents.pyowns text document models and charset decoding;listing.pyowns directory document assembly and Git-vs-walk source selection;ignore.pyowns default/configured ignore rules and.gitignoreloading;walk.pyowns Python directory walking;git.pyowns Git listing/status mechanics;rendering.pyowns prompt-facing runtime text;paths.pyowns shared display/relative path helpers. Keep source selection policy local to the filesystem integration unless a second adapter needs the same behavior. - 2026-07-01: GitHub source runtime is split by integration responsibility.
integrations/sources/github/adapter.pyimplements theSourceRuntimeAdapterport;client.pyownsghprocess execution and typed payload retrieval;models.pyowns Pydanticgh --jsonpayloads;targets.pyownsSourceReftoghtarget arguments;rendering.pyowns prompt-facing PR and issue runtime text;errors.pyowns unavailable-runtime diagnostics. - 2026-07-01: Web source runtime is split by integration responsibility.
integrations/sources/web/adapter.pyimplements theSourceRuntimeAdapterport;client.pyownshttpxstreaming and bounded response reads;models.pyowns typed fetched-response and runtime-document models;documents.pyowns content-type classification, UTF-8 decoding, and Beautiful Soup HTML text extraction;rendering.pyowns prompt-facing metadata/content rendering; anderrors.pyowns unavailable-runtime diagnostics. - 2026-07-11:
manual/is a local support package, not a public CLI surface. It contains bundled wiki-maintenance doctrine.initcopies missing packaged manuals intoalmanac/manual/before the build agent starts, preserves existing local manual files, and gives build agents exact repository-local paths. The reservedmanual/directory is excluded from wiki page indexing. - 2026-07-01: Preserve the archived terminal setup experience in Python:
branded banner, step indicators, raw-mode selection when available,
non-interactive
--yes, idempotent setup, agent/default-model selection, instruction target installation, local automation choices, and a polished next-steps box. Hosted-specific wording must be removed, but the terminal UX quality and setup responsibilities remain first-class. - 2026-07-01:
uninstallshould exist as the reverse ofsetup. It should remove setup-owned instructions and scheduler entries idempotently, with--yesand keep flags where useful. - 2026-07-01: Setup instruction installation is split by target family behind
the service-owned
InstructionInstallerport.integrations/setup/instructions.pyis only theFileInstructionInstallerfacade and target dispatcher;codex.pyowns Codex AGENTS block selection and cleanup;claude.pyowns Claude guide/import files;managed_blocks.pyowns marker text transforms;guide.pyowns package resource loading; andtext_files.pyowns narrow UTF-8 file reads. Do not move provider marker surgery, package resource reads, or target-specific file writes back intoinstructions.py. - 2026-07-01: Setup service orchestration is split from setup planning and
automation-policy helpers.
services/setup/service.pyremains theSetupServicefacade that calls instruction and config services;planning.pyownsSetupPlan, automation recommendations, and next-step command construction; andautomation.pyowns setup automation selection. Do not move duration formatting or automation recommendation construction back intoservice.py. Setup writes one complete user-config update; config reconciliation applies its automation policy to the scheduler. - 2026-07-01: The archive's
reviewcommand family is not required for now. The archive'smigratecommand family is not needed unless a concrete migration path is reopened. - 2026-06-29:
database/owns SQLite connection setup and migration application. Product stores still own their SQL schemas and query semantics. The currentindex.dbmigration strategy is rebuild-on-version-change because the index is a derived read model fromalmanac/**/*.mdandtopics.yaml. - 2026-07-01: Index read views are split by query family.
IndexStoreremains the store facade,services/index/views.pyremains a small compatibility facade, and read-side SQL lives insearch_views.py,summary_views.py,page_views.py,topic_views.py, and health read views.health_views.pyis theHealthReportfacade;health_graph_views.pyowns page/topic/link/file findings, andhealth_source_views.pyowns sources/citations findings. - 2026-07-01: Index write-side responsibilities are split behind
IndexStore.services/index/schema.pyowns the derivedindex.dbschema, migrations, and index connection helper;sources.pyowns page/topic source loading and freshness signatures;projection.pyowns replacement writes and stored source signatures.store.pystays the service-facing facade. - 2026-07-14: Search moves from whole-page strict-AND FTS to section-level
SQLite FTS5 with BM25 ranking. Markdown headings define coherent indexed
sections; a recall-oriented lexical query admits sections matching meaningful
terms; page title and section heading receive more weight than body prose;
and results collapse back to ranked pages while retaining the best matching
heading and excerpt. The public product remains
search -> show, Markdown remains truth, and SQLite remains rebuildable derived state. Do not add semantic retrieval yet. If measured real-repo and benchmark misses repeatedly show relevant sections with insufficient lexical overlap, add dense retrieval as a second candidate generator and fuse it with lexical results rather than replacing exact lexical search. - 2026-07-10:
configowns the only configuration file at~/.codealmanac/config.toml; repository-levelalmanac/config.tomlis removed. The user config stores auto-commit, harness/model, and enabled/ interval policy for Sync, Garden, and Update.config setpersists a value and immediately reconciles affected launchd automation;config applyvalidates direct TOML edits and reconciles all automation. Public automation mutation commands are removed;automation statusremains actual-state inspection. CLI flags still win for one command. No watcher, migration reader, compatibility alias, secrets system, or hosted/account config. - 2026-07-01: Page
sources:are part of the wiki page model, not just prompt guidance. The Python index/read model should parse structuredsources:, project them into SQLite, derive file refs fromsources[type=file], expose them throughshow/viewer APIs, and let health reason about missing/unused citation IDs. This is distinct from ingest source inputs such as paths, diffs, PRs, URLs, and transcripts. - 2026-07-01: Page source address parsing prefers type-specific fields
(
path:forfile,url:forweb, and the documented fields for other types), but accepts generictarget:as a fallback for every source type. This keeps the internalPageSource.targetmodel aligned with prompt-shaped output without making downstream index/search/viewer code know alternate frontmatter spellings. - 2026-07-01: Remove archive/supersede page lineage from the Python product
model. Git history is the archive. Do not keep
archived_at,superseded_by,supersedes,--include-archive, or--archivedas product concepts unless the user reopens that decision. - 2026-07-01:
serveshould browse all registered local wikis, matching the archive's multi-wiki viewer scope, while keeping the local sidebar/wiki reader interaction model. - 2026-07-01: Viewer service boundaries are split by local-reader
responsibility.
services/viewer/service.pyremains the use-case facade for overview/page/search/file/topic/jobs payloads;repository_scope.pyowns current-repository and--wikiselection;projections.pyowns conversion from index/repository models to viewer DTOs. Do not move repository selection or DTO construction back intoservice.py. - 2026-07-01: Server adapter boundaries are split by HTTP-edge
responsibility.
server/app.pyis the FastAPI composition root only;api_routes.pyowns viewer API route request construction;static_routes.pyowns browser-shell route registration;static_assets.pyowns package asset validation and reads; anderrors.pyowns product/Pydantic exception mapping. Do not move route bodies, asset loading, or HTTP error mapping back intoapp.py. - 2026-06-29: The CLI edge is allowed to split by command domain as pressure
appears.
cli/dispatch/admin.pyis an admin-command facade only;setup.pyowns setup/uninstall request construction,diagnostics.pyowns doctor request construction,updates.pyowns update request construction,jobs.pyowns jobs request construction, andautomation.pyowns automation request construction. The root dispatcher delegates to command-domain edges and keeps services/workflows as the product boundary. - 2026-06-29: The SQLite index service separates projection writes from
read-only views.
services/index/store.pystays the service-facing facade;schema.py,sources.py, andprojection.pyown write-side mechanics;services/index/views.pyowns read-only query SQL and row-to-Pydantic view construction. - 2026-06-29: Repository registration lives in the local database. There is no
registry.json, no migration path, and no fallback reader. Repository rows are not auto-dropped; unavailable paths are reported or skipped by the command that encounters them. - 2026-07-01: Repository service boundaries are split by repository mechanic.
services/repositories/service.pyremains the use-case facade for initialization targets, register/get/select/resolve, and path validation.identity.pyowns repository names and ids;selection.pyowns name matching and path containment;state.pyowns availability classification;roots.pyowns fixed-root validation and direct-root marker checks; andstore.pyowns repository persistence incodealmanac.db. Do not move selector mechanics, id generation, or marker-based status checks back intoservice.py. - 2026-06-30: An initialized Almanac tree is identified by wiki markers
(
almanac/README.mdandalmanac/topics.yaml), not by directory existence. Runtime artifacts such asindex.dband WAL files are derived state. They must not make repository status,doctor, or read commands treat an otherwise missing root as available. - 2026-06-30:
docs/python-port/next-agent-brief.mdis a load-bearing continuation artifact. Public-contract tests discover the newestdocs/python-port/slice-N-*.mdfile and require the brief's current-state section to mention that slice, so future agents do not resume from a stale checkpoint after compaction. - 2026-06-30: Public beta release judgment lives in
docs/python-port/public-beta-gate-audit.md. Slice 70 passed the remaining non-toy lifecycle dogfood pass, and slice 71 reran current-head package proof after the state-path change. Current remaining release work is operational: version, changelog, PyPI credentials, publish ownership, and the human publish decision.
Product Frame#
CodeAlmanac maintains a repo-owned wiki for a codebase and the project world around that codebase. It is not a code documentation generator.
The wiki can cover code, architecture, decisions, incidents, conversations, PR context, team conventions, deployment constraints, product strategy, and other durable knowledge that helps future work.
The durable artifact is the repository's almanac/ tree. Git remains the
system of record for wiki changes.
The Python rewrite is a fresh codebase. Use the old CodeAlmanac implementation and Almanac's Python engine as references for behavior and structure. Do not share Almanac's hosted engine as a dependency in v1.
Repository Shape#
Target root:
src/codealmanac/
app.py
core/
database/
services/
workflows/
integrations/
server/
cli/
prompts/
manual/app.py is the composition root. It wires services and integrations through
explicit dependencies.
core/ holds shared errors, identifiers, path/text helpers, and tiny cross-cutting
models that do not belong to one service.
database/ owns SQLite connections and migration application.
services/ holds product nouns:
services/
repositories/
wiki/
index/
sources/
runs/
harnesses/
automation/
config/
diagnostics/
updates/
viewer/workflows/ holds multi-service product verbs:
workflows/
build/
ingest/
garden/
sync/integrations/ holds concrete outside-world adapters. Organize adapters by
the service boundary they implement, not as one flat external-tool list.
integrations/
repositories/
git/
harnesses/
codex/
claude/
sources/
filesystem/
git/
github/
transcripts/
web/
automation/
scheduler/
updates/
package.pyPorts live near the service that owns the product contract:
services/harnesses/ports.py
services/sources/ports.py
services/automation/ports.pyConcrete integrations implement those ports:
integrations/harnesses/codex/adapter.py
integrations/harnesses/claude/adapter.py
integrations/sources/filesystem/adapter.py
integrations/sources/github/adapter.py
integrations/sources/github/client.py
integrations/sources/github/models.py
integrations/sources/transcripts/codex.py
integrations/automation/scheduler/launchd.pyDo not put product decisions inside integrations. An integration translates an outside tool into service-owned models and errors.
cli/ owns command parsing, rendering, stdout/stderr, and exit codes. It does
not own product decisions.
manual/ owns bundled wiki-maintenance doctrine. It is read by prompts from
package resources and checked by diagnostics. It does not add a public command.
Python Service Symmetry#
Inside a service package, use these names when needed:
models.py domain/read models
requests.py service input contracts
ports.py adapter protocols
store.py CRUD/query persistence
records.py row/model conversion
tables.py migrations only
service.py product verbsDo not create empty files just to satisfy symmetry.
SQL belongs in store.py, *_store.py, or tables.py. tables.py defines
migrations only. Row conversion belongs in records.py or a store-owned helper.
Services own product verbs. Stores own CRUD/query. Integrations implement service-owned ports. CLI and future hosted/server edges adapt.
Allowed dependency direction:
cli/server
-> app
-> workflows
-> services
-> stores
-> ports
-> integrationsDo not make stores call services. Do not make integrations call services. Do not make CLI contain product decisions.
Service Ownership#
| Service | Owns | Must Not Own |
|---|---|---|
repositories |
registered repository rows, exact current-directory selection, --wiki name selection, fixed almanac/ root checks, path containment |
page parsing, source discovery, run execution policy |
wiki |
markdown page truth, frontmatter, topics, Markdown links, page writes, health inputs | trigger timing, harness execution, source discovery |
index |
SQLite read model, FTS, mentions, backlinks, query projections | markdown truth, agent execution |
sources |
source observations, source refs, fingerprints, local source state | deciding when AI runs, page writes |
runs |
run ledger, events, outputs, lifecycle state transitions | source discovery, page parsing, provider transports |
harnesses |
normalized Codex/Claude task/session/event contracts and ports | run lifecycle, page writes, source catalog |
automation |
local trigger decisions and scheduler state | run internals, source parsing, provider transports |
config |
user config persistence and automation reconciliation | product workflows |
diagnostics |
doctor-style checks and readiness reports | mutation workflows |
updates |
local package update policy, installer detection, update command planning | scheduler state, hosted release management, package-manager subprocess mechanics |
viewer |
read-only local browser payloads, page/topic/search overview assembly, rendered markdown for the viewer | markdown source of truth, SQLite persistence, AI calls, jobs/review lifecycle |
The viewer's product shape is local wiki browsing. It may adopt UseAlmanac's alpine visual system and shell styling, but it should not replace the existing wiki sidebar/graph-navigation model with the hosted UseAlmanac wiki page-list or search flow.
For frontend structure, read docs/reference/bulletproof-react/CODEALMANAC.md.
If serve remains a static asset bundle, mirror the same boundaries in plain
assets: shared visual primitives, wiki-specific rendering, and API helpers
should stay distinct instead of becoming one unstructured script.
The current static module layout is:
server/assets/app.js
server/assets/viewer/api.js
server/assets/viewer/routes.js
server/assets/viewer/components.js
server/assets/viewer/renderers.js
server/assets/viewer/main.jsWorkflows#
Workflows coordinate services. They do not own durable schema unless a service is missing.
init creates the initial local wiki at almanac/ and starts the initial
agent-backed wiki build. It fails when almanac/ already exists at the target
path. Do not split the user-facing first-build story into a surprising init
scaffold plus unrelated build index refresh unless the CLI names make that
distinction obvious.
ingest updates the wiki from selected local material such as paths, PR refs,
diffs, commit ranges, notes, or transcript refs.
AI-backed ingest must be auditable before it becomes public CLI behavior. The
workflow requires Git change tracking and no non-wiki file mutation during
harness execution. Pre-existing almanac/ edits are allowed when the user
triggers the run. Dirty application files may exist as source material if their
observed state does not change during the run. Workflows persist normalized
harness events into run logs after the harness returns and before later
validation.
sync scans supported local transcript stores for conversations active since
the last completed sync, exact-matches each transcript cwd to a registered
repository root, groups active transcripts by repository, and queues ordinary
local ingest runs. It does not wait for quiet sessions, claim transcript ranges,
or mean cloud sync.
garden maintains existing wiki structure, links, topics, stale pages, and page
quality. It may run without new source material.
Source Model#
source means raw material CodeAlmanac can learn from. It does not mean
"source of truth."
Authority depends on the claim:
- Code is authoritative for runtime behavior.
- Transcripts are authoritative for what was discussed.
- PRs are authoritative for review context.
- The wiki is the maintained synthesis.
Discovery answers: what material exists or changed?
Use answers: what material should this run use?
There is no durable candidate object in v1. Bundling is a run input selection,
not a discovered product entity.
Adding or discovering material does not imply a wiki update. Updating the wiki
is a separate ingest, build, sync, or garden workflow.
Page file/folder evidence belongs to sources: frontmatter, then to wiki and
index, not to the source catalog. File sources power mentions search and
health.
CLI Contract#
The v1 CLI is local-only.
codealmanac init [path]
codealmanac setup
codealmanac list
codealmanac search [query]
codealmanac show <page-id>
codealmanac topics
codealmanac health
codealmanac validate
codealmanac serve
codealmanac reindex
codealmanac ingest <inputs...>
codealmanac sync
codealmanac sync status
codealmanac garden
codealmanac automation status
codealmanac jobs
codealmanac jobs attach <run-id>
codealmanac jobs cancel <run-id>
codealmanac doctor
codealmanac update
codealmanac uninstallCommands that need a current repository require the exact current directory to
be a registered repository root, unless the user passes --wiki <name>.
There is no --root setup option. almanac/ is the only repo wiki root.
Repository registration lives in ~/.codealmanac/codealmanac.db.
Use --wiki <name> to target a different registered local wiki. Do not add
codealmanac use <wiki> in v1; sticky selection is hosted-style state and can
confuse agents.
There is no public capture verb. Conversation collection is part of sync or
a future explicit local source workflow.
There is no public absorb command. The public lifecycle word is ingest.
codealmanac validate is the explicit correctness gate for agents and lifecycle
runs. It refreshes the derived index, checks links, sources, runtime leakage,
and health findings, and exits nonzero when the wiki has validation issues.
codealmanac reindex is the explicit escape hatch for rebuilding the derived
SQLite read model. Query commands may refresh the index implicitly and silently.
codealmanac sync accepts local execution controls such as
--claim-owner, --pending-timeout, and --max-failed-attempts. Automation
uses them to make scheduled sync ownership and retry policy explicit.
Public jobs is required because background lifecycle execution ships. Internal
runs owns records, logs, event streams, outputs, and lifecycle state.
jobs attach streams a running job's event log. jobs cancel requests
cancellation for queued/running work.
CLI commands are not internal APIs. Automation, workers, tests, and server
wrappers must call the same Python services/workflows that CLI dispatch
calls. They must not shell out to codealmanac.
Correct shape:
app.workflows.sync.run(request)
app.workflows.ingest.run(request)
app.runs.record_event(request)
app.viewer.page(request)Incorrect shape:
subprocess.run(["codealmanac", "sync"])
subprocess.run(["codealmanac", "ingest", "..."])
subprocess.run(["codealmanac", "show", "..."])Feature Map#
MVP#
| Area | Features |
|---|---|
| Install/name | codealmanac package and command only |
| Repository | exact root selection, --wiki name selection, local database registration, path containment |
| Wiki | nested Markdown pages, frontmatter, topics, Markdown links, file/folder sources |
| Index | SQLite read model, FTS search, mentions, backlinks, health |
| Sources | transcript/path/Git/GitHub/web input contracts, local observations, and runtime snapshots |
| Runs/jobs | durable run records, queued specs, events, outputs, attach/cancel |
| Harnesses | Codex app-server and Claude SDK/event harnesses behind normalized ports |
| Workflows | build, ingest, sync, garden |
| Automation | local scheduled sync/garden/update |
| CLI | thin local command surface |
| Serve | local read-only wiki viewer |
| Update | foreground and scheduled local package-manager update with conservative source-install refusal |
Explicitly Out Of Scope For V1#
| Area | Decision |
|---|---|
| Hosted product | Do not build hosted shipping now |
| Hosted CLI | No login, connect, upload, hosted sources, hosted jobs |
| SDK | No Python SDK package |
| MCP | No MCP server |
| Compatibility aliases | No public almanac, alm, or absorb |
| Archive lineage | No archived_at / superseded_by page state |
| Semantic search | Section-level FTS5/BM25 first; hybrid only after measured lexical-overlap misses |
Non-Negotiables#
- Public naming is
codealmanac. - The
almanac/tree is the repo-owned wiki artifact. - Global user state lives under
~/.codealmanac/. - Local v1 must not inherit hosted assumptions from merged
devororigin/dev. - CLI edges stay thin.
- Services own product verbs.
- Stores own persistence.
- Integrations sit behind ports.
- Structured models beat loose dictionaries.
- No hidden
getattror dynamic attribute contracts for shaped data. - No source-kind branches smeared across unrelated services.
- No CLI-shaped core.
- No AI-writing path outside explicit lifecycle workflows.
- SQLite remains the local read model for search, refs, topics, and health.
- File/folder mention queries use normalized paths and GLOB-safe matching.
Open Decisions#
- Whether local
adddeserves a v1 command or should wait until source-pool behavior is concrete. - Resolved 2026-07-01: background jobs ship in v1. Public
jobsreturns as the inspection/control noun; internalrunsowns records, events, outputs, worker state, and lifecycle transitions. - Resolved 2026-06-29:
serveis restored after the core CLI/read model, because the Python index can now support a read-only viewer without a second content model.