universal-dev-standards 5.7.3 → 5.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,55 @@
1
+ # UDS Language Packs
2
+
3
+ Language packs provide migration risk labels for specific source→target language pairs.
4
+ They extend the core `feature-manifest-standard` without polluting the core standard.
5
+
6
+ ## Naming Convention
7
+
8
+ ```
9
+ language-pack-<source>-to-<target>.ai.yaml
10
+ ```
11
+
12
+ Examples:
13
+ - `language-pack-php-to-csharp.ai.yaml`
14
+ - `language-pack-java-to-go.ai.yaml`
15
+ - `language-pack-python-to-rust.ai.yaml`
16
+
17
+ ## Available Language Packs
18
+
19
+ | Pack | Source | Target | Labels |
20
+ |------|--------|--------|--------|
21
+ | [language-pack-php-to-csharp.ai.yaml](language-pack-php-to-csharp.ai.yaml) | PHP | C# (ASP.NET Core) | SESSION_HANDLING, ORM_DIFFERENCES, TIMEZONE_HANDLING, FILE_UPLOAD_PATH, REGEX_DIFFERENCES, ARRAY_FUNCTIONS, EXCEPTION_HIERARCHY |
22
+
23
+ ## Usage
24
+
25
+ Reference language pack risk labels in `feature-manifest.yaml` alongside generic labels:
26
+
27
+ ```yaml
28
+ features:
29
+ - id: FM-001
30
+ name: UserLogin
31
+ migration_risks:
32
+ - SESSION_HANDLING # from language-pack-php-to-csharp
33
+ - NULL_SEMANTICS # from generic risks (feature-manifest-standard)
34
+ - ASYNC_MODEL # from generic risks
35
+ ```
36
+
37
+ ## Generic vs Language Pack Labels
38
+
39
+ | Type | Source | When to use |
40
+ |------|--------|-------------|
41
+ | Generic | `feature-manifest-standard.migration_risks.generic` | All migration projects |
42
+ | Language Pack | `ai/language-packs/<pack>.ai.yaml` | Specific source→target pairs |
43
+
44
+ ## Creating a New Language Pack
45
+
46
+ 1. Copy the structure from an existing pack
47
+ 2. Replace `source`, `target`, and `migration_risks` with the new pair's specifics
48
+ 3. Each risk label should have: `label`, `description`, `details` (optional but recommended)
49
+ 4. Submit via pull request to UDS with evidence from real migration projects
50
+
51
+ ## Architecture Decision
52
+
53
+ Language packs were separated from the core `feature-manifest-standard` in UDS v1.1.0
54
+ (XSPEC-203) to keep the core standard language-agnostic. See `ai/standards/feature-manifest-standard.ai.yaml`
55
+ `migration_risks.language_packs` for the extension point declaration.
@@ -0,0 +1,83 @@
1
+ # Language Pack: PHP → C# (ASP.NET Core) Migration Risks
2
+ # Migrated from: feature-manifest-standard v1.0.0 core standard
3
+ # Use with: feature-manifest.yaml features[].migration_risks
4
+
5
+ language_pack:
6
+ id: php-to-csharp
7
+ source: php
8
+ target: csharp
9
+ meta:
10
+ version: "1.0.0"
11
+ updated: "2026-05-13"
12
+ description: PHP to C# (ASP.NET Core) migration risk labels
13
+ migrated_from: "feature-manifest-standard v1.0.0 migration_risks.php_to_csharp"
14
+ references:
15
+ - "XSPEC-203: Language Pack Architecture"
16
+
17
+ migration_risks:
18
+ - label: SESSION_HANDLING
19
+ description: PHP session → ASP.NET Core Session/Cookie middleware differences
20
+ details: |
21
+ PHP sessions are file-backed by default and rely on PHPSESSID cookie.
22
+ ASP.NET Core uses IDistributedCache (Redis/SQL) backed sessions.
23
+ Check: session_start(), $_SESSION usage → ISession / IDistributedCache
24
+
25
+ - label: ORM_DIFFERENCES
26
+ description: Eloquent ORM → Entity Framework behavioral differences (lazy loading, conventions)
27
+ details: |
28
+ Eloquent uses ActiveRecord pattern; EF Core uses DataMapper/Unit of Work.
29
+ Lazy loading is opt-in in EF Core (vs default in Eloquent).
30
+ Check: Model relationships, ->with() eager loading, timestamps conventions
31
+
32
+ - label: TIMEZONE_HANDLING
33
+ description: PHP timezone functions → .NET DateTimeOffset handling
34
+ details: |
35
+ PHP date() and strtotime() are timezone-sensitive via date_default_timezone_set().
36
+ .NET uses DateTimeOffset for timezone-aware datetimes; DateTime is ambiguous.
37
+ Check: date(), strtotime(), Carbon usage → DateTimeOffset, TimeZoneInfo
38
+
39
+ - label: FILE_UPLOAD_PATH
40
+ description: PHP $_FILES superglobal → ASP.NET Core IFormFile interface
41
+ details: |
42
+ PHP uses $_FILES superglobal with move_uploaded_file().
43
+ ASP.NET Core uses IFormFile with CopyToAsync() and streaming.
44
+ Check: $_FILES, move_uploaded_file(), file validation logic
45
+
46
+ - label: REGEX_DIFFERENCES
47
+ description: PHP PCRE syntax vs .NET Regex syntax differences
48
+ details: |
49
+ PHP uses PCRE with delimiters (e.g., '/pattern/i'); .NET has no delimiters.
50
+ Named groups: PHP (?P<name>) vs .NET (?<name>).
51
+ Unicode properties and some PCRE extensions differ.
52
+ Check: preg_match(), preg_replace(), preg_split() usage
53
+
54
+ - label: ARRAY_FUNCTIONS
55
+ description: PHP array_* functions → LINQ equivalents
56
+ details: |
57
+ PHP array functions are procedural; .NET uses LINQ extension methods.
58
+ array_map() → .Select(); array_filter() → .Where(); array_reduce() → .Aggregate()
59
+ array_unique() → .Distinct(); usort() → .OrderBy()
60
+ Check: array_map, array_filter, array_reduce, usort, array_column usage
61
+
62
+ - label: EXCEPTION_HIERARCHY
63
+ description: PHP exception hierarchy vs .NET exception hierarchy differences
64
+ details: |
65
+ PHP: Exception → RuntimeException → InvalidArgumentException etc.
66
+ .NET: Exception → SystemException / ApplicationException → specific types
67
+ PHP allows catching multiple types with | (PHP 8+); .NET uses catch blocks.
68
+ Check: try/catch blocks, custom exception classes, exception type mapping
69
+
70
+ usage_example: |
71
+ # In feature-manifest.yaml:
72
+ features:
73
+ - id: FM-001
74
+ name: UserLogin
75
+ migration_risks:
76
+ - SESSION_HANDLING # from this language pack
77
+ - NULL_SEMANTICS # from generic risks (feature-manifest-standard)
78
+
79
+ - id: FM-007
80
+ name: OrderCancellation
81
+ migration_risks:
82
+ - ORM_DIFFERENCES # from this language pack
83
+ - ASYNC_MODEL # from generic risks
@@ -59,9 +59,48 @@ standard:
59
59
  definition: AC has no tests
