ddduck 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +125 -0
- package/docs/architecture.md +77 -0
- package/docs/cli.md +239 -0
- package/docs/getting-started.md +98 -0
- package/docs/model-reference.md +92 -0
- package/docs/model.md +75 -0
- package/package.json +76 -0
- package/policies/concept-owner-domain.yaml +8 -0
- package/policies/documentation-model-reference-resolution.yaml +8 -0
- package/policies/no-dangling-model-reference.yaml +8 -0
- package/policies/policy-spec.schema.json +23 -0
- package/schemas/context-pack.schema.json +83 -0
- package/schemas/fr-to-code-audit.schema.json +85 -0
- package/schemas/product/concept.schema.json +17 -0
- package/schemas/product/domain-interface.schema.json +18 -0
- package/schemas/product/domain.schema.json +24 -0
- package/schemas/product/evidence-anchor.schema.json +30 -0
- package/schemas/product/guarantee.schema.json +35 -0
- package/schemas/product/model.schema.json +20 -0
- package/schemas/product/relationship.schema.json +21 -0
- package/schemas/product/use-case.schema.json +27 -0
- package/scripts/audit-fr-to-code.mjs +162 -0
- package/scripts/check-generated-docs.mjs +58 -0
- package/scripts/check-generated-graph-svg.mjs +60 -0
- package/scripts/check-generated-graph.mjs +66 -0
- package/scripts/check-model.mjs +488 -0
- package/scripts/ddduck.mjs +542 -0
- package/scripts/generate-agent-readiness-report.mjs +23 -0
- package/scripts/generate-docs.mjs +205 -0
- package/scripts/generate-graph-svg.mjs +359 -0
- package/scripts/generate-graph.mjs +268 -0
- package/scripts/lib/agent-readiness-evals.mjs +433 -0
- package/scripts/lib/agent-readiness-report.mjs +79 -0
- package/scripts/lib/cli-contract.mjs +162 -0
- package/scripts/lib/context-pack.mjs +107 -0
- package/scripts/lib/ddduck-config.mjs +57 -0
- package/scripts/lib/fr-to-code-audit.mjs +144 -0
- package/scripts/lib/product-layout.mjs +93 -0
- package/scripts/lib/product-operation.mjs +431 -0
- package/scripts/lib/product-paths.mjs +43 -0
- package/scripts/lib/product-query.mjs +284 -0
- package/scripts/lib/product-root-resolver.mjs +167 -0
- package/scripts/lib/scan-ignore.mjs +8 -0
- package/scripts/lib/skill-installer.mjs +410 -0
- package/scripts/query-model.mjs +64 -0
- package/scripts/run-agent-readiness-evals.mjs +57 -0
- package/skills/update-ddduck-specs/SKILL.md +98 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 diegomarino
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# ddduck
|
|
2
|
+
|
|
3
|
+
ddduck defines a small, machine-readable product-spec format. The framework keeps
|
|
4
|
+
schemas, validators, policies, and generators separate from each consumer product.
|
|
5
|
+
|
|
6
|
+
Start with [the getting-started guide](docs/getting-started.md) for a working first product.
|
|
7
|
+
Then use [the model guide](docs/model.md), [the model reference](docs/model-reference.md),
|
|
8
|
+
[the CLI reference](docs/cli.md), and [the architecture guide](docs/architecture.md) as needed.
|
|
9
|
+
|
|
10
|
+
## Model graph
|
|
11
|
+
|
|
12
|
+
Every product renders to a verified graph of its model — the ownership spine
|
|
13
|
+
(Model → Domain → members), reference edges, and a legend decoding shapes and
|
|
14
|
+
line styles. `ddduck generate` produces it as a deterministic SVG. This
|
|
15
|
+
framework's own model:
|
|
16
|
+
|
|
17
|
+

