@atlashub/smartstack-cli 4.26.0 → 4.27.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlashub/smartstack-cli",
3
- "version": "4.26.0",
3
+ "version": "4.27.0",
4
4
  "description": "SmartStack Claude Code automation toolkit - GitFlow, EF Core migrations, prompts and more",
5
5
  "author": {
6
6
  "name": "SmartStack",
@@ -18,17 +18,18 @@ Write and update granular JSON files for project-level (multi-app), application-
18
18
  - Module-level: `docs/{app}/{module}/business-analyse/v{X.Y}/index.json` + thematic files
19
19
 
20
20
  **Thematic files (v2 granular architecture):**
21
- - `index.json` — metadata, version, hash manifest, module registry
22
- - `cadrage.json` — stakeholders, problem/vision, risks, acceptance criteria
23
- - `entities.json` — entity definitions with attributes and relationships
24
- - `rules.json` — business rules with categories and conditions
25
- - `usecases.json` — use cases and functional requirements
26
- - `permissions.json` — permission matrix and role assignments
27
- - `screens.json` — UI wireframes and navigation
28
- - `validation.json` — validation rules and consistency checks
29
- - `handoff.json` — complexity, file catalog, BR-to-code mapping
30
- - `consolidation.json` — cross-module interactions and E2E flows (application-level only)
31
- - `review.json` — preserved review comments and change summary
21
+ - `index.json` — metadata, version, hash manifest, module registry (ALL scopes)
22
+ - `cadrage.json` — stakeholders, problem/vision, risks, acceptance criteria (ALL scopes)
23
+ - `validation.json` — validation rules and consistency checks (ALL scopes)
24
+ - `consolidation.json` — cross-module interactions and E2E flows (application/project ONLY)
25
+ - `navigation.json` — navigation tree (application/project ONLY, created by ba-design-ui)
26
+ - `entities.json` — entity definitions with attributes and relationships (**MODULE ONLY**)
27
+ - `rules.json` — business rules with categories and conditions (**MODULE ONLY**)
28
+ - `usecases.json` — use cases and functional requirements (**MODULE ONLY**)
29
+ - `permissions.json` — permission matrix and role assignments (**MODULE ONLY**)
30
+ - `screens.json` — UI wireframes and navigation (**MODULE ONLY**)
31
+ - `handoff.json` — complexity, file catalog, BR-to-code mapping (**MODULE ONLY**)
32
+ - `review.json` — preserved review comments and change summary (ALL scopes)
32
33
 
33
34
  > **Backward compatibility:** If only 1 application, the project level is NOT created. The application-level index.json remains the master.
34
35
 
@@ -62,12 +63,12 @@ Create initial index.json and empty thematic files with metadata and draft statu
62
63
  - For project scope: modules: []
63
64
  - For application scope: modules: []
64
65
  - For module scope: (no modules array)
65
- 5. Create empty thematic files appropriate for scope:
66
- - Project/application: cadrage.json, validation.json, handoff.json, consolidation.json
67
- > **NOTE:** Do NOT create entities.json, rules.json, usecases.json, permissions.json, screens.json at project/application level.
68
- > These thematic files contain module-specific data and are created ONLY at module level by step-03 (specify).
69
- > Creating empty versions at app level causes downstream tools (derive-prd) to read empty arrays instead of module data.
70
- - Module: entities.json, rules.json, usecases.json, permissions.json, screens.json, validation.json, handoff.json
66
+ 5. Create empty thematic files ONLY the files listed for the scope. Creating ANY unlisted file is a **BLOCKING ERROR**.
67
+ - Project: cadrage.json, validation.json, consolidation.json — **EXACTLY 3 files, NO OTHERS**
68
+ - Application: cadrage.json, validation.json, consolidation.json **EXACTLY 3 files, NO OTHERS**
69
+ - Module: entities.json, rules.json, usecases.json, permissions.json, screens.json, validation.json, handoff.json **EXACTLY 7 files**
70
+
71
+ **FORBIDDEN at project/application level:** entities.json, rules.json, usecases.json, permissions.json, screens.json, handoff.json — these exist ONLY at module level.
71
72
  6. Update `.business-analyse/config.json` with new lastFeatureId
72
73
  7. IF scope = "module" AND applicationRef provided AND moduleCode provided:
73
74
  a. Read master index.json (via applicationRef FEAT-NNN)
@@ -110,8 +111,8 @@ Create a project-level index.json for multi-application analysis. Only used when
110
111
  - fileHashes: {}
111
112
  - applications: []
112
113
  - applicationDependencyGraph: {}
113
- 5. Create thematic files (cadrage.json, validation.json, consolidation.json)
114
- > Do NOT create entities/rules/usecases/permissions/screens at project level — these exist only at module level.
114
+ 5. Create thematic files — **EXACTLY 3 files, NO OTHERS:** cadrage.json, validation.json, consolidation.json
115
+ **FORBIDDEN at project level:** entities.json, rules.json, usecases.json, permissions.json, screens.json, handoff.json — these exist ONLY at module level.
115
116
  6. Update `.business-analyse/config.json` with new lastProjectId
116
117
  7. Deploy schemas to `docs/business-analyse/schemas/` (including project-schema.json)
117
118
  8. Return project ID (PROJ-NNN) and path
@@ -178,6 +179,10 @@ Write a complete thematic file and update its hash in index.json.
178
179
 
179
180
  **Process:**
180
181
  1. Find and read index.json (use findFeature if given ID)
182
+ 1b. **SCOPE GUARD (BLOCKING):** If index.json scope is "project" or "application", verify themeName is ALLOWED:
183
+ - Allowed: [cadrage, validation, consolidation, navigation, review]
184
+ - FORBIDDEN: [entities, rules, usecases, permissions, screens, handoff]
185
+ If themeName is FORBIDDEN → **REJECT with BLOCKING ERROR.** Do NOT create the file.
181
186
  2. Determine thematic filename: `{themeName}.json`
182
187
  3. Create full path: `{version_dir}/{themeName}.json`
183
188
  4. Write thematic file with pretty-print (2-space indent)
@@ -335,6 +340,20 @@ Increment the module loop counter in the master index.json.
335
340
  9. Write back index.json
336
341
  10. Return new index and whether loop is complete
337
342
 
343
+ ### cleanupAppLevelFiles
344
+ Remove forbidden thematic files at project/application level and clean their entries from fileHashes.
345
+
346
+ **Input:** featureId: FEAT-NNN
347
+
348
+ **Process:**
349
+ 1. Read index.json (verify scope is "project" or "application")
350
+ 2. FORBIDDEN = [entities.json, rules.json, usecases.json, permissions.json, screens.json, handoff.json]
351
+ 3. For each FORBIDDEN file: if file exists in version directory → DELETE it
352
+ 4. For each FORBIDDEN file: if referenced in fileHashes → REMOVE entry
353
+ 5. If `files` property exists in index.json: remove entries for forbidden files
354
+ 6. Update metadata.updatedAt, write index.json
355
+ 7. Return list of cleaned files
356
+
338
357
  ### createVersion
339
358
  Create a new version for refactoring or major changes.
340
359
 
@@ -470,43 +489,29 @@ docs/business-analyse/
470
489
  v1.0/
471
490
  index.json ← PROJECT metadata
472
491
  cadrage.json
473
- entities.json
474
- rules.json
475
- usecases.json
476
- permissions.json
477
- screens.json
478
492
  validation.json
479
493
  consolidation.json
494
+ # NO entities/rules/usecases/permissions/screens/handoff — MODULE ONLY
480
495
 
481
496
  docs/{app}/business-analyse/
482
497
  v1.0/
483
498
  index.json ← APPLICATION metadata
484
499
  cadrage.json
485
- entities.json
486
- rules.json
487
- usecases.json
488
- permissions.json
489
- screens.json
490
500
  validation.json
491
501
  consolidation.json
492
- v1.1/
493
- index.json
494
- cadrage.json
495
- entities.json
496
- ...
502
+ navigation.json ← (created by ba-design-ui)
503
+ # NO entities/rules/usecases/permissions/screens/handoff — MODULE ONLY
497
504
 
498
505
  docs/{app}/{module}/business-analyse/
499
506
  v1.0/
500
507
  index.json ← MODULE metadata
501
- discovery.json
502
- analysis.json
503
- specification.json
508
+ entities.json
509
+ rules.json
510
+ usecases.json
511
+ permissions.json
512
+ screens.json
504
513
  validation.json
505
514
  handoff.json
506
- v1.1/
507
- index.json
508
- discovery.json
509
- ...
510
515
  ```
511
516
 
512
517
  Versions are stored as separate directories. Each directory contains index.json + thematic files.
@@ -726,11 +731,6 @@ if (estimatedNewSize > 500 * 1024) { // 500KB
726
731
  "fileHashes": {
727
732
  "index.json": "pqr678...",
728
733
  "cadrage.json": "stu901...",
729
- "entities.json": "vwx234...",
730
- "rules.json": "yza567...",
731
- "usecases.json": "bcd890...",
732
- "permissions.json": "efg123...",
733
- "screens.json": "hij456...",
734
734
  "validation.json": "klm789...",
735
735
  "consolidation.json": "nop012..."
736
736
  },
@@ -7,10 +7,8 @@
7
7
  "FailOnMigrationError": true,
8
8
  "EnableDevSeeding": false,
9
9
  "CorsOrigins": [
10
- "http://localhost:5173",
11
- "http://localhost:5174",
12
- "http://localhost:5175",
13
- "http://localhost:6173"
10
+ "http://localhost:3000",
11
+ "http://localhost:5173"
14
12
  ]
15
13
  },
16
14
  "Jwt": {
@@ -34,7 +32,7 @@
34
32
  "SecretExpiresAt": "",
35
33
  "CallbackPath": "/api/auth/google/callback"
36
34
  },
37
- "FrontendUrl": "http://localhost:6173"
35
+ "FrontendUrl": "http://localhost:3000"
38
36
  },
39
37
  "Session": {
40
38
  "IdleTimeoutMinutes": 20,
@@ -125,7 +123,7 @@
125
123
  "Provider": "Development",
126
124
  "FromEmail": "noreply@{{ProjectDomain}}",
127
125
  "FromName": "{{ProjectName}}",
128
- "BaseUrl": "http://localhost:5173",
126
+ "BaseUrl": "http://localhost:3000",
129
127
  "TokenExpiration": {
130
128
  "EmailConfirmation": "24:00:00",
131
129
  "PasswordReset": "01:00:00"
@@ -149,6 +149,7 @@ Execute incremental SmartStack development using the APEX methodology. This skil
149
149
  - **Parallel Agent tool** - Parallel execution for scan (step-01) and within Layer 2/3 (step-03) for multi-entity, unless economy_mode
150
150
  - **Tests inline** - Backend tests run after Layer 2, frontend tests run after Layer 3 (max 3 fix iterations each). Step-07 = final sweep (security + coverage).
151
151
  - **Exception: seed data** — The templates in core-seed-data.md and person-extension-pattern.md are generated directly because no MCP tool covers seed data creation. This is a documented exception to the "orchestrate, never generate" rule.
152
+ - **Frontend pages: ALWAYS via Skill("ui-components")** — economy_mode affects parallelization only, NOT whether /ui-components is called. NEVER generate .tsx pages directly, even in delegate or economy mode.
152
153
  - **Save outputs** if `{save_mode}` = true
153
154
  - **Commits per layer** - Atomic commits after each execution layer
154
155
  - **Delegate mode** (`-d`): Read PRD context, skip challenge questions, auto+economy mode implied. Used when `/ralph-loop` delegates code generation to `/apex`.
@@ -82,8 +82,25 @@ questions:
82
82
  multiSelect: true
83
83
  ```
84
84
 
85
+ **Reserved codes (NOT valid sections):**
86
+ - `detail` — auto-generated as `/:id` route when "list" section exists
87
+ - `create` — auto-generated as `/create` route when "list" section exists
88
+ - `edit` — auto-generated as `/:id/edit` route when "list" section exists
89
+
90
+ These are **view modes**, not sections. A "list" section automatically generates 4 pages: ListPage (index), DetailPage (/:id), CreatePage (/create), EditPage (/:id/edit).
91
+
85
92
  **Validation:**
86
93
  ```
94
+ RESERVED_SECTION_CODES = ["detail", "create", "edit"]
95
+
96
+ IF any section.code IN RESERVED_SECTION_CODES:
97
+ DISPLAY: "'{section.code}' is a view mode, not a section.
98
+ The 'list' section automatically includes detail (/:id), create (/create),
99
+ and edit (/:id/edit) pages. Remove '{section.code}' from your sections."
100
+ → Remove the offending section(s) from the list
101
+ → Re-ask the sections question if {sections}.length == 0
102
+ → DO NOT proceed
103
+
87
104
  IF {sections}.length == 0:
88
105
  DISPLAY: "Every module must have at least one section. Please select or define at least one."
89
106
  → Re-ask the sections question
@@ -117,6 +117,21 @@ if [ -n "$APP_TSX" ]; then
117
117
  fi
118
118
  ```
119
119
 
120
+ ### POST-CHECK S7: Controllers must NOT use Guid.Empty for tenantId/userId (OWASP A01)
121
+
122
+ ```bash
123
+ CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
124
+ if [ -n "$CTRL_FILES" ]; then
125
+ BAD_GUID=$(grep -Pn 'Guid\.Empty' $CTRL_FILES 2>/dev/null)
126
+ if [ -n "$BAD_GUID" ]; then
127
+ echo "BLOCKING (OWASP A01): Controller uses Guid.Empty — tenant isolation bypassed"
128
+ echo "$BAD_GUID"
129
+ echo "Fix: Use _currentTenant.TenantId from ICurrentTenantService"
130
+ exit 1
131
+ fi
132
+ fi
133
+ ```
134
+
120
135
  ---
121
136
 
122
137
  ## Backend — Entity, Service & Controller Checks
@@ -154,6 +169,39 @@ fi
154
169
 
155
170
  ## Frontend — CSS, Forms, Components, I18n
156
171
 
172
+ ### POST-CHECK C3a: Frontend must not be empty if Layer 3 was planned (BLOCKING)
173
+
174
+ ```bash
175
+ # If foundation_mode is false AND App.tsx exists, verify frontend was generated
176
+ APP_TSX=$(find web/ src/ -name "App.tsx" -not -path "*/node_modules/*" 2>/dev/null | head -1)
177
+ if [ -n "$APP_TSX" ]; then
178
+ # Check if applicationRoutes is an empty object
179
+ EMPTY_ROUTES=$(grep -P "applicationRoutes.*=\s*\{[\s/]*\}" "$APP_TSX" 2>/dev/null)
180
+ if [ -n "$EMPTY_ROUTES" ]; then
181
+ echo "BLOCKING: applicationRoutes in App.tsx is empty — Layer 3 frontend was NOT executed"
182
+ echo "Expected: at least one application key with route definitions"
183
+ echo "Fix: Run Layer 3 (scaffold_routes + scaffold_extension + route wiring)"
184
+ exit 1
185
+ fi
186
+
187
+ # Check pages/ directory is not empty
188
+ PAGE_COUNT=$(find web/ src/ -path "*/pages/*" -name "*.tsx" -not -path "*/node_modules/*" 2>/dev/null | wc -l)
189
+ if [ "$PAGE_COUNT" -eq 0 ]; then
190
+ echo "BLOCKING: No page components found in pages/ directory"
191
+ echo "Fix: Generate pages via scaffold_extension or /ui-components"
192
+ exit 1
193
+ fi
194
+
195
+ # Check navRoutes.generated.ts exists
196
+ NAV_ROUTES=$(find web/ src/ -name "navRoutes.generated.ts" -not -path "*/node_modules/*" 2>/dev/null | head -1)
197
+ if [ -z "$NAV_ROUTES" ]; then
198
+ echo "BLOCKING: navRoutes.generated.ts not found — scaffold_routes was never called"
199
+ echo "Fix: Run scaffold_routes(source: 'controllers', outputFormat: 'applicationRoutes')"
200
+ exit 1
201
+ fi
202
+ fi
203
+ ```
204
+
157
205
  ### POST-CHECK C3: Translation files must exist for all 4 languages (if frontend)
158
206
 
159
207
  ```bash
@@ -290,6 +290,24 @@ When launching agents for multi-entity layers:
290
290
  - Agent principal updates activeForm after each entity completion:
291
291
  `TaskUpdate(taskId: layer2_task_id, activeForm: "Building {EntityName} backend (2/5 entities)")`
292
292
 
293
+ ### Controller NavRoute Attribute (MANDATORY)
294
+
295
+ After controller generation, verify `[NavRoute]` attribute is present on every controller:
296
+ - Expected: `[NavRoute("{app_name}.{module_code}.{section_code}")]` on the controller class
297
+ - If missing: Add it manually above `[Authorize]`
298
+ - When calling `scaffold_extension(type: "controller")`, always pass `navRoute` in options
299
+ - This is REQUIRED for `scaffold_routes` to auto-detect routes in Layer 3
300
+
301
+ ```bash
302
+ # Quick check: all controllers must have [NavRoute] (not just [Route])
303
+ CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
304
+ for f in $CTRL_FILES; do
305
+ if ! grep -q "\[NavRoute" "$f" && grep -q "\[Route" "$f"; then
306
+ echo "WARNING: $f has [Route] but no [NavRoute] — add [NavRoute] for route auto-detection"
307
+ fi
308
+ done
309
+ ```
310
+
293
311
  ### Post-Layer 2 Build Gate
294
312
 
295
313
  ```bash
@@ -377,7 +395,18 @@ For each module:
377
395
  → All business applications use `<AppLayout />` as layout wrapper
378
396
  → See `references/frontend-route-wiring-app-tsx.md` for full Pattern A/B detection and examples
379
397
  → Verify: `mcp__smartstack__validate_frontend_routes (scope: 'routes')`
380
- - Pages: use /ui-components skill follow smartstack-frontend.md patterns:
398
+ - Pages per section: When a section exists (e.g., "list"), generate ALL 4 page types:
399
+ → {Section}ListPage.tsx (index route)
400
+ → {Section}DetailPage.tsx (/:id route) — click on list item navigates here
401
+ → Create{Section}Page.tsx (/create route)
402
+ → Edit{Section}Page.tsx (/:id/edit route)
403
+ "detail" is NEVER a separate section — it's the /:id route of the list section.
404
+ - Pages: **MANDATORY — INVOKE Skill("ui-components")** for ALL page generation.
405
+ ⚠ BLOCKING REQUIREMENT: You MUST call Skill("ui-components") for EVERY page (.tsx).
406
+ NEVER generate .tsx page code directly. NEVER write page content in Agent prompts.
407
+ NEVER use Write tool to create pages without first calling Skill("ui-components").
408
+ The skill loads the full style guide + patterns (entity-card, data-table, dashboard-chart, grid-layout).
409
+ Follow smartstack-frontend.md patterns:
381
410
  → React.lazy() for all page imports (named export wrapping)
382
411
  → `<Suspense fallback={<PageLoader />}>` around all lazy components
383
412
  → Page structure: hooks → useEffect(load) → loading → error → content
@@ -466,7 +495,21 @@ IF NOT economy_mode AND entities.length > 1:
466
495
  # All agents launched in parallel
467
496
 
468
497
  ELSE:
469
- # Agent principal handles all entities sequentially
498
+ # Economy mode: Agent principal handles all entities SEQUENTIALLY.
499
+ # MANDATORY: You MUST still call Skill("ui-components") for page generation.
500
+ # economy_mode only disables parallel agents — it does NOT skip /ui-components.
501
+ For each entity (sequentially):
502
+ 1. MCP scaffold_api_client
503
+ 2. MCP scaffold_routes (outputFormat: 'applicationRoutes')
504
+ 3. Wire routes to App.tsx (Pattern A/B — see references/frontend-route-wiring-app-tsx.md)
505
+ 4. **INVOKE Skill("ui-components")** — pass entity context:
506
+ - Entity: {EntityName}, Module: {ModuleName}, App: {AppName}
507
+ - Page types: List, Detail, Create, Edit (+ Dashboard if applicable)
508
+ - "CSS: Use CSS variables ONLY — bg-[var(--bg-card)], text-[var(--text-primary)]"
509
+ - "Forms: Create/Edit are FULL PAGES with own routes (/create, /:id/edit)"
510
+ - "I18n: ALL text uses t('namespace:key', 'Fallback')"
511
+ 5. I18n: 4 JSON files (fr, en, it, de) + register namespace in i18n config
512
+ 6. Form tests: co-located .test.tsx for Create and Edit pages
470
513
  ```
471
514
 
472
515
  ### Parallel Agents + TaskCreate Integration
@@ -495,6 +538,24 @@ When delegating to `/ui-components` skill, include explicit instructions:
495
538
  - "Forms: Create/Edit forms are FULL PAGES with own routes (e.g., `/create`, `/:id/edit`)."
496
539
  - "I18n: ALL text must use `t('namespace:key', 'Fallback')`. Generate JSON files in `src/i18n/locales/`."
497
540
 
541
+ ### HARD RULE — /ui-components is NON-NEGOTIABLE
542
+
543
+ > **VIOLATION CHECK:** If ANY .tsx page file was created by Write tool WITHOUT
544
+ > a prior Skill("ui-components") call in this execution, the frontend layer is INVALID.
545
+ >
546
+ > **You MUST NOT:**
547
+ > - Generate .tsx page code in Agent prompts and dispatch to Snipper agents
548
+ > - Write page content directly via the Write tool
549
+ > - Copy-paste page templates from your knowledge
550
+ >
551
+ > **You MUST:**
552
+ > - Call Skill("ui-components") which loads the style guide, responsive guidelines,
553
+ > accessibility rules, and pattern files (entity-card, data-table, dashboard-chart, grid-layout, kanban)
554
+ > - Let /ui-components generate all pages with correct conventions
555
+ >
556
+ > **Why this matters:** Without the skill, pages miss CSS variables, DataTable/EntityCard,
557
+ > memo()/useCallback, responsive mobile-first design, and accessibility patterns.
558
+
498
559
  ### Frontend Tests Inline
499
560
 
500
561
  > **Tests are scaffolded and run WITHIN Layer 3, not deferred to step-07.**
@@ -357,13 +357,12 @@ IF workflow_mode = "project":
357
357
  })
358
358
 
359
359
  Output path: docs/business-analyse/v{version}/index.json
360
- Thematic files (empty, initialized): docs/business-analyse/v{version}/*.json
360
+ Thematic files — EXACTLY these 3, NO OTHERS:
361
361
  - cadrage.json
362
362
  - validation.json
363
363
  - consolidation.json
364
- // NOTE: entities, rules, usecases, permissions, screens are MODULE-LEVEL only.
365
- // They are created by step-03 (specify) in each module directory.
366
- // Do NOT create them at project/app level — empty files mislead downstream tools.
364
+ FORBIDDEN: entities.json, rules.json, usecases.json, permissions.json, screens.json, handoff.json
365
+ Creating any FORBIDDEN file at project level is a BLOCKING ERROR.
367
366
 
368
367
  Store:
369
368
  project_id: string // PROJ-NNN
@@ -394,18 +393,29 @@ ELSE:
394
393
  })
395
394
 
396
395
  Output path: docs/{app}/business-analyse/v{version}/index.json
397
- Thematic files (empty, initialized): docs/{app}/business-analyse/v{version}/*.json
396
+ Thematic files — EXACTLY these 3, NO OTHERS:
398
397
  - cadrage.json
399
398
  - validation.json
400
- - handoff.json
401
- // NOTE: entities, rules, usecases, permissions, screens are MODULE-LEVEL only.
402
- // They are created by step-03 (specify) in each module directory.
403
- // Do NOT create them at app level — empty files mislead derive-prd and ralph-loop.
399
+ - consolidation.json
400
+ FORBIDDEN: entities.json, rules.json, usecases.json, permissions.json, screens.json, handoff.json
401
+ Creating any FORBIDDEN file at application level is a BLOCKING ERROR.
404
402
  ```
405
403
 
406
404
  > **Note:** In project mode, per-application index.json files are created later in step-01-cadrage.
407
405
  > In single-app mode, step-02 (structure) determines if it's single or multi-module.
408
406
 
407
+ ### POST-CREATE VERIFICATION (MANDATORY)
408
+
409
+ After creating thematic files, verify no forbidden files were created:
410
+
411
+ ```
412
+ List all *.json files in {docs_dir}/
413
+ Expected (project/application): [index.json, cadrage.json, validation.json, consolidation.json]
414
+ If any file NOT in expected list is found → DELETE it immediately + log WARNING
415
+ ```
416
+
417
+ This guard catches any accidental creation of module-only files at the wrong scope.
418
+
409
419
  ## Step 10: Update Config
410
420
 
411
421
  Update `.business-analyse/config.json` with new feature information.
@@ -423,20 +423,8 @@ ba-writer.enrichSection({
423
423
  // Update status
424
424
  ba-writer.updateStatus({feature_id}, "consolidated");
425
425
 
426
- // FIX H3: Clean up app-level index.json files entries
427
- // Only keep entries for files that actually have content at app level.
428
- // Module-level data lives in module directories — do NOT declare empty app-level files.
429
- ba-writer.enrichSection({
430
- featureId: {feature_id},
431
- section: "files",
432
- data: {
433
- // Keep only files that exist and have content at app level
434
- cadrage: { path: "cadrage.json", hash: "framed", lastModified: now() },
435
- validation: { path: "validation.json", hash: "consolidated", lastModified: now() }
436
- // REMOVED: entities, rules, usecases, permissions, screens, handoff
437
- // These exist only at module level (flat-file architecture)
438
- }
439
- });
426
+ // Clean up any forbidden files that may have been created at app level
427
+ ba-writer.cleanupAppLevelFiles({ featureId: {feature_id} });
440
428
 
441
429
  // Save workflow state for resume support
442
430
  ba-writer.enrichSection({
@@ -306,7 +306,38 @@ appendFile('.ralph/progress.txt',
306
306
  );
307
307
  ```
308
308
 
309
- ### C2. Increment Iteration
309
+ ### C2. Skipped Task Audit
310
+
311
+ After apex returns, check for newly skipped tasks:
312
+
313
+ ```javascript
314
+ const newlySkipped = prdCheck.tasks.filter(t =>
315
+ batchIds.includes(t.id) && t.status === 'skipped'
316
+ );
317
+ if (newlySkipped.length > 0) {
318
+ console.warn(`⚠ ${newlySkipped.length} tasks were SKIPPED by apex:`);
319
+ newlySkipped.forEach(t => console.warn(` - ${t.id}: ${t.description}`));
320
+
321
+ // If ALL tasks in a category were skipped → BLOCKING, reset for retry
322
+ const skippedCategories = [...new Set(newlySkipped.map(t => t.category))];
323
+ for (const cat of skippedCategories) {
324
+ const allInCat = prdCheck.tasks.filter(t => t.category === cat);
325
+ const allSkipped = allInCat.every(t => t.status === 'skipped');
326
+ if (allSkipped) {
327
+ console.error(`BLOCKING: ALL tasks in category "${cat}" were skipped — investigate root cause`);
328
+ // Reset to pending for retry
329
+ allInCat.forEach(t => {
330
+ t.status = 'pending';
331
+ t._retryCount = (t._retryCount || 0) + 1;
332
+ t.error = `All tasks in category "${cat}" skipped — auto-retry`;
333
+ });
334
+ }
335
+ }
336
+ writeJSON(currentPrdPath, prdCheck);
337
+ }
338
+ ```
339
+
340
+ ### C3. Increment Iteration
310
341
 
311
342
  ```javascript
312
343
  prdCheck.config.current_iteration++;
@@ -314,7 +345,7 @@ prdCheck.updated_at = new Date().toISOString();
314
345
  writeJSON(currentPrdPath, prdCheck);
315
346
  ```
316
347
 
317
- ### C3. Git Commit (PRD state only — apex already committed code)
348
+ ### C4. Git Commit (PRD state only — apex already committed code)
318
349
 
319
350
  ```bash
320
351
  git add {currentPrdPath} .ralph/progress.txt
@@ -331,7 +362,7 @@ EOF
331
362
  )"
332
363
  ```
333
364
 
334
- ### C4. PRD Sync Verification (HARD CHECK)
365
+ ### C5. PRD Sync Verification (HARD CHECK)
335
366
 
336
367
  > **Note:** `prdCheck` (read in C1) is the authoritative post-apex snapshot.
337
368
  > `completed` (from B3) was computed on a DIFFERENT read — do NOT mix the two.