Skip to content

Commit d4522e4

Browse files
Claude/happy mendel b4 ie u (#344)
Co-authored-by: Claude <noreply@anthropic.com>
1 parent b0b0a22 commit d4522e4

36 files changed

Lines changed: 3570 additions & 571 deletions

.build/dune-project

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
(odoc (and (>= 2.4) :with-doc))
4040
(js_of_ocaml (>= 5.0))
4141
(js_of_ocaml-ppx (>= 5.0))
42-
(ocamlformat (and (>= 0.26) :with-test)))
42+
(ocamlformat (and (>= 0.26) :with-test))
43+
(bisect_ppx (and (>= 2.8) :with-test)))
4344
(tags
4445
(programming-language compiler webassembly affine-types dependent-types row-polymorphism effects ocaml)))

.github/workflows/ci.yml

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,110 @@ jobs:
9898
run: opam exec -- dune build @doc
9999
continue-on-error: true
100100

101+
bench-visibility:
102+
# Visibility-only microbenchmarks per docs/standards/TESTING.adoc
103+
# §"Bench standards". Does NOT block merge. Promotion to a
104+
# ratcheted gate requires a calibrated baseline first.
105+
runs-on: ubuntu-latest
106+
107+
steps:
108+
- name: Checkout code
109+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
110+
111+
- name: Set up OCaml
112+
uses: ocaml/setup-ocaml@e32b06a3e831ff2fbc6f08cf35be2085e3918014 # v3
113+
with:
114+
ocaml-compiler: "5.1"
115+
116+
- name: Install dependencies
117+
run: opam install . --deps-only --with-test --with-doc
118+
119+
- name: Build bench targets
120+
run: opam exec -- dune build @bench --force
121+
continue-on-error: true
122+
123+
- name: Run benches
124+
id: bench
125+
continue-on-error: true
126+
run: |
127+
set -o pipefail
128+
opam exec -- dune runtest @bench --force 2>&1 | tee bench-output.log
129+
130+
- name: Render bench summary
131+
if: always()
132+
run: |
133+
{
134+
echo "# AffineScript microbench output (visibility-only)"
135+
echo ""
136+
echo "Policy: see [docs/standards/TESTING.adoc](docs/standards/TESTING.adoc)."
137+
echo ""
138+
if [ -f bench-output.log ]; then
139+
echo '```'
140+
cat bench-output.log
141+
echo '```'
142+
fi
143+
} >> "$GITHUB_STEP_SUMMARY"
144+
145+
- name: Upload bench log
146+
if: always()
147+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
148+
with:
149+
name: bench-output
150+
path: bench-output.log
151+
retention-days: 30
152+
153+
coverage-visibility:
154+
# Visibility-only coverage report per
155+
# docs/standards/TESTING.adoc §"Coverage (visibility-only)".
156+
# No merge-blocking floor today.
157+
runs-on: ubuntu-latest
158+
159+
steps:
160+
- name: Checkout code
161+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
162+
163+
- name: Set up OCaml
164+
uses: ocaml/setup-ocaml@e32b06a3e831ff2fbc6f08cf35be2085e3918014 # v3
165+
with:
166+
ocaml-compiler: "5.1"
167+
168+
- name: Install dependencies
169+
run: opam install . --deps-only --with-test --with-doc
170+
171+
- name: Run tests with bisect_ppx instrumentation
172+
continue-on-error: true
173+
run: |
174+
opam exec -- dune runtest --force --instrument-with bisect_ppx
175+
176+
- name: Generate HTML coverage report
177+
continue-on-error: true
178+
run: |
179+
opam exec -- bisect-ppx-report html -o _coverage --title="AffineScript coverage"
180+
opam exec -- bisect-ppx-report summary | tee coverage-summary.txt
181+
182+
- name: Render coverage summary
183+
if: always()
184+
run: |
185+
{
186+
echo "# AffineScript coverage (visibility-only)"
187+
echo ""
188+
echo "Policy: see [docs/standards/TESTING.adoc](docs/standards/TESTING.adoc)."
189+
echo ""
190+
if [ -f coverage-summary.txt ]; then
191+
echo '```'
192+
cat coverage-summary.txt
193+
echo '```'
194+
fi
195+
} >> "$GITHUB_STEP_SUMMARY"
196+
197+
- name: Upload coverage HTML
198+
if: always()
199+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
200+
with:
201+
name: coverage-html
202+
path: _coverage
203+
retention-days: 30
204+
101205
vscode-smoke:
102206
# In-editor end-to-end smoke test for the .affine VS Code extension
103207
# (issue #139). Loads the compiled out/extension.cjs in a real VS Code

