Skip to content

Exploration: .affex — face-bridging file format for cross-exolanguage interop #84

@hyperpolymath

Description

@hyperpolymath

⚖️ Closure rule — requirements-target issue

This issue is the requirements-target for .affex. It does not close when a PR lands, when a draft spec is written, or when implementation begins. It closes only when Claude and the user explicitly and mutually agree, in a recorded exchange, that the issue's spec has been satisfied by the work that has landed.

Until that mutual agreement: this issue stays open as the canonical definition of done for .affex. PRs / drafts / partial implementations link back to it but do not close it.

This rule applies to every requirements-target-labelled issue in this repo and (per the user's request) to the corresponding project-board state.

Type: Exploration / sketch idea, not yet specified.
Surfaced by: cross-referenced from hyperpolymath/accessibility-everywhere PR #16 work, 2026-05-03. Externalising it here so it survives across Claude Code sessions and can be developed in a dedicated thread.

Problem statement

AffineScript supports multiple faces (canonical, cafe, jaffa, lucid, pseudo, rattle, …). All faces lower to canonical, so the compiler doesn't care — but humans reading code in a face they don't speak are stuck.

"Someone fluent in canonical and rattle looks at lucid or pseudo and can't tell what is going on."

The friction shows up:

  • At review time: PRs touching files in unfamiliar faces become opaque to teammates who use different faces.
  • At transpile time: when sections need to move between faces (e.g. rattle → canonical, or canonical → cafe for ergonomics), the writer has to mentally re-encode the whole file.
  • At onboarding: a programmer joining an estate has to learn N faces just to read existing code, even though semantically there is one language.

Half-recalled sketch idea: .affex

Original framing (user, paraphrased): a file extension and file type — .affex = "affine exolanguage", in the sense that the named faces (rattle, lucid, etc.) are exolanguage variants of canonical AffineScript. The file format would have properties analogous to ABI/FFI conventions, but applied between AffineScript faces rather than between distinct languages.

Goals:

  • Continuity of coding in .affine files for the programmer using their preferred face — no need to leave their face to interop.
  • ABI/FFI-like benefits between faces — predictable bridging contract instead of ad-hoc translation.
  • Non-canonical-language-system support — i.e., lifts the bridging concern out of canonical itself and into a dedicated layer.

Speculative shapes (not specified, just where the design space looks open)

These are Claude's extrapolations from the problem statement — not part of the original sketch — to seed the dedicated discussion thread:

  1. Rosetta-stone listing: a .affex file holds the same source in N faces side-by-side (or as a primary face + per-other-face footnotes). Reader picks the column they speak. Auto-generatable from the lowering pass since the compiler already has the bidirectional info.
  2. Canonical-equivalence annotations: a .affex file pairs face source with the canonical AST it lowers to, with shape correspondences marked (which canonical decl maps to which face span). Helps a reader translate small windows on demand instead of the whole file.
  3. Face-interop manifest: .affex declares how a face's idioms (e.g. rattle's indentation blocks) correspond to canonical structures, so tooling can render any .affine in any reader-preferred face. The "ABI" analogue: contract for round-tripping syntax.
  4. Mixed: an .affex file can both be hand-written (for tricky cross-face boundaries the compiler can't auto-generate cleanly) and be auto-generated (for bulk).

Where this fits in the cardinal stance

The cardinal stance for the AffineScript ecosystem is "affine is the heart" — canonical is the centre, faces are peripheral. .affex reinforces rather than competes with this: canonical stays the lowering target, faces remain surface conveniences, and .affex is a strictly additive bridging layer that makes the periphery liveable without diluting the core.

What this is NOT

  • Not a Affine↔non-Affine FFI mechanism. That's a separate concern (cross-package imports / extern decls), currently handled by per-package Externs.affine shims in the accessibility-everywhere migration. .affex does not solve or replace that.
  • Not blocking any current migration. Estates working in a single face (e.g. accessibility-everywhere, all canonical per its convention lock) feel zero friction here. .affex only earns its keep once an estate has multi-face adoption.
  • Not a compiler concern. The compiler already lowers all faces to canonical AST — it doesn't need .affex. The audience is humans.

What's needed before this can be built

  • Pick one of the speculative shapes above, or define a new one
  • File extension policy: is .affex parsed by the compiler at all, or is it a docs/tooling artifact only?
  • If compiler-aware: how does it relate to the existing face lowering pipeline?
  • If tooling-only: which tool generates / consumes it (LSP? CLI? web renderer)?
  • Generation strategy: hand-written, auto, or both?
  • Estate-scope: per-package, per-monorepo, or freestanding?

Status

Parked. Not blocking any migration. Will be developed in a dedicated thread when a concrete trigger surfaces (probably first multi-face adoption inside an estate).

🤖 Drafted by Claude Code in a session where the original sketch resurfaced; externalised to this issue so it cannot get lost again. See feedback_affinescript_conventions.md "mentioned-but-undefined" section in the user's auto-memory for the local pointer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestmajorMajor issue — significant scope, broader impact than a feature/bugquestionFurther information is requestedrequirements-targetRequirements-target issue — only closes when Claude and user mutually agree the spec is satisfied

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions