From 242935f90c4c032f3bac68fc3b9a9c4be7065476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabor=20K=C3=B6rber?= Date: Fri, 16 Oct 2026 18:44:10 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20Gremlins=20Phase=203=20digest:?= =?UTF-8?q?=20amendment=20after=20pre-build=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-10-16-18-gremlins-phase3-contract.md | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/docs/workpad/plans/2026-10-16-18-gremlins-phase3-contract.md b/docs/workpad/plans/2026-10-16-18-gremlins-phase3-contract.md index 3564153..8410f98 100644 --- a/docs/workpad/plans/2026-10-16-18-gremlins-phase3-contract.md +++ b/docs/workpad/plans/2026-10-16-18-gremlins-phase3-contract.md @@ -590,3 +590,95 @@ Fixing POSIX beyond the seam (M14, a macOS process table, POSIX survival): hando | C20 | Release title `🤖 Spire 0.3.0: trained implementer, gremlins per block (spire:gremlins)` | the build plan's title with the old name | | — | An `!` injection whose command exits 2 (`where` on a closed platform) is untested: it may fail the skill load on Linux/macOS. Flagged for Phase 4 and the porting Igors; the text keeps the plain form (design §2). | `… where \|\| true` (may break the `Bash(uv:*)` allow match) | | — | WSL uv install only through the permission prompt (download and execute); otherwise the WSL check stops at inventory and interop | install without asking (not allowed) | + +--- + +## Amendment after pre-build check (2026-10-16 18:44 +0200, the Athanor, `claude-opus-5-5[1m]`) + +Source: `docs/workpad/reports/2026-10-16-19-gremlins-phase3-digest-check.md` (`52c5911`, checker `claude-opus-5-5[1m]`). Igor: apply B1 and A1–A12 exactly as stated, plus the `kill`/`expire` timeout note; everything else (C1–C20) stands. **This section overrides the sections above where they conflict.** Block owners read it first. + +### B1. GateTests use a Windows-path fake platform (§4 "Tests") + +- The fake closed platform is a `WindowsPlatform` subclass (or the real `PLATFORM` with `supported` and `describe` patched) with `supported = False`, `is_wsl() → True`, `describe() → {"name": "posix", "sys_platform": "linux", "wsl": True, "supported": False, "detach": "setsid", "override": …}`. Path handling stays Windows, so `--binary C:\…`, `--run C:\…` and `script_path()` work on the host. +- `init` on a closed gate asserts `error == "platform not supported"` (not just exit 2). +- **The gate check is the first thing `init` does**, before `state_dir_for` and any `norm_path`. +- The pure unit tests `PosixPlatform().supported is False` (with `sys.platform` patched to `linux` and `darwin`) and `WindowsPlatform().supported is True` stay as written. + +### A1. The gate also closes launches on an existing registry (§4, C11, §7.2) + +- The dispatcher refuses `train`, `spawn`, `continue`, `rebase` and `tick` on a closed gate (exit 2, `error: "platform not supported"`, `reason`, `platform`), in the same place as the N3 refusal. +- The tick inside `wait` and inside the supervisor launches no pings on a closed gate (`tick_pass` returns early, nothing written). +- Athanor intake (§7.2): run `where` **first**, then adopt (`list`) if the run log has a `Gremlins:` line. If `where` is not ok, use fresh agents even when a state dir exists. +- New `GateTests` case: a registry present (made with the gate open), gate closed → `spawn` exits 2 with `error == "platform not supported"` and no job entry is written. + +### A2. C16 is narrowed (§5.3) + +`kill_tree`'s verification drops a snapshot pid as dead **only** when one of these holds: + +- the table (from the module-level `process_table()`) is **non-empty** and lacks the pid; or +- the snapshot name is known (not `None`), the table's name for that pid differs, **and** `proc_ctime(pid)` is `None` (the unverifiable case, class (a)). + +In every other case `record_alive` decides, as today. Tests (fake table, module-level patches): an empty table keeps the survivor; a snapshot name `None` keeps it; a differing name with a verifiable ctime leaves the decision to `record_alive`; the class (a) case (non-empty table, other name, ctime `None`) drops it. + +### A3. N3 tests split (§5.1) + +- Stored file present, `$CLAUDE_CODE_EXECPATH` unset, PATH without claude: stored path kept, the "could not re-resolve" warning shown, exit 0. +- Stored file deleted: exit 2, `claude binary not found` (today's loud failure). + +### A4. N3 re-resolves through the stored source only (§5.1, C14) + +Re-resolve only through the source the stored path came from: `CLAUDE_CODE_EXECPATH` from `$CLAUDE_CODE_EXECPATH`, `PATH` from a PATH lookup. A `--binary` pin is kept. If the stored source gives nothing now, keep the stored path and warn. Never cross sources. Add a test: stored via EXECPATH, re-run with EXECPATH unset and a different `claude` on PATH → stored path kept, warning. + +### A5. Path matrix row 3 (§3.4) + +`C:` and `C:x` on `WindowsPlatform`: **refused**, as today ("cannot normalise path"). On `PosixPlatform`: refused (drive form). + +### A6. C18 wording (§7.2) + +The fallback line in athanor.md reads: "a fresh agent reads the code the blocks change and writes the trunk notes". The phrase "the code the phase touches" must not appear (G-text 1). + +### A7. No version number in the platform wording + +Release gating keeps 0.3.0 unreleased until Linux and macOS lift their gates, so no text ties "Windows-only" to a version. + +- SKILL.md description (§7.1) and Platforms section, README row (§9.1): "Windows-tested; other platforms fail closed until tested (see Platforms)". Drop "Windows-only in 0.3.0" everywhere, including §7.3's implementation-skill line. +- Gate reason (§4): "spire:gremlins is not tested on <, WSL> yet; use fresh agents for every block (see SKILL.md, Platforms)." +- Release commit body (§9.4): "tested on Windows; Linux and macOS gated pending the platform handoff ()". +- Handoff §8.8 lists every place a porter updates when lifting a gate: `PosixPlatform.supported`, the gate tests, SKILL.md (description and Platforms), the README row, athanor.md's fallback line if wording changes, the handoff report itself, and the changelog of the release that ships it. + +### A8. Three more routed text items (§2.6, §7.1, §7.7) + +- (a) `preamble.md` no longer points at `plugins/spire/skills/implementation/references/run-log.md` (it does not resolve in other projects). Block 3.1 **inlines the handoff-note format** in `preamble.md`: the seven fields `Model`, `Start / end` (from `date`), `Done`, `Deviations from plan`, `Flagged for review`, `Left for later`, `Expected broken`, `Commits`, as a short bullet template. +- (b) SKILL §6/§7: `stalled` together with `clock_jump` is a host suspend, not a hung gremlin; `wait` again before acting. +- (c) SKILL §6: an action per failure reason. `model_refused` → the model id is not admitted (`availableModels`), the user must change settings. `usage_limit` → plan usage hit; stop launching, flag it, hand back. `overloaded`, `api_error` → transient; `continue` once later, else flag. `prompt_too_long` → the base is too large; re-train or rebase smaller. `is_error`, `exit_code`, `no_result` → read `raw/.err` and the note, `continue` once, else flag. `detach_failed`, `launch_error`, `supervisor_error` → helper or host problem; flag it, do not retry blindly. +- Add these three rows to the §7.7 checklist. + +### A9. Vacuous `-forks` assertions (§2.7) + +The existing `assertFalse(os.path.exists(… "-forks"))` lines (`test_forks.py:155`, `:934`, `:1011`, `:1079`, and any other literal state-dir suffix in the tests) must derive the path from `gremlins.state_dir_for(run)` (or `forks`' equivalent at the time of the change), never from a literal suffix. List them in the 3.1 handoff note. + +### A10. Residue categories outside the skill (§2.8) + +New category: **"Igor text (D4) and non-session meanings: leave untouched"**. It covers `plugins/spire/skills/igor/SKILL.md:66`, `igor/references/mindset.md:11`, `igor/references/harness-notes.md:3` and `implementation/references/superpowers.md:36` (repository forks). 3.1 neither edits these lines nor reports them as residue. + +### A11. `where` catches path refusals (§4) + +`cmd_where` catches a `GremlinsError` from `resolve_binary` (and from the binary's `norm_path`) into `binary.error` and `reasons`, so a closed-gate `where` always carries the gate reason and the full report. Test: `where --binary ` → exit 2, `binary.error` set, the report's other keys present. + +### A12. `platform.override` defined (§4) + +- `override = (env set) and not PLATFORM.supported`. On Windows, `CliTests` assert `override: false` even though C13 sets the env. +- `PosixPlatform.describe()` reads `self.supported`, never a literal, so lifting the gate is one change. +- `platform_gate()` reads `PLATFORM.describe()` (and `PLATFORM.supported`), never `sys.platform`, so it passes the seam grep. + +### Naming fix (check note on C12) + +The porting override is **`SPIRE_GREMLIN_UNSUPPORTED_OK`** (singular prefix, like `SPIRE_GREMLIN_CHILD` and `SPIRE_GREMLIN_DETACH_*`). Every mention of `SPIRE_GREMLINS_UNSUPPORTED_OK` above means this name. + +### Other check notes applied + +- **`kill`/`expire` timeout** (check note on C16 (b)): SKILL §7 tells the Athanor to call `kill` and `expire` from Bash with a long `timeout` (600000, as for `wait`): `expire` kills its jobs one after another, each verify can take up to `KILL_VERIFY_S`, and the Bash tool backgrounds a call past its timeout. +- No local variable may be named `platform` in `gremlins.py` (the module imports `platform`). +- C15: set `parent_inactive` only when NEW is actually created, not on the `rebase_conflict` early return. +- C13: the handoff (§8.3) says that a green suite on Linux says nothing about the gate; only `GateTests` cover it. +- C17: Phase 4 must smoke the renamed helper before anything ships (the handoff and the run log say so).