Skip to content

Commit 2a8d9e8

Browse files
committed
Add ArghDA MVP design spec
Parks the proof-workspace-manager design in-tree until ArghDA gets its own repo. Covers: core/panll split, four-state triage file-move machine (inbox/working/proven/rejected), Agda-first linter rules targeting the silent-failure class, DAG JSON schema, first-sprint ordering, and acceptance criteria against echo-types itself as the seed workspace. https://claude.ai/code/session_01DDGQFBtAT9JdRwmu25QzC2
1 parent 093c0b5 commit 2a8d9e8

1 file changed

Lines changed: 218 additions & 0 deletions

File tree

docs/arghda-spec.adoc

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
= ArghDA MVP Specification
2+
:toc: macro
3+
:toclevels: 2
4+
5+
toc::[]
6+
7+
== Purpose
8+
9+
ArghDA is a lightweight proof-workspace manager. Initial target:
10+
Agda. It exists to close a specific user-experience gap that
11+
`agda-mode` and `agda-language-server` do not: making the
12+
_lifecycle_ of a proof (inbox → working → proven | rejected) a
13+
first-class, visible object, with a thin linter that catches the
14+
silent-failure class _before_ the typechecker.
15+
16+
Scope discipline: small tool, narrow job. Not a proof assistant,
17+
not a replacement for `agda-mode`, not a tactic framework.
18+
19+
== Motivation
20+
21+
From echo-types sessions (see `docs/buchholz-plan.adoc`), the
22+
operator repeatedly hit a specific failure mode:
23+
24+
* a proof file exists, looks like it typechecked, but is missing
25+
`--safe` / `--without-K`, or is not imported from `All.agda`,
26+
or is not pinned in `Smoke.agda`
27+
* the "did it really work?" question is answered only by running
28+
the whole suite, which is slow and emotionally punishing
29+
* there is no visible pipeline: no inbox, no "in-progress", no
30+
"proven" tag; just a flat directory of `.agda` files
31+
32+
The existing ecosystem (see ArghDA scout report: `coq-lsp`,
33+
`VSCoq`, `Alectryon`, `LeanInk`, `Mathlib stats`, `Isabelle/jEdit
34+
PIDE`, `agda-language-server`, `agda-unused`) gives excellent
35+
per-file UX and some library-level dependency views, but nobody
36+
treats proofs as _items flowing through pipeline stages_. That is
37+
the novel contribution.
38+
39+
== Split
40+
41+
Two layers, developed and published as two packages:
42+
43+
`arghda-core`:: Language-agnostic engine. Rust. No UI. Exposes a
44+
CLI, a library API, and a JSON event stream. Runs in CI, standalone,
45+
or embedded.
46+
47+
`arghda-panll`:: PanLL presentation layer. Gossamer webview +
48+
ReScript panel consuming `arghda-core`'s event stream. Lives in the
49+
PanLL surface, probably adjacent to Pane-W.
50+
51+
Rationale for the split:
52+
53+
* Proof triage is useful outside PanLL (students, CI, large collabs).
54+
* Testing `arghda-core` doesn't require booting PanLL.
55+
* PanLL gains a polished dependency rather than an internal subsystem.
56+
* Matches PanLL's existing pattern with `panll_beam`.
57+
58+
== State machine (core)
59+
60+
Four states, implemented as four directories under a workspace root:
61+
62+
[source]
63+
----
64+
<workspace>/
65+
inbox/ -- new or returning proof files
66+
working/ -- operator has picked this up
67+
proven/ -- typecheck + lint clean
68+
rejected/ -- typecheck failed, or lint-block, or manual reject
69+
----
70+
71+
Transitions are _file moves_. This keeps the ground truth on disk,
72+
makes recovery obvious (`mv` it back), and survives ArghDA crashing.
73+
74+
Transition rules:
75+
76+
[cols="1,2,3"]
77+
|===
78+
| From | To | Trigger
79+
80+
| inbox | working | operator claim (explicit)
81+
| working | proven | typecheck clean AND lint clean
82+
| working | rejected | typecheck failed OR lint-hard-block
83+
| rejected | inbox | manual re-queue
84+
| proven | inbox | upstream change invalidates (see DAG below)
85+
|===
86+
87+
Every transition emits a JSON event to a rolling log under
88+
`<workspace>/.arghda/events.jsonl`.
89+
90+
== Linter rules (Agda, v0)
91+
92+
The linter targets the _silent-failure_ class — cases where Agda
93+
appears to succeed but the file is not actually in the verified
94+
suite. Rules, each emitting a structured diagnostic:
95+
96+
* `missing-safe-pragma` — file lacks `{-# OPTIONS --safe --without-K #-}`
97+
* `missing-without-k` — `--safe` without `--without-K` (or vice versa)
98+
* `orphan-module` — module not imported (transitively) from `All.agda`
99+
* `unpinned-headline` — module exports a theorem matching a
100+
headline-naming pattern (operator-configurable regex) but the name
101+
is not pinned in `Smoke.agda` via a `using` clause
102+
* `unjustified-postulate` — `postulate` block without an adjacent
103+
comment line starting with `-- JUSTIFY:`
104+
* `unused-import` — shell out to `agda-unused` and re-emit findings
105+
* `tab-mix` — file mixes tabs and spaces at line start
106+
107+
Severity: `hard-block` (forbids `working → proven`) vs `warn`
108+
(surfaced but non-blocking). Default: `missing-safe-pragma`,
109+
`orphan-module`, `unjustified-postulate` are hard-block; the rest
110+
are warn.
111+
112+
Non-goals for v0: semantic linting, redundancy detection,
113+
style-guide enforcement beyond whitespace.
114+
115+
== DAG JSON schema (v0)
116+
117+
Emitted by `arghda-core dag` as a single JSON document. Consumable by
118+
the PanLL panel, a static HTML viewer, or third-party tools.
119+
120+
[source,json]
121+
----
122+
{
123+
"version": "0.1",
124+
"workspace": "/path/to/workspace",
125+
"generated_at": "ISO-8601",
126+
"nodes": [
127+
{
128+
"id": "Ordinal.Closure",
129+
"file": "proofs/agda/Ordinal/Closure.agda",
130+
"status": "proven",
131+
"lint": { "hard_block": [], "warn": [] },
132+
"headlines": ["C-monotone"]
133+
}
134+
],
135+
"edges": [
136+
{ "from": "Ordinal.Closure", "to": "Ordinal.Base", "kind": "imports" }
137+
],
138+
"blocked": [
139+
{
140+
"node": "Ordinal.Buchholz.Psi",
141+
"blocked_by": ["Ordinal.Buchholz.Closure"],
142+
"reason": "prereq not proven"
143+
}
144+
]
145+
}
146+
----
147+
148+
Renderer is a separate concern; the panel consumes this verbatim.
149+
150+
== Integration points
151+
152+
* **LSP** (coq-lsp pattern) — per-file editor feedback.
153+
* **PIDE async model** (Isabelle) — asynchronous document state
154+
for the "checking now" sub-state within `working`.
155+
* **`agda --only-scope-checking`** — cheap pre-pass for quick
156+
syntactic / import validation before full typecheck.
157+
* **`agda-unused`** — shell out; re-emit diagnostics in the
158+
ArghDA diagnostic format.
159+
* **Alectryon-style rendering** (future) — static HTML render of
160+
proof states as a by-product of `proven` transition.
161+
162+
== Non-goals (MVP)
163+
164+
* Tactic language.
165+
* Writing proofs for the operator.
166+
* IDE replacement.
167+
* Multi-prover support (Coq/Lean/Isabelle postponed to v0.2).
168+
* Distributed execution. A single workspace, a single machine.
169+
170+
== First sprint (v0.1)
171+
172+
Ordered by unblock-value:
173+
174+
. `arghda-core` Rust crate skeleton; `Workspace::open(path)` API.
175+
. Filesystem watcher over `inbox/` and `working/` using `notify`.
176+
. Two linter rules: `missing-safe-pragma`, `orphan-module`.
177+
. `arghda-core check <file>` command: runs agda, captures exit code,
178+
emits a `LintReport` JSON.
179+
. `arghda-core promote <file>` / `arghda-core reject <file>`:
180+
the state-machine CLI.
181+
. `arghda-core dag` emitting the schema above for the current
182+
workspace.
183+
. Minimal end-to-end smoke test over `echo-types` itself as the
184+
first real workspace.
185+
186+
PanLL panel (`arghda-panll`) is v0.2 and deliberately not in this
187+
sprint. The CLI + JSON is the interface contract; the panel is a
188+
consumer.
189+
190+
== Open questions
191+
192+
* Language choice for `arghda-core`: Rust (matches PanLL stack) vs
193+
Zig (matches Gossamer / Burble). Default: Rust, pending operator
194+
pushback.
195+
* Should the `proven` state record a content hash so upstream
196+
changes can invalidate it automatically? Default: yes, SHA-256
197+
of the file plus imports.
198+
* Naming convention for "headlines" — regex default
199+
`^[a-z][A-Za-z0-9-]*$` with export-only filter, operator-overridable
200+
per-workspace in `.arghda/config.toml`.
201+
202+
== Acceptance criteria for v0.1
203+
204+
* End-to-end demo: take `echo-types` at its current commit, place
205+
all `*.agda` files into `inbox/`, run `arghda-core` against it,
206+
observe that every file moves to `proven/` with no hard-blocks,
207+
and that the emitted DAG matches the actual import graph.
208+
* Introduce one synthetic bug (delete `--safe` from a file): observe
209+
that file lands in `rejected/` with the correct diagnostic.
210+
* Introduce one synthetic orphan (new `.agda` not imported from
211+
`All.agda`): observe `orphan-module` hard-block.
212+
213+
== References
214+
215+
* ArghDA scout report (this session): existing proof-workspace
216+
landscape across Coq/Rocq, Lean 4, Isabelle, Agda.
217+
* `docs/buchholz-plan.adoc` — the motivating proof pipeline that
218+
surfaced the silent-failure gap.

0 commit comments

Comments
 (0)