Skip to main content

Archkeel

The agent declares before it submits. The check is deterministic.

Archkeel architecture gate and keel

Python 3.11+ CI License: MIT PyPI version

Archkeel checks architecture boundaries, scan coverage, and declared changes in AI-assisted code. It compares an accepted commit with a candidate, then checks the candidate against the configured contract and an expectation published before its first submission. Start with archkeel report; use archkeel check to gate a candidate. See the reference for the protocol and command details.

It catches two failure modes that finding-only diffs miss:

  • the architecture changed without being declared;
  • the scanner saw less of the program, so the result looks clean only because the graph became blinder.

Shop tour focused on app: five components and the json library, seven dashed red connections, eight broken edge rules, and explicit shown-versus-total counts

Negative case: shop.store.repository imports Money, breaking DEP-STORE-NO-MONEY. Filters change the view, not verdicts or evidence. Select a connection to inspect its rule evidence.

Unassigned modules remain visible in a navigation-only group. The group creates no boundary or verdict.

Archkeel rejects Fixture A in the terminal because calls_unresolved rose from 0 to 1

Try the demo · Onboard your project · How it works · Reference · Roadmap

Implemented and planned work is tracked in the roadmap.

What it does, on one sample

The examples use fixtures/F-architecture, a shop with five components. make demo-onboarding replays command implementations against committed decision fixtures; no live approvals occur.

1. The agent drafts; interview decisions come from the architect

archkeel init observes packages and imports, drafts one component per subpackage with its public interface, and leaves dependency pairs open. validate refuses the draft. The replay then checks a committed, architect-attributed fixture contract; it does not record a live approval.

decided_by records who authored a rule, requires entry, or component public list. An agent-authored decision is not evidence of human approval.

Seven-step interview-mode replay across agent, Archkeel and architect lanes. The agent drafts five components with twenty open decisions; Archkeel refuses; a pre-approved top-level decision fixture passes validation; the report flags store; the agent drafts a nested target; an architect-attributed fixture maps sqlite to api and records three requires entries; Archkeel catches a later crossing. No live approvals occur.

Generated from replay results. The committed fixtures stand in for earlier interview answers.

2. A component that outgrows its level gets one of its own

The report flags store: 7 modules at a level with 5 components. This is a review prompt, not an automatic split. init --source shop/store --namespace shop.store --force drafts four nested components and 12 open pairs. The replayed fixture maps shop.store.sqlite to api and records three requires entries. complete_requires makes every other pair forbidden; these are fixture decisions, not live approvals.

Clean shop sample: five components and the json library, seven connections, all twelve modules, with the heaviest connections listed beside the graph

The diagram shows five components and their scoped json dependency. Teal means the displayed imports were checked, with no violation or relevant UNKNOWN. store has 7 modules at a level with 5 components.

The store component opened into its declared inside: api, repository, codec and backend, plus shop.store, the module no sub-component owns

The declared store level contains four components and the unassigned shop.store module.

3. Validation names the boundary, the level and the fix

This fixture removes a required dependency. Validation exits 2 with three rule.violated diagnostics: repository imports backend without requiring it. The aggregate is NOT CHECKED; exact import locations are in the HTML evidence.

archkeel validate reporting three rule.violated diagnostics under store:STORE-REQUIRES-COMPLETE, each naming that repository imports backend without requiring it

What each side gets out of it

For the architect A target contract, measured against the code, with decision authorship recorded.
For the agent A boundary to read before editing and diagnostics that name the rule, level, and importer.
For review Checks for undeclared changes and reduced scan coverage, beyond finding diffs.

Why Archkeel

An agent can keep tests green and introduce no new architecture finding while making the code harder to analyze. If the gate compares finding identities only, that change passes.

Fixture A is the smallest example:

 def run(key: str) -> int:
-    return first() + second()
+    handlers = {"first": first, "second": second}
+    return handlers[key]()

The refactor introduces no new forbidden import, cycle, or private crossing. But static call resolution gets worse:

Observation Accepted Candidate
Resolved calls 2 of 2 0 of 1
Unresolved calls 0 1
New finding fingerprints 0 0
Archkeel verdict baseline FAIL
expectation_fulfilled: FAIL
regression check failed in calls_unresolved: 0->1
regression check failed in unresolved_ratio: 0/2->1/1

