Submodule Topology: prompts + templates
petrova intermediate 4 min read
ELI5
petrova-hq is a binder with two pockets cut into it — core/prompts and core/templates. The pockets are separately bound little books published from their own GitHub repos. You can write inside the binder all you like, but if you want to change the books, you change the books, then slide the new edition back into its pocket.
Technical Deep Dive
Layout
---title: "petrova-hq submodule topology"---flowchart TD operator(("<b>Operator</b><br/>edits prompts/templates")):::person subgraph parent ["**petrova-hq (parent)**"] reg["<b>registry.yaml + state/</b><br/>control-plane state"]:::system cli["<b>cli/ + skills/</b><br/>verb surface"]:::system prompts["<b>core/prompts → petrova-hq/prompts</b><br/>submodule"]:::ext templates["<b>core/templates → petrova-hq/templates</b><br/>submodule"]:::ext end consumer["<b>Consumer repo</b><br/>applies templates"]:::ext operator -- "edit + commit + push (in submodule)" --> prompts operator -- "edit + commit + push (in submodule)" --> templates operator -- "bump submodule pointer" --> parent parent -- "00-bootstrap.md fills <<PLACEHOLDER>>" --> consumer
classDef person fill:#1c1c24,stroke:#e85d3e,color:#f0ece6 classDef system fill:#1c1c24,stroke:#d4a574,color:#f0ece6 classDef ext fill:#141419,stroke:#8b7e74,color:#f0ece6,stroke-dasharray: 4 3 classDef db fill:#1c1c24,stroke:#d4a574,color:#f0ece6 classDef container fill:#1c1c24,stroke:#d4a574,color:#f0ece6After cloning petrova-hq:
git submodule update --init --recursive…populates core/prompts/ and core/templates/. Without this step the prompts and templates are empty.
The two-step commit dance
CLAUDE.md codifies the rule: “edits to prompt or template files happen inside the submodule and must be committed and pushed there before the parent’s submodule pointer can be bumped — do not commit submodule changes from the parent.”
Concretely:
flowchart LR edit["edit core/prompts/02-phase-close.md"] edit --> sub["cd core/prompts"] sub --> c1["git commit -m 'fix: ...'"] c1 --> push1["git push origin main"] push1 --> back["cd ../.."] back --> add["git add core/prompts"] add --> c2["git commit -m 'bump prompts pointer'"] c2 --> push2["git push origin main (parent)"]A parent-only commit that mutates core/prompts/foo.md without first landing the change in the submodule’s own remote is a footgun: the parent now points at a local-only SHA in its submodule.
Why fleet-snapshot.yml skips submodules
.github/workflows/fleet-snapshot.yml runs actions/checkout@v4 without submodules: recursive. The reason is mechanical: the prompts and templates submodules are private and the workflow’s default GITHUB_TOKEN cannot read them. The dashboard generation only needs registry.yaml, state/, and cli/ from the parent — not the submodule contents — so the checkout deliberately leaves them empty (commit 5a040f1, “fix(ci): fleet-snapshot workflow — skip private submodules”).
Why two submodules, not one
The split mirrors the two distinct artefact lifecycles:
core/prompts/— paste-into-Claude prompts. Edits here change agent behaviour at runtime.core/templates/— files copied into a consumer repo at bootstrap. Edits here change consumer scaffolding.
Keeping them separate (MR-3 spirit — sibling files stay sibling) means you can iterate on prompt wording without bumping every consumer’s template baseline.
Key Terms
- Submodule pointer — a 40-char SHA in the parent’s
.gitmodulesindex pointing at the exact submodule commit the parent expects. - Pointer bump — a parent-side commit whose only diff is the submodule SHA moving forward.
- Private-submodule fall-through — CI checkout that succeeds with empty submodule directories because the token cannot fetch the private remote.
Q&A
Q: Where must a prompt edit be committed first — parent or submodule? A: Submodule. Commit and push to the submodule’s own remote first, then bump the pointer in the parent in a separate commit.
Q: Why does fleet-snapshot.yml deliberately skip submodules on checkout?
A: The submodules are private. The workflow’s GITHUB_TOKEN cannot read them, and the dashboard generation needs only parent-tracked files. Skipping is faster and avoids spurious CI failures.
Q: What does a parent commit look like after a submodule pointer bump? A: A single-line diff in the parent’s git index showing the submodule’s old SHA → new SHA, with no file content change in the parent’s working tree.
Examples
Bumping core/templates to add a new MR-15 paragraph: edit META-RULES.md inside core/templates, commit + push to petrova-hq/templates, then cd ..; git add core/templates; git commit -m "bump templates: MR-15 ratified"; git push. Without the second commit, the parent stays pointing at the old templates SHA and consumers using git submodule update keep seeing the pre-MR-15 file.
neighbors on the map
- Rocky Superproject Topology onboarding a new submodule into rocky-hq
- Prompt Artefact Directory Layout scaffolding a new prompt under prompts/
- Documents REST & SQLite Persistence wiring a new document property