Authoring Invariants Skill
Overview
This skill turns an architectural rule in the author's head into a
registered,
enforced catalog entry. It is the on-ramp described in
docs/designs/archive/2026-05-25-invariants-authoring-wizard.md
— an LLM-driven
authoring conversation,
not a stdin question loop.
The division of labor is strict and load-bearing:
- The agent (you) supplies judgment + natural-language elicitation. You
interview the author, draft prose, propose globs, and shape the entry.
- The verbs own validation + writing. Every mutation goes through a
deterministic, schema-validated orchestrate action —
and . You never hand-write or append catalog YAML
yourself. The verb renders it; you confirm it.
This shape is the only one consistent with the agent-first philosophy: inputs
are constrained at the schema level (INV-5a), mutating verbs default to
dry-run (INV-5c), and authoring is event-sourced (INV-1).
When to Use
- The author wants a new architectural rule enforced on their own SDLC path
(surfaced at ideate, turned into acceptance criteria at planning, audited at
review by the
check_invariant_conformance
gate).
- An author says "add an invariant", "enforce that X", "make this a rule".
When NOT to Use
- The author wants to hand-write YAML — point them at the verbs instead; the
skill exists so they don't have to.
- The rule is a one-off lint, not an architectural invariant → a project linter
is the right home.
- You are mid-workflow editing state, running a review, or planning — those are
other skills. This skill only authors catalog entries.
The verbs (what you drive — never bypass)
| Action (MCP) | CLI facade | What it does |
|---|
| exarchos invariants scaffold
| Create a starter catalog file for a tier; idempotently register it in . Never overwrites an existing file. |
| | Validate ONE entry against the v3 schema (including the enforcement DSL — INV-4), then append it. defaults to true: returns the rendered entry + file diff, writes nothing. |
| | Reuse the existing check — the validator. No new validate verb. |
| exarchos view invariants_effective
| The merged, projected catalog the gate will enforce. Post-write confirmation. |
The agent
never declares an entry valid on its own authority — the verb is
the validator.
returns the INV-5b carrier shape: success
carries
(
["doctor", "view invariants_effective"]
); validation
errors carry
/
/
sourced from the
Zod error, so you can self-correct and re-run rather than re-guess.
The interview (6 steps)
Walk the author through these in order. Elicit in prose, never make them think
in YAML field names.
1. Elicit — the rule →
Ask the author to state the rule in one sentence. Distill it into a precise
. Probe for the
failure it prevents ("what goes wrong if this is
violated?") — that sharpens both the summary and the later enforcement.
2. Locate — , , affinities
- : a free-text grouping (e.g. , ).
- : the glob(s) the rule governs — ask the author to name the paths
(, ). These are author-named globs, never
framework-inferred (INV-6: the surface is workload-neutral).
- : phases where it bites (
ideate | plan | delegate | review | synthesize
). Absent ⇒ all phases.
- : workflow types (
feature | debug | refactor | discovery | oneshot
). Absent ⇒ all.
3. Weight — , , and the you author into
- : or .
- (optional): downgrade for cheap workflows
(e.g. ).
- (entry field; enum
substrate | sdlc | authoring | user
):
the entry's override authority, not its namespace. For a consumer-authored
rule this is . (/ are exarchos's own classes — you do
not author those.)
- (the verb arg) picks the catalog namespace, and the choice is
exarchos-substrate vs project-authored, NOT "which developers". This is the
one that bites — get it wrong and you silently collide with exarchos's own ids:
- → ids — your project's own invariants. The default for
everyone consuming exarchos. If you are authoring a rule for your own
repo, this is always the answer (even if your project happens to name its
rules internally — they map to here).
- → ids — exarchos's own reserved substrate catalog.
Exarchos ships its own inside the tool, and they merge into every
projection. Authoring into from a consumer repo
collides your with exarchos's own — a silent namespace clash the
check can't catch (it only flags in a user catalog). Use
only when working inside the exarchos repo itself. The verbs
enforce this: / reject
outside the exarchos repo (heuristic: name ≠
) with a error that redirects to ;
a genuine exarchos fork opts in with .
4. Enforce — DEFAULT , is opt-in
Default to . You draft the
from the elicited
rule — a question the review subagent answers against the diff. Audit mode is
pure judgment and always portable:
yaml
enforcement:
mode: audit
audit-prompt: >-
Does this diff let a request handler return before emitting an audit event?
Cite the offending file + line.
Offer
only as an
advanced opt-in when the rule is mechanically
checkable. If the author opts in, propose a declarative combinator tree over
grep/structural/heuristic leaves and
validate it live via
with before showing it. See
@references/check-mode.md
for the
combinator vocabulary and the opt-in flow. The enforcement DSL is
and declarative-only: there is no
/
/
escape hatch
(INV-4), so you cannot emit an executable check even if asked.
5. Number — auto-id in the target namespace
Do
not pick an id.
auto-assigns the next free id in the
target catalog's namespace (
for user,
for dev). Mention the id the
verb assigned when you show the dry-run.
6. Commit — dry-run → confirm → write → verify
This step is a gate, not a formality. Always:
- Call with (the default). This renders the
entry + diff and writes nothing.
- Show the author the rendered entry and the file diff verbatim. Make the
confirmation explicit: ask "commit this entry?" — do not silently re-invoke.
- Only on explicit confirmation, re-invoke with
. This appends the entry, wires if the catalog
is unregistered, and emits (+ on
first registration — INV-1).
- Run (the check) to validate the resolved catalog.
- Show the delta so the author sees exactly what the
gate will now enforce.
If
returns a
error, the target catalog does
not exist yet — run
first (the error's
names the call), then resume at step 6.
Tool invocations
Scaffold a user catalog (idempotent; never overwrites):
ts
exarchos:exarchos_orchestrate({
action: "invariants_scaffold",
tier: "user",
path: ".exarchos/invariants.md"
})
Dry-run preview (DEFAULT — writes nothing):
ts
exarchos:exarchos_orchestrate({
action: "invariants_add",
tier: "user",
catalog: ".exarchos/invariants.md",
entry: { /* the fields from steps 1-4; NO id — auto-assigned */ }
})
Commit after explicit confirmation:
ts
exarchos:exarchos_orchestrate({
action: "invariants_add",
tier: "user",
catalog: ".exarchos/invariants.md",
entry: { /* same entry */ },
dryRun: false
})
Use
exarchos:exarchos_orchestrate({ action: "describe" })
(or the CLI
) to discover the exact schema at runtime — flags auto-emit from each
action's Zod schema (the CLI is schema-driven; do not assume hand-added flags).
Worked example
For one
entry authored end-to-end through all 6 steps, see
@references/worked-example.md
.
Anti-Patterns
| Don't | Do Instead |
|---|
| Hand-write or catalog YAML | Always route mutations through |
| Declare an entry valid yourself | The verb validates; you confirm |
| Default to | Default to ; is opt-in (@references/check-mode.md
) |
| Skip the dry-run | first, ALWAYS, then explicit confirm |
| Silently re-invoke with | Make the confirmation step explicit |
| Pick an id by hand | The verb auto-assigns the next free id in the namespace |
| Author into / from a consumer repo | is exarchos's reserved substrate namespace — use / (the verb rejects consumer ) |
| Infer globs from the framework | Ask the author to name the globs (INV-6) |
| Skip + after commit | Verify the resolved catalog and show the delta |