Skip to content

Commit 29f78f1

Browse files
committed
docs: substantive CRG C annotation (EXPLAINME.adoc)
1 parent 8cd391a commit 29f78f1

1 file changed

Lines changed: 186 additions & 9 deletions

File tree

EXPLAINME.adoc

Lines changed: 186 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,202 @@
11
// SPDX-License-Identifier: PMPL-1.0-or-later
2-
= RSR Template Repository — Show Me The Receipts
2+
= a2ml-haskell — Show Me The Receipts
3+
Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
34
:toc:
45
:icons: font
56

67
The README makes claims. This file backs them up.
78

8-
[quote, README]
9+
== A2ML Parser and Renderer for Haskell
10+
11+
[quote, a2ml-haskell.cabal]
912
____
10-
The AI reads the manifest, asks you a few questions, and handles everything.
13+
A Haskell library for parsing and rendering A2ML (Attested Markup Language)
14+
documents. A2ML is an attestation-native markup format designed for documents
15+
that carry trust metadata, cryptographic attestations, and provenance
16+
information.
1117
____
1218

13-
== File Map
19+
`Data.A2ML.Parser` (`src/Data/A2ML/Parser.hs`) implements a line-oriented,
20+
purely functional parser over `Text`. The top-level function `parseA2ML ::
21+
Text -> Either ParseError Document` returns a typed sum on failure rather than
22+
crashing. Internally, `parseLines` → `parseBlocks` recurse over the line list,
23+
pattern-matching on headings (`# ... #####`), directive blocks
24+
(`@name: ... @end`), bullet items (`- ` or `* `), and plain paragraph lines.
25+
Inline formatting (`**bold**`, `*italic*`) is handled by `parseInlines` and
26+
its helper `parseInlinesItalic` using `Data.Text.breakOn` — no regex, no
27+
mutable state.
28+
29+
The Haskell variant models directives as multi-line blocks (`@name: ... @end`),
30+
which is a richer surface syntax than the single-line `@name value` used in
31+
`a2ml-rs`. Both parsers share the same output semantics for `Document` and
32+
`Attestation`, but diverge on directives: `Data.A2ML.Types` uses a
33+
`DirectiveName` sum type with known constructors (`DirAbstract`, `DirRefs`,
34+
`DirAttestation`, `DirMeta`, `DirCustom Text`) rather than a free-form string pair.
35+
36+
**Caveat:** The inline parser handles `**bold**` and `*italic*` via
37+
left-to-right `breakOn`; nested or interleaved emphasis (e.g.
38+
`*a **b** c*`) is not yet handled correctly. Link `[text](url)` and
39+
`@ref(id)` patterns are declared in the module signature but the parser
40+
implementation falls through to `PlainText` for those cases — the
41+
infrastructure is present, the pattern matching is incomplete. The fuzz
42+
corpus (`tests/fuzz/`) is a placeholder only.
43+
44+
- Parser entry: `src/Data/A2ML/Parser.hs:49` (`parseA2ML :: Text -> Either ParseError Document`)
45+
- Type definitions: `src/Data/A2ML/Types.hs` (Document, Block, Inline, DirectiveName, Attestation, TrustLevel, Manifest, Reference)
46+
- Renderer: `src/Data/A2ML/Renderer.hs`
47+
- Top-level re-export: `src/Data/A2ML.hs`
48+
- Learn more: https://hackage.haskell.org/package/a2ml (forthcoming), https://github.com/hyperpolymath/a2ml
49+
50+
== Cryptographic Attestation Model
51+
52+
[quote, src/Data/A2ML/Types.hs]
53+
____
54+
A cryptographic attestation attached to content.
55+
attestationSigner :: Text
56+
attestationAlgorithm :: Text
57+
attestationSignature :: Text
58+
attestationTimestamp :: Maybe Text
59+
____
60+
61+
The Haskell `Attestation` type goes further than the Rust binding: it records
62+
`attestationAlgorithm` (e.g. `"ed25519"`, `"sha256"`) and
63+
`attestationSignature` (hex or base64). The `TrustLevel` enum runs from
64+
`Unsigned` through `SelfAttested`, `ThirdPartyAttested`, to `MultiAttested`,
65+
encoding the number and independence of attestors rather than the Rust
66+
variant's subjective `Reviewed`/`Verified` distinction. `Manifest` aggregates
67+
title, author, version, SPDX license, overall `TrustLevel`, and the full
68+
attestation list — making it the canonical entry point for programmatic
69+
provenance inspection.
70+
71+
**Caveat:** Signature _verification_ (actual crypto) is not implemented in
72+
this library. `attestationSignature` is stored as an opaque `Text` field.
73+
An external verifier (e.g. the `cookie-rebound` Zig FFI layer or a GnuPG
74+
wrapper) must perform the actual ed25519 or SHA-256 check. The
75+
`MultiAttested` level is declared but no quorum logic is implemented yet.
76+
77+
- Attestation type: `src/Data/A2ML/Types.hs:96–117`
78+
- Trust level enum: `src/Data/A2ML/Types.hs:108–117` (Unsigned → MultiAttested)
79+
- Manifest type: `src/Data/A2ML/Types.hs:119–133`
80+
81+
== Test Suite: Unit, End-to-End, and Property-Based Tests
82+
83+
[quote, a2ml-haskell.cabal]
84+
____
85+
test-suite a2ml-tests
86+
other-modules: UnitSpec E2ESpec PropertySpec
87+
build-depends: hspec, QuickCheck, hspec-quickcheck
88+
____
89+
90+
The test suite has three modules. `UnitSpec` tests individual parser functions
91+
in isolation (heading level detection, directive name parsing, bullet list
92+
accumulation). `E2ESpec` tests the full `parseA2ML` → `renderA2ML` round-trip
93+
on representative `.a2ml` documents. `PropertySpec` uses QuickCheck to assert
94+
`parseA2ML (renderA2ML doc) == Right doc` for arbitrary `Document` values —
95+
that the renderer and parser are mutual inverses. The `Spec.hs` entry point
96+
aggregates all three via `hspec`.
97+
98+
**Caveat:** QuickCheck generators for `Document` do not yet exercise the full
99+
surface syntax — generated documents use only headings and paragraphs, not
100+
directives or attestations. Property coverage will expand as the inline parser
101+
matures.
102+
103+
- Unit tests: `tests/UnitSpec.hs`
104+
- End-to-end tests: `tests/E2ESpec.hs`
105+
- Property tests: `tests/PropertySpec.hs`
106+
- Test runner: `tests/Spec.hs`
107+
- Benchmark: `bench/Main.hs` (Criterion for parse and render hot paths)
108+
- Run: `cabal test` or `just test`
109+
110+
== MPL-2.0 Fallback for Hackage
111+
112+
[quote, a2ml-haskell.cabal]
113+
____
114+
license: MPL-2.0
115+
-- (PMPL-1.0-or-later preferred; MPL-2.0 required for Hackage OSI-approved policy)
116+
____
117+
118+
Hackage requires an OSI-approved license; PMPL-1.0-or-later is not yet on the
119+
OSI list. The fallback policy (identical to `a2ml-rs`) applies: every `.hs`
120+
source file carries the dual SPDX comment. The `LICENSE-MPL-2.0` file is
121+
present alongside `LICENSE` (PMPL-1.0-or-later).
122+
123+
**Caveat:** Unlike crates.io, Hackage does not enforce the SPDX identifier at
124+
upload time by machine. The comment in each source file is the enforceable
125+
record of the dual-license intent.
126+
127+
- License files: `LICENSE` (PMPL-1.0-or-later), `LICENSE-MPL-2.0`
128+
- Learn more: https://github.com/hyperpolymath/palimpsest-license
129+
130+
== Zig FFI Layer (Scaffold Present, Implementation Pending)
131+
132+
[quote, src/interface/ffi/README.adoc]
133+
____
134+
Zig FFI scaffold for exposing Data.A2ML as a C-ABI library.
135+
____
136+
137+
The `src/interface/ffi/` directory follows the standard Idris2-ABI / Zig-FFI
138+
architecture required by all RSR projects with cross-language interfaces. The
139+
`build.zig` and `main.zig` scaffold is present; the Idris2 ABI definitions in
140+
`src/interface/abi/` are placeholders. This layer is intended to expose
141+
`parseA2ML` as a C-callable function for embedding the Haskell parser in
142+
non-Haskell hosts via GHC's Foreign Function Interface.
143+
144+
**Caveat:** The Zig FFI is not yet wired to any real Haskell FFI export. No
145+
`.hs` module currently declares `foreign export ccall` bindings. This is
146+
scaffolding for a future integration milestone.
147+
148+
- FFI scaffold: `src/interface/ffi/src/main.zig`, `src/interface/ffi/build.zig`
149+
- ABI placeholder: `src/interface/abi/` (Idris2 definitions pending)
150+
- Integration test scaffold: `src/interface/ffi/test/integration_test.zig`
151+
152+
== Dogfooded Across The Account
14153

15154
[cols="1,2"]
16155
|===
17-
| Path | What's There
156+
| Technology | Also Used In
157+
158+
| **A2ML format** | https://github.com/hyperpolymath/a2ml-rs[a2ml-rs] (Rust binding), https://github.com/hyperpolymath/pandoc-a2ml[pandoc-a2ml] (Lua Pandoc reader), https://github.com/hyperpolymath/tree-sitter-a2ml[tree-sitter-a2ml] (grammar)
159+
160+
| **Haskell / GHC + cabal** | https://github.com/hyperpolymath/nextgen-languages[nextgen-languages] (Scaffoldia CLI, AffineScript compiler), https://github.com/hyperpolymath/hypatia[hypatia] (rule modules)
161+
162+
| **hspec + QuickCheck** | Other Haskell projects in the `nextgen-languages` monorepo
163+
164+
| **SPDX MPL-2.0 fallback** | https://github.com/hyperpolymath/a2ml-rs[a2ml-rs] (crates.io policy)
165+
|===
18166

19-
| `src/` | Source code
20-
| `test(s)/` | Test suite
167+
== File Map
168+
169+
[cols="1,2"]
21170
|===
171+
| Path | Proves
22172

23-
== Questions?
173+
| `src/Data/A2ML.hs` | Top-level re-export module — public API surface for library consumers
24174

