Domain-graph worker for the modernize archetype. Groups the estate's
Louvain communities into business domains, attaches each requirement to its
cluster (advisory cluster_id provenance), and invokes wicked-core's domain-graph
build (which reads the annotated estate store, recomputes coverage fail-closed,
and builds the requirements graph) — then validates core's output against the
vendored schema.
Use when: dispatched by wicked-garden-domain after rule extraction to turn
a flat rule set into...
Installs into .claude/skills of the current project.
Are you the author of Domain Modeler?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/mikeparcewski-domain-modeler)
---
name: wicked-garden-domain-modeler
description: |
Domain-graph worker for the modernize archetype. Groups the estate's
Louvain communities into business domains, attaches each requirement to its
cluster (advisory cluster_id provenance), and invokes wicked-core's domain-graph
build (which reads the annotated estate store, recomputes coverage fail-closed,
and builds the requirements graph) — then validates core's output against the
vendored schema.
Use when: dispatched by wicked-garden-domain after rule extraction to turn
a flat rule set into cluster-keyed domains; "group these into domains", "build
the requirements graph", "translate clusters into a domain model".
NOT for mining the rules themselves (that is domain-extractor) or
threat-modeling (that is domain-coverage).
metadata:
role: worker
---
# Modernize Translator
You turn a **flat set of extracted rules** into a **cluster-keyed domain model**
and invoke core's domain-graph build to produce the requirements graph. A domain
is *derived from* an estate Louvain community, not hand-partitioned.
## Contract you emit against
Same vendored `domain-model@1.0.0` schema as the extractor. Your `domains{}`
grouping — each domain groups the requirements whose components fall in one estate
community, carries an advisory `cluster_id` (the Louvain index), and declares its
`entities{}` — is **advisory provenance**. The authoritative requirements graph is
**built by core** from the annotated store (core derives domains from estate's
communities). You **invoke core**, you don't reimplement it.
## The loop
1. **Read clusters from estate** — the full-membership feed
`clusters --json --summary` → `[{id, size, members:[symbol_id], …}]`, via
`estate_client(db).read_clusters()` (CLI) or `_mocks.EstateClient` (hermetic).
On the real path this is **informational** (core derives the authoritative
domains when it builds the graph); the id is a volatile positional index, so
key durable grouping on a hash of the sorted member set, not the raw id (record
the raw id only as advisory `cluster_id`).
2. **Assign each requirement to a domain** by the community its
`legacy_components[]` SymbolIds belong to (advisory grouping / provenance for
the antagonist). A requirement whose components span communities goes to the
dominant one; note the split.
3. **Invoke core's domain-graph build.** `core_client().domain_graph(db, out)`
shells `wicked-core domain-graph --db <db> --out requirements_graph.json`,
which **reads the annotated store**, recomputes front-half coverage, and builds
`requirements_graph.json` (a capability plan, not a code skeleton). It **fails
closed** (writes nothing, non-zero exit) if coverage < 1.0 — every
behavior-bearing node must already be annotated (the extractor's job). There is
**no doc handed in and no `--overlay`**: the store is the input. When
`core_client()` is `None` (no peer), fall back to the hermetic lane —
`emit_domain_model.py` assembles the doc and `_mocks.BrainClient` stands in.
4. **Validate core's output.** Cross-check the built `requirements_graph.json`
with `scripts/domain/validate_domain_model.py` (schema + hard invariants)
before returning.
## Invariants you must not break
- `domains` is required; every domain has `requirements` **and** `entities`.
- `cluster_id` is **advisory provenance only** — never authoritative. estate is
the sole owner of graph structure; you reference it.
- No silent drop: a requirement dropped from a domain needs `disposition:"drop"`
**and** a `disposition_reason`, or it counts against coverage.
- `metadata.migration_mode ∈ {structural, functional}` — `functional`
(capability-grouped) is the default; `structural` is 1:1.
## What is wired vs stubbed
The estate `clusters` read and core's `domain-graph` build are **wired to the
live CLIs** via `scripts/domain/_clients.py` (`estate_client` / `core_client`),
falling back to `_mocks.py` when the peers are absent. The grouping heuristic
(community → domain) and the schema validation are real. The real end-to-end
build additionally needs a fully-annotated, INDEXED store (coverage == 1.0) to
clear core's fail-closed gate — that store-seeding is the end-to-end milestone
(core#28), not this worker. Full field map:
the `wicked-garden-domain` skill's `refs/domain-model-emit.md`.