Commit Graph

153 Commits

Author SHA1 Message Date
709ce22290 Merge pull request 'Report prescan errors on their source lines' (#44) from 10 into main
Some checks failed
akbasic CI Build / coverage (push) Failing after 10m20s
akbasic CI Build / sanitizers (push) Failing after 10m48s
akbasic CI Build / cmake_build (push) Failing after 11m10s
akbasic CI Build / mutation_test (push) Failing after 14m37s
akbasic CI Build / akgl_build (push) Failing after 14m56s
Reviewed-on: #44
Reviewed-by: andrew <andrew@aklabs.net>
2026-08-06 12:51:00 -04:00
959daa2753 Merge branch 'main' into 10
Some checks failed
akbasic CI Build / mutation_test (push) Failing after 11m28s
akbasic CI Build / akgl_build (push) Failing after 11m46s
akbasic CI Build / coverage (push) Failing after 12m10s
akbasic CI Build / sanitizers (push) Failing after 12m21s
akbasic CI Build / cmake_build (push) Failing after 12m33s
2026-08-06 12:50:30 -04:00
f8cf198d35 Report prescan errors on their source lines
Some checks failed
akbasic CI Build / cmake_build (push) Has been cancelled
akbasic CI Build / sanitizers (push) Has been cancelled
akbasic CI Build / coverage (push) Has been cancelled
akbasic CI Build / akgl_build (push) Has been cancelled
akbasic CI Build / mutation_test (push) Has been cancelled
2026-08-06 12:47:31 -04:00
1decfd878d Merge pull request 'Make renumber scratch runtime-owned' (#50) from 12 into main
Some checks failed
akbasic CI Build / cmake_build (push) Has been cancelled
akbasic CI Build / sanitizers (push) Has been cancelled
akbasic CI Build / coverage (push) Has been cancelled
akbasic CI Build / akgl_build (push) Has been cancelled
akbasic CI Build / mutation_test (push) Has been cancelled
Reviewed-on: #50
2026-08-06 12:40:35 -04:00
899476c6dd Merge branch 'main' into 12
Some checks failed
akbasic CI Build / cmake_build (push) Successful in 3m30s
akbasic CI Build / sanitizers (push) Successful in 4m53s
akbasic CI Build / coverage (push) Successful in 3m55s
akbasic CI Build / akgl_build (push) Has been cancelled
akbasic CI Build / mutation_test (push) Has been cancelled
2026-08-06 12:40:18 -04:00
bb3060b29d Merge pull request 'Stop pointer parameters leaking value-pool slots' (#43) from 36 into main
Some checks failed
akbasic CI Build / cmake_build (push) Has been cancelled
akbasic CI Build / sanitizers (push) Has been cancelled
akbasic CI Build / coverage (push) Has been cancelled
akbasic CI Build / akgl_build (push) Has been cancelled
akbasic CI Build / mutation_test (push) Has been cancelled
Reviewed-on: #43
2026-08-06 12:38:28 -04:00
39a0c60bf3 Merge branch 'main' into 36
All checks were successful
akbasic CI Build / cmake_build (push) Successful in 3m25s
akbasic CI Build / coverage (push) Successful in 4m18s
akbasic CI Build / sanitizers (push) Successful in 5m4s
akbasic CI Build / akgl_build (push) Successful in 8m17s
akbasic CI Build / mutation_test (push) Successful in 23m54s
2026-08-06 12:38:05 -04:00
6896df1dff Use bounded string copies when renumbering
Some checks failed
akbasic CI Build / akgl_build (push) Has been cancelled
akbasic CI Build / mutation_test (push) Has been cancelled
akbasic CI Build / coverage (push) Has been cancelled
akbasic CI Build / cmake_build (push) Has been cancelled
akbasic CI Build / sanitizers (push) Has been cancelled
2026-08-06 12:37:37 -04:00
5c04edc5fb Document environment variable creation helpers
Some checks failed
akbasic CI Build / cmake_build (push) Successful in 3m35s
akbasic CI Build / akgl_build (push) Has been cancelled
akbasic CI Build / mutation_test (push) Has been cancelled
akbasic CI Build / sanitizers (push) Has been cancelled
akbasic CI Build / coverage (push) Has been cancelled
2026-08-06 12:33:31 -04:00
530898533d Add doxygen block for environment_create_named
Andrew flagged the new helper introduced for the value-pool-leak fix
as missing documentation. akbasic_environment_create() and
akbasic_environment_create_empty() are already documented in the
header; the shared static helper they both call was the only
undocumented new function definition.

Co-authored-by: andrew <andrew@aklabs.net>
2026-08-06 12:33:31 -04:00
fd6521d381 Stop pointer parameters leaking value-pool slots
Create structure parameters before allocating their representation, then keep pointer references in the call variable's inline slot. Add an 8,000-call regression and document the remaining by-value structure escape limitation.

Co-authored-by: andrew <andrew@aklabs.net>
Co-authored-by: OpenAI Codex (GPT-5) <noreply@openai.com>
2026-08-06 12:33:31 -04:00
4b5d97910c Merge pull request 'Implement generators: GEN, EMIT, END GEN, FOR EACH and DO EACH' (#61) from 57-implement-generators into main
All checks were successful
akbasic CI Build / cmake_build (push) Successful in 3m29s
akbasic CI Build / coverage (push) Successful in 3m58s
akbasic CI Build / sanitizers (push) Successful in 6m21s
akbasic CI Build / akgl_build (push) Successful in 8m23s
akbasic CI Build / mutation_test (push) Successful in 18m29s
2026-08-06 11:56:05 -04:00
Ishikawa
a29c7f34fe Fix generator teardown leaks, add RETURN-in-GEN and LOOP conditions on DO EACH
Review findings and follow-ups from PR #61 review:

- runtime_generator.c: akbasic_runtime_release_generator() now releases the
  forGeneratorEnv of every scope it walks through. Abandoning a generator
  that was itself suspended inside a FOR EACH over another generator
  stranded the inner generator's pool slot; a loop doing so exhausted the
  twelve-slot pool and died far from the cause.
- runtime.c/runtime.h: new akbasic_runtime_unwind_to_environment(), the
  shared teardown for the error unwinds in pump_generator() and
  call_function() -- both previously bare prev_environment() loops with the
  same suspended-generator blindness.
- runtime_commands.c: bare RETURN standing in a GEN's own frame ends the
  generator exactly as END GEN does -- a GEN is a function at heart. RETURN
  with a value there is refused (values leave a GEN only through EMIT). The
  no-frame error message now says "GOSUB, DEF, or GEN".
- runtime_structure.c: LOOP WHILE/UNTIL composes with DO EACH -- checked
  after each trip with the loop variable still holding that trip's value; a
  condition that stops the loop abandons the generator exactly as EXIT
  does. Previously the condition was silently ignored, while the verb
  reference documented it as working.
- parser_commands.c: trailing tokens after the generator call on a FOR
  EACH/DO EACH line are refused at parse. Previously they sat unparsed and
  blew up only after the loop completed, when the parent scope resumed the
  line mid-statement -- an error at the loop's end pointing at its start.
- tests/generators.c: pool-exhaustion tests for the nested-abandonment and
  LOOP-condition paths, RETURN semantics tests, and a direct test of the
  unwind primitive. Three new golden pairs cover RETURN, LOOP conditions
  and the misplaced-condition parse error.
- docs: RETURN and LOOP-condition semantics in 04-control-flow.md and
  11-verb-reference.md; corrected the self-recursion analogy (functions
  are re-entrant here). TODO.md 1.10 records the generator design
  decisions the code comments were already citing, plus the zero-arg
  parameter-list limitation. MAINTENANCE.md gains the abandoned-generators
  invariant those comments also cited.

Co-Authored-By: Andrew Kesterson <andrew@aklabs.net>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 11:55:59 -04:00
f7e8d4b82b Implement generators: GEN, EMIT, END GEN, FOR EACH and DO EACH (issue 57)
All checks were successful
akbasic CI Build / cmake_build (push) Successful in 3m32s
akbasic CI Build / coverage (push) Successful in 4m7s
akbasic CI Build / sanitizers (push) Successful in 4m42s
akbasic CI Build / akgl_build (push) Successful in 8m12s
akbasic CI Build / mutation_test (push) Successful in 23m3s
Adds generator support per the plan in issue 57:

- environment.h: isGenerator/generatorFn on a GEN call's own environment,
  isEachLoop and forGeneratorEnv on a FOR EACH/DO EACH loop's own
  environment.
- runtime.c: splits akbasic_runtime_prev_environment() into
  akbasic_runtime_detach_environment() (return to parent without releasing)
  and akbasic_runtime_release_environment() (give variables and the pool
  slot back, on any environment); prev_environment() is now the two in
  sequence. akbasic_runtime_call_function() refuses to call a GEN like an
  ordinary function.
- verbs.c/verbs.h/scanner: new keywords GEN, EMIT, EACH, IN and the compound
  verb "END GEN" (built by a new akbasic_parse_end(), the same trick
  akbasic_parse_print() uses for PRINT #).
- parser_commands.c: akbasic_parse_gen() (modelled on multi-line DEF),
  akbasic_parse_end(), and EACH branches in akbasic_parse_for()/
  akbasic_parse_do().
- runtime_generator.c (new): akbasic_cmd_gen, akbasic_cmd_emit,
  akbasic_cmd_end_gen, and the invoke/pump/release machinery FOR EACH, DO
  EACH, NEXT and LOOP share. EMIT walks up to the nearest isGenerator
  ancestor rather than assuming it is standing directly in the GEN's own
  call frame, because a GEN body may nest its own FOR/DO/GOSUB around an
  EMIT -- the issue's own ROOMOBJECTS example does exactly that.
- runtime_commands.c/runtime_structure.c: EACH branches in cmd_for/cmd_do,
  matching EACH branches in cmd_next/cmd_loop, and forGeneratorEnv release
  on every path that can abandon a live generator (EXIT, a NEXT that pops
  for a mismatched loop variable).

Deviates from the plan in one place: FunctionDef gained an isGenerator flag
(not in the plan's field list) because refusing a GEN called like a
function has to happen before anything is pushed. Relying on EMIT's own
isGenerator check for that case doesn't work: akbasic_runtime_call_function()
drives its own step loop the same way akbasic_runtime_pump_generator() does,
and a BASIC-level error inside that loop is swallowed by process_line_run()
as reported-but-not-propagated, so the call would silently "succeed" with a
meaningless return value instead of failing.

Also: a zero-argument parameter list is not supported by the DEF/GEN
parameter parser this reuses (a pre-existing limitation, not
generator-specific); every generator in the tests takes at least one
parameter as a result.

Tests: tests/generators.c (pool exhaustion under repeated EXIT, calling a
GEN like a function, EMIT outside a GEN, self-recursion, sibling/nested
invocations) and tests/language/flowcontrol/generators_*.bas -- the
issue's own ROOMOBJECTS example in both loop shapes, an empty generator,
non-numeric EMIT, nested/interleaved invocations, and three error-path
golden cases. Docs: control-flow chapter 4 gets a GEN/EMIT/FOR EACH/DO EACH
section, the verb reference gets GEN/EMIT/END GEN entries and updated
FOR/DO/NEXT/LOOP/EXIT rows, and architecture chapter 14 documents the
detach/release split and the two-environment generator invocation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 10:02:43 -04:00
11e4e96daf Schedule mutation testing outside push CI
All checks were successful
akbasic CI Build / cmake_build (push) Successful in 3m22s
akbasic CI Build / sanitizers (push) Successful in 4m36s
akbasic CI Build / coverage (push) Successful in 4m10s
akbasic CI Build / akgl_build (push) Successful in 8m31s
akbasic CI Build / mutation_test (push) Successful in 22m4s
Move the full mutation suite into a dedicated daily 02:00 EST workflow so merge and release checks do not consume CI agents for hours.
2026-08-06 08:51:35 -04:00
cdb9be03c6 Merge pull request 'Fix Doxygen gate failures on main' (#60) from 56-doxygen-gate-fixes into main
Some checks failed
akbasic CI Build / cmake_build (push) Successful in 3m22s
akbasic CI Build / sanitizers (push) Successful in 4m36s
akbasic CI Build / coverage (push) Successful in 4m35s
akbasic CI Build / akgl_build (push) Has been cancelled
akbasic CI Build / mutation_test (push) Has been cancelled
Reviewed-on: #60
2026-08-06 08:45:37 -04:00
d047ef9c80 Merge pull request 'Take libakerror 2.0.2, libakstdlib and libakgl 0.9.0' (#59) from deps-update into main
Some checks failed
akbasic CI Build / sanitizers (push) Has started running
akbasic CI Build / akgl_build (push) Has been cancelled
akbasic CI Build / mutation_test (push) Has been cancelled
akbasic CI Build / cmake_build (push) Has been cancelled
akbasic CI Build / coverage (push) Has been cancelled
Reviewed-on: #59
2026-08-06 08:44:56 -04:00
9a22fde5ee Pull latest libakerror: static TLS fix landed
All checks were successful
akbasic CI Build / cmake_build (push) Successful in 3m25s
akbasic CI Build / sanitizers (push) Successful in 4m48s
akbasic CI Build / coverage (push) Successful in 4m18s
akbasic CI Build / akgl_build (push) Successful in 8m6s
akbasic CI Build / mutation_test (push) Successful in 16m49s
libakerror main now includes the fix for issue #37 (per-TU static TLS
akerr_last_ignored -> single extern definition in src/error.c, one copy
per thread instead of one per translation unit) and issue #38 (project()
now stamps 2.0.3, matching the release notes).

Bump deps/libakerror to origin/main (9249f8c) and update MAINTENANCE.md:
drop the now-stale 'IGNORE change costs' subsection and the 2.0.2/2.0.3
version-disagreement note, since both are resolved upstream.

Verified: build/basic has no TLS segment and zero -Wunused-variable
warnings for akerr_last_ignored; 114/114 tests pass.

Co-Authored-By: Andrew Kesterson <andrew@aklabs.net>
2026-08-06 07:25:14 -04:00
809a9bf64d Fix Doxygen gate failures on main
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 3m41s
akbasic CI Build / sanitizers (push) Failing after 5m59s
akbasic CI Build / coverage (push) Failing after 4m38s
akbasic CI Build / akgl_build (push) Failing after 8m20s
akbasic CI Build / mutation_test (push) Failing after 3m56s
Four independent defects that made 'doxygen Doxyfile' exit 1:

- runtime.h: a stacked doc block above akbasic_runtime_new_function
  was left directly above akbasic_runtime_call_function's declaration
  with no function in between, so Doxygen glommed both blocks onto
  call_function and duplicated the @param obj/@param dest entries.
  Moved the block back to sit directly above new_function.
- sprite.h/akgl.h/sprite_akgl.c: three #AKGL_*/#AKBASIC_SHAPE_* Doxygen
  autolinks pointed at libakgl symbols outside this Doxyfile's INPUT,
  so they could never resolve. Converted them to plain code spans.
- scanner.c: peek(), peek_next() and match_next_char() were each
  missing @param obj (and match_next_char also cm/truetype/falsetype).
  Filled in the missing entries.
- Doxyfile: the error.h include graph hit DOT_GRAPH_MAX_NODES at
  exactly the default of 50. Raised it to 100.

Verified 'doxygen Doxyfile' now exits 0 with graphviz installed.

Refs #56

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 23:48:07 -04:00
13f1df03ef Unbreak the three example programs the RND merge left behind
All checks were successful
akbasic CI Build / cmake_build (push) Successful in 3m46s
akbasic CI Build / coverage (push) Successful in 4m16s
akbasic CI Build / sanitizers (push) Successful in 8m10s
akbasic CI Build / akgl_build (push) Successful in 8m10s
akbasic CI Build / mutation_test (push) Successful in 17m55s
Adding native RND and ASC (ae2c702) made RND a function name, and a suffixed
identifier that collides with one is refused -- "SYNTAX ERROR Reserved word in
variable name". Three example programs held their PRNG output in a variable
called RND#, or a host field called RND%, and none of them had run since:

  - examples/galaga/ bound RND% as a host field on both ENEMY and GAME. The
    BASIC-visible name is ROLL% now; the C member stays `rnd`. This one was
    caught by example_galaga and example_galaga_interop, which have been
    failing.
  - examples/breakout/characters/breakout.bas and examples/megademo/
    megademo.bas both use RND# for their LCG output, renamed to ROLL#. Neither
    is in any test, so neither failure was visible.

examples/breakout/sprites/breakout.bas was broken a second way: seven REM lines
the reader refuses. Worth recording that the ceiling is not the one the message
names -- src/sink_stdio.c fails when the read filled the buffer without seeing
a terminator, so with AKBASIC_MAX_LINE_LENGTH at 80 the message says "79
character limit" and the real maximum is 78, because a 79-character line leaves
no room for the newline. The sweeps that fixed the corpus and the megademo for
this did not reach this file. The seven comments are reflowed.

The prose went stale with the code. Chapter 21 said "there is no RND verb in
this dialect; issue #16 tracks adding one", chapter 17's historical aside
offered an LCG that no longer parses, and four REM blocks across the two games
said the same thing. All of them now say RND exists, and say why these programs
keep their own generator anyway: the sequence has to be reproducible for a
headless run to be the same game on every machine, which is what lets
interop_test.c assert exact counts.

Chapter 21 also gains the rule that bit them, since a reader writing a host
type will hit it: a host field name is a bare word and shares a namespace with
every verb and function.

None of this came from the submodule bump -- all three were already broken on
main. It was found by running the tutorial games, which nothing else does;
that gap is akbasic issue #58.

Verified: all three run clean under the dummy drivers, and 114/114 default,
116/116 with akgl.

Co-Authored-By: Andrew Kesterson <andrew@aklabs.net>
Co-Authored-By: Claude Code (Claude Opus 5, claude-opus-5[1m]) <noreply@anthropic.com>
2026-08-05 23:26:33 -04:00
a15e172c2a Take libakerror 2.0.2, libakstdlib and libakgl 0.9.0
Bumps all three ak* submodules to their current main, applies what their
upgrade notes require, and retires the two workarounds they make obsolete.

libakerror 2.0.1 -> 2.0.2 (63 commits). Two of its fixes were this
repository's own filed issues, and both workarounds are gone:

  - It namespaces its embedded `coverage` target now, the same
    CMAKE_SOURCE_DIR test it already applied to `mutation` (its issue #15).
    The add_custom_target() shadow that renamed it on the way past could
    only ever fire on a name the dependency has stopped using, so it is
    deleted rather than left as dead code.
  - It installs akerrorConfigVersion.cmake at SameMajorVersion (its issue
    #16). MAINTENANCE.md said to add a `1.0` floor to our find_dependency
    calls when this landed; the floor is now `2.0`, and we have no
    find_dependency calls to add it to, so the paragraph says that instead
    of an instruction nobody can follow.

Its IGNORE context also changed shape: `__akerr_last_ignored` was an extern
pointer, and is now a per-translation-unit `static akerr_last_ignored` holding
a copy, so the pool slot can be released. Nothing here referenced the symbol,
but it costs us 1.35 MiB of thread-local storage -- 38 TUs x 37,296 bytes,
measured as the entire TLS segment of build/basic, where 2.0.1 produced no TLS
segment at all -- plus 84 -Wunused-variable warnings. Filed upstream as
libakerror issue #37 and recorded in MAINTENANCE.md rather than patched here,
because patching a submodule forks it.

libakstdlib gains directory and file-metadata wrappers with no version bump.
aksl_snprintf keeps its `int *count` -- an intermediate commit removed it and
the merge put it back -- but now reports the required length on truncation
rather than 0. Every call site here reads it only after a successful return,
so nothing moved.

The directory wrappers close the gap DIRECTORY was refused for (libakstdlib
issue #10). The verb is still unwritten, so it still refuses, but it no longer
blames a wrapper that exists: the message is "DIRECTORY is not implemented
yet" and tests/disk_verbs.c asserts both that it says so and that it does not
name libakstdlib. What writing it would need is akbasic issue #55.

libakgl moves to the current main at 0.9.0. It registers libccd and tg as
submodules, so a tree that only ran `git submodule update --init --recursive`
before the bump needs it again or the configure fails on a missing
libccd/src/ccd/config.h.cmake.in.

Verified: 114/114 default, 116/116 under -DAKBASIC_WITH_AKGL=ON, docs_examples
green in both. libakerror's UPGRADING.md documents a 2.0.3 that project()
never stamped, so the version tables read 2.0.2 -- libakerror issue #38.

Co-Authored-By: Andrew Kesterson <andrew@aklabs.net>
Co-Authored-By: Claude Code (Claude Opus 5, claude-opus-5[1m]) <noreply@anthropic.com>
2026-08-05 23:26:15 -04:00
ae2c702c2b Merge pull request 'Add native RND and ASC functions' (#45) from 16 into main
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 3m29s
akbasic CI Build / sanitizers (push) Failing after 4m43s
akbasic CI Build / coverage (push) Failing after 3m53s
akbasic CI Build / mutation_test (push) Failing after 3m38s
akbasic CI Build / akgl_build (push) Failing after 8m57s
Reviewed-on: #45
Reviewed-by: andrew <andrew@aklabs.net>
2026-08-05 21:24:35 -04:00
5c58f4c16d Add native RND and ASC functions
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 3m41s
akbasic CI Build / coverage (push) Failing after 3m54s
akbasic CI Build / sanitizers (push) Failing after 4m52s
akbasic CI Build / mutation_test (push) Failing after 3m47s
akbasic CI Build / akgl_build (push) Failing after 8m41s
Implement bounded random integers with lazy clock seeding and add ASC as the inverse of CHR. Cover dispatch, validation, deterministic LCG output, UTF-8 round trips, function reference, and the breakout tutorial.

Closes #16.

Co-authored-by: andrew <andrew@aklabs.net>
2026-08-05 21:17:47 -04:00
e20fd445a4 Make renumber scratch runtime-owned
Some checks failed
akbasic CI Build / cmake_build (push) Successful in 4m21s
akbasic CI Build / akgl_build (push) Failing after 12m38s
akbasic CI Build / coverage (push) Failing after 12m56s
akbasic CI Build / sanitizers (push) Failing after 13m5s
akbasic CI Build / mutation_test (push) Successful in 18m7s
Co-authored-by: Andrew Kesterson <andrew@starfort.tech>
2026-08-05 18:36:08 -04:00
ed6f463897 Merge pull request 'A GALAGA tutorial: C/libakgl engine with akbasic embedded as the enemy-behavior engine' (#37) from galaga-tutorial into main
All checks were successful
akbasic CI Build / cmake_build (push) Successful in 3m32s
akbasic CI Build / sanitizers (push) Successful in 4m38s
akbasic CI Build / coverage (push) Successful in 3m52s
akbasic CI Build / akgl_build (push) Successful in 8m23s
akbasic CI Build / mutation_test (push) Successful in 17m49s
Reviewed-on: #37
2026-08-04 20:08:14 -04:00
d5647758d0 Rework the megademo to fit the 80-column source line limit
All checks were successful
akbasic CI Build / cmake_build (push) Successful in 3m32s
akbasic CI Build / coverage (push) Successful in 4m17s
akbasic CI Build / sanitizers (push) Successful in 4m39s
akbasic CI Build / akgl_build (push) Successful in 8m39s
akbasic CI Build / mutation_test (push) Successful in 23m48s
AKBASIC_MAX_LINE_LENGTH's cut from 256 to 80 left seventeen lines of
examples/megademo unloadable: the sixteen IM$() picture strings (up to
252 characters) and TUNEA/TUNEB's four-bar PLAY strings (174 and 175).
The real ceiling is 78 characters, not 80 -- stdio_readline() refuses a
read that fills the 80-byte buffer without a newline, so content plus
its terminator must fit in 79.

The picture: vaporwave.py's PAYLOAD drops from 240 to 64, so every
emitted IM$(NN) = "..." line fits under the ceiling. chop() no longer
slices blind; it walks the stream a record at a time -- two characters
for a run, three for an R row record -- and never cuts inside one,
because the decoder reads a record's tail with MID on the string it is
walking and a record straddling two IM$ entries decodes as garbage.
The old blind slice at 240 only happened to be safe. verify() now
simulates the CHOPPED strings with the cursor threaded across the
boundaries exactly the way DRAWSTREAM executes them, so a bad cut is
an assertion failure instead of a corrupted screen, and emit_block()
asserts every emitted line fits. The picture is 56 strings where it
was 16; the decoder needed no changes at all, since it already carries
X#/Y# from one IM$ entry to the next.

The music: TUNEA and TUNEB each become four PLAY statements, one bar
apiece. play.c keeps voice, envelope, level and duration state on the
runtime across statements and every PLAY appends to the same queue, so
four bars queue exactly as one long string did. Each bar restates the
V1T3U9S prefix so a bar dropped by QFULL cannot leave the next batch
playing on the drum kit's envelope.

Everything still clears the shrunken pools with room to spare: 1625
source lines of 2048, ~704 array slots of 2048, identifiers within the
24-character symtab key. Verified end to end against this branch's
build: the demo loads, the offscreen host renders every scene, and the
scene-5 still is pixel-identical to vaporwave.py's own preview. The
test suite fails the same seventeen cases with and without this
commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACffnV6F7sxQuG3Y8a1L3s
2026-08-04 20:05:41 -04:00
1a7226eef1 Keep BASIC fixtures within the input line limit
Reflow the approved reference cases, shorten local fixture comments, and split the executable docs line so the 80-byte input buffer can read every source line.

Co-authored-by: andrew <andrew@aklabs.net>
2026-08-04 20:05:41 -04:00
4bfcf72240 revert 3a8478131a
revert Register line-limit fixtures as expected failures

This issue is not something this branch should solve
2026-08-04 20:05:41 -04:00
6ba8a59c5e Register line-limit fixtures as expected failures
Keep the two immutable reference cases and fourteen local cases that exceed the current 80-column input contract in CTest, but mark their existing failures as expected until issue #32 is resolved. This keeps every CI configuration green without editing the reference corpus or weakening the runtime limit.\n\nCo-authored-by: andrew <andrew@aklabs.net>
2026-08-04 20:05:41 -04:00
bb2d896e4b Close the remaining cold-read gaps: bind, labels, menus and main's shape
Three more Haiku-class cold reads of the chapters, each against the
amended text. What each surfaced is now shown rather than described: the
akbasic_host_register_type()/akbasic_host_bind() boot calls, the
declare_play() label listing, the akgl_UiMenu static and its
handle_event signature, one control-handler pair, and main()'s
ATTEMPT/HANDLE_DEFAULT/FINISH_NORETURN shape with the CATCH-inside-
ATTEMPT rule stated. By the fourth read the generated player.c and
enemies.c compiled untouched and every remaining guess was a tuning
value the chapters deliberately leave open.

Co-authored-by: andrew <andrew@aklabs.net>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XiGgpHuXUm2mR4Wzndw3dc
2026-08-04 20:05:41 -04:00
3183908012 Benchmark the boundary and close the cold read's tutorial gaps
The interop test now ends with a measured comparison: 24,000 formation
updates through the script boundary against a line-for-line C
translation of the same state machine. 881 us against 0.01 us per call
on this machine, quoted verbatim in the new chapter 21 Step 11 with the
architectural decisions it prices.

A Haiku-class cold read of the chapters produced a build whose failures
were all mechanical -- invented include paths, never-shown sink statics,
guessed status codes and character names. The chapters now carry the
include lists, the script.c statics, the status-code roster, the
sprite/character table, the full CMake recipe and the explosion spawn's
HANDLE example, so none of those have to be guessed again.

Co-authored-by: andrew <andrew@aklabs.net>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XiGgpHuXUm2mR4Wzndw3dc
2026-08-04 20:05:41 -04:00
ed99d38534 Write the GALAGA tutorial chapters and the repeated-host-calls guide
docs/20 builds the engine and the boundary: the startup order, the
starfield, actors and collision, booting a DEF-only script, the issue #8
mode workaround, the custom update hook, first light, screens, and the
headless harness. docs/21 builds the three shared structures and the AI:
the host type tables, the actor binding, the randomness route around
issue #16, the measured case against structure arguments (issue #36),
the three language rules that shape the script, the maneuvers, the
argued formation decision, the script-death policy, and the interop
proof. Every fenced block runs under tests/docs_examples.sh in both
build configurations; five new preludes carry the C fragments.

docs/10 gains the 'Calling a function every frame' section the chapters
lean on: the per-call akbasic_environment_zero() rule, the set_mode(RUN)
workaround, the clear_error() revival, and the case for rebinding over
structure arguments. Index rows and chapter counts updated.

Co-authored-by: andrew <andrew@aklabs.net>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XiGgpHuXUm2mR4Wzndw3dc
2026-08-04 20:05:41 -04:00
36fa1285b4 Update the breakout chapter's scope-pool figure to this branch's 12
The environment pool shrank from 32 to 12 in the memory-reduction work
and the chapter's exhaustion transcript still asserted the old number,
which is a docs_examples failure on every run of this branch.

Co-authored-by: andrew <andrew@aklabs.net>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XiGgpHuXUm2mR4Wzndw3dc
2026-08-04 20:05:41 -04:00
8ef0b253b2 Add the galaga example: a C engine with akbasic as its enemy brain
A GALAGA-style fixed shooter whose engine is C on libakgl (null physics)
with the interpreter embedded as the scripting engine that owns every
enemy's behavior. One DEF-only script is called per enemy per frame
through a custom akgl_Actor update hook; SELF@, ACTOR@ and GAME@ are host
bindings, so the script reads and writes the engine's real memory -- the
boss even swaps its own damage sprite by raising an actor state bit from
BASIC. Bullets, collision, scoring and screens stay C.

Structure arguments were measured and rejected for the per-frame path:
each pointer parameter spends a value-pool slot the pool never reclaims,
1,015 calls to exhaustion against an unbounded rebind (issue #36).

Built when AKBASIC_WITH_AKGL=ON. Two CTest entries: a 600-frame headless
autoplay run under the dummy SDL drivers, and an interop round-trip test
that links the real script.c and galaga.bas and pins the four boundary
claims, 24,000 sustained calls among them. docs_galaga_figures
regenerates the two checked-in figures. Art is Kenney CC0, byte for
byte, with provenance.

Co-authored-by: andrew <andrew@aklabs.net>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XiGgpHuXUm2mR4Wzndw3dc
2026-08-04 20:05:41 -04:00
61e08812e3 Reset scratch per line and unwind dead scopes in host function calls
akbasic_runtime_call_function()'s body loop drives process_line_run()
directly, skipping the per-line prologue akbasic_runtime_step() provides.
The call environment's value scratch therefore accumulated across the
whole body, and any body past about ten real lines died with 'Maximum
values per line reached' -- a limit that is supposed to be per line. The
loop now runs the same prologue step() does.

A body that died also left its call scopes active: nothing popped them,
so a host absorbing script errors drained the twelve-slot environment
pool after twelve dead calls. The loop now unwinds to the caller's
environment on every exit path.

New: akbasic_runtime_clear_error(), the missing half of host revival. A
run's first BASIC-level error latches deliberately, and set_mode(RUN)
alone cannot un-decide that; a host that absorbed the error calls this
beside it. Both defects and the revival dance are pinned in
tests/user_functions.c.

Co-authored-by: andrew <andrew@aklabs.net>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XiGgpHuXUm2mR4Wzndw3dc
2026-08-04 20:05:41 -04:00
a0ac978541 Reset the scanner's leftover token type between lines
The REM early-exit leaves tokentype holding AKBASIC_TOK_REM, and the scan
loop's post-switch check reads it before the next line's first character
has assigned anything. A line opening with whitespace then re-triggered
the REM break and scanned to nothing: every indented line after a REM was
silently skipped. Numbered programs never saw it -- the line number is the
first token and overwrites the leftover -- which is why the whole golden
corpus missed it and the unnumbered, indented galaga.bas found it.

Co-authored-by: andrew <andrew@aklabs.net>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XiGgpHuXUm2mR4Wzndw3dc
2026-08-04 20:05:41 -04:00
f9742ed5a5 Cut akbasic_Runtime's static footprint from 10.75 MiB to 2.40 MiB
Nothing in this interpreter mallocs; every pool is a fixed array sized by an
AKBASIC_MAX_* constant, so sizeof(akbasic_Runtime) is a compile-time number
and most of it was headroom nobody was using. Measured concurrent-use
high-water marks off examples/breakout and examples/megademo -- the two most
demanding programs this interpreter runs -- against each pool's ceiling:

  AKBASIC_MAX_ENVIRONMENTS   32 -> 12    (measured peak concurrency: 6-7)
  AKBASIC_MAX_FUNCTIONS      64 -> 8     (measured: 0, neither program uses DEF FN)
  AKBASIC_MAX_ARRAY_VALUES 4096 -> 2048  (measured peak: 1618 slots)
  AKBASIC_MAX_SOURCE_LINES 9999 -> 2048  (measured: ~1270-1496 non-blank lines)
  AKBASIC_SYMTAB_MAX_SLOTS  256 -> 172   (no caller ever requests more than 128)
  AKBASIC_SYMTAB_MAX_KEY     64 -> 24    (longest identifier measured: 11 chars)
  AKBASIC_MAX_LINE_LENGTH   256 -> 80    (Commodore BASIC's own line limit)

AKBASIC_MAX_VARIABLES (128) is untouched on purpose: breakout alone reaches
121 of 128 concurrent named variables, so it has the least slack of any pool
measured and is not a shrink candidate.

akbasic_Variable.name shrinks from AKBASIC_MAX_STRING_LENGTH (256) to
AKBASIC_SYMTAB_MAX_KEY: every variable name is registered with
akbasic_symtab_set() right after this field is populated
(akbasic_environment_create(), src/environment.c), and that call already
refuses anything AKBASIC_SYMTAB_MAX_KEY characters or longer. The wider field
was headroom nothing could ever put a byte into.

Two defects surfaced while testing the line-length drop against the golden
corpus, both fixed here because the 80-byte ceiling makes them routine rather
than theoretical:

- sourcepath (runtime.h) was borrowing AKBASIC_MAX_LINE_LENGTH by accident.
  It holds a directory, not a line of BASIC, and this checkout's own test
  paths are 81+ characters deep -- every golden test failed to load until
  this split into its own AKBASIC_MAX_SOURCE_PATH_LENGTH, backed by PATH_MAX
  the way libakerror already sizes its own path buffers.

- src/sink_stdio.c's stdio_readline() called aksl_fgets() but never checked
  its own documented contract: a full buffer with no trailing newline means
  the line was longer than the buffer, and the unread remainder is still in
  the stream. Unchecked, the next readline() picks that remainder up as its
  own statement -- a real line silently becomes two wrong ones instead of a
  clean AKBASIC_ERR_BOUNDS refusal. At 256 bytes this was theoretical; at 80
  it is not, so it now refuses loudly.

tests/value_pool.c's test_pool_is_untouched_by_scopes() was pinned to the old
4x1024=4096 pool math (four max-size arrays proving nothing leaked); rewritten
to 2x1024=2048 for the same proof against the new AKBASIC_MAX_ARRAY_VALUES.

Known consequence, tracked in andrew/akbasic#32 rather than worked around
here: two files in the protected tests/reference/ corpus
(language/functions/mod.bas, language/flowcontrol/nestedforloopwaitingfor
command.bas) have 82-character lines and cannot be shortened -- MAINTENANCE.md
and CMakeLists.txt:585 are explicit that tests/reference/ is never edited to
suit this interpreter. Twelve tests/language/ cases and one docs/18 line are
in the same position but are this project's own content. Shipping 80 anyway,
with the fallout tracked rather than hidden, was an explicit call on this PR
rather than something decided here.

Verified: cmake --build build-akgl && ctest --test-dir build-akgl, 97/112 (15
known failures, all AKBASIC_MAX_LINE_LENGTH-related, filed as #32).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Andrew Kesterson <andrew@aklabs.net>
2026-08-04 20:05:41 -04:00
350deb3a45 Merge pull request 'Move BASIC fixtures into the editable language corpus' (#42) from 40 into main
All checks were successful
akbasic CI Build / cmake_build (push) Successful in 4m2s
akbasic CI Build / sanitizers (push) Successful in 4m59s
akbasic CI Build / coverage (push) Successful in 4m35s
akbasic CI Build / akgl_build (push) Successful in 9m17s
akbasic CI Build / mutation_test (push) Successful in 29m15s
Reviewed-on: #42
2026-08-04 16:30:25 -04:00
8a02674af5 Move BASIC fixtures into the editable language corpus
All checks were successful
akbasic CI Build / cmake_build (push) Successful in 3m34s
akbasic CI Build / coverage (push) Successful in 4m4s
akbasic CI Build / sanitizers (push) Successful in 6m59s
akbasic CI Build / akgl_build (push) Successful in 7m57s
akbasic CI Build / mutation_test (push) Successful in 23m28s
Move every program and expectation out of tests/reference and register the unified tests/language corpus as local cases. Remove the old immutable-corpus protections from build, maintenance, and documentation paths.

Co-authored-by: andrew <andrew@aklabs.net>
2026-08-04 16:22:16 -04:00
fac84acdaa Merge pull request 'Port onto libakstdlib 2b79aca and convert the eight bool predicates (libakstdlib#26)' (#31) from libakstdlib-26 into main
All checks were successful
akbasic CI Build / cmake_build (push) Successful in 3m39s
akbasic CI Build / sanitizers (push) Successful in 5m1s
akbasic CI Build / coverage (push) Successful in 4m47s
akbasic CI Build / akgl_build (push) Successful in 8m7s
akbasic CI Build / mutation_test (push) Successful in 29m11s
Reviewed-on: #31
Reviewed-by: andrew <andrew@aklabs.net>
2026-08-03 21:25:07 -04:00
0d6a817b7c Bound CI build parallelism so five concurrent jobs stop starving the runner
All checks were successful
akbasic CI Build / cmake_build (push) Successful in 3m47s
akbasic CI Build / coverage (push) Successful in 4m4s
akbasic CI Build / sanitizers (push) Successful in 5m1s
akbasic CI Build / akgl_build (push) Successful in 9m29s
akbasic CI Build / mutation_test (push) Successful in 22m11s
Run #31's sanitizers job failed, and not on anything in src/. The ASan build
reached 100% and the runner's Docker daemon went away before ctest emitted a
single line: "Cannot connect to the Docker daemon at unix:///var/run/docker.sock".

The tree under test is identical to run #30's -- 9e5496f touched only this
file -- and run #30's sanitizers job passed 112/112 in 83 s. What changed is
that #30 was still losing akgl_build and coverage early, so #31 was the first
push on which all five jobs did real work at the same time. All five say
runs-on: ubuntu-latest, so all five share one runner, and every build step
asked for a bare --parallel, i.e. nproc compilers each.

The timestamps are unambiguous. Through 20:36:16 the sanitizers job links an
executable in well under a second. akgl_build starts at 20:36:02 and begins
compiling SDL, SDL_image, SDL_mixer, SDL_ttf, libccd and clay; mutation_test
starts at 20:36:41. From 20:36:16 onward a single `Linking C executable` step
takes one to two minutes -- a sixtyfold regression with no source change --
and at 20:49:55 the daemon is gone. ASan links are memory-hungry and the SDL
tree is large; the runner ran out.

The only lever on that is how many compiler and linker processes exist at
once, so bound each build to 2. Worst case is now 8 rather than 5 x nproc.
mutation_test drives its own builds through the harness and is left alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 21:23:32 -04:00
78b7b55103 Clone libakgl's other two submodules, and pin upload-artifact to v3
Two more gaps that only became visible once the errno fix let the jobs
run far enough to hit them.

akgl_build cloned six of libakgl's submodules and needs eight. libakgl
does not add_subdirectory deps/libccd or deps/clay -- their own
CMakeLists are unusable as subprojects -- it compiles them into itself,
so nothing declares them and configuration dies at
deps/libakgl/CMakeLists.txt:282 with "File
deps/libccd/src/ccd/config.h.cmake.in does not exist". deps/tg stays out:
nothing references it.

The X11 packages from the previous commit did their job -- SDL now
configures the lot: "X11 libraries: xcursor xdbe xfixes xinput2 xrandr
xscrnsaver xshape xsync xtest".

coverage passes its gate now (112/112, lines 94.1% against a 90 floor)
and failed on the upload instead. actions/upload-artifact@v4 bundles
@actions/artifact v2, whose isGhes() treats any GITHUB_SERVER_URL that is
not github.com as GitHub Enterprise Server and refuses outright. @v3 uses
the older artifact API, which Gitea 1.25.3 does implement. The step had
never had a file to upload before -- gcovr was never reached, so it
warned "No files were found" and passed -- which is why this surfaced
only now. Same class of accommodation as annotate_only on the junit
reporter.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 21:23:32 -04:00
c8d90c2743 Install the build dependencies CI has been missing since run #2
Every job in ci.yaml has failed on every run since #2, on main and on
every branch, for two reasons that have nothing to do with any of the
code under test.

moreutils, for errno(1). deps/libakerror/scripts/generrno.sh shells out
to it to generate its errno name table and to stamp
AKERR_LAST_ERRNO_VALUE into the generated akerror.h. The runner does not
have it, and its absence does not fail the build -- which is what made
this expensive to find. The script prints "errno: command not found",
emits an empty akerr_init_errno() so no errno ever gets a name, and
substitutes an empty AKERR_LAST_ERRNO_VALUE, so every code in
libakerror's reserved band collapses from (134 + n) to ( + n), i.e. to n.
AKERR_OUTOFBOUNDS is (AKERR_LAST_ERRNO_VALUE + 2) and therefore becomes
literally 2, so ERR(2) answers "Out Of Bounds Error" where ENOENT's "No
such file or directory" belongs. That is the docs_examples failure at
docs/15-error-codes.md:105, and it took cmake_build, sanitizers and
coverage down with it; mutation_test then aborted on "baseline is not
green" because the same suite is its baseline.

Reproduced locally by shimming errno(1) out of PATH: identical failure,
same test #47, same two lines of diff. With errno present the suite is
112/112.

The nine X11 dev packages, for akgl_build, which has never once been
green. SDL_X11 and its sub-options default ON on Linux, and CheckX11()
calls SDL_missing_dependency() -- a hard CMake error, not a downgrade --
for any whose header is absent. The runner has libx11-dev but none of
the extension packages, so configuration stopped at the first of them:
"Couldn't find dependency package for XCURSOR". The list is SDL's own,
from deps/SDL/docs/README-linux.md, reduced to the X11 entries; the
audio, Wayland, KMSDRM and Vulkan backends all degrade to "not found"
and skip themselves rather than erroring, so they cost nothing to leave
out. Turning the missing options OFF would also configure and is the
wrong answer -- the point of that job is that an AKGL build is a real
SDL program. The tests still run headless under SDL_VIDEODRIVER=dummy.

release.yaml's full_mutation job builds the same tree and gets moreutils
for the same reason.

No source file changes. The port itself was never the problem.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 21:23:32 -04:00
b434be1901 Port onto libakstdlib 2b79aca and convert the eight bool predicates
akbasic's src/ now calls libakstdlib 313 times and raw libc 7 -- 2.2%
bypassed, against 86.4% on the same tree before this. The submodule bump
669b2b3 -> 2b79aca needed no source change of its own: the release is
drop-in for what akbasic already used.

Seven of the eight sites the earlier port left on raw libc change their own
signature rather than swallowing an error, per andrew's ruling on
libakstdlib#38. word_is, the is_waiting_for pair, the scanner's is_at_end,
peek, peek_next and match_next_char, format.c's overflow, and sink_akgl's
scroll/newline/putchar_at/echo_line/edit_key chain all return an
akerr_ErrorContext * and hand the answer back through an out parameter.
is_waiting_for and is_waiting_for_any are a public header change; every
call site that used one as a term in a condition hoists it into a
statement first.

verb_compare is the eighth and stays on strcmp. bsearch(3) fixes the
comparator's signature, so there is no out parameter to report through --
which is what libakstdlib#38 concluded. It carries a comment saying so and
saying why the bypass is safe there.

Six snprintf sites stay raw because they want truncation as an answer
rather than an error, and aksl_snprintf cannot express that until
libakstdlib#34 hands the required length back. Each of the six says so at
the site. Two of them, in host.c, are a latent defect rather than a
decision: a host type name over 31 characters truncates silently and two
sharing a prefix then collide, where structtype.c refuses the same case.

DLOAD leaked a file descriptor. Its read loop sat inside an ATTEMPT and the
PASS in it returned past CLEANUP, so a scan error left the file open.
Hoisting the loop into its own helper to convert fgets fixes it.

Refs libakstdlib#26, libakstdlib#38

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 21:23:32 -04:00
330d731cfe Merge pull request 'Make GRAPHIC select the text plane (#23)' (#29) from 23 into main
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 3m42s
akbasic CI Build / coverage (push) Failing after 3m41s
akbasic CI Build / sanitizers (push) Failing after 4m41s
akbasic CI Build / mutation_test (push) Failing after 3m28s
akbasic CI Build / akgl_build (push) Failing after 5m26s
Reviewed-on: #29
2026-08-03 12:04:12 -04:00
3342f2b569 Make GRAPHIC select the text plane
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 3m22s
akbasic CI Build / sanitizers (push) Failing after 4m43s
akbasic CI Build / coverage (push) Failing after 3m43s
akbasic CI Build / akgl_build (push) Failing after 4m48s
akbasic CI Build / mutation_test (push) Failing after 3m31s
Co-authored-by: Andrew Kesterson <andrew@aklabs.net>
2026-08-03 08:08:34 -04:00
c5d13f00f6 Repoint the TODO.md citations that no longer resolve
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 4m7s
akbasic CI Build / coverage (push) Failing after 3m33s
akbasic CI Build / akgl_build (push) Failing after 4m39s
akbasic CI Build / mutation_test (push) Failing after 3m32s
akbasic CI Build / sanitizers (push) Failing after 14m32s
Its own SS0-SS9 structure survived the move to the tracker, so 133 citations
still resolve and are left alone. Thirteen did not.

Six named another repository's TODO.md by a section number: libakstdlib 1.6,
2.2.2 and 2.3, and libakgl's file. Those repositories dropped their numbering
entirely, so each now names UPGRADING.md, an issue, or the tracker.

Three cited 'TODO.md section 12', which has never existed here -- the defect
list is SS6. TODO.md itself caught two others of that class earlier. The
CMakeLists comment carried a stale premise with it: eleven defects
'deliberately reproduced and not yet fixed' stopped being the rule when SS0.1
retired the fidelity constraint.

Four told a reader to record work in TODO.md; the scanner defect is issue #4,
the audio_tables mutation gap is #25, and the UI gaps are libakgl #79 and #80.

Verified: cmake --build build && ctest --test-dir build, 112/112.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Andrew Kesterson <andrew@aklabs.net>
2026-08-02 22:01:28 -04:00
18b30b753b Merge pull request 'Move outstanding work from TODO.md into the issue tracker' (#27) from todo-to-issues into main
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 3m16s
akbasic CI Build / sanitizers (push) Failing after 4m29s
akbasic CI Build / akgl_build (push) Failing after 0s
akbasic CI Build / mutation_test (push) Failing after 0s
akbasic CI Build / coverage (push) Failing after 4m9s
Reviewed-on: #27
2026-08-02 19:46:56 -04:00
e188fecb6d Point the agent instructions at the issue tracker for outstanding work
This repository has no AGENTS.md; CLAUDE.md and MAINTENANCE.md carry the same
rules, so both are updated.

Outstanding work is an issue on the forge now, with the file and line, the
functional consequence, and what closing it would touch. TODO.md is the record
-- settled design decisions, the deviation register, defects already fixed, and
the reasoning behind the measurements -- and its row in CLAUDE.md's reading
table says so, with the tracker listed above it.

The dependency rule gains the correction this migration produced. 'Never work
around a missing dependency capability here' now says to open an issue in that
repository's tracker, and says plainly that recording it here instead is not
filing it: two akgl_ui gaps sat in TODO.md for a release on the reasoning that
changing a submodule is that repository's decision, which is true of changing it
and not of reporting it. Both are libakgl issues now.

Cross-repository citations of deps/*/TODO.md sections point at the trackers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Andrew Kesterson <andrew@aklabs.net>
2026-08-02 19:25:05 -04:00
4163144c2a Move outstanding work from TODO.md into the issue tracker
Twenty-six issues on source.starfort.tech/andrew/akbasic, labelled by kind and
blast radius and milestoned by what they can land in: 0.1.x for anything that
changes no public contract, 0.2.0 for new verbs and observable behaviour
changes, 1.0.0 for the design decisions. Everything carries status::grooming.

Block surgery rather than a rewrite. Every open item is replaced by a line
saying what it was and which issue carries it; everything else -- the settled
design decisions, the deviation register, the fixed defects and the reasoning
behind the measurements -- is byte-identical. 3338 lines to 2966.

Two corrections found while doing it. The "what remains, in priority order"
list named groups A, D, F, J and H as outstanding language work; SS4's own table
shows every one of them done, and what actually survives is one piece of
structural work that is not a verb -- block skipping by source line, which is
why a whole FOR/NEXT on one line never loops. And the two UI gaps SS7 recorded
and declined to file are now libakgl #79 and #80: that section's rule is right
that changing a dependency is that repository's decision, and it does not
follow that reporting the gap is.

Cross-repository references to deps/*/TODO.md sections are repointed at the
trackers that now hold them, here and in MAINTENANCE.md.

Verified: cmake --build build && ctest --test-dir build, 112/112.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Andrew Kesterson <andrew@aklabs.net>
2026-08-02 19:08:31 -04:00