Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 165 additions & 0 deletions .machine_readable/6a2/META.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -1306,3 +1306,168 @@ references = [
"docs/PACKAGING.adoc (INT-04; the JS-package half)",
"docs/specs/SETTLED-DECISIONS.adoc (ADR-019 section)",
]

[[adr]]
id = "ADR-020"
status = "accepted"
date = "2026-05-23"
ratified = "2026-05-24"
title = "Ownership-section schema versioning (0xAF sentinel + u8 version)"
context = """
The `affinescript.ownership` Wasm custom section has been frozen at
v1 since typed-wasm Level 10 closure (2026-04-19) and is emitted in
identical form by AffineScript (`lib/codegen.ml`) and by ephapax
(`hyperpolymath/ephapax/src/ephapax-wasm`). The Rust verifier in
`hyperpolymath/typed-wasm` consumes it as the cross-compat target.

V1 has *no version field*. Today this is fine because both
producers are at v1 and the verifier expects v1 unconditionally.
The first multi-producer ABI change — when L1–6 / L14–16 emission
lands — will need version discrimination. Adding the version field
*now*, while we are still at v1, is much cheaper than retrofitting
it under load.

This ADR is filed alongside the broader typed-wasm widening roadmap
in `docs/specs/TYPED-WASM-ROADMAP.adoc` §"Tranche B".
"""
decision = """
Move from v1 (unversioned) to v2 (versioned, with sentinel):

v1 payload:
u32 entry_count
entry*

v2 payload:
u8 version_tag ; 0xAF — "AffineScript Format"
u8 version ; 0x02 for v2.0
u32 entry_count
entry* ; same entry shape as v1; no new entry fields in v2.0

The 0xAF sentinel is byte-distinct from any plausible v1 entry-count
low byte for a module with ≤ 0xAE entries — i.e. practically every
module in existence. v1 readers fail cleanly on a v2 section (bad
entry-count); v2 readers see the sentinel and dispatch.

Coordinated landing protocol:
1. Land ADR-020 in `hyperpolymath/affinescript` (this repo).
2. Mirror ADR in `hyperpolymath/typed-wasm` (Rust verifier
dispatches on sentinel; accepts both v1 and v2 during
migration).
3. Mirror ADR in `hyperpolymath/ephapax` (second producer emits
v2).
4. Verifier ships v2-parse FIRST. Producers stay on v1 emit.
5. Once verifier is deployed (in both OCaml + Rust paths),
producers flip to v2 emit together.
6. After a deprecation window, v1-parse path is removed from
verifier.

The v2.0 entry shape is identical to v1 — this ADR introduces no
new ownership kinds, no new per-entry fields. Future widening
(e.g. v2.1 adding a region-id field per entry) is a separate ADR.
"""
consequences = """
- Forward-compatible: any future widening (L1–6 / L14–16 carriers,
per-entry annotations) can negotiate via the version byte without
another sentinel-or-no-sentinel war.
- v1 callers fail cleanly on v2 sections (bad entry-count parse) —
no silent corruption.
- Single coordinated landing across three repos; complexity is in
the coordination, not the bytes.
- This decision is PROPOSED. Land or supersede explicitly; do not
silently drift.
- Cross-references the typed-wasm roadmap in
`docs/specs/TYPED-WASM-ROADMAP.adoc` §"B1".
"""
references = [
"docs/specs/TYPED-WASM-INTERFACE.adoc",
"docs/specs/TYPED-WASM-INTERFACE.a2ml",
"docs/specs/TYPED-WASM-ROADMAP.adoc",
"lib/codegen.ml (build_ownership_section, ~line 159)",
"lib/tw_verify.ml (parse_ownership_section)",
"hyperpolymath/typed-wasm (Rust verifier crate — coordination target)",
"hyperpolymath/ephapax (second producer — coordination target)",
]

