Skip to content

[BUG] apm compile distributed/claude/gemini outputs leave literal __BUILD_ID__ placeholder; PR #468 only covered --single-agents #957

@edenfunf

Description

@edenfunf

Describe the bug

apm compile writes the literal string <!-- Build ID: __BUILD_ID__ --> to disk on every compilation target except the legacy --single-agents path. The placeholder is documented in compilation/constants.py as something that "a deterministic Build ID (content hash) is substituted post-generation", but the substitution code only exists in compile/cli.py:506-524 and is gated by if config.strategy == "distributed" and not single_agents -- only the --single-agents legacy path reaches it.

This makes Build-ID-based byte-stable rebuild and cross-platform pre-commit verification (the user-story documented in #467) impossible for the default apm compile and every multi-target setup. #467 / #468 fixed cross-platform sort determinism but did not extend substitution to the affected formatters.

Affected paths

All produce files containing literal __BUILD_ID__:

Path Caller Output
apm compile (default distributed) agents_compiler._write_distributed_file AGENTS.md (root + every distributed sub-dir)
apm compile -t claude agents_compiler._compile_claude_md (inline write) CLAUDE.md
apm compile -t gemini agents_compiler._compile_gemini_md (inline write) GEMINI.md
apm compile --watch (single-file mode) agents_compiler._write_output_file AGENTS.md
compile_agents_md() (public API) agents_compiler._write_output_file AGENTS.md

To Reproduce

mkdir t && cd t && apm init -y
mkdir -p .apm/instructions
cat > .apm/instructions/x.instructions.md <<'INSTR'
---
applyTo: "**/*.py"
description: Test
---
# Test
INSTR
apm compile
apm compile -t claude
apm compile -t gemini
grep '__BUILD_ID__' AGENTS.md CLAUDE.md GEMINI.md

Output on main (666925f):

AGENTS.md:<!-- Build ID: __BUILD_ID__ -->
CLAUDE.md:<!-- Build ID: __BUILD_ID__ -->
GEMINI.md:<!-- Build ID: __BUILD_ID__ -->

Expected behavior

Every compiled output should contain a real 12-char SHA256 hash, e.g. <!-- Build ID: e316dee8ef7f -->, deterministic across re-execution and sensitive to content changes (so pre-commit git diff --exit-code AGENTS.md works).

Why PR #468 did not cover this

PR #468 closed #467 with the verification claim "Both now produce <!-- Build ID: e316dee8ef7f -->". However the PR diff only adds sorted(...) calls in 4 files (context_optimizer.py, template_builder.py, distributed_compiler.py, claude_formatter.py); it does not touch the placeholder-replacement logic. That logic lives at compile/cli.py:506-524 and is reachable only via the --single-agents legacy path, so PR #468's verification must have been run with that flag. The default apm compile and -t claude / -t gemini paths still write __BUILD_ID__ literally on main today.

Root cause

The placeholder substitution is a cross-cutting concern scattered across five compiled-output write sites:

  • commands/compile/cli.py:506-548 -- single-file path: stabilizes
  • compilation/agents_compiler.py:_write_distributed_file (line 870): no stabilization
  • compilation/agents_compiler.py claude write (line 545): no stabilization
  • compilation/agents_compiler.py gemini write (line 640): no stabilization
  • compilation/agents_compiler.py:_write_output_file (line 814): no stabilization (used by watch mode + public API)

There is no chokepoint that guarantees stabilization runs before persisting compiled output, so adding a new target naturally re-exposes the bug. PR #468's pattern (sibling-by-sibling fix in 4 files) is structurally the same anti-pattern and is the reason this case was missed.

Suggested fix

Introduce a CompiledOutputWriter chokepoint in compilation/output_writer.py that all five write sites must route through. The writer extracts the hash logic into a stabilize_build_id() helper, defensively asserts the placeholder is gone before persisting, and atomic-writes to disk. Direct path.write_text / open(...).write on compiled output becomes a contract violation -- adding a new target without using the writer raises rather than silently emitting __BUILD_ID__.

Environment

  • OS: Windows 11
  • Python Version: 3.13.4
  • APM Version: 0.9.2 / latest main (666925f)

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/ci-cdGitHub workflows, merge queue, gh-aw integrations, release pipeline.area/cliCLI command surface, flags, help text (cross-cutting).priority/highShips in current or next milestonestatus/acceptedDirection approved, safe to start work.status/triagedInitial agentic triage complete; pending maintainer ratification (silence = approval).type/bugSomething does not work as documented.

    Type

    No type

    Projects

    Status

    In Progress

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions