Skip to content

Commit b6e025e

Browse files
committed
fix(artifacts): v0.3.1 — JSONB support, domain class diagram, configurable spec versions
Artifact generation: - Storage strategy detection (jsonb-aggregate vs normalized) for SQL projects - Three entity modes in data-model.mmd (physical, JSONB-embedded, reference-only) - Domain class diagram (model.mmd) — always generated alongside data-model - Configurable spec versions via artifacts.versions in config - Defaults updated: OpenAPI 3.1.0, AsyncAPI 3.0.0, JSON Schema 2020-12 - AsyncAPI template updated to 3.0 structure (channels/operations) Bug fixes: - SubagentStop hook: seniority prefix broke agent slug lookup - VERSION bumped to 0.3.1 Docs & tests: - Website artifacts page rewritten - Changelog v0.3.1 entry - 61 new test assertions (test-v031-artifacts.sh) - All 25 test suites pass
1 parent dc0a257 commit b6e025e

13 files changed

Lines changed: 515 additions & 112 deletions

File tree

.edikt/config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# edikt configuration
22
# This file configures edikt for the edikt project itself (dogfooding).
33

4-
edikt_version: "0.2.3"
4+
edikt_version: "0.3.1"
55
base: docs
66

77
stack: [] # edikt is pure markdown, no language stack

CHANGELOG.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,47 @@
11
# edikt changelog
22

3+
## v0.3.1 (2026-04-11)
4+
5+
### Bug fixes
6+
7+
- **Init: guidelines path.** `/edikt:init` now writes `paths.guidelines` correctly.
8+
- **VERSION stamp.** `VERSION` file updated to match release tag.
9+
- **PRINCIPAL prefix.** Compile output no longer prefixes directives with `PRINCIPAL:`.
10+
- **Review output.** `/edikt:sdlc:review` output formatting fixed.
11+
- **SubagentStop hook: seniority prefix.** The fallback agent detection pattern matched "As Principal Architect" → `principal-architect` instead of `architect`, breaking slug lookup and gate matching. Now extracts only the role word.
12+
- **Missing page.** Added `/edikt:guideline:compile` website page (was dead link).
13+
- **Test fixes.** All 25 suites pass after v0.3.0 regressions.
14+
15+
### Artifact generation: JSONB support and domain class diagram
16+
17+
`/edikt:sdlc:artifacts` now handles projects using JSONB aggregate storage (common DDD pattern in PostgreSQL) and generates a domain class diagram alongside the data model.
18+
19+
- **Storage strategy detection.** When DB type is `sql` or `mixed`, the command scans spec content and migrations for JSONB signals (`jsonb`, `json column`, `aggregate storage`, `embedded entity`, `nested entity`, etc.). Detected strategy is shown in the state checkpoint and routing output.
20+
- **Three entity modes in `data-model.mmd`.** When storage strategy is `jsonb-aggregate`, the ERD distinguishes physical tables (normal), JSONB-embedded entities (relationship label contains `jsonb`), and reference-only entities from external bounded contexts (relationship label contains `ref`). Makes nested structure visible instead of hiding it in JSONB column comments.
21+
- **Domain class diagram (`model.mmd`).** New artifact type, always generated alongside the data model regardless of DB type. Mermaid `classDiagram` showing aggregate roots, value objects, entities, inheritance, composition, and domain methods. Reviewed by the architect agent.
22+
23+
### Configurable artifact spec versions
24+
25+
Artifact templates now use configurable spec versions instead of hardcoded values. Defaults updated to latest stable:
26+
27+
| Format | Previous | Now (default) |
28+
|---|---|---|
29+
| OpenAPI | 3.0.0 | **3.1.0** |
30+
| AsyncAPI | 2.6.0 | **3.0.0** |
31+
| JSON Schema | draft-07 | **2020-12** |
32+
33+
Teams can pin older versions in `.edikt/config.yaml`:
34+
35+
```yaml
36+
artifacts:
37+
versions:
38+
openapi: "3.0.0" # pin for tooling compatibility
39+
asyncapi: "2.6.0" # pin if not ready for 3.0 breaking changes
40+
json_schema: "https://json-schema.org/draft/07/schema#"
41+
```
42+
43+
The AsyncAPI template was updated for the 3.0 structure (separate `channels` and `operations` blocks replacing `publish`/`subscribe`). When pinning `asyncapi: "2.6.0"`, the agent uses the 2.x structure.
44+
345
## v0.3.0 (2026-04-10)
446

