CRUMB a card from devarno-cloud

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:#f0ece6

After cloning petrova-hq:

Terminal window
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 .gitmodules index 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