Single-Source Plugin Builds with a Byte-Compared Distribution¶
A single-source plugin build generates each host's package from one tree and fails CI when the checked-in copy differs from a fresh build.
Keep one canonical set of skills and personas, and generate every host package from it with a builder that checks two things. Completeness means the package holds every file the skills read at run time. Freshness means a checked-in generated copy still matches what the builder produces today. In the Elephant-Goldfish Model (EGM), the harness guide states that "No harness gets a fork of the skill text" (harnesses.md). See Elephant-Goldfish design review for the workflow and plugin and extension packaging for bundle layout and install trust.
Thin host adapters¶
Each of EGM's 7 Claude reviewer adapters is a 17-line file that tells the agent to read the shared persona (adapters). It adds tools: [Read, Grep, Glob], model: inherit, and omitClaudeMd: true (comprehension adapter). The synthesizer and bootstrap adapters grant wider tools and omit omitClaudeMd (synthesizer, bootstrap). Upstream gives the reason as "This avoids imposing Claude tool names on other hosts" (harnesses.md). Codex ships no adapter, and a general-purpose child reads the persona file (harnesses.md).
Completeness: the package is a dependency closure¶
EGM warns that installers copying skills/ alone break it, "because the skills read agents/ and resources/document_standards/ at run time" (harnesses.md).
The builder encodes that closure in two lists. Glob allowlists select shared and per-host files for the claude and openai targets (pack_plugin.py). A REQUIRED_SHARED list names 7 skills, 9 personas, 5 document standards, and the reference files the skills load (pack_plugin.py). A missing required file raises an error before any output folder exists (test_packaging.py). The builder also rejects symlinks and paths outside the source tree (pack_plugin.py), and any manifest that declares hooks, MCP servers, or apps (pack_plugin.py).
The required list is kept by hand, and it fell behind at least once. Commit 05f8984 moved the bootstrap README templates into references/templates.md on 2026-10-28 (commit 05f8984). The bootstrap skill tells the agent to read that file before writing any contract file (egm-bootstrap). The builder added the file to its required list on 2026-10-30 (commit e138d8a). In those two days, a tree without the file would have built with no error (inference from the diff).
Freshness: compare the checked-in copy byte for byte¶
EGM checks in one generated runtime, distributions/claude/, and submits that folder through Anthropic's directory portal (directory.md). A unit test builds a fresh copy and asserts that both folders hold the same paths with the same bytes (test_packaging.py). The design adds the rule "Do not edit generated files independently" (runtime_packaging.md). CI runs the unit suite on every pull request (tests.yml, unit job).
For the archives, the builder sorts entries, stamps every ZIP entry 1980-01-01, and sets mode 0644 on every file (pack_plugin.py). A test builds twice and compares the two archives byte for byte (test_packaging.py).
Why it works¶
Upstream gives one reason for a single builder, that "Separate hand-maintained packages would drift" (runtime_packaging.md). The byte comparison turns a stale copy into a failed pull-request check (inference). The required list catches a missing run-time dependency the same way, but only for files someone has added to it.
Where the checks stop¶
- Reproducibility holds in one build environment. The design says "Do not claim reproducibility across untested toolchain versions" (runtime_packaging.md).
- The freshness test guards the checked-in copy only. The Claude marketplace entry uses
"source": "./", the repository root (marketplace.json), so a marketplace install does not readdistributions/claude/(inference). - Output handling is platform-specific. On Windows, exclusive file creation "can follow a dangling symlink". The builder therefore writes the ZIP in a temporary folder and hard-links it into place (pack_plugin.py, commit ed06551).
- Release attestation identifies the workflow run from
main, and the tagged source that run checked out can be a different commit. The guide adds "Attestations do not establish host support or prove that the code is safe" (releases.md). - A complete, fresh package can still load differently on each host. The design says "Package validation does not establish directory approval or runtime support" (runtime_packaging.md). Test each client as per-surface plugin verification describes.
Cost¶
Each edit to an included file needs a regenerate step, and the pull request carries the change twice. Commit 05f8984 changed SKILL.md and templates.md under both skills/ and distributions/claude/ (commit 05f8984). EGM writes the regenerate step into its release checklist (releases.md). A plugin with no checked-in generated copy gains nothing from the freshness test (inference).
Example¶
The EGM freshness test builds into a temporary folder and compares path-to-bytes maps (test_packaging.py):
def test_published_claude_runtime_is_current(tmp_path: Path) -> None:
"""The directory subfolder must contain exactly the reviewed runtime."""
generated, _ = build(ROOT, "claude", tmp_path)
published = ROOT / "distributions/claude"
def contents(folder: Path) -> dict[str, bytes]:
return {p.relative_to(folder).as_posix(): p.read_bytes()
for p in folder.rglob("*") if p.is_file()}
assert contents(published) == contents(generated), (
"Published Claude runtime differs from generated output. Regenerate "
"with `python3 scripts/pack_plugin.py --target claude --output "
"<output-dir>`; replace `distributions/claude` with the generated "
"`egm-claude` folder."
)
The dictionary comparison fails on an extra file, a missing file, or one changed byte. File timestamps play no part, because the test reads file contents only.
Key Takeaways¶
- Add a file to the required list in the same commit that adds a run-time read of it. In EGM the two changes landed in separate commits, two days apart.
- Fix entry order, timestamps, and modes before you compare archive bytes, and claim reproducibility only for the toolchain you test.
- Check which path each marketplace entry installs from. A freshness test on a copy no marketplace installs protects that copy and nothing else.
- Keep provenance, package contents, and host behavior as three separate pieces of evidence. Release attestation and the byte test say nothing about host behavior.
Related¶
- Elephant-Goldfish Design Review — the workflow this build packages
- Per-Surface Verification of Agent Plugin Packages — the host-behavior check that build output does not replace
- Plugin and Extension Packaging — bundle layout, distribution levels, and install trust
- Portable Agent Definitions — translating one definition into many host formats
- Agent Config as a Managed Supply Chain — the consumer side, pinning the bytes you install
- Skill Reuse as Vendored Forking — what happens to copied skill text with no builder behind it