Files
reliquary/docs/workpad/reports/2026-10-17-00-gremlins-mvp-cuts.md

52 lines
4.2 KiB
Markdown

# Gremlins MVP: unrequested-surface calls
- Author model: **claude-sonnet-5-5** · Date: 2026-10-17 · Branch `gremlins-mvp` @ `cfeb1bf`
- Source: [phase review](2026-10-17-gremlins-mvp-review.md) §2. The user ruled on the rows below: keep the useful ones, cut the rest, one-line reason each. This commit records the calls; the cuts follow in the next commit.
| # | Item | Call | Reason |
|---|---|---|---|
| 2 | `briefs/<J>.md`: full prompt per job | keep | Debugging aid. |
| 3 | Ledger torn-last-line repair | keep | Protects the ledger after a hard stop. |
| 4 | In-process `threading.Lock` around ledger append | cut | Each spawn is its own process, so it guards nothing; the spec says "without locks". |
| 5 | `MODEL_RE`, aliases refused at `init` | keep | An alias would change the fingerprint and break cache hits; B1 asks for a full id. |
| 6 | `EFFORTS` choices, default `high` | keep | Trivial argparse guard. |
| 7 | Tool deny list (`Bash(claude:*)`, `git push/rebase/reset --hard`) | keep | Headless unattended gremlins must not recurse or rewrite history. |
| 8 | `SPIRE_GREMLIN_CHILD=1` in the child env | cut | Rider: the refusal it served was dropped, so it is leftover. |
| 11 | `refused` / `model_refused` status case | cut | Not in the spec's classify; the harness substitutes unlisted models silently, so `error` covers it. |
| 12 | Stream fields tracked and never read (`init`, `msg_ids`, `_ids`, `events`, `turns`) | cut | Never read. |
| 13 | `mismatch` in `usage_from_result` | cut | Rider: computed and never read. |
| 14 | `wrote_only_5m` | cut | Rider: never called. |
| 16 | `NAME_RE` on base/job names | keep | Names become file paths. |
| 17 | Note detection by mtime with 2 s slack | cut | Replace with a plain existence check of `notes/<J>.md`, since `spawn` refuses an existing job name (#20). |
| 22 | `base_state` in spawn output | cut | `status` reports warmth and `cache` reports the outcome. |
| 23 | `init` defaults cwd to the git toplevel | keep | cwd is in the fingerprint; a stable default avoids cache misses. |
| 25 | stderr to `raw/<J>.err` | keep | Cheap debugging aid. |
| 26 | Reader join timeout 10 s | keep | Without it an orphan holding stdout hangs the helper (spec §7 leftover). |
| 28 | Unused `ss=None` injection parameter in `call.py` | cut | Used by no caller. |
## Result
Premises checked in the code before each cut. Tests: `uv run --no-project --python 3.12 python -m unittest discover -s tests` gave **24 tests, OK** (run twice).
| File | Added | Removed |
|---|---|---|
| `grm/stream.py` | 9 | 39 |
| `grm/call.py` | 4 | 4 |
| `grm/cli.py` | 2 | 4 |
| `grm/launch.py` | 1 | 1 |
| **Total** | **16** | **48** |
Net code: 32 lines removed (16 added, 48 deleted); no test was removed or changed (none existed only for a cut item).
## Kept against the call
- **#4 `threading.Lock` around ledger append: kept.** Premise half true: in production each spawn is its own process, so no two threads of one process append. But with the lock removed `test_store.test_parallel_appends_give_whole_lines` (96 of 100 lines) and `test_cli.test_parallel_spawns` (2 of 3 ledger lines) fail, on every run. Appends from separate handles on Windows lose lines. The lock only makes those B3 tests pass in-process; across real processes the same race is unguarded either way (the review's open "Windows append atomicity" item). Cutting it would mean weakening two B3 tests, which the call forbids. Needs a decision: either keep the lock as is, or replace it with a cross-process append (for example a lock file or an `msvcrt` byte-range lock) in a separate change.
## Notes on the cuts
- #12: `session_id` from the `init` event is now taken only while unset (`self.session_id is None`), which matches the old "first init wins" behaviour.
- #28: `StreamState` is now imported at the top of `call.py` instead of lazily inside the `ss is None` branch.
- #17: premise holds. `spawn` refuses an existing `jobs/<J>.json` (`cli.py:255`) and `_execute` writes that file before the call starts, so a note that exists afterwards was written by this job.
- #11: nothing outside `stream.py` read `refused` or `model_refused` (code, tests, SKILL.md, templates).
- #8, #13, #14, #22: no reader in code, tests, SKILL.md or templates. SKILL.md and templates mentioned none of the cut items, so they are unchanged.