The same result names the call behind the count in unresolved_call_changes: handlers[key] in sample.work.run at sample/work.py:9, reason expression is dynamic (AD-100).

Archkeel compares raw measurements as well as finding counts and fingerprints. The ratio check uses integer cross-multiplication, never rounded percentages:

U_candidate × T_accepted <= U_accepted × T_candidate   (when both T > 0)

What the gate adds

  • Precommitment with evidence. The agent publishes the intended change before it submits the candidate. Git ancestry and trusted host records prove the order; author timestamps do not.
  • Coverage-aware regression checks. A disappearing edge is not mistaken for an improvement just because a finding disappeared with it.
  • Explicit uncertainty. An incomplete scan, broken lock, empty scope, or runtime mismatch returns exit 2 with a diagnostic. A complete report may instead exit 0 with declared_rules: UNKNOWN when a rule names positions it could not decide. Neither is PASS.
  • Verdict is not coverage. PASS requires complete evaluator evidence for every required rule and no violation among decided positions. Missing scope proof stays UNKNOWN.

Archkeel complements tests, linters, and human review. It does not replace any of them. Its job is narrower: keep architecture changes declared, observable, and mechanically checkable.

Review surface

Shop tour report: a completed scan, failed rules and independent evidence verdicts

The HTML report keeps verdicts and evidence separate:

archkeel report --only violations
archkeel report --only violations --rule DEP-STORE-NO-MONEY
archkeel report --only violations --component store
archkeel report --only calls --component store   # unresolved and partial calls (AD-100)
archkeel report --baseline known-violations.json # read-only fingerprint comparison
  • Separate verdicts. Scan completeness, declared rules, candidate expectations, Git order, and publication order stay separate. PASS, FAIL, UNKNOWN, and NOT CHECKED keep distinct meanings.
  • Evidence stays inspectable. Counts, fingerprints, source locations, digests, and runtime provenance remain available. Each rule shows its result, reason, owner, and source; permissions are declarations, not proof of a passed check.
  • Baselines track findings. Known findings keep a FAIL marker. New fingerprints are reported separately; missing rules or undecided evidence cannot make findings appear resolved.
  • The report stays navigable. Filters do not change verdicts or evidence. The diagram, Actual, Target, and Diff views retain observed imports, declarations, and UNKNOWN evidence as distinct data. Folders are navigation, not contracts; Structure and Review work by keyboard.
  • Claims do not gate. report and validate show five review claims, but they do not affect exit codes. Their limits and measured facade details are in the reference.

UNKNOWN filter keeps a failed boundary-type rule visible with one violation, one undecided position and its scope, rationale and evidence

A rule can fail and still contain undecided evidence. The UNKNOWN filter keeps that mixed result visible without relabelling its confirmed violation as uncertainty.

Try the demo

The demo builds three small Git repositories and runs the real checks. Fixture A is rejected because the call graph got blinder, Fixture B because its expectation was published too late, and Fixture C passes.

git clone https://github.com/rapiddweller/archkeel.git
cd archkeel
make demo

make demo-screenshots OUTPUT=<directory> also captures each HTML report as PNG and each terminal view as SVG.

make demo-onboarding replays the loop from What it does, on one sample on the two-level shop: what init drafts, what validate refuses, what the architect decides, and what the gate says when an agent crosses a boundary inside the level.

Replay a report-capable catalog row to a new JSON report and HTML sidecar:

make demo-architecture VARIANT=class-a-recursive-wide-package OUTPUT=demo-output/architecture.json
make demo-onboarding
archkeel validate --root fixtures/F-architecture   # exit 0, both levels
archkeel report   --root fixtures/F-architecture   # UNKNOWN: store:STORE-REQUIRES-COMPLETE
archkeel validate --root fixtures/F-architecture --config archkeel-tests.toml   # its tests' own scope

Every violation the tool can find has a catalogued variant that produces it, listed in docs/architecture-demo.md.

Onboard your project

Requirements: Python 3.11+ and a Git repository with at least one commit.

uvx archkeel skill install claude    # or codex
uvx archkeel init
uvx archkeel validate