|
|
18
|
+
|
|
19
|
+
## Product layout
|
|
20
|
+
|
|
21
|
+
A consumer product owns one product root. Documentation-centric repositories can use `docs/ddd/`;
|
|
22
|
+
small or dedicated product repositories can use `ddd/`:
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
docs/ddd/
|
|
26
|
+
product.yaml
|
|
27
|
+
model/
|
|
28
|
+
domains/
|
|
29
|
+
concepts/
|
|
30
|
+
relationships/
|
|
31
|
+
use-cases/
|
|
32
|
+
interfaces/
|
|
33
|
+
guarantees/
|
|
34
|
+
decisions/
|
|
35
|
+
generated/
|
|
36
|
+
docs/
|
|
37
|
+
graph/
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`product.yaml` and files under `model/` are canonical source. `generated/` is derived
|
|
41
|
+
output: never edit it by hand.
|
|
42
|
+
|
|
43
|
+
Repository-local ddduck tool metadata lives outside the product root in `.ddduck/`. For example,
|
|
44
|
+
this framework repository stores its own model in `docs/ddd/` and records that selection in:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"schemaVersion": "1",
|
|
49
|
+
"productRoot": "docs/ddd",
|
|
50
|
+
"ignore": ["vendor", "target", "build", "dist", "__pycache__"]
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`ignore` lists directory names the scanners skip on top of the always-ignored
|
|
55
|
+
dot-directories and `node_modules`; it is written pre-filled by `ddduck init`
|
|
56
|
+
and fully editable (see [the CLI reference](docs/cli.md)).
|
|
57
|
+
|
|
58
|
+
## Authoring
|
|
59
|
+
|
|
60
|
+
Install the CLI from npm:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npm install -g ddduck
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Or run it from a local checkout for contributing (`npm install`, then `npm link` or
|
|
67
|
+
`node <checkout>/scripts/ddduck.mjs` — see
|
|
68
|
+
[Install ddduck](docs/getting-started.md#install-ddduck)). Create a product root with:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
ddduck init ddd --id model:<product-id>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`--root <path>` is always the explicit override; without it, ddduck resolves the enclosing
|
|
75
|
+
product root, then `.ddduck/config.json`, then a unique repository candidate, and fails with a
|
|
76
|
+
diagnostic when the choice is ambiguous. The mutation surface is deliberately narrow — `check`,
|
|
77
|
+
`generate`, and the `create`/`move`/`split`/`retire` Guarantee lifecycle commands — and every
|
|
78
|
+
successful source mutation regenerates the derived views. The canonical resolution rules and
|
|
79
|
+
command contracts live in [the CLI reference](docs/cli.md#product-root-resolution).
|
|
80
|
+
|
|
81
|
+
YAML remains the normal human-authored source format, with `ddduck check` as the deterministic
|
|
82
|
+
safety net.
|
|
83
|
+
|
|
84
|
+
## Agent skill
|
|
85
|
+
|
|
86
|
+
Install the evidence-backed model maintenance skill in a consumer repository:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
ddduck install skill update-ddduck-specs --repo <repository-root>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`--repo` defaults to the current directory. See [the CLI reference](docs/cli.md#install-an-agent-skill)
|
|
93
|
+
for the installed layout, host invocation, and plan-only behavior.
|
|
94
|
+
|
|
95
|
+
## Product queries
|
|
96
|
+
|
|
97
|
+
The bounded read API — `ddduck query node|neighbors|impact|anchors|spec|context` — emits JSON
|
|
98
|
+
only (`--json` is optional and a no-op) and never writes source or generated output. Default
|
|
99
|
+
queries expose only active Guarantees; pass `--history` to read a retired or split Guarantee's
|
|
100
|
+
historical record instead of its lifecycle redirect. The full query table and response contract
|
|
101
|
+
live in [the CLI reference](docs/cli.md#queries).
|
|
102
|
+
|
|
103
|
+
### Bounded context packs
|
|
104
|
+
|
|
105
|
+
`ddduck query context` is the agent-facing bounded read for an explicit selection: full
|
|
106
|
+
canonical records for the selected IDs, every direct edge that touches them, one-hop perimeter
|
|
107
|
+
summaries, and a SHA-256 `sourceDigest` identifying the source snapshot — authoritative only
|
|
108
|
+
for reads that did not race a mutation. Its boundaries and refusal rules are documented in
|
|
109
|
+
[the CLI reference](docs/cli.md#queries); responses conform to
|
|
110
|
+
[`schemas/context-pack.schema.json`](schemas/context-pack.schema.json).
|
|
111
|
+
|
|
112
|
+
## Agent-readiness evals
|
|
113
|
+
|
|
114
|
+
Maintainer surface: see
|
|
115
|
+
[Agent-readiness evals in the development guide](https://github.com/diegomarino/ddduck/blob/main/docs/development.md#agent-readiness-evals).
|
|
116
|
+
|
|
117
|
+
## Revision-scoped FR-to-code audits
|
|
118
|
+
|
|
119
|
+
Maintainer surface: see
|
|
120
|
+
[Revision-scoped FR-to-code audits in the development guide](https://github.com/diegomarino/ddduck/blob/main/docs/development.md#revision-scoped-fr-to-code-audits).
|
|
121
|
+
|
|
122
|
+
## Framework contracts
|
|
123
|
+
|
|
124
|
+
Global `PolicySpec` declarations live in `policies/`, outside any product graph: see
|
|
125
|
+
[Framework contracts in the development guide](https://github.com/diegomarino/ddduck/blob/main/docs/development.md#framework-contracts).
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
ddduck keeps product facts separate from framework contracts. Canonical product YAML is the
|
|
4
|
+
source of truth. Schemas, policies, validation, generation, and query code interpret that source;
|
|
5
|
+
they do not become product nodes. Generated views flow outward and are never canonical input.
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
flowchart LR
|
|
9
|
+
subgraph Product[Selected product root]
|
|
10
|
+
Source[product.yaml and model YAML]
|
|
11
|
+
Decisions[decisions]
|
|
12
|
+
Derived[generated views]
|
|
13
|
+
end
|
|
14
|
+
subgraph Framework[ddduck framework]
|
|
15
|
+
Schemas[Schemas and policies]
|
|
16
|
+
Check[Validation]
|
|
17
|
+
Generate[Generators]
|
|
18
|
+
Query[JSON query]
|
|
19
|
+
end
|
|
20
|
+
Source --> Check
|
|
21
|
+
Decisions --> Check
|
|
22
|
+
Schemas --> Check
|
|
23
|
+
Check --> Generate
|
|
24
|
+
Generate --> Derived
|
|
25
|
+
Source --> Query
|
|
26
|
+
Decisions --> Query
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Authoring and verification
|
|
30
|
+
|
|
31
|
+
After manual canonical YAML edits, authors validate source with `ddduck check --source-only`, run
|
|
32
|
+
`ddduck generate`, then run default `ddduck check`. Default `check` verifies source and that
|
|
33
|
+
required generated views are fresh, so it cannot precede generation after a source edit.
|
|
34
|
+
|
|
35
|
+
```mermaid
|
|
36
|
+
flowchart LR
|
|
37
|
+
Edit[Author canonical YAML] --> SourceCheck[ddduck check --source-only]
|
|
38
|
+
SourceCheck -->|valid| Generate[ddduck generate]
|
|
39
|
+
SourceCheck -->|invalid| Fix[Fix diagnostics]
|
|
40
|
+
Generate --> FreshCheck[ddduck check]
|
|
41
|
+
FreshCheck --> Views[Fresh Markdown and graph views]
|
|
42
|
+
Fix --> Edit
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Staged lifecycle mutation
|
|
46
|
+
|
|
47
|
+
Guarantee mutations run against a contained staging copy. The CLI validates the complete staged
|
|
48
|
+
product and regenerated views before publication. A rejected operation publishes no intended
|
|
49
|
+
canonical or generated change; filesystem interruption beyond that tested boundary is a recovery
|
|
50
|
+
concern, not a database transaction guarantee.
|
|
51
|
+
|
|
52
|
+
```mermaid
|
|
53
|
+
flowchart LR
|
|
54
|
+
Request[create move split or retire] --> Lock[Acquire product lock]
|
|
55
|
+
Lock --> Stage[Copy canonical source to staging]
|
|
56
|
+
Stage --> Validate[Transform, validate, generate]
|
|
57
|
+
Validate -->|valid| Publish[Publish canonical and generated views]
|
|
58
|
+
Validate -->|invalid| Cleanup[Discard staging and release lock]
|
|
59
|
+
Publish --> Release[Release lock and report result]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Query boundary
|
|
63
|
+
|
|
64
|
+
Queries are read-only and require `--json`. A context query returns selected canonical records,
|
|
65
|
+
direct touching edges, one-hop summaries for unselected neighbors, and a source digest. It does
|
|
66
|
+
not recursively expand context or write canonical or generated files.
|
|
67
|
+
|
|
68
|
+
```mermaid
|
|
69
|
+
sequenceDiagram
|
|
70
|
+
participant Caller
|
|
71
|
+
participant CLI as ddduck query
|
|
72
|
+
participant Product as Canonical product root
|
|
73
|
+
Caller->>CLI: query context --id ... --root ... --json
|
|
74
|
+
CLI->>Product: Load and resolve selected source
|
|
75
|
+
CLI-->>Caller: Selected records, edges, summaries, digest
|
|
76
|
+
Note over CLI,Caller: No source or generated output is written
|
|
77
|
+
```
|
package/docs/cli.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# CLI reference
|
|
2
|
+
|
|
3
|
+
`ddduck` operates on a selected product root. Run `ddduck --help`, or pass `--help` anywhere
|
|
4
|
+
after a command name (including `ddduck <command> <subcommand> --help`), for the built-in usage
|
|
5
|
+
lines. Option values may be written as `--option value` or `--option=value`; a value that begins
|
|
6
|
+
with `--` must use the `--option=value` form. Every command rejects unknown options, duplicate
|
|
7
|
+
options, missing option values, and unexpected positional arguments. Expected failures write a
|
|
8
|
+
concise diagnostic (multi-error validation reports keep one line per error) plus a safe next
|
|
9
|
+
action to standard error and exit nonzero; errors without a specific next action fall back to a
|
|
10
|
+
command-specific hint. Help exits zero.
|
|
11
|
+
|
|
12
|
+
## Product root resolution
|
|
13
|
+
|
|
14
|
+
Product-facing commands accept `--root <product-root>`. When `--root` is omitted, ddduck resolves
|
|
15
|
+
the root in this order:
|
|
16
|
+
|
|
17
|
+
1. An enclosing product root found by walking upward from the current directory.
|
|
18
|
+
2. `.ddduck/config.json` `productRoot`, when present and the current directory is not inside a
|
|
19
|
+
product root.
|
|
20
|
+
3. A unique primary `product.yaml` candidate under the repository boundary.
|
|
21
|
+
4. A relevant example candidate only when no primary candidate exists.
|
|
22
|
+
|
|
23
|
+
Discovery admits a candidate by shape — a ddduck `product.yaml` with a sibling `model/`
|
|
24
|
+
directory — not by full validation, so a broken product still resolves and the command's own
|
|
25
|
+
validation reports its errors. Discovery excludes `.git/`, `.ddduck/`, `node_modules/`,
|
|
26
|
+
`generated/`, `.superpowers/`, `.worktrees/`, `.ddduck-init-stage-*` staging left by an
|
|
27
|
+
interrupted `init` (the next `init` of the same destination sweeps that debris; staging for
|
|
28
|
+
other destinations is never touched), and test fixtures. Multiple
|
|
29
|
+
viable primary roots
|
|
30
|
+
fail and ask for `--root`, listing every candidate and flagging any that fail validation; the
|
|
31
|
+
resolver never uses an arbitrary first match.
|
|
32
|
+
|
|
33
|
+
Use `.ddduck/config.json` for a repository default:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"schemaVersion": "1",
|
|
38
|
+
"productRoot": "docs/ddd",
|
|
39
|
+
"ignore": ["vendor", "target", "build", "dist", "__pycache__"]
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`ignore` lists extra directory names (basenames only, no globs) that
|
|
44
|
+
every ddduck scanner skips, in addition to the always-skipped dot-directories
|
|
45
|
+
and `node_modules`. Omit the key to accept the defaults shown above; set it to
|
|
46
|
+
`[]` to skip nothing beyond the built-in defaults. `ddduck init` writes this
|
|
47
|
+
file pre-filled when it does not already exist.
|
|
48
|
+
|
|
49
|
+
## Install an agent skill
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
ddduck install skill update-ddduck-specs [--repo <repository-root>]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`--repo` defaults to the current directory. The installer chooses the least intrusive host
|
|
56
|
+
topology from the repository's existing directories:
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
no .agents/ or .claude/ -> .agents/skills/update-ddduck-specs/SKILL.md
|
|
60
|
+
.agents/ only -> .agents/skills/update-ddduck-specs/SKILL.md
|
|
61
|
+
.claude/ only -> .claude/skills/update-ddduck-specs/SKILL.md
|
|
62
|
+
.agents/ and .claude/ -> .agents/skills/update-ddduck-specs/SKILL.md
|
|
63
|
+
.claude/skills/update-ddduck-specs -> ../../.agents/skills/update-ddduck-specs
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The installer writes `.ddduck/agent-skills.lock.json` with the selected canonical path, host
|
|
67
|
+
adapters, package version, and installed `SKILL.md` SHA-256. It does not create a host directory
|
|
68
|
+
for a host that is absent from the repository, except for the `.agents/` fallback when no host
|
|
69
|
+
directory exists. On success it prints one result line naming the action (`created`, `upgraded`,
|
|
70
|
+
or `no-op`), the repository, the canonical skill path, and the lock path. It does not accept
|
|
71
|
+
`--json`.
|
|
72
|
+
|
|
73
|
+
Invoke the skill from the relevant host:
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
Codex: $update-ddduck-specs
|
|
77
|
+
Claude: /update-ddduck-specs
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The skill defaults to plan-only; changing a product model requires explicit apply authorization.
|
|
81
|
+
Its workflow behavior is defined by the installed `SKILL.md` (and its packaged
|
|
82
|
+
[canonical source](../skills/update-ddduck-specs/SKILL.md)).
|
|
83
|
+
|
|
84
|
+
This package is published to npm as `ddduck`; install the CLI globally with `npm install -g ddduck`
|
|
85
|
+
(see [the getting-started guide](getting-started.md#install-ddduck)).
|
|
86
|
+
|
|
87
|
+
## Common behavior
|
|
88
|
+
|
|
89
|
+
Product writes are `init`, `generate`, `create`, `move`, `split`, and `retire`; successful
|
|
90
|
+
mutations regenerate all required views. `check` and every `query` are read-only. A successful
|
|
91
|
+
`check` is silent. Successful product mutations print one concise result line, or one JSON result
|
|
92
|
+
object when `--json` is available.
|
|
93
|
+
|
|
94
|
+
## `init`
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
ddduck init [destination] --id model:<product-id> [--json]
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
| Option or argument | Required | Default | Meaning |
|
|
101
|
+
| ------------------ | -------- | ----------------------------- | ------------------------------------------------- |
|
|
102
|
+
| `destination` | no | config, then `ddd/` directory | Empty directory to create as the product root. |
|
|
103
|
+
| `--id` | yes | none | Root Model ID, matching `model:<lowercase-slug>`. |
|
|
104
|
+
| `--json` | no | false | Emit one JSON result object instead of text. |
|
|
105
|
+
|
|
106
|
+
`init` writes the canonical directory layout, `product.yaml`, and fresh generated docs and graph
|
|
107
|
+
views. It refuses a non-empty destination. On success it reports the Model ID, normalized root,
|
|
108
|
+
`product.yaml`, and all generated paths as one text line or, with `--json`, one object containing
|
|
109
|
+
`operation`, `root`, `affectedIds`, `canonicalPaths`, and `generatedPaths`. On failure it exits
|
|
110
|
+
nonzero without reporting success.
|
|
111
|
+
|
|
112
|
+
## `check`
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
ddduck check [--root <product-root>] [--base <previous-product-root>] \
|
|
116
|
+
[--docs-root <docs-root> ...] [--source-only]
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
| Option | Default | Meaning |
|
|
120
|
+
| --------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
121
|
+
| `--root` | resolved root | Product root to validate. |
|
|
122
|
+
| `--base` | none | Previous product root used to check historical Guarantee retention. |
|
|
123
|
+
| `--docs-root` | product root | Directory scanned for documentation references; repeatable. Passing it replaces the default product-root scope. |
|
|
124
|
+
| `--source-only` | false | Validate canonical source only; skip derived-view freshness checks and skip documentation references inside `generated/`. |
|
|
125
|
+
|
|
126
|
+
By default, `check` validates canonical source, then requires fresh generated Markdown and graph
|
|
127
|
+
views. Documentation references are validated only inside the product root; pass one or more
|
|
128
|
+
`--docs-root` directories to widen (and replace) that scope, mirroring the framework's own
|
|
129
|
+
repository-wide gate. It writes nothing and is quiet on success. It exits nonzero for invalid source, stale
|
|
130
|
+
views, invalid roots, invalid options, a busy root (`.ddduck-operation.lock` held by a live
|
|
131
|
+
ddduck operation), or leftover state from an interrupted operation
|
|
132
|
+
(`.ddduck-operation.lock`, `.ddduck-operation.reclaim`, or `.ddduck-operation-stage-*` with no
|
|
133
|
+
live owning process); running any mutation, such as `ddduck generate`, reclaims that leftover
|
|
134
|
+
state.
|
|
135
|
+
|
|
136
|
+
## `generate`
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
ddduck generate [--root <product-root>] [--json]
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
| Option | Default | Meaning |
|
|
143
|
+
| -------- | ------------- | --------------------------------------------------------------------- |
|
|
144
|
+
| `--root` | resolved root | Product root to validate and regenerate. |
|
|
145
|
+
| `--json` | false | Emit one JSON mutation-result object instead of the text result line. |
|
|
146
|
+
|
|
147
|
+
`generate` validates canonical source before writing `generated/docs/model-overview.md`,
|
|
148
|
+
`generated/graph/model-graph.json`, and `generated/graph/model-graph.ndjson`. A successful text
|
|
149
|
+
result identifies the root, canonical paths (none for generate), and generated paths. It exits
|
|
150
|
+
nonzero without an intended product mutation if validation or contained-output checks fail.
|
|
151
|
+
|
|
152
|
+
## Guarantee mutations
|
|
153
|
+
|
|
154
|
+
All mutation results identify the selected root, affected Guarantee IDs, canonical paths, and
|
|
155
|
+
fresh generated paths. Append `--json` to any command below for one JSON result object.
|
|
156
|
+
|
|
157
|
+
### `create guarantee`
|
|
158
|
+
|
|
159
|
+
```text
|
|
160
|
+
ddduck create guarantee --origin <origin> --classification <invariant|acceptance-criterion> \
|
|
161
|
+
--owner <domain-id> --statement <text> [--root <product-root>] [--json]
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`--origin`, `--classification`, `--owner`, and `--statement` are required. The root is resolved
|
|
165
|
+
through the common product-root rules. `create` allocates the next stable ID for the origin and
|
|
166
|
+
classification, writes its canonical Guarantee YAML, updates the owning Domain, and regenerates
|
|
167
|
+
views. It exits nonzero if the owner is unknown or any input or staged product is invalid.
|
|
168
|
+
|
|
169
|
+
### `move guarantee`
|
|
170
|
+
|
|
171
|
+
```text
|
|
172
|
+
ddduck move guarantee <guarantee-id> --to <domain-id> [--root <product-root>] [--json]
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`<guarantee-id>` and `--to` are required. `move` requires an active Guarantee and an existing
|
|
176
|
+
destination Domain. It changes ownership, records the prior owner in `ownershipHistory`, updates
|
|
177
|
+
both Domain records, and regenerates views. It exits nonzero without an intended mutation when
|
|
178
|
+
the Guarantee is inactive, missing, already owned by the destination, or the staged product fails.
|
|
179
|
+
|
|
180
|
+
### `split guarantee`
|
|
181
|
+
|
|
182
|
+
```text
|
|
183
|
+
ddduck split guarantee <guarantee-id> --into <successor-id[,successor-id ...]> \
|
|
184
|
+
--decision ADR-NNN [--root <product-root>] [--json]
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
`<guarantee-id>`, `--into`, and `--decision` are required. `--into` accepts one or more distinct
|
|
188
|
+
active successor IDs; the decision must resolve in the product decision registry.
|
|
189
|
+
`split` preserves the source ID, marks it `split`, records its successors and lifecycle decision,
|
|
190
|
+
then regenerates views. It exits nonzero without an intended mutation if an active UseCase or
|
|
191
|
+
DomainInterface would retain a reference to the non-effective Guarantee.
|
|
192
|
+
|
|
193
|
+
### `retire guarantee`
|
|
194
|
+
|
|
195
|
+
```text
|
|
196
|
+
ddduck retire guarantee <guarantee-id> --decision ADR-NNN [--root <product-root>] [--json]
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
`<guarantee-id>` and `--decision` are required. `retire` requires an active Guarantee and a
|
|
200
|
+
registered decision, marks the Guarantee `retired`, records `lifecycleDecision`, and regenerates
|
|
201
|
+
views. The same active UseCase and DomainInterface safety gate applies. Invalid input or a failed
|
|
202
|
+
staged validation exits nonzero without an intended mutation.
|
|
203
|
+
|
|
204
|
+
## Queries
|
|
205
|
+
|
|
206
|
+
Every query emits exactly one JSON document and writes no source or generated output. While a
|
|
207
|
+
live ddduck mutation holds `.ddduck-operation.lock`, queries and `check` fail with a busy
|
|
208
|
+
diagnostic instead of reading a partially published snapshot. A read racing the very start of a
|
|
209
|
+
mutation, before the lock exists, may still observe a partial snapshot, so `sourceDigest` is
|
|
210
|
+
authoritative only for reads that did not race a mutation. JSON is
|
|
211
|
+
the only output format, so `--json` is optional and accepted as a no-op for compatibility. The
|
|
212
|
+
document contains `schemaVersion`, `query`, `rootModelId`, `result`, and
|
|
213
|
+
`diagnostics`. Non-context query roots are resolved through the common product-root rules.
|
|
214
|
+
`context` requires an explicit `--root`. Non-context queries accept optional `--history`; without it, a split or retired
|
|
215
|
+
Guarantee resolves to its lifecycle redirect rather than its historical contract.
|
|
216
|
+
|
|
217
|
+
```mermaid
|
|
218
|
+
sequenceDiagram
|
|
219
|
+
participant Caller
|
|
220
|
+
participant CLI as ddduck query
|
|
221
|
+
participant Model as Canonical product root
|
|
222
|
+
Caller->>CLI: query context --id ... --root ... --json
|
|
223
|
+
CLI->>Model: Load and resolve selected source
|
|
224
|
+
CLI-->>Caller: Full selection, direct edges, summaries, digest
|
|
225
|
+
Note over CLI,Caller: Read-only; no source or generated output is written
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
| Command | Required options | Result |
|
|
229
|
+
| -------------------------------------------------------------------------- | ---------------- | ----------------------------------------------------------------------- |
|
|
230
|
+
| `ddduck query node --id <id> [--root <root>] [--history] [--json]` | `--id` | Full canonical node and product-relative source path. |
|
|
231
|
+
| `ddduck query neighbors --id <id> [--root <root>] [--history] [--json]` | `--id` | Deterministically sorted incoming and outgoing edges. |
|
|
232
|
+
| `ddduck query impact --id <id> [--root <root>] [--history] [--json]` | `--id` | Reverse impact closure over ownership and behavioral references. |
|
|
233
|
+
| `ddduck query anchors --id <id> [--root <root>] [--history] [--json]` | `--id` | Evidence, reachable decisions, policies, and view freshness. |
|
|
234
|
+
| `ddduck query spec [--id <model-id>] [--root <root>] [--history] [--json]` | none | Root, domains, view freshness, and verification commands. |
|
|
235
|
+
| `ddduck query context --id <id> [--id <id> ...] --root <root> [--json]` | `--id`, `--root` | Selected records, touching edges, one-hop summaries, and source digest. |
|
|
236
|
+
|
|
237
|
+
`context` accepts one or more distinct, repeatable `--id` options; it rejects `--history`.
|
|
238
|
+
Its selected records are complete, while unselected endpoints appear only as one-hop summaries.
|
|
239
|
+
The command does not recursively expand the perimeter.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
This executable journey creates a product with one Domain, one Concept, and one Guarantee.
|
|
4
|
+
|
|
5
|
+
## Install ddduck
|
|
6
|
+
|
|
7
|
+
Install the published CLI globally from npm:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install -g ddduck
|
|
11
|
+
ddduck --help
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
To contribute or run an unreleased revision, work from a local checkout instead. Either put the
|
|
15
|
+
`ddduck` bin on `PATH` with `npm link`:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
git clone <ddduck-repository-url> ddduck
|
|
19
|
+
cd ddduck
|
|
20
|
+
npm install
|
|
21
|
+
npm link
|
|
22
|
+
ddduck --help
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
or skip linking and invoke the CLI directly from the checkout:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
node <checkout>/scripts/ddduck.mjs --help
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Create the first product
|
|
32
|
+
|
|
33
|
+
Run the complete Bash block from an empty working directory with `ddduck` on `PATH`.
|
|
34
|
+
|
|
35
|
+
```mermaid
|
|
36
|
+
flowchart LR
|
|
37
|
+
Source[Manual canonical YAML edit] --> SourceCheck[ddduck check --source-only]
|
|
38
|
+
SourceCheck -->|valid| Generate[ddduck generate]
|
|
39
|
+
SourceCheck -->|invalid| Diagnostic[Actionable diagnostic]
|
|
40
|
+
Generate --> FreshCheck[ddduck check]
|
|
41
|
+
FreshCheck --> Docs[Generated Markdown is fresh]
|
|
42
|
+
FreshCheck --> Graph[Generated graph is fresh]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
ddduck init ddd --id model:library
|
|
47
|
+
|
|
48
|
+
cat > ddd/model/domains/catalog.yaml <<'YAML'
|
|
49
|
+
schemaVersion: "1"
|
|
50
|
+
kind: Domain
|
|
51
|
+
id: domain:catalog
|
|
52
|
+
model: model:library
|
|
53
|
+
name: Catalog
|
|
54
|
+
purpose: Organize the library catalog.
|
|
55
|
+
concepts:
|
|
56
|
+
- concept:book
|
|
57
|
+
interfaces: []
|
|
58
|
+
guarantees: []
|
|
59
|
+
YAML
|
|
60
|
+
|
|
61
|
+
cat > ddd/model/concepts/book.yaml <<'YAML'
|
|
62
|
+
schemaVersion: "1"
|
|
63
|
+
kind: Concept
|
|
64
|
+
id: concept:book
|
|
65
|
+
model: model:library
|
|
66
|
+
ownerDomain: domain:catalog
|
|
67
|
+
name: Book
|
|
68
|
+
purpose: Identify a catalogued book.
|
|
69
|
+
YAML
|
|
70
|
+
|
|
71
|
+
cat > ddd/product.yaml <<'YAML'
|
|
72
|
+
schemaVersion: "1"
|
|
73
|
+
kind: Model
|
|
74
|
+
id: model:library
|
|
75
|
+
name: library
|
|
76
|
+
purpose: Define the library product.
|
|
77
|
+
domains:
|
|
78
|
+
- domain:catalog
|
|
79
|
+
useCases: []
|
|
80
|
+
decisions: []
|
|
81
|
+
YAML
|
|
82
|
+
|
|
83
|
+
ddduck check --root ddd --source-only
|
|
84
|
+
ddduck generate --root ddd
|
|
85
|
+
ddduck check --root ddd
|
|
86
|
+
ddduck create guarantee --origin catalog --classification invariant \
|
|
87
|
+
--owner domain:catalog --statement "A Book has a stable catalog identity." --root ddd
|
|
88
|
+
ddduck query spec --root ddd --json
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Default `check` requires both valid canonical source and fresh generated views. After a manual
|
|
92
|
+
source edit, use `check --source-only`, run `generate`, then use default `check`. Do not edit
|
|
93
|
+
`generated/` by hand.
|
|
94
|
+
|
|
95
|
+
The successful `create` allocates the first catalog invariant serial, adds it to the Domain, and
|
|
96
|
+
refreshes all generated views. The final query emits one JSON document suitable for a tool or
|
|
97
|
+
agent. See the [model reference](model-reference.md) before adding other node kinds, and use the
|
|
98
|
+
[CLI reference](cli.md) for the complete command contracts.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Model reference
|
|
2
|
+
|
|
3
|
+
Canonical YAML uses schema version `"1"`. IDs are lowercase, hyphenated after their structural
|
|
4
|
+
prefix, except stable Guarantee IDs and decision IDs such as `ADR-001`. A Guarantee ID is keyed
|
|
5
|
+
to its classification: `<ORIGIN>-INV-<serial>` for an `invariant`, `<ORIGIN>-AC-<serial>` for an
|
|
6
|
+
`acceptance-criterion`.
|
|
7
|
+
The checker requires exactly one Model root.
|
|
8
|
+
|
|
9
|
+
## Model root
|
|
10
|
+
|
|
11
|
+
`product.yaml` is the only `Model` node. Unlike every child node, it has no `model` field.
|
|
12
|
+
Required fields are `schemaVersion`, `kind`, `id`, `name`, `purpose`, and `domains`. Optional
|
|
13
|
+
top-level fields include `nameStatus`, `useCases`, `relationships`, `decisions`, and `notes`.
|
|
14
|
+
|
|
15
|
+
```yaml
|
|
16
|
+
schemaVersion: "1"
|
|
17
|
+
kind: Model
|
|
18
|
+
id: model:<product-slug>
|
|
19
|
+
name: A product name
|
|
20
|
+
purpose: Define the product.
|
|
21
|
+
domains:
|
|
22
|
+
- domain:<domain-slug>
|
|
23
|
+
useCases: []
|
|
24
|
+
decisions: []
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Child-node fields
|
|
28
|
+
|
|
29
|
+
All child nodes include `schemaVersion`, `kind`, `id`, and `model`. `model` must name the root
|
|
30
|
+
Model ID. `Domain`, `Concept`, and `DomainInterface` respectively use `domain:`, `concept:`, and
|
|
31
|
+
`interface:` IDs. A child owned by a Domain uses `ownerDomain`; the owning Domain must also list
|
|
32
|
+
the child in the matching collection.
|
|
33
|
+
|
|
34
|
+
### Domain
|
|
35
|
+
|
|
36
|
+
Required: `name`, `purpose`. Optional owned lists: `concepts`, `excludedConcepts`, `interfaces`,
|
|
37
|
+
`guarantees`, and `decisions`.
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
schemaVersion: "1"
|
|
41
|
+
kind: Domain
|
|
42
|
+
id: domain:<domain-slug>
|
|
43
|
+
model: model:<product-slug>
|
|
44
|
+
name: A domain name
|
|
45
|
+
purpose: Describe the domain responsibility.
|
|
46
|
+
concepts: []
|
|
47
|
+
interfaces: []
|
|
48
|
+
guarantees: []
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Concept
|
|
52
|
+
|
|
53
|
+
Required: `ownerDomain`, `name`, `purpose`. Optional: `decisions`.
|
|
54
|
+
|
|
55
|
+
```yaml
|
|
56
|
+
schemaVersion: "1"
|
|
57
|
+
kind: Concept
|
|
58
|
+
id: concept:<concept-slug>
|
|
59
|
+
model: model:<product-slug>
|
|
60
|
+
ownerDomain: domain:<domain-slug>
|
|
61
|
+
name: A concept name
|
|
62
|
+
purpose: Describe the concept responsibility.
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Relationship
|
|
66
|
+
|
|
67
|
+
Required: `from`, `to`, `relationshipType`, `mode`, and `ownedBy` (a Domain ID). Optional:
|
|
68
|
+
`description`, `constraints`, and `decisions`.
|
|
69
|
+
|
|
70
|
+
### UseCase
|
|
71
|
+
|
|
72
|
+
Required: `name`, `goal`, `preconditions`, and `success`. `preconditions.requires` is required;
|
|
73
|
+
`success` requires `preserves` and `establishes` lists. Optional: `interfaces` and evidence
|
|
74
|
+
anchors.
|
|
75
|
+
|
|
76
|
+
### DomainInterface
|
|
77
|
+
|
|
78
|
+
Required: `ownerDomain`, `name`, and `operationKind` (`command` or `query`). Optional:
|
|
79
|
+
`guarantees` and evidence anchors.
|
|
80
|
+
|
|
81
|
+
### Guarantee
|
|
82
|
+
|
|
83
|
+
Required: `ownerDomain`, `classification` (`invariant` or `acceptance-criterion`), `statement`,
|
|
84
|
+
and `status` (`active`, `split`, or `retired`). A split or retired Guarantee also requires a
|
|
85
|
+
`lifecycleDecision`; a split Guarantee names active `successors`. Optional `ownershipHistory`
|
|
86
|
+
records former owning Domains. Evidence anchors are also supported.
|
|
87
|
+
|
|
88
|
+
Evidence anchors contain a product-relative `path`, `anchor`, and `role` (`source`, `decision`,
|
|
89
|
+
or `verification`). The path must resolve to a regular file inside the selected product root.
|
|
90
|
+
|
|
91
|
+
For full schema constraints, inspect the shipped files under `schemas/product/`; use
|
|
92
|
+
[the getting-started guide](getting-started.md) for the minimal working path.
|