Python Port Ownership Map

Updated: 2026-07-01

This map turns docs/python-port-live-agreement.md into implementation boundaries. If code needs a different boundary, update this file and record the reason in idea-evolution.md.

Dependency Direction#

cli/server
  -> app
    -> workflows
      -> services
        -> stores
        -> ports
          -> integrations

src/codealmanac/app.py is the composition root. CLI commands, automation entrypoints, tests, and future server wrappers get an application object from that root instead of constructing stores or adapters themselves.

Services#

Service Owns First implementation pressure
workspaces repo root detection, configurable Almanac root, registry, nearest-root discovery, path containment, local wiki selection, explicit registry cleanup init, list, current-repo queries, --wiki lookup
wiki page files, frontmatter, topics, wikilinks, page writes, health inputs init, show, page parsing for index
index SQLite derived read model, projection refresh/write facade, read-only query views, FTS, mentions, backlinks, health projections search, show --links, health
topics topic use cases, topic DAG validation, topic workspace selection topics, topics show, topics create/describe/link/unlink/rename/delete
sources source observations, source refs, address resolution, fingerprints, local source state, source runtime snapshots, transcript discovery ports and typed transcript candidates SourceAddress/SourceRef/SourceBrief/SourceRuntime, SourceRuntimeAdapter, TranscriptDiscoveryAdapter, TranscriptCandidate, ingest and sync inputs
runs run ledger, events, outputs, lifecycle state transitions, persisted harness transcript identity jobs read surface, lifecycle workflows, future sync exclusion and reconciliation
harnesses normalized Codex/Claude run contracts, provider transcript refs, provider-neutral harness events, and ports HarnessKind/RunHarnessRequest/HarnessRunResult/HarnessTranscriptRef/HarnessEvent/HarnessAdapter, later build, ingest, garden
automation local scheduler decisions, quiet windows, unattended sync command policy, installed task state AutomationTask/ScheduledJob/SchedulerAdapter, sync and garden scheduling
config user/project TOML parsing and precedence for local CLI defaults lifecycle harness default and sync quiet window
diagnostics doctor checks and readiness reports doctor, local install/wiki readiness
updates package update planning, installer metadata, supported foreground update methods codealmanac update, PackageInstallMetadataProvider, PackageCommandRunner
setup setup/uninstall command requests, setup-owned agent instruction result models, and the instruction installer port codealmanac setup, codealmanac uninstall
viewer read-only browser payloads, page/topic/search/file overview assembly, multi-wiki local viewer scope, rendered markdown for the local viewer serve, future non-CLI read adapter

Support Packages#

Package Owns First implementation pressure
database SQLite connection setup, PRAGMA policy, and migration application helpers index.db read model
prompts packaged lifecycle prompt doctrine and operation prompt rendering ingest, garden, future sync
manual packaged wiki-maintenance rulebook and workspace manual materialization helpers build, doctor, lifecycle prompt guidance

Workflows#

Workflow Owns Calls
build initial wiki creation or refresh workspaces, wiki, index
ingest update wiki from selected local material sources, runs, harnesses, index, prompts, lifecycle
sync discover quiet local transcripts, scope them to local wikis, coordinate run-record lookup and ledger persistence, call sync policy for cursor/ledger decisions, run foreground ingest or enqueue background ingest, and assemble sync summaries sources, runs, ingest, run_queue, sync ledger store, sync policy, automation command policy
garden maintain wiki shape, links, topics, staleness, quality health, index, runs, harnesses, prompts, lifecycle

Workflows coordinate. They do not own durable schema unless a missing service is identified and added to this map.

services/wiki/frontmatter.py is the normalization boundary for page frontmatter. It accepts type-specific source address fields first and generic target: as a fallback, then returns normalized PageSource.target values. Index, search, show, health, and viewer code must consume PageSource rather than reading raw source frontmatter keys.

workflows/lifecycle.py owns shared lifecycle execution helpers: harness-result validation and Git-backed wiki-root mutation safety. Operation-specific workflows pass their public verb into LifecycleMutationPolicy, so shared safety does not leak another workflow's product language.

