| name | ssot-enforcer |
| description | Single-source-of-truth refactoring and review workflow. Use when encountering duplicated logic, scattered constants/configuration, parallel implementations, repeated calculations, local policy decisions, model/tool routing outside central registries, or user requests mentioning SSOT, centralize, deduplicate, unify, "two places doing the same thing", "why is this implemented manually", or behavior that should go through shared service/config/registry files. Do not use for merely similar code with different contracts or lifecycles. |
SSOT Enforcer
Use this skill to keep behavior centralized. The goal is to remove parallel sources of truth without creating unnecessary abstraction.
Red Flags — Stop If You Catch Yourself Thinking
| Thought | Reality |
|---|
| "I'll just hardcode the timeout / URL / model ID here too" | If it has an owner (config/registry), import it. A second copy will drift. |
| "I'll copy this regex/parser into the new component" | Export the parser from one place; two copies diverge silently. |
| "These two blocks look the same, I'll merge them" | Same shape is not the same responsibility. Merge only if they must change together. |
| "I'll add one local branch just for this case" | Local policy branches are how a registry rots. Put the decision in the owner. |
| "It's quicker to recompute it in the UI" | The UI may format a value; it must not re-derive the business rule. |
| "I'll wrap the call site with a fallback default" | A fallback at the call site is a second source of truth for that default. Centralize it. |
Relationship To Root Cause First
If duplication is discovered while debugging a concrete failure, first use root-cause-first to prove the failure mechanism. When the proven cause is scattered or duplicated truth, use this skill to consolidate ownership, then return to root-cause-first for regression verification against the original symptom.
Core Rule
One responsibility should have one owner:
- Policy belongs in policy modules.
- Configuration belongs in config/registry modules.
- Calculations belong in calculation/domain modules.
- Tool/model/service routing belongs in routing/provider registries.
- UI formatting can adapt data, but should not reimplement business logic.
If code needs a value, decision, calculation, route, permission, timeout, cost, model choice, parser rule, or state transition that already has an owner, call the owner. Do not duplicate the rule locally.
When Not To Use
Do not use this skill for:
- Code that only looks similar but serves different contracts, data shapes, lifecycle boundaries, or product contexts.
- Pure presentation copy, one-off test fixtures, generated snapshots, or examples where drift cannot affect production behavior.
- Greenfield feature work before there is a clear repeated responsibility or natural owner.
Required Workflow
-
Scan for duplicate responsibility before editing.
- Search for the same constant, enum value, route, model ID, timeout, regex, calculation, parser, state transition, or decision phrase.
- Read both the producer and consumer paths.
- Identify whether the duplication is exact, semantic, or only superficial.
-
Identify the current or missing source of truth.
- Prefer existing central modules over new abstractions.
- Look for registries, config files, shared services, domain utilities, provider interfaces, and type definitions.
- If no owner exists, create the smallest central owner in the natural module boundary.
-
Decide whether to unify.
- Unify when two places answer the same question or must change together.
- Do not unify when similar code serves different contracts, different lifecycle boundaries, or intentionally separate contexts.
- If not unifying, state why the duplication is intentional and how drift is prevented.
- If the duplication caused a concrete bug or regression, keep the original failure from
root-cause-first as the verification target.
-
Move behavior to the owner.
- Export a named function, typed constant, registry entry, or domain helper from the owner.
- Replace local calculations/decisions with calls to that owner.
- Keep adapters thin: translate inputs/outputs, but do not make policy decisions.
-
Protect the contract.
- Add or update tests at the owner level.
- Add one call-site regression test when previous duplication caused a bug.
- Prefer typed unions/const objects over magic strings.
-
Check for drift after the patch.
- Search again for old constants, duplicated logic, and hardcoded values.
- Remove dead exports introduced during consolidation.
- Ensure the final behavior is controlled from the source of truth.
Worked Example
Duplication (what to avoid). The free-shipping threshold lives in two places:
if (cart.total >= 50) order.shipping = 0;
const remaining = 50 - cartTotal;
Marketing changes the threshold to 75. Someone updates the server and forgets the banner — now the banner promises free shipping that checkout does not give. One policy, two sources of truth, one production bug.
Consolidated. Give the policy one owner; let the UI adapt the value for display but not re-decide it.
export const FREE_SHIPPING_THRESHOLD = 75;
export const qualifiesForFreeShipping = (total) => total >= FREE_SHIPPING_THRESHOLD;
if (qualifiesForFreeShipping(cart.total)) order.shipping = 0;
const remaining = Math.max(0, FREE_SHIPPING_THRESHOLD - cartTotal);
Counter-example — do NOT unify. These look identical today but answer different questions:
validateProductName(s)
validateChatMessage(s)
They share a shape, not a responsibility. Product names may later allow 200 chars; chat may add emoji rules. Merging them couples two unrelated contracts, and the next requirement forces an awkward split. Keep them separate — the match is a coincidence, not a shared owner.
Verification. Grep for the old literal (50 and the duplicated >= check) and confirm only pricing.js owns the threshold.
Anti-Patterns To Block
- Hardcoding a model ID, API URL, timeout, route, status, cost, or policy in a tool when a registry/config exists.
- Reimplementing the same calculation in UI and server.
- Adding "just this one" local branch for behavior that belongs in a provider, router, or domain module.
- Copying a parser/regex into a second component instead of exposing a shared parser.
- Creating a new helper that duplicates an existing helper with a different name.
- Adding fallback behavior in a call site when the real fix is central contract enforcement.
Acceptable Local Logic
Local code may:
- Validate local inputs before calling the owner.
- Adapt transport/view shapes to domain shapes.
- Handle presentation-only formatting.
- Contain test fixtures that intentionally duplicate values for readability, when drift cannot affect production behavior.
Final Answer Shape
When this skill affects the work, include:
SSOT: what became or remained the source of truth.
Consolidated: which duplicate paths were removed or routed through it.
Verification: searches/tests proving no obvious duplicate source remains.