# SPEC.yaml — hexa-lang authoritative decision record (SSOT) # # This file is the single source of truth for hexa-lang language/toolchain # decisions. SPEC.md is auto-generated from this file via tool/render_spec.hexa. # # Schema version is incremented on breaking shape changes only. schema_version: 1 title: hexa-lang specification status: directional (decisions locked; P0 stage-1 source-side OOM closed at current scale — A1+A2 → peak ~782 MB verified; deployed `hexa_real` current at HEAD `dae438ee`) last_updated: 2026-05-11 authoritative_rfcs: - proposals/rfc_017_atlas_n6_embedding_and_strict_lint.md - proposals/rfc_018_native_codegen_spec.md - proposals/rfc_019_error_diagnostics_spec.md - proposals/rfc_020_enum_payload_variants.md - proposals/rfc_021_daemon_mode.md - proposals/rfc_022_async_model.md - proposals/rfc_023_firmware_linker_spec.md # ───────────────────────────────────────────────────────────────────────────── # Tree layout (what trees exist and what each is for) # ───────────────────────────────────────────────────────────────────────────── tree_layout: decision_2026_05_09: keep_compiler_tree_alongside_self rationale: | self/ is the existing self-host upstream (parser/typechecker/IR in hexa, transpiled to self/native/hexa_cc.c). compiler/ is the new ground-up native compiler per RFC-018 (5-stage IR, atlas static embed, direct codegen). Both trees coexist; compiler/ migrates patterns from self/ and shares language upstream features (e.g., enum payloads via RFC-020). trees: self_: role: existing self-host upstream contents: - self/main.hexa # `hexa` CLI entry - self/lexer.hexa - self/parser.hexa - self/type_checker.hexa - self/ir/ # self-host IR (instr/types/builder/lowering) - self/native/hexa_cc.c # transpiled C compiler (~20k lines) regenerate_via: hexa cc --regen compiler_: role: new ground-up native compiler (RFC-018) contents: - compiler/main.hexa - compiler/lex/tokens.hexa - compiler/parse/ast.hexa - compiler/ir/hir.hexa - compiler/ir/mir.hexa - compiler/ir/lir.hexa - compiler/diag/catalog.hexa status: phase A0 skeleton # ───────────────────────────────────────────────────────────────────────────── # Language features — enum / sum types (Decision 2026-05-09) # ───────────────────────────────────────────────────────────────────────────── language_features: enum_first_principle: decision: enum_100_percent_first rationale: | Wherever payload-free enums (Rust unit-style) suffice, prefer them over string discriminators. Improves type safety, compile-time exhaustiveness, and parity with self/ir/instr.hexa's CmpKind pattern. applies_to: - compiler/lex/tokens.hexa::TokenKind - compiler/parse/ast.hexa::ItemKind, ExprKind - compiler/diag/catalog.hexa::Severity, FixItKind exempted: - structures whose variants need data payloads — until RFC-020 lands and is verified end-to-end (parser→typechecker→codegen) hexa_lang_upstream_first: decision: fix_at_upstream_not_workaround rationale: | When a language gap is hit (e.g., enum payload codegen), fix it in hexa-lang upstream (self/ + self/native/hexa_cc.c) rather than working around in downstream tools. Maintains language-level uniformity. examples: - RFC-020 — enum payload variant support enum_payload_status: summary: partial_working_needs_completion confirmed_working: - "'enum Shape { Circle(Int), Rect(Int), Unit }' parser declaration" - "'match s { Shape::Circle(r) -> ... }' pattern binding (interpreter mode)" - "7 match patterns: wildcard, literal, binding, variant, struct, tuple, guard" confirmed_missing: - "'E::Variant(x)' construction syntax (parse_primary line 3016)" - "typechecker variant payload type registry" - "typechecker pattern binding — variable scope introduction" - "hexa_cc.c struct/union codegen + payload extraction" - "multi-field variants entirely" target_design: single_field_payload_plus_struct_embed rfc: RFC-020 multi_field_status: deferred_to_later_rfc # ───────────────────────────────────────────────────────────────────────────── # Foundational decisions (from RFC-017/018) # ───────────────────────────────────────────────────────────────────────────── foundation: language_kind: decision: native_compiled rationale: | hexa-lang transitions from interpreter (hexa_interp) to a native compiler. Interpreter remains only as bootstrap stage0. rejected: - interpreter_only # too slow for CI/LSP at atlas size 4.2 MB - interpreter_with_cache # still per-process overhead, not zero references: [RFC-017] atlas_embedding: decision: static_baked_into_compiler rationale: | atlas.n6 + atlas.append.*.n6 are merged at compiler build time and embedded as a packed const into the compiler binary (rodata section). Hash pinned via ATLAS_HASH constant. properties: runtime_cost_ms: 0 compiler_binary_overhead_mb: 1_to_2 drift_handling: ci_auto_rebuild_compiler override: hexa.toml [atlas] path = "..." (rebuilds compiler) rejected: - read_atlas_at_runtime # 200ms per invocation on 4.2 MB atlas - mtime_disk_cache # acceptable but inferior to static references: [RFC-017 §4.5] execution_blocking_lint: decision: strict_compile_time_fatal rationale: | Python SyntaxError + TypeScript strict model. No binary is produced when any S0–S5 or S8 check fails. S6/S7 only fatal when annotated. stages: S0_parse: { fatal: true, description: "syntax / lex" } S1_resolve: { fatal: true, description: "atlas P/C/L/E node existence" } S2_bind: { fatal: true, description: "scope / variable binding" } S3_type: { fatal: true, description: "nominal types, generics" } S4_domain: { fatal: true, description: "ℝ/ℕ/ℤ/ℂ domain consistency" } S5_units: { fatal: true, description: "dimensional analysis" } S6_equational: { fatal: opt_in_via_at_verify, description: "LHS=RHS canonical, sample counter-example" } S7_proof: { fatal: opt_in_via_at_prove, description: "self-prover only (no Z3)" } S8_citation: { fatal: true, description: "atlas L[*] citation present where required" } references: [RFC-017 §3.1] codegen_strategy: decision: direct_codegen rationale: | No LLVM, no C-transpile. Direct source → mach pipeline keeps every IR stage atlas-aware and removes external toolchain dependencies. pipeline: - lex - parse - resolve # S0–S2 - check # S3–S5, S8 - lower # AST → HIR - mono # generic monomorphization - ssa # MIR (CFG, SSA) - optimize # const-fold, dce, conservative inline - regalloc # LIR (target-specific) - emit # asm or machine code - link # ELF / Mach-O references: [RFC-018] # ───────────────────────────────────────────────────────────────────────────── # Targets (Decision 1) # ───────────────────────────────────────────────────────────────────────────── targets: decision: parallel_dual rationale: User runs both Apple Silicon and Linux; both must work from day one. tier_0_concurrent: - triple: arm64-apple-darwin role: primary dev environment - triple: x86_64-linux-gnu role: CI / docker / runners tier_1_followup: - triple: arm64-linux-gnu # ARM containers - triple: thumbv7em-none-eabihf # Cortex-M4F (firmware: hexa-rtsc, hexa-chip) - triple: riscv32imac-unknown-none-elf # RISC-V 32-bit (firmware) tier_2_later: - triple: wasm32-unknown-unknown - triple: riscv64-linux-gnu - triple: thumbv6m-none-eabi # Cortex-M0/M0+ - triple: thumbv7m-none-eabi # Cortex-M3 - triple: xtensa-esp32-none-elf # ESP32 (firmware: hexa-antimatter) # ───────────────────────────────────────────────────────────────────────────── # macOS Mach-O gate (Decision 2026-05-09; warn-only Phase B) # ───────────────────────────────────────────────────────────────────────────── macos_machO_gate: decision_2026_05_09: warn_only_phase_B phases: A_in_repo: landed B_user_wrapper: user_session # $HOME/.hx/bin/hexa edit, separate C_strict_block: deferred # awaits hook-layer migration reference_wrapper: tool/wrappers/hexa_top_wrapper.sh lint_rule: tool/lint_macho_gate.hexa # LINT-MACHO-1 (.hexa exec sites) invocation_lint: tool/lint_macho_gate_invocations.hexa # LINT-MACHO-2 (sh + CI surface) declarative_spec: spec/hexa_macho_gate.spec.yaml # falsifier SSOT (F-HEXA-MACHO-1/2) lint_bypass_env: HEXA_LINT_MACHO_DISABLE=1 # per-session escape hatch (Phase A) warning_template: | ai-native: hexa invocation without --Mach-O on macOS. This call uses the stage0 interpreter; no native Mach-O codegen is produced. Use --Mach-O ONLY when you genuinely need a native arm64 binary (e.g. `hexa build --emit=exec` for a release). For hooks, probes, lints, and most dev work this warning is informational — no action required. Set HEXA_TARGET_MACHO=1 in your shell to silence per-session. silenced_by: - cli_flag: --Mach-O - env_var: HEXA_TARGET_MACHO=1 rationale: | 94% of macOS hexa_interp.real.real invocations don't need native Mach-O codegen (hooks/handlers/probes). The flag makes the intent explicit; downstream tooling can route accordingly. block-0 in Phase B avoids breaking claude-bind hooks; Phase C strict mode follows hook-layer migration. # ───────────────────────────────────────────────────────────────────────────── # Bootstrap (Decision 2) # ───────────────────────────────────────────────────────────────────────────── bootstrap: stage0: decision: existing_hexa_interp rationale: | Existing build/hexa_interp.* interpreter executes hexa-written native compiler source to produce stage1. Zero new external dependencies. accepts_temporary_external: system_as_and_ld_during_stage0_to_stage1_only stages: stage_0: { tool: hexa_interp, output: "stage1 native compiler binary" } stage_1: { tool: stage_0, output: "stage2 compiler (self-compiled by stage1)" } stage_2: { tool: stage_1, output: "stage3 compiler" } stage_3: { tool: stage_2, fixed_point: "byte-equal to stage2" } retire_interpreter_after: stage3_settled # Witness paths for stage 2 self-compile + stage 3 fixed-point harness. # Both harnesses exit 0 (DEFERRED) when their inputs are missing so CI # does not block while A2 (stage 1 binary) is in flight. See # doc/bootstrap_stage_2_3.md for the full failure-mode triage list. witness_paths: stage_2_smoke: tests/bootstrap/stage_2_smoke.hexa stage_3_fixed_point: tests/bootstrap/stage_3_fixed_point.hexa driver: tests/bootstrap/run_bootstrap.sh doc: doc/bootstrap_stage_2_3.md expected_artifacts: stage_2_binary: /tmp/hexa_stage_2 stage_3_binary: /tmp/hexa_stage_3 m0_asm_emitted: /tmp/m0_s2.s # Interpret-mode after stage3 (Decision 2026-05-09 — D+B) # # User wants `hexa run` to keep working as an interpret-style entrypoint # after the standalone interpreter is retired. Two paths chosen: # # D (default) `hexa run x.hexa` ≡ build + exec(out_path). # Zero extra LOC — driver already calls system as/ld and # the resulting binary. atlas static-embed makes the cold # path acceptable. # # B (opt-in) --interp / `hexa repl`. AST evaluator at # compiler/eval/ast_eval.hexa walks the typed AST after # lex/parse/check. Reuses the entire frontend; only adds # ~200-500 LOC for the per-ExprKind evaluate switch. # Used for fast startup, REPL, debugging, hot-edit cycles. # # Rejected: # A — keep a separate ~20k-LOC interpreter alongside the native # compiler. Two code bases, no. # C — JIT (in-memory mmap + emit + jump). Deferred; nice-to-have but # per-platform complexity (page rwx, codesign on macOS). # # No tracing GC interpreter, no separate parser. Both paths share the # compiler/lex + compiler/parse + compiler/check infrastructure. interpret_mode_post_retirement: decision: D_plus_B_combined primary: build_and_exec # `hexa run x.hexa` ≡ build + system exec optional: ast_evaluator # `--interp` / `hexa repl` via compiler/eval/ rejected: [keep_separate_interpreter, jit_first] notes: | ast_evaluator is implemented post-stage3 inside compiler/eval/ — not a fork of self/ tree, but a thin walker over compiler/parse + compiler/check output. # ───────────────────────────────────────────────────────────────────────────── # Language for diagnostics (Decision 3) # ───────────────────────────────────────────────────────────────────────────── diagnostics_language: decision: english_only rationale: | User explicitly fixed ENGLISH ONLY. Korean i18n option closed permanently. Catalog message keys, templates, explain output all English. no_i18n: true applies_to: - compiler diagnostics - hexa explain output - error catalog templates - stdlib documentation excludes: - design RFCs and meta documents (may remain in author's preferred language) # ───────────────────────────────────────────────────────────────────────────── # Opt-out grace label (Decision 4) # ───────────────────────────────────────────────────────────────────────────── opt_out: decision: at_grace_annotation_only syntax: '@grace(HXCODE, until="YYYY-MM-DD", reason="...")' required_fields: [error_code, until, reason] forbidden: - cli_flag_to_disable_strict # --unsafe etc. rejected - environment_variable_override # HEXA_STRICT=0 rejected scope_rule: applies_to_immediately_following_item_only expiry_behavior: | On `until` date pass, compiler emits HX9001 and refuses to build until @grace is removed (item fixed) or `until` extended (which is itself a deliberate decision visible in PR review). # AI-native warn + explicit user consent (Decision 2026-05-09) # # @grace is allowed but NEVER silent. Every @grace site emits an # AI-native HX9000 warning at compile time, and the build is gated # on explicit user consent — a CI/PR-level acknowledgement signal. # Compilers/linters that swallow the warning silently are non-conformant. ai_native_warn_policy: diagnostic: HX9000 severity: Warning language: english # Decision 3 — ENGLISH ONLY message_template: | ai-native: @grace at {file}:{line} suppresses {error_code} until {until}. Reason: "{reason}". Suppressed diagnostic ({error_code} — {suppressed_title}): {suppressed_message} ({suppressed_severity_original}; downgraded by @grace until {until}.) This is a TEMPORARY debt — the author is responsible for resolving the underlying issue before the expiry date. Reviewer acknowledgement REQUIRED before merge. style: ai_native # short, specific, actionable; not generic warn # Decision 2026-05-09 (refinement): HX9000 must include the FULL rendered # message of the diagnostic it suppresses, not just the HX code. This # gives reviewers (and AI assistants reading the output) immediate # visibility into WHAT is being silenced — not just that something is. # # The check pass that detects a @grace site: # 1. Constructs the diagnostic it would normally emit (real path) # 2. Renders it via render_short / render_pretty # 3. Stuffs the rendered text into HX9000.args.suppressed_message # 4. Passes the original DiagSpec.title and DiagSpec.severity in # args.suppressed_title / args.suppressed_severity_original # 5. Does NOT emit the original diagnostic (suppression intact) # 6. Emits HX9000 alone with the suppressed-detail block populated # # If the suppressed diagnostic would have produced did-you-mean or # fix-it info, those fields are inlined into suppressed_message too — # the reviewer should see the full picture. suppressed_diagnostic_inlining: args_keys: [suppressed_title, suppressed_message, suppressed_severity_original] render_format: short # multi-line pretty would clobber HX9000 layout include_did_you_mean: true include_fix_it: true rationale: | AI assistants and human reviewers should not have to guess what @grace is hiding. Inlining the full original message at the suppression site means a single HX9000 diagnostic carries everything needed to evaluate whether the bypass is reasonable. user_consent_mechanism: pr_trailer: 'Acked-grace: HXxxxx by ' commit_trailer: 'acked-grace: by ' ci_check: | CI fails when a commit/PR introduces or modifies a @grace site and no Acked-grace trailer matches the affected HX code(s). Per-site granularity. Covers both new and prior @grace lines. rationale: | User decision: bypassing strict checks must require an explicit human signoff trail. Silent @grace defeats the strict-lint model. The HX9000 warning ensures the bypass is visible at every compile, the consent mechanism ensures it is visible at every review. # ───────────────────────────────────────────────────────────────────────────── # Atlas citation strict (Decision 2026-05-09) # ───────────────────────────────────────────────────────────────────────────── # User decision: "공식 없으면 거절" — formula-bearing functions must be # bound to atlas L[*] node, otherwise compile fails. Two legal binding # paths; otherwise @grace must be used to opt out per-site. atlas_citation_strict: decision: formula_must_cite_atlas_L rationale: | A function carrying a formula annotation (@verify or @law, OR a body that references atlas L[*] nodes) is a knowledge-bearing artifact. The atlas is the single source of truth for theorems / laws; allowing formula code to drift unbound from atlas defeats the SSOT. RFC-017 §3.1 S8 already lists citation as fatal "where required"; this decision pins WHERE. required_when: - item has '@verify' annotation # caller declared it a theorem - item has '@law(...)' annotation # explicit formula docstring - item body references any L[*] node # implicit formula use legal_bindings: - syntax: '@implements(L[])' meaning: cite an already-registered atlas L node - syntax: '@discover(kind="L")' meaning: register THIS function as a NEW atlas L node (RFC-017 §5 ε self-proof; routes through compiler/discover/ staging path on prover pass) on_missing_binding: diagnostic: HX8004 severity: Error template: | formula in `{name}` has no atlas binding — add @implements(L[id]) to cite an existing law, or @discover(kind="L") to register this function as a new one. SPEC.yaml atlas_citation_strict. bypass_path: via: '@grace(HX8004, until="YYYY-MM-DD", reason="...")' notes: | Standard @grace machinery (Decision 4) covers HX8004 like any other HX code. Use ONLY when atlas drift / migration is in flight; the until date forces the binding to land before merge. EVERY @grace site emits HX9000 ai-native warning — see opt_out.ai_native_warn_policy. Bypass is never silent. promotion_from_HX8003: note: | The earlier HX8003 (Warning — "fn uses L refs but no @implements") is superseded by HX8004 (Error). HX8003 demoted or retired in the next citation pass refresh. Tooling should treat both equivalently during transition. # ───────────────────────────────────────────────────────────────────────────── # Atlas self-discovery system (Decision 5: ε) # ───────────────────────────────────────────────────────────────────────────── atlas_self_discovery: decision: epsilon_self_proof rationale: | Verified hexa code automatically registers as atlas L[*] (theorem) nodes. Atlas becomes a living theorem library that grows as the codebase grows. prover_engine: decision: in_house_prover_only # Sub 5a rationale: User mandate — zero external dependency, no Z3/CVC5. initial_capabilities: - equational_rewrite - constant_folding - sample_eval_counter_example - unit_propagation - atlas_graph_consistency deferred_capabilities: - linear_arithmetic - non_linear_arithmetic - quantifiers - real_number_induction registration_authority: decision: fully_automatic # Sub 5b rationale: User chose auto-register on prover pass; safety net is sub 5e. safeguards_baked_in: - record_verifier_version - record_proof_hash - retroactive_revalidation_on_prover_upgrade staging_pipeline: subtree: compiler/discover/ # discover.hexa + staging.hexa + promote.hexa step1_writer: write_staging # emits atlas.proposed.{date}.n6 step2_promoter: promote_to_atlas # ACTIVE — folds proposed → live atlas step2_module: compiler/discover/promote.hexa manifest_path_convention: /tmp/_promote_manifest.{date}.txt # date = UTC YYYY-MM-DD conflict_rules_applied: - fingerprint_dedup_merges_as_alias # sub 5d - id_first_wins_rejects_with_warning # sub 5d - new_emits_full_proof_hash_record # sub 5d rationale: | Step 1 of ε self-proof writes verified discoveries to a staging atlas.proposed.{date}.n6 file. Step 2 (promote_to_atlas) is now ACTIVE: invoked explicitly as a CLI/tool, it loads the live atlas index, applies decision 5d conflict resolution per proposal, and emits a fresh atlas.append.{today}.n6 shard plus a manifest at /tmp/_promote_manifest.{date}.txt summarising counts (seen / promoted_new / merged_alias / rejected_collision) and per-proposal dispositions. The retroactive sweep (sub 5e — tombstone integration on prover upgrade) remains deferred. verification_timing: decision: every_compile_every_function # Sub 5c rationale: User chose maximum coverage; performance handled by caching. mandatory_optimizations: - hash_keyed_skip_for_unchanged_functions - early_reject_for_non_theorem_shapes # side-effect, IO, mutation cost_model: prover_runs_for_every_function_in_every_compile conflict_resolution: decision: fingerprint_dedup_plus_id_first_wins # Sub 5d rules: same_canonical_form: register_under_existing_L_with_alias same_explicit_id_different_meaning: first_wins_with_warning anonymous_auto_id: derived_from_fingerprint_so_collision_implies_dedup invalidation: decision: tombstone_plus_retroactive_sweep # Sub 5e mechanism: manual_tombstone: "hexa atlas tombstone L[id] --reason=\"...\"" automatic_sweep: | On prover version upgrade, CI nightly job re-verifies every registered L node. Failing nodes are auto-tombstoned with PR open for human review. Implementation: compiler/discover/retroactive_sweep.hexa. dependency_tracking: | Status: cascade_tombstones lands. Implementation: compiler/discover/cascade.hexa. proof-hash records lemmas used. When a lemma is tombstoned (manual or sweep), dependent L nodes auto re-verify; failures cascade-tombstone with reason "cascade-tombstone: depends on L[] which was tombstoned". v1 detection is heuristic substring match in normalized_form; semantic dependency analysis (parse the S-expr and walk referenced lemma symbols + transitive closure) is v1.1 work. auto_pr_helper: | tool/auto_pr_tombstone_sweep.hexa — when retroactive_sweep + cascade_tombstones together newly tombstone >0 L nodes, this helper renders a PR body summarizing the prover version transition, counts, and per-tombstone disposition, then invokes `gh pr create`. On any failure (gh missing, no auth, no commits) it falls back to writing /tmp/_tombstone_sweep_pr_body.{date}.txt and printing the suggested command. Returns 0 always (CI infra resilience). Smoke tests should export HEXA_AUTO_PR_DRY_RUN=1 (or call auto_pr_dry_run directly) to skip the git/gh side-effects. citing_tombstoned_l_emits: HX1099_compile_fail history_kept_in: atlas.tombstones.n6 # ───────────────────────────────────────────────────────────────────────────── # Memory model (Decision 6) # ───────────────────────────────────────────────────────────────────────────── memory_model: decision: arena_v1_borrow_check_v2 current_phase: v1_arena_only rationale: | 1.x sticks to arena/region allocation — short-lived processes (compiler, verification, tools) cover 99% of cases. 2.x adds borrow check for long-lived objects without breaking arena code. v1_design: primary: function_local_arena secondary: request_scoped_arena_for_long_running # LSP, daemon static: compile_time_const_in_rodata # atlas user_heap: arena_only_no_manual # 1.x explicitly forbids manual free v2_design: add: borrow_checker_with_explicit_arena_lifetime_handles add: limited_manual_escape_hatch_for_systems_code keep: gc_will_never_be_added # rejected permanently rejected: - tracing_gc # runtime weight, pause times, dependency surface - ref_counting # cycle handling burden, atomic costs # ───────────────────────────────────────────────────────────────────────────── # Linker (Decision 7) # ───────────────────────────────────────────────────────────────────────────── linker: decision: hexa_ld_primary_with_system_fallback primary: hexa_ld fallback: system_ld_or_lld fallback_triggers: - hexa_ld_binary_missing - hexa_ld_runtime_failure - explicit_flag: "--linker=system" hexa_ld_v1_scope: # NOTE (2026-05-09): v1.1 adds Mach-O arm64 emitter alongside ELF. # NOTE (2026-05-10): v1.2 embeds ad-hoc LC_CODE_SIGNATURE so the # kernel accepts the binary on Apple Silicon without any external # `codesign` shellout. ad-hoc only — no notarization, no team-id. # See compiler/link/hexa_ld.hexa: link() autodetects ELF (0x7F ELF) # vs Mach-O 64 (CF FA ED FE) magic and dispatches accordingly. formats: [ELF, Mach-O_arm64] # v1 → ELF; v1.1 → ELF + Mach-O_arm64 capabilities: - static_linking_only - symbol_resolution # ELF: `_start`/`main`; Mach-O: `_main`/`start` - basic_relocations # v1.1: NONE only — full reloc → v1.3 - dwarf_debug_section_emit_basic # v1.1: dropped — lands v1.3 - macho_arm64_execute # v1.1: MH_EXECUTE + LC_MAIN + LC_LOAD_DYLINKER - codesign_embedded # v1.2: ad-hoc LC_CODE_SIGNATURE # (CSMAGIC_EMBEDDED_SIGNATURE wrapping # CSMAGIC_CODEDIRECTORY with # SHA-256 per-4 KiB-page hashes; # no notarization, no team-id) deferred: - dynamic_linking # LC_LOAD_DYLIB / PT_DYNAMIC → v1.3+ - link_time_optimization - section_dead_code_elim - relocation_processing_full # v1.3 - codesign_notarized # v1.4+: requires Apple Developer ID # + altool roundtrip; out of scope bootstrap_carve_out: rule: system_ld_temporarily_permitted_during_stage0_to_stage1 expires_after: stage1_native_compiler_settled # ───────────────────────────────────────────────────────────────────────────── # Migration policy (Decision 8) # ───────────────────────────────────────────────────────────────────────────── migration: decision: big_bang rationale: | User chose to fix all violations in a coordinated PR series and enable strict checks at once, rather than phased grace-based rollout. procedure: - stage1_native_compiler_settled_first - run_full_tree_lint_dump_on_existing_hexa_files - coordinated_pr_series_to_fix_all_violations - flip_strict_globally_in_one_commit fallback_for_unfixable: rule: explicit_at_grace_annotation_per_site note: | If a violation cannot be fixed in the migration window, an EXPLICIT @grace label must be added by the author with reason and until — not auto-generated by tooling. # ───────────────────────────────────────────────────────────────────────────── # Diagnostic system (RFC-019, English-only adjusted) # ───────────────────────────────────────────────────────────────────────────── diagnostics: code_format: 'HX[CCCC]' groups: HX0xxx: parse_lex # S0 HX1xxx: atlas_resolve # S1 HX2xxx: bind_scope # S2 HX3xxx: type # S3 HX4xxx: domain # S4 HX5xxx: units # S5 HX6xxx: equational # S6 HX7xxx: proof # S7 HX8xxx: citation # S8 HX9xxx: codegen_link_runtime # RFC-018 catalog_location: stdlib/diagnostics/catalog.hexa message_templates: stdlib/diagnostics/messages.hexa # English output_modes: [pretty, short, json, github] features: - error_code - precise_span - did_you_mean # Levenshtein over atlas trie + scope identifiers - fix_it - hexa_explain_subcommand - multi_error_collector_with_cascade_compression - snapshot_regression_tests references: [RFC-019] # New diagnostics surfaced 2026-05-11 — stage 1 punch list v2 C4/C5/C10/C16/C20 # promoted from silent fallbacks to real diagnostics. CLI drain wiring # (`compiler/main.hexa` post-lower `hir_to_mir_diags` drain, commit 18c6a536) # is required for HX1101/1102/1103 to surface alongside parser/check/units. new_codes_2026_05_11: HX1101: severity: Error stage: S2 title: unbound ident in lower emitted_at: "compiler/lower/hir_to_mir.hexa (_lower_hexpr ident miss, was silent _const_int_op(0))" HX1102: severity: Error stage: S2 title: unsupported pattern shape in lower emitted_at: "compiler/lower/hir_to_mir.hexa (match-arm pattern fallback)" HX1103: severity: Error stage: S2 title: unhandled HExpr kind in lower emitted_at: "compiler/lower/hir_to_mir.hexa (HExpr default fall-through)" HX2001: severity: Error stage: S2 title: undefined name emitted_at: "compiler/check/types.hexa::_types_check_call (non-Ident callee — auxiliary emit cooperating with bind.hexa's same code)" HX2003: severity: Error stage: S3 title: callee is not callable emitted_at: "compiler/check/types.hexa::_types_check_call (callee_t.kind != \"fn\" — non-fn value invoked)" # Codes referenced by name in SPEC.md prose/examples but not in the # "new" set above — enumerated here so the render_spec drift check # (diag-code-set: SPEC.md ↔ SPEC.yaml) stays clean. Full catalog is # the SSOT at catalog_location, not this file. notable_existing_codes: HX1042: severity: Error # fatal stage: S1 title: atlas node not found emitted_at: "compiler/diag/catalog.hexa (atlas P/C/L/E resolve miss)" note: canonical @grace example in SPEC.md §8 (until=2026-06-01, legacy atlas refactor) # ───────────────────────────────────────────────────────────────────────────── # Roadmap phases (rough) # ───────────────────────────────────────────────────────────────────────────── roadmap: notes: | T = stage1 native compiler settled. Months are relative. phases: A0: { effort: L, goal: backend skeleton + IR types } A1: { effort: L, goal: parser + AST atlas tagging } A2: { effort: M, goal: atlas n6 + append merger inside compiler } A3: { effort: M, goal: atlas packed const codegen + static embed } A4: { effort: S, goal: ATLAS_HASH pin + drift CI } B1: { effort: M, goal: S0–S2 fatal at compile time } B2: { effort: M, goal: S3–S4 type/domain } B3: { effort: M, goal: '@units + S5' } B4: { effort: M, goal: '@law / @implements + S8 citation' } C1: { effort: L, goal: in-house prover v0 (equational + sample-eval) } C2: { effort: L, goal: prover atlas auto-register + tombstone sweep (Decision 5) } D1: { effort: M, goal: hexa_ld v1 (static link, ELF + Mach-O) } D2: { effort: M, goal: LSP using compiler in-process index } E1: { effort: L, goal: full big-bang migration of existing .hexa tree } E2: { effort: M, goal: retire hexa_interp after stage3 fixed point } # Firmware absorption track (Decision 2026-05-10 — Option C). # F0 landed at fc6d48b2; F1..F5 in flight via background tasks #84-#87 # (stdlib/core extraction, firmware/boards/rtsc port, thumbv7em codegen, # remaining board absorptions, and RFC-023 firmware linker spec). F0: { effort: S, goal: SPEC firmware Option C + firmware/ skeleton, status: DONE, evidence: "fc6d48b2" } F1: { effort: M, goal: stdlib/core extraction from current host stdlib, status: DONE, evidence: "2026-05-10 — 16-module split" } F2: { effort: M, goal: firmware/boards/rtsc reference port, status: DONE, evidence: "2026-05-10 — option_A_full_copy" } F3: { effort: L, goal: thumbv7em-none-eabihf codegen target in compiler/codegen, status: DONE, evidence: "2026-05-10 — asm-shape only; HX1110 gate" } F4: { effort: L, goal: "firmware/boards/{chip,cern,antimatter,space} absorptions", status: DONE, evidence: "2026-05-10 — 4-board batch" } F5: { effort: M, goal: RFC-023 firmware linker spec (linker scripts + .bss/.data), status: SPEC LAND, evidence: "2026-05-10 — 2f9789e3 (implementation follow-up)" } revised_timeline: note: | Stage 1 self-compile was host-OOM blocked. Punch list v2 (doc/stage1_punch_list_v2.md, commit 86afadb0) revised the estimate from 6-10 weeks to 14-22 weeks, dominated by category A bootstrap host work. The closure round (2026-05-11) closed P0: A1 phase-arena reset + A2 in-place splice accumulator landed and deployed (~/.hx/bin/hexa_real re-promoted 774c5d32 → 41ecfb97 → dae438ee); #13 re-probe (uncapped) peak ~782 MB vs 5/10 baseline 3510 MB (4f5f8f07). A full stage-1 fixed-point re-estimate is the remaining open work (no longer host-OOM-bound). M4 freelist re-enable is an optional further reduction. stage_1_reach_old_estimate: "6-10 weeks (pre 2026-05-10)" stage_1_reach_new_estimate: "14-22 weeks (post Gap 1+2+14+15 host wall); P0 host-OOM closed 2026-05-11 — re-estimate pending" bottleneck: "stage0_2GB_memory_cap_during_spliced_self_compile (RESOLVED 2026-05-11 — A1+A2, peak ~782 MB)" # ───────────────────────────────────────────────────────────────────────────── # Fork storm prevention (Decision 2026-05-09) # ───────────────────────────────────────────────────────────────────────────── # A per-build "fork storm" occurs when build-hot hexa code calls exec() # for trivial probes (date, uname, mkdir, test, rm). Inventory on # 2026-05-09 found ~1403 exec() sites across compiler/, tool/, and tests/. # The fix is a 4-level ladder: replace exec() with named intrinsics, then # back the intrinsics with libc FFI, then with raw syscalls. See # doc/fork_storm_inventory.md for the full call-site catalog and # compiler/intrinsics/intrinsics.hexa for the v0 module. fork_storm_prevention: decision_2026_05_09: ladder_L0_to_L3_intrinsics_first goal: forks_per_build_today: "5–10" forks_per_build_target: "≤2 (only `as` and `ld`)" ladder: L0: name: fork_storm meaning: "exec() scattered at every call site; 5–10 forks/build" status: current_baseline L1: name: intrinsic_surface meaning: "exec() centralized inside named intrinsic functions; bodies still fork once" status: shipping_2026_05_09 module: compiler/intrinsics/intrinsics.hexa first_three: - now_ns - host_target - mkdir_p second_batch: - rm_rf - rm_file - getenv - path_exists - path_is_dir third_batch: - getcwd - cwd # alias of getcwd, reads better at call sites - list_dir L2: name: libc_ffi meaning: "intrinsic bodies call libc directly via hexa C-FFI; no shell" status: deferred_pending_ffi_landing L3: name: raw_syscall meaning: "intrinsic bodies emit platform syscall trap inline; zero exec()" status: deferred_post_native_v1 exec_call_inventory: snapshot_date: 2026-05-09 total_call_sites: 1403 files_touched: 265 top_categories: - { name: date, count: 160, intrinsic: now_ns_plus_format, effort: S, status: PARTIALLY_ABSORBED, v0: shell_fork, v1: clock_gettime_FFI, v2: linux_syscall_228 } - { name: rm, count: 123, intrinsic: rm_rf_and_rm_file, effort: M, status: PARTIALLY_ABSORBED, v0: shell_fork, v1: opendir_readdir_unlink_FFI, v2: linux_unlink_87_rmdir_84 } - { name: echo, count: 110, intrinsic: getenv, effort: S, status: PARTIALLY_ABSORBED, v0: shell_fork_with_name_validation, v1: libc_getenv, v2: extern_environ_walk } - { name: test, count: 85, intrinsic: path_exists_path_is_dir, effort: S, status: PARTIALLY_ABSORBED, v0: shell_fork, v1: libc_stat, v2: linux_access_21_stat_4 } - { name: uname, count: 84, intrinsic: host_target, effort: S, status: PARTIALLY_ABSORBED, v0: shell_fork, v1: compile_time_constant, v2: na_already_constant } - { name: mkdir, count: 78, intrinsic: mkdir_p, effort: S, status: PARTIALLY_ABSORBED, v0: shell_fork, v1: libc_mkdir_walk, v2: linux_mkdir_83_mkdirat_258 } - { name: pwd, count: 58, intrinsic: getcwd, effort: S, status: PARTIALLY_ABSORBED, v0: shell_fork, v1: libc_getcwd, v2: linux_syscall_79 } - { name: ls, count: 56, intrinsic: list_dir, effort: M, status: PARTIALLY_ABSORBED, v0: shell_fork, v1: opendir_readdir_FFI, v2: linux_getdents64_217 } - { name: cd, count: 45, intrinsic: call_site_rewrite, effort: M, status: PENDING } - { name: git, count: 38, intrinsic: keep_exec_v0, effort: L, status: PENDING } absorbed_site_count_v0: 752 # 160+123+110+85+84+78+58+56 — categories with v0 intrinsic shipped pending_site_count: 83 # 45+38 (cd+git) — categories awaiting future batches # ───────────────────────────────────────────────────────────────────────────── # Stdlib evolution policy (Decision 2026-05-09) # ───────────────────────────────────────────────────────────────────────────── # stdlib modules version independently of the compiler. Each module owns # its own backward-compat contract; additive helper APIs are accepted via # small focused commits with a selftest delta and an .ai.md doc append. # Major rewrites or breaking shape changes require an RFC. # # Documented patches in flight (2026-05-10) — orpheus key-recovery forge # engine downstream consumers; FFI/HTTP enablers usable by any hexa-lang # client (not orpheus-specific): # ───────────────────────────────────────────────────────────────────────────── # Stdlib core/alloc split + firmware tree (Decision 2026-05-10 — Option C) # ───────────────────────────────────────────────────────────────────────────── # Per audit doc/firmware_audit_2026_05_10.md, hexa-lang absorbs five firmware # repos (hexa-rtsc/chip/cern/antimatter/space). Rather than mixing host and # embedded code in a single stdlib (Option A) or fully separating into a # firmware/ tree alone (Option B), Option C splits stdlib into: # # stdlib/core/ — target-agnostic primitives (no alloc, no syscall) # stdlib/alloc/ — heap + arena allocators # stdlib/hal/ — hardware abstraction (target-gated impl) # stdlib/embedded/— bare-metal: panic, WFI, intr vectors # stdlib/mcu/ — MCU-specific helpers (cortex_m, riscv, avr, esp32) # stdlib/(host) — current net/http/fs/process/json/... (host-only) # # firmware/ tree absorbs per-board code from existing hexa-* repos: # firmware/boards/{rtsc,chip,cern,antimatter,space}/ # firmware/bsp/ board-support packages # firmware/linker_scripts/*.ld per-MCU memory layouts firmware_evolution: decision_2026_05_10: option_C_core_alloc_split_plus_firmware_tree audit: doc/firmware_audit_2026_05_10.md absorption_targets: hexa-rtsc: { phase: D+ verified, tests: 70/70 PASS, role: reference impl, dest: firmware/boards/rtsc/, status: applied, applied_in: F2, applied_date: "2026-05-10" } hexa-chip: { phase: D iter 5, role: stdlib/hal consumer, dest: firmware/boards/chip/, status: applied, applied_in: F4, applied_date: "2026-05-10" } hexa-cern: { phase: D2 to E, role: Rust to hexa migrate during Phase E, dest: firmware/boards/cern/, status: applied, applied_in: F4, applied_date: "2026-05-10" } hexa-antimatter: { phase: D workspace, role: multi-vendor, dest: firmware/boards/antimatter/, status: applied, applied_in: F4, applied_date: "2026-05-10" } hexa-space: { phase: E hardware, role: KiCad first deferred, dest: firmware/boards/space/, status: applied, applied_in: F4, applied_date: "2026-05-10" } dependency_direction: allowed: - "firmware/* -> stdlib/core" - "firmware/* -> stdlib/alloc (with embedded allocator)" - "firmware/* -> stdlib/hal" - "firmware/* -> stdlib/mcu" - "firmware/* -> stdlib/embedded" - "compiler/* -> stdlib/core (cross-compile path)" - "compiler/* -> stdlib/alloc" forbidden: - "firmware/* -> stdlib/{net,http,fs,process,...} (host modules)" - "stdlib/* -> compiler/*" target_gate_check: rule: | Compiler rejects host-stdlib imports at compile time when --target=*-none-* is set. Detection by triple suffix. embedded_allocator: default: arena_v1 # Decision 6 v1 arena-only optional: bump_allocator # tight-RAM MCUs forbidden: tracing_gc # Decision 6 permanent reject linker_scripts: location: firmware/linker_scripts/ consumed_by: hexa_ld rfc: RFC-023 (planned — extension of RFC-018 §10) roadmap: F0: { status: done, deliverable: SPEC decisions + firmware/ skeleton } F1: status: done applied_date: "2026-05-10" deliverable: stdlib/core/ + stdlib/alloc/ extraction (Option C split) audit: doc/stdlib_core_extraction_2026_05_10.md core_modules: [math, string, parse, bytes, math/float, math/permille, math/rng, hash/sha256, hash/xxhash] alloc_modules: [collections, path, json, json_object, argparse, math/eigen, math/rng_ctx] total_moved: 16 shim_strategy: backward_compat_via_import_redirect sunset_target: after_F4_absorptions_complete target_gate_check_enforcement: deferred_to_F3 F2: status: done applied_date: "2026-05-10" deliverable: firmware/boards/rtsc/ reference port mechanic: option_A_full_copy source: "~/core/hexa-rtsc" dest: "firmware/boards/rtsc/" files_moved: 112 loc_moved: "~18k incl. README/CHANGELOG; firmware tree alone ~4k LOC" excluded: [".git/", "build/", "firmware/mcu/target/", "firmware/state/markers/", "state/markers/", "state/*.log"] selftest_pre: "70/70 PASS upstream" selftest_post_absorb: sim: "4/4 (43 internal checks via hexa_interp)" lattice_check: "10/10" falsifier_check: "49/49" cross_doc_audit: "7/8 (pre-existing upstream — own_3 rogue-.hexa flag, NOT absorb regression)" hdl: "deferred (iverilog 12/12 upstream — toolchain gated)" mcu: "deferred (cargo 15/15 upstream — toolchain gated)" license_preserved: true citation_preserved: true absorb_date: "2026-05-10" F3: status: done applied_date: "2026-05-10" deliverable: "thumbv7em-none-eabihf target in compiler/codegen + target_gate_check enforcement (HX1110)" codegen: compiler/codegen/thumbv7em_eabihf.hexa gate_check: compiler/check/target_gate.hexa diagnostic: "HX1110 (host-stdlib import in embedded target)" tests: zero_path: tests/firmware/zero_test.hexa # asm-only success path gate_check: tests/firmware/blink_test.hexa # HX1110 enforcement opcodes_v1: [mov, add, sub, mul, sdiv, udiv, and, orr, eor, ldr, str, push, pop, b, bl, cbz, cmp, it] arg_pool: "r0..r3 (>=5th spills to stack — TODO marker)" reg_pool: "r4..r6, r8..r11 (r7 reserved as fp)" deferred: - vfpv4_sp_fpu_instructions # s0..s31 / d0..d15 — F3+ followup - "struct_passing_by_value" # >16B by reference (RFC-018 §4) - longjmp_setjmp # (RFC-022 async path) - large_immediate_literal_pool # rely on assembler relaxation v1 - "fifth_arg_and_beyond_stack_spill" # AAPCS overflow-on-stack — F3+ followup execution: not_attempted_in_F3 # qemu/hardware not required; asm-shape only F4: status: done applied_date: "2026-05-10" deliverable: "firmware/boards/{chip,cern,antimatter,space}/ absorptions" pattern: "F2 reference (option_A_full_copy)" mechanic: option_A_full_copy batch_strategy: single_commit_four_boards absorb_date: "2026-05-10" sources: chip: "~/core/hexa-chip" cern: "~/core/hexa-cern" antimatter: "~/core/hexa-antimatter" space: "~/core/hexa-space" destinations: chip: "firmware/boards/chip/" cern: "firmware/boards/cern/" antimatter: "firmware/boards/antimatter/" space: "firmware/boards/space/" excluded: [".git/", "build/", "target/", "*.o", "*.elf", "*.bin", "state/markers/", "state/*.log", ".DS_Store", "*.swp", "*~", ".claude/"] files_moved: chip: 274 cern: 125 antimatter: 129 space: 113 total: 641 tree_size_post_absorb: chip: "6.2 MB" cern: "1.8 MB (was 547 MB upstream — 532 MB cargo target/ excluded)" antimatter: "1.1 MB" space: "1.1 MB" total: "~10.2 MB" loc_moved: chip: "~107k total / ~24k hexa / ~2.2k verilog / 0 rust" cern: "~31k total / ~9.5k hexa / ~1.2k verilog / ~430 rust" antimatter: "~18k total / ~11k hexa / ~360 verilog / ~440 rust" space: "~18k total / ~5k hexa / ~390 verilog / 0 rust" license_preserved: chip: true cern: true antimatter: true space: true citation_preserved: chip: true cern: true antimatter: true space: true banner_prepended: chip: true cern: true antimatter: true space: true selftest_post_absorb: chip: "verify/falsifier_check.hexa PASS — sat-1+sat-2+sat-3 met, 31 verify scripts on disk" cern: "verify/falsifier_check.hexa PASS — 21/21, 100% T1+T2+T3 closure" antimatter: "verify/falsifier_check.hexa PASS — 28/28, 4/4 100% closure" space: "verify/falsifier_check.hexa PASS — 4/4 falsifiers ≥ 67% sat-1 floor" hdl: "deferred (iverilog — toolchain gated)" mcu: "deferred (cargo — toolchain gated)" hal_promotion_shortlist: - "stdlib/hal/control.hexa (PID/Kalman/state-estimator: rtsc+cern+space)" - "stdlib/hal/sensor/{adc,dac,telemetry}.hexa (cern+space)" - "stdlib/hal/timing.hexa (antimatter+cern+space multi-channel)" - "stdlib/hal/power/{thermal,corner}.hexa (rtsc+chip)" - "stdlib/hal/regfile.hexa (cern memory-mapped reg pattern)" caveats: chip: "fully hexa-native (no cargo); cleanest absorb; pre-existing CHANGELOG dominates LOC" cern: "Rust hybrid retained (Cargo.toml + memory.x + src/.rs); 532 MB target/ filtered; Phase E migration pending F3" antimatter: "multi-vendor: KiCad + Verilog + Rust + hexa coexist; selftest/selftest.hexa requires HEXA_ANTIMATTER_ROOT (deferred)" space: "KiCad-first lives above firmware/ tier (top-level pcb/engineering); firmware/ subtree is sim+HDL clean 1:1" F5: { status: spec_land, applied_date: "2026-05-10", evidence: "2f9789e3", deliverable: RFC-023 firmware linker spec (linker scripts + .bss/.data init — implementation follow-up) } absorption_mechanic: decided_2026_05_10: option_A_full_copy rationale: | Self-contained, no submodule version-skew risk, simpler for downstream consumers. Upstream repo stays standalone for development; firmware/boards// is the consumed-by-hexa-lang view. Each absorb verifies LICENSE + CITATION byte-identical. open_questions: - which current host modules are target-agnostic enough to move into stdlib/core - RFC-023 separate or extension of RFC-018 - CI cross-compile to MCU targets via qemu-system-arm — vendored or in CI image biggest_unknown: | hexa-lang ARM Cortex-M native codegen timeline. F3 (thumbv7em target) is not started; firmware/ stays Rust-gated for hexa-cern/ hexa-antimatter until F3 + RFC-022 land. mitigation: | Parallel Rust path keeps Phase D extending until 2026-06-30; Phase E hardware commission unblocks once stdlib/hal v1.0.0 lands. # ───────────────────────────────────────────────────────────────────────────── # Stdlib evolution policy (Decision 2026-05-09) # ───────────────────────────────────────────────────────────────────────────── stdlib_evolution: decision_2026_05_09: separate_per_module_versioning rationale: | The compiler binary versions on the bootstrap stage cycle (stage0 → stage3 fixed-point). stdlib helpers evolve faster — each module gets its own SemVer-light track. Additive helpers land via small commits; breaking shape changes go through an RFC. policy: - additive helpers — small focused commit, +N selftest, .ai.md append - breaking shape change — requires RFC - new module — requires RFC + module .ai.md skeleton - dependency direction — stdlib MAY depend on stdlib; compiler/ MAY depend on stdlib; stdlib MUST NOT depend on compiler/ (avoids bootstrap cycle) modules_in_flight: c_ffi: file: stdlib/c_ffi.hexa planned_version: v1.1 applied_version: v1.1 applied_date: "2026-05-09" applied_commit: ecc49e1e planned_additions: - { name: c_alloc_ptr_slot, signature: "() -> int", purpose: "heap-allocate one void* slot, return address" } - { name: c_load_ptr, signature: "(slot: int) -> int", purpose: "read stored void* at slot, return pointer-sized int" } use_case_primary: | Out-pointer C APIs like duckdb_open_ext(path, &db_handle, ...). Without an out-pointer slot, hexa cannot pass a writable address to C; subprocess fallback adds 100ms+ per call. compat: backward 100% (additive helpers, no signature changes) downstream_consumers: - orpheus recovery/module/duckdb_native.hexa (commit ffb184b) — subprocess 136ms → FFI 1-3ms (17-50× speedup) forge_engine_link: forge.cond.NATIVE_DUCKDB http: file: stdlib/http.hexa current_version: v1.0.0 (14 http_* GET symbols) planned_version: v1.1 planned_additions: - { name: http_post_with_headers, signature: "(url: str, headers: [str], body: str, timeout_s: int) -> Response", purpose: "POST + JSON body — JSON-RPC enabler" } implementation_path: v0_path_c: curl_shellout # mirrors existing http_get_with_headers v1_path_b: libcurl_C_FFI # depends on c_ffi v1.1 helpers (separate concern) v2_path_a: native_http_emit # syscall + tls_handshake; defer use_case_primary: | bitcoind RPC (localhost:8332): subprocess bitcoin-cli 15-30ms vs persistent connection 3-7ms (5-10× speedup). Generic JSON-RPC clients benefit symmetrically. compat: backward 100% (additive POST API; GET unchanged) downstream_consumers: - orpheus recovery/module/method_bitcoin_core_rpc.hexa forge_engine_link: forge.cond.NATIVE_BTC_RPC http_sse: file: stdlib/http_sse.hexa current_version: v1.0.0 (GET streaming surface) applied_version: v1.1 applied_date: "2026-05-11" applied_commit: faca4134 applied_additions: - { name: http_sse_post, purpose: "streaming POST + body (Anthropic Messages, OpenAI Chat Completions)" } - { name: http_sse_open_post, purpose: "POST variant of http_sse_open" } - { name: http_sse_open_method, purpose: "method-parametric open (GET/POST/...)" } - { name: http_sse_build_curl_method_cmd, purpose: "internal _sse_build_curl factored into _method variant" } use_case_primary: | wilson provider-anthropic streaming POST. GET surface byte-identical. Body routed via `printf %s '...' | curl --data-binary @-`. Interp-mode POST fallback deferred — AOT users get streaming POST today. compat: backward 100% (additive POST API; GET surface byte-identical) downstream_consumers: - wilson provider-anthropic (Messages API streaming) - any OpenAI Chat Completions streaming client semver: file: stdlib/semver.hexa applied_version: v1.0.0 applied_date: "2026-05-11" applied_commit: 4725c619 applied_additions: - { name: semver_parse, purpose: "parse a SemVer 2.0.0 string (major.minor.patch[-prerelease][+build])" } - { name: semver_compare, purpose: "precedence compare two versions (prerelease < release; build ignored)" } - { name: semver_satisfies, purpose: "range-satisfies — ^, ~, >=, <=, >, <, =, x-ranges, hyphen ranges" } use_case_primary: | wilson loader_validate — validate plugin / dependency version constraints against installed versions. SemVer 2.0.0 conformant. compat: new module (v1.0.0) downstream_consumers: - wilson loader_validate (plugin version-constraint checks) selftest: test_semver 110/110 inbox_protocol: decision_2026_05_09: incoming_dir_until_native_compiler_v1 status: abolished_pre_sunset_by_user_direction abolished: | The internal `inbox/` staging folder was abolished by explicit user direction ahead of the original `stage_3_fixed_point` sunset trigger. Upstream patches now flow through the normal PR workflow. The durable record was rehomed (git mv, history preserved): - inbox/PATCHES.yaml -> archive/patches/PATCHES.yaml (frozen manifest) - inbox/manifest_log.jsonl -> archive/patches/manifest_log.jsonl (append-only audit trail) - inbox/patches/ -> archive/patches/ - inbox/fires/ -> archive/fires/ - inbox/notes/ -> docs/notes/ - inbox/rfc_drafts*/ -> docs/rfc/ Cross-repo handoffs are tracked by the root `INBOX` domain (INBOX.md / INBOX.log.md), which is a separate system and remains. manifest: archive/patches/PATCHES.yaml audit_log: archive/patches/manifest_log.jsonl selftest_invariant: | Every additive helper bumps the per-module selftest count and keeps overall stdlib selftest exit 0. Example: c_ffi 35 → 36; http 14 → 15. doc_invariant: | Each helper appends a signature + use-case block to the module's .ai.md (e.g., stdlib/c_ffi.ai.md, stdlib/http.ai.md). # ───────────────────────────────────────────────────────────────────────────── # Open questions (NOT decisions) # ───────────────────────────────────────────────────────────────────────────── open_questions: - inline_asm_syntax - generic_monomorphization_vs_dyn_dispatch_default - async_runtime_model - effect_system_link_to_atlas_L - linker_self_assembler_vs_external_as - wasm32_priority - python_ffi_native_path # RFC-016 import py + native - prover_v2_capability_targets - migration_window_duration_estimate # Surfaced 2026-05-10 (firmware audit + stage 1 punch list v2). - stage0_arena_reset_semantics_for_2GB_OOM_mitigation # punch list v2 A1 — interpreter holds every Module snapshot from # _splice_imported_items; need --phase-arena-reset between # parse / lower / codegen so spliced AST + lower IR fits in <1 GB. - core_alloc_split_criteria_which_modules_are_target_agnostic # firmware audit OQ #1 — math/slice/result/option seem clear, # string is borderline due to alloc dependency. Need a written # rule before F1 stdlib/core extraction begins. - thumbv7em_riscv32imac_codegen_timeline_currently_zero # tier_1_followup targets in this SPEC — not started; firmware/ # stays Rust-gated for hexa-cern/hexa-antimatter until F3 lands. - arm_cortex_m_codegen_blocks_firmware_targets # firmware audit "biggest unknown" — without F3 (thumbv7em # codegen) hexa-rtsc / hexa-chip remain on parallel Rust path. # ───────────────────────────────────────────────────────────────────────────── # Open questions resolved between 2026-05-10 and 2026-05-11. # ───────────────────────────────────────────────────────────────────────────── open_questions_resolved_2026_05_10_to_05_11: core_alloc_split_criteria: resolved_in: F1 (2026-05-10 — 16-module split rule landed) criterion_doc: doc/stdlib_core_extraction_2026_05_10.md rule: target-agnostic primitives in stdlib/core; alloc-dependent helpers in stdlib/alloc thumbv7em_codegen_timeline: resolved_in: F3 (2026-05-10 — asm-shape only; no qemu/hardware run yet) remaining_gate: qemu / hardware commission arm_cortex_m_codegen_blocks_firmware: resolved_in: partial — F3 + HX1110 target_gate_check landed 2026-05-10 remaining_gate: qemu / hardware commission hexa_str_concat_runtime_stub_linkage: resolved_in: | moot for the self/ build path — hexa_str_concat is defined in self/runtime.c:3634 and every transpiled program #includes runtime.c, so codegen_c2.hexa:271 (str_concat -> hexa_str_concat) always resolves. No stub ever shipped in self/native/*.c; the only literal there is the symbol-name string inside the transpiled hexa_cc.c (codegen_c2 symbol map). compiler_tree_followup: | the compiler/ (ground-up native) tree emits bl _hexa_str_concat / call hexa_str_concat from compiler/codegen/{arm64_darwin,x86_64_linux, thumbv7em_eabihf}.hexa; this is verified at the .s-text level by tests/m0/concat_test.hexa. Linking a hexa runtime into compiler/main.hexa's ld step is a downstream codegen follow-up of commit 194d9011, gated behind the still-open compiler-driver gaps (driver aborts before codegen on real source) — tracked there, not a standalone open question. a2_deploy_gap: resolved_in: | ~/.hx/bin/hexa_real re-promoted (774c5d32 → 41ecfb97 → dae438ee), then re-probed uncapped (HEXA_MEM_UNLIMITED=1): peak RSS ~782 MB (vs 5/10 baseline 3510 MB), dropping to ~160 MB once the A1 phase-arena reset fires. 4f5f8f07; doc/stage1_punch_list_v2.md "Update 2026-05-11 (PM)". P0 stage-1 source-side OOM closed at current scale. m4_freelist_reenable_timing: resolved_in: | resolved as a strategy question — the A2-verification gate cleared (~782 MB << 1.5 GB), so A1+A2 alone suffice for P0 closure at current scale; re-enabling the M4 freelist (self/hexa_full.hexa ~L17993) is an optional further reduction, not needed for closure. # ───────────────────────────────────────────────────────────────────────────── # Open questions surfaced 2026-05-11: none currently. The A2 deploy gap + # M4 freelist strategy questions (above) resolved; the still-open work is the # deferred-by-design items under open_questions (Surfaced 2026-05-10 + pre-existing). # ───────────────────────────────────────────────────────────────────────────── open_questions_surfaced_2026_05_11: {} # ───────────────────────────────────────────────────────────────────────────── # Phases completed (2026-05-09) # ───────────────────────────────────────────────────────────────────────────── # Status snapshot derived from git log analysis on 2026-05-09 (main branch). # PASS = landed + tests / smoke witness on disk # IN-PROGRESS = code shipped, witness or follow-up still pending # DEFERRED = roadmapped but not yet started phases_completed_2026_05_09: A0: { status: PASS, evidence: "5f506d06 feat(compiler): phase A0 — backend skeleton + IR types" } A1: { status: PASS, evidence: "0f15a5bf feat(compiler): phase A1 — lexer + parser implementation" } A2: { status: PASS, evidence: "80037e78 feat(compiler): phase A2 atlas merger + B1 S1 resolve pass" } A3: { status: PASS, evidence: "38f8661f feat(compiler/atlas): phase A3 — packed const codegen + static embed" } A4: { status: PASS, evidence: "6c4e7d4b feat(compiler/atlas): phase A4 — wire static_atlas() to real embedded fixture" } B1: { status: PASS, evidence: "80037e78 (S1 resolve) + 6e255165 phase B2 catalog growth covers S0–S2" } B2: { status: PASS, evidence: "6e255165 feat(compiler/check): phase B2 — S2 bind pass + diagnostic catalog growth" } B3: { status: PASS, evidence: "9e82795e feat(compiler/check): phase B3 — S3 type check + S4 domain check; refined by 93fb38a9 B3+ S5 units pass" } B4: { status: PASS, evidence: "6eab6c55 annotation handlers @law/@grace/@discover + 5ee37b49 HX8004 atlas citation strict (S8)" } C1: { status: PASS, evidence: "6786affd / 15809e3d feat(compiler/check): phase B5 — S6 equational verify (in-house prover v0)" } C2: { status: PASS (cross_prover atlas-registration parked), evidence: "3a0ce4d2 Decision 5e tombstone + retroactive sweep + cascade + HX1099 (atlas-side) — smoke witnesses on disk (compiler/discover/{tombstone,cascade,promote}_smoke.hexa); tool/cross_prover.hexa diagonal exists (675ce4b0) but its auto-registration into the prover atlas is a separable nice-to-have, not yet started — parked" } D1: { status: PASS, evidence: "566dd835 hexa_ld v1 minimal static ELF64 + 758659eb v1.1 Mach-O arm64 static binary" } D2: { status: SCAFFOLD (RC met 2026-05-06) / Step 3+ deferred, evidence: "81d2176a LP1+LP2+LP3 capability matrix + LSP 3.17 pin — .roadmap.lsp LP1-LP5 all CLOSED, status PEER_SCAFFOLD_RC_MET; the feature suite (completion/hover/definition/references/rename/formatting/workspace.diagnostic, incremental TextDocumentSync, pull diagnostics) is explicitly deferred to Step 3+, not in-flight" } E1: { status: DEFERRED, evidence: "big-bang migration scheduled post stage1 fixed point" } E2: { status: DEFERRED, evidence: "depends on E1 + stage3 fixed point" } # ───────────────────────────────────────────────────────────────────────────── # Phases completed (2026-05-10) # ───────────────────────────────────────────────────────────────────────────── # Delta over 2026-05-09 snapshot: firmware Option C decision landed, # hexa_ld v1.2 ad-hoc codesign on macOS, RFC-020 enum payload variants # A4/A5 PASS, RFC-021 daemon mode v0 prototype, RFC-022 async + cancel # token spec, stdlib I/O write_text + http_post_with_headers + c_ffi # v1.1, stage 1 punch list v2 (host OOM dominates), Mach-O gate Phase A, # upstream-patch inbox protocol, parser Gaps 1+2+14+15. phases_completed_2026_05_10: F0_firmware_option_c: status: PASS evidence: "fc6d48b2 feat(spec+firmware): Option C — stdlib core/alloc split + firmware/ tree" notes: | SPEC decisions + firmware/ skeleton (firmware/boards, firmware/bsp, firmware/linker_scripts) created. F1..F5 follow via background tasks #84-#87 (stdlib/core extraction, rtsc reference port, thumbv7em codegen, remaining boards, RFC-023 firmware linker). hexa_ld_v1_2_codesign: status: PASS evidence: "96aa37e8 feat(compiler/link): hexa_ld v1.2 — Mach-O ad-hoc codesign emit (kernel accepts)" notes: | Ad-hoc LC_CODE_SIGNATURE — CSMAGIC_EMBEDDED_SIGNATURE wrapping CSMAGIC_CODEDIRECTORY with SHA-256 per-4 KiB-page hashes; no notarization, no team-id. Apple Silicon kernel accepts the binary without external `codesign` shellout. rfc020_enum_payload_a4_a5: status: PASS evidence: "4ed9966e fix(codegen): RFC-020 A4 match-side payload extraction + A5 regression PASS — 15/15" notes: | A1+A2+A3 already landed (3c8be96c + 005d5427). A4 hexa_cc.c codegen for match-side payload extraction completed. A5 regression suite self/test_enum_payload_full.hexa now 15/15 PASS (was stale-build 5/15). rfc022_async_plus_cancel: status: PASS evidence: "925846d0 spec(rfc): RFC-022 async model parity + stdlib cancel token (wilson G2+G3)" notes: | RFC-022 spec only (no compiler/ change in this batch). stdlib cancel token (G3) ships with selftest +1. Unblocks wilson core port G2+G3 alongside G1 (RFC-020 A5). stdlib_io_write_text: status: PASS evidence: "4d414f73 feat(stdlib): write_text(path, content) — large-content file I/O (orpheus duckdb chokepoint unblock)" notes: | Bulk-write helper for orpheus duckdb large-content path. Mirrors stdlib/fs read_text shape; backward-compat additive. stage1_punch_list_v2: status: PASS evidence: "86afadb0 doc: stage 1 punch list v2 — host OOM dominates (was 6-10w, now 14-22w)" notes: | Stage 1 self-compile reach revised: 6-10 weeks → 14-22 weeks. Stage 0 hexa_interp 2 GB cap now dominates after Gap 1+2+14+15 surface enlarged the in-process super-module to ~25,932 lines. New punch list: A1-A4 host bootstrap (P0), B1-B5 recurring, C1-C20 new findings. Critical path: stage 0 arena reset OR stage 0.5 host before any further stage 1 fix lands. rfc020_b1_audit: status: PASS evidence: "77254d91 doc(self/ir/instr): RFC-020 B1 audit + holdout — stage0 binary blocks migration" g7_hexa_ld_dlopen_draft: status: SPEC evidence: "a01fb505 chore(incoming): add g7-hexa-ld-dlopen RFC draft" notes: | Draft RFC for dynamic linking. Priority ★ nice-to-have, NOT blocking — wilson works without it via static absorption. inbox_rfc020_wilson: status: PASS evidence: "c48bbbeb incoming: add rfc020-enum-payload-variants + wilson-pi-port-6-gap-prereq entries" gap_14_lower_match_payload: status: PASS evidence: "47d99c1c feat(compiler): Gap 14 lower coverage match payload index nested struct HX1100" upstream_patch_inbox: status: PASS evidence: "03d5070b feat(incoming+tool+spec): upstream-patch inbox until stage 3" notes: | archive/patches/PATCHES.yaml + tool/inbox_sync.hexa + tool/inbox_promote.hexa + doc/inbox_for_bedrock.md. Sunsets at stage 3 fixed point. rfc020_handoff_pi_port: status: PASS evidence: "36daab7d feat(incoming): RFC-020 enum-payload handoff + wilson pi-port 6-gap inbox + A5 test" rfc021_daemon_v0: status: PASS evidence: "4ab3930c feat(compiler/daemon): RFC-021 v0 prototype — atlas.lookup + shutdown" notes: | Unix socket IPC. atlas.lookup + shutdown commands wired. System-wide zero-fork pattern for build-hot probes. rt_json_numeric_coercion: status: PASS evidence: "2e144e58 fix(rt): json.hexa numeric coercion HEXA_F2 PASS" gap_15_codegen_breadth: status: PASS evidence: "194d9011 feat(compiler): Gap 15 codegen breadth — concat/cmp/cbr/N-arg/rodata" gap_2_cross_file_dedup: status: PASS evidence: "150b004f feat(compiler/check): Gap 2 cross-file dedup with pub visibility" gap_1_multi_file_loader: status: PASS evidence: "0ea8bc95 feat(compiler/parse): Gap 1 multi-file parser loader (3-way merged)" macho_gate_phase_a: status: PASS evidence: "26e06d70 feat(spec+lint): macos Mach-O gate Phase A — invocation-surface lint + declarative SSOT" notes: | LINT-MACHO-1 (.hexa exec sites) + LINT-MACHO-2 (sh + CI surface) + spec/hexa_macho_gate.spec.yaml falsifier SSOT. Warn-only. c_ffi_v1_1_out_pointer: status: PASS evidence: "ecc49e1e feat(stdlib): c_ffi v1.1 — c_alloc_ptr_slot + c_load_ptr (out-pointer slot)" notes: | Out-pointer slot enables duckdb_open_ext-style C APIs. orpheus duckdb_native.hexa 136ms subprocess → 1-3ms FFI. http_post_with_headers: status: PASS evidence: "1d76e4fe feat(stdlib): http_post_with_headers — JSON-RPC enabler (orpheus bitcoind)" notes: | v0 path: curl shellout. orpheus method_bitcoin_core_rpc.hexa bitcoind RPC: subprocess 15-30ms → persistent 3-7ms. stage0_blocker1_audit: status: PASS evidence: "3565f672 doc: Blocker 1 stage0 rebuild audit — NO-OP outcome" i32_lower_literal_coercion: status: PASS evidence: "48f65cf3 fix(compiler): i32 type lower + literal coercion — M0 ASM PASS" parser_gap_6_recovery: status: PASS evidence: "1c7b5861 fix(parse): Gap 6 — parser error recovery (skip-to-resync)" # ───────────────────────────────────────────────────────────────────────────── # Phases completed (2026-05-11) — mirror of SPEC.md §20.3 delta snapshot. # ───────────────────────────────────────────────────────────────────────────── # A2 source-side splice mitigation + 5 cluster merges + #14 drain + #11/#12 # wilson fixes + stage 2/3 witness + SPEC.md sync. (Stage-1 host verification # subsequently CLOSED in the closure round — see phases_completed_2026_05_11_closure # and open_questions_resolved_2026_05_10_to_05_11.a2_deploy_gap.) phases_completed_2026_05_11: stage_2_3_witness_plus_rfc_023: status: PASS evidence: "2f9789e3 doc(bootstrap): stage 2/3 witness harness + RFC-023 firmware linker spec" bak_timestamped_gitignore: status: PASS evidence: "805c61a5 chore: ignore *.bak.* timestamped backup files" a2_in_place_splice_accumulator: status: PASS evidence: "ab2dfcee fix(compiler/parse): A2 — in-place _splice_imported_items accumulator (stage1 OOM)" notes: | Source-side mitigation. Subsequently deployed (~/.hx/bin/hexa_real re-promoted 774c5d32 → 41ecfb97 → dae438ee) and re-probed: peak ~782 MB (4f5f8f07) → P0 stage-1 OOM closed at current scale. stage0_host_rebuild_a2: status: "PASS (source + deployed — re-promoted 774c5d32 → dae438ee)" evidence: "ddb21f21 build(stage0): rebuild host — A2 in-place splice accumulator" superpowers_void_d1_d6_defense: status: PASS evidence: "5d97e9db superpowers/void D1–D6 defense layer design (incident 2026-05-11)" inbox_patches_flip_10: status: PASS evidence: "13143f18 #10 wilson hexa-real-promotion — incoming PATCHES.yaml flip" types_cluster_a4_c4_b2: status: PASS evidence: "bc50db32 Types cluster — A4 side-index / C4 HX2001 non-Ident callee / B2 fn_name pin" c11_parse_only_atlas_literals: status: PASS evidence: "2b67ccb6 C11 parse_only skip on atlas literals (cherry-pick)" lower_cluster_c5_c9_c10_c16: status: PASS evidence: "f3f63b72 Lower cluster — C5 HX1101 unbound ident, C9 else-end recompute, C10 HX1102 pattern, C16 HX1103 unhandled HExpr" b1_hir_lower_type_ref_plus_c19: status: PASS evidence: "4cd39b2a B1 _hir_lower_type_ref empty-name guard + C19 for-desugar" p2_batch_c12_c20: status: PASS evidence: "840c8f7d P2 batch — C12 citation gate (--strict-citations), C13 units early-out, C14 typed diags, C15 lmodule if-expr, C20 HX2003 non-fn callee" http_sse_v1_1_post_body: status: PASS evidence: "faca4134 #11 stdlib http_sse v1.1 — streaming POST + body (wilson provider-anthropic)" cmd_build_out_of_tree_flatten: status: PASS evidence: "21e7b518 #12 hexa build out-of-tree (flatten + $HEXA_LANG/self -I)" inbox_patches_flip_batch: status: PASS evidence: "a06530fb #10/#11/#12 PATCHES flip batch" compiler_main_hir_to_mir_drain: status: PASS evidence: "18c6a536 #14 hir_to_mir_diags drain in compiler/main (HX1101/1102/1103 surface in CLI)" stage1_punch_list_v2_5_11_update: status: PASS evidence: "ee62470a #13 stage 1 punch list v2 — 2026-05-11 update (A2 verify deferred)" macos_smoke_portable_timeout: status: PASS evidence: "1ceece8a fix(tests/integration): macOS-portable timeout + clarify HEXA_BIN default" spec_md_sync_2026_05_11: status: PASS evidence: "d05696a2 doc(spec): 2026-05-11 — sync SPEC.md with 5/11 stage1 + cluster + wilson + drain land" # ───────────────────────────────────────────────────────────────────────────── # Phases completed (2026-05-11 — closure round): P0 stage-1 OOM verified closed # (A1+A2 → peak ~782 MB), hexa_real re-promoted twice more, RFC-020 A4 restored # in SSOT, wilson hexa-lang side closed (hexa build core/main.hexa → wilson 0.0.1). # ───────────────────────────────────────────────────────────────────────────── phases_completed_2026_05_11_closure: stage1_rss_reprobe_post_repromote: status: PASS evidence: "4f5f8f07 #13 RSS re-probe post hexa_real re-promote — peak ~782MB (was 3510MB), P0 OOM closed" hexa_real_repromote_12_a2_clusters: status: PASS evidence: "774c5d32 build(stage0): re-promote hexa_real — #12 cmd_build flatten + A2 splice + clusters + #14" rfc020_a4_codegen_restored_ssot: status: PASS evidence: "41ecfb97 fix(self/codegen): RFC-020 A4 — restore enum-payload match codegen in codegen_c2.hexa (regen had wiped the a85b8a1c hexa_cc.c hand-fix); test_enum_payload_full 15/15 codegen + 15/15 interp" stdlib_semver: status: PASS evidence: "4725c619 feat(stdlib): add stdlib/semver.hexa — SemVer 2.0.0 parse + compare + range-satisfies (wilson loader_validate); test_semver 110/110" install_relative_stdlib_discovery: status: PASS evidence: "df9e7f6b feat(self/module_loader+main): install-relative stdlib/ discovery — works without HEXA_LANG/HEXA_STDLIB_ROOT (orpheus O-002)" gap15_hexa_str_concat_closed_moot: status: PASS evidence: "a8ff675b doc(spec): close out Gap 15 — hexa_str_concat runtime stub linkage" spec_19_20_status_reconcile: status: PASS evidence: "571df583 doc(spec): reconcile §19/§20 status with reality — D2 scaffold/deferred, C2 cross_prover parked, F3 done, dedupe §19" shell_builtin_absorb_pwd_ls: status: PASS evidence: "0ba5fd7d feat(stdlib/runtime): absorb pwd/ls shell builtins → cwd()/list_dir() intrinsics (absorbed_site_count 638→752, pending 197→83)" exec_stream_kill_builtin: status: PASS evidence: "6c0fbac7 feat(self/runtime+codegen): add exec_stream_kill(h) builtin — SIGKILL stream child (wilson tool-core ESC-cancel)" builtin_byvalue_thunk_codegen: status: PASS evidence: "46016739 feat(self/codegen): emit thunks for builtins/methods taken by-value — fixes hexa_callN() undeclared (wilson gap b2); un-doubled hexa_cc.c 32447→21010 lines" hexa_cc_resolve_out_of_tree: status: PASS evidence: "731f41d6 fix(self/main cmd_cc): resolve hexa_cc.c / SSOT modules / -I via $HEXA_LANG > install_dir > ./self (not cwd) — hexa cc works out-of-tree" law_io_selftest_main_moved: status: PASS evidence: "a5de44e2 fix(self/stdlib/law_io): move selftest main() to tool/law_io_selftest.hexa — u_main collision on flatten" hexa_real_repromote_46016739: status: PASS evidence: "dae438ee build(stage0): re-promote hexa_real (46016739) — exec_stream_kill + fnref thunks + un-doubled hexa_cc.c; sha cd817981… (~/.hx/bin/hexa_real + ~/.hx/packages/hexa/hexa.real)" wilson_hexa_lang_side_closed: status: PASS evidence: "340c3788 doc(inbox): wilson<->hexa-lang closure — VERIFIED, wilson hexa build → wilson 0.0.1; all follow-ups landed (closure note docs/notes/2026-05-11-wilson-hexa-lang-closure.md)"