fix(build): kill dead compiled artifacts + tighten Dockerfile context + fix checksums path (closes #147)#148
Merged
Merged
Conversation
… fix checksums path (closes #147) Five interlocking bugs in the `forge build` output that compound to a bloated runtime image and a silently-disabled integrity check: 1. **Prompt-builder body duplication.** A SKILL.md with N `## Tool:` sections parses into N SkillEntry values that all share the same Body. The compiler's prompt builder appended the body once per entry, so the bundled code-review-github skill (4 tools) shipped 4× repeats and aibuilderdemo's compiled/prompt.txt was 1199 lines for what should be ~250. Dedup via a seen-bodies set; per-skill JSON entries still carry the full Body for SDK consumers. 2. **compiled/prompt.txt and compiled/skills/skills.json are dead.** The runtime never opens either file — runner.discoverSkillFiles() re-globs skills/SKILL.md on every startup. Stopped generating them; deleted forge-cli/skills/writer.go; pruned the matching COPY lines from the Dockerfile template and the validate stage's required-files check. External library consumers (forgecore.Compile) still receive the in-memory CompiledSkills struct directly. 3. **Dockerfile `COPY . .` leaked operator-side artifacts into /app.** Expanded .dockerignore to exclude k8s/, build-manifest.json, the entire compiled/ directory, the Dockerfile itself, the .dockerignore itself, and .local-bins/. The runtime image now contains only forge.yaml, guardrails.json, <channel>-config.yaml, agent.json, policy-scaffold.json, skills/, and checksums.json. 4. **checksums.json integrity check silently skipped in container.** The runner looked at <WorkDir>/.forge-output/checksums.json, which never exists inside a Forge container — the file lands at /app/checksums.json directly. Added a fallback that picks the flattened layout when the operator-side layout is absent. 5. **Removed redundant COPY lines from Dockerfile.tmpl** that would have failed after fix #2 anyway (the targets no longer exist). End-to-end verified on the user's aibuilderdemo agent: - Pre-fix: 21 files in .forge-output, compiled/prompt.txt 1199 lines, image contained k8s/ + build-manifest.json + security-audit.json + recursive Dockerfile + dead compiled/ artifacts. - Post-fix: 19 files (compiled/prompt.txt + compiled/skills/skills.json removed), image contains only the 8 files the runtime opens, operator artifacts intact on disk for forge package / kubectl apply. Tests pin all four invariants: - TestCompile_BodyDeduplication_* (3 tests) - TestSkillsStage_WithSkills updated to assert artifacts NOT created - TestDockerignore_ExcludesOperatorSideArtifacts + TestDockerignore_KeepsRuntimeFiles + TestDockerfile_NoLongerCopiesDeadCompiledArtifacts - TestResolveChecksumsDir_* (3 tests) + TestVerifyBuildOutput_ContainerLayoutWorksEndToEnd
Merged
5 tasks
…t} are not generated The integration test still required the now-removed artifacts to exist. Updated to assert their *absence* and to verify the in-memory SkillsCount instead — matching the post-#147 contract.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #147.
Summary
Five interlocking bugs in the `forge build` output that compound to a bloated runtime image and a silently-disabled build-integrity check. Bundle is one PR because they all answer the same question — "what goes into the container image vs. what stays operator-side."
Bug-by-bug
1. Prompt-builder body duplication
`forge-skills/compiler/compiler.go:50` appended the full SKILL.md body once per tool entry. A SKILL.md with N `## Tool:` sections parses into N `SkillEntry` values that all share the same `Body` — so the bundled `code-review-github` skill (4 tools) shipped 4× repeats. On `aibuilderdemo`, `compiled/prompt.txt` was 1199 lines for what should be ~250.
Fix: track seen bodies in a set; emit each body once. Per-skill JSON entries still carry the full `Body` because forgecore SDK consumers may need it for non-prompt purposes.
2. `compiled/prompt.txt` and `compiled/skills/skills.json` are dead
The runtime never opens either file. `runner.discoverSkillFiles()` (runner.go:2619) re-globs `skills/*/SKILL.md` on every startup. Both files were written by the build, copied into the container, and never read.
Fix: stop generating them in `forge-cli/build/skills_stage.go`; delete `forge-cli/skills/writer.go`; remove the matching `COPY` lines from `Dockerfile.tmpl`; remove the required-files check in `validate_stage.go`. External library consumers (`forgecore.Compile`) still receive the in-memory `CompiledSkills` struct directly.
3. Dockerfile `COPY . .` leaks operator-side artifacts into `/app`
Pre-fix the only exclusions were `.env*`, `.enc`, `.key`, `.pem`. Everything else landed in `/app/`: `k8s/.yaml`, `build-manifest.json`, `compiled/security-audit.json`, `Dockerfile` itself (recursive), `.dockerignore` itself, `.local-bins/` (build-only stash).
Fix: expand `.dockerignore` with documented sections separating secrets from operator-side artifacts. The runtime image now contains only the 8 files the runner actually opens: `forge.yaml`, `guardrails.json`, `-config.yaml`, `agent.json`, `policy-scaffold.json`, `skills/`, `checksums.json`.
4. `checksums.json` integrity check silently skipped in container
`runner.go:245` looked at `/.forge-output/checksums.json`. Inside a Forge container, `WorkDir = /app` and the file lives at `/app/checksums.json` directly (no `.forge-output/` prefix — the build-output dir is flattened into `/app` by the `COPY . .` design). `VerifyBuildOutput` took the silent `os.IsNotExist` branch and integrity verification was effectively disabled in every Forge container.
Fix: add a fallback that picks the flattened container layout when the operator-side layout is absent. Both layouts now work end-to-end.
5. Redundant explicit `COPY`s in the template
`Dockerfile.tmpl:77-78` did `COPY compiled/skills/ /app/skills/` and `COPY compiled/prompt.txt /app/compiled/prompt.txt` after `COPY . .` had already copied them. The second was a literal no-op. Both targeted artifacts that are now gone (Bug 2), so these lines would have failed builds anyway.
Removed.
End-to-end repro
User's `aibuilderdemo` agent at `/Users/ibreakthecloud/Data/initializ/Workspace/agentdemo/aibuilderdemo`.
Files changed
Test plan
Out of scope (tracked separately)