[[adr]]
id = "ADR-021"
status = "accepted"
date = "2026-05-23"
ratified = "2026-05-24"
title = "Multi-producer ABI coordination model for typed-wasm carriers"
context = """
typed-wasm is a separate, language-agnostic compilation target with
its own repository (`hyperpolymath/typed-wasm`) and its own
producers (today: AffineScript + ephapax; potentially more later).
The `affinescript.ownership` custom section is the first
multi-producer ABI we own.

Today coordination happens by `Refs #N` across repositories and
good intent. As the typed-wasm widening proposals land (ADR-020
schema versioning, then carriers for L1–6 and L14–16), this
informal model will break. The session-note record makes clear that
each ABI conversation has been one-off and ad-hoc; the failure mode
is two producers landing incompatible changes in parallel because
nobody owns "the protocol".

This ADR is filed alongside the typed-wasm widening roadmap in
`docs/specs/TYPED-WASM-ROADMAP.adoc` §"Tranche D" — D1 explicitly.
The reason to land *this* ADR before any concrete widening proposal
is that the coordination model is cheap now (no code), expensive
later (under conflicting pressure from multiple in-flight proposals).
"""
decision = """
Adopt a formal four-axis multi-producer coordination model:

(1) Spec authority — which artefact is the source of record:
- Today: `lib/tw_verify.ml` (OCaml) is the spec of record.
- On C5.1 closure (INT-12 / CONV-05): authority flips to
`hyperpolymath/typed-wasm` Rust verifier crate.
- The flip is itself an ADR — does not happen silently.
- Until the flip, OCaml binds; on conflict, OCaml wins.

(2) Coordinated landing protocol:
- All ABI-touching ADRs live in all three repos (originated in
one, mirrored in the other two with `Mirrors hyperpolymath/X#N`
cross-references).
- Verifier always ships parse-support FIRST.
- Producers flip to emit support together, in a coordinated
window.
- Deprecation: a removed format MUST have a deprecation window
of at least the cycle between two coordinated landings.

(3) Test parity protocol:
- Every producer maintains a fixture corpus matching the
typed-wasm cross-compat suite (INT-12 / C5.1).
- Each ABI change requires a new fixture in the cross-compat
suite from each producer.
- Verifier accepts a producer's emit only if the cross-compat
corpus is up-to-date for that producer.

(4) Conflict resolution:
- In-flight ABI proposals from different producers MUST be
serialised — no parallel ABI ADRs.
- The serialisation queue is owned by `hyperpolymath/typed-wasm`
(today via issues; future a dedicated coordination ledger).
- A proposal blocked behind another in the queue is paused, not
refused.
"""
consequences = """
- Single coordination ledger replaces ad-hoc Refs trails.
- ABI change cost goes up (more paperwork) but predictability goes
way up.
- Compatible with the existing Hypatia / gitbot rules (Hypatia
DOC-FORMAT / STACK-SIGNAL apply; ISSUE-CLOSURE applies to the
shared ADR mirrors).
- This decision is PROPOSED. Owner ratification required before any
ABI ADR (ADR-020 included) lands; ADR-020 is a "first test" of
this model and its own landing exercises the protocol.
- Cross-references the typed-wasm roadmap in
`docs/specs/TYPED-WASM-ROADMAP.adoc` §"D1".
"""
references = [
"docs/specs/TYPED-WASM-INTERFACE.adoc",
"docs/specs/TYPED-WASM-ROADMAP.adoc",
".claude/CLAUDE.md §Hypatia and gitbot-fleet standing rules",
"hyperpolymath/typed-wasm (coordination target)",
"hyperpolymath/ephapax (second producer — coordination target)",
]
47 changes: 47 additions & 0 deletions docs/TECH-DEBT.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,53 @@ names the INT-01..12 track)
|DOC-08 |MONITOR rule: reject backend-breadth over-claims |DONE (Hypatia/
gitbot DOC-FORMAT)
|DOC-09 |MONITOR rule: reject "production-ready"/stdlib-% over-claims |DONE
|DOC-10 |`docs/standards/TESTING.adoc` — authoritative test taxonomy
(alcotest unit / alcotest E2E / fixture / golden / smoke / microbench),
PR-level expectations, deferred-regression-test protocol; replaces stale
`docs/guides/TESTING-REPORT.adoc` (2025-12-29 snapshot, 47/27 pass rate,
contradicted by live gate since early 2026 — retired in-place with redirect)
|*DONE 2026-05-23*
|DOC-11 |`docs/standards/PANIC-ATTACK.adoc` — SOP for the estate compliance
scanner (disposition vocabulary fix/accept/suppress/defer; standing-
suppression record format; weekly cron via
`.github/workflows/panic-attack.yml`, ad-hoc via `just panic`, pre-release
per RELEASE.md) |*DONE 2026-05-23*
|DOC-12 |Bench harness + coverage instrumentation — `bench/` dune stanza
under `@bench` alias (lex / parse / typecheck+quantity / codegen-wasm
sweeps); CI jobs `bench-visibility` + `coverage-visibility` upload
artifacts; `bisect_ppx` added as with-test depend; `just bench` /
`just bench-record` / `just coverage` recipes. Visibility-only — no
merge-blocking gate today (ratchet prerequisite: calibrated baseline).
|*DONE 2026-05-23*
|DOC-13 |`docs/specs/TYPED-WASM-INTERFACE.adoc` (+ `.a2ml` machine
companion) — *formal* spec of the AS↔typed-wasm interface v1
(carrier binary layout, enforced levels L7+L10+L13, producer/
consumer machinery file pointers, CLI surface, test coverage,
multi-producer responsibilities, deliberate non-features
NF-001..NF-005). Supersedes the §"actual AS↔typed-wasm contract"
paragraph in ECOSYSTEM.adoc as the source of truth. |*DONE 2026-05-23*
|DOC-14 |`docs/specs/TYPED-WASM-ROADMAP.adoc` (+ `.a2ml`) — widening
roadmap making typed-wasm a natural and optimal target. Tranches
A (ergonomics) / B (schema hygiene; ADR-020) / C
(compiler-side foundations for L1–6 / L14–16) / D (multi-producer
ABI; ADR-021) / E (cross-compat closure). Per-tranche
work-items, blockers, recommended sequencing. |*DONE 2026-05-23*
|DOC-15 |ADR-020 + ADR-021 — typed-wasm widening foundations.
ADR-020: ownership-section schema versioning (0xAF sentinel + u8
version). ADR-021: multi-producer ABI coordination model (four
axes — spec authority / coordinated landing / test parity /
conflict resolution). Both *ACCEPTED + RATIFIED 2026-05-24*.
META.a2ml entries were lost in PR #344's squash-merge; restored
here. |*ACCEPTED 2026-05-24*
|DOC-16 |`docs/specs/TYPED-WASM-COORDINATION-LEDGER.adoc` + `.a2ml`
— cross-repo coordination queue mandated by ADR-021. AffineScript's
local copy of the serialised queue; sister repos hold mirrors;
typed-wasm copy is canonical on disagreement. Q-001 = ADR-020
v1→v2 schema versioning, in state `verifier-parse-shipping`
(AffineScript-side parse-support shipped via `claude/tw-verify-v2-
parse`; awaiting sister-repo PRs for the coordinated emit-flip).
Files were lost in PR #344's squash-merge; restored here.
|*DONE 2026-05-24*
|===

