| id | 2606100534 | |
|---|---|---|
| title | Workspace-unique front-matter fields (unique-frontmatter rule) | |
| status | ✅ | |
| model | sonnet | |
| summary | New rule MDS069: within configured include/exclude globs, no two files may carry the same value in a named front-matter field. Enabled for plan ids so a same-minute id collision fails mdsmith check on the second PR's merge-queue run. | |
| depends-on |
|
Timestamp ids (2606100533) shrink the plan-id race to one UTC minute but cannot remove it. Nothing fails today when two plan files share an id. The nine legacy pairs all merged green. The merge queue tests the merged result. A workspace uniqueness check would therefore fail the second PR as soon as both files meet. At that moment the fix is a one-commit rename.
A rule that reports a repeated front-matter value within
a configured file scope. The later file in path order
gets the diagnostic. Its message names the field, the
value, and the earlier file. An id collision then
surfaces as a mdsmith check failure, not as a silent
duplicate.
New rule unique-frontmatter, MDS069, under
internal/rules/uniquefrontmatter/. Settings are flat,
mirroring cross-file-reference-integrity's idiom:
field:— the front-matter key whose values must be distinct. Files without the field are skipped.include:/exclude:— glob lists selecting the scope, with globs.md semantics. Emptyincludedisables the rule, so it ships enabled by default and inert.
A list-of-scopes shape stays out until a second use case
exists; every comparable rule grew from flat settings.
The list settings replace on merge (the default); the
choice is documented next to ApplySettings.
The include globs intentionally repeat the plan kind's glob from kind-assignment. Rule settings have no precedent for referencing kinds, so the two must be updated together if plan files ever move.
Diagnostics are host-file-anchored, like MDS027: when
Check(f) sees that an earlier in-scope file already
holds f's value, it reports on f at its front-matter
line. The earlier file stays clean. One diagnostic per
later file. No rule.RepoScoped marker — each file's
diagnostic tuple differs, so DedupeDiagnostics is not
involved.
Example: with plan/a.md and plan/b.md both carrying
id: 7, only plan/b.md is flagged:
front-matter "id": value 7 already used by plan/a.md
The value-to-first-file index builds once per run, in a
dedicated RunCache slot (UniqueFieldIndex). The
index registers a scope matcher with the slot, so an
LSP edit drops it only when the edited path falls inside
the scope's globs — an unrelated edit keeps the index
warm. Hosts without a RunCache fall back to the
per-File memo. The index reads the workspace as saved on
disk, the same view every cross-file rule gets; routing
the LSP's unsaved-buffer overlay into RootFS
engine-wide is follow-up work.
The repo-wide budget gate's fixture configures no
scopes, so it only exercises the inert path. The rule
therefore ships its own alloc test. That test configures
settings, warms the cache, and asserts ≤ 10 allocs per
Check on the steady state — the repo's per-package
alloc-test pattern, applied to Check itself rather
than a helper.
Enablement is gated on the release train. The
mdsmith-fixed-version CI job runs the pinned release
binary. That binary predates MDS069 and silently ignores
the unknown rule key. Early config would therefore
enforce uniqueness under the branch binary but not the
pinned one. The block lands after the rule ships in a
tagged release and the pin bumps. That keeps both jobs
consistent — the
adopt-new-syntax process.
This plan stays 🔳 with task 4 open as the tracked
reminder. The .mdsmith.yml edit needs
maintainer consent. Approving this plan grants it for
exactly the block below. It nests under the top-level
rules: key:
rules:
unique-frontmatter:
field: id
include: ["plan/*.md"]
exclude: ["plan/proto.md"]A rule, not workflow shell. The release-tooling page bars inline logic in workflows. Per-scope unique fields are also a reusable cross-file integrity feature worth dogfooding: unique ids or titles across any kind.
- Red/green unit tests in
rule_test.go, then the rule: registration, settings parsing, the run-scoped index, and the diagnostic on the later file. - The configured-path alloc test described above;
keep
Checkwithin ≤ 10 allocs/op on the steady state. - Fixtures under
internal/rules/MDS069-unique-frontmatter/(good/bad with expected diagnostics) plus the rule README on the rule-readme schema. - Gated on the pin bump: after a release ships
MDS069 and
MDSMITH_VERSIONinsetup-mdsmith-pinned-versionmoves to it, add the plan-id block to .mdsmith.yml (consent above) and verify a duplicate plan id failsmdsmith check .while the clean tree passes. Done with the v0.40.0 pin bump, which ships MDS069.
- (Gated on task 4.) A duplicated plan id added
locally makes
mdsmith check .fail; the diagnostic lands on the later file in path order and its message names the field, the value, and the earlier file. Verified once pre-gate with a temporary config block and a probe file: the probe failed at itsid:line naming the first holder. Re-verified with the live block: the probe failed the same way, and the clean tree passes under both the branch binary and the pinned v0.40.0 one. - The bad fixture yields exactly one diagnostic per extra file sharing a value; the good fixture is clean.
- A file missing the configured field, or outside the include/exclude scope, is never flagged.
- The configured-path alloc test passes at ≤ 10 allocs/op; the repo-wide budget gate stays green.
- All tests pass:
go test ./... -
go tool golangci-lint runreports no issues