Archkeel checks architecture boundaries and declared changes in AI-assisted code.
1
stars
100
commits
Python
primary language
Sep 15, 2026
updated
The agent declares before it submits. The check is deterministic.
Archkeel checks architecture boundaries and declared changes in AI-assisted code. It compares an accepted commit with a candidate, checks their scans against the configured contract, and verifies that the candidate matches an expectation published before its first submission.
It catches two failure modes that finding-only diffs miss:
Try the demo · Onboard your project · How it works · Reference · Roadmap
Implemented and planned work is tracked in the roadmap.
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
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)
2 with a diagnostic. Unknown never becomes
green.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.
The HTML report is designed for a reviewer making a merge decision:
PASS, REJECT, or UNVERIFIABLE and one sentence explaining it are
visible before details, in the HTML report and in the terminal.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.
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 observes the only top-level package and writes archkeel.toml,
architecture-contract.json and docs/architecture/architecture.md. It proposes one
component per subpackage and forbids every component pair that is not imported today. Each rule
starts with a TODO: rationale, so validate lists every decision that remains, each with a
JSON Pointer. Give the prompt in docs/onboarding.md
to your coding agent, or work through the list yourself. The rule catalog is in
docs/rules.md.
To install it permanently instead, run pip install archkeel. Every command explains itself
with archkeel <command> --help.
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.
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.
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 | Meaning |
|---|---|
0 | Complete report or successful check |
1 | Rejected because at least one verdict is FAIL |
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.
gitGraph
commit id: "M · accepted"
commit id: "B · lock only"
branch candidate
commit id: "E · expectation only"
commit id: "H · implementation"
| Commit | Contract |
|---|---|
| M | Accepted state. Archkeel re-observes it. |
| B | Lock-only child of M and tip of the accepted branch. It binds the config, checker, and observation digests. |
| E | Child of B that changes only the expectation file. It must be published before the first submission of H. |
| H | Descendant of E. It must not modify the lock, config, architecture contract, or expectation. |
archkeel check. Fix the code or revise the proposal in a new protocol
cycle; do not rewrite protected inputs inside H.Fixture B writes its expectation after implementation by deriving it from the observed delta. Its architecture findings are otherwise clean. Archkeel still rejects it:
host_order: FAIL
expectation_fulfilled: FAIL
expectation was not published before the first candidate submission
Precommitment proves "published before submission." It does not prove that no private edit existed before publication.
In GitLab CI, Archkeel reads merge-request diff versions through glab to
establish publication order.
For local testing, replay captured host records:
uv run archkeel check ... --host-records records.json
A local replay validates the record shape and behavior. It does not prove host authenticity.
Run the complete project gate:
make check
This runs Ruff, strict mypy, pytest, and Archkeel's self-check.
Run the full release check, build both distributions, and install each one in isolation:
make release-check
Reproduce the protocol fixtures:
make fixtures
architecture-contract.json holds Archkeel to the rules it sells, and every rule was proven by a deliberate violation:
ir and check never import adapters or presentation; the CLI is
the composition root. The analyzer may import only archkeel.ir.model and archkeel.ir.codec.getattr, hasattr, cast, eval, exec, dynamic imports and
type: ignore are forbidden everywhere.packaging only in the analyzer runtime gate, rich only in the
terminal view, rich_argparse only in the CLI.make check reobserves the repository and compares it with
fixtures/D-self;
CI also runs archkeel validate and uploads the self-observation.
Archkeel is deliberately strict about what it can prove:
import pkg; pkg._member is not detected.accept is a placeholder and returns exit 2.init detects one top-level package; other layouts need --source and
--namespace. It cannot know why a boundary exists, so every rationale stays a decision.Completed work and the ordered UI, CI and release plan live in docs/roadmap.md. Items remain planned until their listed evidence exists.
MIT © 2026 Rapiddweller Asia Co., Ltd.
Maintained by Alexander Kell.
100 commits
Python
98.4%
CSS
1.3%
Archkeel checks architecture boundaries and declared changes in AI-assisted code.
1
stars
100
commits
Python
primary language
Sep 15, 2026
updated
The agent declares before it submits. The check is deterministic.
Archkeel checks architecture boundaries and declared changes in AI-assisted code. It compares an accepted commit with a candidate, checks their scans against the configured contract, and verifies that the candidate matches an expectation published before its first submission.
It catches two failure modes that finding-only diffs miss:
Try the demo · Onboard your project · How it works · Reference · Roadmap
Implemented and planned work is tracked in the roadmap.
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
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)
2 with a diagnostic. Unknown never becomes
green.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.
The HTML report is designed for a reviewer making a merge decision:
PASS, REJECT, or UNVERIFIABLE and one sentence explaining it are
visible before details, in the HTML report and in the terminal.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.
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 observes the only top-level package and writes archkeel.toml,
architecture-contract.json and docs/architecture/architecture.md. It proposes one
component per subpackage and forbids every component pair that is not imported today. Each rule
starts with a TODO: rationale, so validate lists every decision that remains, each with a
JSON Pointer. Give the prompt in docs/onboarding.md
to your coding agent, or work through the list yourself. The rule catalog is in
docs/rules.md.
To install it permanently instead, run pip install archkeel. Every command explains itself
with archkeel <command> --help.
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.
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.
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 | Meaning |
|---|---|
0 | Complete report or successful check |
1 | Rejected because at least one verdict is FAIL |
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.
gitGraph
commit id: "M · accepted"
commit id: "B · lock only"
branch candidate
commit id: "E · expectation only"
commit id: "H · implementation"
| Commit | Contract |
|---|---|
| M | Accepted state. Archkeel re-observes it. |
| B | Lock-only child of M and tip of the accepted branch. It binds the config, checker, and observation digests. |
| E | Child of B that changes only the expectation file. It must be published before the first submission of H. |
| H | Descendant of E. It must not modify the lock, config, architecture contract, or expectation. |
archkeel check. Fix the code or revise the proposal in a new protocol
cycle; do not rewrite protected inputs inside H.Fixture B writes its expectation after implementation by deriving it from the observed delta. Its architecture findings are otherwise clean. Archkeel still rejects it:
host_order: FAIL
expectation_fulfilled: FAIL
expectation was not published before the first candidate submission
Precommitment proves "published before submission." It does not prove that no private edit existed before publication.
In GitLab CI, Archkeel reads merge-request diff versions through glab to
establish publication order.
For local testing, replay captured host records:
uv run archkeel check ... --host-records records.json
A local replay validates the record shape and behavior. It does not prove host authenticity.
Run the complete project gate:
make check
This runs Ruff, strict mypy, pytest, and Archkeel's self-check.
Run the full release check, build both distributions, and install each one in isolation:
make release-check
Reproduce the protocol fixtures:
make fixtures
architecture-contract.json holds Archkeel to the rules it sells, and every rule was proven by a deliberate violation:
ir and check never import adapters or presentation; the CLI is
the composition root. The analyzer may import only archkeel.ir.model and archkeel.ir.codec.getattr, hasattr, cast, eval, exec, dynamic imports and
type: ignore are forbidden everywhere.packaging only in the analyzer runtime gate, rich only in the
terminal view, rich_argparse only in the CLI.make check reobserves the repository and compares it with
fixtures/D-self;
CI also runs archkeel validate and uploads the self-observation.
Archkeel is deliberately strict about what it can prove:
import pkg; pkg._member is not detected.accept is a placeholder and returns exit 2.init detects one top-level package; other layouts need --source and
--namespace. It cannot know why a boundary exists, so every rationale stays a decision.Completed work and the ordered UI, CI and release plan live in docs/roadmap.md. Items remain planned until their listed evidence exists.
MIT © 2026 Rapiddweller Asia Co., Ltd.
Maintained by Alexander Kell.
100 commits
Python
98.4%
CSS
1.3%