feat(governance): port optional module and context workflows

Integrate contributor PRs #3533, #3534, and #3535 on current main. Preserve the read-only module audit, canonical glossary and ADR ownership, and opt-in install closure. Refresh only the manual trigger manifest registry binding and coverage after adding the skill.
This commit is contained in:
Seekers2001 authored and haelyra committed 2026-10-25 17:51:30 -04:00
1 parent e3292b0010
commit 7b1449ef53
52 files changed
+1352 -64

No files matched your search

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Matt Pocock
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+24 -4
View File
@@ -10,7 +10,7 @@ Govern “what these business terms mean” separately from “why this option w
## Domain Context Artifact: Domain Language Only
When terms require repeated explanation, one term has multiple meanings, or unclear language causes module-boundary errors, first reuse the repository's existing glossary/domain-model document or the `context` role in `.governance/docs-map.json`. Only when no equivalent exists should you lazily create the default `CONTEXT.md` from `templates/context.example.md`.
When terms require repeated explanation, one term has multiple meanings, or unclear language causes module-boundary errors, first reuse the repository's existing glossary/domain-model document or the `context` role in `.governance/docs-map.json`. If an existing context map identifies separate business contexts, follow its links to the relevant glossary; ask when the topic's owner is unclear. Different contexts may legitimately use the same word differently. Do not create a glossary for every directory or impose one meaning across unrelated contexts. Only when no equivalent exists and a term has been resolved should you propose the default `CONTEXT.md` from `templates/context.example.md`. Obtain the user's explicit approval to create that new artifact before writing it, and stay within the authorized documentation scope. General permission to update documentation does not itself approve a new artifact; editing an existing mapped glossary within the authorized scope does not require a new-artifact approval.
Record only:
@@ -21,11 +21,23 @@ Record only:
Do not include implementation details, current state, task schedules, full requirements, decision rationale, or history. Keep those in code/MAP, STATUS/Issue Tracker, Spec, ADR, and LOG respectively. Terms must be traceable to code, contracts, or business evidence. Mark unconfirmed items as `needs confirmation`; do not guess.
## Refine Language During the Conversation
This is an active modeling workflow, not a requirement to rewrite the glossary whenever another skill reads it.
- **Challenge contradictions.** Compare the user's wording with the glossary. If “cancellation” already means cancelling an entire order but the request permits cancelling one line, explain that distinction and ask which meaning is intended; do not silently redefine the term.
- **Separate overloaded concepts.** For “account,” establish whether the speaker means the customer organization, a person's login, or a billing relationship. Propose a precise term for each distinct concept. Record rejected synonyms only where they really denote the same concept in this context.
- **Probe relationships with concrete scenarios.** Ask, for example, whether one customer can have multiple logins, or what happens to an invoice when only part of an order is cancelled. Scenarios expose boundaries; they are questions, not invented business rules.
- **Check claims against evidence.** Inspect relevant code, tests, contracts, and existing records. If the implementation cancels only whole orders, surface the mismatch with the proposed partial-cancellation rule. Code demonstrates current behavior, not the business owner's desired behavior; do not decide which is correct by yourself.
- **Capture confirmed terms as they settle.** Within an authorized documentation edit, update the existing glossary at that point instead of deferring all definitions until closeout. Keep definitions to one or two sentences, with a source and context-specific avoided synonyms where useful. Do not rewrite code, contracts, or accepted decisions merely to match a proposed term. In discussion-only or read-only work, present the proposed wording without writing files.
Use the project's existing glossary format. For a new document, the [context template](templates/context.example.md) provides a compact starting point; omit sections without real content. Include business-specific concepts, not generic programming vocabulary such as timeouts or utility functions.
## ADR Handoff: Reuse the Existing Capability
This skill does not define a second ADR directory, numbering scheme, lifecycle, or template. When a decision is hard to reverse, would confuse a future reader without context, and involves a real tradeoff:
1. Read and execute `architecture-decision-records`, preserving the repository's existing ADR location and format.
1. Read and execute `architecture-decision-records`, preserving the repository's existing ADR location, format, and explicit approval gates for initialization and writing.
2. If no convention exists, let that skill choose the minimum viable location; do not create a separate system here.
3. Link to the ADR from `CONTEXT.md`, the project map, or the relevant Spec/Issue instead of copying the decision body.
4. When a decision is accepted, superseded, or deprecated, append an event to project history according to the living-document rules.
@@ -38,14 +50,16 @@ Routine implementation details, easily reversible changes, and short-lived exper
- The project needs a stable domain-language entry point but has no equivalent document.
- Context work uncovers a durable decision that should be handed to `architecture-decision-records`.
Merely consuming established vocabulary during an ordinary bug fix does not require a modeling session or new documentation.
## When to Use
Use this skill when stable domain language needs one evidence-backed home, or when context work reveals a hard-to-reverse decision that belongs in the repository's existing ADR system.
## How It Works
1. Reuse the mapped context or glossary artifact when one exists; create `CONTEXT.md` only when no equivalent source of truth is available.
2. Record precise domain definitions, relationships, confirmed interpretations, and explicitly unconfirmed ambiguities without mixing in implementation or task state.
1. Reuse the mapped context or glossary artifact when one exists; create `CONTEXT.md` only when no equivalent source of truth is available and the user has explicitly approved creating that new artifact.
2. Challenge conflicting language, probe concrete edge cases, and cross-check claims against code and contracts before recording confirmed definitions and relationships. Keep unresolved interpretations explicit without mixing in implementation or task state.
3. Route durable tradeoffs to `architecture-decision-records` and link the resulting ADR instead of copying its rationale into context documents.
4. Keep schedules, blockers, and execution status in the existing issue tracker.
@@ -60,6 +74,8 @@ Use this skill when stable domain language needs one evidence-backed home, or wh
- Copying current tasks, schedules, full requirements, or implementation details into `CONTEXT.md`.
- Creating empty context files or a second ADR system for completeness.
- Treating unconfirmed domain interpretations as facts, or copying ADR bodies into maps and logs.
- Renaming distinct business concepts as synonyms, or forcing separate contexts into one universal glossary.
- Treating a glossary update as permission to change implementation, or treating code as proof of the intended business rule.
## Related Skills
@@ -70,3 +86,7 @@ Use this skill when stable domain language needs one evidence-backed home, or wh
## Scheduling and Task Boundary
Keep tasks, status, blockers, and schedules in the project's existing tracker, such as GitHub Issues or Linear, as the single source of truth. Use local `.scratch/` only when no external tracker exists and the project explicitly adopts it. Do not put schedules in `CONTEXT.md`, ADRs, PROJECT_STATUS, or the history index database.
## Attribution
The active terminology-refinement workflow adapts Matt Pocock's MIT-licensed `domain-modeling` skill. See [source and adaptation boundaries](references/source.md). ECC's existing ADR workflow remains the sole decision-record owner.
@@ -0,0 +1,5 @@
# Source and adaptation
The active terminology-refinement workflow and compact glossary guidance are adapted from [Matt Pocock's domain-modeling skill](https://github.com/mattpocock/skills/tree/c55ee46073ed923f86ce59a5eb3b6d895095d1b7/skills/engineering/domain-modeling), pinned at `c55ee46073ed923f86ce59a5eb3b6d895095d1b7`, under the [MIT license](../LICENSE).
This is an in-place ECC adaptation, not another skill or an external runtime dependency. It preserves ECC's mapped glossary selection, evidence and authorization boundaries, and handoff to `architecture-decision-records`. It does not import a second ADR template, directory, numbering scheme, or lifecycle. The existing ECC context template is extended rather than replaced with a second glossary format.
@@ -1,12 +1,14 @@
# CONTEXT.md — Domain Language
> Create only when needed. Do not include implementation, status, scheduling, or decisions.
> Create only after the first term is resolved, no equivalent glossary exists, and the user has explicitly approved creating this new artifact within the authorized documentation scope. General documentation-update permission alone is not creation approval. Reuse an existing glossary and its format instead of creating a duplicate; authorized edits to that existing glossary do not need a new-artifact approval. Omit sections without content. Do not include implementation, status, scheduling, or decision rationale.
{One or two sentences identifying the business context this vocabulary describes.}
## Core Terms
| Term | Precise definition | Evidence/source |
|---|---|---|
| `{term}` | `{what it means and how it differs from adjacent terms}` | `{business rule, contract, or code location}` |
| Term | Precise definition | Avoid in this context | Evidence/source |
|---|---|---|---|
| `{canonical term}` | `{one or two sentences defining the concept and distinguishing adjacent concepts}` | `{confirmed synonyms to avoid; not names for distinct concepts}` | `{confirmed business source, contract, or code location}` |
## Concept Relationships
+24 -1
View File
@@ -51,7 +51,30 @@ verification step. Resolve the selected skill directory first, then invoke:
python3 <docs-governance-skill>/scripts/audit-docs.py --root <repository> --scope full
```
Available scopes are `spine`, `context`, `adr`, `artifacts`, and `full`.
Available scopes are `spine`, `context`, `adr`, `artifacts`, `modules`, and `full`.
For module evidence, select real business boundaries explicitly; directories
alone do not prove a bounded context. Repeat `--module` for several boundaries:
```bash
python3 <docs-governance-skill>/scripts/audit-docs.py --root <repository> --scope modules --module src/inventory --module src/billing
```
`modules` requires a selected directory. `full` also accepts `--module`; other
scopes reject it rather than silently skipping the requested checks. The module
check finds local `AGENTS.md` and `CONTEXT.md`, or reports the nearest ancestor
and mapped project sources as shared candidates. Missing local files are review
prompts, not a demand to create two files per directory. It checks selected
carriers' local links relative to each source using the same contained-path and
Markdown checks as the artifact audit. It never infers harness loading from
file presence.
After mechanical checks, inspect the actual module code, callers, contracts and
tests: compare responsibilities and import/call boundaries with documented
rules, and terminology with the canonical context source. Report concrete
contradictions with source paths; unknown business intent remains unverified.
Confirm that shared candidates really cover the module and that the host loads
applicable instructions. Reuse existing contract and ADR owners rather than
copying them into `AGENTS.md`. Passing links is not passing this semantic review.
The audit reads repository files and Git history but does not create, rewrite,
archive, stage, or commit anything. It reports:
@@ -66,6 +66,14 @@ The audit does not determine whether prose is true, whether a requirement is
satisfied, whether a task is complete, or whether a change should be merged.
Warnings explicitly require semantic review.
Module auditing is opt-in through explicit `--module` directories with scope
`modules` or `full`. Local `AGENTS.md` and `CONTEXT.md` are conventional evidence
locations, not mandatory per-directory artifacts. Ancestor and mapped sources
are shared candidates whose applicability must be confirmed manually; the audit
does not equate a file's existence with instruction loading or business coverage.
Module responsibilities, code/contract boundaries and vocabulary require source
and test evidence after the contained-path and source-relative link checks.
For link checks, any `scheme://` destination is external. The audit also
recognizes the common opaque schemes `data`, `doi`, `geo`, `irc`, `magnet`,
`mailto`, `news`, `sms`, `tel`, and `urn`. Other colon-shaped destinations are
@@ -16,6 +16,7 @@ from markdown_links import (
normalize_link_target,
without_fenced_code,
)
from module_audit import check_modules
DEFAULT_ROLES = {
"constitution": "CLAUDE.md",
@@ -242,10 +243,12 @@ def resolve_target(root: Path, source: Path, target: str) -> Path | None:
return resolve_within_root(root, source.parent, target)
def check_markdown_links(root: Path, files: list[Path], report: Report) -> None:
def check_markdown_links(
root: Path, files: list[Path], report: Report, *, skip_templates: bool = True
) -> None:
broken: list[str] = []
for source in files:
if "templates" in source.relative_to(root).parts:
if skip_templates and "templates" in source.relative_to(root).parts:
continue
text = source.read_text(encoding="utf-8")
for raw in markdown_link_targets(text):
@@ -716,11 +719,19 @@ def main() -> int:
parser.add_argument("--root", type=Path, default=Path.cwd())
parser.add_argument(
"--scope",
choices=("spine", "context", "adr", "artifacts", "full"),
choices=("spine", "context", "adr", "artifacts", "modules", "full"),
default="full",
)
parser.add_argument(
"--module", action="append", default=[],
help="Repository-relative module directory; repeat for several modules",
)
parser.add_argument("--log-threshold", type=int, default=200)
args = parser.parse_args()
if args.scope == "modules" and not args.module:
parser.error("--scope modules requires at least one --module directory")
if args.module and args.scope not in ("modules", "full"):
parser.error("--module is supported only with --scope modules or full")
root = args.root.resolve()
if not root.is_dir():
@@ -728,6 +739,11 @@ def main() -> int:
return 2
report = Report()
roles = load_roles(root, report)
if args.module:
check_modules(
root, roles, args.module, report,
resolved_within_root, read_utf8, check_markdown_links,
)
if args.scope in ("spine", "full"):
check_spine(root, roles, report, args.log_threshold)
if args.scope in ("context", "full"):
@@ -0,0 +1,89 @@
"""Read-only evidence discovery for explicitly selected business modules."""
from collections.abc import Callable
from pathlib import Path
from typing import Protocol, TypeVar
class AuditReport(Protocol):
def section(self, title: str) -> None: ...
def ok(self, message: str) -> None: ...
def warn(self, message: str) -> None: ...
def fail(self, message: str) -> None: ...
ReportT = TypeVar("ReportT", bound=AuditReport, contravariant=True)
class LinkChecker(Protocol[ReportT]):
def __call__(
self, root: Path, files: list[Path], report: ReportT,
*, skip_templates: bool = True,
) -> None: ...
def carrier_candidates(root: Path, module: Path, name: str, mapped: str) -> list[Path]:
directories = [module, *module.parents]
directories = [root] if module == root else directories[:directories.index(root) + 1]
candidates = [directory / name for directory in directories] + [root / mapped]
return list(dict.fromkeys(candidates))
def check_modules(
root: Path,
roles: dict[str, str],
names: list[str],
report: ReportT,
resolve_source: Callable[[Path, Path], Path | None],
read_source: Callable[[Path, ReportT, str], str | None],
check_links: LinkChecker[ReportT],
) -> None:
"""Use the audit's existing containment, UTF-8 and Markdown checks unchanged."""
report.section("modules")
sources: set[Path] = set()
for name in sorted(set(names)):
requested = Path(name)
module = resolve_source(root, root / requested)
if requested.is_absolute() or module is None or not module.is_dir():
report.fail(
f"Module must be a repository-relative directory inside root: {name!r}"
)
continue
report.ok(f"Selected module: {str(module.relative_to(root))!r}")
for role, filename in (("agents", "AGENTS.md"), ("context", "CONTEXT.md")):
found = False
for candidate in carrier_candidates(root, module, filename, roles[role]):
if not candidate.exists() and not candidate.is_symlink():
continue
resolved = resolve_source(root, candidate)
if resolved is None or not resolved.is_file():
report.fail(
f"Module {role} source must be a repository-contained regular file: "
f"{str(candidate.relative_to(root))!r}"
)
found = True
break
if read_source(resolved, report, f"module {role} source") is None:
found = True
break
sources.add(resolved)
source_label = str(candidate.relative_to(root))
if candidate.parent == module:
report.ok(f"Local {role} source: {source_label!r}")
else:
report.warn(
f"Shared {role} candidate: {source_label!r}; "
f"confirm it covers module {str(module.relative_to(root))!r}"
)
found = True
break
if not found:
report.warn(
f"No {role} source found for module {str(module.relative_to(root))!r}; "
"review existing rules or glossary before creating a file"
)
check_links(root, sorted(sources), report, skip_templates=False)
report.warn(
"Module responsibilities, instruction loading, terminology and code/contract "
"consistency require semantic review; file presence is not proof of coverage"
)
+130
View File
@@ -0,0 +1,130 @@
---
name: module-governance
description: >-
Establish or maintain a business module's local agent instructions, domain-context links, and interface ownership when implementing a new module or changing its responsibilities. Reuse project-level rules and existing ECC workflows; ordinary bug fixes, styling, and read-only design discussions do not start module documentation setup.
metadata:
origin: ECC
---
# Module Governance
Connect module-level working rules to existing domain language, contracts, and
decisions. This is an orchestration skill, not a second DDD methodology or an
automatic documentation generator.
## When to Activate
- The user asks to implement a new business module, without needing to name this skill.
- The user asks to document an existing module or maintain a confirmed responsibility change.
- An authorized module change needs local instructions and canonical knowledge pointers.
Do not start setup for an ordinary bug, styling change, internal refactor with
unchanged boundaries, or a new directory/UI component that simply inherits
parent conventions. For design discussion or an audit alone, inspect and report
without creating files. Intent-based selection is not a background hook and
does not grant permission for unrelated code, commits, pushes, or business choices.
## How It Works
### 1. Discover the boundary before creating documents
Read applicable project and ancestor harness instructions, the existing map,
requirements, relevant code, and contracts. Identify the module's responsibilities,
non-responsibilities, state ownership, and actual consumers. Directory names alone
do not establish a bounded context. Surface material ambiguities instead of
inventing business rules.
For an existing boundary change, use `change-impact` first. Its read-only and
authorization constraints still apply; do not run an unrelated development workflow.
### 2. Separate project-wide rules from local rules
The project-level instruction source owns global constraints, module navigation,
and cross-module collaboration. A local `AGENTS.md` owns only additional module
constraints: responsibilities, state ownership, public-interface pointers, and
verification entry points. Local rules cannot waive project-wide constraints.
Reuse an established `CLAUDE.md` or other active harness surface instead of
forcing a rename or maintaining two copies. If parent instructions already
cover the module and no local rule is missing, link that source and do not create
a redundant local file. Follow an existing explicit local-entrypoint requirement.
Do not claim that a harness automatically loads every nested instruction file.
### 3. Reuse the existing knowledge owners
| Information | Existing ECC owner |
|---|---|
| Stable terminology and concept relationships | `context-and-decisions`, using the existing glossary or context role |
| Hard-to-reverse decisions and rationale | `architecture-decision-records`, through the existing context handoff |
| Cross-boundary machine-checkable interfaces | `contract-first` |
| Documentation navigation and lifecycle | `living-docs-governance` |
| Affected consumers and boundary-change evidence | `change-impact` |
Load the applicable skill when its work is needed; do not duplicate its method
here. Keep terminology in the relevant existing `CONTEXT.md` or equivalent,
not in AGENTS. Frontend and backend in the same business context can share that
glossary; UI-specific working rules may be local without creating a glossary per
page. Reuse existing contracts and ADRs by reference, not by copying their bodies.
### 4. Maintain only the authorized records
Within the user's requested module work, update the smallest missing local rules
and confirmed context entries. Use `living-docs-governance` for affected navigation
and lifecycle updates, retaining its approval requirement for new top-level
artifacts. Do not create empty files to meet a two-file quota. When implementation
does not yet exist, label the boundary as confirmed design with code unverified;
do not declare documentation/code consistency from prose alone.
### 5. Verify both meaning and links
Compare each claimed owner, interface, and constraint with code, canonical
contracts, and executable evidence. Report contradictions for owner resolution;
do not rewrite the rules merely to match an accidental implementation.
Resolve relative links from **each referencing document's directory**, not the
terminal working directory. Resolve the installed `docs-governance` skill path
and run its existing read-only link check as an agreed verification step:
```bash
python3 <docs-governance-skill>/scripts/audit-docs.py --root <repository> --scope artifacts
```
Confirm that the audit scanned the changed local instructions, context, and
navigation; check its link results and exit code. Fix broken references and rerun,
without creating empty targets. If the script is unavailable or excludes a target,
report that target as unverified. A passing business test does not prove links are
correct, and an existing same-named file may still be the wrong knowledge owner.
## Examples
- **“Implement an inventory module.”** Inspect existing boundaries; add local
rules only for genuinely new constraints; reuse the domain glossary and API
contract; link them from existing navigation; verify code and source-relative links.
- **“Should payments become a separate module?”** Explain boundary options and
evidence without writing files or treating the discussion as implementation approval.
- **“Fix the inventory badge color.”** Read applicable instructions and fix the
requested UI behavior; do not generate AGENTS, CONTEXT, or ADR documents.
- **“Audit module documentation.”** Use the existing read-only audit and semantic
review; a missing local file is not by itself permission to create one.
## Anti-Patterns
- A mandatory AGENTS/CONTEXT pair for every directory or frontend page.
- Duplicating global constraints, glossary definitions, contracts, or ADR rationale.
- Treating documentation checks as proof of completed business implementation.
- Installing hooks or asserting automatic nested-file loading without real harness wiring.
## Delivery
Report canonical record paths, their project/module scope, actual changes,
verification evidence, and unresolved ownership questions. Module governance
delivers working rules and knowledge entry points, not a complete DDD conversion.
## Related Skills
- `context-and-decisions`
- `architecture-decision-records`
- `contract-first`
- `change-impact`
- `living-docs-governance`
- `docs-governance`
@@ -0,0 +1,6 @@
interface:
display_name: "Module Governance"
short_description: "Connect module rules, context, and contracts"
default_prompt: "Use $module-governance to establish or maintain module documentation using existing project knowledge owners."
policy:
allow_implicit_invocation: true
+1 -1
View File
@@ -11,7 +11,7 @@
{
"name": "ecc",
"source": "./",
"description": "Harness-native ECC operator layer - 71 agents, 311 skills, 97 legacy command shims, reusable hooks, rules, selective install profiles, and production-ready workflows for Claude Code, Codex, OpenCode, Cursor, and related agent harnesses",
"description": "Harness-native ECC operator layer - 71 agents, 312 skills, 97 legacy command shims, reusable hooks, rules, selective install profiles, and production-ready workflows for Claude Code, Codex, OpenCode, Cursor, and related agent harnesses",
"version": "2.2.3",
"author": {
"name": "Affaan Mustafa",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "ecc",
"version": "2.2.3",
"description": "Harness-native ECC plugin for engineering teams - 71 agents, 311 skills, 97 legacy command shims, reusable hooks, rules, MCP conventions, and operator workflows for Claude Code plus adjacent agent harnesses",
"description": "Harness-native ECC plugin for engineering teams - 71 agents, 312 skills, 97 legacy command shims, reusable hooks, rules, MCP conventions, and operator workflows for Claude Code plus adjacent agent harnesses",
"author": {
"name": "Affaan Mustafa",
"url": "https://x.com/affaanmustafa"
+1 -1
View File
@@ -4,7 +4,7 @@ This file provides Gemini CLI with the baseline ECC workflow, review standards,
## Overview
ECC is a cross-harness coding system with 71 specialized agents, 311 skills, and 97 commands.
ECC is a cross-harness coding system with 71 specialized agents, 312 skills, and 97 commands.
Gemini support is currently focused on a strong project-local instruction layer via `.gemini/GEMINI.md`, plus the shared MCP catalog and package-manager setup assets shipped by the installer.
+2 -2
View File
@@ -1,6 +1,6 @@
# ECC — Agent Instructions
This is a **production-ready AI coding plugin** providing 71 specialized agents, 311 skills, 97 commands, and automated hook workflows for software development.
This is a **production-ready AI coding plugin** providing 71 specialized agents, 312 skills, 97 commands, and automated hook workflows for software development.
**Version:** 2.2.3
@@ -202,7 +202,7 @@ Troubleshoot failures: check test isolation → verify mocks → fix implementat
```
agents/ — 71 specialized subagents
skills/ — 311 workflow skills and domain knowledge
skills/ — 312 workflow skills and domain knowledge
commands/ — 97 slash commands
hooks/ — Trigger-based automations
rules/ — Always-follow guidelines (common + per-language)
+3 -3
View File
@@ -141,12 +141,12 @@ Instead of rebuilding that process in every prompt, you install it once and make
ECC is MIT-licensed open source. It works best with Claude Code today, has native Codex and Qoder plugin paths, and provides capability-limited adapters for Cursor, OpenCode, Gemini, Zed, GitHub Copilot, Antigravity, Qwen, and other harnesses. See the [support status matrix](#platform-support) before assuming feature parity.
Access to 71 agents, 311 skills, and 97 legacy command shims, plus hooks, rules, memory, continuous learning, and AgentShield security scanning. The agents are specialized for planning, review, build repair, security, architecture, and domain work.
Access to 71 agents, 312 skills, and 97 legacy command shims, plus hooks, rules, memory, continuous learning, and AgentShield security scanning. The agents are specialized for planning, review, build repair, security, architecture, and domain work.
| Included | Count | What it gives you |
| ---------------- | ----------: | ------------------------------------------------------------------------------------ |
| Agents | 71 agents | Planning, review, build repair, security, architecture, and domain work |
| Skills | 311 skills | TDD, research, security, docs, frontend, data, ML, operations, and more |
| Skills | 312 skills | TDD, research, security, docs, frontend, data, ML, operations, and more |
| Commands | 97 commands | Convenient entry points while ECC moves to a skills-first surface |
| Hooks and memory | Runtime | Enforcement, session summaries, continuous learning, instincts, and context controls |
| Rules | Selective | Always-loaded standards you choose by language or project |
@@ -859,7 +859,7 @@ Stable graduation of the 2.0 line: control-pane substrate, worktree lifecycle se
```text
ECC/
|-- agents/ # 71 specialized subagents for delegation
|-- skills/ # 311 reusable workflows loaded on demand
|-- skills/ # 312 reusable workflows loaded on demand
|-- commands/ # 97 maintained slash-command shims
|-- rules/ # opt-in common and language standards
|-- hooks/ # runtime automation and enforcement
+1 -1
View File
@@ -200,7 +200,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/"
/plugin list ecc@ecc
```
**完成!** 你现在可以使用 71 个代理、311 个技能和 97 个命令。
**完成!** 你现在可以使用 71 个代理、312 个技能和 97 个命令。
### multi-* 命令需要额外配置
+1 -1
View File
@@ -1,7 +1,7 @@
# Soul
## Core Identity
ECC is a production-ready AI coding plugin with 71 specialized agents, 311 skills, 97 commands, and automated hook workflows for software development.
ECC is a production-ready AI coding plugin with 71 specialized agents, 312 skills, 97 commands, and automated hook workflows for software development.
## Core Principles
1. **Agent-First** — route work to the right specialist as early as possible.
+1
View File
@@ -107,6 +107,7 @@ skills:
- market-research
- mcp-dependency-review
- mcp-server-patterns
- module-governance
- module-regression
- mybatis-patterns
- motion-advanced
+2 -2
View File
@@ -1,6 +1,6 @@
# ECC — Agent Talimatları
Bu, yazılım geliştirme için 71 özel agent, 311 skill, 97 command ve otomatik hook iş akışları sağlayan **üretime hazır bir AI kodlama eklentisidir**.
Bu, yazılım geliştirme için 71 özel agent, 312 skill, 97 command ve otomatik hook iş akışları sağlayan **üretime hazır bir AI kodlama eklentisidir**.
**Sürüm:** 2.2.3
@@ -142,7 +142,7 @@ Başarısızlık sorunlarını giderin: test izolasyonunu kontrol edin → mockl
```
agents/ — 71 özel subagent
skills/ — 311 iş akışı skillleri ve alan bilgisi
skills/ — 312 iş akışı skillleri ve alan bilgisi
commands/ — 97 slash command
hooks/ — Tetikleyici tabanlı otomasyonlar
rules/ — Her zaman uyulması gereken kurallar (ortak + dile özel)
+2 -2
View File
@@ -1,6 +1,6 @@
# ECC — 智能体指令
这是一个**生产就绪的 AI 编码插件**,提供 71 个专业代理、311 项技能、97 条命令以及自动化钩子工作流,用于软件开发。
这是一个**生产就绪的 AI 编码插件**,提供 71 个专业代理、312 项技能、97 条命令以及自动化钩子工作流,用于软件开发。
**版本:** 2.2.3
@@ -147,7 +147,7 @@
```
agents/ — 71 个专业子代理
skills/ — 311 个工作流技能和领域知识
skills/ — 312 个工作流技能和领域知识
commands/ — 97 个斜杠命令
hooks/ — 基于触发的自动化
rules/ — 始终遵循的指导方针(通用 + 每种语言)
+3 -3
View File
@@ -264,7 +264,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/"
/plugin list ecc@ecc
```
**搞定!** 你现在可以使用 71 个智能体、311 项技能和 97 个命令了。
**搞定!** 你现在可以使用 71 个智能体、312 项技能和 97 个命令了。
***
@@ -1178,7 +1178,7 @@ opencode
|---------|---------------|----------|--------|
| 智能体 | PASS: 71 个 | PASS: 12 个 | **Claude Code 领先** |
| 命令 | PASS: 97 个 | PASS: 35 个 | **Claude Code 领先** |
| 技能 | PASS: 311 项 | PASS: 37 项 | **Claude Code 领先** |
| 技能 | PASS: 312 项 | PASS: 37 项 | **Claude Code 领先** |
| 钩子 | PASS: 8 种事件类型 | PASS: 11 种事件 | **OpenCode 更多!** |
| 规则 | PASS: 29 条 | PASS: 13 条指令 | **Claude Code 领先** |
| MCP 服务器 | PASS: 14 个 | PASS: 完整 | **完全对等** |
@@ -1286,7 +1286,7 @@ ECC 是**第一个最大化利用每个主要 AI 编码工具的插件**。以
|---------|-----------------------|------------|-----------|----------|
| **智能体** | 71 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 |
| **命令** | 97 | 共享 | 基于指令 | 35 |
| **技能** | 311 | 共享 | 10 (原生格式) | 37 |
| **技能** | 312 | 共享 | 10 (原生格式) | 37 |
| **钩子事件** | 8 种类型 | 15 种类型 | SessionStart(1 种类型) | 11 种类型 |
| **钩子脚本** | 20+ 个脚本 | 16 个脚本 (DRY 适配器) | 1 个 SessionStart 引导脚本 | 插件钩子 |
| **规则** | 34 (通用 + 语言) | 34 (YAML 前页) | 基于指令 | 13 条指令 |
@@ -1,6 +1,6 @@
{
"coverage": {
"skills": 311,
"skills": 312,
"withTriggers": 33
},
"generatedAt": "2026-09-24T23:51:22.784Z",
@@ -10,7 +10,7 @@
"id": "hand-seeded",
"source": "manual-curation-pending-regeneration"
},
"registryDigest": "846ac8826dd91e81cd838c2b55d7be536af427eb453efa127501f891e5778ff8",
"registryDigest": "963baacb17709ebf0df08643b75e0a76ce0882978131564d72f0d08995f93c02",
"schemaVersion": 1,
"triggers": {
"skill:agent-runtime-gateway-smoke-test": [
+8
View File
@@ -734,6 +734,14 @@
"documentation-change-impact"
]
},
{
"id": "capability:module-governance",
"family": "capability",
"description": "Optional module-local rules, domain context, and contract ownership workflow.",
"modules": [
"documentation-module-governance"
]
},
{
"id": "capability:documentation-test-regression",
"family": "capability",
+29
View File
@@ -1389,6 +1389,35 @@
"cost": "light",
"stability": "beta"
},
{
"id": "documentation-module-governance",
"kind": "skills",
"description": "Optional module-local instructions and documentation ownership workflow.",
"paths": [
"skills/module-governance"
],
"targets": [
"claude",
"claude-project",
"cursor",
"antigravity",
"codex",
"opencode",
"codebuddy",
"joycode",
"qwen",
"zed"
],
"dependencies": [
"documentation-context",
"documentation-governance",
"documentation-change-impact",
"framework-language"
],
"defaultInstall": false,
"cost": "light",
"stability": "beta"
},
{
"id": "documentation-test-regression",
"kind": "skills",
+1
View File
@@ -105,6 +105,7 @@
"documentation-context",
"hermes-hooks",
"documentation-change-impact",
"documentation-module-governance",
"documentation-test-regression"
]
}
+1
View File
@@ -114,6 +114,7 @@
"living-docs-governance",
"make-interfaces-feel-better",
"mcp-server-patterns",
"module-governance",
"module-regression",
"ml-adoption-playbook",
"mle-workflow",
+1
View File
@@ -449,6 +449,7 @@
"skills/marketing-campaign/",
"skills/master-agreement-generator/",
"skills/ml-adoption-playbook/",
"skills/module-governance/",
"skills/module-regression/",
"skills/motion-advanced/",
"skills/motion-foundations/",
+1 -1
View File
@@ -1,6 +1,6 @@
# Curation
pi/core includes 132 of 311 skills and 24 of 97 commands from the root of ECC.
pi/core includes 133 of 312 skills and 24 of 97 commands from the root of ECC.
Everything excluded is listed here with its reason.
## Rules
+1 -1
View File
@@ -1,6 +1,6 @@
# ecc-pi-core
A curated, Pi-native profile of ECC: 132 portable
A curated, Pi-native profile of ECC: 133 portable
engineering skills and 24 pure prompt-workflow commands, with no extensions,
no hooks, no runtime downloads, and no network or SaaS dependencies.
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Matt Pocock
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+24 -4
View File
@@ -10,7 +10,7 @@ Govern “what these business terms mean” separately from “why this option w
## Domain Context Artifact: Domain Language Only
When terms require repeated explanation, one term has multiple meanings, or unclear language causes module-boundary errors, first reuse the repository's existing glossary/domain-model document or the `context` role in `.governance/docs-map.json`. Only when no equivalent exists should you lazily create the default `CONTEXT.md` from `templates/context.example.md`.
When terms require repeated explanation, one term has multiple meanings, or unclear language causes module-boundary errors, first reuse the repository's existing glossary/domain-model document or the `context` role in `.governance/docs-map.json`. If an existing context map identifies separate business contexts, follow its links to the relevant glossary; ask when the topic's owner is unclear. Different contexts may legitimately use the same word differently. Do not create a glossary for every directory or impose one meaning across unrelated contexts. Only when no equivalent exists and a term has been resolved should you propose the default `CONTEXT.md` from `templates/context.example.md`. Obtain the user's explicit approval to create that new artifact before writing it, and stay within the authorized documentation scope. General permission to update documentation does not itself approve a new artifact; editing an existing mapped glossary within the authorized scope does not require a new-artifact approval.
Record only:
@@ -21,11 +21,23 @@ Record only:
Do not include implementation details, current state, task schedules, full requirements, decision rationale, or history. Keep those in code/MAP, STATUS/Issue Tracker, Spec, ADR, and LOG respectively. Terms must be traceable to code, contracts, or business evidence. Mark unconfirmed items as `needs confirmation`; do not guess.
## Refine Language During the Conversation
This is an active modeling workflow, not a requirement to rewrite the glossary whenever another skill reads it.
- **Challenge contradictions.** Compare the user's wording with the glossary. If “cancellation” already means cancelling an entire order but the request permits cancelling one line, explain that distinction and ask which meaning is intended; do not silently redefine the term.
- **Separate overloaded concepts.** For “account,” establish whether the speaker means the customer organization, a person's login, or a billing relationship. Propose a precise term for each distinct concept. Record rejected synonyms only where they really denote the same concept in this context.
- **Probe relationships with concrete scenarios.** Ask, for example, whether one customer can have multiple logins, or what happens to an invoice when only part of an order is cancelled. Scenarios expose boundaries; they are questions, not invented business rules.
- **Check claims against evidence.** Inspect relevant code, tests, contracts, and existing records. If the implementation cancels only whole orders, surface the mismatch with the proposed partial-cancellation rule. Code demonstrates current behavior, not the business owner's desired behavior; do not decide which is correct by yourself.
- **Capture confirmed terms as they settle.** Within an authorized documentation edit, update the existing glossary at that point instead of deferring all definitions until closeout. Keep definitions to one or two sentences, with a source and context-specific avoided synonyms where useful. Do not rewrite code, contracts, or accepted decisions merely to match a proposed term. In discussion-only or read-only work, present the proposed wording without writing files.
Use the project's existing glossary format. For a new document, the [context template](templates/context.example.md) provides a compact starting point; omit sections without real content. Include business-specific concepts, not generic programming vocabulary such as timeouts or utility functions.
## ADR Handoff: Reuse the Existing Capability
This skill does not define a second ADR directory, numbering scheme, lifecycle, or template. When a decision is hard to reverse, would confuse a future reader without context, and involves a real tradeoff:
1. Read and execute `architecture-decision-records`, preserving the repository's existing ADR location and format.
1. Read and execute `architecture-decision-records`, preserving the repository's existing ADR location, format, and explicit approval gates for initialization and writing.
2. If no convention exists, let that skill choose the minimum viable location; do not create a separate system here.
3. Link to the ADR from `CONTEXT.md`, the project map, or the relevant Spec/Issue instead of copying the decision body.
4. When a decision is accepted, superseded, or deprecated, append an event to project history according to the living-document rules.
@@ -38,14 +50,16 @@ Routine implementation details, easily reversible changes, and short-lived exper
- The project needs a stable domain-language entry point but has no equivalent document.
- Context work uncovers a durable decision that should be handed to `architecture-decision-records`.
Merely consuming established vocabulary during an ordinary bug fix does not require a modeling session or new documentation.
## When to Use
Use this skill when stable domain language needs one evidence-backed home, or when context work reveals a hard-to-reverse decision that belongs in the repository's existing ADR system.
## How It Works
1. Reuse the mapped context or glossary artifact when one exists; create `CONTEXT.md` only when no equivalent source of truth is available.
2. Record precise domain definitions, relationships, confirmed interpretations, and explicitly unconfirmed ambiguities without mixing in implementation or task state.
1. Reuse the mapped context or glossary artifact when one exists; create `CONTEXT.md` only when no equivalent source of truth is available and the user has explicitly approved creating that new artifact.
2. Challenge conflicting language, probe concrete edge cases, and cross-check claims against code and contracts before recording confirmed definitions and relationships. Keep unresolved interpretations explicit without mixing in implementation or task state.
3. Route durable tradeoffs to `architecture-decision-records` and link the resulting ADR instead of copying its rationale into context documents.
4. Keep schedules, blockers, and execution status in the existing issue tracker.
@@ -60,6 +74,8 @@ Use this skill when stable domain language needs one evidence-backed home, or wh
- Copying current tasks, schedules, full requirements, or implementation details into `CONTEXT.md`.
- Creating empty context files or a second ADR system for completeness.
- Treating unconfirmed domain interpretations as facts, or copying ADR bodies into maps and logs.
- Renaming distinct business concepts as synonyms, or forcing separate contexts into one universal glossary.
- Treating a glossary update as permission to change implementation, or treating code as proof of the intended business rule.
## Related Skills
@@ -70,3 +86,7 @@ Use this skill when stable domain language needs one evidence-backed home, or wh
## Scheduling and Task Boundary
Keep tasks, status, blockers, and schedules in the project's existing tracker, such as GitHub Issues or Linear, as the single source of truth. Use local `.scratch/` only when no external tracker exists and the project explicitly adopts it. Do not put schedules in `CONTEXT.md`, ADRs, PROJECT_STATUS, or the history index database.
## Attribution
The active terminology-refinement workflow adapts Matt Pocock's MIT-licensed `domain-modeling` skill. See [source and adaptation boundaries](references/source.md). ECC's existing ADR workflow remains the sole decision-record owner.
@@ -0,0 +1,5 @@
# Source and adaptation
The active terminology-refinement workflow and compact glossary guidance are adapted from [Matt Pocock's domain-modeling skill](https://github.com/mattpocock/skills/tree/c55ee46073ed923f86ce59a5eb3b6d895095d1b7/skills/engineering/domain-modeling), pinned at `c55ee46073ed923f86ce59a5eb3b6d895095d1b7`, under the [MIT license](../LICENSE).
This is an in-place ECC adaptation, not another skill or an external runtime dependency. It preserves ECC's mapped glossary selection, evidence and authorization boundaries, and handoff to `architecture-decision-records`. It does not import a second ADR template, directory, numbering scheme, or lifecycle. The existing ECC context template is extended rather than replaced with a second glossary format.
@@ -1,12 +1,14 @@
# CONTEXT.md — Domain Language
> Create only when needed. Do not include implementation, status, scheduling, or decisions.
> Create only after the first term is resolved, no equivalent glossary exists, and the user has explicitly approved creating this new artifact within the authorized documentation scope. General documentation-update permission alone is not creation approval. Reuse an existing glossary and its format instead of creating a duplicate; authorized edits to that existing glossary do not need a new-artifact approval. Omit sections without content. Do not include implementation, status, scheduling, or decision rationale.
{One or two sentences identifying the business context this vocabulary describes.}
## Core Terms
| Term | Precise definition | Evidence/source |
|---|---|---|
| `{term}` | `{what it means and how it differs from adjacent terms}` | `{business rule, contract, or code location}` |
| Term | Precise definition | Avoid in this context | Evidence/source |
|---|---|---|---|
| `{canonical term}` | `{one or two sentences defining the concept and distinguishing adjacent concepts}` | `{confirmed synonyms to avoid; not names for distinct concepts}` | `{confirmed business source, contract, or code location}` |
## Concept Relationships
+24 -1
View File
@@ -51,7 +51,30 @@ verification step. Resolve the selected skill directory first, then invoke:
python3 <docs-governance-skill>/scripts/audit-docs.py --root <repository> --scope full
```
Available scopes are `spine`, `context`, `adr`, `artifacts`, and `full`.
Available scopes are `spine`, `context`, `adr`, `artifacts`, `modules`, and `full`.
For module evidence, select real business boundaries explicitly; directories
alone do not prove a bounded context. Repeat `--module` for several boundaries:
```bash
python3 <docs-governance-skill>/scripts/audit-docs.py --root <repository> --scope modules --module src/inventory --module src/billing
```
`modules` requires a selected directory. `full` also accepts `--module`; other
scopes reject it rather than silently skipping the requested checks. The module
check finds local `AGENTS.md` and `CONTEXT.md`, or reports the nearest ancestor
and mapped project sources as shared candidates. Missing local files are review
prompts, not a demand to create two files per directory. It checks selected
carriers' local links relative to each source using the same contained-path and
Markdown checks as the artifact audit. It never infers harness loading from
file presence.
After mechanical checks, inspect the actual module code, callers, contracts and
tests: compare responsibilities and import/call boundaries with documented
rules, and terminology with the canonical context source. Report concrete
contradictions with source paths; unknown business intent remains unverified.
Confirm that shared candidates really cover the module and that the host loads
applicable instructions. Reuse existing contract and ADR owners rather than
copying them into `AGENTS.md`. Passing links is not passing this semantic review.
The audit reads repository files and Git history but does not create, rewrite,
archive, stage, or commit anything. It reports:
@@ -66,6 +66,14 @@ The audit does not determine whether prose is true, whether a requirement is
satisfied, whether a task is complete, or whether a change should be merged.
Warnings explicitly require semantic review.
Module auditing is opt-in through explicit `--module` directories with scope
`modules` or `full`. Local `AGENTS.md` and `CONTEXT.md` are conventional evidence
locations, not mandatory per-directory artifacts. Ancestor and mapped sources
are shared candidates whose applicability must be confirmed manually; the audit
does not equate a file's existence with instruction loading or business coverage.
Module responsibilities, code/contract boundaries and vocabulary require source
and test evidence after the contained-path and source-relative link checks.
For link checks, any `scheme://` destination is external. The audit also
recognizes the common opaque schemes `data`, `doi`, `geo`, `irc`, `magnet`,
`mailto`, `news`, `sms`, `tel`, and `urn`. Other colon-shaped destinations are
@@ -16,6 +16,7 @@ from markdown_links import (
normalize_link_target,
without_fenced_code,
)
from module_audit import check_modules
DEFAULT_ROLES = {
"constitution": "CLAUDE.md",
@@ -242,10 +243,12 @@ def resolve_target(root: Path, source: Path, target: str) -> Path | None:
return resolve_within_root(root, source.parent, target)
def check_markdown_links(root: Path, files: list[Path], report: Report) -> None:
def check_markdown_links(
root: Path, files: list[Path], report: Report, *, skip_templates: bool = True
) -> None:
broken: list[str] = []
for source in files:
if "templates" in source.relative_to(root).parts:
if skip_templates and "templates" in source.relative_to(root).parts:
continue
text = source.read_text(encoding="utf-8")
for raw in markdown_link_targets(text):
@@ -716,11 +719,19 @@ def main() -> int:
parser.add_argument("--root", type=Path, default=Path.cwd())
parser.add_argument(
"--scope",
choices=("spine", "context", "adr", "artifacts", "full"),
choices=("spine", "context", "adr", "artifacts", "modules", "full"),
default="full",
)
parser.add_argument(
"--module", action="append", default=[],
help="Repository-relative module directory; repeat for several modules",
)
parser.add_argument("--log-threshold", type=int, default=200)
args = parser.parse_args()
if args.scope == "modules" and not args.module:
parser.error("--scope modules requires at least one --module directory")
if args.module and args.scope not in ("modules", "full"):
parser.error("--module is supported only with --scope modules or full")
root = args.root.resolve()
if not root.is_dir():
@@ -728,6 +739,11 @@ def main() -> int:
return 2
report = Report()
roles = load_roles(root, report)
if args.module:
check_modules(
root, roles, args.module, report,
resolved_within_root, read_utf8, check_markdown_links,
)
if args.scope in ("spine", "full"):
check_spine(root, roles, report, args.log_threshold)
if args.scope in ("context", "full"):
@@ -0,0 +1,89 @@
"""Read-only evidence discovery for explicitly selected business modules."""
from collections.abc import Callable
from pathlib import Path
from typing import Protocol, TypeVar
class AuditReport(Protocol):
def section(self, title: str) -> None: ...
def ok(self, message: str) -> None: ...
def warn(self, message: str) -> None: ...
def fail(self, message: str) -> None: ...
ReportT = TypeVar("ReportT", bound=AuditReport, contravariant=True)
class LinkChecker(Protocol[ReportT]):
def __call__(
self, root: Path, files: list[Path], report: ReportT,
*, skip_templates: bool = True,
) -> None: ...
def carrier_candidates(root: Path, module: Path, name: str, mapped: str) -> list[Path]:
directories = [module, *module.parents]
directories = [root] if module == root else directories[:directories.index(root) + 1]
candidates = [directory / name for directory in directories] + [root / mapped]
return list(dict.fromkeys(candidates))
def check_modules(
root: Path,
roles: dict[str, str],
names: list[str],
report: ReportT,
resolve_source: Callable[[Path, Path], Path | None],
read_source: Callable[[Path, ReportT, str], str | None],
check_links: LinkChecker[ReportT],
) -> None:
"""Use the audit's existing containment, UTF-8 and Markdown checks unchanged."""
report.section("modules")
sources: set[Path] = set()
for name in sorted(set(names)):
requested = Path(name)
module = resolve_source(root, root / requested)
if requested.is_absolute() or module is None or not module.is_dir():
report.fail(
f"Module must be a repository-relative directory inside root: {name!r}"
)
continue
report.ok(f"Selected module: {str(module.relative_to(root))!r}")
for role, filename in (("agents", "AGENTS.md"), ("context", "CONTEXT.md")):
found = False
for candidate in carrier_candidates(root, module, filename, roles[role]):
if not candidate.exists() and not candidate.is_symlink():
continue
resolved = resolve_source(root, candidate)
if resolved is None or not resolved.is_file():
report.fail(
f"Module {role} source must be a repository-contained regular file: "
f"{str(candidate.relative_to(root))!r}"
)
found = True
break
if read_source(resolved, report, f"module {role} source") is None:
found = True
break
sources.add(resolved)
source_label = str(candidate.relative_to(root))
if candidate.parent == module:
report.ok(f"Local {role} source: {source_label!r}")
else:
report.warn(
f"Shared {role} candidate: {source_label!r}; "
f"confirm it covers module {str(module.relative_to(root))!r}"
)
found = True
break
if not found:
report.warn(
f"No {role} source found for module {str(module.relative_to(root))!r}; "
"review existing rules or glossary before creating a file"
)
check_links(root, sorted(sources), report, skip_templates=False)
report.warn(
"Module responsibilities, instruction loading, terminology and code/contract "
"consistency require semantic review; file presence is not proof of coverage"
)
@@ -97,6 +97,16 @@ History is append-oriented for traceability, but not immutable at the expense of
- preserve a short sanitized note explaining the correction when safe;
- do not silently rewrite a decision to make the past look cleaner.
### Module-local maintenance
For an authorized new business module or a confirmed responsibility change,
use optional `module-governance`, when installed, to connect local harness instructions to the existing
context, contract, and decision owners. Keep global rules at project scope;
update only affected navigation and lifecycle records here. Ordinary fixes
with unchanged boundaries do not require a new local AGENTS/CONTEXT pair.
If unavailable, report the optional capability rather than installing it or
copying its workflow into project instructions.
## Lightweight Adoption Template
Start with a role map, not four new files:
+130
View File
@@ -0,0 +1,130 @@
---
name: module-governance
description: >-
Establish or maintain a business module's local agent instructions, domain-context links, and interface ownership when implementing a new module or changing its responsibilities. Reuse project-level rules and existing ECC workflows; ordinary bug fixes, styling, and read-only design discussions do not start module documentation setup.
metadata:
origin: ECC
---
# Module Governance
Connect module-level working rules to existing domain language, contracts, and
decisions. This is an orchestration skill, not a second DDD methodology or an
automatic documentation generator.
## When to Activate
- The user asks to implement a new business module, without needing to name this skill.
- The user asks to document an existing module or maintain a confirmed responsibility change.
- An authorized module change needs local instructions and canonical knowledge pointers.
Do not start setup for an ordinary bug, styling change, internal refactor with
unchanged boundaries, or a new directory/UI component that simply inherits
parent conventions. For design discussion or an audit alone, inspect and report
without creating files. Intent-based selection is not a background hook and
does not grant permission for unrelated code, commits, pushes, or business choices.
## How It Works
### 1. Discover the boundary before creating documents
Read applicable project and ancestor harness instructions, the existing map,
requirements, relevant code, and contracts. Identify the module's responsibilities,
non-responsibilities, state ownership, and actual consumers. Directory names alone
do not establish a bounded context. Surface material ambiguities instead of
inventing business rules.
For an existing boundary change, use `change-impact` first. Its read-only and
authorization constraints still apply; do not run an unrelated development workflow.
### 2. Separate project-wide rules from local rules
The project-level instruction source owns global constraints, module navigation,
and cross-module collaboration. A local `AGENTS.md` owns only additional module
constraints: responsibilities, state ownership, public-interface pointers, and
verification entry points. Local rules cannot waive project-wide constraints.
Reuse an established `CLAUDE.md` or other active harness surface instead of
forcing a rename or maintaining two copies. If parent instructions already
cover the module and no local rule is missing, link that source and do not create
a redundant local file. Follow an existing explicit local-entrypoint requirement.
Do not claim that a harness automatically loads every nested instruction file.
### 3. Reuse the existing knowledge owners
| Information | Existing ECC owner |
|---|---|
| Stable terminology and concept relationships | `context-and-decisions`, using the existing glossary or context role |
| Hard-to-reverse decisions and rationale | `architecture-decision-records`, through the existing context handoff |
| Cross-boundary machine-checkable interfaces | `contract-first` |
| Documentation navigation and lifecycle | `living-docs-governance` |
| Affected consumers and boundary-change evidence | `change-impact` |
Load the applicable skill when its work is needed; do not duplicate its method
here. Keep terminology in the relevant existing `CONTEXT.md` or equivalent,
not in AGENTS. Frontend and backend in the same business context can share that
glossary; UI-specific working rules may be local without creating a glossary per
page. Reuse existing contracts and ADRs by reference, not by copying their bodies.
### 4. Maintain only the authorized records
Within the user's requested module work, update the smallest missing local rules
and confirmed context entries. Use `living-docs-governance` for affected navigation
and lifecycle updates, retaining its approval requirement for new top-level
artifacts. Do not create empty files to meet a two-file quota. When implementation
does not yet exist, label the boundary as confirmed design with code unverified;
do not declare documentation/code consistency from prose alone.
### 5. Verify both meaning and links
Compare each claimed owner, interface, and constraint with code, canonical
contracts, and executable evidence. Report contradictions for owner resolution;
do not rewrite the rules merely to match an accidental implementation.
Resolve relative links from **each referencing document's directory**, not the
terminal working directory. Resolve the installed `docs-governance` skill path
and run its existing read-only link check as an agreed verification step:
```bash
python3 <docs-governance-skill>/scripts/audit-docs.py --root <repository> --scope artifacts
```
Confirm that the audit scanned the changed local instructions, context, and
navigation; check its link results and exit code. Fix broken references and rerun,
without creating empty targets. If the script is unavailable or excludes a target,
report that target as unverified. A passing business test does not prove links are
correct, and an existing same-named file may still be the wrong knowledge owner.
## Examples
- **“Implement an inventory module.”** Inspect existing boundaries; add local
rules only for genuinely new constraints; reuse the domain glossary and API
contract; link them from existing navigation; verify code and source-relative links.
- **“Should payments become a separate module?”** Explain boundary options and
evidence without writing files or treating the discussion as implementation approval.
- **“Fix the inventory badge color.”** Read applicable instructions and fix the
requested UI behavior; do not generate AGENTS, CONTEXT, or ADR documents.
- **“Audit module documentation.”** Use the existing read-only audit and semantic
review; a missing local file is not by itself permission to create one.
## Anti-Patterns
- A mandatory AGENTS/CONTEXT pair for every directory or frontend page.
- Duplicating global constraints, glossary definitions, contracts, or ADR rationale.
- Treating documentation checks as proof of completed business implementation.
- Installing hooks or asserting automatic nested-file loading without real harness wiring.
## Delivery
Report canonical record paths, their project/module scope, actual changes,
verification evidence, and unresolved ownership questions. Module governance
delivers working rules and knowledge entry points, not a complete DDD conversion.
## Related Skills
- `context-and-decisions`
- `architecture-decision-records`
- `contract-first`
- `change-impact`
- `living-docs-governance`
- `docs-governance`
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Matt Pocock
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+24 -4
View File
@@ -10,7 +10,7 @@ Govern “what these business terms mean” separately from “why this option w
## Domain Context Artifact: Domain Language Only
When terms require repeated explanation, one term has multiple meanings, or unclear language causes module-boundary errors, first reuse the repository's existing glossary/domain-model document or the `context` role in `.governance/docs-map.json`. Only when no equivalent exists should you lazily create the default `CONTEXT.md` from `templates/context.example.md`.
When terms require repeated explanation, one term has multiple meanings, or unclear language causes module-boundary errors, first reuse the repository's existing glossary/domain-model document or the `context` role in `.governance/docs-map.json`. If an existing context map identifies separate business contexts, follow its links to the relevant glossary; ask when the topic's owner is unclear. Different contexts may legitimately use the same word differently. Do not create a glossary for every directory or impose one meaning across unrelated contexts. Only when no equivalent exists and a term has been resolved should you propose the default `CONTEXT.md` from `templates/context.example.md`. Obtain the user's explicit approval to create that new artifact before writing it, and stay within the authorized documentation scope. General permission to update documentation does not itself approve a new artifact; editing an existing mapped glossary within the authorized scope does not require a new-artifact approval.
Record only:
@@ -21,11 +21,23 @@ Record only:
Do not include implementation details, current state, task schedules, full requirements, decision rationale, or history. Keep those in code/MAP, STATUS/Issue Tracker, Spec, ADR, and LOG respectively. Terms must be traceable to code, contracts, or business evidence. Mark unconfirmed items as `needs confirmation`; do not guess.
## Refine Language During the Conversation
This is an active modeling workflow, not a requirement to rewrite the glossary whenever another skill reads it.
- **Challenge contradictions.** Compare the user's wording with the glossary. If “cancellation” already means cancelling an entire order but the request permits cancelling one line, explain that distinction and ask which meaning is intended; do not silently redefine the term.
- **Separate overloaded concepts.** For “account,” establish whether the speaker means the customer organization, a person's login, or a billing relationship. Propose a precise term for each distinct concept. Record rejected synonyms only where they really denote the same concept in this context.
- **Probe relationships with concrete scenarios.** Ask, for example, whether one customer can have multiple logins, or what happens to an invoice when only part of an order is cancelled. Scenarios expose boundaries; they are questions, not invented business rules.
- **Check claims against evidence.** Inspect relevant code, tests, contracts, and existing records. If the implementation cancels only whole orders, surface the mismatch with the proposed partial-cancellation rule. Code demonstrates current behavior, not the business owner's desired behavior; do not decide which is correct by yourself.
- **Capture confirmed terms as they settle.** Within an authorized documentation edit, update the existing glossary at that point instead of deferring all definitions until closeout. Keep definitions to one or two sentences, with a source and context-specific avoided synonyms where useful. Do not rewrite code, contracts, or accepted decisions merely to match a proposed term. In discussion-only or read-only work, present the proposed wording without writing files.
Use the project's existing glossary format. For a new document, the [context template](templates/context.example.md) provides a compact starting point; omit sections without real content. Include business-specific concepts, not generic programming vocabulary such as timeouts or utility functions.
## ADR Handoff: Reuse the Existing Capability
This skill does not define a second ADR directory, numbering scheme, lifecycle, or template. When a decision is hard to reverse, would confuse a future reader without context, and involves a real tradeoff:
1. Read and execute `architecture-decision-records`, preserving the repository's existing ADR location and format.
1. Read and execute `architecture-decision-records`, preserving the repository's existing ADR location, format, and explicit approval gates for initialization and writing.
2. If no convention exists, let that skill choose the minimum viable location; do not create a separate system here.
3. Link to the ADR from `CONTEXT.md`, the project map, or the relevant Spec/Issue instead of copying the decision body.
4. When a decision is accepted, superseded, or deprecated, append an event to project history according to the living-document rules.
@@ -38,14 +50,16 @@ Routine implementation details, easily reversible changes, and short-lived exper
- The project needs a stable domain-language entry point but has no equivalent document.
- Context work uncovers a durable decision that should be handed to `architecture-decision-records`.
Merely consuming established vocabulary during an ordinary bug fix does not require a modeling session or new documentation.
## When to Use
Use this skill when stable domain language needs one evidence-backed home, or when context work reveals a hard-to-reverse decision that belongs in the repository's existing ADR system.
## How It Works
1. Reuse the mapped context or glossary artifact when one exists; create `CONTEXT.md` only when no equivalent source of truth is available.
2. Record precise domain definitions, relationships, confirmed interpretations, and explicitly unconfirmed ambiguities without mixing in implementation or task state.
1. Reuse the mapped context or glossary artifact when one exists; create `CONTEXT.md` only when no equivalent source of truth is available and the user has explicitly approved creating that new artifact.
2. Challenge conflicting language, probe concrete edge cases, and cross-check claims against code and contracts before recording confirmed definitions and relationships. Keep unresolved interpretations explicit without mixing in implementation or task state.
3. Route durable tradeoffs to `architecture-decision-records` and link the resulting ADR instead of copying its rationale into context documents.
4. Keep schedules, blockers, and execution status in the existing issue tracker.
@@ -60,6 +74,8 @@ Use this skill when stable domain language needs one evidence-backed home, or wh
- Copying current tasks, schedules, full requirements, or implementation details into `CONTEXT.md`.
- Creating empty context files or a second ADR system for completeness.
- Treating unconfirmed domain interpretations as facts, or copying ADR bodies into maps and logs.
- Renaming distinct business concepts as synonyms, or forcing separate contexts into one universal glossary.
- Treating a glossary update as permission to change implementation, or treating code as proof of the intended business rule.
## Related Skills
@@ -70,3 +86,7 @@ Use this skill when stable domain language needs one evidence-backed home, or wh
## Scheduling and Task Boundary
Keep tasks, status, blockers, and schedules in the project's existing tracker, such as GitHub Issues or Linear, as the single source of truth. Use local `.scratch/` only when no external tracker exists and the project explicitly adopts it. Do not put schedules in `CONTEXT.md`, ADRs, PROJECT_STATUS, or the history index database.
## Attribution
The active terminology-refinement workflow adapts Matt Pocock's MIT-licensed `domain-modeling` skill. See [source and adaptation boundaries](references/source.md). ECC's existing ADR workflow remains the sole decision-record owner.
@@ -0,0 +1,5 @@
# Source and adaptation
The active terminology-refinement workflow and compact glossary guidance are adapted from [Matt Pocock's domain-modeling skill](https://github.com/mattpocock/skills/tree/c55ee46073ed923f86ce59a5eb3b6d895095d1b7/skills/engineering/domain-modeling), pinned at `c55ee46073ed923f86ce59a5eb3b6d895095d1b7`, under the [MIT license](../LICENSE).
This is an in-place ECC adaptation, not another skill or an external runtime dependency. It preserves ECC's mapped glossary selection, evidence and authorization boundaries, and handoff to `architecture-decision-records`. It does not import a second ADR template, directory, numbering scheme, or lifecycle. The existing ECC context template is extended rather than replaced with a second glossary format.
@@ -1,12 +1,14 @@
# CONTEXT.md — Domain Language
> Create only when needed. Do not include implementation, status, scheduling, or decisions.
> Create only after the first term is resolved, no equivalent glossary exists, and the user has explicitly approved creating this new artifact within the authorized documentation scope. General documentation-update permission alone is not creation approval. Reuse an existing glossary and its format instead of creating a duplicate; authorized edits to that existing glossary do not need a new-artifact approval. Omit sections without content. Do not include implementation, status, scheduling, or decision rationale.
{One or two sentences identifying the business context this vocabulary describes.}
## Core Terms
| Term | Precise definition | Evidence/source |
|---|---|---|
| `{term}` | `{what it means and how it differs from adjacent terms}` | `{business rule, contract, or code location}` |
| Term | Precise definition | Avoid in this context | Evidence/source |
|---|---|---|---|
| `{canonical term}` | `{one or two sentences defining the concept and distinguishing adjacent concepts}` | `{confirmed synonyms to avoid; not names for distinct concepts}` | `{confirmed business source, contract, or code location}` |
## Concept Relationships
+24 -1
View File
@@ -51,7 +51,30 @@ verification step. Resolve the selected skill directory first, then invoke:
python3 <docs-governance-skill>/scripts/audit-docs.py --root <repository> --scope full
```
Available scopes are `spine`, `context`, `adr`, `artifacts`, and `full`.
Available scopes are `spine`, `context`, `adr`, `artifacts`, `modules`, and `full`.
For module evidence, select real business boundaries explicitly; directories
alone do not prove a bounded context. Repeat `--module` for several boundaries:
```bash
python3 <docs-governance-skill>/scripts/audit-docs.py --root <repository> --scope modules --module src/inventory --module src/billing
```
`modules` requires a selected directory. `full` also accepts `--module`; other
scopes reject it rather than silently skipping the requested checks. The module
check finds local `AGENTS.md` and `CONTEXT.md`, or reports the nearest ancestor
and mapped project sources as shared candidates. Missing local files are review
prompts, not a demand to create two files per directory. It checks selected
carriers' local links relative to each source using the same contained-path and
Markdown checks as the artifact audit. It never infers harness loading from
file presence.
After mechanical checks, inspect the actual module code, callers, contracts and
tests: compare responsibilities and import/call boundaries with documented
rules, and terminology with the canonical context source. Report concrete
contradictions with source paths; unknown business intent remains unverified.
Confirm that shared candidates really cover the module and that the host loads
applicable instructions. Reuse existing contract and ADR owners rather than
copying them into `AGENTS.md`. Passing links is not passing this semantic review.
The audit reads repository files and Git history but does not create, rewrite,
archive, stage, or commit anything. It reports:
@@ -66,6 +66,14 @@ The audit does not determine whether prose is true, whether a requirement is
satisfied, whether a task is complete, or whether a change should be merged.
Warnings explicitly require semantic review.
Module auditing is opt-in through explicit `--module` directories with scope
`modules` or `full`. Local `AGENTS.md` and `CONTEXT.md` are conventional evidence
locations, not mandatory per-directory artifacts. Ancestor and mapped sources
are shared candidates whose applicability must be confirmed manually; the audit
does not equate a file's existence with instruction loading or business coverage.
Module responsibilities, code/contract boundaries and vocabulary require source
and test evidence after the contained-path and source-relative link checks.
For link checks, any `scheme://` destination is external. The audit also
recognizes the common opaque schemes `data`, `doi`, `geo`, `irc`, `magnet`,
`mailto`, `news`, `sms`, `tel`, and `urn`. Other colon-shaped destinations are
+19 -3
View File
@@ -16,6 +16,7 @@ from markdown_links import (
normalize_link_target,
without_fenced_code,
)
from module_audit import check_modules
DEFAULT_ROLES = {
"constitution": "CLAUDE.md",
@@ -242,10 +243,12 @@ def resolve_target(root: Path, source: Path, target: str) -> Path | None:
return resolve_within_root(root, source.parent, target)
def check_markdown_links(root: Path, files: list[Path], report: Report) -> None:
def check_markdown_links(
root: Path, files: list[Path], report: Report, *, skip_templates: bool = True
) -> None:
broken: list[str] = []
for source in files:
if "templates" in source.relative_to(root).parts:
if skip_templates and "templates" in source.relative_to(root).parts:
continue
text = source.read_text(encoding="utf-8")
for raw in markdown_link_targets(text):
@@ -716,11 +719,19 @@ def main() -> int:
parser.add_argument("--root", type=Path, default=Path.cwd())
parser.add_argument(
"--scope",
choices=("spine", "context", "adr", "artifacts", "full"),
choices=("spine", "context", "adr", "artifacts", "modules", "full"),
default="full",
)
parser.add_argument(
"--module", action="append", default=[],
help="Repository-relative module directory; repeat for several modules",
)
parser.add_argument("--log-threshold", type=int, default=200)
args = parser.parse_args()
if args.scope == "modules" and not args.module:
parser.error("--scope modules requires at least one --module directory")
if args.module and args.scope not in ("modules", "full"):
parser.error("--module is supported only with --scope modules or full")
root = args.root.resolve()
if not root.is_dir():
@@ -728,6 +739,11 @@ def main() -> int:
return 2
report = Report()
roles = load_roles(root, report)
if args.module:
check_modules(
root, roles, args.module, report,
resolved_within_root, read_utf8, check_markdown_links,
)
if args.scope in ("spine", "full"):
check_spine(root, roles, report, args.log_threshold)
if args.scope in ("context", "full"):
@@ -0,0 +1,89 @@
"""Read-only evidence discovery for explicitly selected business modules."""
from collections.abc import Callable
from pathlib import Path
from typing import Protocol, TypeVar
class AuditReport(Protocol):
def section(self, title: str) -> None: ...
def ok(self, message: str) -> None: ...
def warn(self, message: str) -> None: ...
def fail(self, message: str) -> None: ...
ReportT = TypeVar("ReportT", bound=AuditReport, contravariant=True)
class LinkChecker(Protocol[ReportT]):
def __call__(
self, root: Path, files: list[Path], report: ReportT,
*, skip_templates: bool = True,
) -> None: ...
def carrier_candidates(root: Path, module: Path, name: str, mapped: str) -> list[Path]:
directories = [module, *module.parents]
directories = [root] if module == root else directories[:directories.index(root) + 1]
candidates = [directory / name for directory in directories] + [root / mapped]
return list(dict.fromkeys(candidates))
def check_modules(
root: Path,
roles: dict[str, str],
names: list[str],
report: ReportT,
resolve_source: Callable[[Path, Path], Path | None],
read_source: Callable[[Path, ReportT, str], str | None],
check_links: LinkChecker[ReportT],
) -> None:
"""Use the audit's existing containment, UTF-8 and Markdown checks unchanged."""
report.section("modules")
sources: set[Path] = set()
for name in sorted(set(names)):
requested = Path(name)
module = resolve_source(root, root / requested)
if requested.is_absolute() or module is None or not module.is_dir():
report.fail(
f"Module must be a repository-relative directory inside root: {name!r}"
)
continue
report.ok(f"Selected module: {str(module.relative_to(root))!r}")
for role, filename in (("agents", "AGENTS.md"), ("context", "CONTEXT.md")):
found = False
for candidate in carrier_candidates(root, module, filename, roles[role]):
if not candidate.exists() and not candidate.is_symlink():
continue
resolved = resolve_source(root, candidate)
if resolved is None or not resolved.is_file():
report.fail(
f"Module {role} source must be a repository-contained regular file: "
f"{str(candidate.relative_to(root))!r}"
)
found = True
break
if read_source(resolved, report, f"module {role} source") is None:
found = True
break
sources.add(resolved)
source_label = str(candidate.relative_to(root))
if candidate.parent == module:
report.ok(f"Local {role} source: {source_label!r}")
else:
report.warn(
f"Shared {role} candidate: {source_label!r}; "
f"confirm it covers module {str(module.relative_to(root))!r}"
)
found = True
break
if not found:
report.warn(
f"No {role} source found for module {str(module.relative_to(root))!r}; "
"review existing rules or glossary before creating a file"
)
check_links(root, sorted(sources), report, skip_templates=False)
report.warn(
"Module responsibilities, instruction loading, terminology and code/contract "
"consistency require semantic review; file presence is not proof of coverage"
)
+10
View File
@@ -97,6 +97,16 @@ History is append-oriented for traceability, but not immutable at the expense of
- preserve a short sanitized note explaining the correction when safe;
- do not silently rewrite a decision to make the past look cleaner.
### Module-local maintenance
For an authorized new business module or a confirmed responsibility change,
use optional `module-governance`, when installed, to connect local harness instructions to the existing
context, contract, and decision owners. Keep global rules at project scope;
update only affected navigation and lifecycle records here. Ordinary fixes
with unchanged boundaries do not require a new local AGENTS/CONTEXT pair.
If unavailable, report the optional capability rather than installing it or
copying its workflow into project instructions.
## Lightweight Adoption Template
Start with a role map, not four new files:
+130
View File
@@ -0,0 +1,130 @@
---
name: module-governance
description: >-
Establish or maintain a business module's local agent instructions, domain-context links, and interface ownership when implementing a new module or changing its responsibilities. Reuse project-level rules and existing ECC workflows; ordinary bug fixes, styling, and read-only design discussions do not start module documentation setup.
metadata:
origin: ECC
---
# Module Governance
Connect module-level working rules to existing domain language, contracts, and
decisions. This is an orchestration skill, not a second DDD methodology or an
automatic documentation generator.
## When to Activate
- The user asks to implement a new business module, without needing to name this skill.
- The user asks to document an existing module or maintain a confirmed responsibility change.
- An authorized module change needs local instructions and canonical knowledge pointers.
Do not start setup for an ordinary bug, styling change, internal refactor with
unchanged boundaries, or a new directory/UI component that simply inherits
parent conventions. For design discussion or an audit alone, inspect and report
without creating files. Intent-based selection is not a background hook and
does not grant permission for unrelated code, commits, pushes, or business choices.
## How It Works
### 1. Discover the boundary before creating documents
Read applicable project and ancestor harness instructions, the existing map,
requirements, relevant code, and contracts. Identify the module's responsibilities,
non-responsibilities, state ownership, and actual consumers. Directory names alone
do not establish a bounded context. Surface material ambiguities instead of
inventing business rules.
For an existing boundary change, use `change-impact` first. Its read-only and
authorization constraints still apply; do not run an unrelated development workflow.
### 2. Separate project-wide rules from local rules
The project-level instruction source owns global constraints, module navigation,
and cross-module collaboration. A local `AGENTS.md` owns only additional module
constraints: responsibilities, state ownership, public-interface pointers, and
verification entry points. Local rules cannot waive project-wide constraints.
Reuse an established `CLAUDE.md` or other active harness surface instead of
forcing a rename or maintaining two copies. If parent instructions already
cover the module and no local rule is missing, link that source and do not create
a redundant local file. Follow an existing explicit local-entrypoint requirement.
Do not claim that a harness automatically loads every nested instruction file.
### 3. Reuse the existing knowledge owners
| Information | Existing ECC owner |
|---|---|
| Stable terminology and concept relationships | `context-and-decisions`, using the existing glossary or context role |
| Hard-to-reverse decisions and rationale | `architecture-decision-records`, through the existing context handoff |
| Cross-boundary machine-checkable interfaces | `contract-first` |
| Documentation navigation and lifecycle | `living-docs-governance` |
| Affected consumers and boundary-change evidence | `change-impact` |
Load the applicable skill when its work is needed; do not duplicate its method
here. Keep terminology in the relevant existing `CONTEXT.md` or equivalent,
not in AGENTS. Frontend and backend in the same business context can share that
glossary; UI-specific working rules may be local without creating a glossary per
page. Reuse existing contracts and ADRs by reference, not by copying their bodies.
### 4. Maintain only the authorized records
Within the user's requested module work, update the smallest missing local rules
and confirmed context entries. Use `living-docs-governance` for affected navigation
and lifecycle updates, retaining its approval requirement for new top-level
artifacts. Do not create empty files to meet a two-file quota. When implementation
does not yet exist, label the boundary as confirmed design with code unverified;
do not declare documentation/code consistency from prose alone.
### 5. Verify both meaning and links
Compare each claimed owner, interface, and constraint with code, canonical
contracts, and executable evidence. Report contradictions for owner resolution;
do not rewrite the rules merely to match an accidental implementation.
Resolve relative links from **each referencing document's directory**, not the
terminal working directory. Resolve the installed `docs-governance` skill path
and run its existing read-only link check as an agreed verification step:
```bash
python3 <docs-governance-skill>/scripts/audit-docs.py --root <repository> --scope artifacts
```
Confirm that the audit scanned the changed local instructions, context, and
navigation; check its link results and exit code. Fix broken references and rerun,
without creating empty targets. If the script is unavailable or excludes a target,
report that target as unverified. A passing business test does not prove links are
correct, and an existing same-named file may still be the wrong knowledge owner.
## Examples
- **“Implement an inventory module.”** Inspect existing boundaries; add local
rules only for genuinely new constraints; reuse the domain glossary and API
contract; link them from existing navigation; verify code and source-relative links.
- **“Should payments become a separate module?”** Explain boundary options and
evidence without writing files or treating the discussion as implementation approval.
- **“Fix the inventory badge color.”** Read applicable instructions and fix the
requested UI behavior; do not generate AGENTS, CONTEXT, or ADR documents.
- **“Audit module documentation.”** Use the existing read-only audit and semantic
review; a missing local file is not by itself permission to create one.
## Anti-Patterns
- A mandatory AGENTS/CONTEXT pair for every directory or frontend page.
- Duplicating global constraints, glossary definitions, contracts, or ADR rationale.
- Treating documentation checks as proof of completed business implementation.
- Installing hooks or asserting automatic nested-file loading without real harness wiring.
## Delivery
Report canonical record paths, their project/module scope, actual changes,
verification evidence, and unresolved ownership questions. Module governance
delivers working rules and knowledge entry points, not a complete DDD conversion.
## Related Skills
- `context-and-decisions`
- `architecture-decision-records`
- `contract-first`
- `change-impact`
- `living-docs-governance`
- `docs-governance`
+8 -5
View File
@@ -17,6 +17,7 @@ const MIRRORED_FILES = [
'references/artifact-role-contract.md',
'scripts/audit-docs.py',
'scripts/markdown_links.py',
'scripts/module_audit.py',
];
const ROUTED_SKILLS = [
'ai-regression-testing',
@@ -42,11 +43,13 @@ function test(name, fn) {
console.log('\n=== Documentation governance surface tests ===\n');
test('Codex distribution files match the canonical skill', () => {
for (const relativePath of MIRRORED_FILES) {
const canonical = fs.readFileSync(path.join(CANONICAL, relativePath), 'utf8');
const mirror = fs.readFileSync(path.join(CODEX_MIRROR, relativePath), 'utf8');
assert.strictEqual(mirror, canonical, `${relativePath} mirror is stale`);
test('Codex and Pi distribution files match the canonical skill', () => {
for (const mirrorRoot of [CODEX_MIRROR, path.dirname(PI_CORE)]) {
for (const relativePath of MIRRORED_FILES) {
const canonical = fs.readFileSync(path.join(CANONICAL, relativePath), 'utf8');
const mirror = fs.readFileSync(path.join(mirrorRoot, relativePath), 'utf8');
assert.strictEqual(mirror, canonical, `${mirrorRoot}/${relativePath} mirror is stale`);
}
}
});
+94
View File
@@ -0,0 +1,94 @@
#!/usr/bin/env node
'use strict';
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const { listInstallComponents, resolveInstallPlan } = require('../../scripts/lib/install-manifests');
const { createManifestInstallPlan } = require('../../scripts/lib/install-executor');
const ROOT = path.resolve(__dirname, '../..');
const OWNERS = [
'module-governance', 'context-and-decisions', 'architecture-decision-records',
'contract-first', 'change-impact', 'living-docs-governance', 'docs-governance',
];
let passed = 0;
let failed = 0;
function test(name, run) {
try {
run();
passed += 1;
console.log(` ✓ ${name}`);
} catch (error) {
failed += 1;
console.error(` ✗ ${name}: ${error.message}`);
}
}
for (const target of ['claude', 'codex']) {
test(`opt-in ${target} installation includes every delegated knowledge owner`, () => {
const plan = resolveInstallPlan({
repoRoot: ROOT, profileId: 'minimal',
includeComponentIds: ['capability:module-governance'], target,
});
const paths = new Set(plan.selectedModules.flatMap(module => module.paths));
for (const owner of OWNERS) {
assert.ok(paths.has(`skills/${owner}`), `missing ${owner}`);
}
});
}
for (const target of ['claude', 'claude-project', 'cursor', 'antigravity', 'codex',
'opencode', 'codebuddy', 'joycode', 'qwen', 'zed']) {
test(`advertised ${target} capability produces copies of every delegated owner`, () => {
assert.ok(listInstallComponents({ repoRoot: ROOT, target }).some(
component => component.id === 'capability:module-governance'
));
const plan = createManifestInstallPlan({
sourceRoot: ROOT, target, homeDir: '/private/tmp/ecc-module-plan-preview',
projectRoot: '/private/tmp/ecc-module-plan-preview', profileId: 'minimal',
includeComponentIds: ['capability:module-governance'],
// This checks skill-copy planning; installer tests validate the compiled payload.
exemptValidationCodes: ['opencode-plugin-not-built'],
});
for (const owner of OWNERS) {
assert.ok(plan.operations.some(operation => operation.kind === 'copy-file'
&& operation.sourceRelativePath.endsWith(`skills/${owner}/SKILL.md`)),
`missing ${owner} copy on ${target}`);
}
});
}
for (const target of ['hermes', 'openclaw', 'kimi', 'adal', 'copilot']) {
test(`does not advertise the incomplete ${target} dependency closure`, () => {
assert.ok(!listInstallComponents({ repoRoot: ROOT, target }).some(
component => component.id === 'capability:module-governance'
), `module governance is not installable on ${target}`);
const plan = createManifestInstallPlan({
sourceRoot: ROOT, target, homeDir: '/private/tmp/ecc-module-plan-preview',
projectRoot: '/private/tmp/ecc-module-plan-preview', profileId: 'minimal',
includeComponentIds: ['capability:module-governance'],
});
assert.ok(plan.skippedModuleIds.includes('documentation-module-governance'));
assert.ok(!plan.selectedModuleIds.includes('documentation-module-governance'));
// agents-core may copy Codex metadata; that is not this capability's installation.
assert.ok(!plan.operations.some(operation => operation.moduleId
=== 'documentation-module-governance'));
});
}
test('Codex module workflow is the canonical implementation', () => {
assert.strictEqual(
fs.readFileSync(path.join(ROOT, 'skills/module-governance/SKILL.md'), 'utf8'),
fs.readFileSync(path.join(ROOT, '.agents/skills/module-governance/SKILL.md'), 'utf8')
);
});
test('standalone Pi carries the workflow and its delegated knowledge owners', () => {
for (const owner of OWNERS) {
assert.ok(fs.existsSync(path.join(ROOT, `pi/core/skills/${owner}/SKILL.md`)), owner);
}
});
console.log(`\nPassed: ${passed}\nFailed: ${failed}`);
process.exitCode = failed ? 1 : 0;
+184 -2
View File
@@ -17,10 +17,13 @@ def project(tmp_path: Path) -> Path:
def run_audit(
project: Path, scope: str, *, timeout: float | None = None
project: Path, scope: str, *, timeout: float | None = None,
modules: tuple[str, ...] = (),
script: Path = SCRIPT,
) -> subprocess.CompletedProcess[str]:
module_args = [argument for module in modules for argument in ("--module", module)]
return subprocess.run(
[sys.executable, str(SCRIPT), "--root", str(project), "--scope", scope],
[sys.executable, str(script), "--root", str(project), "--scope", scope, *module_args],
text=True,
capture_output=True,
check=False,
@@ -28,6 +31,185 @@ def run_audit(
)
def test_module_audit_checks_local_carriers_links_and_is_read_only(project: Path) -> None:
module = project / "src" / "inventory"
module.mkdir(parents=True)
(module / "AGENTS.md").write_text("[terms](CONTEXT.md)\n", encoding="utf-8")
(module / "CONTEXT.md").write_text("[contract](../../contract.md)\n", encoding="utf-8")
(project / "contract.md").write_text("# Contract\n", encoding="utf-8")
before = {path: path.read_bytes() for path in project.rglob("*") if path.is_file()}
result = run_audit(project, "modules", modules=("src/inventory",))
assert result.returncode == 0, result.stdout + result.stderr
assert "Local agents source: 'src/inventory/AGENTS.md'" in result.stdout
assert "Local context source: 'src/inventory/CONTEXT.md'" in result.stdout
assert "All local Markdown links resolve" in result.stdout
assert "semantic review" in result.stdout
after = {path: path.read_bytes() for path in project.rglob("*") if path.is_file()}
assert after == before
@pytest.mark.parametrize("distribution", ("skills", ".agents/skills", "pi/core/skills"))
@pytest.mark.parametrize("role,filename", (("agents", "AGENTS.md"), ("context", "CONTEXT.md")))
@pytest.mark.parametrize("broken", (False, True))
@pytest.mark.parametrize("module_path", (".", "src/inventory"))
def test_module_audit_checks_root_carrier_before_mapped_source(
project: Path, distribution: str, role: str, filename: str, broken: bool,
module_path: str,
) -> None:
(project / module_path).mkdir(parents=True, exist_ok=True)
(project / ".governance").mkdir()
(project / "docs").mkdir()
(project / ".governance/docs-map.json").write_text(
json.dumps({"agents": "docs/rules.md", "context": "docs/glossary.md"}),
encoding="utf-8",
)
for mapped in ("rules", "glossary"):
(project / f"docs/{mapped}.md").write_text("# Shared source\n", encoding="utf-8")
if not broken:
(project / "docs/target.md").write_text("# Link target\n", encoding="utf-8")
(project / filename).write_text("[target](docs/target.md)\n", encoding="utf-8")
before = {path: path.read_bytes() for path in project.rglob("*") if path.is_file()}
result = run_audit(
project, "modules", modules=(module_path,),
script=ROOT / distribution / "docs-governance/scripts/audit-docs.py",
)
assert result.returncode == (1 if broken else 0), result.stdout + result.stderr
source_label = f"Local {role} source" if module_path == "." else f"Shared {role} candidate"
assert f"{source_label}: '{filename}'" in result.stdout
assert (f"Broken Markdown link: '{filename}' -> 'docs/target.md'" in result.stdout) == broken
after = {path: path.read_bytes() for path in project.rglob("*") if path.is_file()}
assert after == before, "module audit must remain read-only in every distribution"
@pytest.mark.parametrize("scope", ("modules", "full"))
def test_module_audit_reports_nearest_shared_candidates(project: Path, scope: str) -> None:
(project / "src" / "inventory").mkdir(parents=True)
(project / "AGENTS.md").write_text("# Project rules\n", encoding="utf-8")
(project / "src" / "AGENTS.md").write_text("# Shared module rules\n", encoding="utf-8")
(project / "CONTEXT.md").write_text("# Shared language\n", encoding="utf-8")
result = run_audit(project, scope, modules=("src/inventory",))
assert result.returncode == 0, result.stdout + result.stderr
assert "Shared agents candidate: 'src/AGENTS.md'" in result.stdout
assert "Shared context candidate: 'CONTEXT.md'" in result.stdout
assert "confirm it covers module 'src/inventory'" in result.stdout
assert "Local agents source" not in result.stdout
def test_module_audit_reuses_mapped_sources_without_creating_shells(project: Path) -> None:
(project / "inventory").mkdir()
(project / "docs").mkdir()
(project / ".governance").mkdir()
(project / ".governance/docs-map.json").write_text(
json.dumps({"agents": "docs/rules.md", "context": "docs/glossary.md"}), encoding="utf-8"
)
for name in ("rules", "glossary"):
(project / f"docs/{name}.md").write_text(f"# {name}\n", encoding="utf-8")
result = run_audit(project, "modules", modules=("inventory",))
assert result.returncode == 0, result.stdout + result.stderr
assert "Shared agents candidate: 'docs/rules.md'" in result.stdout
assert "Shared context candidate: 'docs/glossary.md'" in result.stdout
assert list((project / "inventory").iterdir()) == []
def test_module_audit_checks_selected_carriers_in_template_named_modules(project: Path) -> None:
(project / "templates/inventory").mkdir(parents=True)
(project / "templates/inventory/AGENTS.md").write_text("[missing](missing.md)\n", encoding="utf-8")
result = run_audit(project, "modules", modules=("templates/inventory",))
assert result.returncode == 1, result.stdout + result.stderr
assert "Broken Markdown link: 'templates/inventory/AGENTS.md'" in result.stdout
def test_module_audit_missing_carriers_are_manual_review_not_failure(project: Path) -> None:
(project / "inventory").mkdir()
result = run_audit(project, "modules", modules=("inventory",))
assert result.returncode == 0, result.stdout + result.stderr
assert "No agents source found for module 'inventory'" in result.stdout
assert "No context source found for module 'inventory'" in result.stdout
assert list((project / "inventory").iterdir()) == []
@pytest.mark.parametrize("module", ("missing", "../", "/", "not-a-directory"))
def test_module_audit_rejects_invalid_directories(project: Path, module: str) -> None:
(project / "not-a-directory").write_text("not a module\n", encoding="utf-8")
result = run_audit(project, "modules", modules=(module,))
assert result.returncode == 1, result.stdout + result.stderr
assert "Module must be a repository-relative directory inside root" in result.stdout
assert "Traceback" not in result.stderr
@pytest.mark.parametrize("filename", ("AGENTS.md", "CONTEXT.md"))
def test_module_audit_does_not_read_escaping_sources(project: Path, filename: str) -> None:
module = project / "inventory"
module.mkdir()
outside = project.parent / f"outside-{filename}"
outside.write_text("[secret](secret-missing.md)\n", encoding="utf-8")
try:
(module / filename).symlink_to(outside)
result = run_audit(project, "modules", modules=("inventory",))
assert result.returncode == 1, result.stdout + result.stderr
assert "repository-contained regular file" in result.stdout
assert "secret-missing" not in result.stdout
finally:
outside.unlink(missing_ok=True)
def test_module_audit_reports_invalid_utf8_carrier(project: Path) -> None:
(project / "inventory").mkdir()
(project / "inventory/AGENTS.md").write_bytes(b"\xff")
result = run_audit(project, "modules", modules=("inventory",))
assert result.returncode == 1, result.stdout + result.stderr
assert "Cannot read module agents source" in result.stdout
def test_module_audit_reuses_link_parser_and_source_relative_resolution(project: Path) -> None:
module = project / "src/inventory"
module.mkdir(parents=True)
(module / "AGENTS.md").write_text("[terms](.\\CONTEXT.md)\n", encoding="utf-8")
(module / "CONTEXT.md").write_text("[contract](../contract.md)\n", encoding="utf-8")
(project / "contract.md").write_text("# Wrong base\n", encoding="utf-8")
result = run_audit(project, "modules", modules=("src/inventory",))
assert result.returncode == 1, result.stdout + result.stderr
assert "Broken Markdown link: 'src/inventory/CONTEXT.md' -> '../contract.md'" in result.stdout
assert "AGENTS.md' ->" not in result.stdout
def test_module_audit_requires_explicit_scope_and_boundary(project: Path) -> None:
missing = run_audit(project, "modules")
ignored = run_audit(project, "context", modules=("inventory",))
assert missing.returncode == ignored.returncode == 2
assert "requires at least one --module" in missing.stderr
assert "supported only with --scope modules or full" in ignored.stderr
def test_module_audit_checks_each_selected_boundary_once(project: Path) -> None:
for module in ("inventory", "billing"):
(project / module).mkdir()
result = run_audit(project, "modules", modules=("inventory", "billing", "inventory"))
assert result.returncode == 0, result.stdout + result.stderr
assert result.stdout.count("Selected module: 'inventory'") == 1
assert result.stdout.count("Selected module: 'billing'") == 1
@pytest.mark.parametrize("kind", ("directory", "broken-symlink"))
def test_module_audit_reports_non_file_carriers(project: Path, kind: str) -> None:
(project / "inventory").mkdir()
carrier = project / "inventory/AGENTS.md"
if kind == "directory":
carrier.mkdir()
else:
carrier.symlink_to(project / "missing.md")
result = run_audit(project, "modules", modules=("inventory",))
assert result.returncode == 1, result.stdout + result.stderr
assert "repository-contained regular file" in result.stdout
def test_module_audit_rejects_escaping_module_directory(project: Path) -> None:
(project / "escape").symlink_to(project.parent, target_is_directory=True)
result = run_audit(project, "modules", modules=("escape",))
assert result.returncode == 1, result.stdout + result.stderr
assert "Module must be a repository-relative directory inside root" in result.stdout
def configure_git(project: Path) -> None:
subprocess.run(["git", "init"], cwd=project, capture_output=True, check=True)
subprocess.run(