Integration Rule#

Concrete adapters live under integrations/ by the service port they implement:

integrations/
  harnesses/
    codex/
    claude/
  sources/
    filesystem/
    git/
    github/
    transcripts/
    web/
  automation/
    scheduler/
  setup/
  updates/

An integration translates outside-world behavior into service-owned models and errors. It does not decide product policy.

src/codealmanac/app.py is the only production module that wires concrete integrations. Tests enforce that cli/, workflows/, and services/ do not import codealmanac.integrations.

services/workspaces/ports.py also owns WorkspaceChangeProbe, the port used by lifecycle workflows to inspect repo/worktree mutation state. The concrete Git implementation lives in integrations/workspaces/git/. Ingest policy decides what mutations are allowed; the Git integration only reports observed state.

integrations/command.py holds captured subprocess execution shared by local integration adapters. It is not a service port because it describes local process mechanics, not a product contract.

database/ holds local SQLite mechanics shared by stores. connect_sqlite creates parent directories, applies row factory and PRAGMA policy, sets a busy timeout before WAL mode, and returns the connection. apply_migrations applies typed SQLiteMigration values in version order. Product stores still own SQL schema text and write/query semantics.

services/index/store.py is the service-facing IndexStore facade. services/index/schema.py owns derived index.db schema, migrations, and the index connection helper. The pages table stores the current page read model; it does not store archive or supersede lineage. services/index/sources.py owns page/topic source loading and freshness signatures. services/index/projection.py owns replacement writes and stored source signatures. services/index/views.py is a small read facade. services/index/search_views.py owns FTS and file-mention query construction, summary_views.py owns count summaries, page_views.py owns page detail projections, topic_views.py owns topic DAG reads, health_views.py assembles HealthReport, health_graph_views.py owns page/topic/link/file findings, and health_source_views.py owns sources/citations findings.

services/config/ owns local config parsing and precedence. ConfigStore uses pydantic-settings TOML sources to build the frozen UserConfig settings model. ConfigService supplies sources in precedence order: selected project <almanac-root>/config.toml, then user config, then model defaults. CLI flags remain the final override at the command edge. There is no public config command in v1.

services/diagnostics/service.py is the doctor facade. install.py owns install/runtime/manual-package readiness checks, wiki.py owns selected-wiki registry/index/manual/health readiness checks, and messages.py owns shared doctor message formatting.

services/topics/service.py is the use-case facade for topic commands. services/topics/graph.py owns topic existence checks, self-parent validation, and cycle traversal over TopicDefinition values. services/topics/read_model.py owns topic slug lookup through the derived index. services/topics/workspace.py owns current-repo vs explicit---wiki selection through WorkspacesService.

services/workspaces/roots.py owns repo-local Almanac root validation and nearest-root discovery. DEFAULT_ALMANAC_ROOT is almanac/, and the conventional discoverable roots are almanac/, docs/almanac/, and .almanac/, detected by topics.yaml + pages/. The registry stores each workspace's repo-relative almanac_root; downstream services use workspace.almanac_path instead of concatenating a literal path. services/workspaces/service.py is the use-case facade. identity.py owns workspace name/id generation, selection.py owns selector matching and path containment, status.py owns marker-based registry availability, and store.py owns registry JSON persistence.

integrations/harnesses/git_status.py holds Git porcelain changed-file snapshots shared by Claude and Codex harness adapters.

Harness adapters translate provider output into HarnessEvent values before workflows persist anything to run logs. Raw provider transcript files are not a workflow contract; they are optional provider evidence for exclusion and debugging. services/harnesses/models.py is a facade for the service-owned harness contracts. kinds.py owns provider/status enums, actors.py owns root/helper attribution models, events.py owns normalized event payloads, and results.py owns readiness, transcript references, run results, and terminal event helpers. Codex app-server event normalization is split by provider-edge reason to change: events.py dispatches notifications, state.py stores mutable run state, actors.py assigns root/helper attribution, item_events.py maps tool items and output deltas, agent_events.py maps helper-agent lifecycle traces, and result.py maps usage, provider-session, turn completion, and final done events. Codex app-server transport stays separate from provider policy helpers: app_server.py owns process startup, handshake requests, JSON-RPC reads, and turn flow; responses.py owns noninteractive server-request responses; sandbox.py owns sandbox mode/env policy and turn sandbox payloads; turn_completion.py owns root-turn completion detection; run_result.py owns HarnessRunResult projection; and timeouts.py owns tolerant timeout-env parsing.

