| id | 210 |
|---|---|
| title | Single source of truth for product messaging via `mdsmith extract` |
| status | ✅ |
| summary | Hold the mdsmith product slogan, lead, and per-surface descriptions in one schema-conformant Markdown file. Generate Markdown intros via `<?include?>` fragments; sync non-Markdown surfaces from the same source via a new `mdsmith-release sync-messaging` subcommand. A CI drift check fails the build when any surface diverges. |
| model | opus |
| depends-on |
Hold every product-level slogan, lead, and description in one canonical Markdown file. Every public surface derives its copy from that source. Drift fails CI.
Today the same idea is restated in eleven places. Each phrasing is slightly different. Updating the slogan touches every file by hand. Surfaces drift between releases.
The extract command projects a schema-bound
Markdown file into JSON. That gives us a
one-to-many pipeline. It fits the existing
generated-section idiom.
A new file at docs/brand/messaging.md under a
new messaging kind in .mdsmith.yml. The
kind is wired via kind-assignment (not the
kind's path-pattern) so the synced website
parallel at website/content/docs/brand/messaging.md
can also be assigned the kind without
tripping MDS020. All fields live in
frontmatter, which extract projects as keys
under the root frontmatter object. The body
is a short prose explanation of how the file
is consumed.
Frontmatter fields (final names settled during implementation):
title,summary— kind contract.eyebrow— short label above the hero headline.headline-pre,headline-em,headline-post— hero headline parts. Split so the website template can style the<em>segment.lead— multi-line hero lead and README opening paragraph.tagline— one-sentence short form. Used for footers and package manifestdescriptionfields.vscode-description— role-scoped variant for the VS Code extensionpackage.json.claude-code-lsp-description— role-scoped variant for the Claude Code LSP plugin manifest.claude-code-skills-description,claude-code-audit-description— role-scoped variants for the two Claude Code plugins that carry product framing.
Markdown surfaces (READMEs, website body)
consume two generated fragment files via
<?include?>:
docs/brand/fragments/lead.fragment.md— the multi-line lead.docs/brand/fragments/tagline.fragment.md— the one-sentence tagline.
The fragments are produced by the sync command, not authored by hand. They carry the standard "do not edit by hand" header comment.
Non-Markdown surfaces are patched directly
by the sync command. Eleven JSON, TOML, and
YAML-frontmatter fields across nine files. The
implementation extends internal/release. A
new MessagingTargets() registry sits
alongside the existing TrackedManifests().
Each entry names a file plus a typed patcher
(JSON-key, TOML-key, or YAML-frontmatter).
A new mdsmith-release sync-messaging
subcommand registered in
cmd/mdsmith-release/main.go:
sync-messaging— load the source by shelling out togo run ./cmd/mdsmith extract messaging docs/brand/messaging.md --format jsonso the loader rides the existing, kind-aware extract pipeline and themessagingkind schema validates the input before sync runs. Regenerate fragment files. Patch every registered surface. Print a summary of bytes changed.sync-messaging --check— same load and render. Compare against on-disk contents. Exit non-zero on drift. CI uses this gate.
- Plan and brand source. Land this plan.
Draft
docs/brand/messaging.mdwith the slogan copy locked in conversation. Add themessagingkind to.mdsmith.ymlwith an inline schema (kind declaration only — no rule changes). - Extract round-trip test. Add an e2e
test that runs
mdsmith extract messaging docs/brand/messaging.mdand asserts the JSON tree contains every required field. - Sync command — read path. Add a
sync-messagingsubcommand that loads the source by invokinggo run ./cmd/mdsmith extract messagingand prints the parsed messaging struct. Unit test for the loader. - Patchers per target type. JSON-key
patcher for
package.jsonandplugin.json. TOML-key patcher forhugo.toml. YAML-frontmatter patcher for_index.md. Fragment writer for<?include?>-consumable files. Each lands ininternal/releasewith unit tests. The existing TOML version-stamp helper atinternal/release/version.gois the precedent. - Target registry. Add
MessagingTargets()listing every tracked surface and the patcher each one uses. Wire thesync-messagingapply path to walk it. - Drift check. Implement
sync-messaging --check. Add an integration test that mutates a surface and asserts the command exits non-zero with a clear message. - Wire the surfaces. Run
sync-messagingonce. Commit the generated fragments and the patched surfaces. Replace the hand-written intro paragraphs inREADME.md,npm/mdsmith/README.md, andpython/README.mdwith<?include?>directives that pull from the generated fragments. - CI gate. Add
mdsmith-release sync-messaging --checkto the existing CI workflow. Document the workflow indocs/development/release-tooling.md.
-
docs/brand/messaging.mdexists andmdsmith check .passes against the newmessagingkind. -
mdsmith extract messaging docs/brand/messaging.md -f jsonemits a tree whosefrontmatterobject contains every documented field as a non-empty string. -
mdsmith-release sync-messagingregenerates fragments and patches every tracked surface. Running it twice in a row is a no-op (byte-stable). -
mdsmith-release sync-messaging --checkexits 0 when the tree is clean. It exits non-zero with a diff-style message when any surface drifts. - Every tracked surface renders the locked slogan copy after the sync.
-
<?include?>directives in the three README intros pull from the generated fragments.mdsmith fixis a no-op against them. - CI runs
sync-messaging --checkand blocks merge on drift. - All tests pass:
go test ./.... -
go tool golangci-lint runreports no issues.
- Updating role-specific descriptions for
claude-code-dev,claude-code-reviewer, andclaude-code-autofix. They describe narrow tools, not the product. The registry is easy to extend later. - Translating slogans. Single-language source for now.
- The website topnav copy. It is currently the bare wordmark with no tagline.