|
| 1 | +<!-- |
| 2 | +SPDX-License-Identifier: CC-BY-SA-4.0 |
| 3 | +SPDX-FileCopyrightText: 2025-2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk> |
| 4 | +--> |
| 5 | + |
| 6 | +[](https://github.com/sponsors/hyperpolymath) |
| 7 | +[](https://www.mozilla.org/MPL/2.0/) |
| 8 | + |
| 9 | +Lightweight proof-workspace manager for Agda — the language-agnostic |
| 10 | +engine half of **arghda**. Extracted from |
| 11 | +[`hyperpolymath/echo-types`](https://github.com/hyperpolymath/echo-types) |
| 12 | +to its own repository on 2026-05-30 (see echo-types#159 for the move |
| 13 | +record). |
| 14 | + |
| 15 | +arghda-core organises, lints, and reports on an Agda proof workspace; it |
| 16 | +**never proves anything itself** — Agda does. It manages a four-state |
| 17 | +triage workspace (inbox → working → proven → rejected) as file moves, |
| 18 | +runs a lint pack targeting the *silent-failure* class (cases where Agda |
| 19 | +appears to succeed but the file is not actually in the verified suite), |
| 20 | +builds a first-class import-graph DAG, and emits a JSON + event-stream |
| 21 | +contract a visual layer consumes. |
| 22 | + |
| 23 | +# Status |
| 24 | + |
| 25 | +v0.1 is feature-complete against `docs/arghda-spec.adoc`: all seven v0 |
| 26 | +Agda lint rules, the four-state workspace state machine, the |
| 27 | +import-graph `dag`, the `check` command (Agda typecheck + lint verdict), |
| 28 | +content-hash invalidation of `proven`, and `.arghda/config.toml` |
| 29 | +operator configuration. The live status of record is |
| 30 | +[`STATE.a2ml`](.machine_readable/6a2/STATE.a2ml). |
| 31 | + |
| 32 | +# Lint rules |
| 33 | + |
| 34 | +Hard-block (forbids the `working` `→` `proven` transition): |
| 35 | + |
| 36 | +- `missing-safe-pragma` — file lacks `{-#` `OPTIONS` `--safe` |
| 37 | + `--without-K` `#-}`. |
| 38 | + |
| 39 | +- `orphan-module` — `.agda` file unreachable from any CI root (roots are |
| 40 | + auto-discovered as `All.agda`/`Smoke.agda`, or passed via `--entry`; |
| 41 | + reachability is the *union*, so a module verified from any root is not |
| 42 | + an orphan). |
| 43 | + |
| 44 | +- `unjustified-postulate` — `postulate` without an adjacent `--` |
| 45 | + `JUSTIFY:` comment. |
| 46 | + |
| 47 | +Warn (surfaced, non-blocking): |
| 48 | + |
| 49 | +- `escape-hatch` — termination overrides (`TERMINATING`, |
| 50 | + `NON_TERMINATING`, `NO_TERMINATION_CHECK`) and trust primitives |
| 51 | + (`believe_me`/`primTrustMe`). |
| 52 | + |
| 53 | +- `tab-mix` — a tab in leading whitespace. |
| 54 | + |
| 55 | +- `unpinned-headline` — a top-level theorem whose name matches the |
| 56 | + headline pattern is not pinned in any `Smoke.agda` via a `using` `(` |
| 57 | + `…` `)` clause (the estate "every headline pinned in Smoke" |
| 58 | + discipline). The pattern is operator-configurable; its default |
| 59 | + `^[a-z][A-Za-z0-9-]*$` is deliberately broad, so operators narrow it |
| 60 | + to their own convention. Self-skips when no `Smoke.agda` is in scope. |
| 61 | + |
| 62 | +- `unused-import` — re-emits the findings of the external |
| 63 | + [`agda-unused`](https://github.com/msuperdock/agda-unused) tool. |
| 64 | + Opt-in behind `scan` `--unused`; invoked per file in local mode with |
| 65 | + `LC_ALL=C.UTF-8`; skipped with a note if the binary is not on `PATH`. |
| 66 | + |
| 67 | +# Commands |
| 68 | + |
| 69 | +The CLI is `arghda`. |
| 70 | + |
| 71 | +| Command | What it does | |
| 72 | +|----|----| |
| 73 | +| `init` | Create the four-state workspace layout at a path. | |
| 74 | +| `scan` | Lint every `.agda` file under a path. `--unused` adds the agda-unused pass; `--config`/`--headline-pattern` tune configuration. | |
| 75 | +| `check` | Run Agda on one file and lint it; combined verdict (degrades when `agda` is absent). | |
| 76 | +| `dag` | Emit the dependency-DAG JSON — nodes (lint status + declared `headlines`), import edges, and a blocked list. | |
| 77 | +| `claim` / `promote` / `reject` / `requeue` / `invalidate` | State-machine transitions; each is a file move logged to `.arghda/events.jsonl`. | |
| 78 | +| `events` | Replay the workspace event log. | |
| 79 | +| `stale` | List `proven` files whose content changed since promotion; `--invalidate` returns them to inbox. | |
| 80 | +| `watch` | Watch `inbox/` and `working/` and print events. | |
| 81 | + |
| 82 | +# Configuration |
| 83 | + |
| 84 | +`scan` and `dag` read `.arghda/config.toml` (from |
| 85 | +`<PATH>/.arghda/config.toml`, or an explicit `--config` `<file>`). |
| 86 | +Precedence is built-in default \< `config.toml` \< CLI flag. |
| 87 | + |
| 88 | +```toml |
| 89 | +[lint] |
| 90 | +headline_pattern = "^[a-z][A-Za-z0-9-]*$" |
| 91 | +``` |
| 92 | + |
| 93 | +# Build |
| 94 | + |
| 95 | +```sh |
| 96 | +just check # fmt-check + clippy (-D warnings) + build + test — the CI gate |
| 97 | +cargo build |
| 98 | +cargo test |
| 99 | +``` |
| 100 | + |
| 101 | +`agda` and `agda-unused` are optional: the `check` and `scan` `--unused` |
| 102 | +paths degrade gracefully (with a note) when the binary is absent, so the |
| 103 | +rest of the engine still works in an Agda-less environment. |
| 104 | + |
| 105 | +# Smoke against an Agda workspace |
| 106 | + |
| 107 | +```sh |
| 108 | +cargo run -- scan path/to/your/agda/sources |
| 109 | +``` |
| 110 | + |
| 111 | +Dogfooded against the echo-types corpus (193 modules): `dag` emits the |
| 112 | +903-edge import graph; multi-root discovery (`All.agda`, `Smoke.agda`, |
| 113 | +`Ordinal/Buchholz/Smoke.agda`, `characteristic/All.agda`, |
| 114 | +`examples/All.agda`) narrows orphan reports from 38 to the 17 genuine |
| 115 | +orphans. |
| 116 | + |
| 117 | +# Ecosystem |
| 118 | + |
| 119 | +Part of the [hyperpolymath ecosystem](https://github.com/hyperpolymath). |
| 120 | +arghda splits into `arghda-core` (this engine) and the planned |
| 121 | +`arghda-studio` / `arghda-panll` visual layers, which consume the `dag` |
| 122 | +JSON + `events.jsonl` contract. The motivating workspace was the |
| 123 | +echo-types proof pipeline, but arghda-core has no echo-types-specific |
| 124 | +code. |
| 125 | + |
| 126 | +Adjacent projects: |
| 127 | + |
| 128 | +- [echo-types](https://github.com/hyperpolymath/echo-types) — the Agda |
| 129 | + library that motivated arghda’s design (not a build dependency). |
| 130 | + |
| 131 | +- [absolute-zero](https://github.com/hyperpolymath/absolute-zero) — a |
| 132 | + sister Agda library under the same `--safe` `--without-K` discipline. |
| 133 | + |
| 134 | +# Machine-readable |
| 135 | + |
| 136 | +Per the Rhodium Standard, structured project metadata lives under |
| 137 | +[`.machine_readable/6a2/`](.machine_readable/6a2/) — `STATE`, `META`, |
| 138 | +`ECOSYSTEM`, `AGENTIC`, `NEUROSYM`, `PLAYBOOK` in A2ML — with |
| 139 | +[`0-AI-MANIFEST.a2ml`](0-AI-MANIFEST.a2ml) as the AI entry point and |
| 140 | +[`EXPLAINME.adoc`](EXPLAINME.adoc) as the orientation pointer. |
| 141 | + |
| 142 | +# Licence |
| 143 | + |
| 144 | +Code, configuration and scripts are `MPL-2.0` (see [LICENSE](LICENSE)); |
| 145 | +prose documentation is `CC-BY-SA-4.0`, per the [hyperpolymath licence |
| 146 | +policy](https://github.com/hyperpolymath/standards) (Rule 1). This split |
| 147 | +is a **formal invariant**: every tracked file carries the appropriate |
| 148 | +SPDX header, checked by `scripts/check-spdx.sh` in `just` `check` and in |
| 149 | +CI. Third-party, generated, and test-data files are explicitly excluded |
| 150 | +([`licensing-policy.toml`](.machine_readable/licensing-policy.toml)) and |
| 151 | +are never relicensed — vendoring third-party source in-tree fails the |
| 152 | +check until it is listed as excluded with its original SPDX preserved. |
0 commit comments