services/runs/store.py is the service-facing repository facade for the run ledger. services/runs/paths.py owns run-id validation and file naming, services/runs/io.py owns JSON record/spec and JSONL event file mechanics, services/runs/locks.py owns worker lock ownership, and services/runs/transitions.py owns grouped record-plus-event transition writes. services/runs/factory.py owns run-id and initial RunRecord construction. services/runs/queries.py owns sorted record listing and oldest spec-backed queued-run selection. Callers should not hand-pair record rewrites with status event appends outside the transition writer.

services/sources/ports.py owns TranscriptDiscoveryAdapter, the port used by sync to observe local transcript stores. Concrete Codex and Claude JSONL scanners live in integrations/sources/transcripts/. Those integrations parse raw provider JSON and return typed TranscriptCandidate values; they do not decide quiet windows, cursor state, or whether ingest should run. TranscriptCandidate carries both repo_root and almanac_path so sync can write ledgers under the configured root without guessing.

workflows/sync/service.py owns sync selection orchestration. It discovers transcripts, scopes them to the requested local wiki, loads run records and ledgers, evaluates policy decisions, and assembles status/sync summaries. workflows/sync/execution.py owns sync run execution effects: foreground Ingest execution, background queueing, worker-spawn failure handling, pending/failed/absorbed ledger writes, and started summary rows.

workflows/sync/policy.py is the deterministic sync-policy facade imported by SyncWorkflow. decisions.py owns cursor decisions and pending-run reconciliation. entries.py owns pending/absorbed/failed ledger-entry transitions. identity.py owns workspace, session, run-record, and ledger-key identity helpers. snapshots.py owns transcript snapshot reading, line counts, and cursor-prefix hashes. reporting.py owns skipped/started summary rows. guidance.py owns generated cursor guidance for Ingest. Scheduled sync uses the same workflow/policy split.

The same source service owns SourceRuntimeAdapter, the port used by Ingest to turn selected source refs into bounded readable material before harness execution. services/sources/address_resolution.py owns source-address syntax, and dispatches to source-family modules. address_git.py owns Git range/diff syntax, address_github.py owns GitHub shorthand and URL decomposition, address_web.py owns HTTP URL validation and generic web refs, address_path.py owns local path classification and file fingerprints, address_transcript.py owns transcript refs, address_hints.py owns prompt hints, and address_numbers.py owns shared positive-integer parsing. SourcesService calls it from the resolve verb but does not own those parsing rules. integrations/sources/filesystem/ reads explicit local files and bounded directory material. adapter.py implements the service-owned SourceRuntimeAdapter port and delegates to module-level responsibilities: documents.py owns text document models, file byte bounds, and charset-normalizer decoding; listing.py owns directory document assembly and Git-vs-walk source selection; ignore.py owns default/configured ignore rules and .gitignore loading; walk.py owns Python directory walking; git.py owns Git listing, Git status parsing, repo-root probing, and Git command tolerance; selection.py owns semantic diversity for clean directory selection; rendering.py owns prompt-facing runtime text; paths.py owns shared display/relative path helpers. The adapter normalizes the request cwd before delegating because source refs are already normalized; this keeps symlinked and macOS alias paths rendered relative to the selected workspace. InspectSourceRuntimeRequest.context.ignored_directories carries workflow-owned runtime policy such as the resolved workspace.almanac_root; filesystem adapters apply those ignores but do not hard-code Almanac root names. integrations/sources/git/ uses Git CLI commands for local git:diff and git:range refs. integrations/sources/github/ uses GitHub CLI for PR and issue refs. adapter.py implements the service-owned SourceRuntimeAdapter port, client.py owns gh command execution and typed payload retrieval, models.py owns Pydantic gh --json payloads, targets.py owns SourceRef to gh target argument policy, rendering.py owns prompt-facing PR and issue runtime text, and errors.py owns unavailable runtime diagnostics. integrations/sources/transcripts/ reads provider JSONL transcripts with jsonlines, validates known Codex and Claude shapes with Pydantic, and renders bounded transcript snapshots. runtime.py implements the service-owned SourceRuntimeAdapter port, models.py owns typed transcript line and entry models, reader.py owns JSONL file reading, entries.py owns line-to-entry normalization, rendering.py owns prompt-facing text and truncation, paths.py owns path resolution, and errors.py owns unavailable diagnostics. Ingest does not branch on source kind. integrations/sources/web/ uses httpx plus Beautiful Soup to fetch generic web URLs, remove non-readable HTML nodes, and render bounded HTML/text snapshots through the same source-runtime port. adapter.py implements the port, client.py owns streaming HTTP reads, models.py owns typed web response/document models, documents.py owns content-kind classification and HTML/text extraction, rendering.py owns prompt-facing metadata/content rendering, and errors.py owns unavailable diagnostics.

services/automation/ports.py owns SchedulerAdapter, the port used by local automation install/status/uninstall. services/automation/service.py is the install/status/uninstall facade. selection.py owns task defaulting and install-selection validation. definitions.py owns static task metadata. jobs.py owns ScheduledJob construction, including sync's stable claim owner, pending timeout, failed-attempt budget, launch PATH, plist path, interval, and working directory. The launchd implementation lives in integrations/automation/scheduler/ and only translates ScheduledJob models into plist files plus launchctl calls. It does not decide which jobs should exist or what sync and garden mean.

services/updates/ports.py owns package install metadata and package command execution ports. integrations/updates/ reads importlib.metadata install metadata and runs foreground package-manager commands. The service decides whether an install is safe to update; the integration only reports metadata and executes the chosen command.

services/setup/ports.py owns InstructionInstaller, the port used by setup and uninstall for global agent instruction artifacts. integrations/setup/ translates that port into filesystem edits for Codex and Claude: a managed codealmanac AGENTS block for Codex, and a ~/.claude/codealmanac.md guide plus CLAUDE.md import for Claude. The adapter removes only current setup-owned codealmanac artifacts; old almanac artifacts are outside Python v1 compatibility scope.

services/viewer owns the local browser payload for file references. Its file verb delegates to the index's mentions query and returns matching wiki pages. It must not read repo source contents directly or reuse source runtime adapters; selected source material belongs to lifecycle workflows, while viewer file routes are graph-navigation routes. services/viewer/renderer.py owns Markdown rendering and token-level wikilink rewriting for the local viewer; it rewrites text inline tokens and leaves code tokens untouched. services/viewer also owns local wiki scope for the viewer. Overview responses return the selected workspace plus available registered local workspaces, using workspace_id as the stable selector. The service skips unavailable registry entries without dropping them. The server and browser pass optional wiki selectors; they do not decide registry availability or workspace fallback. services/viewer/workspace_scope.py owns that selection and navigation ordering. services/viewer/projections.py owns viewer DTO conversion from index/workspace models. services/viewer/service.py should read as the use-case facade that calls the index, runs service, renderer, scope, and projection helpers.

server/assets/ owns the packaged browser shell for serve. It can borrow UseAlmanac's visual language, but it must preserve services/viewer as the product contract and keep local wiki navigation first: sidebar, overview, page, topic, search, file-reference graph routes, and read-only page viewing. Hosted UseAlmanac account, billing, settings, and hosted wiki-search flows do not belong in this local viewer.