60
60
  criteria: No test case references this AC
61
61
 
62
+ - status: not_implemented
63
+ symbol: "🚫"
64
+ definition: AC has no corresponding implementation (feature code does not exist)
65
+ criteria: |
66
+ No business logic in src/ corresponds to this AC.
67
+ Distinct from uncovered: uncovered = code exists but no test; not_implemented = code does not exist.
68
+ Typical signals: throw NotImplementedException(), empty stub body, FEATURE_STUB: marker.
69
+ decision_tree: |
70
+ Q1: Does the corresponding code exist in src/?
71
+ No → not_implemented
72
+ Yes → Q2: Does any test reference this AC?
73
+ No → uncovered
74
+ Yes → Q3: Are all conditions in the AC tested?
75
+ Yes → covered
76
+ No → partial
77
+
62
78
  calculation:
63
- formula: "AC Coverage % = (covered_count + partial_count × 0.5) / total_ac_count × 100"
64
- note: Partial counts as 0.5 for coverage calculation
79
+ formula: "AC Coverage % = (covered_count + partial_count × 0.5) / (total_ac_count - not_implemented_count) × 100"
80
+ note: |
81
+ not_implemented ACs are excluded from the coverage denominator (separate dimension).
82
+ Partial counts as 0.5 for coverage calculation.
83
+ Example: 20 ACs total, 3 not_implemented, 15 covered, 2 uncovered →
84
+ coverage = (15 + 0) / (20 - 3) × 100 = 88.2%
85
+
86
+ ci_gate:
87
+ not_implemented_blocking: true
88
+ rule: "not_implemented_count > 0 → CI fails with [AC-NOT-IMPL] before UAT gate"
89
+ report_format: |
90
+ AC Traceability Report
91
+ ──────────────────────
92
+ 🚫 NOT IMPLEMENTED (<count>) — BLOCKING
93
+ AC-007 OrderCancellation src/orders/ 無對應實作
94
+ AC-012 RefundCalculation src/payments/ 無對應實作
95
+
96
+ ❌ UNCOVERED (<count>) — WARNING
97
+ AC-003 BulkImport test/import/ 缺測試
98
+
99
+ ✅ COVERED (<count>)
100
+ ⚠️ PARTIAL (<count>)
101
+
102
+ AC Coverage: <pct>% (<covered>/<implemented_total> implemented ACs)
103
+ Status: BLOCKED by <not_impl_count> not_implemented AC(s)
65
104
 
