From 11abb0f93aa19e959189cfd8cf33baf3dbc4e212 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 31 May 2026 07:03:17 +0000 Subject: [PATCH] chore(docs,config): #488 partial-port state note; allow compiler oracle (Refs #488) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wrap-up for the res-to-affine partial-port slices (#494/#495/#496). - STATE.a2ml — dated session note documenting --partial slices 1-3 (fn skeletons + switch->match + pipe/if/blocks + array/record literals), the local build/test/parse verification, and the remaining #488 work (JS objects / template strings, labelled-arg refinement, --partial+--translate combine, module-qualified-reference resolution as a module-mapping policy). - .claude/settings.json — allow `_build/default/bin/main.exe` (the compiler oracle used to parse/type-check generated output) in the permission allowlist; existing entries + the SessionStart hook preserved. Docs/config only, no code. settings.json valid JSON; doc-truthing guard passes; STATE.a2ml mirror keys intact. Refs #488 https://claude.ai/code/session_017T8SzHr2yXav8hm4Ho76Uw --- .claude/settings.json | 1 + .machine_readable/6a2/STATE.a2ml | 1 + 2 files changed, 2 insertions(+) diff --git a/.claude/settings.json b/.claude/settings.json index 63601bd..6c63226 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -43,6 +43,7 @@ "Bash(dune exec:*)", "Bash(dune test:*)", "Bash(tree-sitter:*)", + "Bash(_build/default/bin/main.exe:*)", "mcp__github__get_me", "mcp__github__get_commit", diff --git a/.machine_readable/6a2/STATE.a2ml b/.machine_readable/6a2/STATE.a2ml index cb761ce..910409c 100644 --- a/.machine_readable/6a2/STATE.a2ml +++ b/.machine_readable/6a2/STATE.a2ml @@ -10,6 +10,7 @@ authoritative-status-doc = "docs/CAPABILITY-MATRIX.adoc" drift-flag = "STALE as of 2026-05-23 PM: this file's [components]/[features]/[project-context] still predate landed PRs since 2026-05-19. It MIRRORS, it does not LEAD. Authoritative sources by topic — readiness: docs/CAPABILITY-MATRIX.adoc; spine + AS↔typed-wasm contract: docs/ECOSYSTEM.adoc; coordination ledger / critical path: docs/TECH-DEBT.adoc; test taxonomy + PR-level gates: docs/standards/TESTING.adoc (added 2026-05-23); panic-attack SOP: docs/standards/PANIC-ATTACK.adoc (added 2026-05-23). Gate baseline: CAPABILITY-MATRIX records 260/260 at 2026-05-19 reconstruction; subsequent borrow-checker work has lifted it (#240 → 263, return-escape → 271/274, &mut surface → 278/281). The exact live number for any given commit comes from `dune runtest --force` — do not hard-code it here. (DOC-05, issue #176.)" session-note-2026-05-26-publish-104 = "ISSUE #104 CLOSED — FIRST NPM PUBLISH LANDED. @hyperpolymath/affine-vscode@0.1.0 is now on registry.npmjs.org. Owner-action sequence completed today: (1) npm org `hyperpolymath` created on free public-package tier; (2) Granular Access Token generated for scope @hyperpolymath/* with Read+Write, uploaded to repo secret NPM_TOKEN; (3) signed annotated tag affine-vscode-v0.1.0 pushed at origin/main (RSA key 9639451754496E51D6B537CAD119017EBF695AB1); (4) .github/workflows/affine-vscode-publish.yml ran green — `npm publish --access public` succeeded; (5) `npm view @hyperpolymath/affine-vscode` resolves. Downstream consumers (my-lang#66, standards#160) which un-vendored their adapters on 2026-05-21 now have a working `npm install` path; vscode-smoke workflow (skipped per #381 while package was unpublished) will start exercising the live package on its next PR run. Lineage for future @hyperpolymath/* publishes: org+token are reusable; mirror `affine-vscode-publish.yml`'s shape (tag trigger, version-match guard, .npmrc write from secret, npm publish --access public). NPM_TOKEN rotation advised post-publish (token value transited a session transcript during wire-up); see .machine_readable/6a2/PLAYBOOK.a2ml [npm-publish] for the runbook." session-note-2026-05-30 = "DOC-16 + DOC-17 — DOC-TRUTHING MONITOR FULLY MECHANICAL (issue #176). Two complementary in-repo guards now enforce the doc-truthing rules that were previously external-bot-only. DOC-16 (PR #476, tools/check-doc-truthing.sh): banner-PRESENCE invariant — fails if any over-claiming doc loses its CAPABILITY-MATRIX banner, if the matrix stops self-declaring primacy / loses its 'What AffineScript is NOT' section, or if STATE.a2ml drops its mirror keys; deliberately does NOT phrase-scan (a naive grep false-positives on the negating banners + future-roadmap text). DOC-17 (PR #475, tools/check-doc-overclaims.sh + tools/doc-overclaims.allow): the phrase-detection complement — a frozen-baseline RATCHET over README + docs/** (excluding CAPABILITY-MATRIX + TECH-DEBT, which quote the rule) that fails any NEW backend-breadth / 'production-ready' / stdlib-% occurrence beyond the 13-line baseline (all current entries legit: future-tense roadmap milestones + dated history snapshots + corrective banners); re-baseline via `--update` / `just doc-overclaims-bless`, diff = audit trail. Both wired into `just check` (guard recipe) + ci.yml build job, both toolchain-free bash (run without OCaml). #475 was originally a superset of #476 (built in parallel, same session); on discovering #476 had merged first, #475 was reworked to drop the duplicated banner check and keep only the unique ratchet, renamed check-doc-truth.sh→check-doc-overclaims.sh to avoid the name clash (DOC-DEDUP-clean). FOLLOW-UP (same day, post-#475-merge): the two near-identically-named guards were consolidated so neither lingers — the ratchet (incl. `--update` mode + tools/doc-overclaims.allow baseline) was folded INTO tools/check-doc-truthing.sh using #476's fail-accumulator/note() structure, and tools/check-doc-overclaims.sh was deleted. A SINGLE toolchain-free guard now enforces both presence invariants (DOC-04/05) and the over-claim ratchet (DOC-08/09) in one run; one CI step; the `guard` recipe runs it; re-baseline via `just doc-truth-bless`. DOC-17 folded into DOC-16 in the ledger. Gate-number policy unchanged (DOC-05): live `dune runtest --force` count never hard-coded. No compiler code touched. Refs #176 / #175." +session-note-2026-05-31-partial-port-488 = "RES-TO-AFFINE PARTIAL-PORT MODE #488 SLICES 1-3 (successor to closed #57). New --partial flag, a DISTINCT model from --translate: renders module-top-level functions (let f = (params) => body) into AffineScript fn skeletons whose output DELIBERATELY does not type-check (un-annotated ReScript fns can't yield a compilable fn) but DOES parse — un-inferable types are `_` holes, un-translatable expr/pattern are () /* TODO */ / _ /* TODO */ islands. (1) Slice 1 (#494 / c157a0f): fn skeletons + switch->match (variant/tuple/literal patterns) + expression translation (literals / idents / calls / binary-ops with float-op +.->+ and identity-equality ===->== normalisation / ++ concat / member + module-qualified access / ternary). The binary operator is an anonymous tree-sitter token, sliced from source between the operands. (2) Slice 2 (#495 / 78906f9): pipe-first -> desugaring (a->f(b) -> f(a, b); chains are left-nested so x->f->g(2) -> g(f(x), 2) falls out of the recursion); if/else; blocks with let statements. (3) Slice 3 (#496 / 4d4d1d4): array literals ([a, b] -> [a, b]) + record literals ({x, y} -> Rec #{ x: x, y: y }; AffineScript records are NOMINAL so an anonymous record gets the placeholder type Rec for the human to rename; field punning {x} -> x: x). Walker-internal: translate_expr / translate_switch / translate_pipe / translate_if / translate_as_block / translate_block(_inner) / translate_block_let (mutually recursive) + translate_pattern + partial_function + collect_partial + Walker.translate_partial; emitter emit_partial; main --partial (precedence over --translate, walker-only). 32 res-to-affine walker tests. VERIFIED LOCALLY each slice (apt-bootstrapped toolchain): full dune build exit 0; dune runtest green; main.exe check on the generated skeletons reaches resolution/type-checking WITHOUT a parse error (the partial-port bar — type/resolution errors are expected). REMAINING #488: JS objects / interpolated template strings, labelled-arg refinement, combining --partial with --translate (declarations + functions in one pass), and module-qualified-reference RESOLUTION (a ReScript->AffineScript module-mapping POLICY decision, deliberately not built without owner scoping). #488 stays OPEN. Refs #488 / #57 (closed)." session-note-2026-05-31-migration-phase3-slice3-and-closure = "MIGRATION-ASSISTANT PHASE 3 SLICE 3 + #57 CLOSURE + SUCCESSOR #488. (1) PR #484 (squash ea8bef5): res-to-affine --translate extended to module-level literal value bindings — let = lowered to a typed AffineScript const name: T = value; for int/float/string/bool (AffineScript has no module-level let; a top-level value binding is const, annotation + semicolon required). New walker.ml helpers classify_number / translate_literal / translate_let_const + a let_declaration branch in collect_translations. Conservative: call bodies, ref(...) mutable-globals, destructuring patterns, and exotic numbers (hex/octal/binary/signed/scientific/underscored) are skipped — number forms limited to plain decimal int and D+.D+ float. New fixture test/fixtures/phase3c.res + 4 gated walker tests (walker-phase3c-let-const). (2) VERIFIED LOCALLY (apt-bootstrapped toolchain): full dune build exit 0; 22 res-to-affine walker tests green against pinned grammar 990214a; compiler oracle main.exe check on the generated consts returns Type checking passed. (3) #57 CLOSED as completed (owner-directed) — the declaration-translation goal is delivered across slices 1 (#477 / 2763909) / 2 (#481 / 3484010) / 3 (#484 / ea8bef5) plus the session-start toolchain hook (#482 / 138baac); --translate now renders every self-contained top-level declaration (type aliases, sums, structs, generics, literal consts) to compiler-verified AffineScript. (4) SUCCESSOR #488 filed: res-to-affine partial-port mode (switch->match / function translation) + module-qualified-reference resolution — both OUT of the every-emitted-form-type-checks model by construction (switch->match is an expression needing whole-function translation of usually-un-annotated ReScript fns, which AffineScript fn signatures can't type-check; qualified refs parse post-#228 but won't resolve without a module-mapping story). #488 is a child of estate migration epic #406; remaining migration work continues there. Refs #57 (closed) / #488 / #406." session-note-2026-05-30-migration-phase3 = "MIGRATION-ASSISTANT PHASE 3 SLICES 1+2 (issue #57) + SESSION-START HOOK. (1) res-to-affine --translate now turns fully-structural ReScript type declarations into compiler-verified AffineScript; two slices landed off main. Slice 1 (PR #477, squash 2763909): primitive type aliases (type userId = int -> type UserId = Int) and simple sum types (type color = Red | Green -> type Color = | Red | Green; payloads mapped, Circle(float) -> Circle(Float)). Slice 2 (PR #481, squash 3484010): record types -> AffineScript struct (type point = {x: int} -> struct Point { x: Int }) and GENERICS — ReScript type params 'a mapped to AffineScript [A], threaded through aliases, sums, and records (param refs in bodies map 'a -> A). (2) New code is walker-internal (tools/res-to-affine/walker.ml): translate_type_ref / translate_variant / translate_record_fields / affine_type_param / extract_type_params / translate_type_binding / collect_translations, plus Walker.translate and Emitter.emit_translation; CLI gains --translate (walker-only; no-op under --engine=scanner). Detection keys on tree-sitter node TYPE not field labels, robust to the grammar's optional alias/body-field ambiguity. (3) CONSERVATIVE BY CONSTRUCTION — a decl translates only when every part is representable; qualified-path RHS, non-primitive/opaque refs, nested generics (array), object types, GADT returns, variant spreads, and records with mutable or optional-? fields are SKIPPED (left in the marker block + quoted original), never mis-translated. Two normalisations keep output referenceable: lower-case type names capitalised (color->Color) and type vars mapped ('a->A), because lib/parser.mly reads a lower-case name in type position as a TyVar not a TyCon. (4) QUALIFIED-PATH RHS DEFERRED: Belt.Map.t now PARSES (the #228 grammar gap closed — parser.mly:515 wires qualified_type_name into type_expr_primary) but Belt::Map::T would not RESOLVE against a non-existent target module, which would break the every-translated-form-type-checks guarantee; it waits for a module-mapping story. Remaining Phase-3 slices: let->const for literal bindings, switch->match (needs body translation), qualified-path resolution. #57 stays OPEN. (5) VERIFICATION: unlike earlier res-to-affine work this was built+tested locally — the OCaml toolchain was apt-bootstrapped (opam.ocaml.org is network-blocked here, but Ubuntu packages match the project pins: dune 3.14 / cmdliner 1.2 / alcotest 1.7 / menhir 20231231 / sedlex 3.2 / ppx_deriving 5.2 / ppxlib 0.32 / yojson 2.1 / js_of_ocaml 5.6). Full dune build exit 0; dune runtest green (21 res-to-affine cases incl. gated walker tests against pinned grammar 990214a); and the COMPILER ORACLE (main.exe check, the #228 method) returns Type checking passed on every generated form. (6) CONFIG: added .claude/hooks/session-start.sh (SessionStart hook) registered in .claude/settings.json so future web sessions auto-provision this exact apt toolchain + tree-sitter CLI + pinned grammar — synchronous, idempotent, best-effort, web-only (CLAUDE_CODE_REMOTE guard); settings.json also gains dune/tree-sitter build+test permissions. (7) Both #477 and #481 were admin-merged while CI runners were backlogged; #481 was fully locally verified before merge. Refs #57 / #228." session-note-2026-05-26 = "MIGRATION-ASSISTANT PHASE 2C + REPO-TIDY STACK (T-1..T-7) + STDLIB BLOCKER CLOSURES. (1) PR #357 — feat(res-to-affine) Phase 2c on branch claude/epic-gauss-Mbi0E: tree-sitter walker extended from #322's single Side_effect_import detector to all six anti-patterns. New detectors in tools/res-to-affine/walker.ml — detect_raw_js (any extension_expression node), detect_untyped_exception (try_expression / call to value_identifier \"raise\" / member_expression starting with Js.Exn or ending with Promise.catch), detect_mutable_global (top-level let_declaration whose body is call to value_identifier \"ref\", OR top-level mutation_expression), detect_inline_callback_record (>=3 inline function values in a record literal or a call_expression's arguments list — handles direct function children + labeled_argument + record_field wrappers), detect_oversized_function (function node whose stop.row - start.row + 1 > 50). Module-toplevel predicate refactored to a single at_module_toplevel helper that walks the ancestor chain refusing on `function` or `let_binding` body. Findings deduped by (kind, line) so the AST walker doesn't emit more bullets than the line-based scanner. CLI flipped --engine=walker to the default in tools/res-to-affine/main.ml (scanner remains as fallback when grammar / tree-sitter CLI missing — pre-existing graceful-fallback path from #322 unchanged). Scanner.kind extended with Inline_callback_record + Oversized_function variants; scanner.ml gives them labels + guidance. New fixture test/fixtures/phase2c.res exercises the two walker-only kinds. test_walker.ml grew per-kind tests under three new suites (walker-side-effect-import / walker-phase2c-parity / walker-phase2c-new-kinds). README + walker.mli updated. (2) Phase 2c CI fix push (e7a3a44): initial Phase 2c commit had build+lint failing; defensive rewrite replaced the labeled-only `mk_finding ~kind:K ~line:L ~excerpt:E :: acc` emit pattern with explicit `let finding : Scanner.finding = { ... } in finding :: acc` matching Phase 2b's working style, plus replaced non-ASCII glyphs in comments (≥ → >=, … → ...) defensively. (3) DIAGNOSED: `main` itself is red. PR #359 (T-1, pure file-rename + delete + one-line CONTRIBUTING.md edit, zero OCaml touched) also fails on build+lint. That conclusively shows the build failure is inherited from main, not introduced by Phase 2c or any tidy work. Matches CLAUDE.md §\"CI signal reliability\" — auto-merge fires even when build is red; the historical pattern of #334/#335/#336/#344 landing red applies. Root cause on main is NOT diagnosed in this session (needs the actual `dune build` log; WebFetch on the actions UI returns React skeletons, not log content; container has no OCaml toolchain to repro locally). Filed implicit follow-up: someone with shell access should run `gh run view --log-failed ` against any of the recent failing runs to identify the underlying lib/ compile error. (4) REPO-TIDY STACK (T-1..T-7) — six small PRs landed off origin/main, each reviewable in isolation, none touching .ml/.mli, all inheriting the same baseline build/lint failure as PR #359 (proof above). T-1 PR #359 *MERGED* — AI.a2ml → 0-AI-MANIFEST.a2ml (Hypatia root_hygiene rule + sibling-repo convention), delete AI.djot (superseded), docs/TECH-DEBT-alt.adoc → docs/TECH-DEBT.adoc (restoring the canonical name every cross-link in the repo already points at; PR #356's -alt suffix during the #351 split is dead weight post-#355). T-2 PR #360 — delete 2,182 lines of submarine-game docs (DAMAGE-SYSTEM.md, CONTROLS-REFERENCE.md, GAME-BUNDLING-STRATEGY.md), zero cross-references. T-3 PR #365 — 13 loose root .md/.adoc moved into docs/ subtree (ABI-FFI-README → docs/reference/ABI-FFI.md, ALPHA-1-RELEASE-NOTES → docs/history/, BACKEND-{ANALYSIS,IMPLEMENTATION} → docs/architecture/, COMPILER-CAPABILITIES → docs/reference/, EXPLAINME/KNOWN-ISSUES/NAVIGATION/PROOF-NEEDS/ROADMAP → docs/, LICENSING-GUIDE/SECURITY-SETUP → docs/governance/, RSR_OUTLINE → docs/standards/RSR-OUTLINE.adoc), two new subdirs docs/architecture/ + docs/reference/, cross-refs fixed in CAPABILITY-MATRIX / BACKEND-IMPLEMENTATION / CONTRIBUTING / NAVIGATION / res-to-affine; root drops 17→5 community-health files. T-4 PR #366 — RSR_COMPLIANCE.adoc at root with four documented deviations (no guix.scm/flake.nix; STATE.scm substituted by .machine_readable/6a2/STATE.a2ml because .scm is reserved for Guix per language policy; 7 TS exemptions; 2 runtime exemptions — all cite CLAUDE.md as authoritative). T-5 PR #367 — wiki/README.md rewrite (drops ~20 dead links to non-existent .md files, updates stale ../ROADMAP.md → ../docs/ROADMAP.adoc path, annotates Features-at-a-Glance examples with current maturity, switches Ownership example from &File/&mut File sigil syntax to canonical ref File/mut File keyword types) + parse-only honesty banner on wiki/language-reference/dependent-types.md + partial honesty banner on wiki/language-reference/traits.md. T-6 — direct issue triage, no PR: closed #246 (ESC-02 JSON.t — stdlib/json.affine delivers Json ADT + encoders/decoders/get_field/stringify) and #247 (ESC-03 Dict.t — stdlib/dict.affine delivers empty/from_pairs/get/contains/size/insert/set/remove/keys/values), both with confirmation comments referencing the LANDED status in docs/TECH-DEBT.adoc STDLIB-02/03 rows. T-7 this PR — adds this session-note + the T-1..T-7 ledger entry below in docs/TECH-DEBT.adoc. (5) ALSO RESOLVED EARLIER IN SESSION (pre-T-6, as part of addressing the sustainabot tracker blockers the user asked about): #161 (Json) and #162 (Dict) closed with explicit owner-author-acknowledged closure comments dated 2026-05-24; this session was the explicit owner request to act on those. Migration-assistant tracker #57 stays open — Phase 2c lands #322's Phase 2b → 2c walker work but Phase 3 (partial translation of pure-structural forms — the phase the sustainabot tracker's exit criterion 3 specifically gates on) remains unstarted. (6) OPEN PRs AT SESSION END: #357 (Phase 2c, ready to merge — author/owner discretion on the baseline-red CI), #360/#365/#366/#367 (T-2/T-3/T-4/T-5, same baseline-red situation; pure-rename / pure-delete / pure-doc-add changes, no behavioural risk). (7) NOT DONE IN THIS SESSION: actual root-cause diagnosis of main's red build/lint; ~20 missing wiki pages (installation, hello-world, expressions, errors, package-manager, lsp, formatter, linter, stdlib subpages, design/*); guix.scm or flake.nix addition; full wiki content refresh beyond README + two banners; sync of wiki/ to the GitHub-wiki upstream repo (separate git repo, owner pushes manually). (8) CROSS-REPO: this session is AffineScript-only — the gitbot-fleet sustainabot tracker that prompted #161/#162/#57 lives in a different repo and was not touched here."