@behaghel/pi-domain-tree
Domain-driven structure with explicit groups, strict manifests, normative domain and system corpora, and specification-wiki integrity.
Package details
Install @behaghel/pi-domain-tree from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@behaghel/pi-domain-tree- Package
@behaghel/pi-domain-tree- Version
0.4.2- Published
- Nov 4, 2026
- Downloads
- 263/mo · 31/wk
- Author
- behaghel
- License
- MPL-2.0
- Types
- extension, skill
- Size
- 145.8 KB
- Dependencies
- 1 dependency · 1 peer
Pi manifest JSON
{
"skills": [
"./skills"
],
"extensions": [
"./pi/extension.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
domain-tree
Domain-driven codebase structure and modelling guidance — maintain the mirror between colocated specifications and code namespaces, and help consumers speak domain language without reconstructing provider or storage machinery.
Philosophy
The domain tree is a structural contract informed by Domain-Driven Design. Its manifest encodes three things:
- Where things live — the 1:1 namespace mirror between domain specs and code
- How much rigor each domain deserves — core vs supporting vs generic classification
- How domains communicate — the context map declaring integration patterns
domains.yaml at the project root is the source of truth for that contract. Domain markdown files live beside the code they govern; README.md is the required main domain spec, and sibling Markdown is normative only when it declares domain and status frontmatter.
Domains aren't limited to business logic. Security, UX, CI/CD, and infrastructure are domains too — anything with behavior worth specifying. A technical domain like security can be core if it's central to the product's value.
Domain Navigator also guides domain modelling and encapsulation: start from a consumer's intention, encapsulate meaning and resolution policy, and keep source and surface concerns at their boundaries. Eager and lazy resolution are implementation choices; neither is preferred. A valid tree or green check establishes structural integrity, not a good domain model. Semantic design remains the implementing actor's responsibility; there is no automated architecture score or additional approval stage.
DDD concepts used
| Concept | How we use it |
|---|---|
| Structural group | kind: group provides namespace and navigation without pretending to be a bounded context |
| Subdomain classification | type: core/supporting/generic on each domain — drives spec rigor and review requirements |
| Bounded context | Domains declare intended semantic boundaries; review the model and its language rather than inferring a coherent context from directory ownership |
| Context map | context-map: section declaring cross-domain relationships and integration patterns |
| Shared kernel | type: shared-kernel for a small jointly-owned model, including shared behaviour and invariants where appropriate — strictest change rules |
| Anti-corruption layer | Declared in the context map; Domain Navigator reviews translation and leakage at the boundary |
| Ubiquitous language | language: field in domains, with optional <domain code path>/README.md support — terms that code and specs must use consistently |
Ubiquitous language should be enriched on demand. Add new terms only when the domain needs sharper language, and get explicit developer approval before extending the glossary so the language stays intentional rather than drifting.
Relationship to other plugins
This plugin is the structural backbone for spec-driven development:
| Plugin | Role | What domain-tree provides |
|---|---|---|
spec-driven |
How to write specs | Where the spec lives, how much rigor it needs (core vs generic) |
spec-tdd |
How to execute specs | Which domain boundaries constrain iterations, which relationships to respect |
domain-tree |
Where things live | Namespace contract, classification, context map, enforcement |
Install any combination. They complement each other but don't depend on each other.
What's included
| Component | Type | Description |
|---|---|---|
| Domain Navigator | Skill | Resolves ownership, guides encapsulation and domain resolution, classification-aware spec-on-touch |
/domain-tree:init |
Command | Bootstrap tree with classification and context map from existing codebase |
/domain-tree:map |
Command | Coverage dashboard with domain types, wiki terms, and context map visualization |
/domain-tree:check |
Command | Structural, normative-wiki, context-map, and classification validation |
domain_tree_resolve_term |
Tool | Exact canonical-term and alias lookup with domain ownership |
| Boundary review | Skill guidance | Reviews semantic coupling, consumer knowledge, declared context contracts and shared meaning |
Pi package
The publishable package is @behaghel/pi-domain-tree. Its third-party YAML parser is a normal runtime dependency; Pi installs it automatically when the package comes from npm. Pi supplies typebox, so it remains a peer dependency.
After the first npm release, install it with:
pi install npm:@behaghel/pi-domain-tree
A local path is for plugin development and Pi intentionally leaves its dependency tree to the author. Prepare and test a checkout explicitly:
devenv -q shell -- npm ci --prefix marketplace/plugins/domain-tree
devenv -q shell -- npm test --prefix marketplace/plugins/domain-tree
pi -e ./marketplace/plugins/domain-tree
Do not commit node_modules or a generated extension bundle.
Structural groups
Use explicit groups when names such as business, foundation, or technical organize bounded contexts but do not own behavior themselves:
domains:
business:
kind: group
domains:
time-management:
type: core
code: [src/business/time-management/]
Groups may nest. They do not own code, require specifications, receive classifications, or participate in context-map relationships. Their names remain part of each child domain's fully qualified identity.
domains.yaml is validated strictly. Groups, domains, and context-map relationships reject unknown or malformed fields. Every real domain must provide a specification anchor through code or spec. Invalid manifests fail closed: ownership resolution, coverage maps, and spec checks remain unavailable until /domain-tree:check diagnostics are fixed. Legacy structural nodes are reported with manual migration guidance and are never rewritten automatically.
Domain classification
Every domain has a type that determines investment level:
| Type | What it means | Spec rigor |
|---|---|---|
| core | Competitive advantage — why the project exists | Full spec collection, thorough TDD, human-approved specs |
| supporting | Necessary, custom-built, not differentiating | Good specs, standard TDD, self-approval OK |
| generic | Solved problem — use libraries/standards | Spec the integration boundary only |
| shared-kernel | Shared types across domains — joint ownership | High rigor, contract tests from all consumers |
Context map
The context-map in domains.yaml declares how domains communicate:
context-map:
- provider: issuance
consumers: [wallet]
pattern: open-host-service
contract: services/issuance-gateway/README.md
- provider: verification
consumers: [wallet]
pattern: anti-corruption-layer
contract: services/verifier/presentation/README.md
Supported patterns: shared-kernel, customer-supplier, conformist, anti-corruption-layer, open-host-service, published-language, partnership, separate-ways.
Domain Navigator uses the context map to guide cross-domain changes. Context entries are reserved for semantic contracts, not ordinary imports; the provider owns the canonical contract and consumers follow the declared pattern. The source repository also retains an agents/boundary-enforcer.md review template; the Pi package does not register or ship a separate agent.
Quick start
New or existing project
/domain-tree:init
Scans your codebase, proposes domains with classifications, detects semantic cross-domain contracts, and scaffolds colocated README.md specs in each domain's code directory. Parent/child overlap follows subsidiarity: the most-specific matching child owns a file.
Day-to-day
- Before coding: Domain Navigator resolves ownership and governing requirements. For design changes, start with the consumer's domain intention and identify which meaning, policy and invariants the model should encapsulate.
- During coding: Keep provider translation, surface concerns and application coordination at their boundaries. Consult the context map for semantic cross-domain contracts and keep shared model changes compatible with their consumers.
- When work must cross domains: Deliver a small end-to-end slice across the necessary boundaries. Do not default to separate domain handoffs or leave integration until the end; keep genuinely unrelated work out of the slice.
- For complex multi-domain work: Start with a plan that the developer can challenge and approve before implementation begins.
- Periodic health check:
/domain-tree:checkvalidates structure, context map, and classification gaps./domain-tree:mapshows coverage weighted by domain importance.
Migrating from the legacy spec/ layout
The first /domain-tree:check on a legacy tree reports a migration section. The short version:
- Move
spec/domains.yamltodomains.yamlat the project root. - For each domain, move
spec/<domain>/index.mdto the domain's code directory asREADME.md. - Move other domain
*.mdspecs into that same code directory. - Remove old
spec:fields or update them to explicit colocated paths. - Delete the old
spec/tree once empty.
Specification corpora
Domain specifications are the default. They define durable behavior, ubiquitous language, boundaries, invariants, and semantic contracts at the narrowest capable owner.
Use system-specs only for stable, project-wide requirements that subsidiarity cannot assign to one domain. System specs use system/status frontmatter and RFC 2119 requirements. Temporary iteration specs remain non-normative and outside both corpora.
Apply specification DRY: define each concept, invariant, or contract once, then link to its canonical owner and describe only local consequences. Normative specs reject roadmaps, rollout or migration plans, progress, delivery metadata, verification plans, legacy comparisons, and other circumstantial concerns.
The normative corpus forms a portable Markdown wiki. Canonical term pages declare term and optional aliases; each case-insensitive label has exactly one repository-wide owner. Relative links must resolve, Markdown fragments must identify real heading anchors, and links labelled with a canonical term or alias must target its owning page. Link meaningful first occurrences rather than every repetition. Use domain_tree_resolve_term for exact canonical or alias lookup.
The spec-on-touch convention
The first time you modify a domain, write its spec.
Rigor scales with classification:
- Core domains: spec required before any code change
- Shared kernel: spec required, all consumers notified
- Supporting domains: warning when missing
- Generic domains: only when the integration boundary changes
The Pi spec-on-touch guard permits write and edit of .md files directly in the owning domain's specification directory, including a missing or invalid README.md. This allows specification creation and repair without disabling enforcement. The directory is the explicit spec: override or, otherwise, the first code: path. Markdown elsewhere and non-Markdown files in that directory are not exempt. Core and shared-kernel code changes remain blocked until the domain's README.md passes normative validation; repairing it unlocks code changes in the same session.
License
domain-tree is free software licensed under the Mozilla Public License 2.0.