Skip to content

Commit fda360d

Browse files
Panel-clades pivot: ADR-0001 + Axiom general-form characterisation + P2 design (#39)
* docs(adr): ADR-0001 panel-clades pivot + freeze src-gossamer legacy (P0) Pin down decisions surfaced during tech-debt remediation: - SoT = panel-clades (Idris2 ABI + Zig FFI + a2ml clades); estate-conformant - src-gossamer Rust = FROZEN LEGACY (STATUS.adoc marker); PR #37 = interim legacy-stabilisation only - Reference = Axiom.jl's backend-organisation APPROACH (the pattern, not its coprocessor code); oo7/jtv experimental, out of scope - Backend set (11): FPGA DSP math physics tensor vector I/O audio neural crypto quantum - panel-clades found to be a spec-complete but UNINSTANTIATED template shell (rsr {{placeholders}}, just init never run) - GATE: characterise + re-validate Axiom.jl's approach before any downstream clade/backend build (no speculative work) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(adr): trust only Axiom.jl general form; non-conforming code = presumed mess Per owner guidance: do not look at oo7/jtv at all; any coprocessor/ backend code not resembling the Axiom.jl general form is presumed bot-mess / interference / incomplete and is NOT reference material. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: characterise Axiom.jl general form + gate re-validation verdict Cloned hyperpolymath/Axiom.jl; characterised the standard backend- organisation approach in docs/architecture/AXIOM-GENERAL-FORM.adoc (AbstractBackend taxonomy + SmartBackend per-op dispatch + AcceleratorGate device selection + Idris2-spec/Zig-authoritative split + bidirectional host bridge + parity/smoke CI). Re-validation: Idris2 ABI typechecks clean (0.8.0); Zig FFI main.zig FAILS — 17 'Axiom.jl_*' invalid identifiers from a bad token-substitution bot (should be 'axiom_*'; header/tests/README clean; design sound; trivial repair). Same corruption class also in panel-clades/ffi/zig/. ADR-0001 gate: PASSED with caveat (pattern+spec trusted; P2 build gated on the Axiom.jl de-corruption landing). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(p2): coprocessor panel-clade design (against validated Axiom form) Design-only (no build): 11-backend taxonomy mapped to Axiom structs, panel-clades artefact mapping (a2ml clade spec + Idris2 ABI + Zig FFI + SmartBackend dispatch + bidirectional bridge + honest-status precedence), modelled on the now-validated Axiom general form (Axiom.jl PR #17, 9/9). Open questions flagged (not guessed): AcceleratorGate consumption boundary for an Idris2/Zig clade; reference-backend language; I/O+audio op vocab. Gates G1 (AcceleratorGate registration) / G2 (panel-clades template instantiation + de-corruption) / G3 (owner resolves Qs). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * G2(partial): instantiate panel-clades Zig FFI {{project}}->panel_clades Consistency fix (the exact thing Axiom got wrong): build.zig + integration_test.zig now use panel_clades_* matching main.zig exports and Foreign.idr (C:panel_clades_*, libpanel_clades). Remaining: same mechanical Zig-0.15 drift class as Axiom (addSharedLibrary->addLibrary, callconv(.C)->.c, opaque-with-fields) — tracked, not yet applied. * G2: panel-clades Zig FFI builds green under Zig 0.15 build.zig migrated to 0.15 API (addSharedLibrary/addStaticLibrary -> addLibrary+linkage, addTest/addExecutable -> root_module via createModule, addInstallHeader -> addInstallFileWithDir, .link_libc on all modules). main.zig: opaque-with-fields -> struct, callconv(.C) -> callconv(.c), removed pointless discard. zig build test now exits 0 (was: didn't compile). Same proven pattern as Axiom.jl PR #17. * docs(p2): durable G2 status + remaining-placeholder triage (handoff, no re-derive) Captures what was only in conversation: G1 dissolved (AcceleratorGate decoupled), G2 partial (Zig FFI 6/6 green), and the exact triage of the ~50 remaining template placeholders into derivable / owner-decision / crypto-material buckets, with the safe resume path. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 51306ea commit fda360d

4 files changed

Lines changed: 234 additions & 76 deletions

File tree

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// SPDX-License-Identifier: PMPL-1.0-or-later
2+
// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
3+
= P2 — Coprocessor panel-clade design (the exemplar)
4+
:toc: left
5+
:sectnums:
6+
7+
Status: DESIGN ONLY (2026-05-18). No build until the gates below clear.
8+
Authority: models the *validated* Axiom general form
9+
(`docs/architecture/AXIOM-GENERAL-FORM.adoc`, re-validated against
10+
`Axiom.jl` PR #17 — 9/9 green) under ADR-0001. This clade is the
11+
exemplar every other PanLL panel-clade will follow.
12+
13+
== Goal
14+
15+
Instantiate `panel-clades/` (today an uninstantiated rsr template) into a
16+
*concrete* coprocessor panel-clade that organises heterogeneous compute
17+
backends exactly as Axiom.jl does, exposing it through the estate language
18+
architecture (ABI = Idris2, FFI = Zig).
19+
20+
== Backend set (11) → Axiom taxonomy mapping
21+
22+
[cols="1,1,2"]
23+
|===
24+
| PanLL backend | Axiom struct | Notes
25+
26+
| math | `MathBackend` | math coprocessor
27+
| physics | `PPUBackend` | physics processing unit
28+
| tensor | `TPUBackend` | tensor processing unit
29+
| vector | `VPUBackend` | vector/vision processing unit
30+
| neural | `NPUBackend` | neural processing unit
31+
| crypto | `CryptoBackend` | crypto coprocessor
32+
| quantum | `QPUBackend` | quantum processing unit
33+
| FPGA | `FPGABackend` | FPGA accelerator
34+
| DSP | `DSPBackend` | digital signal processor
35+
| I/O | (new) `IOBackend` | *no Axiom precedent* — add following the identical pattern
36+
| audio | (new) `AudioBackend` | *no Axiom precedent* — add following the identical pattern
37+
|===
38+
39+
Plus the two *implementation* backends from the general form: a reference
40+
backend (Axiom's `JuliaBackend` role — pure, always-correct, the fallback)
41+
and a high-performance native backend (Axiom's `ZigBackend` role).
42+
43+
== Structure (Axiom element → panel-clades artefact)
44+
45+
. **Abstract supertype + per-class subtypes** → the a2ml clade spec
46+
`panel-clades/clades/coprocessors/Coprocessors.a2ml` declares the 11
47+
backends + the reference/native impl pair + the dispatch policy.
48+
. **`SmartBackend` per-operation dispatcher** → a clade-level dispatch
49+
table (operation → preferred backend) with **graceful fallback to the
50+
reference backend** when the preferred one is absent. Benchmark-driven,
51+
per-operation, never global.
52+
. **Device selection delegated** → consume capability/cost selection rather
53+
than reinventing it (Axiom uses `AcceleratorGate`). *See open question.*
54+
. **Idris2 ABI = formal spec** → `panel-clades/src/abi/{Types,Layout,Foreign}.idr`,
55+
concrete `panll_copro_*` symbols (no `{{placeholders}}`), must typecheck
56+
(`idris2 --check`), mirroring Axiom's `src/Abi/*.idr` (which we verified
57+
typecheck clean).
58+
. **Zig FFI = authoritative runtime** → instantiate
59+
`panel-clades/ffi/zig/` (template today): concrete `panll_copro_*` C ABI,
60+
`include/`, `build.zig`, `test/integration_test.zig`. Authoritative for
61+
release per the general form.
62+
. **Bidirectional host bridge** → host→runtime + runtime→host callback
63+
(the pattern just implemented & tested in Axiom.jl PR #17:
64+
`register_callback(u64 ptr)` + `invoke_callback`).
65+
. **Honest-status precedence** → reference backend = fallback; Idris2 ABI =
66+
must-typecheck scaffold; Zig FFI = authoritative; CI = backend-parity +
67+
runtime-smoke.
68+
69+
== Open design questions (resolve before build — do NOT guess)
70+
71+
. **AcceleratorGate consumption boundary.** Axiom.jl is Julia and consumes
72+
`AcceleratorGate` (a Julia package) directly. PanLL's coprocessor clade
73+
is Idris2-ABI / Zig. Options: (a) the clade calls AcceleratorGate across
74+
a bridge; (b) AcceleratorGate's capability/cost-selection logic is
75+
reimplemented in the Zig FFI; (c) a thin Julia sidecar. This is the
76+
single biggest design decision and depends on AcceleratorGate's public
77+
surface — which is not inspectable until it is registered/cloned.
78+
. **Reference backend language.** Axiom's fallback is `JuliaBackend`. The
79+
PanLL clade's reference backend should be the always-available, provably
80+
correct path — candidate: a pure-Zig reference, keeping the clade free of
81+
a Julia runtime dependency. Confirm.
82+
. **I/O & audio backends** have no Axiom precedent — define their operation
83+
vocabulary from the PanLL panel requirements, following the identical
84+
subtype pattern.
85+
86+
== Gates (build blocked until all clear)
87+
88+
* **G1** Dissolved. `AcceleratorGate` need not be registered: Axiom.jl was
89+
decoupled by vendoring it (precedent set); `AcceleratorGate.jl` is cloned
90+
locally for Q1 analysis. Julia-registry timing is OFF this critical path.
91+
* **G2** `panel-clades/` template instantiation — *partially done*; see
92+
triage below.
93+
* **G3** Q1–Q3 resolved by the owner.
94+
95+
== G2 status & remaining-placeholder triage (handoff — do not re-derive)
96+
97+
**Done (2026-05-18, on `feat/panel-clades-pivot`):** `panel-clades/ffi/zig/`
98+
fully instantiated + migrated to Zig 0.15 — `{{project}}` → `panel_clades`
99+
(matches `main.zig` exports + `Foreign.idr` `C:panel_clades_*,
100+
libpanel_clades`); `build.zig` ported (addLibrary/linkage, root_module,
101+
addInstallFileWithDir, `.link_libc`); `main.zig` opaque→struct,
102+
callconv(.C)→.c. `zig build test` → **6/6 green**.
103+
104+
**Remaining** = the ~50 non-FFI `{{TOKEN}}` placeholders across ~45 files.
105+
The template's intended instantiator is its own interactive `just init`
106+
(see `panel-clades/PLACEHOLDERS.md`). Hand-sed is hazardous: the literal
107+
`panel-clades` is overloaded across 3 roles (dir name, uppercase ident,
108+
one-line description) — blind replace = the Axiom corruption class.
109+
Triage of the remaining tokens:
110+
111+
[cols="1,3"]
112+
|===
113+
| Mechanically derivable (safe to auto-fill)
114+
| `{{AUTHOR}}`=Jonathan D.A. Jewell, `{{AUTHOR_EMAIL}}`=j.d.a.jewell@open.ac.uk,
115+
`{{OWNER}}`=hyperpolymath, `{{FORGE}}`=github.com, `{{REPO}}`=panll,
116+
`{{CURRENT_YEAR}}`=2026, `{{CURRENT_DATE}}`/`{{GENERATED_AT}}`=instantiation date,
117+
`{{VERSION}}`=0.1.0, `{{PASCAL_NAME}}`=PanelClades, `{{PROJECT}}`=PANEL_CLADES,
118+
`{{project}}`=panel_clades (FFI already done)
119+
120+
| Owner decisions (do NOT guess)
121+
| `{{LICENSE}}` (internal pkg — estate PMPL vs MPL-2.0), `{{PORT}}`,
122+
`{{PROTOCOL}}`, `{{PROJECT_KIND}}`, `{{DOMAIN}}`/`{{PROJECT_DOMAIN}}`/`{{WEBSITE}}`,
123+
`{{CLADE_ID}}`, `{{CLADE_NAME}}`, `{{SHORT}}`, `{{ICON}}`,
124+
`{{ONE_LINE_DESCRIPTION}}`/`{{PROJECT_DESCRIPTION}}`/`{{PROJECT_PURPOSE}}`,
125+
`{{REGISTRY}}`, `{{SECURITY_EMAIL}}`/`{{CONDUCT_EMAIL}}`/`{{CONDUCT_TEAM}}`,
126+
`{{RESPONSE_TIME}}`, `{{EXPIRES_AT}}`, `{{ON_INIT_HANDLER}}`/`{{ON_DISPOSE_HANDLER}}`
127+
128+
| Real cryptographic material (MUST come from estate key inventory — never fabricate)
129+
| `{{SPHINCS_PLUS_PUBLIC_KEY}}`, `{{PRIMARY_SIGNATURE}}`,
130+
`{{FALLBACK_SIGNATURE}}`, `{{PGP_KEY_URL}}`, `{{ZONEMD}}`,
131+
`{{TRUSTFILE_PATH}}`, `{{TRUSTFILE_VERSION}}`
132+
|===
133+
134+
Resume path: owner provides the decision values + points at real estate
135+
keys → run/emulate `just init` with that value-set (NOT blind sed). Paused
136+
pending the suite audit, which may reprioritise the whole estate first.
137+
138+
== Anti-goals (per ADR-0001)
139+
140+
Do not look at `oo7`/`jtv`. Trust only the Axiom general form. Do not
141+
instantiate/build before G1–G3. `src-gossamer` stays frozen.

panel-clades/ffi/zig/build.zig

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,41 +7,49 @@ pub fn build(b: *std.Build) void {
77
const target = b.standardTargetOptions(.{});
88
const optimize = b.standardOptimizeOption(.{});
99

10-
// Shared library (.so, .dylib, .dll)
11-
const lib = b.addSharedLibrary(.{
12-
.name = "{{project}}",
10+
// Root module shared by the library/test artifacts (Zig 0.15 API).
11+
const root_mod = b.createModule(.{
12+
.link_libc = true,
1313
.root_source_file = b.path("src/main.zig"),
1414
.target = target,
1515
.optimize = optimize,
1616
});
1717

18-
// Set version
19-
lib.version = .{ .major = 0, .minor = 1, .patch = 0 };
18+
// Shared library (.so, .dylib, .dll)
19+
const lib = b.addLibrary(.{
20+
.name = "panel_clades",
21+
.linkage = .dynamic,
22+
.root_module = root_mod,
23+
.version = .{ .major = 0, .minor = 1, .patch = 0 },
24+
});
2025

2126
// Static library (.a)
22-
const lib_static = b.addStaticLibrary(.{
23-
.name = "{{project}}",
24-
.root_source_file = b.path("src/main.zig"),
25-
.target = target,
26-
.optimize = optimize,
27+
const lib_static = b.addLibrary(.{
28+
.name = "panel_clades",
29+
.linkage = .static,
30+
.root_module = root_mod,
2731
});
2832

2933
// Install artifacts
3034
b.installArtifact(lib);
3135
b.installArtifact(lib_static);
3236

33-
// Generate header file for C compatibility
34-
const header = b.addInstallHeader(
35-
b.path("include/{{project}}.h"),
36-
"{{project}}.h",
37+
// Install the C header (Zig 0.15 API).
38+
const header = b.addInstallFileWithDir(
39+
b.path("include/panel_clades.h"),
40+
.header,
41+
"panel_clades.h",
3742
);
3843
b.getInstallStep().dependOn(&header.step);
3944

4045
// Unit tests
4146
const lib_tests = b.addTest(.{
42-
.root_source_file = b.path("src/main.zig"),
43-
.target = target,
44-
.optimize = optimize,
47+
.root_module = b.createModule(.{
48+
.link_libc = true,
49+
.root_source_file = b.path("src/main.zig"),
50+
.target = target,
51+
.optimize = optimize,
52+
}),
4553
});
4654

4755
const run_lib_tests = b.addRunArtifact(lib_tests);
@@ -51,9 +59,12 @@ pub fn build(b: *std.Build) void {
5159

5260
// Integration tests
5361
const integration_tests = b.addTest(.{
54-
.root_source_file = b.path("test/integration_test.zig"),
55-
.target = target,
56-
.optimize = optimize,
62+
.root_module = b.createModule(.{
63+
.link_libc = true,
64+
.root_source_file = b.path("test/integration_test.zig"),
65+
.target = target,
66+
.optimize = optimize,
67+
}),
5768
});
5869

5970
integration_tests.linkLibrary(lib);
@@ -65,9 +76,12 @@ pub fn build(b: *std.Build) void {
6576

6677
// Documentation
6778
const docs = b.addTest(.{
68-
.root_source_file = b.path("src/main.zig"),
69-
.target = target,
70-
.optimize = .Debug,
79+
.root_module = b.createModule(.{
80+
.link_libc = true,
81+
.root_source_file = b.path("src/main.zig"),
82+
.target = target,
83+
.optimize = .Debug,
84+
}),
7185
});
7286

7387
const docs_step = b.step("docs", "Generate documentation");
@@ -79,10 +93,13 @@ pub fn build(b: *std.Build) void {
7993

8094
// Benchmark (if needed)
8195
const bench = b.addExecutable(.{
82-
.name = "{{project}}-bench",
83-
.root_source_file = b.path("bench/bench.zig"),
84-
.target = target,
85-
.optimize = .ReleaseFast,
96+
.name = "panel_clades-bench",
97+
.root_module = b.createModule(.{
98+
.link_libc = true,
99+
.root_source_file = b.path("bench/bench.zig"),
100+
.target = target,
101+
.optimize = .ReleaseFast,
102+
}),
86103
});
87104

88105
bench.linkLibrary(lib);

panel-clades/ffi/zig/src/main.zig

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,13 @@ pub const CladeKind = enum(u32) {
5959
terminal = 12,
6060
};
6161

62-
/// Library handle (opaque to prevent direct access)
63-
pub const Handle = opaque {
62+
/// Library handle. A real struct internally; exposed across the C ABI as
63+
/// an opaque pointer (the header forward-declares it). `opaque{}` cannot
64+
/// have fields or be `create`d, so this must be a `struct`.
65+
pub const Handle = struct {
6466
// Internal state hidden from C
6567
allocator: std.mem.Allocator,
6668
initialized: bool,
67-
// Add your fields here
6869
};
6970

7071
//==============================================================================
@@ -154,8 +155,7 @@ export fn panel_clades_load_clade(handle: ?*Handle, clade_id: ?[*:0]const u8) Re
154155
return .invalid_param;
155156
}
156157

157-
// TODO: store clade definition internally
158-
_ = id_str;
158+
// TODO: store clade definition internally (id_str validated above)
159159

160160
clearError();
161161
return .ok;
@@ -318,7 +318,7 @@ export fn panel_clades_build_info() [*:0]const u8 {
318318
//==============================================================================
319319

320320
/// Callback function type (C ABI)
321-
pub const Callback = *const fn (u64, u32) callconv(.C) u32;
321+
pub const Callback = *const fn (u64, u32) callconv(.c) u32;
322322

323323
/// Register a callback
324324
export fn panel_clades_register_callback(

0 commit comments

Comments
 (0)