From 7f38a666f6fc67b0486b4c90d96e6b99432a861e Mon Sep 17 00:00:00 2001 From: Axel Meyer Date: Sat, 16 May 2026 16:48:30 +0200 Subject: [PATCH] docs: migrate README to API-Doc-Convention (S2-cascade.libs test-lib) Restructured per ADR-0038 with Coverage-Statement-Block per Fault- Injection-Audit-Convention. Topology auto-populated. Pre-commit hook installed. Co-Authored-By: Claude Opus 4.7 (1M context) --- .githooks/pre-commit | 13 +++++ README.md | 106 +++++++++++++++++++++++++++------------ scripts/install-hooks.sh | 3 ++ 3 files changed, 91 insertions(+), 31 deletions(-) create mode 100644 .githooks/pre-commit create mode 100644 scripts/install-hooks.sh diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100644 index 0000000..1ebdad0 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,13 @@ +#!/bin/sh +# Sporel API-Doc-Convention pre-commit hook. +SPOREL_EXE="${SPOREL_EXE:-$(command -v Sporel.exe 2>/dev/null || command -v sporel 2>/dev/null)}" +if [ -z "$SPOREL_EXE" ]; then + echo "INFO: Sporel.exe not on PATH. Skipping lint." >&2 + exit 0 +fi +"$SPOREL_EXE" --lint="$(pwd)" --fix +RC=$? +if git diff --cached --name-only | grep -qx 'README.md'; then + git add README.md +fi +exit $RC diff --git a/README.md b/README.md index 4fe2c39..b6c394e 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,76 @@ -# sporel-lib-core.input-test +# lib-core.input-test -P.3.3 test-lib — validates `lib-core.input` v0.4.0 API contracts (BIND_MAP, -`was_action_released`, engine mouse-surface) via the `engine.test.*` assertion surface. -17 assertions total. +Test-lib for `lib-core.input` (Tests-as-Libs per ADR-0037). -- Lib-ID: `lib-core.input-test` -- Version: `0.1.0` -- Kind: `test-lib` (headless; M.run_tests(ctx) invoked by engine test-runner) -- Spec: `meta/docs/superpowers/specs/2026-05-12-p3-3-mouse-input-design.md` +**Version:** 0.1.0 +**Lib-ID:** lib-core.input-test +**Requires:** lib-core.input v>=0.4.0 +**Tags:** test, input -## Run +## Topology + + +```mermaid +graph LR + this["lib-core.input-test"] + lib_core_input["lib-core.input"] + this --> lib_core_input + engine["engine.*"] + this --> engine +``` + + +## Coverage Statement + +Per Fault-Injection-Audit-Convention (2026-05-16): + +| Section | Assertion-Count | Coverage | +|---|---|---| +| Engine surface presence | 7 | `MOUSE_LEFT/RIGHT/MIDDLE` constants, `is_mouse_down`, `was_mouse_pressed`, `was_released`, `was_mouse_released` | +| `bind` mouse-strings | 1 | `action_count == 1` after `bind("click", {"mouse_left"})` | +| `bind` mixed key+mouse | 1 | `action_count == 2` after mixed bind `{"e", "mouse_left"}` | +| `bind` rejects unknown mouse-name | 3 | Lua error raised, message mentions bad string, no partial state mutation | +| Headless query stubs | 4 | `is_action_down`, `was_action_pressed`, `was_action_released` return false; `was_action_released` for unknown returns false | +| Key-only regression | 1 | `bind("walk_left", {"a","left"})` still works after BIND_MAP refactor | +| **TOTAL** | **17** | | + +### Tested-Surface +- `input.bind` (mouse-string acceptance, mixed key+mouse, unknown-name rejection, atomicity) +- `input.action_count` +- `input.is_action_down` (bound action headless = false) +- `input.was_action_pressed` +- `input.was_action_released` (bound action headless = false; unknown action = false) +- Engine surface: `MOUSE_LEFT/RIGHT/MIDDLE`, `is_mouse_down`, `was_mouse_pressed`, `was_released`, `was_mouse_released` function presence + +### Fault-Injection Audit (2026-05-16) + +Verified via deliberate-mutation in fixture-mirror; mutations reverted after observation. + +#### Caught Mutations + +| # | Mutation | Caught by | +|---|---|---| +| M1 | `function M.was_action_released` renamed (API-break) | Runtime error "attempt to call a nil value (field 'was_action_released')" — test aborts after assertion 14 | +| M2 | `if not entries then return false end` changed to `return nil` | `was_action_released for unknown action returns false` fails (`not ok`, got nil) | + +#### Uncaught Mutations (Gaps) + +| # | Mutation | Why uncaught | Severity | +|---|---|---|---| +| M3 | Dispatch-swap in `was_action_released` for-loop (key-kind ↔ mouse-kind) | Headless: all input queries return false regardless of swapped dispatch — no real events differentiate the paths | Med | + +**Resolution:** Headless-unreachable. Requires real key/mouse events. Future-Slice B: integration test with simulated input events. No cheap headless fill possible. + +### Structurally Untestable +- Real keyboard/mouse event delivery — headless mode never produces input events. +- `was_action_released` dispatch correctness for key-kind vs mouse-kind binding — see Gap M3. + +### Deferred +- Integration test with simulated input events (Future-Slice B). + +## Running + +Via `--test-libs=lib-core.input-test` (Smoke P0-S-batch Stage): ```bash Sporel.exe --libs-dir= --test-libs=lib-core.input-test @@ -17,26 +78,9 @@ Sporel.exe --libs-dir= --test-libs=lib-core.input-test Output is TAP version 13 on stdout; exit-code = failure-count (0 = all pass). -## Coverage Statements (2026-05-16 Fault-Injection Audit) +## References -Verified via deliberate-mutation in fixture-mirror; mutations reverted after observation. - -### Caught Mutations - -| # | Mutation | Caught by | -|---|---|---| -| M1 | `function M.was_action_released` renamed to `M._was_action_released_disabled` (API-break) | Runtime error "attempt to call a nil value (field 'was_action_released')" — test aborts after assertion 14 | -| M2 | `if not entries then return false end` changed to `return nil` (wrong return type for unknown action) | Assertion "was_action_released for unknown action returns false" (ok 16) — `not ok`, got nil | - -### Uncaught Mutations (Gaps) - -| # | Mutation | Why uncaught | Severity | -|---|---|---|---| -| M3 | Dispatch-swap in `was_action_released` for-loop: key-kind entries call `was_mouse_released`, mouse-kind call `was_released` | Headless mode: all input queries return false regardless of correct vs swapped dispatch — no real events to differentiate the paths | Med | - -**Resolution per Gap:** -- Gap M3: Headless-unreachable — requires real key/mouse events to distinguish `was_released` from `was_mouse_released`. Future-Slice B: integration test with simulated input events. No cheap headless fill possible. - -### Tested Production-Lib Surface - -`lib-core.input` v0.4.0 — Tests cover behavior of: `input.bind` (mouse-string acceptance, mixed key+mouse, unknown-name rejection, atomicity), `input.action_count`, `input.is_action_down`, `input.was_action_pressed`, `input.was_action_released` (bound action headless=false; unknown action=false). Engine surface assertions: `MOUSE_LEFT/RIGHT/MIDDLE` constants, `is_mouse_down`, `was_mouse_pressed`, `was_released`, `was_mouse_released` function presence. +- ADR-0037 (Tests-as-Libs Convention) +- ADR-0038 (API-Doc-Convention) +- Production-Lib: `~/Projects/Sporel/sporel-libs/lib-core/input/README.md` +- Spec: `meta/docs/superpowers/specs/2026-05-12-p3-3-mouse-input-design.md` diff --git a/scripts/install-hooks.sh b/scripts/install-hooks.sh new file mode 100644 index 0000000..2ff6e41 --- /dev/null +++ b/scripts/install-hooks.sh @@ -0,0 +1,3 @@ +#!/bin/sh +git config core.hooksPath .githooks +echo "Hooks activated."