.github/workflows/panic-attack.yml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# panic-attack.yml — estate compliance scanner, weekly schedule.
3+
#
4+
# Per docs/standards/PANIC-ATTACK.adoc:
5+
# - This runs on a weekly cron; per-PR invocation is intentionally
6+
# out of scope (CodeQL/Semgrep/secret-scanner cover per-PR).
7+
# - Findings are surfaced as a workflow summary. Critical/high
8+
# findings that are not in the false-positive registry will
9+
# open a tracking issue.
10+
# - Release cuts call `just panic` manually per RELEASE.md.
11+
#
12+
# Tool: `panic-attacker` Rust crate from
13+
# road-skate/features/panic-attacker. Installed via cargo.
14+
15+
name: Panic-Attack (compliance scan)
16+
17+
on:
18+
schedule:
19+
# Sundays 03:00 UTC — quiet window for the estate's CI fleet.
20+
- cron: '0 3 * * 0'
21+
workflow_dispatch:
22+
23+
# Read-only audit workflow — safe to cancel superseded runs.
24+
concurrency:
25+
group: ${{ github.workflow }}-${{ github.ref }}
26+
cancel-in-progress: true
27+
28+
permissions:
29+
contents: read
30+
issues: write # opens a tracking issue on unresolved critical/high
31+
32+
jobs:
33+
scan:
34+
runs-on: ubuntu-latest
35+
steps:
36+
- name: Checkout
37+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
38+
with:
39+
persist-credentials: false
40+
41+
- name: Install Rust toolchain (stable)
42+
uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # stable
43+
with:
44+
toolchain: stable
45+
46+
- name: Install panic-attacker
47+
# Source-install from the estate crate. Once published to
48+
# crates.io this can switch to `cargo install panic-attacker`.
49+
run: |
50+
if [ -d road-skate/features/panic-attacker ]; then
51+
cargo install --path road-skate/features/panic-attacker --locked || \
52+
cargo install --path road-skate/features/panic-attacker
53+
else
54+
echo "::warning::road-skate/features/panic-attacker not found; trying crates.io"
55+
cargo install panic-attacker || {
56+
echo "::error::panic-attacker unavailable from both estate path and crates.io"
57+
exit 1
58+
}
59+
fi
60+
61+
- name: Run panic-attack
62+
id: scan
63+
continue-on-error: true
64+
run: |
65+
set -o pipefail
66+
panic-attack assail 2>&1 | tee panic-attack.log
67+
68+
- name: Render summary
69+
if: always()
70+
run: |
71+
{
72+
echo "# panic-attack weekly scan"
73+
echo ""
74+
echo "Repository: \`${{ github.repository }}\`"
75+
echo "Ref: \`${{ github.sha }}\`"
76+
echo ""
77+
if [ -f panic-attack.log ]; then
78+
echo "## Output"
79+
echo '```'
80+
tail -200 panic-attack.log
81+
echo '```'
82+
else
83+
echo "(no output captured)"
84+
fi
85+
echo ""
86+
echo "Policy: see [docs/standards/PANIC-ATTACK.adoc](docs/standards/PANIC-ATTACK.adoc)."
87+
} >> "$GITHUB_STEP_SUMMARY"
88+
89+
- name: Upload log artifact
90+
if: always()
91+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
92+
with:
93+
name: panic-attack-log
94+
path: panic-attack.log
95+
retention-days: 30

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ secrets/
8080
# Test/Coverage
8181
/coverage/
8282
htmlcov/
83+
/_coverage/
84+
bisect*.coverage
85+
86+
# Benchmark archives (visibility-only output, see docs/standards/TESTING.adoc)
87+
/bench-runs/
8388

8489
# Logs
8590
*.log

.machine_readable/6a2/META.a2ml

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,3 +1283,166 @@ references = [
12831283
"docs/PACKAGING.adoc (INT-04; the JS-package half)",
12841284
"docs/specs/SETTLED-DECISIONS.adoc (ADR-019 section)",
12851285
]
1286+
1287+
[[adr]]
1288+
id = "ADR-020"
1289+
status = "proposed"
1290+
date = "2026-05-23"
1291+
title = "Ownership-section schema versioning (0xAF sentinel + u8 version)"
1292+
context = """
1293+
The `affinescript.ownership` Wasm custom section has been frozen at
1294+
v1 since typed-wasm Level 10 closure (2026-04-19) and is emitted in
1295+
identical form by AffineScript (`lib/codegen.ml`) and by ephapax
1296+
(`hyperpolymath/ephapax/src/ephapax-wasm`). The Rust verifier in
1297+
`hyperpolymath/typed-wasm` consumes it as the cross-compat target.
1298+
1299+
V1 has *no version field*. Today this is fine because both
1300+
producers are at v1 and the verifier expects v1 unconditionally.
1301+
The first multi-producer ABI change — when L1–6 / L14–16 emission
1302+
lands — will need version discrimination. Adding the version field
1303+
*now*, while we are still at v1, is much cheaper than retrofitting
1304+
it under load.
1305+
1306+
This ADR is filed alongside the broader typed-wasm widening roadmap
1307+
in `docs/specs/TYPED-WASM-ROADMAP.adoc` §"Tranche B".
1308+
"""
1309+
decision = """
1310+
Move from v1 (unversioned) to v2 (versioned, with sentinel):
1311+
1312+
v1 payload:
1313+
u32 entry_count
1314+
entry*
1315+
1316+
v2 payload:
1317+
u8 version_tag ; 0xAF — "AffineScript Format"
1318+
u8 version ; 0x02 for v2.0
1319+
u32 entry_count
1320+
entry* ; same entry shape as v1; no new entry fields in v2.0
1321+
1322+
The 0xAF sentinel is byte-distinct from any plausible v1 entry-count
1323+
low byte for a module with ≤ 0xAE entries — i.e. practically every
1324+
module in existence. v1 readers fail cleanly on a v2 section (bad
1325+
entry-count); v2 readers see the sentinel and dispatch.
1326+
1327+
Coordinated landing protocol:
1328+
1. Land ADR-020 in `hyperpolymath/affinescript` (this repo).
1329+
2. Mirror ADR in `hyperpolymath/typed-wasm` (Rust verifier
1330+
dispatches on sentinel; accepts both v1 and v2 during
1331+
migration).
1332+
3. Mirror ADR in `hyperpolymath/ephapax` (second producer emits
1333+
v2).
1334+
4. Verifier ships v2-parse FIRST. Producers stay on v1 emit.
1335+
5. Once verifier is deployed (in both OCaml + Rust paths),
1336+
producers flip to v2 emit together.
1337+
6. After a deprecation window, v1-parse path is removed from
1338+
verifier.
1339+
1340+
The v2.0 entry shape is identical to v1 — this ADR introduces no
1341+
new ownership kinds, no new per-entry fields. Future widening
1342+
(e.g. v2.1 adding a region-id field per entry) is a separate ADR.
1343+
"""
1344+
consequences = """
1345+
- Forward-compatible: any future widening (L1–6 / L14–16 carriers,
1346+
per-entry annotations) can negotiate via the version byte without
1347+
another sentinel-or-no-sentinel war.
1348+
- v1 callers fail cleanly on v2 sections (bad entry-count parse) —
1349+
no silent corruption.
1350+
- Single coordinated landing across three repos; complexity is in
1351+
the coordination, not the bytes.
1352+
- This decision is PROPOSED. Land or supersede explicitly; do not
1353+
silently drift.
1354+
- Cross-references the typed-wasm roadmap in
1355+
`docs/specs/TYPED-WASM-ROADMAP.adoc` §"B1".
1356+
"""
1357+
references = [
1358+
"docs/specs/TYPED-WASM-INTERFACE.adoc",
1359+
"docs/specs/TYPED-WASM-INTERFACE.a2ml",
1360+
"docs/specs/TYPED-WASM-ROADMAP.adoc",
1361+
"lib/codegen.ml (build_ownership_section, ~line 159)",
1362+
"lib/tw_verify.ml (parse_ownership_section)",
1363+
"hyperpolymath/typed-wasm (Rust verifier crate — coordination target)",
1364+
"hyperpolymath/ephapax (second producer — coordination target)",
1365+
]
1366+
1367+
[[adr]]
1368+
id = "ADR-021"
1369+
status = "proposed"
1370+
date = "2026-05-23"
1371+
title = "Multi-producer ABI coordination model for typed-wasm carriers"
1372+
context = """
1373+
typed-wasm is a separate, language-agnostic compilation target with
1374+
its own repository (`hyperpolymath/typed-wasm`) and its own
1375+
producers (today: AffineScript + ephapax; potentially more later).
1376+
The `affinescript.ownership` custom section is the first
1377+
multi-producer ABI we own.
1378+
1379+
Today coordination happens by `Refs #N` across repositories and
1380+
good intent. As the typed-wasm widening proposals land (ADR-020
1381+
schema versioning, then carriers for L1–6 and L14–16), this
1382+
informal model will break. The session-note record makes clear that
1383+
each ABI conversation has been one-off and ad-hoc; the failure mode
1384+
is two producers landing incompatible changes in parallel because
1385+
nobody owns "the protocol".
1386+
1387+
This ADR is filed alongside the typed-wasm widening roadmap in
1388+
`docs/specs/TYPED-WASM-ROADMAP.adoc` §"Tranche D" — D1 explicitly.
1389+
The reason to land *this* ADR before any concrete widening proposal
1390+
is that the coordination model is cheap now (no code), expensive
1391+
later (under conflicting pressure from multiple in-flight proposals).
1392+
"""
1393+
decision = """
1394+
Adopt a formal four-axis multi-producer coordination model:
1395+
1396+
(1) Spec authority — which artefact is the source of record:
1397+
- Today: `lib/tw_verify.ml` (OCaml) is the spec of record.
1398+
- On C5.1 closure (INT-12 / CONV-05): authority flips to
1399+
`hyperpolymath/typed-wasm` Rust verifier crate.
1400+
- The flip is itself an ADR — does not happen silently.
1401+
- Until the flip, OCaml binds; on conflict, OCaml wins.
1402+
1403+
(2) Coordinated landing protocol:
1404+
- All ABI-touching ADRs live in all three repos (originated in
1405+
one, mirrored in the other two with `Mirrors hyperpolymath/X#N`
1406+
cross-references).
1407+
- Verifier always ships parse-support FIRST.
1408+
- Producers flip to emit support together, in a coordinated
1409+
window.
1410+
- Deprecation: a removed format MUST have a deprecation window
1411+
of at least the cycle between two coordinated landings.
1412+
1413+
(3) Test parity protocol:
1414+
- Every producer maintains a fixture corpus matching the
1415+
typed-wasm cross-compat suite (INT-12 / C5.1).
1416+
- Each ABI change requires a new fixture in the cross-compat
1417+
suite from each producer.
1418+
- Verifier accepts a producer's emit only if the cross-compat
1419+
corpus is up-to-date for that producer.
1420+
1421+
(4) Conflict resolution:
1422+
- In-flight ABI proposals from different producers MUST be
1423+
serialised — no parallel ABI ADRs.
1424+
- The serialisation queue is owned by `hyperpolymath/typed-wasm`
1425+
(today via issues; future a dedicated coordination ledger).
1426+
- A proposal blocked behind another in the queue is paused, not
1427+
refused.
1428+
"""
1429+
consequences = """
1430+
- Single coordination ledger replaces ad-hoc Refs trails.
1431+
- ABI change cost goes up (more paperwork) but predictability goes
1432+
way up.
1433+
- Compatible with the existing Hypatia / gitbot rules (Hypatia
1434+
DOC-FORMAT / STACK-SIGNAL apply; ISSUE-CLOSURE applies to the
1435+
shared ADR mirrors).
1436+
- This decision is PROPOSED. Owner ratification required before any
1437+
ABI ADR (ADR-020 included) lands; ADR-020 is a "first test" of
1438+
this model and its own landing exercises the protocol.
1439+
- Cross-references the typed-wasm roadmap in
1440+
`docs/specs/TYPED-WASM-ROADMAP.adoc` §"D1".
1441+
"""
1442+
references = [
1443+
"docs/specs/TYPED-WASM-INTERFACE.adoc",
1444+
"docs/specs/TYPED-WASM-ROADMAP.adoc",
1445+
".claude/CLAUDE.md §Hypatia and gitbot-fleet standing rules",
1446+
"hyperpolymath/typed-wasm (coordination target)",
1447+
"hyperpolymath/ephapax (second producer — coordination target)",
1448+
]

0 commit comments

Comments
 (0)