547
### Project Adaptation (ADR-008, ADR-009)

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.2.3
1+
0.3.1

commands/gov/score.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,14 @@ Overall: {x}/10
147147
}
148148
```
149149

150+
### 8. Confirm
151+
152+
```
153+
✅ Governance scored: {overall}/10
154+
155+
Next: Run /edikt:gov:review to review language quality, or /edikt:invariant:review for per-artifact scoring.
156+
```
157+
150158
---
151159

152160
REMEMBER: This command scores the COMPILED output, not source documents. It answers: "How well will Claude follow our governance?" Run per-artifact reviews for source quality. Run this for the aggregate picture. Designed for CI — the `--json` output can be parsed by any CI tool.

commands/init.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,12 @@ artifacts:
800800
# Values: yaml | json | sql
801801
format: yaml
802802
803+
versions:
804+
# Spec versions for generated artifacts. Override if your team pins older versions.
805+
# openapi: "3.1.0" # default — OpenAPI spec version for contracts/api.yaml
806+
# asyncapi: "3.0.0" # default — AsyncAPI spec version for contracts/events.yaml
807+
# json_schema: "https://json-schema.org/draft/2020-12/schema" # default — JSON Schema URI for data-model.schema.yaml
808+
803809
sdlc:
804810
commit-convention: {choice or "none"}
805811
pr-template: {true/false}

commands/sdlc/artifacts.md

Lines changed: 138 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ And stop.
3232

3333
Read `.edikt/config.yaml`. Specs directory from `paths.specs` (default: `docs/product/specs/`). Invariants directory from `paths.invariants` (default: `docs/architecture/invariants/`).
3434

35+
**Artifact versions** — read from `artifacts.versions` in config. Use these defaults when a key is absent:
36+
37+
| Config key | Default | Used in |
38+
|---|---|---|
39+
| `artifacts.versions.openapi` | `3.1.0` | `contracts/api.yaml` |
40+
| `artifacts.versions.asyncapi` | `3.0.0` | `contracts/events.yaml` |
41+
| `artifacts.versions.json_schema` | `https://json-schema.org/draft/2020-12/schema` | `data-model.schema.yaml` |
42+
43+
Store the resolved values as OPENAPI_VERSION, ASYNCAPI_VERSION, JSON_SCHEMA_URI for use in artifact templates.
44+
3545
### 2. Resolve Context
3646

