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) <noreply@anthropic.com>
This commit is contained in:
13
.githooks/pre-commit
Normal file
13
.githooks/pre-commit
Normal file
@@ -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
|
||||
117
README.md
117
README.md
@@ -1,48 +1,89 @@
|
||||
# sporel-module-lib-core.command-test
|
||||
# lib-core.command-test
|
||||
|
||||
P.3.7 reference test-module — validates `lib-core.command` API contracts
|
||||
via the `engine.test.*` assertion surface.
|
||||
Test-lib for `lib-core.command` (Tests-as-Libs per ADR-0037).
|
||||
|
||||
- Module-ID: `lib-core.command-test`
|
||||
- Version: `0.1.0`
|
||||
- Kind: `test-module` (headless; runs init() once + exits)
|
||||
- Spec: `meta/docs/superpowers/specs/2026-05-14-p3-7-lib-command-design.md`
|
||||
**Version:** 0.1.0
|
||||
**Lib-ID:** lib-core.command-test
|
||||
**Requires:** lib-core.command v>=0.1.0, lib-core.input v>=0.4.0, lib-core.camera v>=0.3.0
|
||||
**Tags:** test, command
|
||||
|
||||
## Run
|
||||
## Topology
|
||||
|
||||
<!-- topology:start (auto-generated; do not edit) -->
|
||||
```mermaid
|
||||
graph LR
|
||||
this["lib-core.command-test"]
|
||||
lib_core_command["lib-core.command"]
|
||||
this --> lib_core_command
|
||||
lib_core_input["lib-core.input"]
|
||||
this --> lib_core_input
|
||||
lib_core_camera["lib-core.camera"]
|
||||
this --> lib_core_camera
|
||||
```
|
||||
<!-- topology:end -->
|
||||
|
||||
## Coverage Statement
|
||||
|
||||
Per Fault-Injection-Audit-Convention (2026-05-16):
|
||||
|
||||
| Section | Assertion-Count | Coverage |
|
||||
|---|---|---|
|
||||
| Register / Unregister | 3 | `register` handle, `count_registered` after register/unregister |
|
||||
| Move-Issue + State-Query | 3 | `move_to`, `current` returns move-data, `is_moving` true after issue, false for idle |
|
||||
| State-Query (idle) | 2 | `current` returns nil for idle, `is_moving` false for unknown handle |
|
||||
| Update-step | 2 | `update` moves 10px in 0.1s @ 100 px/s, y unchanged for horizontal move |
|
||||
| Arrival-snap | 2 | Snap to target.x when step >= dist, `current` cleared after arrival |
|
||||
| Cancel + cancel_all | 5 | `cancel(h2)` clears h2 only, `cancel_all` clears remaining; cancel_all clears 2-unit batch |
|
||||
| Speed-Setter | 2 | `speed` from register, `set_speed` updates |
|
||||
| Arrive-Threshold + Enabled | 3 | Default threshold = 2, `set_arrive_threshold` writes, `enabled` toggle |
|
||||
| Error-Handling | 2 | `register` with non-function errors, `move_to` with unknown handle errors |
|
||||
| **TOTAL** | **24** | |
|
||||
|
||||
### Tested-Surface
|
||||
- `command.register`, `command.unregister`, `command.count_registered`
|
||||
- `command.move_to`, `command.current`, `command.is_moving`
|
||||
- `command.cancel`, `command.cancel_all`
|
||||
- `command.speed`, `command.set_speed`
|
||||
- `command.arrive_threshold`, `command.set_arrive_threshold`
|
||||
- `command.enabled`, `command.set_enabled`
|
||||
- `command.update` (step + arrival-snap)
|
||||
|
||||
### Fault-Injection Audit (2026-05-16)
|
||||
|
||||
Verified via deliberate-mutation in fixture-mirror; mutations reverted after observation.
|
||||
|
||||
#### Caught Mutations
|
||||
|
||||
| # | Mutation | Caught by |
|
||||
|---|---|---|
|
||||
| M1 | `M.move_to` prefixed with `do return end` (complete no-op) | 6 assertions fail: `current returns move-command data`, `is_moving true after move_to`, `update moves 10px`, `arrival snap to target.x`, `cancel doesn't affect h3`, `move_to with unknown handle errors` |
|
||||
| M2 | `M.is_moving` prefixed with `do return false end` (always false) | `is_moving true after move_to` fails |
|
||||
|
||||
#### Uncaught Mutations (Gaps) — Cheap-Fill Applied
|
||||
|
||||
| # | Original Gap | Fill | Re-Inject Verifies |
|
||||
|---|---|---|---|
|
||||
| M3 | `M.cancel_all` rewritten to break after first entry | Added 2-unit batch scenario; asserts h2 + h3 both nil after `cancel_all` | ✓ |
|
||||
|
||||
### Structurally Untestable
|
||||
- None identified at headless level — entire surface exercisable via mocked-unit closure-state helper.
|
||||
|
||||
### Deferred
|
||||
- Multi-frame integration with `lib-core.player_control` consumer flow (covered indirectly by player_control-test).
|
||||
|
||||
## Running
|
||||
|
||||
Via `--test-libs=lib-core.command-test` (Smoke P0-S-batch Stage):
|
||||
|
||||
```bash
|
||||
Sporel.exe --test-libs=lib-core.command-test
|
||||
Sporel.exe --libs-dir=<libs-path> --test-libs=lib-core.command-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 | `M.move_to` prefixed with `do return end` (complete no-op) | 6 assertions fail: "current returns move-command data" (ok 4), "is_moving true after move_to" (ok 5), "update moves 10px" (ok 9), "arrival snap to target.x" (ok 11), "cancel doesn't affect h3" (ok 14), "move_to with unknown handle errors" (ok 23) |
|
||||
| M2 | `M.is_moving` prefixed with `do return false end` (always false) | Assertion "is_moving true after move_to" (ok 5) |
|
||||
|
||||
### Uncaught Mutations (Gaps)
|
||||
|
||||
| # | Mutation | Why uncaught | Severity |
|
||||
|---|---|---|---|
|
||||
| M3 | `M.cancel_all` rewritten to loop-and-break after first entry (only cancels one command) | Existing test cancels h2 manually before calling `cancel_all`, leaving only h3 active — single-entry cancel_all succeeds trivially | Medium |
|
||||
|
||||
**Resolution per Gap:**
|
||||
- Gap M3: Cheap-fill applied — added a second `cancel_all` scenario where both h2 and h3 are active simultaneously; asserts both are nil after `cancel_all` (commit see below).
|
||||
|
||||
### Cheap-Gap-Fills Applied
|
||||
|
||||
| Gap | New Assertion | Re-Inject Verifies |
|
||||
|---|---|---|
|
||||
| M3 | `cancel_all clears h2 of 2-unit batch` — `current(h2) == nil` after issuing move to both then cancel_all | ✓ |
|
||||
| M3 | `cancel_all clears h3 of 2-unit batch` — `current(h3) == nil` after same cancel_all | ✓ |
|
||||
|
||||
### Tested Production-Lib Surface
|
||||
|
||||
`lib-core.command` v0.1.0 — Tests cover: `register`, `unregister`, `count_registered`, `move_to`, `current`, `is_moving`, `cancel`, `cancel_all`, `speed`, `set_speed`, `arrive_threshold`, `set_arrive_threshold`, `enabled`, `set_enabled`, `update` (step + arrival-snap).
|
||||
- ADR-0037 (Tests-as-Libs Convention)
|
||||
- ADR-0038 (API-Doc-Convention)
|
||||
- Production-Lib: `~/Projects/Sporel/sporel-libs/lib-core/command/README.md`
|
||||
- Spec: `meta/docs/superpowers/specs/2026-05-14-p3-7-lib-command-design.md`
|
||||
|
||||
3
scripts/install-hooks.sh
Normal file
3
scripts/install-hooks.sh
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
git config core.hooksPath .githooks
|
||||
echo "Hooks activated."
|
||||
Reference in New Issue
Block a user