init drafts one component per subpackage, proposes public interfaces, and leaves dependency decisions open. The contract describes the target architecture; it does not copy the current code. Choose interview mode to keep decisions with a human, or explicitly delegate them to auto mode. decided_by records authorship; it does not prove human approval. The installed skill reviews physical packages recursively, but a green rule does not certify the whole design.

The report separates observed modules (Actual), declared architecture (Target), and their differences (Diff). Optional module responsibilities and physical package groups aid navigation; they do not establish ownership. Select once for Details; use Enter, double-click, or Open selected to explore. See the onboarding guide for the agent workflow and architecture rules for rule semantics.

Target view drilled into a declared Python module and its responsibility

archkeel validate --write-graph can update a marked component graph after contract edits.

To track existing violations, write a baseline and gate on changes:

archkeel validate --baseline known-violations.json --write-baseline   # initial file, then review it
archkeel validate --baseline known-violations.json                    # in CI
archkeel validate --baseline known-violations.json --write-baseline   # resolved-only cleanup
archkeel validate --baseline known-violations.json --write-baseline --accept-new  # deliberate widening
archkeel validate --root mobile --baseline known-violations.json      # reads mobile/known-violations.json

An existing baseline rejects new or increased violations unless --accept-new is explicit. Fingerprints use rules and subjects, not source lines. The target-first guide covers baseline maintenance; the reference documents measurement budgets, historical comparisons, and baseline schemas.

To install it permanently instead, run pip install archkeel. Every command explains itself with archkeel <command> --help.

Quickstart

Observe the current repository:

archkeel report

The command writes the canonical architecture.json and a self-contained architecture.report.html beside it. A terminal shows the decision and verdicts; pipes and --json receive the JSON result.

The report separates observed imports (Diagram and Actual), declared architecture (Target), and their differences (Diff). Target edges are declarations, not observed imports or execution order. Selecting an item shows its declared responsibility only when its identity can be matched; this does not describe observed behavior. Details keeps declarations and unresolved placement evidence available. Use Enter, double-click, or Open selected to explore one level. Folders are navigation, not ownership. Report controls and layout warnings do not change architecture verdicts; see known limits for details.

Checked on DATAMIMIC CE

The 1 October 2026 CE experiment (b38899c9 plus local changes) parsed 491 modules and recorded 102 violations and 155 counted UNKNOWN positions. It shows that a nested repository can be scanned and explored. It does not prove CE is finished, every boundary is well designed, or runtime behavior matches imports. UNKNOWN remains unresolved work.

--output X.json writes the JSON report and X.report.html.

Target view for the Shop demo: declared store hierarchy, physical placement and the selected responsibility

Target component without a responsibility remains visible in the diagram and index

Check a candidate against its published expectation:

archkeel check \
  --root /repo \
  --baseline "$B" \
  --expectation-commit "$E" \
  --head "$H" \
  --expected expectation.json \
  --expected-digest "$DIGEST" \
  --accepted-branch main \
  --branch candidate

Run the Python version required by the repository being scanned. A mismatch is reported as runtime_mismatch with exit 2, not as broken source code.

How it works

flowchart TB
    B["Locked accepted state"] --> O["Observe accepted + candidate"]
    E["Expectation published first"] --> H["Candidate submitted"]
    H --> O
    O --> C{"Deterministic check"}
    C --> P["0 · pass"]
    C --> R["1 · reject"]
    C --> U["2 · unverifiable"]

    classDef locked fill:#141414,stroke:#C5F82A,color:#E8E8E2
    classDef declared fill:#141414,stroke:#5EEAD4,color:#E8E8E2
    classDef candidate fill:#141414,stroke:#8A8A84,color:#E8E8E2
    classDef gate fill:#C5F82A,stroke:#C5F82A,color:#0D1F05
    classDef result fill:#141414,stroke:#2A2A28,color:#E8E8E2

    class B locked
    class E declared
    class H,O candidate
    class C gate
    class P,R,U result

A check answers three independent questions. It never compresses them into a single score.

Verdict Question Typical failure
observation_complete Did the scan see everything it claims to see? Incomplete scan, empty scope, rule without subjects
declared_rules Does the code obey the architecture contract? Forbidden import between components
expectation_fulfilled Did the candidate match the declaration without regressions? Coverage regression, undeclared change, late expectation

Exit codes

