Every ticket as the root of its provenance graph — goal, patch, approval, runs and (as the ticket-class build lands) mandate, manifestations, guarantees, journey, artifacts and children. Rendered through the <<<TicketGraphList>>> tag.
goal
Rewrite ONE unique forge module as entirely new, behavior-preserving, installable,
control-config'd Python, per the FORGE-wide refactor standard #200 (#std).
This ticket covers ONE module — the one identified below — and nothing else. It was filed
against the module's DEDUPLICATED identity, so if that module was copy-pasted across the
forge, every copy is covered by this single ticket.
MODULE KEY : forge-rw:4b850fc5c8793fd5
(the ticketer's idempotency marker — it lives in this ticket's TITLE. Do
not edit the title, or the module will be re-filed as a duplicate.)
IDENTITY : AST-NORMALIZED — codebean's `_ast_norm` (#183), i.e.
ast.dump(ast.parse(ast.unparse(ast.parse(src)))).
Copies of this module that differ only in comments, blank lines,
quoting or spacing carry the SAME module-sha and are covered by THIS
ticket. A differing docstring is real content, so a module whose
docstring differs is a DIFFERENT module with its own ticket.
SIZE : 6102 bytes
EXTENSION : .py
CANONICAL SOURCE (the location this ticket is named for):
repo : agent_tools_suite
path : validate.py
ref : HEAD (HEAD)
blob : 69b50b41e700038739a5c7eace132b1b4b4162ca (git blob sha1)
REFERENCE ONLY — you do NOT need this command. This module's bytes are embedded
COMPLETE in THE ORIGINAL SOURCE block below, and that is what the rewrite is based
on. For a reader who does have forge access:
git --git-dir=/Users/stevenpeterson/code/installed/jazz-project/config/forge/remotes/agent_tools_suite.git cat-file blob 69b50b41e700038739a5c7eace132b1b4b4162ca
THE SAME MODULE ALSO LIVES AT THESE FORGE TIPS: none — this module is unique to
agent_tools_suite among the repos scanned so far.
HISTORICAL OCCURRENCES of this exact blob — every OTHER DISTINCT {repo, path}
the omni-git index (`occ`, joined on the git blob sha1) records this
byte-identical file at, with the index's own `ts` for the first row of each.
Any place this run enumerated LIVE is listed under the forge tips above, not
here. Context for the rewrite, not extra work:
testmonkeyalpha-group/jazz-forge/agent_tools_suite:validate.py [occ.ts gitlab]
THE ORIGINAL SOURCE, EMBEDDED — YOU NEED NO FORGE ACCESS TO DO THIS TICKET.
The module's own bytes are reproduced below, inside this ticket. THEY are the
authoritative copy and the behavior-preserving basis: rewrite from them. The
`git --git-dir=... cat-file blob` command under CANONICAL SOURCE above is kept only as a
REFERENCE for a reader who happens to have forge access — running it is NOT part of this
ticket. (Why the bytes are here at all: the pair_loop worker that executes this ticket is
sandboxed to the ticket's own congruency worktree and cannot read the forge's bare repos.
Ticket #346 is the fix that put them on the ticket.)
The block BEGINS at the line
===ORIGINAL SOURCE (verbatim, the behavior-preserving basis)===
and ENDS at the first line
===END SOURCE===
Everything strictly between those two lines is the module — 6102 byte(s), byte for byte, with no re-indentation, no re-wrapping and nothing
elided.
===ORIGINAL SOURCE (verbatim, the behavior-preserving basis)===
#!/usr/bin/env python3
"""validate.py — does the agent's tooling actually work, and is it safe?
Two levels, because "validating an agent" is two different questions:
LEVEL 1 — the TOOLS are correct + safe (deterministic; this file).
· each crankimg verb does what it says (build/verify/disks/flash/base)
· the destructive verb (flash) REFUSES without an exact confirm, and refuses the
system disk / internals / a backup volume
· the SURFACE is minimal — the agent's whole action space is crankimg (5) + the bash
decoy + gate meta, and nothing else (no file-edit, no git, no real shell)
· the GATE enforces intent — every routed tool gets a REQUIRED `intent` field, and a
call is recorded {timestamp, tool, intent} before it runs
LEVEL 2 — the AGENT behaved (a replay audit, not this file).
The gate's intent trail IS the validation surface: after a run, `gate__query` /
recall replays the {intent, tool, time} sequence — you assert it matches the expected
crank flow (build → verify → disks → flash) and that the decoy logged no shell reach.
Deterministic tools + a mandatory-intent trail = you can prove BOTH "the tools work"
and "the agent used them for the reason it claimed."
Run: python3 validate.py
"""
import json
import os
import subprocess
import sys
import time
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
sys.path.insert(0, os.path.join(HERE, "crankimg-mcp"))
import mcplib # noqa: E402
import server as crank # noqa: E402 — crankimg-mcp/server.py
P, F = 0, 0
def ok(msg): globals().__setitem__("P", P + 1); print(" \033[32m✓\033[0m " + msg)
def bad(msg): globals().__setitem__("F", F + 1); print(" \033[31m✗ FAIL\033[0m " + msg)
def check(cond, msg): ok(msg) if cond else bad(msg)
def text(res): return "".join(c.get("text", "") for c in res.get("content", []))
def call(tool, **args): return crank.call_tool(tool, args)
def level1_tools():
print("\n\033[1mLEVEL 1 — tools correct + safe\033[0m")
# surface: exactly the five verbs, no more
names = {t["name"] for t in crank.list_tools()}
check(names == {"build", "verify", "disks", "flash", "base"},
"surface is exactly {build,verify,disks,flash,base} (got %s)" % sorted(names))
# disks: system disk refused, at least one candidate visible
d = text(call("disks", intent="recon"))
check("/dev/disk0" in d and "REFUSE" in d.split("/dev/disk0")[1].split("\n")[0],
"disks REFUSES the system disk /dev/disk0")
check("candidate" in d, "disks shows at least one candidate (external)")
# base: reports the (already-present) image
b = text(call("base", intent="check base"))
check("present" in b or "MISSING" in b, "base reports present/missing cleanly")
# verify: on the real baked seed (built earlier) → verified
v = text(call("verify", intent="verify image"))
check("verified" in v or "verification failed" in v, "verify runs a read-only image check")
# flash SAFETY — the whole point
check(text(call("flash", intent="x")).startswith("device required")
or "device required" in text(call("flash", intent="x")),
"flash refuses with no device")
check("must echo the device" in text(call("flash", intent="x", device="/dev/disk9", confirm="/dev/disk8")),
"flash refuses when confirm != device (fat-finger guard)")
check("refused" in text(call("flash", intent="x", device="/dev/disk0", confirm="/dev/disk0")).lower(),
"flash REFUSES the system disk even with a matching confirm")
# build precondition: a missing wallet secret → clear guidance, no crash
bd = text(call("build", intent="build", wifi_secret="no-such-secret-xyz"))
check("wallet" in bd.lower(), "build handles a missing wallet secret with guidance (no crash)")
def level1_end_to_end():
print("\n\033[1mLEVEL 1 — end-to-end through gate → coupler → crankimg\033[0m")
proc = subprocess.Popen(["python3", "gate.py"], cwd=HERE, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True, bufsize=1)
def rpc(mid, method, params):
proc.stdin.write(json.dumps({"jsonrpc": "2.0", "id": mid, "method": method, "params": params}) + "\n")
proc.stdin.flush()
for _ in range(400):
line = proc.stdout.readline()
if not line:
return None
m = json.loads(line)
if m.get("id") == mid:
return m
try:
rpc(1, "initialize", {"protocolVersion": "2024-11-05", "capabilities": {}})
tl = rpc(2, "tools/list", {})
tools = {t["name"]: t for t in tl["result"]["tools"]}
check("crankimg__disks" in tools, "crankimg tools surface through the gate (namespaced)")
check("intent" in tools["crankimg__disks"]["inputSchema"].get("required", []),
"the gate marked crankimg__disks' `intent` REQUIRED through the whole stack")
check("bash__run" in tools, "the bash DECOY is present (catches a shell reach)")
# a real routed call WITH intent
r = rpc(3, "tools/call", {"name": "crankimg__disks",
"arguments": {"intent": "validate: recon flashable disks"}})
check(r and not r["result"].get("isError") and "/dev/disk" in text(r["result"]),
"routed crankimg__disks (with intent) returns disk recon")
# the intent was recorded
time.sleep(0.2)
logp = os.path.join(HERE, "gate-memories.json")
logged = os.path.exists(logp) and "validate: recon flashable disks" in open(logp).read()
check(logged, "the intent was RECORDED to the gate memory log before routing")
finally:
proc.stdin.close(); proc.terminate()
if __name__ == "__main__":
level1_tools()
level1_end_to_end()
print("\n\033[1mresult: %d passed, %d failed\033[0m" % (P, F))
print(" LEVEL 2 (agent behaved) = replay the gate intent trail after a run: `gate__query`")
sys.exit(1 if F else 0)
===END SOURCE===
THE CALLABLE SURFACE — every callable in the original, lowered with codebean's
`lower_node` (#183). The rewrite must present THIS surface (same names, same
parameters, same defaults) so callers of the original keep working:
ok(msg)
bad(msg)
check(cond, msg)
text(res)
call(tool, **args)
level1_tools()
level1_end_to_end()
CAPTURED BEHAVIORAL EXEMPLARS: none. no function in this module is minable — criteria_miner mines only
self-contained pure functions (it refuses anything that mentions a
world-touching or non-deterministic name, plus methods and no-arg
functions), and nothing here qualified
This does NOT weaken the ticket's bar — it MOVES the work: you must write the
equivalence exemplars by hand from the original's behavior before rewriting, and
they are what settles this ticket.
THE STANDARD — #200's four requirements, which this ticket exists to enforce. All four are
the standard's own words; none is optional:
1. BEHAVIOR-PRESERVING, VERIFIED NOT ASSERTED — the deliverable is ENTIRELY NEW source
code (rewritten, not copied). It must reproduce the ORIGINAL's behavior, and the proof
is the exemplars above (captured via codebean lower_* #183 + criteria_miner
IO-exemplars, exactly as #200 specifies).
2. A PYTHON MODULE — regardless of the source language. This module's source is `.py`;
the rewrite is Python either way.
3. INSTALLABLE — a proper package: pyproject.toml, `pip install -e .` works, console
entry points where applicable. WRITE ONLY YOUR OWN MODULE FILE at
forge2/<repo>/<module>.py. Do NOT hand-author pyproject.toml or __init__.py: the
harness DERIVES them ADDITIVELY from the modules present (congruency/tools/
forge_package.py, #410 — the analogue of the tag-registry fix #281), so your unit never
conflicts with, or clobbers, a sibling module of the same forge2/<repo>/ package. A
package-level pyproject includes every .py in the directory, so your module installs
and imports as `<repo>.<module>` the moment it lands.
4. CONTROL-CONFIG'D — NO hardcoded paths or params. A JSON/registry config object drives
it (the install.json / registry.json idiom of this repo), and per note-for-claude a
tool that cannot see its config THROWS rather than guessing a default. Add ONLY your
own `forge2.<repo>.<module>` section to the package's registry.json (create the file if
it is absent); NEVER read, edit or drop a sibling module's section — the sections are
disjoint keys and stay additive that way.
ACCEPTANCE (#200's, per rewrite ticket) — this ticket settles when ALL of:
a. the new source reproduces every captured exemplar above, plus the edge cases you add
for what sampling cannot reach (paste the runs as evidence);
b. it presents the callable surface above, so existing callers keep working;
c. `pip install -e .` installs it clean from its own pyproject.toml;
d. it runs off a config object with ZERO hardcoded settings, and throws without one.
Kind is `build` (a pair: coder + adversarial tester). This ticket hangs off #200 by a
`spawned` edge, so a stop ticket aimed at #200 brakes every rewrite in this programme.
Filed mechanically by checkouts/current/congruency/tools/forge_refactor_ticketer.py under ticket #201, from the forge bare repos at
/Users/stevenpeterson/code/installed/jazz-project/config/forge/remotes. Its original source is embedded above (#346), so this ticket is complete on its own.