Skip to content

Drift guards: sharpen the single-source rule and ship the shared-constants check - #148

Merged
missingbulb merged 3 commits into
mainfrom
claude/laughing-dijkstra-610ehh
Jul 7, 2026
Merged

Drift guards: sharpen the single-source rule and ship the shared-constants check#148
missingbulb merged 3 commits into
mainfrom
claude/laughing-dijkstra-610ehh

Conversation

@missingbulb

@missingbulb missingbulb commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Closes #147.

Two layers, one story — the rule and the conformance check that backs it.

1. Sharpen the rule (engineering-practices line 11)

One definitive place for every fact. Split it across more than one place only when a technological constraint forbids a single home — and then the drift-guard test must state, in its own text, the two-or-more places it watches and why the split is forced.

2. Ship the check that enforces it (packs/universal/shared-constants.mjs)

Promotes GoogleCalendarEventCreator's bespoke shared_constants "uber test" into a universal, data-driven Claudinite check, so every repo gets it instead of reinventing it.

  • Cases declared per-repo in .claudinite-checks.json under a new sharedConstants key — the same category as accept (per-repo parameterization of a shared check). Empty/absent → no-op.
  • Entry shape { what, value, counts }: counts maps a repo-relative path to the exact flat-literal occurrence count; what names the places and why the split is forced. That what + counts is line 11's self-documentation requirement, so the check's own data satisfies the rule it enforces.
  • Findings on: count drift, a declared file that no longer exists, a non-integer count, and a malformed entry missing its what.
  • Preserves flat-literal counting semantics (comments included; line-wrapped occurrences invisible — same as a grep/sed rename).

3. Regex mode for values that change over time

A value that must stay in sync but is bumped each release (a version string) can't be pinned as a literal. With "regex": true the entry's value is matched as a pattern, counts still bound the matches per file, and every matched substring across the declared files must be byte-identical — so the guard catches version drift without ever being re-pinned. Invalid patterns and count mismatches still surface as findings; literal entries are unchanged.

Tests: 73 green, including see-it-fail fixtures for literal mismatch, missing file, missing what, no-op, regex in-sync/out-of-sync, regex count, and invalid regex. run.mjs sweep clean.

Why this shape (not a design skill)

Started as a proposed design skill; it settled here because the rule's true trigger is "writing or editing code" (already engineering-practices' trigger), and a broad design skill would fire at the wrong moments. See #147 for the full thread.

Sibling PR

GoogleCalendarEventCreator#646 (draft) migrates GCEC onto this check — including the version constant as a regex entry — and retires its bespoke test. Held until this lands on main.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UTWoDMjJUdK13msGdqwHq4

One definitive place for every fact; split it across more than one place only
when a technological constraint forbids a single home — and then the drift-guard
test must state, in its own text, the two-or-more places it watches and why the
split is forced, so the coupling is self-documenting and reviewed.

Lands the design-decision refinement from #147 at its true trigger ("writing or
editing code") instead of a separate `design` skill: a broad "design" trigger
would fire at the wrong moments and miss the instant the rule applies (about to
duplicate a fact), and the rule is a sharpening of an existing bullet, not a new
body of guidance.

Refs #147

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTWoDMjJUdK13msGdqwHq4
@missingbulb
missingbulb force-pushed the claude/laughing-dijkstra-610ehh branch from f7d27cc to 8a3c2b6 Compare July 7, 2026 10:56
@missingbulb missingbulb changed the title Add a design skill: guard a deliberate separation, don't collapse it Sharpen the single-source/drift-guard rule in engineering-practices Jul 7, 2026
Promotes GoogleCalendarEventCreator's bespoke shared-constants "uber test" into a
universal, data-driven Claudinite check. Cases are declared per-repo in
.claudinite-checks.json under "sharedConstants" (same category as "accept"): each
entry is { what, value, counts }, where counts maps a repo-relative path to the
exact flat-literal occurrence count and what names the places and why the split
is forced — so the guard self-documents, satisfying the line-11 rule it enforces.
Findings fire on count drift, a vanished declared file, a non-integer count, and
a malformed entry missing its "what"; an empty/absent list is a no-op.

Slims engineering-practices line 11 from describing the bespoke test to pointing
at the check (the finding owns the mechanics now), keeping only the one-line
flat-literal authoring gotcha.

See-it-fail fixtures cover mismatch, missing file, missing "what", and the
no-cases no-op.

Refs #147

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTWoDMjJUdK13msGdqwHq4
@missingbulb missingbulb changed the title Sharpen the single-source/drift-guard rule in engineering-practices Drift guards: sharpen the single-source rule and ship the shared-constants check Jul 7, 2026
A value that must stay in sync but changes over time (a version string bumped
each release) can't be pinned as a literal. With "regex": true the entry's value
is matched as a pattern, counts still bound the matches per file, and every
matched substring across the declared files must be byte-identical — so the guard
catches version drift without ever being re-pinned on a bump. Invalid patterns
and count mismatches still surface as findings; literal entries are unchanged.

Refs #147

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTWoDMjJUdK13msGdqwHq4
@missingbulb
missingbulb marked this pull request as ready for review July 7, 2026 12:08
@missingbulb
missingbulb merged commit 4ba0932 into main Jul 7, 2026
1 check passed
@missingbulb
missingbulb deleted the claude/laughing-dijkstra-610ehh branch July 7, 2026 12:08
missingbulb added a commit to missingbulb/GoogleCalendarEventCreator that referenced this pull request Jul 7, 2026
…st (#646)

Moves the 8 shared-constants cases from dev/procedures/test/uber/shared_constants/
into a "sharedConstants" array in .claudinite-checks.json, where the universal
shared-constants check (missingbulb/Claudinite#148) reads them. Deletes the
bespoke uber test and its data dir, and drops the now-missing file from the
test:offline script.

The version constant becomes a regex entry — the check requires its match
identical across manifest.json and package.json, so it never needs re-pinning on
a bump. With version-sync.json gone, bump-patch-version.js drops its third sync
file (and its test drops it too), and the human "bump version" procedure loses
that step.

Refs #642


Claude-Session: https://claude.ai/code/session_01UTWoDMjJUdK13msGdqwHq4

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a design skill: guard a deliberate separation with a conformance check instead of collapsing it

2 participants