Exit Meaning
0 Complete report or successful check
1 Rejected because at least one verdict is FAIL, or validate --baseline found a violation the baseline does not state, or one it states that nobody violates any more
2 Unverifiable input, always with at least one diagnostic

Every exit 2 diagnostic contains:

kind · subject · unknown_claim · remedy

A broken lock is therefore not interpreted as an empty accepted state.

The M → B → E → H protocol

Start at accepted commit M. Lock commit B binds the config, checker, and observation. Expectation commit E follows B and must be published before the first submission of candidate H. H must preserve the lock, config, contract, and expectation. selected_changes: [] rejects every observed semantic change. A non-empty list checks named changes plus fixed guardrails; other undeclared changes can pass. With trusted host records, the check verifies publication order, not private editing history. A local replay checks supplied records; it does not authenticate them. See the reference for commit rules and host evidence.

Development

Run the complete project gate, including Archkeel's own contract validation:

make gate

This runs the locked release checks (Ruff, strict mypy, pytest, builds and smoke tests) and then archkeel validate --root . --baseline architecture-baseline.json --json. CI uses this same Make entry point. make check remains the faster source, type and test loop.

CI also runs the interactive report checks with pinned Playwright and Chromium:

make browser-install
make report-browser OUTPUT=test-artifacts/report-browser

The browser lane uses only synthetic demo fixtures and checks deep/wide navigation, filters, status labels, narrow screens and the no-JavaScript fallback. Screenshots and traces are written under the chosen output directory; use a new directory for each run.

A change to Python code under src/ or to an architecture contract moves the saved self-observation that check compares against; regenerate it with make self-observation. The contributing guide lists what a pull request needs.

Run the full release check, build both distributions, and install each one in isolation:

make release-check

Reproduce the protocol fixtures:

make fixtures

Archkeel checks itself

architecture-contract.json holds Archkeel to its own rules. Seven components cover all 42 ordered pairs; all 19 rules have rationales, and deliberate violations exercise them. The deterministic core has no adapter or presentation imports. The nested check component has its own contract. See the architecture guide for the declared boundaries.

make check compares a fresh observation with fixtures/D-self, including the canonical model, provenance digests, and verdicts. make gate also validates the contract.

Current boundaries

Archkeel observes static source within the configured roots and namespace. It does not observe runtime behavior, data flow, or performance. Incomplete evidence remains UNKNOWN, and a green scan does not establish that every design choice is sound. A separate test tree needs its own scan and contract. Archkeel's Python version must meet the target repository's requirement.

Baselines are compared, not authenticated; validate --against checks contract widening when a reviewer or CI runs it. See known limits and the reference for details.

Roadmap

Completed work and the ordered UI, CI and release plan live in docs/roadmap.md. Items remain planned until their listed evidence exists.

License

MIT © 2026 Rapiddweller Asia Co., Ltd.

Maintained by Alexander Kell.

Metadata

Release files for archkeel 0.8.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for archkeel 0.8.3
File Size Uploaded
archkeel-0.8.3.tar.gz 362.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for archkeel 0.8.3
File Interpreter ABI Platform
archkeel-0.8.3-py3-none-any.whl Python 3 none any Details

Total release size: 768.8 kB

Release files / archkeel-0.8.3.tar.gz

Download URL archkeel-0.8.3.tar.gz
Size 362.7 kB
Tags Source
SHA-256 checksum
How to use checksums
79cb0fc2407a100ade6e7e8aba6dfd94f76d89d9c5b95585701491badb299364
BLAKE2b-256 checksum
How to use checksums
e987a78898501462a3c384bf780b60dd77e46ebd99f32732196f457a06ae5ba5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release files / archkeel-0.8.3-py3-none-any.whl

Download URL archkeel-0.8.3-py3-none-any.whl
Size 406.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9fa339b7b9596f11dec9c76cb35b8ec9c99b646b186afe0d673ccbce6f6019d1
BLAKE2b-256 checksum
How to use checksums
311d31c664459f976414ad5fd44a6e6cafc4e92c6c5aee6693d676f3069029bb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release history Release notifications | RSS feed

2.0.0

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.0

2 release files

0.9.0

2 release files

0.8.6

2 release files

0.8.5

2 release files

0.8.4

2 release files

This release

0.8.3 This release

2 release files

0.8.2

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page