Skip to content

Run the container-side python as package modules - #32

Closed
CrypticSwarm wants to merge 4 commits into
swarmforge-package-skeletonfrom
container-modules-into-package
Closed

Run the container-side python as package modules#32
CrypticSwarm wants to merge 4 commits into
swarmforge-package-skeletonfrom
container-modules-into-package

Conversation

@CrypticSwarm

Copy link
Copy Markdown
Owner

Stacked on swarmforge-package-skeleton.

The agent translator and the opencode.json merger were flat files copied into /usr/local/lib/swarmforge, sitting next to the swarmforge package they already imported. That left the image with two notions of where Swarmforge's python lives — one of them a directory whose whole purpose was "files that get COPY'd in".

Both move into the package:

  • anvil/translate_agents.pyswarmforge/agents/translate.py
  • anvil/merge_opencode_json.pyswarmforge/config/merge_opencode.py

The Dockerfile drops its two flat COPY lines and keeps only the package copy; the entrypoint runs python3 -P -m swarmforge.agents.translate and python3 -P -m swarmforge.config.merge_opencode with the package's parent as the import root. anvil/ is now just the Dockerfile and the entrypoint.

Both modules are otherwise untouched — the only edits inside them are the usage string and the warn() prefix, which named files that no longer exist. Nothing reads or parses either string.

Why -P

python3 -m does not resolve imports the way python3 <script> does: it puts the working directory first on sys.path, where the script form put the script's own directory. The entrypoint's working directory is /workspace, and these run as root, before privileges are dropped. Without -P, a repository that happens to carry its own swarmforge/ would shadow the image's copy and have it executed. -P suppresses that entry, which restores exactly what the script form did. The image builds CPython 3.12.7, so the flag is available.

Tests

The image-layout tests had pinned the entrypoint to literal copy destinations, which is the wrong thing to assert once the layout is a package. They now check the properties that actually have to hold, each verified to fail when its subject regresses:

  • every module the entrypoint runs ships in the package,
  • each names the import root the package lands in,
  • none lets the working directory onto sys.path, and the pinned interpreter is new enough for the flag that guarantees it,
  • the translator's [ -f ] guard covers the module its own function runs,
  • both modules run correctly against a staged copy of the image layout, with the checkout off sys.path and off the working directory.

The merger gained coverage it never had here, including its --replace-mcp-entries argv — the tong MCP merge, which has no || continue behind it, so a rejected argument is a container that never starts rather than a degraded merge.

Suite goes 478 → 483, all green. The passthrough and launch-path invariant tests are untouched and pass.

Not yet verified

A real make build_opencode and a no-tongs run_opencode smoke run have not been done — the environment this was written in has no docker. Worth running both before merge.

The translator and the opencode.json merger were flat files copied into
/usr/local/lib/swarmforge next to the package they already imported, which
left the image with two notions of where Swarmforge's python lives. Move both
into the package -- swarmforge/agents/translate.py and
swarmforge/config/merge_opencode.py -- so the Dockerfile copies one tree and
the entrypoint runs modules out of it.

`python3 -m` resolves imports differently from `python3 <script>`: it puts the
working directory first on sys.path instead of the script's directory. The
entrypoint's working directory is the workspace and these run as root, before
privileges are dropped, so a repository carrying its own swarmforge/ would
have shadowed the image's copy and been executed. -P restores the old
behaviour of never consulting the working directory.

The image-layout tests followed the same seam. They had pinned the entrypoint
to a literal copy destination; now they check the properties that actually
have to hold -- every module the entrypoint runs ships in the package, names
the import root the package lands in, and keeps the workspace off sys.path --
and they exercise the merger through the staged layout the way they already
exercised the translator.
The README, the contributor notes, the tongs docstring, and the dockerignore
header all described anvil/ as holding the unified-agent translator and
swarmforge/ as a package the container-side scripts import. There are no
container-side scripts any more: the entrypoint runs the package directly, and
anvil/ is down to the Dockerfile and the entrypoint itself.
The image-layout tests checked the entrypoint's translator guard and its
`python3 -m` runs independently, so a guard probing one module while the run
named another passed. Derive the module the guard covers from its path and
require the entrypoint to actually run it.

Three narrower holes alongside it. The staged runs did not pass -P, so they
did not execute the flag set the entrypoint uses. -P itself needs python 3.11
and the image builds its own interpreter from a pinned version, with nothing
tying the two together -- on an older pin the merge would exit on an unknown
option and take the container with it. And that merge's --replace-mcp-entries
argv, the one with no fallback behind it, was never exercised end to end.

The invocation scan also read comments, so prose about running a module
counted as running one.
Three of the guards were looser than they read. The translator guard only
had to name some module the entrypoint runs, so pointing it at the config
merge left translation unguarded and the suite green; scope it to the runs
inside the function the guard sits in. The python pin was read with a single
search, which misses a stage redeclaring the arg with its own default -- and
the stage that compiles the interpreter is not the one carrying the global
default. And stripping whole comment lines left a trailing comment able to
forge an invocation.

The staged runs now pass -P only when the host python has it. The image's
always does, but these tests run on whatever the contributor has, and the
staging dir has no swarmforge/ of its own for the working directory to
resolve through.
@CrypticSwarm
CrypticSwarm force-pushed the container-modules-into-package branch from 7ed7cf8 to f64f92a Compare August 4, 2026 04:07
@CrypticSwarm
CrypticSwarm deleted the container-modules-into-package branch August 4, 2026 04:10
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