| name | unikit-gd-verify |
| description | Mechanical consistency check for game design — answers "is the design consistent with itself?" — plus changed-scope impact analysis across all three GDD axes: systems, flows, and content types. Offline and deterministic: greps the facts registry against the documents (numbers, terms, IDs, duplicate & dangling references, roster↔disk, map freshness, Depends 3-way, status/version, AC presence, placeholder leaks, flow & content schema), and from a git diff computes which dependent systems, flows, and content a change affects. Scope is inferred: a named document checks that document; an unverified diff triggers a changed-scope pass; otherwise everything. Use when the user wants a consistency or impact check, e.g. "verify the design", "what did this change affect", "find broken references in the design". Conflicts triage into four tracks; the entailed fixes hand off to /unikit-gd-apply in one pass. This is the mechanical pass — for a subjective "is this design good" quality critique use /unikit-gd-review. |
| argument-hint | [system name | SYS-slug | question] (scope inferred; no flags) |
| allowed-tools | ["Read","Glob","Grep","Bash(ls *)","Bash(find *)","Bash(wc *)","Bash(date *)","Bash(git diff *)","Bash(git status *)","AskUserQuestion"] |
| disable-model-invocation | false |
| user-invocable | true |
| metadata | {"author":"unikit","version":"1.0","category":"game-design"} |
Game Design — Consistency & Impact Verification
Answer "is the design consistent with itself?" — a mechanical, deterministic
check of every document against the facts registry, plus the changed-scope
impact of a recent edit. This is the design-side mirror of unikit-verify and a
CI-linter to unikit-gd-review's senior reviewer: cheap, binary, run after every
edit. A verify conflict cannot be declined (unlike a review finding) — it is
resolved.
unikit-gd-verify is fully read-only — it writes nothing (no report file, no
changelog line, no doc_status bump, no [gen]-map re-render). It detects and prints;
every fix is performed by unikit-gd-apply (the entailed deltas) or the owning zones
(authoring + the [gen]-map re-render on their next write — B1: the writer of GD-IDS
re-renders the affected maps).
Conflicts are precise, but their fix is not always predetermined, so each is sorted
into one of four tracks — freshness (print-only; the owner re-renders the
[gen] map on its next write), entailed → apply-ready (a named fix — incl. a
status/version header correction — handed to unikit-gd-apply), direction interview
(the user picks which fix, never whether), and authoring → owner / explore. The
apply-ready set hands off in one move — inline prose (verify writes no handoff
file, unlike unikit-gd-review's durable report), recommend-only. The handoff + interview
run only on a standalone /unikit-gd-verify; when verify is unikit-gd-apply's
closing Phase 3 (the apply-phase3 sentinel) they are suppressed, so
apply → verify → apply never loops. The triage engine is shared with unikit-gd-review
(gd-critique → Handoff Engine).
Verification is offline and reproducible: no web research, no expert judgment,
no LLM guessing where a grep will do. The same inputs always yield the same result
(idempotent).
Language Awareness — BLOCKING PRE-REQUISITE
BEFORE producing ANY output, silently read .unikit/system/LANGUAGE_RULES.md
and apply it to all output and the report (fall back to English if it is missing) —
including the rule to translate concepts, not transliterate jargon.
gd-principles → "Language" adds the game-design specifics: which IDs and stored
field values stay English. Do not announce the language setting.
Phase 0 — Bootstrap
Silently load — do not narrate:
.unikit/system/gamedesign/gd-principles.md (the core) — the facts
registry / ID conventions (registry wins until the user resolves otherwise;
never delete an ID — deprecate it) and the language rules. Plus, from the same
gamedesign/ folder, the shards this skill needs: gd-lifecycle.md (the
status/version coherence spine), gd-flow-axis.md (the flow coherence
contract — mode: ↔ structure, GOAL → SYS/AC, Win/Lose ↔ terminal GOAL),
gd-content-axis.md (the content coherence contract — CU ⊆ CT, ref<>
resolution, scale ↔ structure, belongs_to 3-way, content-map freshness,
RES/TRACK/KNOB, cross-axis SYS→CT staleness), and gd-critique.md (the
severity rubric — a conflict is Critical/Major evidence). This skill
applies them. If missing, warn (unikit-ai update) and continue with the
conventions summarized here.
.unikit/gamedesign/GD-IDS.yaml — the single source of truth: pillars,
systems, flows, entities, formulas, terms. Current values only.
Schema guard (clean break — no automatic migration): it MUST be version: 2.
On a pre-v2 version: 1 registry, STOP and report that the design workspace
is on the pre-v2 layout (the standalone markdown system-index was dropped; the
roster now renders into GAME.md ## System Map [gen]) — there is no automatic
migration; tell the user to upgrade via /unikit-gd-spec before verifying.
.unikit/gamedesign/GAME.md — incl. its generated ## System Map [gen],
## Flow Map [gen], and ## Funnel [gen] renders (the human-readable maps; the
truth is GD-IDS), and its ## Win / Lose Conditions (for the Win/Lose ↔ terminal
GOAL check).
.unikit/gamedesign/systems/*.md and .unikit/gamedesign/flows/*.md — the
system and flow documents checked against the registry.
One-way boundary: never read .unikit/code/, project source, or build
artifacts. Web research is forbidden here (gd-principles) — verification must
stay deterministic. The only git use is reading the design-workspace diff.
Phase 1 — Resolve Scope (no flags)
Loop-guard sentinel (first). If the argument is exactly apply-phase3 — the
reserved loop-guard sentinel (gd-critique → Handoff Engine), passed by
unikit-gd-apply as its closing Phase 3 — this run is the in-apply gate, not a
scope. Treat it as a bare invocation for scope purposes (derive changed-scope from the
git diff exactly as below), but set the in-apply flag: the standalone handoff offer
and the direction interview (Phase 4 Track 3 / Phase 6) are suppressed this run, so
apply → verify → apply cannot loop. A standalone /unikit-gd-verify (no sentinel)
leaves the flag off — the handoff is active.
Scope is a function of context:
- The argument names a system, flow or content type → check that document and
its registry facts (a
FLOW-<slug> runs the flow checks, a CT-<slug> the content
checks below).
- There is an unverified diff under
.unikit/gamedesign/ (git diff /
git status shows changed design docs), or the prompt asks "what did this
change affect" → changed-scope pass (Phase 3).
- Otherwise → full check of every system, every flow, every content type,
and the whole registry.
Announce the resolved scope in one line.
Phase 2 — Mechanical Checks (grep-first)
Run every check deterministically; each mismatch is a CONFLICT with a citation:
| Check | Method | Conflict when |
|---|
| Facts | grep each GD-IDS entity/formula value across the docs — match the value on word boundaries, not as a bare substring | a document states a number/name that disagrees with the registry |
| Terminology drift | grep each term's forbidden_aliases values (the listed aliases, not the field name) across the docs | a forbidden alias is used in place of the canonical term |
| ID validity | scan every SYS-/ENT-/FORM-/AC-/PIL-/FLOW-/GOAL- id for shape (analytics events are snake_case tokens, not prefixed ids) | an id is malformed — wrong case, bad separator, or an unknown prefix |
| Duplicate IDs | group every declared id by value | the same id is declared for two different things |
| Dangling references | resolve every referenced id against GD-IDS | a referenced id does not exist, or a live (non-deprecated) section points at a deprecated entry |
| Unregistered cross-doc fact | grep FORM-/ENT- ids that surface in two or more documents | a fact crosses a document boundary yet has no GD-IDS entry |
| Roster ↔ disk | compare GD-IDS systems ids to the systems/*.md files on disk | a skeleton-or-later system has no file, or a systems/*.md file has no GD-IDS entry (→ route to unikit-gd-spec to register it — verify never writes the roster) |
| Map freshness (3-surface) | compare the GAME.md ## System Map [gen] rows to GD-IDS systems — membership, Status, Ver, Depends | the render disagrees with GD-IDS — not a conflict to resolve: print a freshness notice; the owning zone re-renders the block on its next write (verify is read-only — B1); see Map freshness below |
| Depends 3-way | for each A → B edge, check it agrees across A's section F, A's GD-IDS depends_on, and A's ## System Map Depends cell, and that B carries the reciprocal | the three sources disagree, or the reciprocal edge is missing or contradictory |
| Status coherence | a system's doc_status agrees across the two places that must agree — the header > Status: and GD-IDS doc_status (enum not-started · skeleton · detailed) | the two disagree for a system — system docs only; see the carve-outs below |
| Version coherence | the header Version and GD-IDS version agree; a not-started system carries no version, a skeleton-or-later system carries one | the two disagree, or a skeleton+ system is missing a version / a not-started system has one |
| AC presence | for a detailed-or-later system, section H is non-empty and every AC-<sys>-N id is unique | a detailed+ system has an empty H, or repeats an AC-<sys>-N — gaps in the numbering are not a conflict (numbering is stable after an AC is removed) |
| Placeholder leak | grep [To be designed] inside detailed-or-later documents (a <!-- deferred --> marker is intentional — never a leak) | a detailed+ document still carries a skeleton [To be designed] placeholder |
| Core-floor coherence | for a detailed document, check that no core section carries <!-- deferred --> — the core-set per zone (gd-lifecycle: system A/B/C/D/H · flow A/B · content A/B/C) | a detailed+ document defers a core section — the status was raised past the floor (the soft guard in gd-authoring was bypassed); route to the owning zone to author the core section or lower the status. A deferred non-core section is not a conflict (it is the inferred detailed · partial — see the render below) |
The registry is authoritative: when a document disagrees with GD-IDS, the
registry wins until the user resolves it the other way (gd-principles).
Map freshness — print-only, not a conflict. The ## System Map [gen] block in
GAME.md is a deterministic render of GD-IDS systems (gd-lifecycle →
Lifecycle & Status), so a disagreement is a freshness issue, never a coherence
conflict to resolve. On any of —
- a
GD-IDS system missing from the map (no row);
- a phantom row in the map with no matching
GD-IDS system;
- a row whose Status / Ver / Depends differs from
GD-IDS,
— print a freshness notice naming the stale rows (## System Map [gen] stale — <rows>; owner re-renders on next write). verify is read-only: it does not
re-render the map. The owning zone re-renders on its next write — B1: the writer of
GD-IDS (unikit-gd-spec for the System Map, unikit-gd-flow / unikit-gd-content for
theirs) re-renders the affected [gen] block with the same deterministic render (group
by category, sort by tier then design order, display-precedence on
deprecated/implemented). A systems/*.md file with no GD-IDS entry (the
Roster ↔ disk check) is likewise routed: the roster is unikit-gd-spec's to write,
so verify routes the user to /unikit-gd-spec to register the system.
Deferred markers, the · partial (n/m) render, and the self-check (all three maps).
A <!-- deferred --> marker is an intentional omission (gd-lifecycle — a section
the author chose to skip at this depth), never a placeholder leak. Its only
mechanical consequence at verify time is the render: a detailed+ document carrying
≥1 <!-- deferred --> should show · partial (n/m) appended to its row Status in the
relevant ## *Map [gen], where n = the count of deferred non-core sections (those
carrying the marker) and m = the document's total deferrable (non-core) sections
(gd-lifecycle → the canonical render format + per-zone core-set). This applies
identically to the System, Flow, and Content maps; like all freshness it is
print-only, not a coherence conflict — verify does not re-render (the owner
appends the suffix on its next re-render), and the status value itself stays the read-only
enum (· partial is a render annotation, not a doc_status, and is not part of the
two-place status/version coherence). Self-check (marker ⟺ render): every document with
≥1 <!-- deferred --> must render · partial (n/m) in its map row, and every
· partial suffix in a map must correspond to a document that actually carries the
marker — a mismatch in either direction is a freshness discrepancy, printed (the
owner fixes it on re-render). Annotate the freshness notice with the count, e.g.
## System Map [gen] stale (partial 2/5 on SYS-combat); owner re-renders.
Scope & carve-outs (Status / Version / AC / Placeholder). These four checks
read the system spine only — the header ↔ GD-IDS pair of a systems/*.md
document (the ## System Map [gen] render is not a coherence surface — its
agreement is handled by Map freshness above):
- Lifecycle-enum scope.
GAME.md (drafted | approved) and CONCEPT.md
(exploring | drafted | approved) keep their own lifecycle enums — a
Status: approved there is correct and is never a status conflict.
- Display precedence. A system with
status: deprecated legitimately shows
deprecated in the ## System Map [gen] Status over its underlying doc_status;
the read-only implemented value (a code-set display overlay) behaves the same.
Both are display overlays in the render — never part of the two-place header ↔
GD-IDS doc_status comparison. The companion GD-IDS implemented_version
field (also code-set, by unikit-verify on all-AC-met) is a code-owned field —
not a doc_status and not a version — so it never participates in Status
or Version coherence and is never flagged as drift.
- Non-id metadata (
research:). The GD-IDS research: field — a path pointer
to the explore research that seeded an artifact (written by unikit-gd-spec
add-system on a system row, by unikit-gd-flow on a flow flows[] row, and by
unikit-gd-content on a content type content_types[] row) — is non-semantic
metadata, not a registry id and not a doc_status / version. It is excluded
from status/version coherence on all three axes, and the id-resolving checks pass it
over by construction: Dangling references only resolves id tokens
(SYS-/ENT-/FORM-/AC-/PIL-/FLOW-/GOAL-/CT-/CU-), and Unregistered
cross-doc fact only greps FORM-/ENT- ids — a folder path matches neither — so no
special-case logic is needed.
Axis-aware checks — flow + content (load on demand)
Beyond the system checks above, the registry may carry flows and content
types, each with its own check family — the mirror of the system checks plus the
axis-specific ones. When GD-IDS flows and/or content_types is non-empty, load
the matching family from {{skills_dir}}/{{self_name}}/references/axis-checks.md and run
it; an empty flows: [] / content_types: [] (or no key) → skip that family
silently (not a conflict). The registry-wins rule and the freshness-vs-conflict
distinction apply exactly as above — freshness is print-only (the owner re-renders).
| Registry has | Family (in references/axis-checks.md) — coverage |
|---|
flows non-empty | the flow family: GOAL-id validity, dangling goal-targets, flow status/version 2-place, flow Depends 3-way, wiring-mode-vs-structure, win/lose-vs-terminal-goal, funnel continuity, flow/funnel map freshness (Realized cross-axis derived) |
content_types non-empty | the content family: CT/CU-id validity, CU-fields-subset-CT, ref-target resolution, scale-vs-structure, belongs-to 3-way, content status/version 2-place, content map freshness, RES/TRACK/KNOB fact coherence |
The cross-axis impact of a system edit on dependent flows / content types is the
reverse-edge walk in Phase 3 (it stays here — it is impact analysis, not a per-axis
check).
Phase 3 — Changed-Scope Impact (when a diff is unverified)
Compute the blast radius of a recent edit:
-
Changed set = git diff HEAD (and git status) restricted to
.unikit/gamedesign/ → the systems whose documents changed on disk. (There is no
stored "pending re-verify" state to re-scan — status records readiness only; a change
is carried by the git diff + version, not by a doc_status.)
-
Walk GD-IDS depends_on edges (rendered in the ## System Map Depends column)
to the transitive closure of systems that depend (directly or indirectly) on
a changed one.
-
Classify each affected system into an Affected table:
| System / Flow | Relation | Verdict |
|---|
| SYS-enemy-ai | depends on SYS-combat (changed) | Needs Review |
| SYS-economy | soft dep, no touched interface | Still Valid |
| SYS-loot | uses a removed AC | Likely Stale |
| FLOW-first-session | exercises SYS-combat (changed) via GOAL→AC | Needs Review |
Verdicts: Still Valid / Needs Review / Likely Stale.
-
Print the impact (verify writes nothing):
- Print the Affected table to the console — the changed system + every dependent
with its verdict. verify does not write the
Affected (gd-verify): line into
section K (it is read-only); the owner records that line on its next authoring touch
if it acts on the impact (gd-authoring → Delta Discipline).
- For each affected dependent with a
Needs Review / Likely Stale verdict,
recommend a re-author pass to its owner — verify does not bump any
doc_status (status records readiness only; a re-author bumps Ver, not status).
- Idempotent: re-running on the same diff yields the same Affected table — there is
no state to mutate.
Cross-axis impact (system → flow, one-way). A system edit can stale a flow that
exercises it (through GOAL → SYS / GOAL → AC) — gd-flow-axis → Flow Axis. Extend
the pass across the axis (skip when flows: []):
- Reverse-edge walk: for each changed system, find every flow whose
flows[].depends_on includes it, or whose goals[].targets reference it
(GOAL → SYS / GOAL → AC). Those flows join the Affected table as flow rows
(Relation: exercises SYS-x (changed)), with the same Still Valid / Needs Review /
Likely Stale verdicts (a flow using a removed AC is Likely Stale). The reverse
does not hold — editing a flow never stales a system.
- Print impact (dependent flow): list affected flows in the printed Affected table.
For each
Needs Review / Likely Stale dependent flow, recommend a re-author pass
to unikit-gd-flow — verify writes nothing (no doc_status bump, no FLOW.md
header write, no section-F note, no ## Flow Map [gen] re-render). Acting on the
recommendation (the Ver+1 + changelog touch) is the owner's call.
Cross-axis impact (system → content type, one-way). A system edit can stale a
content type that feeds it (through belongs_to / a ref<SYS> field) —
gd-content-axis → Content Axis. Extend the pass across the axis (skip when
content_types: []):
- Reverse-edge walk: for each changed system, find every content type whose
content_types[].belongs_to names it, or whose CT.fields carry a ref<SYS> to it.
Those types join the Affected table as content rows (Relation: feeds SYS-x (changed)), with the same Still Valid / Needs Review / Likely Stale verdicts (a type
whose consuming system removed a relied-on contract is Likely Stale). The reverse does
not hold — editing a content type never stales a system.
- Print impact (dependent CT): list affected types in the printed Affected table.
For each
Needs Review / Likely Stale dependent type, recommend a re-author pass
to unikit-gd-content — verify writes nothing (no doc_status bump, no
CONTENT-TYPE.md header write, no section-F note, no ## Content Map [gen]
re-render). Acting on the recommendation is the owner's call.
Phase 4 — Triage Conflicts (4 tracks)
A verify conflict is precise (the grep found a real disagreement) but its fix is
not always predetermined, and it can never be declined — only resolved or routed.
Using the shared handoff engine (gd-critique → Handoff Engine), sort every conflict
into one of four tracks:
-
Freshness — print-only — verify is read-only, so it prints, never fixes. The
[gen]-map freshness (Phase 2) is announced as a stale-rows notice; the owning zone
re-renders on its next write (B1). A status / version header ↔ GD-IDS disagreement
(where GD-IDS is the machine truth) is not healed here — its correction (set the
header to GD-IDS) is a clean entailed fix and goes to Track 2.
-
Entailed → apply-ready — the conflict's fix passes the silent ENTAILED criterion
(one concrete target · the correct value already authoritative in GD-IDS · exactly one
local fix · no external knowledge) but needs an owner write verify cannot make
(verify writes nothing): a status / version header to correct to GD-IDS, a
duplicate id to renumber, a terminology drift to swap to the canonical term, a
dangling reference to repoint. Put it in the apply-ready set — the named fix
handed to unikit-gd-apply (Phase 6) as a prose delta, authored by the owner, not here.
-
Direction fork → interview — the conflict admits more than one valid direction:
a value conflict (the document or the registry could each be right), a dangling
GOAL → SYS / AC fixable three ways across three zones, a roster ↔ disk mismatch
(register vs delete), an unregistered cross-doc FORM-/ENT- (register vs remove).
The user picks the direction — never whether to act (decline-vs-direction,
gd-critique). The interview is direction-only and batched (AskUserQuestion, ≤ 4
per call); it is suppressed under the in-apply flag (Phase 1 sentinel):
AskUserQuestion: CONFLICT — <doc/section> vs GD-IDS: <the disagreement>. Which direction?
Options:
1. Fix as A — e.g. registry is right → correct the document
2. Fix as B — e.g. document is right → update GD-IDS to <X> (with approval; record it)
3. This is authoring / needs a decision → route to the owner or /unikit-gd-explore
There is no "Decline" — a consistency conflict is a fact, not an opinion. A chosen
A/B that is a clean local edit becomes an apply-ready prose delta (it joins Track 2);
option 3 routes it to Track 4.
-
Authoring → owner / explore — the conflict needs authoring, not a mechanical
fix: an empty section H / missing AC, a placeholder leak in a detailed+
document, a core section deferred past the floor. It is neither entailed nor a
direction pick — route it to the owning zone (unikit-gd-system / unikit-gd-flow /
unikit-gd-content / unikit-gd-spec), or to /unikit-gd-explore when it needs a
design decision first.
verify never writes the registry — it is read-only; the registry-wins rule still governs
the direction of a fix (apply / the owner sets the document to match GD-IDS), and a
deprecated ID is never deleted. Log every conflict + its track + resolution in the printed
report (Phase 5). The coherence-vs-presence split still holds underneath: a coherence
conflict (status / version / Depends 3-way, where GD-IDS is authoritative) is now
Track 2 (entailed → apply-ready — the header corrected to GD-IDS), and the
[gen]-map freshness is Track 1 (print-only); a presence conflict (empty H,
duplicate id, placeholder leak, unregistered cross-doc fact) is Track 2 (entailed →
apply-ready) or Track 4 (authoring), resolved by editing the document, never the registry.
Phase 5 — Report (printed to the console)
verify is read-only — the report is printed to the console, never written to a file.
A clean full PASS is a single chat line. On CONFLICTS FOUND or a changed-scope pass,
print the full report block:
# Verify: <scope> — <YYYY-MM-DD>
> Result: <PASS | CONFLICTS FOUND> · Scope: <SYS-slug | changed | all>
## Conflicts
| Severity | Document / Section | Check | Conflict (evidence) | Recommended fix |
|----------|--------------------|-------|---------------------|-----------------|
## Affected (changed-scope only)
| System / Flow | Relation | Verdict |
|---------------|----------|---------|
## Freshness
<[gen]-map rows stale → owner re-renders on next write (B1), or "up to date">
This is printed, not a file — a verify pass is ephemeral and cheap to re-run (the
single-session verify → apply pair is by design: a /clear between them is fixed by
re-running verify). The asymmetry with unikit-gd-review (which leaves a durable
two-bucket report) is intentional. When the same conflict recurs across systems, record it
as a gamedesign library rule via /unikit-memory --module gamedesign — durable domain
knowledge, not re-discovered each pass.
Phase 6 — Handoff (standalone only; inline prose, recommend-only)
LOOP-GUARD. This phase runs only on a standalone /unikit-gd-verify. Under the
in-apply flag (the apply-phase3 sentinel resolved in Phase 1), skip this phase
entirely — verify prints freshness (Track 1) and the report (Phase 5), but never offers
a handoff back to unikit-gd-apply. That is the silent gate that stops
apply → verify → apply from looping.
When Tracks 2 and 3 produced apply-ready deltas (the entailed fixes + the
direction-resolved edits), hand them to unikit-gd-apply in one move rather than
routing each conflict into an owner skill by hand. verify's handoff is inline prose from
this session — it writes no file (Phase 5): verify prints the deltas as plain
prose (each citing its target + zone), then ends with a bare /unikit-gd-apply.
A bare /unikit-gd-apply (no argument, no fence) reads the deltas from the last verify
output in the session — apply's no-arg session mode.
Hand off — recommend-only (never a Skill call). Neither unikit-gd-review nor
unikit-gd-verify carries Skill in allowed-tools; each prints the
/unikit-gd-apply recommendation for the user to run, and on Codex the auto-invoke nudge
runs it — the two handoff tails are identical by contract.
Auto-invoke the handoff — BLOCKING PRE-REQUISITE
When this phase prints the /unikit-gd-apply recommendation, automatically invoke it
($unikit-gd-apply, bare — it reads the printed deltas from this session) rather than
asking the user to run it by hand — the printed command is the recommend-only fallback for
agents without auto-invocation. This is a single handoff: invoke unikit-gd-apply once and
let it route the deltas to the owners (it closes with its own apply-phase3 verify — no
loop).
- apply-ready non-empty: print the deltas as plain prose (each citing
target +
zone), then the bare command on its own line —
/unikit-gd-apply
unikit-gd-apply reads the printed deltas from the session (no file, no argument —
verify writes none), dispatches them to the owning zones, and closes with its own
apply-phase3 verify pass. A single-zone set is bounced to its owner by apply's GATE 2.
- apply-ready empty (only freshness + authoring / research routes): no apply
handoff — list the Track-4 routes instead (
/unikit-gd-system / /unikit-gd-flow /
/unikit-gd-content / /unikit-gd-spec, or /unikit-gd-explore for the ones needing a
design decision).
Final: Compact Report
Follow the Handoff Tail contract (gd-critique → Handoff Engine): the runnable
command is the last block on screen — the report and any route note go above it,
nothing prints after it. Under the apply-phase3 sentinel the handoff is
suppressed, so there is no command tail (the loop-guard).
Print the compact report:
Scope: <SYS-slug | changed | all>
Result: <PASS | CONFLICTS FOUND (<n>)>
Checks: facts · terms · IDs · dup-IDs · dangling · unregistered-fact · roster↔disk · map-freshness · Depends-3way · status · version · AC-presence · placeholder · core-floor — <pass/fail each>
Flow checks (when flows exist): GOAL-ids · dangling-GOAL · flow-status/version · flow-Depends-3way · mode↔structure · win/lose↔terminal-GOAL · funnel-continuity · flow/funnel-freshness — <pass/fail each>
Content checks (when content_types exist): CT/CU-ids · CU⊆CT · ref<>-resolve · scale↔structure · belongs_to-3way · content-status/version · content-map-freshness · RES/TRACK/KNOB · cross-axis SYS→CT — <pass/fail each>
Affected (changed-scope): <k systems + flows + content types — Needs Review: …, Likely Stale: …>
Freshness: <## System Map [gen] stale — <rows> (· partial n/m where deferred); owner re-renders | up to date>
Tracks: <apply-ready: N · direction-interview: N · authoring/explore: N>
Handoff: <printed deltas + bare /unikit-gd-apply | apply-ready empty → routes only | in-apply (apply-phase3) → suppressed>
Report: <printed to console | none (clean PASS)>
The actionable routes when apply-ready is empty live in the Tracks line above —
read them there. No summary document beyond the conditional report file.
Then end with the handoff as the LAST block — one command, nothing after it:
- apply-ready non-empty → printed deltas (each citing
target + zone), then
🛠️ /unikit-gd-apply (bare — apply reads them from the session)
- apply-ready empty, a conflict needs a design call →
🔎 /unikit-gd-explore
- apply-ready empty, authoring only → the matching owner:
🔧 /unikit-gd-system
/ /unikit-gd-flow / /unikit-gd-content / /unikit-gd-spec
- clean PASS →
🔍 /unikit-gd-review <system> (fresh session)
- in-apply (
apply-phase3) → suppressed — no command tail.
Nothing prints after the command — no recap, no description of what the called
skill does next.
Ownership Boundaries
- Owns: nothing on disk —
unikit-gd-verify is fully read-only. It produces a
console conflict report + Affected table, and prints freshness / coherence
recommendations; every fix is performed by unikit-gd-apply (the apply-ready deltas) or
the owning zones (authoring + the [gen]-map re-render on their next write — B1: the
writer of GD-IDS re-renders the affected maps).
- Read-only: every design document,
GD-IDS.yaml, and GAME.md (incl. all
[gen]-map blocks) — verify writes none of them.
- Not this skill: quality judgment →
unikit-gd-review; applying the apply-ready set
in one pass → unikit-gd-apply (the handoff is recommend-only — verify prints the
command, never invokes it); authoring → unikit-gd-system (systems) / unikit-gd-flow
(flows) / unikit-gd-content (content types) / unikit-gd-spec (GAME.md + the roster);
a conflict needing a design decision → unikit-gd-explore.
- Never: write any file (no report file, no
Affected (gd-verify): line, no
doc_status bump, no [gen]-map re-render — verify carries no Write/Edit/mkdir);
use web research; guess where a grep settles it; change a GD-IDS value; delete or
renumber an ID; write the roster (route to unikit-gd-spec); carry Skill/Agent in
allowed-tools or invoke unikit-gd-apply itself (the handoff is a printed prose
recommendation read by apply's no-arg session mode); offer the handoff or run the
direction interview when invoked as apply's Phase 3 (the apply-phase3 loop-guard); read
the code workspace or project source.
Quick Reference
/unikit-gd-verify SYS-combat → check one system → triage conflicts (4 tracks) → handoff apply-ready to /unikit-gd-apply
/unikit-gd-verify → unverified diff → changed-scope impact; else full check
/unikit-gd-verify what did that change affect → changed-scope impact from git diff
/unikit-gd-verify all → full registry + roster + map-freshness + Depends pass
# internal: /unikit-gd-apply passes apply-phase3 as its Phase 3 — verify suppresses the handoff/interview (loop-guard)