fix(pe): generate the template manifests instead of hand-writing them - #274
Merged
Conversation
The blueprint and controller templates each carry an integrity manifest that `validate_pair.py` gates in CI, and nothing regenerated either one. Satisfying that gate meant hand-writing digest YAML — or running the core generator, which emitted one fixed shape for all three manifests and so rewrote a template's `template:` key as `artifact:` and added `bytes:` to every entry, producing a manifest its own validator rejects. The generator now reads the shape from the target rather than deciding it: an existing manifest keeps its top-level keys, their order, and its per-file key set, and only the digests (and `summary`, where there is one) are recomputed. `--schema`/`--artifact` are still required to create a manifest that does not exist yet. One code path now serves all three, and a manifest with a new shape needs no code change. `sync_pe_templates` regenerates both under the existing `pe_touched` branch, so the command governance already mandates — `sync_generated_artifacts.py --force` — covers them. No new command to learn: the failure mode was running the documented command and still failing CI. Both manifests are registered in `GENERATED_PATH_PREFIXES` and attributed to the `l9-generated` merge driver. They were neither before, which is why they hard- conflicted on every stacked merge that touched them — including the one that blocked #263. TODO.md's "Program Execution MANIFEST.json — advisory by decision" entry is corrected, not overruled: its claim that nothing in the PR gate or CI enforces that manifest is stale, because `validate_campaign_promotion.py` hard-fails on its digests from a test the repo-root suite collects. The contradiction — declared advisory, so deliberately not auto-generated, while a default-suite test enforces it — produced the stale-hash failures in #263. Both ways out are recorded; neither is taken here. Verified: gate FAIL on an undeclared new file, then PASS after the documented command, with no hand-editing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FqpeGwrbdAuwq1ZgoMANMZ
Resolves the one conflict, in the generated environment/program-execution/core/MANIFEST.yaml. Both sides only moved `summary.total_bytes`, so the resolution is regeneration rather than a hand pick: main's rewrite of program-execution-controller-template/scripts/pec/exec_env.py changes that file's sha256 and byte count, and the rolled-up total with it. Resolved by taking this branch's side, then running `ops/scripts/sync_generated_artifacts.py --force` on the merged tree so the manifest is derived from the merged content. `.l9/pr/regen-required.txt` is empty and a second regeneration run is a no-op.
`Test Suite` failed on the merge with test_generated_prefixes_have_not_drifted: this branch adds program-execution-blueprint-template/MANIFEST.yaml and program-execution-controller-template/MANIFEST.yaml to sync_generated_artifacts.GENERATED_PATH_PREFIXES, and main meanwhile grew a drift check asserting that the git-guardrails copy of that list differs from the sync script's only by DELIBERATELY_NOT_GENERATED. Neither side conflicts textually, so the merge was clean and the contract still broke. Both paths belong in the gate's list rather than the carve-out. They are generated deterministically by generate_manifest.py — the point of this branch — and this branch already attributes both `merge=l9-generated` in .gitattributes, so regeneration is what heals their conflicts. The carve-out is for paths the sync script touches that no gate-run generator produces (the hand-authored AUTONOMY_MANIFEST.yaml routing SSOT, and the advisory PE manifest); these are not that. Leaving them unregistered would only have made the clean gate treat a regenerable artifact as user content — noisy, not unsafe — but it would also have left the two lists out of contract. tests/ops/autonomy/test_git_guardrails.py: 92 passed. Remediation-Cycle: #274/cycle-1
cryptoxdog
pushed a commit
that referenced
this pull request
Aug 22, 2026
The org auto-seeder recreated this branch on top of main after #274 merged and force-pushed, which reverted both remediation commits and restored the stock templates. Merged its new seed commit rather than force-pushing over it. Both conflicts were add/add on the two workflows, and the re-seeded copies are byte-identical to the ones already remediated here (verified: `git diff 2325f08:<path> 40c87c7:<path>` is empty for both, and for .vscode/extensions.json), so taking this branch's side drops nothing from upstream and keeps: - l9-lint-test-node.yml scoped to Biome (no setup-node cache hard-fail, no duplicate `Test Suite` required context) - semgrep pinned to the AGENTS.md range - the identity-map wiring and the two governance artifacts that let the strict SDK run succeed If the seeder recreates this branch again the same reconciliation applies. Landing it on main is what stops the loop: the seed is missing-only, so it leaves files that already exist alone.
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.
Summary
Mechanical commit subjects (judgment sentence still required):
Type of Change
Governance Checklist
setup_workspace_symlinks.sh, symlinks resolve (§2) — not measured by open_pr_after_gate.sh — do not treat as verifiedls -la .cursor/rules .cursor/skills .cursor/commandsall resolve — not measured by open_pr_after_gate.sh — do not treat as verifiedsolo_ruleset(CODEOWNERS auto-request is disabled; seeCODEOWNERS). Protected-root rewrites carry anALLOW-ROOT-DELETION:marker and pass the repo gates; CODEOWNERS review applies only when owners are re-enabled — not measured by open_pr_after_gate.sh — do not treat as verified@v1, never@mainor bare SHA — not measured by open_pr_after_gate.sh — do not treat as verifiedkernels/Recursive Alignment.mdthenkernels/Validate & Repair.md;l4_local.py authorize-releasebefore this PRBreaking Change
If checked, describe the impact and migration path:
Rollback Plan
Related Issues
Closes #
Commits
Test plan
make pr-checklocal gate receipt presentrelease_authorized)Changed files
Mechanical evidence
gate-receipt.json present: schema=l9.pr_gate_receipt.v1 head=c5edf2a9a9adac6da769e86813302b82c949e95e passed_at=2026-08-22T02:41:17Z
L4 receipt present: phase=release_authorized head=312cf14dd7f3bc3125ca4971d3d8deeaf3deb2e9
Generated by Claude Code