server/app.py is the local viewer server composition root. It creates the FastAPI app and registers error handlers, viewer API routes, and static routes. server/api_routes.py owns HTTP request construction for services/viewer. server/static_routes.py owns browser-shell route registration. server/static_assets.py owns package asset validation and reads. server/errors.py owns CodeAlmanac/Pydantic exception mapping to HTTP JSON. No route body, package-resource read, or product-error mapping belongs back in server/app.py.

cli/render/root.py is a re-export facade over domain render modules so dispatch imports stay stable. cli/render/lifecycle.py owns build/ingest/garden/sync/job-start output. cli/render/wiki.py is a wiki-render facade only; search.py owns search/reindex output, pages.py owns show/page output, topics.py owns topic output, health.py owns health output, and tagging.py owns tag/untag output. workspaces.py owns local wiki registry list/drop output. cli/render/admin.py is an admin-render facade only; automation.py owns automation output, diagnostics.py owns doctor output, jobs.py owns jobs output, updates.py owns update output, and setup.py owns setup/uninstall output plus Rich-backed setup/uninstall terminal presentation. common.py owns shared formatting helpers. Render modules display service-returned facts only. Rich must not be imported from services, workflows, integrations, or stores.

cli/dispatch/wiki.py is the wiki-command facade. It owns direct routing for search, show, health, reindex, tag, and untag. cli/dispatch/topics.py owns topic subcommands, cli/dispatch/workspaces.py owns local registry list/drop commands, and cli/dispatch/serve.py owns viewer startup. Dispatch modules construct service request models and then call render modules; they do not contain service logic or presentation formatting.

cli/dispatch/admin.py is the admin-command facade. cli/dispatch/setup.py owns setup/uninstall request construction, diagnostics.py owns doctor request construction, updates.py owns update request construction, jobs.py owns jobs request construction, and automation.py owns automation request construction. Setup may reuse automation quiet-duration resolution; automation must not call setup.

cli/dispatch/lifecycle.py is the lifecycle-command facade. build.py owns init/build request construction, operations.py owns ingest/garden foreground/background dispatch, sync.py owns sync and sync status request construction plus transcript app parsing, and worker.py owns the hidden queue-drain entrypoint. Lifecycle dispatch modules adapt CLI args into workflow requests; lifecycle workflows own product behavior.

cli/parser/admin.py is the admin-parser facade. cli/parser/setup.py owns setup/uninstall flags, diagnostics.py owns doctor flags, updates.py owns update flags, jobs.py owns jobs flags, and automation.py owns automation flags plus task choices. Parser modules define CLI shape only; they do not import services beyond enum choices needed for argparse validation.

The browser shell stays static package data while it is small. app.js is the module entrypoint. server/assets/viewer/api.js owns HTTP calls, routes.js owns hash parsing and href construction, components.js owns shared DOM pieces, renderers.js owns screen assembly, and main.js wires browser events and the selected wiki switcher to those modules. Sidebar page/topic active state is browser route state over the existing hash routes, not a second server contract. The server owns validation and delivery of nested package assets through /assets/{path}.

manual/ is a support package rather than a product service. ManualLibrary loads bundled Markdown resources, installs missing files into <almanac-root>/manual/, and reports workspace manual completeness plus exact differences from bundled docs. app.py constructs it once and injects it into WikiService and DiagnosticsService. There is no public manual command in v1. Manual drift is diagnostic: doctor can ask the user to review changed files, but build/init do not overwrite local manual edits.

services/wiki/topic_models.py owns the parsed topics.yaml topic model and title defaults. topic_read.py owns the forgiving read path used by index refresh: invalid or missing topic files collapse to an empty topic set. topic_file.py owns the strict round-trip mutation path used by topic organization commands: it preserves comments, quotes, and line endings while writing through a temporary file. services/wiki/topics.py is only an import facade for existing callers.

First Slice Boundary#

The first Python implementation slice should prove:

  • package install metadata exists
  • codealmanac invokes a Python CLI
  • app.py wires a minimal application object
  • workspaces can resolve and initialize a local configurable Almanac root
  • tests run through public service/CLI entrypoints, not hidden helpers

This is intentionally smaller than the full product surface. It should create the spine that future slices extend without rework.