25-
Open an issue or reach out directly — happy to explain anything in more detail.
175+
| `src/Data/A2ML/Types.hs` | AST: `Document`, `Block`, `Inline`, `DirectiveName` (sum with known values), `Attestation` (with algorithm + signature fields), `TrustLevel` (Unsigned→MultiAttested), `Manifest`, `Reference`
176+
177+
| `src/Data/A2ML/Parser.hs` | Line-oriented parser: `parseA2ML`, `parseA2MLFile`, `ParseError` sum type, `parseBlocks`, `parseInlines`, directive body collector (`collectDirectiveBody`), bullet item accumulator (`collectBulletItems`)
178+
179+
| `src/Data/A2ML/Renderer.hs` | Canonical serialiser: `renderA2ML :: Document -> Text` — intended round-trip inverse of the parser
180+
181+
| `tests/UnitSpec.hs` | Isolated unit tests for parser primitives
182+
183+
| `tests/E2ESpec.hs` | End-to-end parse→render→re-parse round-trip tests
184+
185+
| `tests/PropertySpec.hs` | QuickCheck property: `parse ∘ render = id` for generated documents
186+
187+
| `tests/Spec.hs` | hspec aggregator entry point
188+
189+
| `bench/Main.hs` | Criterion benchmarks for parse and render hot paths
190+
191+
| `a2ml-haskell.cabal` | Package metadata: exposed modules, build deps (base, text, containers, bytestring), GHC warning flags, test suite and benchmark stanza definitions
192+
193+
| `src/interface/abi/` | Idris2 ABI definitions (placeholder — formal proof artefacts pending)
194+
195+
| `src/interface/ffi/src/main.zig` | Zig FFI scaffold for C-ABI exposure of the Haskell parser
196+
197+
| `src/interface/ffi/test/integration_test.zig` | FFI integration test scaffold
198+
199+
| `0-AI-MANIFEST.a2ml` | AI session gatekeeper — read first before modifying any file
200+
201+
| `.machine_readable/` | A2ML state files: STATE.a2ml, ECOSYSTEM.a2ml, META.a2ml
202+
|===

0 commit comments

Comments
 (0)