Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD across contents and paths, upstream package/repo coordinates -> @golem15/msd-core and golem15com/msd-core. Deep links into upstream history, sibling upstream packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is. Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line, package/plugin identity, regenerated lockfile, install-tree fixtures, derived registries and benchmark baseline; migration checksum baseline re-locked (MSD keeps its own install state, so no install had applied the old sums); sort-order and regex-escaped expectations in tests adjusted.
7.8 KiB
Partition rules for compact-content splits
(ADR-4139 Decision 5, epic #4139,
Phase 3 #4403.) These are the rules a
workflow.compact_content split — a workflow file broken into a spine plus one or more
detail/*.md parts — must obey, and the CI guard (tests/compact-content-partition-guard.test.cjs)
that enforces them. See docs/adr/4139-compact-content-seam.md for the full design rationale;
this document is the operational reference for anyone performing a split.
The rule
A split moves text. It does not restate it. The spine and its detail parts are pieces of one document, not two. There is exactly one copy of each sentence, so there is no stale twin that can exist — which is what replaces the drift-parity check an earlier design of this feature would have needed forever.
Rewriting for terseness is permitted within one half and is never a way to move a sentence into both. Where a split cannot be made by moving text alone without breaking the spine's ability to run the workflow correctly on its own, the correct answer is a different split point, not a duplicated paragraph.
The protected-content list
Content in this list may never leave a workflow spine during a split — it stays directly in
the eagerly-loaded spine file, never moved to a detail/*.md part, regardless of how much it
would shrink the spine:
- Negative instructions and guardrails — any "do not X" / "never X" instruction that
changes what the orchestrator must refuse to do (e.g. "Never call
ScheduleWakeup... to literalize this wait"). - Output-format contracts — any block defining the literal shape of output another
system consumes: a prompt template handed to a subagent, a JSON/XML schema, a
<quality_gate>or<success_criteria>checklist. - Few-shot examples the workflow's own steps depend on — a worked example whose absence
would leave a later instruction ambiguous (e.g. a
<verify>/<fails_when>XML pair a planner prompt's own rule depends on). - Security and prompt-injection language — any text establishing a security boundary or defending against injected instructions.
- Machine-parsed structural headings — a heading or marker another tool locates by exact
text (a
## PLANNING COMPLETE-style return marker, a<!-- msd:section -->directive, a<process>/</process>boundary).
Marking
A sentinel comment declares protection at authoring time. The guard checks for the sentinel-wrapped content's continued presence in the spine, never for category membership — a guard cannot judge prose category on its own, so protection is declared, not inferred:
<!-- msd:protected -->
… one protected block …
<!-- msd:protected:start -->
… a protected region spanning several blocks …
<!-- msd:protected:end -->
The categories above are authoring guidance for where to place a sentinel when splitting a file — they are never what the automated guard evaluates; only the sentinel-wrapped content's continued presence in the spine is.
The five checks
The guard discovers registered splits by scanning msd-core/workflows/** for any
<name>/detail/*.md path and pairing it with msd-core/workflows/<name>.md. There is no
separate registry to maintain — a pair is registered by existing on disk.
-
Completeness — once, at split time. Fires only on the PR that introduces a new
<name>/detail/*.mdpath (i.e. the PR performing the split). The union of the new spine and its new detail parts, whitespace-normalized, must contain every non-trivial line the old spine carried at the merge-base. This is what makes a split reviewable; it never fires again for that pair afterward. -
Disjointness — ongoing. No non-trivial line may appear in both a spine and any of its detail parts, checked on every PR against every registered pair regardless of what the PR touched. This is the invariant that keeps duplication from creeping back in.
-
Registration — ongoing. A
<name>/detail/*.mdwith no<name>.mdspine, or a spine whose prose names a detail path that does not exist on disk, fails and names the orphan. -
Protected content — ongoing, and cannot be excused. For every registered spine a PR's diff touches, every line that sat inside a
<!-- msd:protected -->sentinel at the merge-base must still be physically present in the spine. Deleting it or moving it into a detail part both fail — naming the sentinel's first line and, for a move, the destination path. NoBoundary-Move-Declaredtrailer excuses this one: protected content is categorically barred from leaving the spine, not merely required to be declared when it does. -
Boundary moves are declared — ongoing. For ordinary (non-protected) content: if a non-trivial line is removed from a registered spine and the identical line appears newly added in that spine's own detail parts in the same diff, one of the PR's own commits (
merge-base..HEAD) must carry:Boundary-Move-Declared: msd-core/workflows/<name>.md — <why this moved>A missing trailer fails and names the spine and the moved line. This mirrors ADR-3942's
Emitted-Drift-Ack-Hash/-Growthtrailers exactly — samegit log $(git merge-base <base> HEAD)..HEADrange, same fail-closed behavior when that range is uncomputable (a shallow clone throws, it never silently reports "no violation"), same de-duplication of identical trailers across a rebase, same hard error when two commits declare the same spine with different reasons. SeeCONTRIBUTING.md's "Editing shipped content" section for the trailer mechanism's general shape.
Checks 1 and 4–5 read merge-base..HEAD, never base..HEAD (two-dot) — the same correction
ADR-3942 made for its own trailer range, for the same reason: a two-dot range would let the
set of commits being checked and the set of files being diffed disagree about what "this PR"
means.
Deciding whether a file is worth splitting
Not every eagerly-@-included workflow benefits from a spine/detail partition. Epic #4139
Phase 5 (#4405) established the working
criteria, applied to every file in the eager-window corpus:
- Split it when the file has one or more clearly-delineated steps or sub-sections that are
genuinely optional or rare in normal execution — gated by an explicit flag, an off-by-default
config key, an uncommon runtime condition, or a fallback path most runs never take. The five
files split in Phase 5 (
execute-phase.md,docs-update.md,new-project.md,verify-work.md,complete-milestone.md) all had this shape. - Record it as not worth splitting when either: (a) the file's size comes predominantly
from safety-critical, always-relevant orchestration logic and documented bug-history
comments rather than deferrable narrative elaboration — extracting from it would butcher
core happy-path logic or bury a regression-preventing "why" comment (
review.md's disposition in Phase 5, despite being named among the epic's heaviest files); or (b) the file is small enough that the fixed structural cost of a split — a newdetail/directory, the five checks' ongoing enforcement surface,docs/INVENTORY.md/manifest bookkeeping, and reviewer attention — is not justified by the achievable savings. The smallest file split in Phase 5 (complete-milestone.md, 41,278 bytes) still only yielded roughly 10.4 KB of actual reduction; a file well under that size buys proportionally less for the same fixed cost.
A "not worth splitting" disposition is not permanent — re-evaluate a file if it grows substantially, or a later change gives it a genuinely optional or rare execution branch it didn't have before.