Skip to content

Hold every registered harness to one conformance suite - #56

Open
CrypticSwarm wants to merge 6 commits into
harness-container-execfrom
harness-conformance
Open

Hold every registered harness to one conformance suite#56
CrypticSwarm wants to merge 6 commits into
harness-container-execfrom
harness-conformance

Conversation

@CrypticSwarm

Copy link
Copy Markdown
Owner

Adding a harness is now one directory plus one registry line — and a conformance suite that checks the result with no new test-writing. tests/test_harness_conformance.py parameterizes over the registry: every spec field present with a shaped value and every waiver carrying a reason; a full init run over staged tmpdir layers landing assets at the declared destinations with the layer precedence; MCP fragments and delivery modes consistent with each other; execute passthrough for default-hook harnesses; the recorded run argv carrying the shared layer mounts, tmpfs masks over every native asset destination under a writable bind, and no mount under the image's package path; and the registry matching the directory listing, each entry shipping its install script and make fragment.

Cleanup rides along: the retired swarmforge/commands/ import path and the translator re-exports nothing consumes are gone, and the README and AGENTS.md describe the harness directory layout, the build events, and the per-run lifecycle as the container drivers implement them.

The suite runs over the registry rather than naming harnesses, so a new
harness is checked the moment it is registered: its descriptor is
complete and well-shaped (an opted-out field carries a Waiver with a
reason on record), a full root-phase run over staged layers lands
config and assets at its declared destinations in layer order, its MCP
fragment serializes and travels by a delivery the drivers implement,
its exec is byte-identical for the default pre-exec hook, its recorded
run argv carries the shared layer mounts and masks every asset
directory a writable mount would carry to the next run, and the
registry matches the harness directory listing with install.sh and
harness.mk present.

The staged-import-root suite also runs the root-phase driver the way
the entrypoint invokes it, so the first python the container executes
proves its imports resolve from the image's package copy.
The codex command translator's old module path was kept as a python -m
shim while the container still invoked it there; the container drives
translation through the codex harness module now, so the shim's package
goes, its one test importing through it retargets, and the translator's
own usage line and warning prefix name the module where it lives.

The agent translator likewise re-exported the rendering helpers and
per-harness tables of the modules they moved to. Nothing imports them
through it anymore, so the pass-through imports and their __all__
entries go, keeping the names its CLI consumers still reach for. The
per-harness override keys stay readable from
swarmforge.harness.agent_override_keys(), which the harness modules
already call.
The entrypoint carries no harness-specific logic: it invokes the config
driver as root and the pre-exec driver as the anvil user, and those are
what merge the layers, translate agents, install assets, build Claude's
settings, and shape the exec. The README, AGENTS.md, and the comments
that attributed that work to the entrypoint now name the drivers and
hooks that do it, and the claim that Claude's translated agents land in
~/.claude/agents is corrected to the container-local config dir.

A new README section documents the harness lifecycle end to end: the
per-harness directory, the registry, the build events, the per-run
phases, and what adding a harness takes.
- The recorded SWARMFORGE_AGENT_BIN is held to the registry name as
  well as the binary: the entrypoint hands the same word to both
  drivers as the registry key, so a spec whose name and recorded
  variable diverge is a container that refuses to start.
- Only a tmpfs counts as a mask over an asset destination a writable
  bind mount carries; the mount itself is another host directory, not
  a mask, and a tmpfs standing anywhere above the destination covers
  it.
- The staged run redirects every absolute non-template asset
  destination and Claude's wrapper directory, so a harness declaring
  a pinned skills or commands path cannot write outside the tmpdir.
- The config-precedence run stages a file only the repo and user
  layers contest, pinning the repo-user order the org win says
  nothing about.
- The exec checks scrub every launch variable, run with a built
  settings file standing so a flag-splicing hook takes its real
  branch, and read the shared read-only mounts as pairs rather than
  through a last-wins dict.
- A waived mcp_fragment is accepted with its reason, matching the
  guards the fragment-shape tests already carry.
The anvil overview described the tong MCP delivery by naming two of
the four harnesses; three deliver by the same env variable and only
Claude takes the flag, so the paragraph states the split by the
spec's delivery with the merges each route feeds. The initialize
bullet orders the config phase the way the driver runs it, the
repo-shape test list names all three files that hold it, and the
test-naming convention covers the suites that span the repo.
The staged run redirects every pinned asset destination and three of
Claude's module constants; the class docstring still described the
narrower set it started with. A declared MCP merge also has to come
with a shaped fragment, or the merge names a file nothing produces.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant