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:d654dce464c49da7
(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 : 5121 bytes
EXTENSION : .py
CANONICAL SOURCE (the location this ticket is named for):
repo : agent_tools_suite
path : audit_guard.py
ref : HEAD (HEAD)
blob : a8ac29e086c136b9b36904a1896caa3ae9c19cfc (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 a8ac29e086c136b9b36904a1896caa3ae9c19cfc
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:audit_guard.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 — 5121 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
"""audit_guard — run an UNTRUSTED crank patch under a RUNTIME veto. (Canonical copy, owned by ~/jail.)
A crank patch runs as a real subprocess with full host rights. Inspecting the patch's SOURCE (AST) is
theater — the model builds the call/path dynamically and walks around it. This installs a
`sys.addaudithook` that fires on the ACTUAL runtime operation, however the source spelled it, and
REFUSES anything that:
- shells out / execs / spawns a process → the "python, not bash" rule
- loads native FFI (ctypes) — which could disable this very hook
- opens a network connection or resolves a host
- writes / deletes / renames outside the build repo, or touches a sensitive path
Reads of the stdlib / site-packages are allowed so imports work; bytecode writing is disabled so an
import can't write a .pyc outside the repo. Not airtight (native code loaded another way, or an
interpreter bug, can still escape — the guaranteed boundary is the VM); this is the strong in-process
approximation used by crank_discipline.run_confined.
python audit_guard.py <patch.py> [args...] # runs patch.py as __main__ under the veto
env: CRANK_REPO=<repo root> (defaults to cwd)
"""
import os
import runpy
import sys
import tempfile
sys.dont_write_bytecode = True # no .pyc writes to site-packages while the hook confines writes
REPO = os.path.realpath(os.environ.get("CRANK_REPO") or os.getcwd())
_TMP = tuple(os.path.realpath(p) for p in {tempfile.gettempdir(), "/tmp", "/private/tmp"})
_WRITE_OK = (REPO,) + _TMP
_SENSITIVE = tuple(os.path.realpath(os.path.expanduser("~/" + n)) for n in
(".ssh", ".secrets", ".claude", ".aws", ".gnupg", ".config/jazz", ".jazz")) + \
("id_ed25519", "id_rsa", "wallet-seed", "master-seed", "authorized_keys",
"/etc/", "/private/etc")
_MUTATE = {"os.remove": (0,), "os.unlink": (0,), "os.rmdir": (0,), "os.mkdir": (0,),
"os.makedirs": (0,), "os.rename": (0, 1), "os.replace": (0, 1), "os.link": (0, 1),
"os.symlink": (0, 1), "os.chmod": (0,), "os.chown": (0,), "os.truncate": (0,),
"shutil.rmtree": (0,), "shutil.move": (0, 1), "shutil.copyfile": (0, 1),
"shutil.copytree": (0, 1), "shutil.copymode": (0, 1), "shutil.copystat": (0, 1)}
class CrankEscape(RuntimeError):
"""A crank patch tried to leave the build repo — refused at runtime."""
def _under(path, roots):
try:
real = os.path.realpath(path)
except Exception: # noqa: BLE001
return False
return any(real == root or real.startswith(root + os.sep) for root in roots)
def _sensitive(path):
text = str(path)
return any(s in os.path.realpath(text) or s in text for s in _SENSITIVE)
def _writing(mode, flags):
if isinstance(mode, str):
return any(c in mode for c in "wax+")
if isinstance(flags, int):
return bool(flags & (os.O_WRONLY | os.O_RDWR | os.O_CREAT | os.O_APPEND | os.O_TRUNC))
return False
def _hook(event, args):
if event in ("os.system", "subprocess.Popen", "os.posix_spawn", "pty.spawn"):
raise CrankEscape(f"shelling out is forbidden ({event}) — do it in pure python")
if event.startswith("os.exec") or event.startswith("os.spawn"):
raise CrankEscape(f"process exec/spawn is forbidden ({event})")
if event == "import" and args and str(args[0]).split(".")[0] in ("ctypes", "_ctypes"):
raise CrankEscape(f"native FFI import '{args[0]}' is forbidden")
if event.startswith("ctypes."):
raise CrankEscape(f"ctypes/native is forbidden ({event})")
if event in ("socket.connect", "socket.getaddrinfo"):
raise CrankEscape(f"network access is forbidden ({event})")
if event == "open":
path, mode, flags = (list(args) + [None, None, None])[:3]
if not isinstance(path, (str, bytes)):
return
if _sensitive(path):
raise CrankEscape(f"touching a sensitive path is forbidden ({path})")
if _writing(mode, flags) and not _under(path, _WRITE_OK):
raise CrankEscape(f"writing outside the build repo is forbidden ({path})")
elif event in _MUTATE:
for index in _MUTATE[event]:
if index < len(args) and isinstance(args[index], (str, bytes)):
if _sensitive(args[index]):
raise CrankEscape(f"mutating a sensitive path is forbidden ({event}: {args[index]})")
if not _under(args[index], _WRITE_OK):
raise CrankEscape(f"mutating outside the build repo is forbidden ({event}: {args[index]})")
def main(argv):
if len(argv) < 2:
sys.stderr.write("usage: audit_guard.py <patch.py> [args...]\n")
return 2
patch = argv[1]
sys.argv = [patch] + list(argv[2:])
sys.addaudithook(_hook) # one-way; installed before the patch runs
try:
runpy.run_path(patch, run_name="__main__")
except CrankEscape as escape:
sys.stderr.write(f"CRANK-VETO: {escape}\n")
return 3
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))
===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:
_under(path, roots)
_sensitive(path)
_writing(mode, flags)
_hook(event, args)
main(argv)
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.