aw-spec-cli 0.1.5 → 0.1.6
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/assets/.agents/agent.json +1 -1
- package/assets/.agents/skills/{aw-spec-orch → appweaver}/SKILL.md +10 -10
- package/assets/.agents/skills/{aw-spec-orch-demo → appweaver-demo}/SKILL.md +9 -9
- package/assets/.claude/agent.json +1 -1
- package/assets/.claude/skills/{aw-spec-orch → appweaver}/SKILL.md +10 -10
- package/assets/.claude/skills/{aw-spec-orch-demo → appweaver-demo}/SKILL.md +9 -9
- package/assets/.kilocode/agent.json +1 -1
- package/assets/.kilocode/workflows/{aw-spec-orch-demo.md → appweaver-demo.md} +9 -9
- package/assets/.kilocode/workflows/{aw-spec-orch.md → appweaver.md} +10 -10
- package/assets/docs/pharma-constitution.my-ref.md +86 -0
- package/dist/utils/paths.js +2 -2
- package/dist/utils/paths.js.map +1 -1
- package/package.json +1 -1
- package/assets/.aw-spec/DESIGN-FORMAT.md +0 -151
- package/assets/.aw-spec/ORCH-DIALECT.md +0 -156
- package/assets/.aw-spec/RULE-PARTITION.md +0 -126
- package/assets/.aw-spec/domain.json +0 -3
- package/assets/.aw-spec/extensions/.registry +0 -19
- package/assets/.aw-spec/extensions/agent-context/README.md +0 -57
- package/assets/.aw-spec/extensions/agent-context/agent-context-config.yml +0 -4
- package/assets/.aw-spec/extensions/agent-context/commands/aw-spec.agent-context.update.md +0 -26
- package/assets/.aw-spec/extensions/agent-context/extension.yml +0 -37
- package/assets/.aw-spec/extensions/agent-context/scripts/bash/update-agent-context.sh +0 -201
- package/assets/.aw-spec/extensions/agent-context/scripts/powershell/update-agent-context.ps1 +0 -237
- package/assets/.aw-spec/extensions.yml +0 -16
- package/assets/.aw-spec/feature.json +0 -3
- package/assets/.aw-spec/init-options.json +0 -9
- package/assets/.aw-spec/integration.json +0 -15
- package/assets/.aw-spec/integrations/aw-spec.manifest.json +0 -9
- package/assets/.aw-spec/interpreter/run-skill.py +0 -1158
- package/assets/.aw-spec/memory/constitution.md +0 -50
- package/assets/.aw-spec/orchestrator/AGENT-PROFILE.md +0 -94
- package/assets/.aw-spec/orchestrator/README.md +0 -300
- package/assets/.aw-spec/orchestrator/adapters/claude_agent_sdk.py +0 -74
- package/assets/.aw-spec/orchestrator/adapters/google_antigravity.py +0 -74
- package/assets/.aw-spec/orchestrator/modules/delta-design.orch.yaml +0 -105
- package/assets/.aw-spec/orchestrator/modules/delta-spec.orch.yaml +0 -79
- package/assets/.aw-spec/orchestrator/modules/domain-select.orch.yaml +0 -81
- package/assets/.aw-spec/orchestrator/modules/tech-select.orch.yaml +0 -75
- package/assets/.aw-spec/orchestrator/orchestrator.py +0 -1721
- package/assets/.aw-spec/orchestrator/rev-templates/ai-rules.reference.md +0 -40
- package/assets/.aw-spec/orchestrator/rev-templates/comandrulemap.skeleton.md +0 -59
- package/assets/.aw-spec/orchestrator/rev-templates/commandtechrulemap.skeleton.md +0 -97
- package/assets/.aw-spec/orchestrator/rev-templates/domain-constitution.skeleton.md +0 -61
- package/assets/.aw-spec/orchestrator/rev-templates/domain-requirements.skeleton.md +0 -48
- package/assets/.aw-spec/orchestrator/rev-templates/domain-rule-file.skeleton.md +0 -39
- package/assets/.aw-spec/orchestrator/rev-templates/technical-constitution.skeleton.md +0 -106
- package/assets/.aw-spec/orchestrator/rev-templates/technical-rule-file.skeleton.md +0 -54
- package/assets/.aw-spec/orchestrator/workflows/_smoke.orch.yaml +0 -160
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-add-component.orch.yaml +0 -244
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-adopt.orch.yaml +0 -775
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-analyze-delta-design.orch.yaml +0 -248
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-analyze-design.orch.yaml +0 -261
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +0 -425
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-build.orch.yaml +0 -188
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-characterize.orch.yaml +0 -474
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-close-delta.orch.yaml +0 -181
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-constitution.orch.yaml +0 -284
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-demo.orch.yaml +0 -103
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-dependency-tree.orch.yaml +0 -69
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-deploy.orch.yaml +0 -85
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-design.orch.yaml +0 -382
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +0 -382
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-foundation.orch.yaml +0 -246
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-implement-delta.orch.yaml +0 -231
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-implement.orch.yaml +0 -258
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-inventory.orch.yaml +0 -236
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-onboard.orch.yaml +0 -949
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-parity.orch.yaml +0 -293
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-run.orch.yaml +0 -177
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-select-domain.orch.yaml +0 -79
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-select-feature.orch.yaml +0 -128
- package/assets/.aw-spec/orchestrator/workflows/aw-spec-specify.orch.yaml +0 -412
- package/assets/.aw-spec/scripts/bash/aw-inventory.sh +0 -14
- package/assets/.aw-spec/scripts/bash/aw-parity.sh +0 -13
- package/assets/.aw-spec/scripts/bash/aw-rev-constitution.sh +0 -13
- package/assets/.aw-spec/scripts/bash/aw-spec-actions.sh +0 -1899
- package/assets/.aw-spec/scripts/bash/aw-xref.sh +0 -15
- package/assets/.aw-spec/scripts/bash/common.sh +0 -533
- package/assets/.aw-spec/scripts/powershell/aw-inventory.ps1 +0 -14
- package/assets/.aw-spec/scripts/powershell/aw-parity.ps1 +0 -13
- package/assets/.aw-spec/scripts/powershell/aw-rev-constitution.ps1 +0 -13
- package/assets/.aw-spec/scripts/powershell/aw-spec-actions.ps1 +0 -1563
- package/assets/.aw-spec/scripts/powershell/aw-xref.ps1 +0 -15
- package/assets/.aw-spec/scripts/python/aw-inventory.py +0 -683
- package/assets/.aw-spec/scripts/python/aw-parity.py +0 -380
- package/assets/.aw-spec/scripts/python/aw-rev-constitution.py +0 -312
- package/assets/.aw-spec/scripts/python/aw-xref.py +0 -1330
- package/assets/.aw-spec/scripts/python/validate_okf.py +0 -349
- package/assets/.aw-spec/templates/constitution-domain-template.md +0 -75
- package/assets/.aw-spec/templates/constitution-technical-template.md +0 -70
- package/assets/.aw-spec/templates/delta-design-template.md +0 -69
- package/assets/.aw-spec/templates/delta-spec-template.md +0 -60
- package/assets/.aw-spec/templates/design-template.md +0 -383
- package/assets/.aw-spec/templates/okf-concept-template.md +0 -103
- package/assets/.aw-spec/templates/parity-manifest-template.json +0 -49
- package/assets/.aw-spec/templates/source-map-template.md +0 -85
- package/assets/.aw-spec/templates/spec-template.md +0 -252
- package/assets/docs/ModularMonolith/ModularMonolith.Reference.pack.txt +0 -25248
- package/assets/docs/ModularMonolith/ModularMonolith.pack.txt +0 -5726
- package/assets/docs/ModularMonolith/pack-foundation.ps1 +0 -145
- package/assets/docs/ModularMonolith/pack-foundation.sh +0 -196
- package/assets/docs/ModularMonolith/pack-reference-foundation.ps1 +0 -157
- package/assets/docs/ModularMonolith/pack-reference-foundation.sh +0 -209
- package/assets/docs/ModularMonolith/unpack-foundation.ps1 +0 -126
- package/assets/docs/ModularMonolith/unpack-foundation.sh +0 -217
- package/assets/docs/ModularMonolith/unpack-reference-foundation.ps1 +0 -129
- package/assets/docs/ModularMonolith/unpack-reference-foundation.sh +0 -218
- package/assets/docs/README.md +0 -106
- package/assets/docs/VerticalSlice/PACKAGING.md +0 -185
- package/assets/docs/VerticalSlice/VerticalSlice.Reference.pack.txt +0 -19318
- package/assets/docs/VerticalSlice/VerticalSlice.pack.txt +0 -2833
- package/assets/docs/VerticalSlice/pack-foundation.ps1 +0 -145
- package/assets/docs/VerticalSlice/pack-foundation.sh +0 -196
- package/assets/docs/VerticalSlice/pack-reference-foundation.ps1 +0 -157
- package/assets/docs/VerticalSlice/pack-reference-foundation.sh +0 -209
- package/assets/docs/VerticalSlice/unpack-foundation.ps1 +0 -126
- package/assets/docs/VerticalSlice/unpack-foundation.sh +0 -217
- package/assets/docs/VerticalSlice/unpack-reference-foundation.ps1 +0 -129
- package/assets/docs/VerticalSlice/unpack-reference-foundation.sh +0 -218
- package/assets/docs/architecture/clean-architecture.md +0 -71
- package/assets/docs/architecture/cqrs.md +0 -141
- package/assets/docs/architecture/eventing.md +0 -83
- package/assets/docs/architecture/folder-structure.md +0 -61
- package/assets/docs/architecture/modular-monolith.md +0 -47
- package/assets/docs/architecture/persistence.md +0 -269
- package/assets/docs/architecture/testing.md +0 -55
- package/assets/docs/architecture/vertical-slices.md +0 -201
- package/assets/docs/components/Email/README.md +0 -119
- package/assets/docs/components/Email/ReferenceEndpoints/SendEmailEndpoint.cs +0 -84
- package/assets/docs/components/Email/integration.md +0 -300
- package/assets/docs/components/Jobs/README.md +0 -113
- package/assets/docs/components/Jobs/integration.md +0 -259
- package/assets/docs/components/QrGenerator/README.md +0 -85
- package/assets/docs/components/QrGenerator/ReferenceEndpoints/GenerateQrEndpoint.cs +0 -57
- package/assets/docs/components/QrGenerator/integration.md +0 -230
- package/assets/docs/components/README.md +0 -74
- package/assets/docs/domain/domain-driven-design.md +0 -212
- package/assets/docs/domainspecs/healthcare/ai-rules.domain.md +0 -34
- package/assets/docs/domainspecs/healthcare/api-versioning.domain.md +0 -17
- package/assets/docs/domainspecs/healthcare/auditing.domain.md +0 -97
- package/assets/docs/domainspecs/healthcare/authorization.domain.md +0 -26
- package/assets/docs/domainspecs/healthcare/clinical-terms.md +0 -20
- package/assets/docs/domainspecs/healthcare/coding-standards.domain.md +0 -26
- package/assets/docs/domainspecs/healthcare/comandrulemap.md +0 -100
- package/assets/docs/domainspecs/healthcare/commandtechrulemap.md +0 -119
- package/assets/docs/domainspecs/healthcare/consent.md +0 -19
- package/assets/docs/domainspecs/healthcare/cqrs.domain.md +0 -12
- package/assets/docs/domainspecs/healthcare/data-classification.domain.md +0 -28
- package/assets/docs/domainspecs/healthcare/dependency-policy.domain.md +0 -11
- package/assets/docs/domainspecs/healthcare/dicom.md +0 -15
- package/assets/docs/domainspecs/healthcare/domain-driven-design.domain.md +0 -28
- package/assets/docs/domainspecs/healthcare/domain-requirements.md +0 -55
- package/assets/docs/domainspecs/healthcare/eventing.domain.md +0 -18
- package/assets/docs/domainspecs/healthcare/fhir.md +0 -36
- package/assets/docs/domainspecs/healthcare/folder-structure.domain.md +0 -23
- package/assets/docs/domainspecs/healthcare/hipaa.md +0 -34
- package/assets/docs/domainspecs/healthcare/hl7.md +0 -15
- package/assets/docs/domainspecs/healthcare/integration-terms.md +0 -21
- package/assets/docs/domainspecs/healthcare/persistence.domain.md +0 -20
- package/assets/docs/domainspecs/healthcare/terminology.md +0 -30
- package/assets/docs/domainspecs/healthcare/testing.domain.md +0 -15
- package/assets/docs/domainspecs/healthcare/vertical-slices.domain.md +0 -18
- package/assets/docs/domainspecs/healthcare/zero-trust.domain.md +0 -12
- package/assets/docs/domainspecs/pharma/ai-rules.domain.md +0 -32
- package/assets/docs/domainspecs/pharma/api-versioning.domain.md +0 -20
- package/assets/docs/domainspecs/pharma/atc.md +0 -22
- package/assets/docs/domainspecs/pharma/auditing.domain.md +0 -90
- package/assets/docs/domainspecs/pharma/authorization.domain.md +0 -25
- package/assets/docs/domainspecs/pharma/coding-standards.domain.md +0 -28
- package/assets/docs/domainspecs/pharma/comandrulemap.md +0 -108
- package/assets/docs/domainspecs/pharma/commandtechrulemap.md +0 -119
- package/assets/docs/domainspecs/pharma/cqrs.domain.md +0 -16
- package/assets/docs/domainspecs/pharma/data-classification.domain.md +0 -24
- package/assets/docs/domainspecs/pharma/data-provenance.md +0 -21
- package/assets/docs/domainspecs/pharma/dependency-policy.domain.md +0 -11
- package/assets/docs/domainspecs/pharma/domain-driven-design.domain.md +0 -36
- package/assets/docs/domainspecs/pharma/domain-requirements.md +0 -99
- package/assets/docs/domainspecs/pharma/eventing.domain.md +0 -18
- package/assets/docs/domainspecs/pharma/fhir.md +0 -31
- package/assets/docs/domainspecs/pharma/folder-structure.domain.md +0 -24
- package/assets/docs/domainspecs/pharma/hl7.md +0 -17
- package/assets/docs/domainspecs/pharma/idmp.md +0 -26
- package/assets/docs/domainspecs/pharma/integration-terms.md +0 -21
- package/assets/docs/domainspecs/pharma/ndc.md +0 -28
- package/assets/docs/domainspecs/pharma/persistence.domain.md +0 -23
- package/assets/docs/domainspecs/pharma/pharma-terms.md +0 -27
- package/assets/docs/domainspecs/pharma/product-lifecycle.domain.md +0 -30
- package/assets/docs/domainspecs/pharma/regulatory-compliance.md +0 -19
- package/assets/docs/domainspecs/pharma/terminology.md +0 -32
- package/assets/docs/domainspecs/pharma/testing.domain.md +0 -19
- package/assets/docs/domainspecs/pharma/vertical-slices.domain.md +0 -17
- package/assets/docs/domainspecs/pharma/zero-trust.domain.md +0 -12
- package/assets/docs/glossary/ai-rules.md +0 -59
- package/assets/docs/governance/api-versioning.md +0 -99
- package/assets/docs/governance/coding-standards.md +0 -87
- package/assets/docs/governance/dependency-policy.md +0 -40
- package/assets/docs/interop/ihe-conformance.md +0 -31
- package/assets/docs/requirement-spec.txt +0 -27
- package/assets/docs/security/auditing.md +0 -160
- package/assets/docs/security/authorization-matrix.md +0 -14
- package/assets/docs/security/authorization.md +0 -87
- package/assets/docs/security/data-classification.md +0 -38
- package/assets/docs/security/zero-trust.md +0 -76
- package/assets/docs/technology-constitution.md +0 -172
|
@@ -1,349 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""validate_okf.py — conformance checker for OKF v0.1 bundles.
|
|
3
|
-
|
|
4
|
-
Implements OKF §9 conformance rules as ERRORs and the spec's soft
|
|
5
|
-
guidance as WARNINGs:
|
|
6
|
-
|
|
7
|
-
ERRORS (bundle is non-conformant):
|
|
8
|
-
E1 Non-reserved .md file has no parseable YAML frontmatter block, or
|
|
9
|
-
could not be read.
|
|
10
|
-
E2 Frontmatter lacks a non-empty `type` field.
|
|
11
|
-
E3 Reserved file structure violations:
|
|
12
|
-
- index.md with frontmatter anywhere except the bundle root
|
|
13
|
-
(root index.md may carry only okf_version per §11)
|
|
14
|
-
- log.md date headings not in ISO 8601 YYYY-MM-DD form (§7)
|
|
15
|
-
E4 log.md date block missing a required `Commit: \\`<sha>\\`` line, or
|
|
16
|
-
the line is present but not a valid hex commit SHA. This is an
|
|
17
|
-
ERROR because /speckit.okf.update depends on it to resume.
|
|
18
|
-
|
|
19
|
-
WARNINGS (consumers must tolerate; reported for quality):
|
|
20
|
-
W0 PyYAML not installed — using the lenient fallback parser.
|
|
21
|
-
W1 Missing recommended frontmatter (title, description).
|
|
22
|
-
W2 Broken bundle-internal links (may be not-yet-written knowledge).
|
|
23
|
-
W3 Directory containing concepts but no index.md.
|
|
24
|
-
W4 Concept body is empty.
|
|
25
|
-
W5 Possible secret-looking string in a file.
|
|
26
|
-
W6 A `source_files` entry does not exist on disk (may be pending
|
|
27
|
-
reconciliation by /speckit.okf.update).
|
|
28
|
-
W7 Possible duplicate concept: another file shares the same
|
|
29
|
-
`type` + `title`.
|
|
30
|
-
W8 Concept has unresolved `open_questions` (run /speckit.okf.clarify).
|
|
31
|
-
|
|
32
|
-
Usage: validate_okf.py <bundle_dir> [--config PATH] [--exclude GLOB]...
|
|
33
|
-
[--repo-root PATH] [--json]
|
|
34
|
-
Exit code 0 if conformant (warnings allowed), 1 if non-conformant, 2 on
|
|
35
|
-
bad usage.
|
|
36
|
-
|
|
37
|
-
Only stdlib is required; PyYAML is used if available for stricter YAML
|
|
38
|
-
parsing, with a lenient fallback otherwise.
|
|
39
|
-
"""
|
|
40
|
-
|
|
41
|
-
import argparse
|
|
42
|
-
import fnmatch
|
|
43
|
-
import json
|
|
44
|
-
import os
|
|
45
|
-
import re
|
|
46
|
-
import sys
|
|
47
|
-
|
|
48
|
-
try:
|
|
49
|
-
import yaml # type: ignore
|
|
50
|
-
HAVE_YAML = True
|
|
51
|
-
except ImportError:
|
|
52
|
-
HAVE_YAML = False
|
|
53
|
-
|
|
54
|
-
RESERVED = {"index.md", "log.md"}
|
|
55
|
-
DATE_HEADING = re.compile(r"^##\s+(\d{4}-\d{2}-\d{2})\s*$")
|
|
56
|
-
COMMIT_LINE = re.compile(r"^Commit:\s+`([0-9a-fA-F]{7,40})`\s*$")
|
|
57
|
-
LINK = re.compile(r"\[[^\]]*\]\(([^)]+)\)")
|
|
58
|
-
SECRET_PATTERNS = [
|
|
59
|
-
# key: "value" / key = 'value' (quoted)
|
|
60
|
-
re.compile(r"(?i)(api[_-]?key|secret|password|token)\s*[:=]\s*['\"][A-Za-z0-9+/_\-]{16,}['\"]"),
|
|
61
|
-
# key: value / key = value (unquoted)
|
|
62
|
-
re.compile(r"(?i)(api[_-]?key|secret|password|token)\s*[:=]\s*[A-Za-z0-9+/_\-]{16,}\b"),
|
|
63
|
-
# AWS-style access key ID
|
|
64
|
-
re.compile(r"\bAKIA[0-9A-Z]{16}\b"),
|
|
65
|
-
# PEM private key block start
|
|
66
|
-
re.compile(r"-----BEGIN (RSA |EC |OPENSSH |)PRIVATE KEY-----"),
|
|
67
|
-
]
|
|
68
|
-
|
|
69
|
-
errors: list[str] = []
|
|
70
|
-
warnings: list[str] = []
|
|
71
|
-
concept_meta: list[tuple[str, str, str]] = [] # (rel, type, title)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def split_frontmatter(text: str):
|
|
75
|
-
"""Return (frontmatter_str or None, body)."""
|
|
76
|
-
if not text.startswith("---"):
|
|
77
|
-
return None, text
|
|
78
|
-
parts = text.split("\n")
|
|
79
|
-
if parts[0].strip() != "---":
|
|
80
|
-
return None, text
|
|
81
|
-
for i in range(1, len(parts)):
|
|
82
|
-
if parts[i].strip() == "---":
|
|
83
|
-
return "\n".join(parts[1:i]), "\n".join(parts[i + 1:])
|
|
84
|
-
return None, text # unterminated block
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def parse_yaml(fm: str):
|
|
88
|
-
"""Parse frontmatter; return dict or None on failure."""
|
|
89
|
-
if HAVE_YAML:
|
|
90
|
-
try:
|
|
91
|
-
data = yaml.safe_load(fm)
|
|
92
|
-
return data if isinstance(data, dict) else None
|
|
93
|
-
except yaml.YAMLError:
|
|
94
|
-
return None
|
|
95
|
-
# Lenient fallback: top-level "key: value" lines only.
|
|
96
|
-
data = {}
|
|
97
|
-
for line in fm.splitlines():
|
|
98
|
-
if not line.strip() or line.startswith(("#", " ", "\t", "-")):
|
|
99
|
-
continue
|
|
100
|
-
if ":" not in line:
|
|
101
|
-
return None
|
|
102
|
-
k, _, v = line.partition(":")
|
|
103
|
-
data[k.strip()] = v.strip().strip("'\"")
|
|
104
|
-
return data
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def read_text(path: str) -> tuple[str, None] | tuple[None, str]:
|
|
108
|
-
"""Read a file as text; return (text, None) or (None, error_message)."""
|
|
109
|
-
try:
|
|
110
|
-
with open(path, encoding="utf-8", errors="replace") as f:
|
|
111
|
-
return f.read(), None
|
|
112
|
-
except OSError as e:
|
|
113
|
-
return None, str(e)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def check_concept(path: str, rel: str, bundle: str, repo_root: str):
|
|
117
|
-
text, err = read_text(path)
|
|
118
|
-
if text is None:
|
|
119
|
-
errors.append(f"E1 {rel}: could not read file ({err})")
|
|
120
|
-
return
|
|
121
|
-
fm, body = split_frontmatter(text)
|
|
122
|
-
if fm is None:
|
|
123
|
-
errors.append(f"E1 {rel}: missing or unterminated YAML frontmatter block")
|
|
124
|
-
return
|
|
125
|
-
data = parse_yaml(fm)
|
|
126
|
-
if data is None:
|
|
127
|
-
errors.append(f"E1 {rel}: frontmatter is not parseable YAML")
|
|
128
|
-
return
|
|
129
|
-
t = data.get("type")
|
|
130
|
-
if not (isinstance(t, str) and t.strip()):
|
|
131
|
-
errors.append(f"E2 {rel}: frontmatter has no non-empty `type` field")
|
|
132
|
-
for field in ("title", "description"):
|
|
133
|
-
if not data.get(field):
|
|
134
|
-
warnings.append(f"W1 {rel}: missing recommended field `{field}`")
|
|
135
|
-
if not body.strip():
|
|
136
|
-
warnings.append(f"W4 {rel}: concept body is empty")
|
|
137
|
-
check_links(body, rel, os.path.dirname(path), bundle)
|
|
138
|
-
check_source_files(data, rel, repo_root)
|
|
139
|
-
check_open_questions(data, rel)
|
|
140
|
-
if isinstance(t, str) and t.strip() and data.get("title"):
|
|
141
|
-
concept_meta.append((rel, t.strip(), str(data["title"]).strip()))
|
|
142
|
-
for pattern in SECRET_PATTERNS:
|
|
143
|
-
if pattern.search(text):
|
|
144
|
-
warnings.append(f"W5 {rel}: possible secret-looking value — review before sharing")
|
|
145
|
-
break
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
def check_source_files(data: dict, rel: str, repo_root: str):
|
|
149
|
-
sf = data.get("source_files")
|
|
150
|
-
if not isinstance(sf, list):
|
|
151
|
-
return
|
|
152
|
-
for entry in sf:
|
|
153
|
-
if not isinstance(entry, str) or not entry.strip():
|
|
154
|
-
continue
|
|
155
|
-
candidate = os.path.join(repo_root, entry)
|
|
156
|
-
if not os.path.exists(candidate):
|
|
157
|
-
warnings.append(f"W6 {rel}: source_files entry '{entry}' does not exist")
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
def check_open_questions(data: dict, rel: str):
|
|
161
|
-
oq = data.get("open_questions")
|
|
162
|
-
if not isinstance(oq, list):
|
|
163
|
-
return
|
|
164
|
-
pending = [q for q in oq if isinstance(q, str) and q.strip()]
|
|
165
|
-
if pending:
|
|
166
|
-
n = len(pending)
|
|
167
|
-
warnings.append(
|
|
168
|
-
f"W8 {rel}: {n} unresolved open question{'s' if n != 1 else ''} "
|
|
169
|
-
f"— run /speckit.okf.clarify"
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
def check_links(body: str, rel: str, filedir: str, bundle: str):
|
|
174
|
-
for target in LINK.findall(body):
|
|
175
|
-
target = target.split("#")[0].strip()
|
|
176
|
-
if not target or "://" in target or target.startswith("mailto:"):
|
|
177
|
-
continue
|
|
178
|
-
if target.startswith("/"):
|
|
179
|
-
dest = os.path.join(bundle, target.lstrip("/"))
|
|
180
|
-
else:
|
|
181
|
-
dest = os.path.join(filedir, target)
|
|
182
|
-
# Directory links (progressive disclosure) are fine if the dir exists.
|
|
183
|
-
if not (os.path.exists(dest) or os.path.exists(dest.rstrip("/"))):
|
|
184
|
-
warnings.append(f"W2 {rel}: broken link -> {target}")
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
def check_index(path: str, rel: str, is_root: bool, bundle: str):
|
|
188
|
-
text, err = read_text(path)
|
|
189
|
-
if text is None:
|
|
190
|
-
errors.append(f"E1 {rel}: could not read file ({err})")
|
|
191
|
-
return
|
|
192
|
-
fm, body = split_frontmatter(text)
|
|
193
|
-
if fm is not None:
|
|
194
|
-
if not is_root:
|
|
195
|
-
errors.append(f"E3 {rel}: index.md must not contain frontmatter (only the bundle root may, per §11)")
|
|
196
|
-
else:
|
|
197
|
-
data = parse_yaml(fm) or {}
|
|
198
|
-
extra = set(data) - {"okf_version"}
|
|
199
|
-
if extra:
|
|
200
|
-
errors.append(f"E3 {rel}: root index.md frontmatter may only declare okf_version (found: {sorted(extra)})")
|
|
201
|
-
check_links(body if fm is not None else text, rel, os.path.dirname(path), bundle)
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
def check_log(path: str, rel: str):
|
|
205
|
-
text, err = read_text(path)
|
|
206
|
-
if text is None:
|
|
207
|
-
errors.append(f"E1 {rel}: could not read file ({err})")
|
|
208
|
-
return
|
|
209
|
-
lines = text.splitlines()
|
|
210
|
-
for i, ln in enumerate(lines):
|
|
211
|
-
if not ln.startswith("## "):
|
|
212
|
-
continue
|
|
213
|
-
stripped = ln.rstrip()
|
|
214
|
-
if not DATE_HEADING.match(stripped):
|
|
215
|
-
errors.append(f"E3 {rel}: log heading not ISO 8601 YYYY-MM-DD: '{stripped}'")
|
|
216
|
-
continue
|
|
217
|
-
# Look for a "Commit: `<sha>`" line before the next date heading.
|
|
218
|
-
found_sha = None
|
|
219
|
-
for j in range(i + 1, len(lines)):
|
|
220
|
-
if lines[j].startswith("## "):
|
|
221
|
-
break
|
|
222
|
-
m = COMMIT_LINE.match(lines[j].strip())
|
|
223
|
-
if m:
|
|
224
|
-
found_sha = m.group(1)
|
|
225
|
-
break
|
|
226
|
-
if found_sha is None:
|
|
227
|
-
errors.append(f"E4 {rel}: date block '{stripped}' missing required 'Commit: `<sha>`' line")
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
def find_repo_root(bundle: str) -> str:
|
|
231
|
-
p = bundle
|
|
232
|
-
while True:
|
|
233
|
-
if os.path.isdir(os.path.join(p, ".git")):
|
|
234
|
-
return p
|
|
235
|
-
parent = os.path.dirname(p)
|
|
236
|
-
if parent == p:
|
|
237
|
-
return os.path.dirname(bundle)
|
|
238
|
-
p = parent
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
def load_config_excludes(path: str) -> list[str]:
|
|
242
|
-
if not path or not os.path.isfile(path):
|
|
243
|
-
return []
|
|
244
|
-
text, err = read_text(path)
|
|
245
|
-
if err is not None or text is None:
|
|
246
|
-
return []
|
|
247
|
-
if HAVE_YAML:
|
|
248
|
-
try:
|
|
249
|
-
data = yaml.safe_load(text) or {}
|
|
250
|
-
except yaml.YAMLError:
|
|
251
|
-
return []
|
|
252
|
-
excl = (data.get("okf") or {}).get("exclude") or []
|
|
253
|
-
return [str(x) for x in excl if isinstance(x, str)]
|
|
254
|
-
# Lenient fallback: scan for an `exclude:` block of `- "glob"` lines.
|
|
255
|
-
globs = []
|
|
256
|
-
in_exclude = False
|
|
257
|
-
for line in text.splitlines():
|
|
258
|
-
stripped = line.strip()
|
|
259
|
-
if re.match(r"^exclude\s*:\s*$", stripped):
|
|
260
|
-
in_exclude = True
|
|
261
|
-
continue
|
|
262
|
-
if in_exclude:
|
|
263
|
-
m = re.match(r"^-\s*['\"]?([^'\"#]+)['\"]?\s*(#.*)?$", stripped)
|
|
264
|
-
if m:
|
|
265
|
-
globs.append(m.group(1).strip())
|
|
266
|
-
continue
|
|
267
|
-
if stripped and not stripped.startswith("#"):
|
|
268
|
-
in_exclude = False
|
|
269
|
-
return globs
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
def is_excluded(rel: str, patterns: list[str]) -> bool:
|
|
273
|
-
return any(fnmatch.fnmatch(rel, pat) for pat in patterns)
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
def main() -> int:
|
|
277
|
-
parser = argparse.ArgumentParser(
|
|
278
|
-
description="Conformance checker for OKF v0.1 knowledge bundles.",
|
|
279
|
-
)
|
|
280
|
-
parser.add_argument("bundle_dir", help="Path to the knowledge bundle directory")
|
|
281
|
-
parser.add_argument("--config", help="Path to okf-config.yml (exclude list is honored)")
|
|
282
|
-
parser.add_argument("--exclude", action="append", default=[], help="Glob to exclude (repeatable)")
|
|
283
|
-
parser.add_argument("--repo-root", help="Repo root for resolving source_files (default: nearest .git above bundle_dir)")
|
|
284
|
-
parser.add_argument("--json", action="store_true", help="Emit machine-readable JSON instead of text")
|
|
285
|
-
args = parser.parse_args()
|
|
286
|
-
|
|
287
|
-
if not os.path.isdir(args.bundle_dir):
|
|
288
|
-
parser.error(f"{args.bundle_dir} is not a directory")
|
|
289
|
-
|
|
290
|
-
bundle = os.path.abspath(args.bundle_dir)
|
|
291
|
-
repo_root = os.path.abspath(args.repo_root) if args.repo_root else find_repo_root(bundle)
|
|
292
|
-
exclude_patterns = load_config_excludes(args.config) + list(args.exclude)
|
|
293
|
-
|
|
294
|
-
if not HAVE_YAML:
|
|
295
|
-
warnings.append("W0 (global): PyYAML not installed — using lenient fallback parser, results may be less strict")
|
|
296
|
-
|
|
297
|
-
concepts = 0
|
|
298
|
-
for dirpath, dirnames, filenames in os.walk(bundle):
|
|
299
|
-
dirnames[:] = [d for d in dirnames if not d.startswith(".")]
|
|
300
|
-
rel_dir = os.path.relpath(dirpath, bundle)
|
|
301
|
-
mds = [
|
|
302
|
-
f for f in filenames
|
|
303
|
-
if f.endswith(".md") and not is_excluded(
|
|
304
|
-
os.path.relpath(os.path.join(dirpath, f), bundle), exclude_patterns
|
|
305
|
-
)
|
|
306
|
-
]
|
|
307
|
-
if any(f not in RESERVED for f in mds) and "index.md" not in mds:
|
|
308
|
-
warnings.append(f"W3 {rel_dir or '.'}: directory has concepts but no index.md")
|
|
309
|
-
for f in mds:
|
|
310
|
-
path = os.path.join(dirpath, f)
|
|
311
|
-
rel = os.path.relpath(path, bundle)
|
|
312
|
-
if f == "index.md":
|
|
313
|
-
check_index(path, rel, dirpath == bundle, bundle)
|
|
314
|
-
elif f == "log.md":
|
|
315
|
-
check_log(path, rel)
|
|
316
|
-
else:
|
|
317
|
-
concepts += 1
|
|
318
|
-
check_concept(path, rel, bundle, repo_root)
|
|
319
|
-
|
|
320
|
-
# W7: duplicate (type, title) pairs across the bundle.
|
|
321
|
-
seen: dict[tuple[str, str], str] = {}
|
|
322
|
-
for rel, t, title in concept_meta:
|
|
323
|
-
key = (t.lower(), title.lower())
|
|
324
|
-
if key in seen:
|
|
325
|
-
warnings.append(f"W7 {rel}: possible duplicate concept — same type+title as {seen[key]}")
|
|
326
|
-
else:
|
|
327
|
-
seen[key] = rel
|
|
328
|
-
|
|
329
|
-
if args.json:
|
|
330
|
-
print(json.dumps({
|
|
331
|
-
"concepts": concepts,
|
|
332
|
-
"errors": errors,
|
|
333
|
-
"warnings": warnings,
|
|
334
|
-
"result": "NON-CONFORMANT" if errors else "CONFORMANT",
|
|
335
|
-
}, indent=2))
|
|
336
|
-
else:
|
|
337
|
-
print(f"OKF validation of {bundle}")
|
|
338
|
-
print(f" concepts: {concepts} errors: {len(errors)} warnings: {len(warnings)}\n")
|
|
339
|
-
for e in errors:
|
|
340
|
-
print(f" ERROR {e}")
|
|
341
|
-
for w in warnings:
|
|
342
|
-
print(f" WARNING {w}")
|
|
343
|
-
print("\nRESULT:", "NON-CONFORMANT" if errors else "CONFORMANT (OKF v0.1)")
|
|
344
|
-
|
|
345
|
-
return 1 if errors else 0
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
if __name__ == "__main__":
|
|
349
|
-
sys.exit(main())
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
# [PROJECT_NAME] Constitution — [DOMAIN_NAME] Addenda
|
|
2
|
-
<!-- Example: Medicinal Product Registry Constitution — Pharma Addenda -->
|
|
3
|
-
<!--
|
|
4
|
-
AppWeaver split-constitution model. This is the DOMAIN half — the domain
|
|
5
|
-
addenda derived from docs/<domain>-constitution.md + the domain rule files
|
|
6
|
-
(docs/domainspecs/<domain>/…). It carries ONLY domain-specific material:
|
|
7
|
-
domain principles (XVI+), domain identifiers/terminology, regulatory regime,
|
|
8
|
-
interoperability surfaces, and domain governance overrides.
|
|
9
|
-
|
|
10
|
-
It EXTENDS the paired constitution.technical.md — it does not restate the
|
|
11
|
-
technical principles (I–XV) or the base governance; it references them and adds
|
|
12
|
-
the domain layer on top ("Extends technology X and XIV with …").
|
|
13
|
-
|
|
14
|
-
Loading policy: this file is a SPECIFY-only standing input and a GATE checklist.
|
|
15
|
-
It is deliberately NOT loaded by design or implement — domain reaches those
|
|
16
|
-
phases only through the spec. Expand the principle slots to the full domain set
|
|
17
|
-
from the domain addenda — the three shown are a scaffold, not a limit.
|
|
18
|
-
|
|
19
|
-
THIN AUTHORITY: each principle is ONE declarative, testable line + a "→ [doc.md]"
|
|
20
|
-
pointer to its rule doc. Do NOT restate the rule doc's detail here — this file says
|
|
21
|
-
WHICH domain rules are supreme/non-negotiable and adopted; the domain docs (also
|
|
22
|
-
loaded in specify) carry the detail. That keeps the authority/detail overlap to one line.
|
|
23
|
-
-->
|
|
24
|
-
|
|
25
|
-
## Adopted Modelling Standard
|
|
26
|
-
<!--
|
|
27
|
-
DDD is the ratified domain-modelling standard, and it is the ONE technical principle
|
|
28
|
-
that specify needs (specify loads THIS file, not constitution.technical). State it here
|
|
29
|
-
as a thin authority line so specify carries the DDD mandate in its constitution frame —
|
|
30
|
-
the detail lives in the rule docs (both loaded in specify), do NOT restate it.
|
|
31
|
-
-->
|
|
32
|
-
Domain modelling follows **Principle III — Domain-Driven Design (NON-NEGOTIABLE)** of
|
|
33
|
-
the technical base → docs/domain/domain-driven-design.md (generic tactical patterns) +
|
|
34
|
-
docs/domainspecs/[DOMAIN]/domain-driven-design.domain.md (this domain's canonical model).
|
|
35
|
-
The spec expresses the model in DDD terms (ubiquitous language, aggregates + roots,
|
|
36
|
-
entities vs value objects, aggregate-owned invariants, bounded context) as domain MODEL,
|
|
37
|
-
never code.
|
|
38
|
-
|
|
39
|
-
## Domain Principles
|
|
40
|
-
<!-- Numbered to CONTINUE the technical base (which ends at XV): start at XVI. -->
|
|
41
|
-
|
|
42
|
-
### [PRINCIPLE_16_NAME]
|
|
43
|
-
<!-- Example: XVI. Pharmaceutical Product Identification (NON-NEGOTIABLE) -->
|
|
44
|
-
[PRINCIPLE_16_DESCRIPTION]
|
|
45
|
-
<!-- Example: NDC modeled as a Value Object with discrete Labeler/Product/Package segments; uniqueness enforced; validation rules configurable; no invented codes; model aligns with ISO IDMP concepts. Cite docs/domainspecs/<domain>/ sources. -->
|
|
46
|
-
|
|
47
|
-
### [PRINCIPLE_17_NAME]
|
|
48
|
-
<!-- Example: XVII. Domain Terminology & Classification (NON-NEGOTIABLE) -->
|
|
49
|
-
[PRINCIPLE_17_DESCRIPTION]
|
|
50
|
-
<!-- Example: Coded data uses standard terminologies; every coded field is a Codeable value object; lookup/validation via the Terminology module; no invented or free-text codes. -->
|
|
51
|
-
|
|
52
|
-
### [PRINCIPLE_18_NAME]
|
|
53
|
-
<!-- Example: Continue XVIII+ from the domain addenda (e.g. Regulatory Compliance: Audit/Provenance/Versioning; Lifecycle Integrity; Interoperability Readiness). Add one '### ' block per domain principle; state which technical principle each EXTENDS. -->
|
|
54
|
-
[PRINCIPLE_18_DESCRIPTION]
|
|
55
|
-
<!-- Example: Immutable append-only audit records; full version history; retirement is a logical state change, never a hard delete. Extends technology X and XIV with domain compliance controls. -->
|
|
56
|
-
|
|
57
|
-
## Domain Folder Additions
|
|
58
|
-
<!-- Example: Pharma Folder Additions -->
|
|
59
|
-
|
|
60
|
-
[FOLDER_ADDITIONS_CONTENT]
|
|
61
|
-
<!-- Example: Interop/Fhir + Hl7v2 adapters, InteropTests, terminology seed fixtures, and provenance/version-history stores. See docs/domainspecs/<domain>/folder-structure.domain.md. These ADD to the base layout in constitution.technical.md. -->
|
|
62
|
-
|
|
63
|
-
## Regulatory Posture & Security Addenda
|
|
64
|
-
|
|
65
|
-
[REGULATORY_POSTURE_CONTENT]
|
|
66
|
-
<!-- Example: Regulatory-compliance readiness (auditability, traceability, data integrity, provenance); data-integrity expectations (e.g. ALCOA+/GxP); identifier/terminology/standard alignment; data residency. Zero-trust posture; change-authority enforcement; data classification (public vs sensitive/pre-market/externally-sourced). Cite docs/domainspecs/<domain>/ + docs/security/*.domain.md. -->
|
|
67
|
-
|
|
68
|
-
## Domain Governance Overrides
|
|
69
|
-
<!-- Extends the Governance section of constitution.technical.md with domain-specific change authority. -->
|
|
70
|
-
|
|
71
|
-
[DOMAIN_GOVERNANCE_CONTENT]
|
|
72
|
-
<!-- Example: Mandatory approver for any identifier/classification/lifecycle-rule change (e.g. Regulatory/Compliance Officer); Domain Owners per bounded context; review cadence tied to regulatory/terminology releases; exception/waiver process. The base amendment procedure + SemVer policy in constitution.technical.md still apply. -->
|
|
73
|
-
|
|
74
|
-
**Version**: [CONSTITUTION_VERSION] | **Ratified**: [RATIFICATION_DATE] | **Last Amended**: [LAST_AMENDED_DATE]
|
|
75
|
-
<!-- Example: Version: 1.0.0 | Ratified: 2026-06-13 | Last Amended: 2026-06-13. Versions INDEPENDENTLY of constitution.technical.md — e.g. a WHO ATC annual update bumps this file only. -->
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# [PROJECT_NAME] Constitution — Technical Base
|
|
2
|
-
<!-- Example: Medicinal Product Registry Constitution — Technical Base -->
|
|
3
|
-
<!--
|
|
4
|
-
AppWeaver split-constitution model. This is the TECHNICAL half — the
|
|
5
|
-
domain-agnostic constitution derived from docs/technology-constitution.md
|
|
6
|
-
(Core Principles I–XV, base governance, folder/convention pointers) + the base
|
|
7
|
-
AI rules (docs/glossary/ai-rules.md). It carries NO domain-specific principle,
|
|
8
|
-
identifier, terminology, or regulatory rule — those live in the paired
|
|
9
|
-
constitution.domain.md, authored from the domain addenda.
|
|
10
|
-
|
|
11
|
-
This file is the standing technical authority for the TECHNICAL authoring phases —
|
|
12
|
-
DESIGN and IMPLEMENT (specify loads constitution.domain, not this; the gates load
|
|
13
|
-
neither). Keeping it domain-free is what lets design author architecture from "spec
|
|
14
|
-
+ technical docs only" and lets the tech base stay swappable. Expand the principle
|
|
15
|
-
slots to the full I–XV set from the technology base — the five shown are a scaffold,
|
|
16
|
-
not a limit. Do NOT add domain principles here.
|
|
17
|
-
|
|
18
|
-
THIN AUTHORITY: each principle is ONE declarative, testable line + a "→ [doc.md]"
|
|
19
|
-
pointer to its rule doc. Do NOT restate the rule doc's detail here — this file says
|
|
20
|
-
WHICH rules are supreme/non-negotiable and adopted; the docs (also loaded in the
|
|
21
|
-
phase) carry the detail. That keeps the authority/detail overlap to one line.
|
|
22
|
-
-->
|
|
23
|
-
|
|
24
|
-
## Core Principles
|
|
25
|
-
|
|
26
|
-
### [PRINCIPLE_1_NAME]
|
|
27
|
-
<!-- Example: I. Modular Monolith with Vertical Slices (NON-NEGOTIABLE) -->
|
|
28
|
-
[PRINCIPLE_1_DESCRIPTION]
|
|
29
|
-
<!-- Example: Single deployable service partitioned into modules; each module = Domain/Features/Persistence/Shared; each feature is a self-contained vertical slice; no cross-module Domain coupling; no horizontal service layers. -->
|
|
30
|
-
|
|
31
|
-
### [PRINCIPLE_2_NAME]
|
|
32
|
-
<!-- Example: II. Clean Architecture Dependency Rule (NON-NEGOTIABLE) -->
|
|
33
|
-
[PRINCIPLE_2_DESCRIPTION]
|
|
34
|
-
<!-- Example: Dependencies flow inward: API → Application → Domain ← Infrastructure; Domain is framework-agnostic; endpoints call IMediator, never repositories/DbContext. -->
|
|
35
|
-
|
|
36
|
-
### [PRINCIPLE_3_NAME]
|
|
37
|
-
<!-- Example: III. Domain-Driven Design with Rich Aggregates (NON-NEGOTIABLE) -->
|
|
38
|
-
[PRINCIPLE_3_DESCRIPTION]
|
|
39
|
-
<!-- Example: Access entities through aggregate roots; strongly-typed IDs; sealed validating value objects; immutable domain events; invariants enforced in the aggregate; transaction boundary = aggregate boundary. -->
|
|
40
|
-
|
|
41
|
-
### [PRINCIPLE_4_NAME]
|
|
42
|
-
<!-- Example: IV. CQRS via Mediator with Result Pattern (NON-NEGOTIABLE) -->
|
|
43
|
-
[PRINCIPLE_4_DESCRIPTION]
|
|
44
|
-
<!-- Example: All logic dispatched via the mediator; commands/queries return Result<T>; errors are Result.Failure (not exceptions); writes run in a transaction and publish domain events. -->
|
|
45
|
-
|
|
46
|
-
### [PRINCIPLE_5_NAME]
|
|
47
|
-
<!-- Example: Continue V–XV from the technology base (Specification-Driven Persistence, Optimistic Concurrency, Multi-Provider/Multi-Tenant Persistence, Security by Default, Observability, Cloud-Native Orchestration, REST API Standards, Test-First, Data Integration / Single Source of Truth, Evolutionary Path to Microservices). Add one '### ' block per principle. STOP at the last technical principle — domain principles (XVI+) go in constitution.domain.md. -->
|
|
48
|
-
[PRINCIPLE_5_DESCRIPTION]
|
|
49
|
-
<!-- Example: Keep each principle declarative and testable; mark NON-NEGOTIABLE ones explicitly; the rules live in docs/ — cite the technology-constitution.md source. -->
|
|
50
|
-
|
|
51
|
-
## [SECTION_2_NAME]
|
|
52
|
-
<!-- Example: Mandatory Folder Structure (base) -->
|
|
53
|
-
|
|
54
|
-
[SECTION_2_CONTENT]
|
|
55
|
-
<!-- Example: Module = Domain/Features/Persistence/Shared; cross-cutting files at the AppWeaver template level. See docs/architecture/folder-structure.md. Domain folder ADDITIONS are declared in constitution.domain.md, not here. -->
|
|
56
|
-
|
|
57
|
-
## [SECTION_3_NAME]
|
|
58
|
-
<!-- Example: Conventions, Forbidden Patterns, AI Rules & Quality Gates (base) -->
|
|
59
|
-
|
|
60
|
-
[SECTION_3_CONTENT]
|
|
61
|
-
<!-- Example: Naming/coding conventions, base Forbidden Patterns, CI Quality Gates (docs/governance/coding-standards.md); package governance (docs/governance/dependency-policy.md); base AI operational rules (docs/glossary/ai-rules.md). Domain overlays are referenced from constitution.domain.md. -->
|
|
62
|
-
|
|
63
|
-
## Governance
|
|
64
|
-
<!-- Example: Constitution supersedes README/CLAUDE.md/AGENTS.md; amendments via PR with a migration plan + SemVer bump; reviewers cite the principle number. This base governance also applies to constitution.domain.md; domain-specific approver/change-authority overrides are declared there. -->
|
|
65
|
-
|
|
66
|
-
[GOVERNANCE_RULES]
|
|
67
|
-
<!-- Example: Amendment procedure; SemVer-for-governance (MAJOR = weaken a NON-NEGOTIABLE or break the folder structure; MINOR = new principle or bounded context; PATCH = clarifications); compliance review on every PR; quarterly audit. The paired constitution.domain.md extends this section with domain change-authority. -->
|
|
68
|
-
|
|
69
|
-
**Version**: [CONSTITUTION_VERSION] | **Ratified**: [RATIFICATION_DATE] | **Last Amended**: [LAST_AMENDED_DATE]
|
|
70
|
-
<!-- Example: Version: 1.0.0 | Ratified: 2026-06-13 | Last Amended: 2026-06-13. This file versions INDEPENDENTLY of constitution.domain.md — a technical-base change need not bump the domain half, and vice-versa. -->
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
# Delta Design — [CR-ID]: [TITLE]
|
|
2
|
-
|
|
3
|
-
**Feature (aggregate)**: [Aggregate]
|
|
4
|
-
|
|
5
|
-
**Base version**: [vX.Y] <!-- the current master design version; the design phase bumps the minor on close -->
|
|
6
|
-
|
|
7
|
-
<!--
|
|
8
|
-
This delta applies to the SINGLE master design at specs/[Aggregate]/design.md —
|
|
9
|
-
never a new design doc. Classify every design change ADDED / MODIFIED / REMOVED
|
|
10
|
-
against the current master, per LAYER section (Domain · Application · API · Infra),
|
|
11
|
-
in the SAME technology-agnostic form as the master: behaviour as signatures +
|
|
12
|
-
pre/post/invariant/raises, contracts as field shapes — NO code. A new command
|
|
13
|
-
(e.g. UpdateOrder) is a new USER STORY realised as delta design elements, NOT a
|
|
14
|
-
new feature/aggregate. After this delta is applied to design.md, `implement`
|
|
15
|
-
regenerates the touched slice; regression re-tests the shared-element dependents.
|
|
16
|
-
-->
|
|
17
|
-
|
|
18
|
-
## Change summary
|
|
19
|
-
|
|
20
|
-
[One paragraph: what the design change is and which user story / spec delta it serves.]
|
|
21
|
-
|
|
22
|
-
## ADDED — new design elements
|
|
23
|
-
|
|
24
|
-
<!-- New entities/VOs/events, use-cases/handlers, endpoints, ports, adapters, and the
|
|
25
|
-
realisation of any new spec constraint/classification/contract. Serves = a spec id
|
|
26
|
-
(FR-/BR-/CN-/DC-/IC-/US-); design reads the spec, never the domain rule files. -->
|
|
27
|
-
|
|
28
|
-
| Layer (§) | Element | Kind | Serves |
|
|
29
|
-
|---|---|---|---|
|
|
30
|
-
| Application (§2) | `[Command/Query]` + handler | use-case | US-[n] · FR-[nnn] |
|
|
31
|
-
| API (§3) | `[VERB] [/route]` | endpoint | US-[n] · FR-[nnn] |
|
|
32
|
-
| Domain (§1) | `[Event]` / `[VO]` | event / value object | BR-[nnn] · CN-[nnn] |
|
|
33
|
-
| Infrastructure (§4.2) | `I[External]` adapter | contract realisation | IC-[nnn] |
|
|
34
|
-
| Cross-cutting (§5) | [audit / retention / exposure decision] | constraint realisation | CN-[nnn] · DC-[nnn] |
|
|
35
|
-
|
|
36
|
-
## MODIFIED — existing design elements
|
|
37
|
-
|
|
38
|
-
<!-- Edits to elements the master already defines (add a behaviour, change a contract). -->
|
|
39
|
-
|
|
40
|
-
| Layer (§) | Element | What changes | Why |
|
|
41
|
-
|---|---|---|---|
|
|
42
|
-
| Domain (§1) | `[Root].[Behaviour]` | add [method] + invariant | US-[n] |
|
|
43
|
-
| Infrastructure (§4) | `I[Aggregate]Repository` | add [operation] | US-[n] |
|
|
44
|
-
|
|
45
|
-
## REMOVED
|
|
46
|
-
|
|
47
|
-
- [design element removed + why] — or `None`
|
|
48
|
-
|
|
49
|
-
## Cross-layer impact
|
|
50
|
-
|
|
51
|
-
[Which layer sections ripple; shared elements touched (e.g. the aggregate root);
|
|
52
|
-
new migration needed? (yes/no); any contract change other systems couple to.]
|
|
53
|
-
|
|
54
|
-
## Regression targets
|
|
55
|
-
|
|
56
|
-
[Existing user stories / behaviours to re-verify because a shared element changed —
|
|
57
|
-
e.g. re-test CreateOrder when the Order root or its repository is MODIFIED.]
|
|
58
|
-
|
|
59
|
-
## New assumptions / workarounds (from this change)
|
|
60
|
-
|
|
61
|
-
<!-- Anything this change defers — folds into the master's §7 / §8 ledgers on apply. -->
|
|
62
|
-
|
|
63
|
-
- **DA-[n]**: [assumption to clarify later] — or `None`
|
|
64
|
-
- **WA-[n]**: [structural workaround to clear later] — or `None`
|
|
65
|
-
|
|
66
|
-
## On close
|
|
67
|
-
|
|
68
|
-
The design phase amends the master design.md doc-first, appends the Change History
|
|
69
|
-
row, bumps the version (minor), and archives this record under `changes/archive/`.
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# Delta Spec — [CR-ID]: [TITLE]
|
|
2
|
-
|
|
3
|
-
**Feature (aggregate)**: [Aggregate]
|
|
4
|
-
|
|
5
|
-
**Change type**: [new-requirement | changed-requirement | defect | refactor]
|
|
6
|
-
|
|
7
|
-
**Created**: [DATE]
|
|
8
|
-
|
|
9
|
-
**Base version**: [vX.Y] <!-- the current master version; close-delta bumps the minor at the end of the chain -->
|
|
10
|
-
|
|
11
|
-
<!--
|
|
12
|
-
A change request is a DELTA on the feature's existing spec.md — never a new spec.
|
|
13
|
-
Classify every item ADDED / MODIFIED / REMOVED against the current master, using
|
|
14
|
-
the SAME vocabulary as the master spec: System Requirements in EARS, Business
|
|
15
|
-
Rules (BR-n) as invariants, User Stories with Given/When/Then acceptance criteria,
|
|
16
|
-
and — where the domain applies — Constraints & NFRs (CN-n), Data Classification
|
|
17
|
-
(DC-n), and Integration Contracts (IC-n). A CR that changes a retention/audit rule,
|
|
18
|
-
a data-exposure rule, or an interop obligation is a CN-/DC-/IC- delta, not a smuggled
|
|
19
|
-
code change. Adding a command (e.g. UpdateOrder to the Order aggregate) is a new
|
|
20
|
-
user story (US-n), NOT a new feature. Feature = aggregate (see spec-template.md).
|
|
21
|
-
-->
|
|
22
|
-
|
|
23
|
-
## Change summary
|
|
24
|
-
|
|
25
|
-
[One paragraph: what is changing and why.]
|
|
26
|
-
|
|
27
|
-
## ADDED
|
|
28
|
-
|
|
29
|
-
<!-- New user stories, system requirements, business rules, entities, or domain-lift items (CN/DC/IC). -->
|
|
30
|
-
|
|
31
|
-
- **US-[n] — [title]** (Priority: P?)
|
|
32
|
-
- **Why this priority**: [the value and why it ranks here]
|
|
33
|
-
- **Exercises**: [FR-nnn, BR-nnn]
|
|
34
|
-
- **Acceptance**: Given [state], When [action], Then [outcome]
|
|
35
|
-
- **Independent test**: [how this story is verified on its own]
|
|
36
|
-
- **FR-[nnn]**: WHEN [trigger] THE SYSTEM SHALL [response] <!-- EARS — pick the fitting pattern -->
|
|
37
|
-
- **BR-[nnn]**: [a new invariant that must always hold] — or `None`
|
|
38
|
-
- **CN-[nnn]**: [new constraint / NFR — retention, immutability, audit, performance-as-a-limit; cite lifted source] — or `None`
|
|
39
|
-
- **DC-[nnn]**: [new data-classification category or exposure rule] — or `None`
|
|
40
|
-
- **IC-[nnn]**: [new integration contract / interop obligation] — or `None`
|
|
41
|
-
- **Entity**: [new entity / value object + what it represents] — or `None`
|
|
42
|
-
|
|
43
|
-
## MODIFIED
|
|
44
|
-
|
|
45
|
-
- **FR-[nnn]**: was [old] → now [new] <!-- keep EARS form -->
|
|
46
|
-
- **BR-[nnn]**: was [old] → now [new]
|
|
47
|
-
- **CN-[nnn]** / **DC-[nnn]** / **IC-[nnn]**: was [old] → now [new]
|
|
48
|
-
- **US-[n]**: [what changes and why]
|
|
49
|
-
|
|
50
|
-
## REMOVED
|
|
51
|
-
|
|
52
|
-
- [FR / BR / CN / DC / IC / US / entity removed and why] — or `None`
|
|
53
|
-
|
|
54
|
-
## Impact
|
|
55
|
-
|
|
56
|
-
- **Affected FR / BR / CN / DC / IC / US / SC**: [ids]
|
|
57
|
-
- **Regression risk**: [existing behaviour that must still hold — e.g. CreateOrder must still pass]
|
|
58
|
-
- **On merge**: the new US-[n] is tagged `Added: (this CR)` in the master; finalize-delta
|
|
59
|
-
merges this delta into spec.md + design.md and appends the Change History row, and
|
|
60
|
-
close-delta (after analyze-slice) archives the change and bumps the minor version.
|