MONITOR enforcement (DOC-08/09): any PR re-introducing backend-breadth,
Expand Down
122 changes: 122 additions & 0 deletions docs/specs/TYPED-WASM-COORDINATION-LEDGER.a2ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# SPDX-License-Identifier: MPL-2.0
# SPDX-FileCopyrightText: 2026 hyperpolymath
#
# TYPED-WASM-COORDINATION-LEDGER.a2ml — machine-readable form of
# the cross-repo coordination ledger mandated by ADR-021.
#
# AffineScript's local copy. Sister repos hold mirrors. If they
# disagree, the typed-wasm copy is canonical (ADR-021 axis 4).

[metadata]
title = "Typed-WASM Multi-Producer Coordination Ledger"
human-companion = "docs/specs/TYPED-WASM-COORDINATION-LEDGER.adoc"
mandate = "ADR-021 (accepted 2026-05-24)"
local-copy-for-repo = "hyperpolymath/affinescript"
canonical-when-disagree = "hyperpolymath/typed-wasm"
last-updated = "2026-05-24"

# ──────────────────────────────────────────────────────────────────────────────
# State machine (states a slot may be in)
# ──────────────────────────────────────────────────────────────────────────────

[[state]]
id = "queued"
description = "ADR not yet ratified in all three repos"

[[state]]
id = "verifier-parse-shipping"
description = "ADR ratified; verifier-parse PRs in flight"

[[state]]
id = "producer-emit-flipping"
description = "all verifiers ship parse; producers flipping emit atomically"

[[state]]
id = "landed"
description = "all producers + verifiers on the new version"

[[state]]
id = "deprecation-window"
description = "at least one coordinated cycle elapsed; deprecated version still parsable"

[[state]]
id = "closed"
description = "deprecated version dropped from verifier-parse"

# ──────────────────────────────────────────────────────────────────────────────
# Rules (mirror ADR-021 + ledger conventions)
# ──────────────────────────────────────────────────────────────────────────────

[rules]
parallel-abi-proposals = "forbidden — at most one slot active at a time"
local-copies-must-agree = true
canonical-on-disagreement = "hyperpolymath/typed-wasm"
mirror-discipline-window-days = 1
verifier-first-rule = "no producer-emit PR in ANY repo before ALL repos ship verifier-parse"
cross-compat-parity = "no promotion to 'landed' without each producer's fixture corpus updated"

# ──────────────────────────────────────────────────────────────────────────────
# Live queue
# ──────────────────────────────────────────────────────────────────────────────

[[queue-entry]]
slot = "Q-001"
title = "ADR-020 — ownership-section schema versioning"
state = "verifier-parse-shipping"
state-since = "2026-05-24"
owner = "@hyperpolymath"

[queue-entry.adr]
local = { repo = "hyperpolymath/affinescript", id = "ADR-020", status = "accepted", file = ".machine_readable/6a2/META.a2ml" }
mirror-typed-wasm = { repo = "hyperpolymath/typed-wasm", status = "to-be-filed" }
mirror-ephapax = { repo = "hyperpolymath/ephapax", status = "to-be-filed" }

[queue-entry.change]
summary = "Move affinescript.ownership from v1 (unversioned) to v2 (0xAF sentinel + u8 version + entry_count + entries)"
v1-format = "u32 entry_count | entry*"
v2-format = "u8 0xAF | u8 version (0x02) | u32 entry_count | entry*"
v2-entry-shape = "identical to v1 (no new fields in v2.0)"
v1-on-v2-section = "fails cleanly (bad entry-count parse)"

[queue-entry.verifier-work]
local = "lib/tw_verify.ml accepts both v1 and v2 (THIS SESSION, B1)"
typed-wasm = "Rust verifier accepts both v1 and v2"
ephapax = "second producer's verifier (if any) accepts both"

[queue-entry.producer-work]
local = "emitter stays on v1; flip deferred until typed-wasm + ephapax verifier-parse deployed"
ephapax = "emitter stays on v1; flip together with this repo"

[queue-entry.cross-compat-fixtures]
local = "tests/typed-wasm-cross-compat/ corpus added THIS SESSION (E1)"
typed-wasm = "sister-repo PR commits binaries + extends crates/typed-wasm-verify/tests/cross_compat.rs"

[queue-entry.deprecation]
v1-emit-stays-for = "at least one coordinated cycle after v2 emit lands"
v1-parse-stays-for = "indefinite (backward compatibility)"

[queue-entry.blocked-on]
items = ["sister-repo ADR mirrors", "verifier-parse-support PRs in typed-wasm + ephapax"]

# ──────────────────────────────────────────────────────────────────────────────
# Queued (paused, not refused)
# ──────────────────────────────────────────────────────────────────────────────

# (none at present)

# ──────────────────────────────────────────────────────────────────────────────
# Closed
# ──────────────────────────────────────────────────────────────────────────────

# (none at present)

# ──────────────────────────────────────────────────────────────────────────────
# Cross-references
# ──────────────────────────────────────────────────────────────────────────────

[cross-references]
adr-020 = ".machine_readable/6a2/META.a2ml [[adr]] id=ADR-020"
adr-021 = ".machine_readable/6a2/META.a2ml [[adr]] id=ADR-021"
interface-spec = "docs/specs/TYPED-WASM-INTERFACE.adoc"
roadmap = "docs/specs/TYPED-WASM-ROADMAP.adoc"
human-companion = "docs/specs/TYPED-WASM-COORDINATION-LEDGER.adoc"
Loading
Loading