3747
Work through this checklist before proceeding. Record each value explicitly.
@@ -71,11 +81,20 @@ Work through this checklist before proceeding. Record each value explicitly.
7181
```
7282
- Build ACTIVE CONSTRAINTS block, or set to `none`
7383
84+
**STORAGE_STRATEGY** — resolve when DB_TYPE is `sql` or `mixed` (sql sub-type):
85+
1. Scan spec content for JSONB signals: `jsonb`, `json column`, `json field`, `aggregate storage`, `embedded entity`, `nested entity`, `document-in-relational`, `json aggregate`
86+
2. Scan any referenced migrations (if they exist in the spec folder) for: `jsonb`, `json NOT NULL`, `::json`
87+
3. If any signal found → `STORAGE_STRATEGY = jsonb-aggregate`. Note matched signals.
88+
4. If no signal found → `STORAGE_STRATEGY = normalized` (default)
89+
5. Skip this check entirely for non-sql DB types — set `STORAGE_STRATEGY = n/a`
90+
7491
**State checkpoint — confirm before proceeding:**
7592
```
7693
- [ ] DB_TYPE = {one of: sql | document-mongo | document-dynamo | key-value | mixed}
7794
source: {spec-frontmatter | config | keyword-scan | user}
7895
if mixed: list all detected sub-types
96+
- [ ] STORAGE_STRATEGY = {normalized | jsonb-aggregate | n/a}
97+
if jsonb-aggregate: list matched signals
7998
- [ ] CONSTRAINTS = {ACTIVE CONSTRAINTS block text, or "none"}
8099
- [ ] Spec status: accepted
81100
```
@@ -93,6 +112,7 @@ Apply these detection rules:
93112
| If the spec mentions... | Artifact | Primary agent | Secondary |
94113
|---|---|---|---|
95114
| database, model, schema, entity, table, column, field, relationship | *(see data model lookup table)* | dba | architect |
115+
| *(auto-triggers when data-model is generated)* | `model.mmd` *(domain class diagram)* | architect | dba |
96116
| API, endpoint, route, REST, GraphQL, request, response, contract | `contracts/api.yaml` | api | architect |
97117
| gRPC, protobuf, proto, service definition | `contracts/proto/` | api | engineer |
98118
| migration, schema change, ALTER, CREATE TABLE | `migrations/` *(sql or mixed only)* | dba | sre |
@@ -127,6 +147,7 @@ Show the list with checkmarks. Include DB_TYPE source warning when applicable:
127147
128148
```
129149
✓ data-model.mmd — sql detected via config (Mermaid ERD)
150+
✓ model.mmd — domain class diagram (auto, alongside data-model)
130151
✓ contracts/api.yaml — API endpoint references
131152
✓ test-strategy.md — testing strategy section
132153
✗ migrations/ — no schema changes
@@ -150,6 +171,7 @@ Each agent receives:
150171
- The source PRD content (read from `source_prd:` in spec frontmatter)
151172
- The project-context.md for project context
152173
- Any referenced ADRs
174+
- STORAGE_STRATEGY value (when generating data-model or model artifacts)
153175
- The ACTIVE CONSTRAINTS block (if CONSTRAINTS is not `none`) injected before the artifact-specific instruction, in this format:
154176
155177
```
@@ -162,10 +184,13 @@ ACTIVE CONSTRAINTS (from governance — these override artifact defaults):
162184
163185
Show routing as it happens. Include constraint count when CONSTRAINTS is not `none`:
164186
```
165-
🔀 edikt: routing to dba (2 active constraints applied) — data-model.mmd
187+
🔀 edikt: routing to dba (2 active constraints applied) — data-model.mmd [jsonb-aggregate]
188+
🔀 edikt: routing to architect — model.mmd
166189
🔀 edikt: routing to api — contracts/api.yaml
167190
```
168191
192+
When STORAGE_STRATEGY is `jsonb-aggregate`, include `[jsonb-aggregate]` in the routing line for `data-model.mmd`. When `normalized`, omit the tag.
193+
169194
If CONSTRAINTS is `none`, omit the constraint count entirely.
170195
171196
### 5. Artifact Templates
@@ -183,26 +208,60 @@ Each generated artifact gets a design blueprint header. Native-format artifacts
183208
**Frontmatter artifacts** (`test-strategy.md`, `config-spec.md`): use YAML frontmatter with `type: artifact`, `artifact_type:`, `status: draft`, `reviewed_by:`.
184209
185210
**data-model.mmd** — Mermaid ERD (when DB_TYPE is `sql`):
211+
212+
The ERD supports three entity modes based on STORAGE_STRATEGY:
213+
214+
| Mode | When | Rendering |
215+
|---|---|---|
216+
| Physical table | Entity has its own table (default) | Normal entity block |
217+
| JSONB-embedded | STORAGE_STRATEGY is `jsonb-aggregate` and entity is stored as JSONB inside another table | Entity block with `%% JSONB-embedded in {ParentTable}` comment. Relationship label MUST include `jsonb` (e.g., `"contains jsonb"`) |
218+
| Reference-only | Entity belongs to an external bounded context | Entity block with `%% External: {bounded context}` comment. Only PK field shown. Relationship label MUST include `ref` (e.g., `"references ref"`) |
219+
220+
When STORAGE_STRATEGY is `normalized`, all entities are physical tables (standard ERD behavior).
221+
222+
When STORAGE_STRATEGY is `jsonb-aggregate`:
223+
- The aggregate root is a physical table with a JSONB column
224+
- Nested entities stored in that JSONB column are JSONB-embedded entities
225+
- Show the JSONB column in the aggregate root as `jsonb {column_name} "JSONB"` with a comment listing what it contains
226+
- JSONB-embedded entities still get their own entity block (so the structure is visible) but are marked with the JSONB comment and relationship label
227+
186228
```
187229
%% Design blueprint — implement in your stack's native format. This artifact defines intent, not implementation.
188230
%% edikt:artifact type=data-model spec=SPEC-{NNN} status=draft reviewed_by=dba
231+
%% storage_strategy={normalized|jsonb-aggregate}
189232
%% created_at={ISO8601 timestamp}
190233
erDiagram
191-
{ENTITY} {
234+
%% Physical table
235+
{AGGREGATE_ROOT} {
192236
uuid id PK
193237
{type} {field} "{constraints}"
238+
jsonb {column_name} "JSONB"
239+
}
240+
241+
%% JSONB-embedded in {AGGREGATE_ROOT}
242+
{EMBEDDED_ENTITY} {
243+
{type} {field} "{constraints}"
194244
}
245+
{AGGREGATE_ROOT} ||--o{ {EMBEDDED_ENTITY} : "contains jsonb"
246+
247+
%% External: {bounded context}
248+
{EXTERNAL_ENTITY} {
249+
uuid id PK
250+
}
251+
{AGGREGATE_ROOT} }o--|| {EXTERNAL_ENTITY} : "references ref"
252+
253+
%% Standard relationship (physical tables)
195254
{ENTITY} ||--o{ {OTHER_ENTITY} : "{relationship}"
196255
```
197256
198-
Include one entity block per entity. Add `PK`, `FK`, `UK` markers on key fields. List all relationships with cardinality. Add `%% Index: {field} — {rationale}` comments for recommended indexes.
257+
Include one entity block per entity. Add `PK`, `FK`, `UK` markers on key fields. List all relationships with cardinality. Add `%% Index: {field} — {rationale}` comments for recommended indexes. When STORAGE_STRATEGY is `normalized`, omit the `storage_strategy` comment and JSONB-specific elements — produce a standard ERD.
199258
200259
**data-model.schema.yaml** — JSON Schema in YAML (when DB_TYPE is `document-mongo`):
201260
```yaml
202261
# Design blueprint — implement in your stack's native format. This artifact defines intent, not implementation.
203262
# edikt:artifact type=data-model spec=SPEC-{NNN} status=draft reviewed_by=dba
204263
# created_at={ISO8601 timestamp}
205-
$schema: "https://json-schema.org/draft/07/schema#"
264+
$schema: "{JSON_SCHEMA_URI}"
206265
collection: "{collection_name}"
207266
title: "{EntityName}"
208267
type: object
@@ -276,12 +335,55 @@ indexes:
276335

277336
For `mixed` DB_TYPE, generate one data model artifact per detected sub-type using the suffix naming convention (`data-model-sql.mmd`, `data-model-mongo.schema.yaml`, `data-model-dynamo.md`, `data-model-kv.md`).
278337

279-
**contracts/api.yaml** — OpenAPI 3.0:
338+
**model.mmd** — Domain class diagram (always generated alongside data-model, any DB_TYPE):
339+
340+
This artifact shows the domain model independent of storage — entities, value objects, inheritance, and relationships. It complements the data-model artifact (which shows storage structure) by showing domain semantics.
341+
342+
```
343+
%% Design blueprint — implement in your stack's native format. This artifact defines intent, not implementation.
344+
%% edikt:artifact type=domain-model spec=SPEC-{NNN} status=draft reviewed_by=architect
345+
%% created_at={ISO8601 timestamp}
346+
classDiagram
347+
class {EntityName} {
348+
+UUID id
349+
+{Type} {field}
350+
+{method}() {ReturnType}
351+
}
352+
353+
class {ValueObjectName} {
354+
<<value object>>
355+
+{Type} {field}
356+
}
357+
358+
class {AggregateRoot} {
359+
<<aggregate root>>
360+
+UUID id
361+
+{Type} {field}
362+
+{command}() void
363+
}
364+
365+
{AggregateRoot} *-- {ValueObjectName} : "contains"
366+
{AggregateRoot} o-- {EntityName} : "has many"
367+
{ChildEntity} --|> {ParentEntity} : "extends"
368+
{Entity} --> {ExternalEntity} : "references"
369+
```
370+
371+
Guidelines for the domain class diagram:
372+
- Mark aggregate roots with `<<aggregate root>>` stereotype
373+
- Mark value objects with `<<value object>>` stereotype
374+
- Use composition (`*--`) for owned value objects and entities
375+
- Use aggregation (`o--`) for collections
376+
- Use inheritance (`--|>`) for type hierarchies
377+
- Use dependency (`-->`) for cross-aggregate references
378+
- Include key domain methods (commands, queries) — not getters/setters
379+
- Do NOT mirror storage details — this is the domain model, not the database schema
380+
381+
**contracts/api.yaml** — OpenAPI (version from OPENAPI_VERSION):
280382
```yaml
281383
# Design blueprint — implement in your stack's native format. This artifact defines intent, not implementation.
282384
# edikt:artifact type=api-contract spec=SPEC-{NNN} status=draft reviewed_by=api
283385
# created_at={ISO8601 timestamp}
284-
openapi: "3.0.0"
386+
openapi: "{OPENAPI_VERSION}"
285387
info:
286388
title: "{Feature Name} API"
287389
version: "0.1.0"
@@ -326,29 +428,35 @@ components:
326428
type: {http|apiKey|oauth2}
327429
```
328430
329-
**contracts/events.yaml** — AsyncAPI 2.6:
431+
**contracts/events.yaml** — AsyncAPI (version from ASYNCAPI_VERSION):
330432
```yaml
331433
# Design blueprint — implement in your stack's native format. This artifact defines intent, not implementation.
332434
# edikt:artifact type=event-contract spec=SPEC-{NNN} status=draft reviewed_by=architect
333435
# created_at={ISO8601 timestamp}
334-
asyncapi: "2.6.0"
436+
asyncapi: "{ASYNCAPI_VERSION}"
335437
info:
336438
title: "{Feature Name} Events"
337439
version: "0.1.0"
338440
channels:
339-
{topic.or.queue.name}:
340-
publish:
341-
operationId: {EventName}Published
342-
summary: "{what triggers this event}"
343-
message:
344-
name: {EventName}
441+
{channelName}:
442+
address: "{topic.or.queue.name}"
443+
messages:
444+
{EventName}:
345445
payload:
346446
type: object
347447
required: [{required_fields}]
348448
properties:
349449
{field}:
350450
type: {type}
351451
description: "{description}"
452+
operations:
453+
{operationId}:
454+
action: send
455+
channel:
456+
$ref: "#/channels/{channelName}"
457+
summary: "{what triggers this event}"
458+
messages:
459+
- $ref: "#/channels/{channelName}/messages/{EventName}"
352460
x-producer: "{service/component}"
353461
x-consumers:
354462
- "{service/component}"
@@ -487,6 +595,7 @@ REMEMBER: Every artifact must be reviewed by the appropriate specialist agent. N
487595
✅ Artifacts created in {spec_folder}/
488596

489597
{data-model file} — {format}, reviewed by dba
598+
model.mmd — domain class diagram, reviewed by architect
490599
contracts/api.yaml — OpenAPI 3.0, reviewed by api
491600
contracts/events.yaml — AsyncAPI 2.6, reviewed by architect
492601
migrations/001_{name}.sql — SQL migration, reviewed by dba
@@ -530,6 +639,12 @@ Use this for priority-3 keyword scan. First match wins per DB type — if multip
530639
| document-dynamo | `data-model.md` | Access patterns table → entity prefixes → PK/SK/GSI design |
531640
| key-value | `data-model.md` | Key schema table — key pattern, value type, TTL, purpose, namespace |
532641
642+
**Domain class diagram (always alongside data-model):**
643+
644+
| DB_TYPE | File | Format |
645+
|---|---|---|
646+
| *(any)* | `model.mmd` | Mermaid classDiagram — entities, value objects, aggregate roots, inheritance, domain methods |
647+
533648
**Mixed — suffix per sub-type to avoid collision:**
534649
535650
| Sub-type | File |
@@ -539,6 +654,15 @@ Use this for priority-3 keyword scan. First match wins per DB type — if multip
539654
| document-dynamo | `data-model-dynamo.md` |
540655
| key-value | `data-model-kv.md` |
541656
657+
### Storage Strategy Detection (sql / mixed only)
658+
659+
| Signal in spec or migrations | STORAGE_STRATEGY |
660+
|---|---|
661+
| `jsonb`, `json column`, `json field`, `aggregate storage`, `embedded entity`, `nested entity`, `document-in-relational`, `json aggregate`, `::json` | `jsonb-aggregate` |
662+
| *(none of the above)* | `normalized` |
663+
664+
When `jsonb-aggregate`: `data-model.mmd` uses three entity modes (physical, JSONB-embedded, reference-only). When `normalized`: standard ERD only.
665+
542666
### Migration Generation Rules
543667
544668
| DB_TYPE | Generate migrations/ |

templates/hooks/subagent-stop.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ fi
5353

5454
# If no known agent detected, try to extract from "As <Role>" pattern
5555
if [ -z "$AGENT_NAME" ]; then
56-
AGENT_NAME=$(echo "$INPUT" | grep -oiE 'As (Staff|Senior|Principal) [A-Za-z]+' | head -1 | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
56+
AGENT_NAME=$(echo "$INPUT" | grep -oiE 'As (Staff |Senior |Principal )?[A-Za-z]+' | head -1 | awk '{print $NF}' | tr '[:upper:]' '[:lower:]')
5757
fi
5858

5959
# If still no agent name, exit silently

test/test-e2e.sh

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,13 @@ done
7070

7171
cp "$PROJECT_ROOT/VERSION" "$INSTALL_HOME/VERSION"
7272

73-
# Verify command count (48 commands: 11 flat + 3 adr + 3 invariant + 3 guideline + 4 gov + 7 sdlc + 2 docs + 15 deprecated)
74-
# v0.3.0 Phase 1 added commands/guideline/compile.md for parity with adr/invariant compile (ADR-009).
73+
# Verify command count (49 commands: 11 flat + 3 adr + 3 invariant + 3 guideline + 5 gov + 7 sdlc + 2 docs + 15 deprecated)
74+
# v0.3.0 added commands/guideline/compile.md + commands/gov/score.md
7575
CMD_COUNT=$(find "$INSTALL_HOME/commands/edikt/" -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
76-
if [ "$CMD_COUNT" -eq 48 ]; then
77-
pass "48 commands installed"
76+
if [ "$CMD_COUNT" -eq 49 ]; then
77+
pass "49 commands installed"
7878
else
79-
fail "Expected 48 commands, found $CMD_COUNT"
79+
fail "Expected 49 commands, found $CMD_COUNT"
8080
fi
8181

8282
# Verify agent count (19 agents)

0 commit comments

Comments
 (0)