aw-spec-cli 0.1.8 → 0.1.9
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/.appweaver/DESIGN-FORMAT.md +200 -0
- package/assets/.appweaver/ORCH-DIALECT.md +156 -0
- package/assets/.appweaver/RULE-PARTITION.md +175 -0
- package/assets/.appweaver/STACK-PROFILES.md +461 -0
- package/assets/.appweaver/ai-rules.md +180 -0
- package/assets/.appweaver/domain.json +1 -0
- package/assets/.appweaver/extensions/.registry +19 -0
- package/assets/.appweaver/extensions/agent-context/README.md +57 -0
- package/assets/.appweaver/extensions/agent-context/agent-context-config.yml +4 -0
- package/assets/.appweaver/extensions/agent-context/commands/aw-spec.agent-context.update.md +26 -0
- package/assets/.appweaver/extensions/agent-context/extension.yml +37 -0
- package/assets/.appweaver/extensions/agent-context/scripts/bash/update-agent-context.sh +201 -0
- package/assets/.appweaver/extensions/agent-context/scripts/powershell/update-agent-context.ps1 +237 -0
- package/assets/.appweaver/extensions.yml +16 -0
- package/assets/.appweaver/feature.json +3 -0
- package/assets/.appweaver/init-options.json +9 -0
- package/assets/.appweaver/integration.json +15 -0
- package/assets/.appweaver/integrations/aw-spec.manifest.json +9 -0
- package/assets/.appweaver/interpreter/run-skill.py +1176 -0
- package/assets/.appweaver/memory/constitution.md +50 -0
- package/assets/.appweaver/orchestrator/.smoke-out/smoke.md +6 -0
- package/assets/.appweaver/orchestrator/AGENT-PROFILE.md +94 -0
- package/assets/.appweaver/orchestrator/README.md +300 -0
- package/assets/.appweaver/orchestrator/adapters/claude_agent_sdk.py +74 -0
- package/assets/.appweaver/orchestrator/adapters/google_antigravity.py +74 -0
- package/assets/.appweaver/orchestrator/modules/delta-design.orch.yaml +105 -0
- package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +101 -0
- package/assets/.appweaver/orchestrator/modules/domain-select.orch.yaml +90 -0
- package/assets/.appweaver/orchestrator/modules/lane-gate.orch.yaml +126 -0
- package/assets/.appweaver/orchestrator/modules/tech-select.orch.yaml +123 -0
- package/assets/.appweaver/orchestrator/orchestrator.py +1721 -0
- package/assets/.appweaver/orchestrator/rev-templates/ai-rules.reference.md +40 -0
- package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +101 -0
- package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +245 -0
- package/assets/.appweaver/orchestrator/rev-templates/domain-constitution.skeleton.md +63 -0
- package/assets/.appweaver/orchestrator/rev-templates/domain-requirements.skeleton.md +48 -0
- package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +59 -0
- package/assets/.appweaver/orchestrator/rev-templates/domain-rule-file.skeleton.md +39 -0
- package/assets/.appweaver/orchestrator/rev-templates/frontend-rule-file.skeleton.md +63 -0
- package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +116 -0
- package/assets/.appweaver/orchestrator/rev-templates/technical-constitution.skeleton.md +106 -0
- package/assets/.appweaver/orchestrator/rev-templates/technical-rule-file.skeleton.md +54 -0
- package/assets/.appweaver/orchestrator/rev-templates/ui-constitution.skeleton.md +125 -0
- package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +123 -0
- package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +294 -0
- package/assets/.appweaver/orchestrator/workflows/_smoke.orch.yaml +160 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-add-component.orch.yaml +270 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +934 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-delta-design.orch.yaml +251 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-design.orch.yaml +265 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +437 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +197 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +475 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +181 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +727 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-demo.orch.yaml +100 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-dependency-tree.orch.yaml +69 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-deploy.orch.yaml +85 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-design-ui.orch.yaml +386 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-design.orch.yaml +382 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +462 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-foundation.orch.yaml +265 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-delta.orch.yaml +253 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-ui.orch.yaml +267 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +287 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +238 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +1822 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +293 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-run.orch.yaml +194 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-domain.orch.yaml +83 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +128 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +296 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +439 -0
- package/assets/.appweaver/scripts/bash/aw-inventory.sh +17 -0
- package/assets/.appweaver/scripts/bash/aw-parity.sh +13 -0
- package/assets/.appweaver/scripts/bash/aw-rev-constitution.sh +13 -0
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +2626 -0
- package/assets/.appweaver/scripts/bash/aw-xref.sh +15 -0
- package/assets/.appweaver/scripts/bash/common.sh +533 -0
- package/assets/.appweaver/scripts/powershell/aw-inventory.ps1 +17 -0
- package/assets/.appweaver/scripts/powershell/aw-parity.ps1 +13 -0
- package/assets/.appweaver/scripts/powershell/aw-rev-constitution.ps1 +13 -0
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +2232 -0
- package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +15 -0
- package/assets/.appweaver/scripts/python/aw-component-catalog.py +121 -0
- package/assets/.appweaver/scripts/python/aw-inventory.py +1180 -0
- package/assets/.appweaver/scripts/python/aw-parity.py +380 -0
- package/assets/.appweaver/scripts/python/aw-rev-constitution.py +329 -0
- package/assets/.appweaver/scripts/python/aw-stack.py +1251 -0
- package/assets/.appweaver/scripts/python/aw-xref.py +1330 -0
- package/assets/.appweaver/scripts/python/validate_okf.py +352 -0
- package/assets/.appweaver/stack-profiles/angular.json +16 -0
- package/assets/.appweaver/stack-profiles/blazor-wasm.json +17 -0
- package/assets/.appweaver/stack-profiles/dotnet-webapi.json +15 -0
- package/assets/.appweaver/stack-profiles/go-chi.json +14 -0
- package/assets/.appweaver/stack-profiles/java-spring.json +16 -0
- package/assets/.appweaver/stack-profiles/nextjs.json +16 -0
- package/assets/.appweaver/stack-profiles/node-express.json +15 -0
- package/assets/.appweaver/stack-profiles/python-fastapi.json +16 -0
- package/assets/.appweaver/stack-profiles/react-vite.json +17 -0
- package/assets/.appweaver/stack-profiles/sveltekit.json +16 -0
- package/assets/.appweaver/stack-profiles/vue-vite.json +17 -0
- package/assets/.appweaver/stacks.json +3 -0
- package/assets/.appweaver/templates/constitution-domain-template.md +116 -0
- package/assets/.appweaver/templates/constitution-technical-template.md +108 -0
- package/assets/.appweaver/templates/constitution-ui-template.md +116 -0
- package/assets/.appweaver/templates/delta-design-template.md +69 -0
- package/assets/.appweaver/templates/delta-spec-template.md +63 -0
- package/assets/.appweaver/templates/design-template.md +383 -0
- package/assets/.appweaver/templates/okf-concept-template.md +114 -0
- package/assets/.appweaver/templates/parity-manifest-template.json +49 -0
- package/assets/.appweaver/templates/source-map-template.md +85 -0
- package/assets/.appweaver/templates/spec-template.md +309 -0
- package/assets/.appweaver/templates/ui-design-template.md +273 -0
- package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.Reference.pack.txt +25248 -0
- package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.pack.txt +5726 -0
- package/assets/foundations/dotnet/ModularMonolith/pack-foundation.ps1 +145 -0
- package/assets/foundations/dotnet/ModularMonolith/pack-foundation.sh +196 -0
- package/assets/foundations/dotnet/ModularMonolith/pack-reference-foundation.ps1 +157 -0
- package/assets/foundations/dotnet/ModularMonolith/pack-reference-foundation.sh +209 -0
- package/assets/foundations/dotnet/ModularMonolith/unpack-foundation.ps1 +126 -0
- package/assets/foundations/dotnet/ModularMonolith/unpack-foundation.sh +217 -0
- package/assets/foundations/dotnet/ModularMonolith/unpack-reference-foundation.ps1 +129 -0
- package/assets/foundations/dotnet/ModularMonolith/unpack-reference-foundation.sh +218 -0
- package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.Reference.pack.txt +19318 -0
- package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.pack.txt +2833 -0
- package/assets/foundations/dotnet/VerticalSlice/pack-foundation.ps1 +145 -0
- package/assets/foundations/dotnet/VerticalSlice/pack-foundation.sh +196 -0
- package/assets/foundations/dotnet/VerticalSlice/pack-reference-foundation.ps1 +157 -0
- package/assets/foundations/dotnet/VerticalSlice/pack-reference-foundation.sh +209 -0
- package/assets/foundations/dotnet/VerticalSlice/unpack-foundation.ps1 +126 -0
- package/assets/foundations/dotnet/VerticalSlice/unpack-foundation.sh +217 -0
- package/assets/foundations/dotnet/VerticalSlice/unpack-reference-foundation.ps1 +129 -0
- package/assets/foundations/dotnet/VerticalSlice/unpack-reference-foundation.sh +218 -0
- package/assets/foundations/dotnet/components/Email/README.md +119 -0
- package/assets/foundations/dotnet/components/Email/ReferenceEndpoints/SendEmailEndpoint.cs +84 -0
- package/assets/foundations/dotnet/components/Email/integration.md +300 -0
- package/assets/foundations/dotnet/components/Jobs/README.md +113 -0
- package/assets/foundations/dotnet/components/Jobs/integration.md +259 -0
- package/assets/foundations/dotnet/components/QrGenerator/README.md +85 -0
- package/assets/foundations/dotnet/components/QrGenerator/ReferenceEndpoints/GenerateQrEndpoint.cs +57 -0
- package/assets/foundations/dotnet/components/QrGenerator/integration.md +230 -0
- package/assets/foundations/dotnet/components/README.md +74 -0
- package/assets/guardrails/README.md +222 -0
- package/assets/guardrails/domain/healthcare/ai-rules.domain.md +34 -0
- package/assets/guardrails/domain/healthcare/api-versioning.domain.md +17 -0
- package/assets/guardrails/domain/healthcare/auditing.domain.md +97 -0
- package/assets/guardrails/domain/healthcare/authorization.domain.md +26 -0
- package/assets/guardrails/domain/healthcare/clinical-terms.md +20 -0
- package/assets/guardrails/domain/healthcare/coding-standards.domain.md +26 -0
- package/assets/guardrails/domain/healthcare/consent.md +19 -0
- package/assets/guardrails/domain/healthcare/cqrs.domain.md +12 -0
- package/assets/guardrails/domain/healthcare/data-classification.domain.md +28 -0
- package/assets/guardrails/domain/healthcare/dependency-policy.domain.md +11 -0
- package/assets/guardrails/domain/healthcare/dicom.md +15 -0
- package/assets/guardrails/domain/healthcare/domain-constitution.md +119 -0
- package/assets/guardrails/domain/healthcare/domain-driven-design.domain.md +28 -0
- package/assets/guardrails/domain/healthcare/eventing.domain.md +18 -0
- package/assets/guardrails/domain/healthcare/fhir.md +36 -0
- package/assets/guardrails/domain/healthcare/folder-structure.domain.md +25 -0
- package/assets/guardrails/domain/healthcare/hipaa.md +34 -0
- package/assets/guardrails/domain/healthcare/hl7.md +15 -0
- package/assets/guardrails/domain/healthcare/ihe-conformance.md +31 -0
- package/assets/guardrails/domain/healthcare/integration-terms.md +21 -0
- package/assets/guardrails/domain/healthcare/persistence.domain.md +20 -0
- package/assets/guardrails/domain/healthcare/terminology.md +30 -0
- package/assets/guardrails/domain/healthcare/testing.domain.md +15 -0
- package/assets/guardrails/domain/healthcare/ux/accessibility-standards.md +36 -0
- package/assets/guardrails/domain/healthcare/ux/content-design.md +32 -0
- package/assets/guardrails/domain/healthcare/ux/interaction-states.md +44 -0
- package/assets/guardrails/domain/healthcare/ux/journey-mapping.md +40 -0
- package/assets/guardrails/domain/healthcare/vertical-slices.domain.md +18 -0
- package/assets/guardrails/domain/healthcare/zero-trust.domain.md +12 -0
- package/assets/guardrails/domain/pharma/ai-rules.domain.md +32 -0
- package/assets/guardrails/domain/pharma/api-versioning.domain.md +20 -0
- package/assets/guardrails/domain/pharma/atc.md +22 -0
- package/assets/guardrails/domain/pharma/auditing.domain.md +90 -0
- package/assets/guardrails/domain/pharma/authorization.domain.md +25 -0
- package/assets/guardrails/domain/pharma/coding-standards.domain.md +28 -0
- package/assets/guardrails/domain/pharma/cqrs.domain.md +16 -0
- package/assets/guardrails/domain/pharma/data-classification.domain.md +24 -0
- package/assets/guardrails/domain/pharma/data-provenance.md +21 -0
- package/assets/guardrails/domain/pharma/dependency-policy.domain.md +11 -0
- package/assets/guardrails/domain/pharma/domain-constitution.md +113 -0
- package/assets/guardrails/domain/pharma/domain-driven-design.domain.md +36 -0
- package/assets/guardrails/domain/pharma/domain-requirements.sample.md +103 -0
- package/assets/guardrails/domain/pharma/eventing.domain.md +18 -0
- package/assets/guardrails/domain/pharma/fhir.md +31 -0
- package/assets/guardrails/domain/pharma/folder-structure.domain.md +26 -0
- package/assets/guardrails/domain/pharma/hl7.md +17 -0
- package/assets/guardrails/domain/pharma/idmp.md +26 -0
- package/assets/guardrails/domain/pharma/integration-terms.md +21 -0
- package/assets/guardrails/domain/pharma/ndc.md +28 -0
- package/assets/guardrails/domain/pharma/persistence.domain.md +23 -0
- package/assets/guardrails/domain/pharma/pharma-terms.md +27 -0
- package/assets/guardrails/domain/pharma/product-lifecycle.domain.md +30 -0
- package/assets/guardrails/domain/pharma/regulatory-compliance.md +19 -0
- package/assets/guardrails/domain/pharma/terminology.md +32 -0
- package/assets/guardrails/domain/pharma/testing.domain.md +19 -0
- package/assets/guardrails/domain/pharma/vertical-slices.domain.md +17 -0
- package/assets/guardrails/domain/pharma/zero-trust.domain.md +12 -0
- package/assets/guardrails/projects/dotnet-default-project/domain/domain-requirements.md +58 -0
- package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +113 -0
- package/assets/guardrails/projects/dotnet-default-project/frontend/ui-requirements.md +43 -0
- package/assets/guardrails/projects/dotnet-default-project/service/technical-requirements.md +40 -0
- package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +263 -0
- package/assets/guardrails/requirement-spec.txt +27 -0
- package/assets/guardrails/technology/dotnet-webapi/PACKAGING.md +184 -0
- package/assets/guardrails/technology/dotnet-webapi/api-versioning.md +44 -0
- package/assets/guardrails/technology/dotnet-webapi/auditing.md +96 -0
- package/assets/guardrails/technology/dotnet-webapi/authorization-matrix.md +14 -0
- package/assets/guardrails/technology/dotnet-webapi/authorization.md +39 -0
- package/assets/guardrails/technology/dotnet-webapi/clean-architecture.md +54 -0
- package/assets/guardrails/technology/dotnet-webapi/coding-standards.md +87 -0
- package/assets/guardrails/technology/dotnet-webapi/cqrs.md +63 -0
- package/assets/guardrails/technology/dotnet-webapi/data-classification.md +38 -0
- package/assets/guardrails/technology/dotnet-webapi/dependency-policy.md +40 -0
- package/assets/guardrails/technology/dotnet-webapi/domain-driven-design.md +91 -0
- package/assets/guardrails/technology/dotnet-webapi/eventing.md +57 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/api-versioning.md +75 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/auditing.md +84 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/authorization.md +68 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/clean-architecture.md +37 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/cqrs.md +97 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/domain-driven-design.md +141 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/eventing.md +46 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/persistence.md +207 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/vertical-slices.md +151 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/zero-trust.md +48 -0
- package/assets/guardrails/technology/dotnet-webapi/folder-structure.md +61 -0
- package/assets/guardrails/technology/dotnet-webapi/modular-monolith.md +47 -0
- package/assets/guardrails/technology/dotnet-webapi/persistence.md +82 -0
- package/assets/guardrails/technology/dotnet-webapi/technology-constitution.md +162 -0
- package/assets/guardrails/technology/dotnet-webapi/testing.md +55 -0
- package/assets/guardrails/technology/dotnet-webapi/vertical-slices.md +70 -0
- package/assets/guardrails/technology/dotnet-webapi/zero-trust.md +48 -0
- package/assets/guardrails/technology/java-spring/README.md +62 -0
- package/assets/guardrails/technology/nextjs/accessibility-implementation.md +56 -0
- package/assets/guardrails/technology/nextjs/api-client.md +56 -0
- package/assets/guardrails/technology/nextjs/api-integration.dotnet-webapi.md +174 -0
- package/assets/guardrails/technology/nextjs/api-integration.java-spring.md +203 -0
- package/assets/guardrails/technology/nextjs/auth-session.md +68 -0
- package/assets/guardrails/technology/nextjs/client-security.md +69 -0
- package/assets/guardrails/technology/nextjs/component-architecture.md +60 -0
- package/assets/guardrails/technology/nextjs/component-conventions.md +58 -0
- package/assets/guardrails/technology/nextjs/design-system.md +60 -0
- package/assets/guardrails/technology/nextjs/error-handling.md +72 -0
- package/assets/guardrails/technology/nextjs/forms-and-validation.md +75 -0
- package/assets/guardrails/technology/nextjs/frontend-testing.md +94 -0
- package/assets/guardrails/technology/nextjs/i18n.md +57 -0
- package/assets/guardrails/technology/nextjs/observability.md +67 -0
- package/assets/guardrails/technology/nextjs/performance-budgets.md +66 -0
- package/assets/guardrails/technology/nextjs/routing-navigation.md +52 -0
- package/assets/guardrails/technology/nextjs/state-management.md +70 -0
- package/assets/guardrails/technology/nextjs/ui-constitution.md +166 -0
- package/package.json +1 -1
- package/assets/docs/pharma-constitution.my-ref.md +0 -86
|
@@ -0,0 +1,352 @@
|
|
|
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` / `ui_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
|
+
# Both halves of the slice's file list get the same existence check — a stale UI
|
|
150
|
+
# path is exactly as misleading to a reverse step as a stale service one.
|
|
151
|
+
for key in ("source_files", "ui_source_files"):
|
|
152
|
+
sf = data.get(key)
|
|
153
|
+
if not isinstance(sf, list):
|
|
154
|
+
continue
|
|
155
|
+
for entry in sf:
|
|
156
|
+
if not isinstance(entry, str) or not entry.strip():
|
|
157
|
+
continue
|
|
158
|
+
candidate = os.path.join(repo_root, entry)
|
|
159
|
+
if not os.path.exists(candidate):
|
|
160
|
+
warnings.append(f"W6 {rel}: {key} entry '{entry}' does not exist")
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def check_open_questions(data: dict, rel: str):
|
|
164
|
+
oq = data.get("open_questions")
|
|
165
|
+
if not isinstance(oq, list):
|
|
166
|
+
return
|
|
167
|
+
pending = [q for q in oq if isinstance(q, str) and q.strip()]
|
|
168
|
+
if pending:
|
|
169
|
+
n = len(pending)
|
|
170
|
+
warnings.append(
|
|
171
|
+
f"W8 {rel}: {n} unresolved open question{'s' if n != 1 else ''} "
|
|
172
|
+
f"— run /speckit.okf.clarify"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def check_links(body: str, rel: str, filedir: str, bundle: str):
|
|
177
|
+
for target in LINK.findall(body):
|
|
178
|
+
target = target.split("#")[0].strip()
|
|
179
|
+
if not target or "://" in target or target.startswith("mailto:"):
|
|
180
|
+
continue
|
|
181
|
+
if target.startswith("/"):
|
|
182
|
+
dest = os.path.join(bundle, target.lstrip("/"))
|
|
183
|
+
else:
|
|
184
|
+
dest = os.path.join(filedir, target)
|
|
185
|
+
# Directory links (progressive disclosure) are fine if the dir exists.
|
|
186
|
+
if not (os.path.exists(dest) or os.path.exists(dest.rstrip("/"))):
|
|
187
|
+
warnings.append(f"W2 {rel}: broken link -> {target}")
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def check_index(path: str, rel: str, is_root: bool, bundle: str):
|
|
191
|
+
text, err = read_text(path)
|
|
192
|
+
if text is None:
|
|
193
|
+
errors.append(f"E1 {rel}: could not read file ({err})")
|
|
194
|
+
return
|
|
195
|
+
fm, body = split_frontmatter(text)
|
|
196
|
+
if fm is not None:
|
|
197
|
+
if not is_root:
|
|
198
|
+
errors.append(f"E3 {rel}: index.md must not contain frontmatter (only the bundle root may, per §11)")
|
|
199
|
+
else:
|
|
200
|
+
data = parse_yaml(fm) or {}
|
|
201
|
+
extra = set(data) - {"okf_version"}
|
|
202
|
+
if extra:
|
|
203
|
+
errors.append(f"E3 {rel}: root index.md frontmatter may only declare okf_version (found: {sorted(extra)})")
|
|
204
|
+
check_links(body if fm is not None else text, rel, os.path.dirname(path), bundle)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def check_log(path: str, rel: str):
|
|
208
|
+
text, err = read_text(path)
|
|
209
|
+
if text is None:
|
|
210
|
+
errors.append(f"E1 {rel}: could not read file ({err})")
|
|
211
|
+
return
|
|
212
|
+
lines = text.splitlines()
|
|
213
|
+
for i, ln in enumerate(lines):
|
|
214
|
+
if not ln.startswith("## "):
|
|
215
|
+
continue
|
|
216
|
+
stripped = ln.rstrip()
|
|
217
|
+
if not DATE_HEADING.match(stripped):
|
|
218
|
+
errors.append(f"E3 {rel}: log heading not ISO 8601 YYYY-MM-DD: '{stripped}'")
|
|
219
|
+
continue
|
|
220
|
+
# Look for a "Commit: `<sha>`" line before the next date heading.
|
|
221
|
+
found_sha = None
|
|
222
|
+
for j in range(i + 1, len(lines)):
|
|
223
|
+
if lines[j].startswith("## "):
|
|
224
|
+
break
|
|
225
|
+
m = COMMIT_LINE.match(lines[j].strip())
|
|
226
|
+
if m:
|
|
227
|
+
found_sha = m.group(1)
|
|
228
|
+
break
|
|
229
|
+
if found_sha is None:
|
|
230
|
+
errors.append(f"E4 {rel}: date block '{stripped}' missing required 'Commit: `<sha>`' line")
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def find_repo_root(bundle: str) -> str:
|
|
234
|
+
p = bundle
|
|
235
|
+
while True:
|
|
236
|
+
if os.path.isdir(os.path.join(p, ".git")):
|
|
237
|
+
return p
|
|
238
|
+
parent = os.path.dirname(p)
|
|
239
|
+
if parent == p:
|
|
240
|
+
return os.path.dirname(bundle)
|
|
241
|
+
p = parent
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def load_config_excludes(path: str) -> list[str]:
|
|
245
|
+
if not path or not os.path.isfile(path):
|
|
246
|
+
return []
|
|
247
|
+
text, err = read_text(path)
|
|
248
|
+
if err is not None or text is None:
|
|
249
|
+
return []
|
|
250
|
+
if HAVE_YAML:
|
|
251
|
+
try:
|
|
252
|
+
data = yaml.safe_load(text) or {}
|
|
253
|
+
except yaml.YAMLError:
|
|
254
|
+
return []
|
|
255
|
+
excl = (data.get("okf") or {}).get("exclude") or []
|
|
256
|
+
return [str(x) for x in excl if isinstance(x, str)]
|
|
257
|
+
# Lenient fallback: scan for an `exclude:` block of `- "glob"` lines.
|
|
258
|
+
globs = []
|
|
259
|
+
in_exclude = False
|
|
260
|
+
for line in text.splitlines():
|
|
261
|
+
stripped = line.strip()
|
|
262
|
+
if re.match(r"^exclude\s*:\s*$", stripped):
|
|
263
|
+
in_exclude = True
|
|
264
|
+
continue
|
|
265
|
+
if in_exclude:
|
|
266
|
+
m = re.match(r"^-\s*['\"]?([^'\"#]+)['\"]?\s*(#.*)?$", stripped)
|
|
267
|
+
if m:
|
|
268
|
+
globs.append(m.group(1).strip())
|
|
269
|
+
continue
|
|
270
|
+
if stripped and not stripped.startswith("#"):
|
|
271
|
+
in_exclude = False
|
|
272
|
+
return globs
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def is_excluded(rel: str, patterns: list[str]) -> bool:
|
|
276
|
+
return any(fnmatch.fnmatch(rel, pat) for pat in patterns)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def main() -> int:
|
|
280
|
+
parser = argparse.ArgumentParser(
|
|
281
|
+
description="Conformance checker for OKF v0.1 knowledge bundles.",
|
|
282
|
+
)
|
|
283
|
+
parser.add_argument("bundle_dir", help="Path to the knowledge bundle directory")
|
|
284
|
+
parser.add_argument("--config", help="Path to okf-config.yml (exclude list is honored)")
|
|
285
|
+
parser.add_argument("--exclude", action="append", default=[], help="Glob to exclude (repeatable)")
|
|
286
|
+
parser.add_argument("--repo-root", help="Repo root for resolving source_files (default: nearest .git above bundle_dir)")
|
|
287
|
+
parser.add_argument("--json", action="store_true", help="Emit machine-readable JSON instead of text")
|
|
288
|
+
args = parser.parse_args()
|
|
289
|
+
|
|
290
|
+
if not os.path.isdir(args.bundle_dir):
|
|
291
|
+
parser.error(f"{args.bundle_dir} is not a directory")
|
|
292
|
+
|
|
293
|
+
bundle = os.path.abspath(args.bundle_dir)
|
|
294
|
+
repo_root = os.path.abspath(args.repo_root) if args.repo_root else find_repo_root(bundle)
|
|
295
|
+
exclude_patterns = load_config_excludes(args.config) + list(args.exclude)
|
|
296
|
+
|
|
297
|
+
if not HAVE_YAML:
|
|
298
|
+
warnings.append("W0 (global): PyYAML not installed — using lenient fallback parser, results may be less strict")
|
|
299
|
+
|
|
300
|
+
concepts = 0
|
|
301
|
+
for dirpath, dirnames, filenames in os.walk(bundle):
|
|
302
|
+
dirnames[:] = [d for d in dirnames if not d.startswith(".")]
|
|
303
|
+
rel_dir = os.path.relpath(dirpath, bundle)
|
|
304
|
+
mds = [
|
|
305
|
+
f for f in filenames
|
|
306
|
+
if f.endswith(".md") and not is_excluded(
|
|
307
|
+
os.path.relpath(os.path.join(dirpath, f), bundle), exclude_patterns
|
|
308
|
+
)
|
|
309
|
+
]
|
|
310
|
+
if any(f not in RESERVED for f in mds) and "index.md" not in mds:
|
|
311
|
+
warnings.append(f"W3 {rel_dir or '.'}: directory has concepts but no index.md")
|
|
312
|
+
for f in mds:
|
|
313
|
+
path = os.path.join(dirpath, f)
|
|
314
|
+
rel = os.path.relpath(path, bundle)
|
|
315
|
+
if f == "index.md":
|
|
316
|
+
check_index(path, rel, dirpath == bundle, bundle)
|
|
317
|
+
elif f == "log.md":
|
|
318
|
+
check_log(path, rel)
|
|
319
|
+
else:
|
|
320
|
+
concepts += 1
|
|
321
|
+
check_concept(path, rel, bundle, repo_root)
|
|
322
|
+
|
|
323
|
+
# W7: duplicate (type, title) pairs across the bundle.
|
|
324
|
+
seen: dict[tuple[str, str], str] = {}
|
|
325
|
+
for rel, t, title in concept_meta:
|
|
326
|
+
key = (t.lower(), title.lower())
|
|
327
|
+
if key in seen:
|
|
328
|
+
warnings.append(f"W7 {rel}: possible duplicate concept — same type+title as {seen[key]}")
|
|
329
|
+
else:
|
|
330
|
+
seen[key] = rel
|
|
331
|
+
|
|
332
|
+
if args.json:
|
|
333
|
+
print(json.dumps({
|
|
334
|
+
"concepts": concepts,
|
|
335
|
+
"errors": errors,
|
|
336
|
+
"warnings": warnings,
|
|
337
|
+
"result": "NON-CONFORMANT" if errors else "CONFORMANT",
|
|
338
|
+
}, indent=2))
|
|
339
|
+
else:
|
|
340
|
+
print(f"OKF validation of {bundle}")
|
|
341
|
+
print(f" concepts: {concepts} errors: {len(errors)} warnings: {len(warnings)}\n")
|
|
342
|
+
for e in errors:
|
|
343
|
+
print(f" ERROR {e}")
|
|
344
|
+
for w in warnings:
|
|
345
|
+
print(f" WARNING {w}")
|
|
346
|
+
print("\nRESULT:", "NON-CONFORMANT" if errors else "CONFORMANT (OKF v0.1)")
|
|
347
|
+
|
|
348
|
+
return 1 if errors else 0
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
if __name__ == "__main__":
|
|
352
|
+
sys.exit(main())
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "angular",
|
|
3
|
+
"kind": "frontend",
|
|
4
|
+
"title": "Angular",
|
|
5
|
+
"language": "TypeScript",
|
|
6
|
+
"ecosystem": "node",
|
|
7
|
+
"root": "web/",
|
|
8
|
+
"run_from": "root",
|
|
9
|
+
"build": "npm run build",
|
|
10
|
+
"test": "npm test",
|
|
11
|
+
"run": "npm start",
|
|
12
|
+
"prototype_run": "npm start --prefix prototype",
|
|
13
|
+
"detect": [
|
|
14
|
+
"web/angular.json"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "blazor-wasm",
|
|
3
|
+
"kind": "frontend",
|
|
4
|
+
"title": "Blazor WebAssembly",
|
|
5
|
+
"language": "C#",
|
|
6
|
+
"ecosystem": "dotnet",
|
|
7
|
+
"root": "web/",
|
|
8
|
+
"run_from": "root",
|
|
9
|
+
"build": "dotnet build",
|
|
10
|
+
"test": "dotnet test",
|
|
11
|
+
"run": "dotnet run",
|
|
12
|
+
"prototype_run": "dotnet run --project prototype/",
|
|
13
|
+
"detect": [
|
|
14
|
+
"web/**/*.csproj",
|
|
15
|
+
"web/**/wwwroot/index.html"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "dotnet-webapi",
|
|
3
|
+
"kind": "backend",
|
|
4
|
+
"title": "ASP.NET Core Web API",
|
|
5
|
+
"language": "C#",
|
|
6
|
+
"ecosystem": "dotnet",
|
|
7
|
+
"root": "src/",
|
|
8
|
+
"build": "dotnet build",
|
|
9
|
+
"test": "dotnet test",
|
|
10
|
+
"run": "dotnet run --project {{root}}",
|
|
11
|
+
"detect": [
|
|
12
|
+
"*.sln",
|
|
13
|
+
"src/**/*.csproj"
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "java-spring",
|
|
3
|
+
"kind": "backend",
|
|
4
|
+
"title": "Spring Boot",
|
|
5
|
+
"language": "Java",
|
|
6
|
+
"ecosystem": "java",
|
|
7
|
+
"root": "src/main/java/",
|
|
8
|
+
"run_from": "root",
|
|
9
|
+
"build": "./mvnw package -DskipTests",
|
|
10
|
+
"test": "./mvnw test",
|
|
11
|
+
"run": "./mvnw spring-boot:run",
|
|
12
|
+
"detect": [
|
|
13
|
+
"pom.xml",
|
|
14
|
+
"build.gradle"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "nextjs",
|
|
3
|
+
"kind": "frontend",
|
|
4
|
+
"title": "Next.js",
|
|
5
|
+
"language": "TypeScript",
|
|
6
|
+
"ecosystem": "node",
|
|
7
|
+
"root": "web/",
|
|
8
|
+
"run_from": "root",
|
|
9
|
+
"build": "npm run build",
|
|
10
|
+
"test": "npm test",
|
|
11
|
+
"run": "npm run dev",
|
|
12
|
+
"prototype_run": "npm run dev --prefix prototype",
|
|
13
|
+
"detect": [
|
|
14
|
+
"web/next.config.*"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "node-express",
|
|
3
|
+
"kind": "backend",
|
|
4
|
+
"title": "Express + TypeScript",
|
|
5
|
+
"language": "Node.js",
|
|
6
|
+
"ecosystem": "node",
|
|
7
|
+
"root": "src/",
|
|
8
|
+
"build": "npm run build",
|
|
9
|
+
"test": "npm test",
|
|
10
|
+
"run": "npm run dev",
|
|
11
|
+
"detect": [
|
|
12
|
+
"package.json",
|
|
13
|
+
"src/**/*.ts"
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "python-fastapi",
|
|
3
|
+
"kind": "backend",
|
|
4
|
+
"title": "FastAPI",
|
|
5
|
+
"language": "Python",
|
|
6
|
+
"ecosystem": "python",
|
|
7
|
+
"root": "app/",
|
|
8
|
+
"build": "python -m compileall {{root}}",
|
|
9
|
+
"test": "pytest",
|
|
10
|
+
"run": "uvicorn app.main:app --reload",
|
|
11
|
+
"detect": [
|
|
12
|
+
"pyproject.toml",
|
|
13
|
+
"requirements.txt",
|
|
14
|
+
"app/main.py"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "react-vite",
|
|
3
|
+
"kind": "frontend",
|
|
4
|
+
"title": "React + Vite",
|
|
5
|
+
"language": "TypeScript",
|
|
6
|
+
"ecosystem": "node",
|
|
7
|
+
"root": "web/",
|
|
8
|
+
"run_from": "root",
|
|
9
|
+
"build": "npm run build",
|
|
10
|
+
"test": "npm test",
|
|
11
|
+
"run": "npm run dev",
|
|
12
|
+
"prototype_run": "npm run dev --prefix prototype",
|
|
13
|
+
"detect": [
|
|
14
|
+
"web/package.json",
|
|
15
|
+
"web/vite.config.*"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "sveltekit",
|
|
3
|
+
"kind": "frontend",
|
|
4
|
+
"title": "SvelteKit",
|
|
5
|
+
"language": "TypeScript",
|
|
6
|
+
"ecosystem": "node",
|
|
7
|
+
"root": "web/",
|
|
8
|
+
"run_from": "root",
|
|
9
|
+
"build": "npm run build",
|
|
10
|
+
"test": "npm test",
|
|
11
|
+
"run": "npm run dev",
|
|
12
|
+
"prototype_run": "npm run dev --prefix prototype",
|
|
13
|
+
"detect": [
|
|
14
|
+
"web/svelte.config.*"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "vue-vite",
|
|
3
|
+
"kind": "frontend",
|
|
4
|
+
"title": "Vue 3 + Vite",
|
|
5
|
+
"language": "TypeScript",
|
|
6
|
+
"ecosystem": "node",
|
|
7
|
+
"root": "web/",
|
|
8
|
+
"run_from": "root",
|
|
9
|
+
"build": "npm run build",
|
|
10
|
+
"test": "npm test",
|
|
11
|
+
"run": "npm run dev",
|
|
12
|
+
"prototype_run": "npm run dev --prefix prototype",
|
|
13
|
+
"detect": [
|
|
14
|
+
"web/vite.config.*",
|
|
15
|
+
"web/src/App.vue"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# [PROJECT_NAME] Constitution — [DOMAIN_NAME] Addenda
|
|
2
|
+
<!--
|
|
3
|
+
AppWeaver split-constitution model. This is the DOMAIN half — the domain
|
|
4
|
+
addenda derived from the set mapped under "## /aw-spec-constitution" in the project's
|
|
5
|
+
domain-rule-bindings.md: the mapped industry corpus's domain-constitution.md, the STANDARD
|
|
6
|
+
industry rules beside it, and the project team's own domain/domain-requirements.md. In a
|
|
7
|
+
project reverse-engineered by `onboard` that map points at the project's OWN generated
|
|
8
|
+
domain rule files and its own charter, both derived from the code — same partition, a
|
|
9
|
+
different corpus. It carries ONLY domain-specific material:
|
|
10
|
+
the domain principles, domain identifiers/terminology, regulatory regime,
|
|
11
|
+
interoperability surfaces, and domain governance overrides.
|
|
12
|
+
|
|
13
|
+
══ THIS FILE IS SHAPE. THE MAPPED CORPUS IS CONTENT. ═════════════════════════════
|
|
14
|
+
This template is part of the kit and governs projects in any industry. It therefore
|
|
15
|
+
names NO industry, regulation, terminology system, identifier or modelling methodology
|
|
16
|
+
anywhere — deliberately. An example that named one would be a suggestion, and a
|
|
17
|
+
suggestion in a template becomes ratified law in a domain it does not fit.
|
|
18
|
+
|
|
19
|
+
Take the principles — their SUBJECTS, their WORDING, their ORDER and their COUNT —
|
|
20
|
+
from the mapped domain rule files and the charter they are headed by. The three slots
|
|
21
|
+
below are a scaffold, not a set.
|
|
22
|
+
═══════════════════════════════════════════════════════════════════════════════════
|
|
23
|
+
|
|
24
|
+
It EXTENDS the paired constitution.technical.md — it does not restate that half's
|
|
25
|
+
technical principles or the base governance; it references them and adds the domain
|
|
26
|
+
layer on top.
|
|
27
|
+
|
|
28
|
+
Loading policy: this file is a SPECIFY-only standing input and a GATE checklist.
|
|
29
|
+
It is deliberately NOT loaded by design or implement — domain reaches those
|
|
30
|
+
phases only through the spec.
|
|
31
|
+
|
|
32
|
+
THIN AUTHORITY: each principle is ONE declarative, testable line + a pointer to its
|
|
33
|
+
rule doc. Do NOT restate the rule doc's detail here — this file says WHICH domain rules
|
|
34
|
+
are supreme/non-negotiable and adopted; the domain docs (also loaded in specify) carry
|
|
35
|
+
the detail. That keeps the authority/detail overlap to one line.
|
|
36
|
+
-->
|
|
37
|
+
|
|
38
|
+
## Adopted Modelling Standard
|
|
39
|
+
|
|
40
|
+
[MODELLING_STANDARD_STATEMENT]
|
|
41
|
+
<!--
|
|
42
|
+
Form: ONE thin authority line naming the domain-modelling methodology this project has
|
|
43
|
+
ratified, the principle of the technical half that mandates it CITED BY ITS ACTUAL
|
|
44
|
+
NUMERAL, and the rule docs that carry the method.
|
|
45
|
+
|
|
46
|
+
READ constitution.technical.md (in context) to find that numeral. It is a property of
|
|
47
|
+
this project's technology base and differs between projects — never a fixed number, and
|
|
48
|
+
never assumed from another project's constitution. If the technical half mandates no
|
|
49
|
+
modelling methodology, say exactly that instead of inventing one.
|
|
50
|
+
|
|
51
|
+
This section exists because specify loads THIS file and not constitution.technical, so
|
|
52
|
+
this line is the only way the modelling mandate reaches specify at all. State the
|
|
53
|
+
mandate and point at the docs; do not restate the method itself — the rule docs are
|
|
54
|
+
loaded in specify alongside this file.
|
|
55
|
+
-->
|
|
56
|
+
|
|
57
|
+
## Domain Principles
|
|
58
|
+
<!--
|
|
59
|
+
Numbered to CONTINUE the technical half: start at the number FOLLOWING its last
|
|
60
|
+
principle. Read constitution.technical.md (in context) to find that number — it is
|
|
61
|
+
whatever the mapped technology base ended on, not a fixed value. A base ending at XII
|
|
62
|
+
makes the first domain principle XIII; one ending at XXVI makes it XXVII. Numbering
|
|
63
|
+
runs unbroken across the two halves: no gap, no restart, no number used by both.
|
|
64
|
+
|
|
65
|
+
Where constitution.technical.md is ABSENT, the project declared no service lane and
|
|
66
|
+
there is no technical half: this half OPENS the sequence at I. That is a complete
|
|
67
|
+
constitution for a UI-only project, not a broken one — the lane that would have
|
|
68
|
+
ratified a technology base does not exist, and the UI half runs its own UX-* series
|
|
69
|
+
alongside. Say so in the Sync Impact Report so the gap is visible rather than guessed.
|
|
70
|
+
-->
|
|
71
|
+
|
|
72
|
+
### [DOMAIN_PRINCIPLE_1_NAME]
|
|
73
|
+
<!-- Form: "<next numeral>. <the domain concern this rule governs> (NON-NEGOTIABLE)".
|
|
74
|
+
Title by the CONCERN, never by a feature or an aggregate name. -->
|
|
75
|
+
[DOMAIN_PRINCIPLE_1_DESCRIPTION]
|
|
76
|
+
<!-- Form: ONE declarative, testable sentence stating what the domain mandates, the
|
|
77
|
+
technical principle it EXTENDS (by that principle's actual numeral), and a pointer
|
|
78
|
+
to the rule file that carries the detail. -->
|
|
79
|
+
|
|
80
|
+
### [DOMAIN_PRINCIPLE_2_NAME]
|
|
81
|
+
[DOMAIN_PRINCIPLE_2_DESCRIPTION]
|
|
82
|
+
|
|
83
|
+
### [DOMAIN_PRINCIPLE_3_NAME]
|
|
84
|
+
<!-- Form: continue one '### ' block per principle the mapped domain rules actually
|
|
85
|
+
state, then stop. Do not import a domain rule the mapped set does not carry. -->
|
|
86
|
+
[DOMAIN_PRINCIPLE_3_DESCRIPTION]
|
|
87
|
+
|
|
88
|
+
## Domain Folder Additions
|
|
89
|
+
|
|
90
|
+
[FOLDER_ADDITIONS_CONTENT]
|
|
91
|
+
<!-- Form: only the structural ADDITIONS this domain requires on top of the base layout
|
|
92
|
+
in constitution.technical.md, with a pointer to the rule file that defines them; or
|
|
93
|
+
"none beyond the base". -->
|
|
94
|
+
|
|
95
|
+
## Regulatory Posture & Security Addenda
|
|
96
|
+
|
|
97
|
+
[REGULATORY_POSTURE_CONTENT]
|
|
98
|
+
<!-- Form: the regulatory regime this domain operates under, its data-integrity and
|
|
99
|
+
retention expectations, its identifier / terminology / interoperability alignment,
|
|
100
|
+
and its data classification — each as a thin adopted line citing the rule file that
|
|
101
|
+
carries it. Take every one of these from the mapped rules; a domain with no
|
|
102
|
+
regulatory regime says so. -->
|
|
103
|
+
|
|
104
|
+
## Domain Governance Overrides
|
|
105
|
+
<!-- Extends the Governance section of constitution.technical.md with domain-specific
|
|
106
|
+
change authority. -->
|
|
107
|
+
|
|
108
|
+
[DOMAIN_GOVERNANCE_CONTENT]
|
|
109
|
+
<!-- Form: who must approve a change to a domain identifier / classification / lifecycle
|
|
110
|
+
rule, who owns each bounded context, the review cadence, and the exception process.
|
|
111
|
+
The base amendment procedure + SemVer policy in constitution.technical.md still
|
|
112
|
+
apply and are not restated here. -->
|
|
113
|
+
|
|
114
|
+
**Version**: [CONSTITUTION_VERSION] | **Ratified**: [RATIFICATION_DATE] | **Last Amended**: [LAST_AMENDED_DATE]
|
|
115
|
+
<!-- ISO YYYY-MM-DD dates. Versions INDEPENDENTLY of constitution.technical.md — a change
|
|
116
|
+
to the domain's rules bumps this file only. -->
|