66
105
  quality_thresholds:
67
106
  defaults:
@@ -147,7 +186,14 @@ standard:
147
186
 
148
187
  - id: honest-status
149
188
  trigger: reporting AC coverage
150
- instruction: Use honest status classification (covered/partial/uncovered); never inflate coverage
189
+ instruction: "Use honest status classification (covered/partial/uncovered/not_implemented); never inflate coverage"
190
+ priority: required
191
+
192
+ - id: not-implemented-gate
193
+ trigger: AC traceability matrix contains not_implemented status
194
+ instruction: |
195
+ Flag CI as BLOCKED with [AC-NOT-IMPL] message. List all not_implemented ACs.
196
+ not_implemented must be resolved before UAT. Exclude not_implemented from coverage denominator.
151
197
  priority: required
152
198
 
153
199
  - id: track-all-ac
@@ -0,0 +1,272 @@
1
+ # Behavior Snapshot Standard - AI Optimized
2
+ # Source: core/behavior-snapshot.md
3
+
4
+ standard:
5
+ id: behavior-snapshot
6
+ name: Behavior Snapshot Standard
7
+ description: Multi-modal golden file format for migration parity verification and refactoring characterization; supports HTTP, CLI, File, and Event adapters; defines snapshot schema, parity gate, and Gate 0 protocol
8
+
9
+ meta:
10
+ version: "1.1.0"
11
+ updated: "2026-05-13"
12
+ source: core/behavior-snapshot.md
13
+ references:
14
+ - "XSPEC-201: Refactor/Migration Completeness Protocol"
15
+ - "XSPEC-203: Language Pack Architecture (multi-modal adapter extension)"
16
+ - "Michael Feathers: Working Effectively with Legacy Code (characterization tests)"
17
+ - "Golden Master Testing pattern"
18
+
19
+ snapshot_schema:
20
+ description: Format of a single snapshot JSON file in .snapshots/<feature-id>/<scenario>.json
21
+ required_fields:
22
+ - name: adapter
23
+ type: string
24
+ description: "Snapshot adapter type: http (default) | cli | file | event"
25
+ default: "http"
26
+ backward_compat: "Omitting adapter field is equivalent to adapter: http — existing snapshots remain valid"
27
+ - name: feature_id
28
+ type: string
29
+ description: FM-NNN from feature-manifest.yaml
30
+ - name: scenario
31
+ type: string
32
+ description: "Scenario name (snake_case): happy_path, not_found, invalid_credentials, etc."
33
+ - name: ignore_fields
34
+ type: list
35
+ description: |
36
+ Fields to skip during parity comparison.
37
+ Use for legitimately non-deterministic values: timestamps, tokens, IDs.
38
+ Do NOT use to hide business logic differences.
39
+
40
+ http_example: |
41
+ {
42
+ "adapter": "http",
43
+ "feature_id": "FM-007",
44
+ "scenario": "happy_path",
45
+ "request": {
46
+ "method": "POST",
47
+ "path": "/api/orders/123/cancel",
48
+ "headers": { "Content-Type": "application/json" },
49
+ "body": { "reason": "customer_request" }
50
+ },
51
+ "response": {
52
+ "status": 200,
53
+ "body": {
54
+ "success": true,
55
+ "order_status": "cancelled",
56
+ "refund_initiated": true
57
+ }
58
+ },
59
+ "ignore_fields": ["refund_id", "cancelled_at"]
60
+ }
61
+
62
+ adapters:
63
+ description: Adapter schemas for different software interaction types. Choose based on how the feature interacts with the outside world.
64
+
65
+ http:
66
+ description: HTTP request/response adapter — for web services and REST APIs (default)
67
+ fields:
68
+ request:
69
+ method: "HTTP method (GET|POST|PUT|PATCH|DELETE)"
70
+ path: "URL path (no base URL)"
71
+ headers: "Optional request headers (omit auth tokens — use placeholder)"
72
+ body: "Optional request body (JSON)"
73
+ response:
74
+ status: "HTTP status code (integer)"
75
+ body: "Response body (JSON object)"
76
+ when_to_use: "Feature is triggered by HTTP request and returns HTTP response"
77
+
78
+ cli:
79
+ description: CLI tool adapter — for command-line tools, scripts, and batch processors
80
+ fields:
81
+ args: "Command arguments array (excluding the program name itself)"
82
+ stdin: "Standard input string (null if the command reads no stdin)"
83
+ stdout: "Expected standard output string"
84
+ stderr: "Expected standard error string (empty string if none expected)"
85
+ exit_code: "Expected exit code (integer: 0 = success)"
86
+ when_to_use: "Feature is invoked via command-line arguments and produces stdout/stderr output"
87
+ example: |
88
+ {
89
+ "adapter": "cli",
90
+ "feature_id": "FM-012",
91
+ "scenario": "convert_csv_to_json",
92
+ "args": ["convert", "--input", "data.csv", "--format", "json"],
93
+ "stdin": null,
94
+ "stdout": "{\"rows\": 42, \"status\": \"ok\"}",
95
+ "stderr": "",
96
+ "exit_code": 0,
97
+ "ignore_fields": []
98
+ }
99
+
100
+ file:
101
+ description: File I/O adapter — for batch processors, report generators, and data pipelines
102
+ fields:
103
+ input_files: "List of input file descriptors: {path, content_hash}"
104
+ output_files: "List of expected output file descriptors: {path, content_hash}"
105
+ content_hash_format: "sha256:<hex> — use fixtures for deterministic input; capture hash from reference run for expected output"
106
+ when_to_use: "Feature reads input files and produces output files (no HTTP or CLI interaction)"
107
+ example: |
108
+ {
109
+ "adapter": "file",
110
+ "feature_id": "FM-015",
111
+ "scenario": "generate_monthly_report",
112
+ "input_files": [
113
+ { "path": "fixtures/sales_2026_04.csv", "content_hash": "sha256:abc123" }
114
+ ],
115
+ "output_files": [
116
+ { "path": "output/report_2026_04.pdf", "content_hash": "sha256:def456" }
117
+ ],
118
+ "ignore_fields": []
119
+ }
120
+
121
+ event:
122
+ description: Event/message adapter — for event-driven systems, message queues, and daemons
123
+ fields:
124
+ trigger: "The triggering event or message: {type, queue/topic, payload}"
125
+ expected_state_changes: "List of expected database/state changes: {entity, field, from, to}"
126
+ expected_side_effects: "List of expected side effects using standard labels: EMAIL_SENT, QUEUE_MESSAGE_PUBLISHED, FILE_WRITTEN, HTTP_CALL_MADE"
127
+ when_to_use: "Feature is triggered by an event/message and produces state changes or side effects"
128
+ example: |
129
+ {
130
+ "adapter": "event",
131
+ "feature_id": "FM-020",
132
+ "scenario": "order_placed_triggers_inventory_deduction",
133
+ "trigger": {
134
+ "type": "message",
135
+ "queue": "orders.placed",
136
+ "payload": { "order_id": "ORD-001", "product_id": "PROD-007", "quantity": 1 }
137
+ },
138
+ "expected_state_changes": [
139
+ { "entity": "inventory", "field": "quantity", "from": 10, "to": 9 }
140
+ ],
141
+ "expected_side_effects": ["EMAIL_SENT", "QUEUE_MESSAGE_PUBLISHED"],
142
+ "ignore_fields": ["processed_at", "message_id"]
143
+ }
144
+
145
+ directory_structure:
146
+ root: ".snapshots/"
147
+ layout: ".snapshots/<feature-id>/<scenario>.json"
148
+ example: |
149
+ .snapshots/
150
+ FM-001-UserLogin/ ← http adapter
151
+ happy_path.json
152
+ invalid_credentials.json
153
+ FM-007-OrderCancellation/ ← http adapter
154
+ happy_path.json
155
+ order_not_found.json
156
+ MANUAL-refund_webhook.json
157
+ FM-012-ConvertCsv/ ← cli adapter
158
+ convert_csv_to_json.json
159
+ missing_input_file.json
160
+ FM-015-MonthlyReport/ ← file adapter
161
+ generate_monthly_report.json
162
+ FM-020-InventoryDeduction/ ← event adapter
163
+ order_placed_triggers_inventory_deduction.json
164
+ manual_prefix:
165
+ description: "Files prefixed MANUAL- contain manually authored snapshots (cannot be auto-recorded)"
166
+ examples:
167
+ - webhook endpoints triggered by third parties
168
+ - scenarios requiring specific database state
169
+ - background job/queue-triggered flows
170
+
171
+ ignore_fields_guidance:
172
+ always_ignore:
173
+ - created_at, updated_at, timestamp, recorded_at
174
+ - token, session_id, csrf_token, access_token, refresh_token
175
+ - request_id, trace_id, correlation_id
176
+ always_compare:
177
+ - status, code, message, error_code
178
+ - user_id, order_id (when using fixed test data)
179
+ - amount, quantity, price (business calculation results)
180
+ - "success, refunded, cancelled (boolean business outcomes)"
181
+
182
+ parity_gate:
183
+ description: CI gate comparing new system responses against recorded snapshots
184
+ tool: "npx tsx scripts/parity-check.ts --url <target-url> --snapshots .snapshots [--env <uat|staging>]"
185
+ pass_criteria: "100% of non-MANUAL snapshots must match (status + non-ignored body fields)"
186
+ failure_behavior:
187
+ uat_or_production: "Exit 1 — deployment blocked with [PARITY-BLOCK] message"
188
+ staging: "Exit 2 — warning only with [PARITY-WARN] message"
189
+ report_format: |
190
+ Parity Results: <passed>/<total> passed (<pct>%)
191
+ ✅ FM-001 / happy_path
192
+ ✅ FM-001 / invalid_credentials
193
+ ❌ [PARITY-FAIL] FM-007 / happy_path
194
+ body.order_status: expected "cancelled", got "pending"
195
+
196
+ gate_zero_protocol:
197
+ description: Characterization test gate for refactoring (XSPEC-201 Phase 1)
198
+ when: "--variant refactor in /vo-pipeline"
199
+ requirement: |
200
+ Characterization tests must exist and ALL pass before refactoring begins.
201
+ Characterization tests lock existing behavior — they do not test correctness,
202
+ they test that the behavior does not change during refactoring.
203
+ test_annotation: "@characterization or describe('characterization')"
204
+ enforcement: |
205
+ Gate 0: Run characterization tests before first refactoring commit.
206
+ Any failure during refactoring triggers immediate warning.
207
+ Gate 2: All characterization tests must pass before refactoring is considered complete.
208
+ anti_pattern: "Do NOT start refactoring without Gate 0 — behavior drift becomes undetectable"
209
+
210
+ rules:
211
+ - id: snapshot-before-migration
212
+ trigger: starting --variant migration pipeline
213
+ instruction: |
214
+ .snapshots/ must exist with at least one snapshot per feature before pipeline starts.
215
+ This is Gate 1b in the pre-flight check.
216
+ priority: required
217
+
218
+ - id: characterization-before-refactor
219
+ trigger: starting --variant refactor pipeline
220
+ instruction: |
221
+ Characterization tests must exist and pass before any refactoring begins.
222
+ If characterization tests don't exist, halt and prompt developer to write them.
223
+ priority: required
224
+
225
+ - id: ignore-fields-discipline
226
+ trigger: creating or reviewing snapshot ignore_fields
227
+ instruction: |
228
+ Only add fields to ignore_fields if they are legitimately non-deterministic
229
+ (timestamps, tokens, random IDs). Business logic fields must always be compared.
230
+ Overuse of ignore_fields defeats the purpose of parity testing.
231
+ priority: required
232
+
233
+ - id: manual-snapshots
234
+ trigger: snapshot cannot be auto-recorded
235
+ instruction: |
236
+ Prefix filename with MANUAL- and note why auto-recording is not possible.
237
+ MANUAL- snapshots must be authored by a human with full business knowledge.
238
+ They are excluded from auto-replay but count toward coverage reporting.
239
+ priority: recommended
240
+
241
+ - id: parity-100-before-uat
242
+ trigger: before UAT deployment in migration pipeline
243
+ instruction: |
244
+ Run parity check against all non-MANUAL snapshots.
245
+ 100% pass rate required. Any failure blocks UAT deployment.
246
+ Fix parity failures before promoting — they represent behavioral divergence.
247
+ priority: required
248
+
249
+ - id: adapter-selection
250
+ trigger: creating a behavior snapshot
251
+ instruction: |
252
+ Select the adapter matching the feature's interaction type.
253
+ Use http ONLY for actual HTTP endpoint features.
254
+ Use cli for command-line tool features.
255
+ Use file for batch I/O and report generation features.
256
+ Use event for message-driven and daemon features.
257
+ When in doubt about which adapter to use, refer to the adapters section's when_to_use fields.
258
+ priority: required
259
+
260
+ - id: backward-compatibility
261
+ trigger: reading existing snapshot files without adapter field
262
+ instruction: |
263
+ Treat snapshot files missing the adapter field as adapter=http.
264
+ Do NOT require migration of existing snapshot files to add adapter field.
265
+ New snapshots should always include the adapter field explicitly.
266
+ priority: required
267
+
268
+ related_standards:
269
+ - feature-manifest-standard.ai.yaml
270
+ - acceptance-criteria-traceability.ai.yaml
271
+ - refactoring-standards.ai.yaml
272
+ - testing.ai.yaml
@@ -12,10 +12,13 @@ standard:
12
12
  - "Automate build, test, deploy, and rollback"
13
13
 
14
14
  meta:
15
- version: "1.0.0"
16
- updated: "2026-02-09"
15
+ version: "1.1.0"
16
+ updated: "2026-05-13"
17
17
  source: core/deployment-standards.md
18
- description: Safe deployment strategies, feature flags, rollback, environment parity, and DORA metrics
18
+ description: >
19
+ Safe deployment strategies, feature flags, rollback, environment parity, and DORA metrics.
20
+ v1.1.0: Added environment stratification responsibility matrix and stub server CI/CD
21
+ lifecycle rules (XSPEC-204).
19
22
 
20
23
  principles:
21
24
  core:
@@ -125,6 +128,108 @@ checklist:
125
128
  medium_term_24hr: ["Batch jobs successful", "Data consistency verified", "No memory leaks"]
126
129
  long_term_1wk: ["Flag cleanup scheduled", "Retrospective completed", "Docs updated"]
127
130
 
131
+ # ─────────────────────────────────────────────────────────
132
+ # Environment Stratification Responsibility Matrix (v1.1.0)
133
+ # ─────────────────────────────────────────────────────────
134
+ environment_stratification_matrix:
135
+ rule: >
136
+ Projects with external dependencies MUST build an environment stratification
137
+ responsibility matrix at test-planning time (not at release candidate review).
138
+ This matrix answers: "Which test flows can be fully verified in which environment?"
139
+ when_required: "Any project with external service dependencies (SMS, payment, IdP, messaging, etc.)"
140
+ where: "docs/testing/environment-stratification-matrix.md or in the test plan"
141
+
142
+ template: |
143
+ ## Environment Stratification Responsibility Matrix
144
+
145
+ | Flow / Feature | local UAT | Remote UAT | PRD |
146
+ |------------------------|:----------------:|:----------------:|:------------------:|
147
+ | Auth / Login | ✅ stub IdP | ✅ Keycloak/AD | ✅ Enterprise IdP |
148
+ | SMS Sending | ⚠️ L2-stub | ❌ / ⚠️ | ✅ Real + billing |
149
+ | Payment / Purchase | ⚠️ L2-stub | ⚠️ network-dep | ✅ Real + reconcile|
150
+ | User / Dept Management | ✅ | ✅ cross-machine | ✅ |
151
+ | Background Jobs | ✅ in-process | ✅ | ✅ |
152
+
153
+ Legend:
154
+ ✅ Full E2E verification possible
155
+ ⚠️ Flow passes through stub/mock — real-world dimensions NOT verified
156
+ ❌ Cannot test in this environment layer
157
+
158
+ ### PRD-only verification items (must plan PRD smoke test coverage)
159
+ - SMS billing correctness
160
+ - Payment real debit + bank reconciliation
161
+ - Enterprise AD group sync behavior
162
+
163
+ mandatory_rule: >
164
+ This matrix MUST be reviewed at test planning stage alongside
165
+ acceptance-criteria-traceability. It is NOT acceptable to discover
166
+ PRD-only verification items for the first time during release candidate review.
167
+
168
+ # ─────────────────────────────────────────────────────────
169
+ # Stub Server CI/CD Lifecycle Rules (v1.1.0)
170
+ # ─────────────────────────────────────────────────────────
171
+ stub_server_cicd_rules:
172
+ rationale: >
173
+ Production CI/CD artifacts commonly exclude stub server directories for valid
174
+ security reasons (e.g., MockSoap/, tests/stub-server/ excluded from build.ps1).
175
+ Without explicit rules, this creates an undocumented "UAT cannot test X" situation
176
+ that is discovered too late in the release cycle.
177
+
178
+ build_time:
179
+ rules:
180
+ - "Production artifact MUST exclude all stub server code directories"
181
+ - "Exclusion rule MUST be documented in docs/uat/STUB-EXCLUSION-RATIONALE.md"
182
+ - "CI build verification step MUST confirm stub directories are absent from artifact"
183
+ example_ps1: |
184
+ # build.ps1 — explicit exclusion with documentation comment
185
+ # Stub server excluded from production artifact (see docs/uat/STUB-EXCLUSION-RATIONALE.md)
186
+ # Consequence: UAT machine cannot run SMS/MMS flow without sidecar deployment (see Option A below)
187
+ $exclude = @("MockSoap", "tests/stub-server", "test-fixtures")
188
+
189
+ uat_deployment:
190
+ rule: "Choose Option A or Option B; document the choice; do NOT leave it undocumented"
191
+ options:
192
+ option_a_sidecar_deploy:
193
+ name: "Sidecar Deployment (Recommended)"
194
+ description: >
195
+ Stub server deployed as separate process to UAT machine;
196
+ application configured via env var to reach the stub endpoint.
197
+ benefit: "Decoupled from app artifact; independently updatable"
198
+ example_github_actions: |
199
+ deploy-stub-server:
200
+ environment: uat
201
+ steps:
202
+ - name: Deploy stub server to UAT
203
+ run: |
204
+ ssh uat-machine "cd /opt/stub-server && pm2 start server.js --name stub-server"
205
+ - name: Configure app to use stub
206
+ run: echo "SMS_ENDPOINT=http://uat-machine:9999/soap" >> $GITHUB_ENV
207
+ example_gitlab_ci: |
208
+ deploy-stub-server:
209
+ stage: deploy
210
+ environment: uat
211
+ script:
212
+ - ssh uat-machine "pm2 start tests/stub-server/server.js --name stub-server"
213
+ only: [uat-branch, tags]
214
+
215
+ option_b_prd_smoke_deferral:
216
+ name: "PRD Smoke Deferral"
217
+ description: >
218
+ Document which flows cannot be tested in UAT; explicitly mark them as
219
+ PRD-smoke-only items. These items must appear in the environment stratification matrix.
220
+ requirement: "Must be reflected as ❌ in environment stratification matrix with PRD smoke plan"
221
+
222
+ prd_prohibitions:
223
+ - "MUST NOT deploy or run stub servers in PRD environment"
224
+ - "MUST NOT include stub server code in PRD artifact"
225
+ - "MUST NOT allow network access to stub server endpoints from PRD"
226
+
227
+ forbidden_state: >
228
+ It is NEVER acceptable to have "no stub server AND no real service AND no documented
229
+ deferral" for a testable flow. Every flow must be either:
230
+ (a) fully testable in UAT via real service or Level 2 stub server, OR
231
+ (b) explicitly documented as PRD-smoke-only with a smoke test plan.
232
+
128
233
  physical_spec:
129
234
  type: custom_script
130
235
  validator: