atdd 0.2.1__py3-none-any.whl
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.
- atdd/__init__.py +6 -0
- atdd/__main__.py +4 -0
- atdd/cli.py +404 -0
- atdd/coach/__init__.py +0 -0
- atdd/coach/commands/__init__.py +0 -0
- atdd/coach/commands/add_persistence_metadata.py +215 -0
- atdd/coach/commands/analyze_migrations.py +188 -0
- atdd/coach/commands/consumers.py +720 -0
- atdd/coach/commands/infer_governance_status.py +149 -0
- atdd/coach/commands/initializer.py +177 -0
- atdd/coach/commands/interface.py +1078 -0
- atdd/coach/commands/inventory.py +565 -0
- atdd/coach/commands/migration.py +240 -0
- atdd/coach/commands/registry.py +1560 -0
- atdd/coach/commands/session.py +430 -0
- atdd/coach/commands/sync.py +405 -0
- atdd/coach/commands/test_interface.py +399 -0
- atdd/coach/commands/test_runner.py +141 -0
- atdd/coach/commands/tests/__init__.py +1 -0
- atdd/coach/commands/tests/test_telemetry_array_validation.py +235 -0
- atdd/coach/commands/traceability.py +4264 -0
- atdd/coach/conventions/session.convention.yaml +754 -0
- atdd/coach/overlays/__init__.py +2 -0
- atdd/coach/overlays/claude.md +2 -0
- atdd/coach/schemas/config.schema.json +34 -0
- atdd/coach/schemas/manifest.schema.json +101 -0
- atdd/coach/templates/ATDD.md +282 -0
- atdd/coach/templates/SESSION-TEMPLATE.md +327 -0
- atdd/coach/utils/__init__.py +0 -0
- atdd/coach/utils/graph/__init__.py +0 -0
- atdd/coach/utils/graph/urn.py +875 -0
- atdd/coach/validators/__init__.py +0 -0
- atdd/coach/validators/shared_fixtures.py +365 -0
- atdd/coach/validators/test_enrich_wagon_registry.py +167 -0
- atdd/coach/validators/test_registry.py +575 -0
- atdd/coach/validators/test_session_validation.py +1183 -0
- atdd/coach/validators/test_traceability.py +448 -0
- atdd/coach/validators/test_update_feature_paths.py +108 -0
- atdd/coach/validators/test_validate_contract_consumers.py +297 -0
- atdd/coder/__init__.py +1 -0
- atdd/coder/conventions/adapter.recipe.yaml +88 -0
- atdd/coder/conventions/backend.convention.yaml +460 -0
- atdd/coder/conventions/boundaries.convention.yaml +666 -0
- atdd/coder/conventions/commons.convention.yaml +460 -0
- atdd/coder/conventions/complexity.recipe.yaml +109 -0
- atdd/coder/conventions/component-naming.convention.yaml +178 -0
- atdd/coder/conventions/design.convention.yaml +327 -0
- atdd/coder/conventions/design.recipe.yaml +273 -0
- atdd/coder/conventions/dto.convention.yaml +660 -0
- atdd/coder/conventions/frontend.convention.yaml +542 -0
- atdd/coder/conventions/green.convention.yaml +1012 -0
- atdd/coder/conventions/presentation.convention.yaml +587 -0
- atdd/coder/conventions/refactor.convention.yaml +535 -0
- atdd/coder/conventions/technology.convention.yaml +206 -0
- atdd/coder/conventions/tests/__init__.py +0 -0
- atdd/coder/conventions/tests/test_adapter_recipe.py +302 -0
- atdd/coder/conventions/tests/test_complexity_recipe.py +289 -0
- atdd/coder/conventions/tests/test_component_taxonomy.py +278 -0
- atdd/coder/conventions/tests/test_component_urn_naming.py +165 -0
- atdd/coder/conventions/tests/test_thinness_recipe.py +286 -0
- atdd/coder/conventions/thinness.recipe.yaml +82 -0
- atdd/coder/conventions/train.convention.yaml +325 -0
- atdd/coder/conventions/verification.protocol.yaml +53 -0
- atdd/coder/schemas/design_system.schema.json +361 -0
- atdd/coder/validators/__init__.py +0 -0
- atdd/coder/validators/test_commons_structure.py +485 -0
- atdd/coder/validators/test_complexity.py +416 -0
- atdd/coder/validators/test_cross_language_consistency.py +431 -0
- atdd/coder/validators/test_design_system_compliance.py +413 -0
- atdd/coder/validators/test_dto_testing_patterns.py +268 -0
- atdd/coder/validators/test_green_cross_stack_layers.py +168 -0
- atdd/coder/validators/test_green_layer_dependencies.py +148 -0
- atdd/coder/validators/test_green_python_layer_structure.py +103 -0
- atdd/coder/validators/test_green_supabase_layer_structure.py +103 -0
- atdd/coder/validators/test_import_boundaries.py +396 -0
- atdd/coder/validators/test_init_file_urns.py +593 -0
- atdd/coder/validators/test_preact_layer_boundaries.py +221 -0
- atdd/coder/validators/test_presentation_convention.py +260 -0
- atdd/coder/validators/test_python_architecture.py +674 -0
- atdd/coder/validators/test_quality_metrics.py +420 -0
- atdd/coder/validators/test_station_master_pattern.py +244 -0
- atdd/coder/validators/test_train_infrastructure.py +454 -0
- atdd/coder/validators/test_train_urns.py +293 -0
- atdd/coder/validators/test_typescript_architecture.py +616 -0
- atdd/coder/validators/test_usecase_structure.py +421 -0
- atdd/coder/validators/test_wagon_boundaries.py +586 -0
- atdd/conftest.py +126 -0
- atdd/planner/__init__.py +1 -0
- atdd/planner/conventions/acceptance.convention.yaml +538 -0
- atdd/planner/conventions/appendix.convention.yaml +187 -0
- atdd/planner/conventions/artifact-naming.convention.yaml +852 -0
- atdd/planner/conventions/component.convention.yaml +670 -0
- atdd/planner/conventions/criteria.convention.yaml +141 -0
- atdd/planner/conventions/feature.convention.yaml +371 -0
- atdd/planner/conventions/interface.convention.yaml +382 -0
- atdd/planner/conventions/steps.convention.yaml +141 -0
- atdd/planner/conventions/train.convention.yaml +552 -0
- atdd/planner/conventions/wagon.convention.yaml +275 -0
- atdd/planner/conventions/wmbt.convention.yaml +258 -0
- atdd/planner/schemas/acceptance.schema.json +336 -0
- atdd/planner/schemas/appendix.schema.json +78 -0
- atdd/planner/schemas/component.schema.json +114 -0
- atdd/planner/schemas/feature.schema.json +197 -0
- atdd/planner/schemas/train.schema.json +192 -0
- atdd/planner/schemas/wagon.schema.json +281 -0
- atdd/planner/schemas/wmbt.schema.json +59 -0
- atdd/planner/validators/__init__.py +0 -0
- atdd/planner/validators/conftest.py +5 -0
- atdd/planner/validators/test_draft_wagon_registry.py +374 -0
- atdd/planner/validators/test_plan_cross_refs.py +240 -0
- atdd/planner/validators/test_plan_uniqueness.py +224 -0
- atdd/planner/validators/test_plan_urn_resolution.py +268 -0
- atdd/planner/validators/test_plan_wagons.py +174 -0
- atdd/planner/validators/test_train_validation.py +514 -0
- atdd/planner/validators/test_wagon_urn_chain.py +648 -0
- atdd/planner/validators/test_wmbt_consistency.py +327 -0
- atdd/planner/validators/test_wmbt_vocabulary.py +632 -0
- atdd/tester/__init__.py +1 -0
- atdd/tester/conventions/artifact.convention.yaml +257 -0
- atdd/tester/conventions/contract.convention.yaml +1009 -0
- atdd/tester/conventions/filename.convention.yaml +555 -0
- atdd/tester/conventions/migration.convention.yaml +509 -0
- atdd/tester/conventions/red.convention.yaml +797 -0
- atdd/tester/conventions/routing.convention.yaml +51 -0
- atdd/tester/conventions/telemetry.convention.yaml +458 -0
- atdd/tester/schemas/a11y.tmpl.json +17 -0
- atdd/tester/schemas/artifact.schema.json +189 -0
- atdd/tester/schemas/contract.schema.json +591 -0
- atdd/tester/schemas/contract.tmpl.json +95 -0
- atdd/tester/schemas/db.tmpl.json +20 -0
- atdd/tester/schemas/e2e.tmpl.json +17 -0
- atdd/tester/schemas/edge_function.tmpl.json +17 -0
- atdd/tester/schemas/event.tmpl.json +17 -0
- atdd/tester/schemas/http.tmpl.json +19 -0
- atdd/tester/schemas/job.tmpl.json +18 -0
- atdd/tester/schemas/load.tmpl.json +21 -0
- atdd/tester/schemas/metric.tmpl.json +19 -0
- atdd/tester/schemas/pack.schema.json +139 -0
- atdd/tester/schemas/realtime.tmpl.json +20 -0
- atdd/tester/schemas/rls.tmpl.json +18 -0
- atdd/tester/schemas/script.tmpl.json +16 -0
- atdd/tester/schemas/sec.tmpl.json +18 -0
- atdd/tester/schemas/storage.tmpl.json +18 -0
- atdd/tester/schemas/telemetry.schema.json +128 -0
- atdd/tester/schemas/telemetry_tracking_manifest.schema.json +143 -0
- atdd/tester/schemas/test_filename.schema.json +194 -0
- atdd/tester/schemas/test_intent.schema.json +179 -0
- atdd/tester/schemas/unit.tmpl.json +18 -0
- atdd/tester/schemas/visual.tmpl.json +18 -0
- atdd/tester/schemas/ws.tmpl.json +17 -0
- atdd/tester/utils/__init__.py +0 -0
- atdd/tester/utils/filename.py +300 -0
- atdd/tester/validators/__init__.py +0 -0
- atdd/tester/validators/cleanup_duplicate_headers.py +116 -0
- atdd/tester/validators/cleanup_duplicate_headers_v2.py +135 -0
- atdd/tester/validators/conftest.py +5 -0
- atdd/tester/validators/coverage_gap_report.py +321 -0
- atdd/tester/validators/fix_dual_ac_references.py +179 -0
- atdd/tester/validators/remove_duplicate_lines.py +93 -0
- atdd/tester/validators/test_acceptance_urn_filename_mapping.py +359 -0
- atdd/tester/validators/test_acceptance_urn_separator.py +166 -0
- atdd/tester/validators/test_artifact_naming_category.py +307 -0
- atdd/tester/validators/test_contract_schema_compliance.py +706 -0
- atdd/tester/validators/test_contracts_structure.py +200 -0
- atdd/tester/validators/test_coverage_adequacy.py +797 -0
- atdd/tester/validators/test_dual_ac_reference.py +225 -0
- atdd/tester/validators/test_fixture_validity.py +372 -0
- atdd/tester/validators/test_isolation.py +487 -0
- atdd/tester/validators/test_migration_coverage.py +204 -0
- atdd/tester/validators/test_migration_criteria.py +276 -0
- atdd/tester/validators/test_migration_generation.py +116 -0
- atdd/tester/validators/test_python_test_naming.py +410 -0
- atdd/tester/validators/test_red_layer_validation.py +95 -0
- atdd/tester/validators/test_red_python_layer_structure.py +87 -0
- atdd/tester/validators/test_red_supabase_layer_structure.py +90 -0
- atdd/tester/validators/test_telemetry_structure.py +634 -0
- atdd/tester/validators/test_typescript_test_naming.py +301 -0
- atdd/tester/validators/test_typescript_test_structure.py +84 -0
- atdd-0.2.1.dist-info/METADATA +221 -0
- atdd-0.2.1.dist-info/RECORD +184 -0
- atdd-0.2.1.dist-info/WHEEL +5 -0
- atdd-0.2.1.dist-info/entry_points.txt +2 -0
- atdd-0.2.1.dist-info/licenses/LICENSE +674 -0
- atdd-0.2.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1009 @@
|
|
|
1
|
+
version: "2.0"
|
|
2
|
+
name: "Contract Convention"
|
|
3
|
+
description: "Rules for contract schema structure and metadata. For artifact naming rules, see artifact-naming.convention.yaml"
|
|
4
|
+
|
|
5
|
+
# Naming Authority
|
|
6
|
+
naming_authority:
|
|
7
|
+
convention: "atdd/planner/conventions/artifact-naming.convention.yaml"
|
|
8
|
+
note: |
|
|
9
|
+
For artifact naming rules ($id format, URN pattern, theme taxonomy, separator semantics,
|
|
10
|
+
physical file mapping), refer to artifact-naming.convention.yaml. This convention focuses
|
|
11
|
+
exclusively on contract schema structure and metadata requirements.
|
|
12
|
+
|
|
13
|
+
# Agent Responsibilities
|
|
14
|
+
agent_responsibilities:
|
|
15
|
+
planner:
|
|
16
|
+
role: "Creates wagon/feature manifests in plan/ with produce/consume interfaces"
|
|
17
|
+
output: "Wagon YAML files with artifact declarations"
|
|
18
|
+
location: "plan/{wagon}/_<wagon>.yaml"
|
|
19
|
+
|
|
20
|
+
tester:
|
|
21
|
+
role: "Generates contract schemas from planner interfaces"
|
|
22
|
+
input: "Wagon produce/consume artifacts"
|
|
23
|
+
output: "Contract JSON schema files"
|
|
24
|
+
location: "contracts/{theme}/{domain}/{aspect}.schema.json"
|
|
25
|
+
note: "Full 3-level structure: theme/domain/aspect"
|
|
26
|
+
|
|
27
|
+
principle: "Planner defines WHAT (interfaces), Tester implements HOW (contracts)"
|
|
28
|
+
|
|
29
|
+
# Schema Structure (Root Truth)
|
|
30
|
+
schema_structure:
|
|
31
|
+
description: "Contract schemas follow this structure. Schema is the source of truth."
|
|
32
|
+
|
|
33
|
+
required_fields:
|
|
34
|
+
core:
|
|
35
|
+
- "$schema" # JSON Schema draft-07
|
|
36
|
+
- "$id" # {theme}:{path}:{resource} (hierarchy with colons, NO "contract:" prefix, NO version)
|
|
37
|
+
- "title" # PascalCase name
|
|
38
|
+
- "description" # Purpose description
|
|
39
|
+
- "version" # Semantic version (1.0.0) - separate field, NOT in $id
|
|
40
|
+
- "type" # Always "object"
|
|
41
|
+
- "x-artifact-metadata"
|
|
42
|
+
|
|
43
|
+
metadata_required:
|
|
44
|
+
- "domain" # From artifact name (before colon)
|
|
45
|
+
- "resource" # From artifact name (after colon)
|
|
46
|
+
- "version" # Semantic version
|
|
47
|
+
- "hash" # SHA-256 content hash
|
|
48
|
+
- "producer" # wagon:{name} from wagon produce[]
|
|
49
|
+
- "consumers" # Array of wagon:{name} from consume[] (can be empty array)
|
|
50
|
+
- "dependencies" # Array of contract:{name} this depends on (can be empty array)
|
|
51
|
+
- "api" # HTTP method, path, auth
|
|
52
|
+
- "traceability" # Links to wagon/features (MANDATORY)
|
|
53
|
+
|
|
54
|
+
traceability_required:
|
|
55
|
+
- "wagon_ref" # Path to wagon manifest (MANDATORY)
|
|
56
|
+
- "feature_refs" # Array of feature URNs (MANDATORY, at least 1)
|
|
57
|
+
- "acceptance_refs" # Array of acceptance URNs (optional)
|
|
58
|
+
|
|
59
|
+
id_vs_urn:
|
|
60
|
+
schema_id: "{theme}(:{path})*:{resource}"
|
|
61
|
+
schema_id_examples:
|
|
62
|
+
- "commons:ux:foundations:color"
|
|
63
|
+
- "match:dilemma:current"
|
|
64
|
+
- "mechanic:timebank:exhausted"
|
|
65
|
+
urn: "contract:{theme}(:{path})*:{resource}"
|
|
66
|
+
urn_examples:
|
|
67
|
+
- "contract:commons:ux:foundations:color"
|
|
68
|
+
- "contract:match:dilemma:current"
|
|
69
|
+
note: |
|
|
70
|
+
$id in schema: NO "contract:" prefix (e.g., commons:ux:foundations:color)
|
|
71
|
+
URN in wagon manifests: WITH "contract:" prefix (e.g., contract:commons:ux:foundations:color)
|
|
72
|
+
|
|
73
|
+
Hierarchy (colons) for path depth, mirrors filesystem structure.
|
|
74
|
+
Version is separate field, NOT in $id.
|
|
75
|
+
|
|
76
|
+
Examples:
|
|
77
|
+
File: contracts/commons/ux/foundations/color.schema.json
|
|
78
|
+
$id: commons:ux:foundations:color
|
|
79
|
+
URN: contract:commons:ux:foundations:color
|
|
80
|
+
|
|
81
|
+
properties:
|
|
82
|
+
version:
|
|
83
|
+
type: "string"
|
|
84
|
+
pattern: "^\\d+\\.\\d+\\.\\d+$"
|
|
85
|
+
description: "Semantic version"
|
|
86
|
+
|
|
87
|
+
additionalProperties: false
|
|
88
|
+
|
|
89
|
+
id_format:
|
|
90
|
+
pattern: "{theme}(:{path})*:{resource}"
|
|
91
|
+
description: "Hierarchical identifier with colons for path segments, NO 'contract:' prefix, mirrors filesystem structure"
|
|
92
|
+
examples:
|
|
93
|
+
- "commons:ux:foundations:color"
|
|
94
|
+
- "mechanic:timebank:exhausted"
|
|
95
|
+
- "match:dilemma:current"
|
|
96
|
+
- "player:session:active"
|
|
97
|
+
|
|
98
|
+
template:
|
|
99
|
+
location: "atdd/tester/schemas/contract.tmpl.json"
|
|
100
|
+
purpose: "Template with placeholders for generating new contracts"
|
|
101
|
+
|
|
102
|
+
meta_schema:
|
|
103
|
+
location: "atdd/tester/schemas/contract.schema.json"
|
|
104
|
+
purpose: "Meta-schema that validates common structure of all contracts"
|
|
105
|
+
|
|
106
|
+
# Composite Contracts (Frontend Extensions)
|
|
107
|
+
composite_contracts:
|
|
108
|
+
description: |
|
|
109
|
+
Some contracts define core domain truth while frontends extend them with UI-only
|
|
110
|
+
fields. Contracts must remain UI-agnostic; extensions live in frontend code.
|
|
111
|
+
|
|
112
|
+
rules:
|
|
113
|
+
core_only:
|
|
114
|
+
requirement: "Contract schemas must contain only core domain fields (no UI props)"
|
|
115
|
+
examples_forbidden: ["icon", "color", "tokenKey", "i18nKey", "ariaKey"]
|
|
116
|
+
rationale: "Keep contracts stable and backend-compatible"
|
|
117
|
+
|
|
118
|
+
frontend_extension:
|
|
119
|
+
requirement: "Frontend may extend contract types with UI-specific fields"
|
|
120
|
+
location: "web/src/commons/domain/"
|
|
121
|
+
note: "Extensions must not backflow into contract schema"
|
|
122
|
+
|
|
123
|
+
traceability:
|
|
124
|
+
requirement: "Frontend extensions must reference the contract $id via @contract"
|
|
125
|
+
format: "@contract {schema_id}"
|
|
126
|
+
example: "@contract commons:domain:catalog"
|
|
127
|
+
applies_to: "TypeScript files in web/src/commons/domain/"
|
|
128
|
+
|
|
129
|
+
# Contract Versioning and Lifecycle
|
|
130
|
+
contract_versioning:
|
|
131
|
+
description: "Semantic versioning strategy for contract maturity and backward compatibility tracking"
|
|
132
|
+
|
|
133
|
+
semantic_versioning:
|
|
134
|
+
format: "MAJOR.MINOR.PATCH (e.g., 0.1.0, 1.2.3, 2.0.0)"
|
|
135
|
+
rules:
|
|
136
|
+
major:
|
|
137
|
+
when: "Breaking changes that require code updates"
|
|
138
|
+
examples:
|
|
139
|
+
- "Remove required field"
|
|
140
|
+
- "Change field type (string → number)"
|
|
141
|
+
- "Change field semantics/meaning"
|
|
142
|
+
- "Rename field"
|
|
143
|
+
- "Change $id structure"
|
|
144
|
+
version_bump: "v1.5.2 → v2.0.0"
|
|
145
|
+
backward_compatible: false
|
|
146
|
+
app_impact: "MUST handle both old and new formats"
|
|
147
|
+
|
|
148
|
+
minor:
|
|
149
|
+
when: "Backward-compatible additions"
|
|
150
|
+
examples:
|
|
151
|
+
- "Add optional field"
|
|
152
|
+
- "Add new enum value"
|
|
153
|
+
- "Expand validation (make less strict)"
|
|
154
|
+
- "Add new operation to API"
|
|
155
|
+
version_bump: "v1.2.3 → v1.3.0"
|
|
156
|
+
backward_compatible: true
|
|
157
|
+
app_impact: "Old apps ignore new fields, new apps get bonus data"
|
|
158
|
+
|
|
159
|
+
patch:
|
|
160
|
+
when: "Non-functional changes (no schema impact)"
|
|
161
|
+
examples:
|
|
162
|
+
- "Fix description/documentation"
|
|
163
|
+
- "Fix examples"
|
|
164
|
+
- "Update metadata (traceability, testing refs)"
|
|
165
|
+
- "Improve error messages"
|
|
166
|
+
version_bump: "v1.2.3 → v1.2.4"
|
|
167
|
+
backward_compatible: true
|
|
168
|
+
app_impact: "None - documentation only"
|
|
169
|
+
|
|
170
|
+
lifecycle_stages:
|
|
171
|
+
draft:
|
|
172
|
+
version_range: "0.x.x"
|
|
173
|
+
governance_status: "draft"
|
|
174
|
+
description: "Contract under active development, structure may change"
|
|
175
|
+
created_by: "planner (scaffolded) or tester (initial schema)"
|
|
176
|
+
iteration_rule: "Field additions/changes increment MINOR (0.1.0 → 0.2.0)"
|
|
177
|
+
stability: "Unstable - do not use in production code"
|
|
178
|
+
graduation: "Promote to v1.0.0 when schema is stable and tested"
|
|
179
|
+
|
|
180
|
+
active:
|
|
181
|
+
version_range: "1.x.x+"
|
|
182
|
+
governance_status: "active"
|
|
183
|
+
description: "Stable contract in production use"
|
|
184
|
+
created_by: "tester (promotes from draft)"
|
|
185
|
+
compatibility_guarantee: "Semantic versioning rules enforced"
|
|
186
|
+
breaking_changes: "Require major version bump (v1.x → v2.0)"
|
|
187
|
+
stability: "Stable - safe for production use"
|
|
188
|
+
|
|
189
|
+
deprecated:
|
|
190
|
+
version_range: "Any stable version"
|
|
191
|
+
governance_status: "deprecated"
|
|
192
|
+
description: "Contract sunset warning - will be retired"
|
|
193
|
+
migration_path: "Must document replacement contract"
|
|
194
|
+
timeline: "Provide deprecation timeline (e.g., 6 months)"
|
|
195
|
+
stability: "Stable but discouraged - migrate to replacement"
|
|
196
|
+
|
|
197
|
+
retired:
|
|
198
|
+
version_range: "Any stable version"
|
|
199
|
+
governance_status: "retired"
|
|
200
|
+
description: "Contract no longer supported"
|
|
201
|
+
breaking_change: "Yes - consumers must migrate"
|
|
202
|
+
stability: "Unsupported - do not use"
|
|
203
|
+
|
|
204
|
+
version_in_data:
|
|
205
|
+
description: "Runtime data should include version field for backward compatibility"
|
|
206
|
+
field_name: "_version"
|
|
207
|
+
format: "String with major version (e.g., '1', '2') or full semver (e.g., '1.0.0')"
|
|
208
|
+
purpose: "Allow application code to handle multiple contract versions in JSONB storage"
|
|
209
|
+
|
|
210
|
+
example:
|
|
211
|
+
v1_data:
|
|
212
|
+
_version: "1"
|
|
213
|
+
id: "abc123"
|
|
214
|
+
amount: "100" # string in v1
|
|
215
|
+
|
|
216
|
+
v2_data:
|
|
217
|
+
_version: "2"
|
|
218
|
+
id: "abc123"
|
|
219
|
+
amount: 100 # number in v2
|
|
220
|
+
currency: "USD" # new field in v2
|
|
221
|
+
|
|
222
|
+
application_handling:
|
|
223
|
+
description: "App reads _version field to parse data correctly"
|
|
224
|
+
code_example: |
|
|
225
|
+
def parse_transaction(data: dict) -> Transaction:
|
|
226
|
+
version = data.get("_version", "1") # default to v1 for old records
|
|
227
|
+
|
|
228
|
+
if version == "1":
|
|
229
|
+
return Transaction(
|
|
230
|
+
id=data["id"],
|
|
231
|
+
amount=int(data["amount"]) # convert string to int
|
|
232
|
+
)
|
|
233
|
+
else: # version 2+
|
|
234
|
+
return Transaction(
|
|
235
|
+
id=data["id"],
|
|
236
|
+
amount=data["amount"], # already int
|
|
237
|
+
currency=data.get("currency", "USD") # new field
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
database_migrations:
|
|
241
|
+
description: "Contract version changes vs database schema migrations"
|
|
242
|
+
principle: "JSONB storage decouples contract evolution from database migrations"
|
|
243
|
+
|
|
244
|
+
no_migration_needed:
|
|
245
|
+
- scenario: "Add optional field to contract"
|
|
246
|
+
contract_version: "v1.2.0"
|
|
247
|
+
reason: "Old records lack field, app provides default"
|
|
248
|
+
|
|
249
|
+
- scenario: "Change field type (string → int)"
|
|
250
|
+
contract_version: "v2.0.0"
|
|
251
|
+
reason: "JSONB stores both, app handles per _version"
|
|
252
|
+
|
|
253
|
+
- scenario: "Remove field"
|
|
254
|
+
contract_version: "v2.0.0"
|
|
255
|
+
reason: "Old records still have it in JSONB (ignored)"
|
|
256
|
+
|
|
257
|
+
migration_required:
|
|
258
|
+
- scenario: "New contract introduced"
|
|
259
|
+
contract_version: "v0.1.0"
|
|
260
|
+
migration: "CREATE TABLE if persistent entity"
|
|
261
|
+
|
|
262
|
+
- scenario: "Add query index on JSONB field"
|
|
263
|
+
contract_version: "Any"
|
|
264
|
+
migration: "CREATE INDEX idx_name ON table ((data->>'field'))"
|
|
265
|
+
|
|
266
|
+
- scenario: "Extract frequently-queried field to column"
|
|
267
|
+
contract_version: "Any"
|
|
268
|
+
migration: "ALTER TABLE ADD COLUMN status TEXT GENERATED ALWAYS AS (data->>'status') STORED"
|
|
269
|
+
|
|
270
|
+
version_changelog:
|
|
271
|
+
description: "Maintain CHANGELOG.md for each major contract"
|
|
272
|
+
location: "contracts/{theme}/{domain}/{aspect}/CHANGELOG.md"
|
|
273
|
+
format: "Keep-a-Changelog style"
|
|
274
|
+
example: |
|
|
275
|
+
# Changelog - match:dilemma:current
|
|
276
|
+
|
|
277
|
+
## [2.0.0] - 2025-11-15
|
|
278
|
+
### Changed
|
|
279
|
+
- BREAKING: `timeout_ms` changed from string to integer
|
|
280
|
+
|
|
281
|
+
## [1.1.0] - 2025-10-20
|
|
282
|
+
### Added
|
|
283
|
+
- Added optional `theme` field to presentation object
|
|
284
|
+
|
|
285
|
+
## [1.0.0] - 2025-10-01
|
|
286
|
+
### Changed
|
|
287
|
+
- Promoted from draft to stable
|
|
288
|
+
|
|
289
|
+
## [0.2.0] - 2025-09-15
|
|
290
|
+
### Added
|
|
291
|
+
- Added `selection_metadata` object
|
|
292
|
+
|
|
293
|
+
enforcement:
|
|
294
|
+
validation: "Platform tests enforce version format and governance.status presence"
|
|
295
|
+
test_location: "tests/platform_validation/test_contract_versioning.py"
|
|
296
|
+
rules:
|
|
297
|
+
- "version field MUST be present and match semver pattern"
|
|
298
|
+
- "governance.status MUST be present"
|
|
299
|
+
- "0.x.x contracts MUST have status='draft'"
|
|
300
|
+
- "1.x.x+ contracts MUST have status='active', 'deprecated', or 'retired'"
|
|
301
|
+
|
|
302
|
+
# Persistence Traceability
|
|
303
|
+
persistence_traceability:
|
|
304
|
+
description: "Bidirectional traceability between contracts and database tables"
|
|
305
|
+
|
|
306
|
+
purpose: |
|
|
307
|
+
Link contracts to their persistence layer for:
|
|
308
|
+
- Schema evolution tracking (contract changes vs migrations)
|
|
309
|
+
- Impact analysis (what breaks if table changes?)
|
|
310
|
+
- Documentation generation (ER diagrams from contracts)
|
|
311
|
+
- Validation (ensure table exists for persistent contracts)
|
|
312
|
+
|
|
313
|
+
metadata_structure:
|
|
314
|
+
location: "x-artifact-metadata.persistence"
|
|
315
|
+
required_for: "Contracts identified by migration criteria (has 'id' field, persistent entities)"
|
|
316
|
+
optional_for: "Events, DTOs, value objects (transient data)"
|
|
317
|
+
|
|
318
|
+
fields:
|
|
319
|
+
strategy:
|
|
320
|
+
type: "enum: none | jsonb | relational"
|
|
321
|
+
required: true
|
|
322
|
+
values:
|
|
323
|
+
none: "No persistence (events, DTOs, computed values)"
|
|
324
|
+
jsonb: "Document storage in JSONB column (recommended)"
|
|
325
|
+
relational: "Normalized tables with FK relationships (legacy/complex)"
|
|
326
|
+
|
|
327
|
+
table:
|
|
328
|
+
type: "string"
|
|
329
|
+
required: "If strategy != none"
|
|
330
|
+
pattern: "^[a-z][a-z0-9_]*$"
|
|
331
|
+
convention: "{theme}_{domain}_{aspect}"
|
|
332
|
+
examples:
|
|
333
|
+
- "commons_ux_skin"
|
|
334
|
+
- "match_dilemma_current"
|
|
335
|
+
- "match_dilemma_paired"
|
|
336
|
+
|
|
337
|
+
migration:
|
|
338
|
+
type: "string"
|
|
339
|
+
required: "If strategy != none"
|
|
340
|
+
pattern: "supabase/migrations/{timestamp}_{description}.sql"
|
|
341
|
+
purpose: "Link to migration that created this table"
|
|
342
|
+
example: "supabase/migrations/20251030151434_create_entity_tables.sql"
|
|
343
|
+
|
|
344
|
+
indexes:
|
|
345
|
+
type: "array"
|
|
346
|
+
optional: true
|
|
347
|
+
purpose: "Document existing indexes for query optimization"
|
|
348
|
+
structure:
|
|
349
|
+
name: "Index name (e.g., idx_dilemma_current_timestamp)"
|
|
350
|
+
type: "Index type: btree | gin | gist | hash"
|
|
351
|
+
fields: "Indexed fields (JSONB: use -> notation)"
|
|
352
|
+
|
|
353
|
+
examples:
|
|
354
|
+
persistent_entity:
|
|
355
|
+
contract: "match:dilemma:current"
|
|
356
|
+
persistence:
|
|
357
|
+
strategy: "jsonb"
|
|
358
|
+
table: "match_dilemma_current"
|
|
359
|
+
migration: "supabase/migrations/20251030151434_create_entity_tables.sql"
|
|
360
|
+
indexes:
|
|
361
|
+
- name: "idx_match_dilemma_current_data"
|
|
362
|
+
type: "gin"
|
|
363
|
+
fields: ["data"]
|
|
364
|
+
- name: "idx_match_dilemma_current_timestamp"
|
|
365
|
+
type: "btree"
|
|
366
|
+
fields: ["data->'selection_metadata'->>'timestamp'"]
|
|
367
|
+
|
|
368
|
+
ephemeral_event:
|
|
369
|
+
contract: "mechanic:timebank:exhausted"
|
|
370
|
+
persistence:
|
|
371
|
+
strategy: "none"
|
|
372
|
+
note: "Events don't need tables - logged to event streams"
|
|
373
|
+
|
|
374
|
+
value_object:
|
|
375
|
+
contract: "commons:identifiers:uuid"
|
|
376
|
+
persistence:
|
|
377
|
+
strategy: "none"
|
|
378
|
+
note: "Generated on-demand, not stored"
|
|
379
|
+
|
|
380
|
+
naming_convention:
|
|
381
|
+
description: "Table name derived from contract $id"
|
|
382
|
+
pattern: "{theme}_{domain}_{aspect}"
|
|
383
|
+
transformation: "Replace ':' with '_', use snake_case"
|
|
384
|
+
|
|
385
|
+
examples:
|
|
386
|
+
- contract_id: "commons:ux:themes:skin"
|
|
387
|
+
table_name: "commons_ux_skin"
|
|
388
|
+
|
|
389
|
+
- contract_id: "match:dilemma:current"
|
|
390
|
+
table_name: "match_dilemma_current"
|
|
391
|
+
|
|
392
|
+
- contract_id: "player:session:active"
|
|
393
|
+
table_name: "player_session_active"
|
|
394
|
+
|
|
395
|
+
bidirectional_lookup:
|
|
396
|
+
contract_to_table:
|
|
397
|
+
query: "Read contract x-artifact-metadata.persistence.table"
|
|
398
|
+
example: |
|
|
399
|
+
# Python
|
|
400
|
+
with open("contracts/match/dilemma/current.schema.json") as f:
|
|
401
|
+
contract = json.load(f)
|
|
402
|
+
table = contract["x-artifact-metadata"]["persistence"]["table"]
|
|
403
|
+
# → "match_dilemma_current"
|
|
404
|
+
|
|
405
|
+
table_to_contract:
|
|
406
|
+
query: "Read PostgreSQL table COMMENT"
|
|
407
|
+
example: |
|
|
408
|
+
SELECT obj_description('match_dilemma_current'::regclass, 'pg_class');
|
|
409
|
+
-- → "Selected dilemma for player presentation. Contract: match:dilemma:current"
|
|
410
|
+
|
|
411
|
+
registry_approach:
|
|
412
|
+
description: "Generate persistence registry for fast lookup"
|
|
413
|
+
location: ".claude/registry/persistence.yaml"
|
|
414
|
+
structure: |
|
|
415
|
+
tables:
|
|
416
|
+
commons_ux_skin:
|
|
417
|
+
contract: "commons:ux:themes:skin"
|
|
418
|
+
migration: "supabase/migrations/20251030151434_create_entity_tables.sql"
|
|
419
|
+
strategy: "jsonb"
|
|
420
|
+
|
|
421
|
+
match_dilemma_current:
|
|
422
|
+
contract: "match:dilemma:current"
|
|
423
|
+
migration: "supabase/migrations/20251030151434_create_entity_tables.sql"
|
|
424
|
+
strategy: "jsonb"
|
|
425
|
+
|
|
426
|
+
validation:
|
|
427
|
+
rules:
|
|
428
|
+
- rule: "Persistent entities MUST have persistence.strategy != 'none'"
|
|
429
|
+
test: "Check contracts with 'id' field have persistence metadata"
|
|
430
|
+
|
|
431
|
+
- rule: "persistence.table MUST match naming convention"
|
|
432
|
+
test: "Verify table name = contract_id.replace(':', '_')"
|
|
433
|
+
|
|
434
|
+
- rule: "persistence.migration file MUST exist"
|
|
435
|
+
test: "Check migration file exists at specified path"
|
|
436
|
+
|
|
437
|
+
- rule: "Table in database MUST match persistence.table"
|
|
438
|
+
test: "Query Supabase: SELECT table_name FROM information_schema.tables"
|
|
439
|
+
|
|
440
|
+
- rule: "Table COMMENT MUST reference contract $id"
|
|
441
|
+
test: "Query table comment, verify contains contract ID"
|
|
442
|
+
|
|
443
|
+
test_location: "tests/platform_validation/test_persistence_traceability.py"
|
|
444
|
+
|
|
445
|
+
workflow:
|
|
446
|
+
1_identify_persistent_contracts:
|
|
447
|
+
command: "python atdd/coach/commands/migration.py"
|
|
448
|
+
output: "List of contracts needing persistence"
|
|
449
|
+
|
|
450
|
+
2_create_migration:
|
|
451
|
+
command: "supabase migration new <name>"
|
|
452
|
+
action: "Write SQL with table name matching contract"
|
|
453
|
+
include: "COMMENT ON TABLE referencing contract $id"
|
|
454
|
+
|
|
455
|
+
3_update_contract:
|
|
456
|
+
action: "Add persistence metadata to contract"
|
|
457
|
+
fields:
|
|
458
|
+
- "strategy: jsonb"
|
|
459
|
+
- "table: {derived_name}"
|
|
460
|
+
- "migration: supabase/migrations/{timestamp}_{name}.sql"
|
|
461
|
+
- "indexes: [...] (document existing indexes)"
|
|
462
|
+
|
|
463
|
+
4_apply_migration:
|
|
464
|
+
command: "supabase db push"
|
|
465
|
+
verify: "Table exists in database"
|
|
466
|
+
|
|
467
|
+
5_validate_traceability:
|
|
468
|
+
command: "pytest tests/platform_validation/test_persistence_traceability.py"
|
|
469
|
+
checks:
|
|
470
|
+
- "Contract persistence.table matches database table"
|
|
471
|
+
- "Migration file exists"
|
|
472
|
+
- "Table comment references contract"
|
|
473
|
+
|
|
474
|
+
# Artifact Metadata Structure
|
|
475
|
+
artifact_metadata:
|
|
476
|
+
description: "x-artifact-metadata captures wagon linkage and governance"
|
|
477
|
+
|
|
478
|
+
required_fields:
|
|
479
|
+
identity:
|
|
480
|
+
- domain # From artifact name (before colon)
|
|
481
|
+
- resource # From artifact name (after colon)
|
|
482
|
+
- version # Semantic version
|
|
483
|
+
- hash # SHA-256 content hash
|
|
484
|
+
|
|
485
|
+
lifecycle:
|
|
486
|
+
- producer # wagon:{name} from wagon produce[]
|
|
487
|
+
- consumers # Array of wagon:{name} from consume[] references (MANDATORY)
|
|
488
|
+
- dependencies # Array of contract:{name} this depends on (MANDATORY - can be empty array)
|
|
489
|
+
|
|
490
|
+
api:
|
|
491
|
+
- method # GET|POST|PUT|DELETE (inferred)
|
|
492
|
+
- path # REST endpoint pattern
|
|
493
|
+
|
|
494
|
+
traceability:
|
|
495
|
+
- wagon_ref # Path to wagon manifest (MANDATORY)
|
|
496
|
+
- feature_refs # Array of feature URNs (MANDATORY - at least 1)
|
|
497
|
+
- acceptance_refs # Array of acceptance URNs (optional)
|
|
498
|
+
|
|
499
|
+
optional_fields:
|
|
500
|
+
- telemetry_refs # Links to telemetry signals
|
|
501
|
+
- governance # Status, stability
|
|
502
|
+
- persistence # Database storage configuration (REQUIRED for persistent entities)
|
|
503
|
+
- testing # Optional test metadata for documentation
|
|
504
|
+
- extensions # Domain-specific metadata
|
|
505
|
+
|
|
506
|
+
rationale: |
|
|
507
|
+
All contracts MUST have complete traceability to:
|
|
508
|
+
- Enable bidirectional navigation (code → plan, plan → code)
|
|
509
|
+
- Support impact analysis for changes
|
|
510
|
+
- Enforce test coverage requirements
|
|
511
|
+
- Maintain governance and audit trails
|
|
512
|
+
|
|
513
|
+
Empty arrays are acceptable for consumers/dependencies when none exist,
|
|
514
|
+
but the fields must be present to make the absence explicit.
|
|
515
|
+
|
|
516
|
+
# Directory Structure
|
|
517
|
+
directory_structure:
|
|
518
|
+
authority: "artifact-naming.convention.yaml (contract_file_mapping section)"
|
|
519
|
+
note: "For physical file path mapping rules, see artifact-naming.convention.yaml"
|
|
520
|
+
|
|
521
|
+
validation:
|
|
522
|
+
meta_validation:
|
|
523
|
+
structure_location: "atdd/tester/validators/test_contracts_structure.py"
|
|
524
|
+
content_location: "atdd/tester/validators/test_contract_schema_compliance.py"
|
|
525
|
+
scope: "Platform-level structural and content validation"
|
|
526
|
+
type: "infrastructure"
|
|
527
|
+
urn_required: false
|
|
528
|
+
checks:
|
|
529
|
+
- "Directory structure (domain/resource hierarchy)"
|
|
530
|
+
- "File naming patterns (*.schema.json)"
|
|
531
|
+
- "JSON Schema Draft-07 compliance"
|
|
532
|
+
- "Required metadata ($schema, $id, version)"
|
|
533
|
+
- "Semantic versioning (MAJOR.MINOR.PATCH)"
|
|
534
|
+
- "Reference integrity (dependencies and $ref)"
|
|
535
|
+
- "Acceptance traceability (acceptance_refs exist)"
|
|
536
|
+
- "Duplicate detection (unique $id)"
|
|
537
|
+
|
|
538
|
+
examples:
|
|
539
|
+
flat:
|
|
540
|
+
- "contracts/commons/ux/foundations/color.schema.json"
|
|
541
|
+
|
|
542
|
+
nested:
|
|
543
|
+
- "contracts/commons/ux/primitives/icons/icon.schema.json"
|
|
544
|
+
|
|
545
|
+
# Contract Generation Workflow
|
|
546
|
+
generation_workflow:
|
|
547
|
+
description: "How tester generates contracts from planner interfaces"
|
|
548
|
+
|
|
549
|
+
inputs:
|
|
550
|
+
- source: "plan/{wagon}/_<wagon>.yaml"
|
|
551
|
+
extract: "produce[] array - artifacts this wagon creates"
|
|
552
|
+
|
|
553
|
+
- source: "All wagon manifests in plan/"
|
|
554
|
+
extract: "consume[] arrays - find consumers of each artifact"
|
|
555
|
+
|
|
556
|
+
- source: "Wagon features[] array"
|
|
557
|
+
extract: "Feature references for traceability"
|
|
558
|
+
|
|
559
|
+
- source: "interface.convention.yaml"
|
|
560
|
+
extract: "Naming patterns and API mapping rules"
|
|
561
|
+
|
|
562
|
+
steps:
|
|
563
|
+
1_scan_wagons: "Scan all wagon manifests for produce[] artifacts"
|
|
564
|
+
2_classify: "Parse artifact name → domain, resource, category"
|
|
565
|
+
3_find_consumers: "Scan all consume[] for references to this artifact"
|
|
566
|
+
4_extract_deps: "Extract dependencies from producing wagon's consume[]"
|
|
567
|
+
5_infer_api: "Infer HTTP method from resource name per interface.convention"
|
|
568
|
+
6_populate_metadata: "Build x-artifact-metadata from collected data"
|
|
569
|
+
7_generate_schema: "Create contract schema from template"
|
|
570
|
+
|
|
571
|
+
metadata_population:
|
|
572
|
+
theme: "First part of artifact name (before first colon)"
|
|
573
|
+
domain: "Second part of artifact name (between colons)"
|
|
574
|
+
aspect: "Third part of artifact name (after second colon, before dot)"
|
|
575
|
+
producer: "wagon:{name} from wagon declaring in produce[]"
|
|
576
|
+
consumers: "wagon:{name} for each wagon consuming this artifact"
|
|
577
|
+
dependencies: "contract:{name} for each item in producing wagon's consume[]"
|
|
578
|
+
|
|
579
|
+
traceability:
|
|
580
|
+
wagon_ref: "plan/{wagon}/_<wagon>.yaml"
|
|
581
|
+
feature_refs: "Extracted from wagon.features[] array"
|
|
582
|
+
acceptance_refs: "Extracted from feature YAML files (optional)"
|
|
583
|
+
|
|
584
|
+
# API Structure (REST Best Practices)
|
|
585
|
+
api_structure:
|
|
586
|
+
description: "Contract API definitions follow REST best practices with flexible operations"
|
|
587
|
+
|
|
588
|
+
principle: |
|
|
589
|
+
REST APIs organize around resources with multiple operations.
|
|
590
|
+
HTTP methods describe operations (GET, POST, PUT, DELETE), not resource names.
|
|
591
|
+
Each contract can define multiple operations for the same resource.
|
|
592
|
+
|
|
593
|
+
operations_array:
|
|
594
|
+
description: "x-artifact-metadata.api.operations array contains all operations for this contract"
|
|
595
|
+
structure:
|
|
596
|
+
method: "HTTP method (GET, POST, PUT, PATCH, DELETE)"
|
|
597
|
+
path: "REST endpoint path (flexible per operation)"
|
|
598
|
+
description: "Operation description"
|
|
599
|
+
requestBody: "Request schema reference (for POST/PUT/PATCH)"
|
|
600
|
+
responses: "Response schemas mapped to HTTP status codes"
|
|
601
|
+
headers: "Required and optional headers"
|
|
602
|
+
security: "Authentication schemes (OAuth2, JWT, API key)"
|
|
603
|
+
parameters: "Query/path parameters (for collections, filtering)"
|
|
604
|
+
idempotent: "Boolean indicating retry-safety"
|
|
605
|
+
|
|
606
|
+
example:
|
|
607
|
+
operations:
|
|
608
|
+
- method: "GET"
|
|
609
|
+
path: "/players/{id}"
|
|
610
|
+
description: "Retrieve player identity"
|
|
611
|
+
responses:
|
|
612
|
+
"200":
|
|
613
|
+
description: "Player found"
|
|
614
|
+
schema: "$ref: #/definitions/PlayerIdentity"
|
|
615
|
+
"404":
|
|
616
|
+
description: "Player not found"
|
|
617
|
+
schema: "$ref: #/definitions/ErrorResponse"
|
|
618
|
+
headers:
|
|
619
|
+
- name: "Content-Type"
|
|
620
|
+
value: "application/json"
|
|
621
|
+
required: true
|
|
622
|
+
- name: "Authorization"
|
|
623
|
+
description: "Bearer token"
|
|
624
|
+
required: true
|
|
625
|
+
security:
|
|
626
|
+
- type: "jwt"
|
|
627
|
+
scheme: "bearer"
|
|
628
|
+
idempotent: true
|
|
629
|
+
|
|
630
|
+
- method: "PUT"
|
|
631
|
+
path: "/players/{id}"
|
|
632
|
+
description: "Update player identity"
|
|
633
|
+
requestBody:
|
|
634
|
+
schema: "$ref: #/definitions/PlayerIdentity"
|
|
635
|
+
required: true
|
|
636
|
+
responses:
|
|
637
|
+
"200":
|
|
638
|
+
description: "Player updated"
|
|
639
|
+
schema: "$ref: #/definitions/PlayerIdentity"
|
|
640
|
+
"400":
|
|
641
|
+
description: "Invalid request"
|
|
642
|
+
schema: "$ref: #/definitions/ErrorResponse"
|
|
643
|
+
"404":
|
|
644
|
+
description: "Player not found"
|
|
645
|
+
schema: "$ref: #/definitions/ErrorResponse"
|
|
646
|
+
idempotent: true
|
|
647
|
+
|
|
648
|
+
response_schemas:
|
|
649
|
+
description: "Each operation defines response schemas per HTTP status code"
|
|
650
|
+
|
|
651
|
+
status_codes:
|
|
652
|
+
success:
|
|
653
|
+
"200": "OK - Resource retrieved/updated"
|
|
654
|
+
"201": "Created - Resource created"
|
|
655
|
+
"204": "No Content - Resource deleted"
|
|
656
|
+
|
|
657
|
+
client_error:
|
|
658
|
+
"400": "Bad Request - Invalid input"
|
|
659
|
+
"401": "Unauthorized - Authentication required"
|
|
660
|
+
"403": "Forbidden - Insufficient permissions"
|
|
661
|
+
"404": "Not Found - Resource doesn't exist"
|
|
662
|
+
"409": "Conflict - Resource state conflict"
|
|
663
|
+
|
|
664
|
+
server_error:
|
|
665
|
+
"500": "Internal Server Error"
|
|
666
|
+
"503": "Service Unavailable"
|
|
667
|
+
|
|
668
|
+
structure:
|
|
669
|
+
description: "Human-readable description"
|
|
670
|
+
schema: "JSON Schema reference or inline schema"
|
|
671
|
+
headers: "Response headers"
|
|
672
|
+
|
|
673
|
+
error_response_pattern:
|
|
674
|
+
type: "object"
|
|
675
|
+
required: ["error", "message", "timestamp"]
|
|
676
|
+
properties:
|
|
677
|
+
error:
|
|
678
|
+
type: "string"
|
|
679
|
+
description: "Error code"
|
|
680
|
+
message:
|
|
681
|
+
type: "string"
|
|
682
|
+
description: "Human-readable error message"
|
|
683
|
+
details:
|
|
684
|
+
type: "object"
|
|
685
|
+
description: "Additional error context"
|
|
686
|
+
timestamp:
|
|
687
|
+
type: "string"
|
|
688
|
+
format: "date-time"
|
|
689
|
+
|
|
690
|
+
request_schemas:
|
|
691
|
+
description: "POST/PUT/PATCH operations include requestBody schema"
|
|
692
|
+
structure:
|
|
693
|
+
schema: "JSON Schema reference"
|
|
694
|
+
required: "Boolean indicating if body is required"
|
|
695
|
+
contentType: "application/json (default)"
|
|
696
|
+
|
|
697
|
+
example:
|
|
698
|
+
requestBody:
|
|
699
|
+
schema: "$ref: #/definitions/PlayerIdentity"
|
|
700
|
+
required: true
|
|
701
|
+
contentType: "application/json"
|
|
702
|
+
|
|
703
|
+
headers:
|
|
704
|
+
description: "Operations document required and optional headers"
|
|
705
|
+
|
|
706
|
+
standard_headers:
|
|
707
|
+
- name: "Content-Type"
|
|
708
|
+
values: ["application/json", "application/xml"]
|
|
709
|
+
description: "Request/response format"
|
|
710
|
+
|
|
711
|
+
- name: "Accept"
|
|
712
|
+
values: ["application/json", "application/xml"]
|
|
713
|
+
description: "Acceptable response formats"
|
|
714
|
+
|
|
715
|
+
- name: "Authorization"
|
|
716
|
+
format: "Bearer {token}"
|
|
717
|
+
description: "Authentication token"
|
|
718
|
+
required_for: "Protected operations"
|
|
719
|
+
|
|
720
|
+
custom_headers:
|
|
721
|
+
example:
|
|
722
|
+
- name: "X-Request-ID"
|
|
723
|
+
type: "string"
|
|
724
|
+
description: "Unique request identifier for tracing"
|
|
725
|
+
|
|
726
|
+
- name: "X-API-Version"
|
|
727
|
+
type: "string"
|
|
728
|
+
description: "API version override"
|
|
729
|
+
|
|
730
|
+
versioning:
|
|
731
|
+
description: "API versioning enables evolution without breaking changes"
|
|
732
|
+
|
|
733
|
+
strategy: "path-based"
|
|
734
|
+
pattern: "/v{version}/{resource}"
|
|
735
|
+
|
|
736
|
+
examples:
|
|
737
|
+
- version: "v1"
|
|
738
|
+
path: "/v1/players/{id}"
|
|
739
|
+
contract_version: "1.0.0"
|
|
740
|
+
|
|
741
|
+
- version: "v2"
|
|
742
|
+
path: "/v2/players/{id}"
|
|
743
|
+
contract_version: "2.0.0"
|
|
744
|
+
breaking_changes: ["Renamed field 'username' to 'handle'"]
|
|
745
|
+
|
|
746
|
+
version_mapping:
|
|
747
|
+
description: "API version maps to contract schema version"
|
|
748
|
+
rule: "Major API version increment for breaking schema changes"
|
|
749
|
+
|
|
750
|
+
authentication:
|
|
751
|
+
description: "Enhanced auth model supporting OAuth2, JWT, API keys"
|
|
752
|
+
|
|
753
|
+
schemes:
|
|
754
|
+
oauth2:
|
|
755
|
+
type: "oauth2"
|
|
756
|
+
flows:
|
|
757
|
+
authorizationCode:
|
|
758
|
+
authorizationUrl: "/oauth/authorize"
|
|
759
|
+
tokenUrl: "/oauth/token"
|
|
760
|
+
scopes:
|
|
761
|
+
"read:player": "Read player data"
|
|
762
|
+
"write:player": "Modify player data"
|
|
763
|
+
|
|
764
|
+
example:
|
|
765
|
+
security:
|
|
766
|
+
- type: "oauth2"
|
|
767
|
+
scopes: ["read:player", "write:player"]
|
|
768
|
+
|
|
769
|
+
jwt:
|
|
770
|
+
type: "http"
|
|
771
|
+
scheme: "bearer"
|
|
772
|
+
bearerFormat: "JWT"
|
|
773
|
+
|
|
774
|
+
example:
|
|
775
|
+
security:
|
|
776
|
+
- type: "jwt"
|
|
777
|
+
scheme: "bearer"
|
|
778
|
+
|
|
779
|
+
apiKey:
|
|
780
|
+
type: "apiKey"
|
|
781
|
+
in: ["header", "query", "cookie"]
|
|
782
|
+
name: "X-API-Key"
|
|
783
|
+
|
|
784
|
+
example:
|
|
785
|
+
security:
|
|
786
|
+
- type: "apiKey"
|
|
787
|
+
name: "X-API-Key"
|
|
788
|
+
in: "header"
|
|
789
|
+
|
|
790
|
+
none:
|
|
791
|
+
description: "Explicitly mark public endpoints"
|
|
792
|
+
example:
|
|
793
|
+
security: []
|
|
794
|
+
|
|
795
|
+
collection_operations:
|
|
796
|
+
description: "Collections support pagination, filtering, sorting"
|
|
797
|
+
|
|
798
|
+
pagination:
|
|
799
|
+
parameters:
|
|
800
|
+
- name: "page"
|
|
801
|
+
type: "integer"
|
|
802
|
+
description: "Page number (1-indexed)"
|
|
803
|
+
default: 1
|
|
804
|
+
|
|
805
|
+
- name: "limit"
|
|
806
|
+
type: "integer"
|
|
807
|
+
description: "Items per page"
|
|
808
|
+
default: 20
|
|
809
|
+
maximum: 100
|
|
810
|
+
|
|
811
|
+
- name: "offset"
|
|
812
|
+
type: "integer"
|
|
813
|
+
description: "Items to skip (alternative to page)"
|
|
814
|
+
|
|
815
|
+
response_metadata:
|
|
816
|
+
total:
|
|
817
|
+
type: "integer"
|
|
818
|
+
description: "Total items across all pages"
|
|
819
|
+
|
|
820
|
+
page:
|
|
821
|
+
type: "integer"
|
|
822
|
+
description: "Current page number"
|
|
823
|
+
|
|
824
|
+
hasNext:
|
|
825
|
+
type: "boolean"
|
|
826
|
+
description: "More pages available"
|
|
827
|
+
|
|
828
|
+
links:
|
|
829
|
+
type: "object"
|
|
830
|
+
properties:
|
|
831
|
+
next: "URL to next page"
|
|
832
|
+
prev: "URL to previous page"
|
|
833
|
+
first: "URL to first page"
|
|
834
|
+
last: "URL to last page"
|
|
835
|
+
|
|
836
|
+
sorting:
|
|
837
|
+
parameters:
|
|
838
|
+
- name: "sort"
|
|
839
|
+
type: "string"
|
|
840
|
+
description: "Field to sort by"
|
|
841
|
+
example: "createdAt"
|
|
842
|
+
|
|
843
|
+
- name: "order"
|
|
844
|
+
type: "string"
|
|
845
|
+
enum: ["asc", "desc"]
|
|
846
|
+
default: "asc"
|
|
847
|
+
|
|
848
|
+
filtering:
|
|
849
|
+
description: "Field-specific filters via query parameters"
|
|
850
|
+
examples:
|
|
851
|
+
- parameter: "status"
|
|
852
|
+
type: "string"
|
|
853
|
+
description: "Filter by status"
|
|
854
|
+
example: "/players?status=active"
|
|
855
|
+
|
|
856
|
+
- parameter: "createdAfter"
|
|
857
|
+
type: "string"
|
|
858
|
+
format: "date-time"
|
|
859
|
+
description: "Filter by creation date"
|
|
860
|
+
example: "/players?createdAfter=2024-01-01T00:00:00Z"
|
|
861
|
+
|
|
862
|
+
flexible_paths:
|
|
863
|
+
description: "Path patterns are flexible per operation, not rigidly inferred"
|
|
864
|
+
|
|
865
|
+
patterns:
|
|
866
|
+
singular:
|
|
867
|
+
description: "Single resource access"
|
|
868
|
+
examples:
|
|
869
|
+
- "/players/{id}"
|
|
870
|
+
- "/profile"
|
|
871
|
+
- "/session"
|
|
872
|
+
|
|
873
|
+
collection:
|
|
874
|
+
description: "Multiple resource access"
|
|
875
|
+
examples:
|
|
876
|
+
- "/players"
|
|
877
|
+
- "/matches"
|
|
878
|
+
- "/dilemmas"
|
|
879
|
+
|
|
880
|
+
nested:
|
|
881
|
+
description: "Sub-resource access"
|
|
882
|
+
examples:
|
|
883
|
+
- "/matches/{matchId}/dilemmas"
|
|
884
|
+
- "/players/{playerId}/sessions"
|
|
885
|
+
- "/sponsors/{sponsorId}/partnerships"
|
|
886
|
+
|
|
887
|
+
action:
|
|
888
|
+
description: "Action endpoints (not pure CRUD)"
|
|
889
|
+
examples:
|
|
890
|
+
- "/auth/login"
|
|
891
|
+
- "/auth/logout"
|
|
892
|
+
- "/matches/{id}/start"
|
|
893
|
+
- "/matches/{id}/pause"
|
|
894
|
+
|
|
895
|
+
no_rigid_pattern: "Paths defined per operation, NOT inferred from domain name"
|
|
896
|
+
|
|
897
|
+
idempotency:
|
|
898
|
+
description: "Operations document retry-safety behavior"
|
|
899
|
+
|
|
900
|
+
idempotent_methods:
|
|
901
|
+
GET:
|
|
902
|
+
idempotent: true
|
|
903
|
+
description: "Safe to retry, no side effects"
|
|
904
|
+
|
|
905
|
+
PUT:
|
|
906
|
+
idempotent: true
|
|
907
|
+
description: "Multiple identical requests have same effect as single request"
|
|
908
|
+
|
|
909
|
+
DELETE:
|
|
910
|
+
idempotent: true
|
|
911
|
+
description: "Deleting non-existent resource returns same result"
|
|
912
|
+
|
|
913
|
+
non_idempotent_methods:
|
|
914
|
+
POST:
|
|
915
|
+
idempotent: false
|
|
916
|
+
description: "Each request may create new resource"
|
|
917
|
+
|
|
918
|
+
PATCH:
|
|
919
|
+
idempotent: false
|
|
920
|
+
description: "Partial updates may not be idempotent"
|
|
921
|
+
|
|
922
|
+
rest_examples:
|
|
923
|
+
player_identity:
|
|
924
|
+
artifact: "player:identity"
|
|
925
|
+
operations:
|
|
926
|
+
- method: "GET"
|
|
927
|
+
path: "/players/{id}"
|
|
928
|
+
description: "Retrieve player identity"
|
|
929
|
+
|
|
930
|
+
- method: "PUT"
|
|
931
|
+
path: "/players/{id}"
|
|
932
|
+
description: "Update player identity"
|
|
933
|
+
|
|
934
|
+
- method: "DELETE"
|
|
935
|
+
path: "/players/{id}"
|
|
936
|
+
description: "Delete player account"
|
|
937
|
+
|
|
938
|
+
match_dilemma:
|
|
939
|
+
artifact: "match:dilemma"
|
|
940
|
+
operations:
|
|
941
|
+
- method: "GET"
|
|
942
|
+
path: "/matches/{matchId}/dilemmas/{dilemmaId}"
|
|
943
|
+
description: "Get current dilemma in match"
|
|
944
|
+
|
|
945
|
+
- method: "POST"
|
|
946
|
+
path: "/matches/{matchId}/dilemmas"
|
|
947
|
+
description: "Create new dilemma for match"
|
|
948
|
+
|
|
949
|
+
auth_login:
|
|
950
|
+
artifact: "auth:session"
|
|
951
|
+
operations:
|
|
952
|
+
- method: "POST"
|
|
953
|
+
path: "/auth/login"
|
|
954
|
+
description: "Authenticate and create session"
|
|
955
|
+
|
|
956
|
+
- method: "POST"
|
|
957
|
+
path: "/auth/logout"
|
|
958
|
+
description: "End current session"
|
|
959
|
+
|
|
960
|
+
# Validation Rules
|
|
961
|
+
validation:
|
|
962
|
+
schema_requirements:
|
|
963
|
+
id_pattern: "^contract:[a-z_]+:[a-z_]+(\\.[a-z_]+)?$"
|
|
964
|
+
version_pattern: "^\\d+\\.\\d+\\.\\d+$"
|
|
965
|
+
additional_properties: false
|
|
966
|
+
required_fields_enforced: true
|
|
967
|
+
|
|
968
|
+
metadata_requirements:
|
|
969
|
+
producer_format: "^wagon:[a-z\\-]+$"
|
|
970
|
+
consumer_format: "^(wagon|external):[a-z\\-]+$"
|
|
971
|
+
dependency_format: "^contract:[a-z_]+:[a-z_]+(\\.[a-z_]+)?$"
|
|
972
|
+
|
|
973
|
+
bidirectional_linkage:
|
|
974
|
+
wagon_to_contract: "Wagon produce[].urn resolves to contract $id"
|
|
975
|
+
contract_to_wagon: "Contract producer references existing wagon"
|
|
976
|
+
|
|
977
|
+
meta_validation:
|
|
978
|
+
rule: "Contract schema validation is centralized in atdd/tester/audits"
|
|
979
|
+
note: "No co-located tests/ directories are required"
|
|
980
|
+
|
|
981
|
+
# Removed Concepts (Version 2.0)
|
|
982
|
+
removed_concepts:
|
|
983
|
+
- concept: "wagon_contracts vs design_contracts distinction"
|
|
984
|
+
reason: "All contracts come from wagons. UX wagon produces contracts just like any other wagon."
|
|
985
|
+
removed_in: "v2.0"
|
|
986
|
+
|
|
987
|
+
- concept: "I/O type classification (dto/command/event/query)"
|
|
988
|
+
reason: "Implementation detail, not contract interface concern. Contracts are agnostic."
|
|
989
|
+
removed_in: "v2.0"
|
|
990
|
+
|
|
991
|
+
- concept: "Port mapping (Http/Repo/Bus/Knowledge)"
|
|
992
|
+
reason: "Internal routing concern, not exposed in contract schema."
|
|
993
|
+
removed_in: "v2.0"
|
|
994
|
+
|
|
995
|
+
- concept: "SDK generation metadata in contracts"
|
|
996
|
+
reason: "Tooling concern. SDK generation derives from contracts independently."
|
|
997
|
+
removed_in: "v2.0"
|
|
998
|
+
|
|
999
|
+
- concept: "Pack manifest integration"
|
|
1000
|
+
reason: "Packaging concern, separate from contract definition."
|
|
1001
|
+
removed_in: "v2.0"
|
|
1002
|
+
|
|
1003
|
+
# Cross-References
|
|
1004
|
+
references:
|
|
1005
|
+
naming_authority: "atdd/planner/conventions/interface.convention.yaml"
|
|
1006
|
+
schema_template: "atdd/tester/schemas/contract.tmpl.json"
|
|
1007
|
+
schema_validator: "atdd/tester/schemas/contract.schema.json"
|
|
1008
|
+
telemetry_example: "atdd/tester/schemas/telemetry.schema.json"
|
|
1009
|
+
platform_validation: "atdd/tester/validators/test_contracts_structure.py"
|