From b0529e4592386e43bfc6b33688cd9040b246d37f Mon Sep 17 00:00:00 2001 From: Volt Date: Wed, 21 Oct 2026 14:42:32 +0000 Subject: [PATCH] tools: the write guard reaches the three flags named something other than --json The write-axis leg grades ONE surface per tool: `--json PATH`. Measured at c3ffee9 with caller_write_flags.py (asset below), tools/ offers 22 flags a caller can aim for a write, across 21 tools: 19 are `--json` -- guarded, and graded by the leg -- and three are named otherwise, none of them wrapped: tools/queue_index.py :581 --write BARE tools/tool_map.py :510 --write BARE tools/doc_census_check.py :1831 --save-vocab BARE BARE = `open(path, "w")` with no try. Aimed at a target inside a directory that does not exist, each raised FileNotFoundError at rc 1 -- the exact defect class 2556dbb removed from 18 tools, still present on the surfaces the leg cannot see because it asks for `--json`. Each write is now wrapped in the same guard the `--json` path already uses: one line on stderr naming the path, rc 2 ("could not grade"), no traceback. Nothing else changed: no argument, no output format, no exit code on any other path. VERIFIED, both directions, in a throwaway worktree (never this tree): /root/.hermes/wizard-outputs/volt/2026-10-21-write-axis-bites/write_flag_probe.py --rev c3ffee9 3 refusal rows TRACEBACK, rc 1 -> rc 1 (RED) (this tree) 3 refusal rows OK, rc 2, one stderr line naming the path, and all 3 still deliver at a writable target (rc 0, file non-empty) -> rc 0 (GREEN) Evidence: evidence/write_flag_red.json, evidence/write_flag_green.json. Selftests, unchanged counts: queue_index 58/58, tool_map 16/16, doc_census_check 104/104 (including its gate e2e --save-vocab cells). repo gate: 6 legs, 6 pass, 0 fault, rc 0 (selftest-sweep 21 tools 0 fatal). REMAINING BLIND SPOT, stated so it is not read as closed: write_probe.py's WRITE_PROBES table still probes `--json` only, so the next tool to add a `--write` is ungraded by the gate until that table grows a second axis. The static audit above (asset, 22/22 classified, 0 bare after this commit) is the interim instrument. --- tools/doc_census_check.py | 13 +++++++++---- tools/queue_index.py | 8 ++++++-- tools/tool_map.py | 8 ++++++-- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/tools/doc_census_check.py b/tools/doc_census_check.py index 1574ae3..bc26980 100644 --- a/tools/doc_census_check.py +++ b/tools/doc_census_check.py @@ -1828,10 +1828,15 @@ def main(): units[name].update(k for k in entry[0][0] if k in keep) observed = sum(len(v) for v in seen.values()) kept = sum(len(v) for v in units.values()) - with open(a.save_vocab, "w", encoding="utf-8") as fh: - json.dump({"rev": a.rev, "repo": a.repo, - "tools": {k: sorted(v) for k, v in units.items()}}, - fh, indent=1, sort_keys=True) + try: + with open(a.save_vocab, "w", encoding="utf-8") as fh: + json.dump({"rev": a.rev, "repo": a.repo, + "tools": {k: sorted(v) for k, v in units.items()}}, + fh, indent=1, sort_keys=True) + except OSError as exc: + print("cannot write --save-vocab " + a.save_vocab + ": " + str(exc), + file=sys.stderr) + return 2 print(f"wrote vocabulary {a.save_vocab}: {len(units)} tool(s), " f"{kept} unit(s) at {a.rev} ({observed} key(s) observed, " f"{observed - kept} unspendable by any claim)") diff --git a/tools/queue_index.py b/tools/queue_index.py index 698b23f..844818b 100644 --- a/tools/queue_index.py +++ b/tools/queue_index.py @@ -578,8 +578,12 @@ def run(args): return 0 if args.write: - with open(args.write, "w", encoding="utf-8") as fh: - fh.write(build_doc(payload)) + try: + with open(args.write, "w", encoding="utf-8") as fh: + fh.write(build_doc(payload)) + except OSError as exc: + print("cannot write --write " + args.write + ": " + str(exc), file=sys.stderr) + return 2 print("wrote %s (%d bytes)" % (args.write, os.path.getsize(args.write))) return 0 diff --git a/tools/tool_map.py b/tools/tool_map.py index 2699e5f..a58926d 100644 --- a/tools/tool_map.py +++ b/tools/tool_map.py @@ -507,8 +507,12 @@ def main(): print(f"tool_map: wrote {args.json}") if args.write: - with open(args.write, "w", encoding="utf-8") as fh: - fh.write(text) + try: + with open(args.write, "w", encoding="utf-8") as fh: + fh.write(text) + except OSError as exc: + print("cannot write --write " + args.write + ": " + str(exc), file=sys.stderr) + return 2 print(f"tool_map: wrote {args.write} ({len(text)} B, " f"{len(data['rows'])} tools, {len(data['legs'])} gate legs)") return 0