Skip to content

Components

EstimateKit is a hexagonal FastAPI + CLI engine (domain / application / adapters) with a React SPA, packaged as one container. This is the part-by-part map; what it produces and why is in the overview.

Part Lives in Role
Domain engine/domain Entities and value objects (Project, ProjectSlug, AuditReport), domain services (scheduling), constants, exceptions. No outward imports.
Application engine/application Inbound/outbound ports and the use cases that orchestrate them: discover, scaffold a project or mint a phase in one, project lifecycle (rename, archive, unarchive, delete, summary), generate roadmap/excels/slides/all (the deck for one named phase), audit, file I/O, architecture diagrams (list, render), outputs, import/export.
HTTP adapter engine/adapters/inbound/http FastAPI app factory and routers (system, guide, projects, generation, files, audit, slides, architecture), DTOs and mappers, under /api. Serves the built SPA at /.
MCP adapter engine/adapters/inbound/mcp The same use cases as tools at /mcp, plus the estimate-project prompt; see The MCP door.
CLI adapter engine/adapters/inbound/cli An argparse command registry over the same use cases.
Persistence engine/adapters/outbound/persistence File-backed project and template repositories — projects live on the /data volume.
Generators engine/adapters/outbound/{roadmap,excel,slidev,plantuml} Render domain entities into a TSV roadmap, .xlsx workbooks (openpyxl), a Slidev deck, and the per-phase PlantUML architecture diagrams.
Auditor adapter engine/adapters/outbound/auditor Runs the standalone auditor/ pytest suite over a project and collects structured results.
Events adapter engine/adapters/outbound/events Publishes the engine’s facts to the platform event bus where the instance holds a grant; see Events.
Composition root engine/container.py Wires the adapters into the use cases (the single place all layers meet).
Web web/ React + Vite + Tailwind SPA (MVVM); served as static files by the backend in the built image.
flowchart LR
    data["project folder<br/>(YAML + TSV)"] --> audit{"auditor:<br/>data valid?"}
    audit -- "errors" --> fix["left for the author to fix"]
    audit -- "clean" --> engine["engine use cases"]
    engine --> roadmap["roadmap (TSV)"]
    engine --> excel["Excel workbooks"]
    engine --> slides["Slidev deck"]

The auditor runs first and reports hard errors; on top of those it raises risk suggestions (e.g. zero test lag, milestones bunched into one month) drawn from delivery experience. Generation reads validated domain entities, never raw files.

The MCP server mounts at /mcp on the same FastAPI app (streamable HTTP), one tool per use case, so an agent runs the whole estimation loop the SPA and CLI run:

Tool Does
list_projects / get_project_summary The project list, one project’s headline facts.
scaffold_project A complete example project whose sentinel-marked values the audit refuses until authored.
scaffold_phase A new phase in an existing project: its phase-table row, its matrix column and its whole sentinel-marked file set (activities, architecture, executive summary, notes), so a minted phase audits red until authored.
delete_project / rename_project / archive_project / unarchive_project The project lifecycle.
list_project_files / read_project_file / write_project_file / delete_project_file The data files (YAML + TSV) an authoring agent edits.
append_project_rows / replace_project_row Row-level writes to a TSV data file: append rows, or replace the one row whose ID column holds a given ID, each row checked against the header row, so a follow-on never re-sends the rows it must not touch.
append_project_text Append text after the last line of a non-TSV data file, such as a new phase’s ask onto the customer-requirements text, leaving every earlier byte as it was; a TSV is refused, since its rows go through append_project_rows.
list_architecture_diagrams / render_architecture_diagram The per-phase PlantUML architecture diagrams.
run_audit Audit the whole project, or a selected phase plus shared integrity; the report names its scope and carries advisory project-wide risk suggestions.
generate_artifacts / list_output_files Trigger the roadmap, workbooks, and the deck for one named phase (a multi-phase project refuses slides without it); enumerate what generation produced.
get_guide The authoring guide the estimation loop follows.

The estimate-project prompt publishes the whole loop (scaffold, author from the brief, audit until clean, generate) as a runbook the connected agent executes. Generated artifacts stay on the instance: workbooks download over the HTTP API (/api/projects/<slug>/outputs/<path>) and the deck reviews at /projects/<slug>/slides/. The deck sells one phase: its executive summary, speaker notes, architecture slide and per-phase sections are that phase’s own (slides/P<n>-executive-summary.tsv, slides/P<n>-notes.tsv, architecture/P<n>-components.tsv, the guidelines whose Phase IDs name it), with the cross-phase slides as context; regenerating for another phase replaces it at the same URL.

The pre and prod deployments run ESTIMATEKIT_AUTH_MODE=entra: people sign in through Microsoft Entra ID with authorization-code + PKCE, and the app holds a signed session cookie. The id_token’s RS256 signature, issuer, audience, nonce, tenant and expiry are verified before the session opens. Fail-closed HTTP Basic narrows to the /mcp machine door, which cannot complete an interactive login. Dev runs ESTIMATEKIT_AUTH_MODE=basic temporarily, because its Entra app registration does not exist yet, so the same Basic pair gates every surface there until the registration lands. An entra deployment stood up without the Entra block answers 503, never open, so a misconfigured deploy refuses to serve rather than leak estimation data. The gate lives in engine/adapters/inbound/http/auth.py. /ping and /api/health stay exempt for probes; the readiness check also writes and removes one file under the projects root, so a green probe proves the volume is writable. The session secret, Entra client secret and MCP Basic pair come from Vault through an ExternalSecret; ESTIMATEKIT_AUTH_DISABLED=true is the explicit local-dev opt-out. Either way the hostname is safe to publish.

Packaged by the root Dockerfile and deployed by the CEP pipeline (cicd/cicd.yaml) from the k8s/helm chart and the terraform/ root. The app serves at estimatekit-<env>.<zone>; the CEP service identity is asdlc-estimatekit. This docs site ships as its own deployable (docs/cicd/cicd.yaml, chart estimatekit-docs) at estimatekit-docs-<env>.<zone>.