speccrew 0.6.56 → 0.6.59

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.
@@ -353,6 +353,21 @@ Before executing the workflow, verify the following inputs:
353
353
  </block>
354
354
 
355
355
  <!-- ==================== STEP 5A: COPY TEMPLATE TO DOCUMENT PATH ==================== -->
356
+ <!-- Document Output Path Rule -->
357
+ <block type="rule" id="R-DOCPATH" level="mandatory" desc="Document output path MUST use documentPath parameter">
358
+ <field name="text">
359
+ The output document file MUST be created at the EXACT path specified by ${documentPath} input parameter.
360
+ DO NOT use the template file name (e.g., FEATURE-DETAIL-TEMPLATE-*.md) as the output file name.
361
+ The documentPath already contains the correct target path including file name (e.g., speccrew-workspace/knowledges/bizs/backend-system/admin/cache/cache_controller.md).
362
+ Before creating the file, ensure the parent directory exists (create if necessary).
363
+ </field>
364
+ </block>
365
+
366
+ <!-- Ensure Document Output Directory Exists -->
367
+ <block type="task" id="B14a" action="run-script" desc="Ensure document output directory exists">
368
+ <field name="command">node -e "require('fs').mkdirSync(require('path').dirname('${documentPath}'), {recursive: true})"</field>
369
+ </block>
370
+
356
371
  <!-- Replace Top-Level Placeholders -->
357
372
  <block type="task" id="B14" action="replace-placeholders" desc="Replace template placeholders">
358
373
  <field name="template" value="${templateContent}"/>
@@ -278,13 +278,17 @@ Continue with knowledge base generation?
278
278
  <sequence id="S1a" name="Stage 1a: Entry Directory Recognition" status="pending" desc="Identify entry directories for each platform and classify into business modules">
279
279
 
280
280
  <block type="rule" id="S1a-R1" level="mandatory" desc="Stage 1a mandatory rules">
281
- <field name="text">This stage is executed DIRECTLY by the dispatch agent (Leader), NOT delegated to a Worker Agent</field>
281
+ <field name="text">ALL platform entry directory recognition tasks MUST be dispatched IN PARALLEL sequential execution is FORBIDDEN</field>
282
+ <field name="text">ALL Worker dispatch calls in S1a-L1 MUST be issued SIMULTANEOUSLY in a SINGLE orchestration turn</field>
283
+ <field name="text">DO NOT wait for any Worker to complete before dispatching the next Worker</field>
284
+ <field name="text">Dispatch all ${max_concurrent_workers} workers at once, then wait for ALL to complete</field>
285
+ <field name="text">Sequential one-by-one dispatch is STRICTLY FORBIDDEN</field>
282
286
  </block>
283
287
 
284
- <block type="loop" id="S1a-L1" over="${platforms}" as="platform" desc="Iterate each platform to identify entry directories">
288
+ <block type="loop" id="S1a-L1" over="${platforms}" as="platform" parallel="true" max-concurrency="${max_concurrent_workers}" desc="Dispatch entry directory recognition for each platform IN PARALLEL">
285
289
  <!-- Step 1: Read source directory tree -->
286
- <block type="task" id="S1a-B1" action="run-skill" status="pending" desc="Invoke entry identification Skill">
287
- <field name="skill">speccrew-knowledge-bizs-identify-entries-xml</field>
290
+ <block type="task" id="S1a-B1" action="dispatch-to-worker" status="pending" desc="Dispatch entry identification to Worker">
291
+ <field name="worker">speccrew-knowledge-bizs-identify-entries-xml</field>
288
292
  <field name="source_path" value="${platform.sourcePath}"/>
289
293
  <field name="platform_id" value="${platform.platformId}"/>
290
294
  <field name="platform_type" value="${platform.platformType}"/>
@@ -317,11 +321,15 @@ Continue with knowledge base generation?
317
321
  <sequence id="S1b" name="Stage 1b: Generate Feature Inventory" status="pending" desc="Generate Feature inventory for each platform">
318
322
 
319
323
  <block type="rule" id="S1b-R1" level="mandatory" desc="Stage 1b mandatory rules">
320
- <field name="text">This stage is executed DIRECTLY by the dispatch agent (Leader), NOT delegated to a Worker Agent</field>
324
+ <field name="text">ALL platform feature inventory generation tasks MUST be dispatched IN PARALLEL sequential execution is FORBIDDEN</field>
325
+ <field name="text">ALL Worker dispatch calls in S1b-L1 MUST be issued SIMULTANEOUSLY in a SINGLE orchestration turn</field>
326
+ <field name="text">DO NOT wait for any Worker to complete before dispatching the next Worker</field>
327
+ <field name="text">Dispatch all ${max_concurrent_workers} workers at once, then wait for ALL to complete</field>
328
+ <field name="text">Sequential one-by-one dispatch is STRICTLY FORBIDDEN</field>
321
329
  <field name="text">Worker Agents do not have run_in_terminal capability, which is required for script execution</field>
322
330
  </block>
323
331
 
324
- <block type="loop" id="S1b-L1" over="${platforms}" as="platform" desc="Generate Feature inventory for each platform">
332
+ <block type="loop" id="S1b-L1" over="${platforms}" as="platform" parallel="true" max-concurrency="${max_concurrent_workers}" desc="Generate Feature inventory for each platform IN PARALLEL">
325
333
  <!-- Step 1: Read platform mapping config -->
326
334
  <block type="task" id="S1b-B1" action="run-script" status="pending" desc="Read platform mapping config">
327
335
  <field name="command">node "${ide_skills_dir}/speccrew-knowledge-bizs-init-features/scripts/generate-inventory.js"</field>
@@ -422,6 +430,10 @@ Continue with knowledge base generation?
422
430
  <field name="text">MUST use batch-orchestrator for batch management — DO NOT manually track batches</field>
423
431
  <field name="text">MUST dispatch Workers for feature analysis — DO NOT analyze features yourself</field>
424
432
  <field name="text">ALL workers for the same stage MUST be dispatched in PARALLEL — sequential execution is FORBIDDEN</field>
433
+ <field name="text">ALL Worker dispatch calls in S2-L2 MUST be issued SIMULTANEOUSLY in a SINGLE orchestration turn</field>
434
+ <field name="text">DO NOT wait for any Worker to complete before dispatching the next Worker</field>
435
+ <field name="text">Dispatch all ${max_concurrent_workers} workers at once, then wait for ALL to complete</field>
436
+ <field name="text">Sequential one-by-one dispatch is STRICTLY FORBIDDEN</field>
425
437
  <field name="text">Monitor completion via marker files, NOT by polling worker status</field>
426
438
  </block>
427
439
 
@@ -470,6 +482,7 @@ Continue with knowledge base generation?
470
482
  </block>
471
483
 
472
484
  <!-- Step 2: Dispatch Worker for each Feature -->
485
+ <!-- PARALLEL EXECUTION MANDATORY: All Workers MUST be dispatched SIMULTANEOUSLY in ONE turn -->
473
486
  <block type="loop" id="S2-L2" over="${batch_response.batch}" as="feature" parallel="true" max-concurrency="${max_concurrent_workers}" desc="Dispatch analysis Worker for each Feature">
474
487
 
475
488
  <!-- Route to different Skill based on platformType -->
@@ -552,6 +565,7 @@ Requirements:
552
565
  </block>
553
566
 
554
567
  <!-- Step 2.5: Dispatch Graph Worker -->
568
+ <!-- PARALLEL EXECUTION MANDATORY: All Graph Workers MUST be dispatched SIMULTANEOUSLY in ONE turn -->
555
569
  <block type="loop" id="S2-L25" over="${batch_response.batch}" as="feature" parallel="true" max-concurrency="${max_concurrent_workers}" desc="Dispatch Graph Worker for each Feature IN PARALLEL">
556
570
  <block type="gateway" id="S2-G2" mode="exclusive" desc="Route Graph Worker based on analysis type">
557
571
  <branch test="${feature.platformType} == 'backend'" name="API Graph">
@@ -656,6 +670,11 @@ Requirements:
656
670
 
657
671
  <block type="rule" id="S3-R1" level="mandatory" desc="Stage 3 mandatory rules">
658
672
  <field name="text">Worker dispatch is handled by the calling Agent (Team Leader). This Skill only prepares the task plan and parameters.</field>
673
+ <field name="text">ALL module summary workers MUST be dispatched IN PARALLEL — sequential execution is FORBIDDEN</field>
674
+ <field name="text">ALL Worker dispatch calls in S3-L2 MUST be issued SIMULTANEOUSLY in a SINGLE orchestration turn</field>
675
+ <field name="text">DO NOT wait for any Worker to complete before dispatching the next Worker</field>
676
+ <field name="text">Dispatch all ${max_concurrent_workers} workers at once, then wait for ALL to complete</field>
677
+ <field name="text">Sequential one-by-one dispatch is STRICTLY FORBIDDEN</field>
659
678
  <field name="text">Workers MUST NOT create any temporary scripts or workaround files</field>
660
679
  </block>
661
680
 
@@ -666,7 +685,7 @@ Requirements:
666
685
  </block>
667
686
 
668
687
  <!-- Step 2: Prepare module summary tasks for each platform -->
669
- <block type="loop" id="S3-L1" over="${platforms}" as="platform" desc="Prepare module summary tasks for each platform">
688
+ <block type="loop" id="S3-L1" over="${platforms}" as="platform" parallel="true" max-concurrency="${max_concurrent_workers}" desc="Prepare module summary tasks for each platform IN PARALLEL">
670
689
  <!-- Step 2.1: Read platform features -->
671
690
  <block type="task" id="S3-B2" action="run-script" status="pending" desc="Read platform features">
672
691
  <field name="command">node -e "console.log(require('fs').readFileSync('${sync_state_bizs_dir}/features-${platform.platformId}.json', 'utf8'))"</field>
@@ -680,6 +699,7 @@ Requirements:
680
699
  </block>
681
700
 
682
701
  <!-- Step 2.3: Dispatch Worker for each module -->
702
+ <!-- PARALLEL EXECUTION MANDATORY: All Module Summary Workers MUST be dispatched SIMULTANEOUSLY in ONE turn -->
683
703
  <block type="loop" id="S3-L2" over="${platform_modules}" as="module_name" parallel="true" max-concurrency="${max_concurrent_workers}" desc="Dispatch summary Worker for each module">
684
704
  <block type="task" id="S3-B4" action="dispatch-to-worker" status="pending" desc="Dispatch module summary Worker">
685
705
  <field name="worker">speccrew-knowledge-module-summarize-xml</field>
@@ -725,10 +745,15 @@ Requirements:
725
745
  <block type="rule" id="S35-R1" level="mandatory" desc="Stage 3.5 mandatory rules">
726
746
  <field name="text">Worker dispatch is handled by the calling Agent (Team Leader). This Skill only prepares the task plan and parameters.</field>
727
747
  <field name="text">ALL UI style extraction workers MUST be dispatched IN PARALLEL — sequential execution is FORBIDDEN</field>
748
+ <field name="text">ALL Worker dispatch calls in S35-L1 MUST be issued SIMULTANEOUSLY in a SINGLE orchestration turn</field>
749
+ <field name="text">DO NOT wait for any Worker to complete before dispatching the next Worker</field>
750
+ <field name="text">Dispatch all ${max_concurrent_workers} workers at once, then wait for ALL to complete</field>
751
+ <field name="text">Sequential one-by-one dispatch is STRICTLY FORBIDDEN</field>
728
752
  <field name="text">This stage writes to techs knowledge base, not bizs knowledge base</field>
729
753
  </block>
730
754
 
731
755
  <!-- Dispatch UI Style Extract Worker for each frontend platform -->
756
+ <!-- PARALLEL EXECUTION MANDATORY: All UI Style Workers MUST be dispatched SIMULTANEOUSLY in ONE turn -->
732
757
  <block type="loop" id="S35-L1" over="${platforms}" as="platform" parallel="true" max-concurrency="${max_concurrent_workers}" desc="Dispatch UI style extraction Workers for frontend platforms IN PARALLEL">
733
758
  <block type="gateway" id="S35-G1" mode="exclusive" desc="Execute for UI platforms only">
734
759
  <branch test="${platform.platformType} in ['web', 'mobile', 'desktop']" name="UI platform">
@@ -63,6 +63,14 @@ For each platform, generates:
63
63
  <field name="text">Do not include leading or trailing slashes in entryDirs paths</field>
64
64
  </block>
65
65
 
66
+ <block type="rule" id="R-TECHSTACK" level="mandatory" desc="techStack values MUST match tech-stack-mappings.json keys">
67
+ <field name="text">
68
+ The techStack array values MUST exactly match keys defined in tech-stack-mappings.json (e.g., "fastapi", "vue3", "uniapp").
69
+ DO NOT prefix with language name (e.g., use "fastapi" NOT "python-fastapi", use "express" NOT "node-express").
70
+ The tech_identifier input parameter value should be used as the primary techStack entry.
71
+ </field>
72
+ </block>
73
+
66
74
  <!-- ============================================================
67
75
  Global Continuous Execution Rules
68
76
  ============================================================ -->
@@ -110,6 +110,21 @@ function parseArgs() {
110
110
  return params;
111
111
  }
112
112
 
113
+ /**
114
+ * Parse boolean parameter from string value
115
+ * @param {string|boolean} value - Parameter value
116
+ * @param {boolean} defaultValue - Default value if not provided
117
+ * @returns {boolean} Parsed boolean value
118
+ */
119
+ function parseBooleanParam(value, defaultValue = false) {
120
+ if (value === undefined || value === null) return defaultValue;
121
+ if (typeof value === 'boolean') return value;
122
+ if (typeof value === 'string') {
123
+ return value.toLowerCase() === 'true';
124
+ }
125
+ return defaultValue;
126
+ }
127
+
113
128
  // Normalize path separators to forward slashes
114
129
  function normalizePath(filePath) {
115
130
  if (!filePath) return '';
@@ -215,6 +230,23 @@ function loadPlatformConfig(platformId, projectRoot) {
215
230
  return platformConfig;
216
231
  }
217
232
 
233
+ /**
234
+ * Normalize tech identifier by removing common language prefixes.
235
+ * @param {string} id - Tech identifier like "python-fastapi", "node-express"
236
+ * @returns {string} Normalized identifier like "fastapi", "express"
237
+ */
238
+ function normalizeTechIdentifier(id) {
239
+ if (!id) return id;
240
+ // Remove common language prefixes: python-, node-, java-, etc.
241
+ const prefixes = ['python-', 'node-', 'java-', 'kotlin-', 'swift-', 'dart-', 'go-', 'rust-', 'php-', 'ruby-'];
242
+ for (const prefix of prefixes) {
243
+ if (id.toLowerCase().startsWith(prefix)) {
244
+ return id.substring(prefix.length);
245
+ }
246
+ }
247
+ return id;
248
+ }
249
+
218
250
  /**
219
251
  * Load tech stack configuration from tech-stack-mappings.json
220
252
  * @param {string} platformType - Platform type like "backend", "web"
@@ -228,21 +260,38 @@ function loadTechStackConfig(platformType, framework, projectRoot) {
228
260
  console.error(`Warning: tech-stack-mappings.json not found at ${configPath}`);
229
261
  return { extensions: [], exclude_file_suffixes: [] };
230
262
  }
231
-
263
+
232
264
  const configContent = fs.readFileSync(configPath, 'utf8');
233
265
  const config = JSON.parse(configContent);
234
-
235
- if (config.tech_stacks &&
236
- config.tech_stacks[platformType] &&
266
+
267
+ // Try exact match first
268
+ let techConfig = null;
269
+ if (config.tech_stacks &&
270
+ config.tech_stacks[platformType] &&
237
271
  config.tech_stacks[platformType][framework]) {
238
- const techConfig = config.tech_stacks[platformType][framework];
272
+ techConfig = config.tech_stacks[platformType][framework];
273
+ }
274
+
275
+ // If not found, try normalized identifier (remove language prefix)
276
+ if (!techConfig) {
277
+ const normalizedFramework = normalizeTechIdentifier(framework);
278
+ if (normalizedFramework !== framework &&
279
+ config.tech_stacks &&
280
+ config.tech_stacks[platformType] &&
281
+ config.tech_stacks[platformType][normalizedFramework]) {
282
+ techConfig = config.tech_stacks[platformType][normalizedFramework];
283
+ console.log(`Using normalized tech identifier: ${framework} → ${normalizedFramework}`);
284
+ }
285
+ }
286
+
287
+ if (techConfig) {
239
288
  return {
240
289
  extensions: techConfig.extensions || [],
241
290
  exclude_file_suffixes: techConfig.exclude_file_suffixes || [],
242
291
  exclude_file_names: techConfig.exclude_file_names || []
243
292
  };
244
293
  }
245
-
294
+
246
295
  return { extensions: [], exclude_file_suffixes: [], exclude_file_names: [] };
247
296
  }
248
297
 
@@ -389,7 +438,7 @@ function findFilesInDir(dir, extensions, baseDir) {
389
438
  * @param {string} outputDir - Output directory for features JSON
390
439
  * @returns {boolean} Success status
391
440
  */
392
- function generateFromEntryDirs(entryDirsData, platformConfig, projectRoot, outputDir) {
441
+ function generateFromEntryDirs(entryDirsData, platformConfig, projectRoot, outputDir, overwrite) {
393
442
  const { platformId, sourcePath, modules } = entryDirsData;
394
443
  const { platformType, platformSubtype, framework } = platformConfig;
395
444
 
@@ -506,8 +555,8 @@ function generateFromEntryDirs(entryDirsData, platformConfig, projectRoot, outpu
506
555
  const outputFileName = `features-${platformId}.json`;
507
556
  const outputPath = path.join(outputDir, outputFileName);
508
557
 
509
- // Incremental: if features file already exists, write to *.new.json
510
- const actualOutputPath = fs.existsSync(outputPath)
558
+ // Incremental: if features file already exists and overwrite is not set, write to *.new.json
559
+ const actualOutputPath = (!overwrite && fs.existsSync(outputPath))
511
560
  ? outputPath.replace(/\.json$/, '.new.json')
512
561
  : outputPath;
513
562
 
@@ -566,6 +615,9 @@ function findFiles(dir, extensions, excludeDirs, baseDir) {
566
615
  function main() {
567
616
  const params = parseArgs();
568
617
 
618
+ // Parse overwrite parameter (default: false for backward compatibility)
619
+ const overwrite = parseBooleanParam(params.overwrite, false);
620
+
569
621
  // Check for whitelist mode (--entryDirsFile provided)
570
622
  if (params.entryDirsFile) {
571
623
  // Whitelist mode: scan only specified entry directories
@@ -654,7 +706,7 @@ function main() {
654
706
  }
655
707
 
656
708
  // Generate features from entry dirs
657
- const success = generateFromEntryDirs(entryDirsData, platformConfig, projectRoot, outputDir);
709
+ const success = generateFromEntryDirs(entryDirsData, platformConfig, projectRoot, outputDir, overwrite);
658
710
  process.exit(success ? 0 : 1);
659
711
  }
660
712
 
@@ -696,38 +748,53 @@ function main() {
696
748
 
697
749
  // Load tech-stack-specific exclude_dirs
698
750
  let techExcludeDirs = [];
699
- if (config.tech_stacks &&
700
- config.tech_stacks[platformType] &&
751
+ let effectiveTechIdentifier = techIdentifier;
752
+
753
+ // Try exact match first
754
+ if (config.tech_stacks &&
755
+ config.tech_stacks[platformType] &&
701
756
  config.tech_stacks[platformType][techIdentifier] &&
702
757
  config.tech_stacks[platformType][techIdentifier].exclude_dirs) {
703
758
  techExcludeDirs = config.tech_stacks[platformType][techIdentifier].exclude_dirs;
759
+ } else {
760
+ // Try normalized identifier (remove language prefix like "python-fastapi" → "fastapi")
761
+ const normalizedIdentifier = normalizeTechIdentifier(techIdentifier);
762
+ if (normalizedIdentifier !== techIdentifier &&
763
+ config.tech_stacks &&
764
+ config.tech_stacks[platformType] &&
765
+ config.tech_stacks[platformType][normalizedIdentifier] &&
766
+ config.tech_stacks[platformType][normalizedIdentifier].exclude_dirs) {
767
+ techExcludeDirs = config.tech_stacks[platformType][normalizedIdentifier].exclude_dirs;
768
+ effectiveTechIdentifier = normalizedIdentifier;
769
+ console.log(`Using normalized tech identifier for exclude_dirs: ${techIdentifier} → ${normalizedIdentifier}`);
770
+ }
704
771
  }
705
-
772
+
706
773
  // Load global exclude_dirs (applies to all platforms)
707
774
  const globalExcludeDirs = config.global_exclude_dirs || [];
708
-
775
+
709
776
  // Merge: global + tech-specific
710
777
  const mergedDirs = [...new Set([...globalExcludeDirs, ...techExcludeDirs])];
711
778
  excludeDirsStr = JSON.stringify(mergedDirs);
712
779
  console.log(`Loaded exclude_dirs from tech-stack-mappings.json (${globalExcludeDirs.length} global + ${techExcludeDirs.length} tech-specific = ${mergedDirs.length} total)`);
713
-
780
+
714
781
  // Load tech-stack-specific exclude_file_suffixes
715
- if (config.tech_stacks &&
716
- config.tech_stacks[platformType] &&
717
- config.tech_stacks[platformType][techIdentifier] &&
718
- config.tech_stacks[platformType][techIdentifier].exclude_file_suffixes) {
719
- excludeFileSuffixes = config.tech_stacks[platformType][techIdentifier].exclude_file_suffixes;
782
+ if (config.tech_stacks &&
783
+ config.tech_stacks[platformType] &&
784
+ config.tech_stacks[platformType][effectiveTechIdentifier] &&
785
+ config.tech_stacks[platformType][effectiveTechIdentifier].exclude_file_suffixes) {
786
+ excludeFileSuffixes = config.tech_stacks[platformType][effectiveTechIdentifier].exclude_file_suffixes;
720
787
  if (excludeFileSuffixes.length > 0) {
721
788
  console.log(`Loaded exclude_file_suffixes from tech-stack-mappings.json: ${excludeFileSuffixes.join(', ')}`);
722
789
  }
723
790
  }
724
-
791
+
725
792
  // Load tech-stack-specific exclude_file_names
726
- if (config.tech_stacks &&
727
- config.tech_stacks[platformType] &&
728
- config.tech_stacks[platformType][techIdentifier] &&
729
- config.tech_stacks[platformType][techIdentifier].exclude_file_names) {
730
- excludeFileNames = config.tech_stacks[platformType][techIdentifier].exclude_file_names;
793
+ if (config.tech_stacks &&
794
+ config.tech_stacks[platformType] &&
795
+ config.tech_stacks[platformType][effectiveTechIdentifier] &&
796
+ config.tech_stacks[platformType][effectiveTechIdentifier].exclude_file_names) {
797
+ excludeFileNames = config.tech_stacks[platformType][effectiveTechIdentifier].exclude_file_names;
731
798
  if (excludeFileNames.length > 0) {
732
799
  console.log(`Loaded exclude_file_names from tech-stack-mappings.json: ${excludeFileNames.join(', ')}`);
733
800
  }
@@ -924,8 +991,8 @@ function main() {
924
991
  fs.mkdirSync(syncStateDir, { recursive: true });
925
992
  }
926
993
 
927
- // Incremental: if features file already exists, write to *.new.json
928
- const actualOutputPath = fs.existsSync(outputPath)
994
+ // Incremental: if features file already exists and overwrite is not set, write to *.new.json
995
+ const actualOutputPath = (!overwrite && fs.existsSync(outputPath))
929
996
  ? outputPath.replace(/\.json$/, '.new.json')
930
997
  : outputPath;
931
998
 
@@ -285,6 +285,21 @@ Analyze one specific UI feature from source code, extract business functionality
285
285
 
286
286
  <!-- ==================== STEP 5A: COPY TEMPLATE TO DOCUMENT PATH ==================== -->
287
287
  <sequence id="S5a" name="Step 5a: Copy Template" status="pending" desc="Copy template to document path">
288
+ <!-- Document Output Path Rule -->
289
+ <block type="rule" id="R-DOCPATH" level="mandatory" desc="Document output path MUST use documentPath parameter">
290
+ <field name="text">
291
+ The output document file MUST be created at the EXACT path specified by ${documentPath} input parameter.
292
+ DO NOT use the template file name (e.g., FEATURE-DETAIL-TEMPLATE-*.md) as the output file name.
293
+ The documentPath already contains the correct target path including file name (e.g., speccrew-workspace/knowledges/bizs/web-vue/src/views/system/user/index.md).
294
+ Before creating the file, ensure the parent directory exists (create if necessary).
295
+ </field>
296
+ </block>
297
+
298
+ <!-- Ensure Document Output Directory Exists -->
299
+ <block type="task" id="B5a0" action="run-script" desc="Ensure document output directory exists">
300
+ <field name="command">node -e "require('fs').mkdirSync(require('path').dirname('${documentPath}'), {recursive: true})"</field>
301
+ </block>
302
+
288
303
  <!-- Prepare Placeholder Replacements -->
289
304
  <block type="task" id="B5a1" action="analyze" desc="Prepare replacements">
290
305
  <field name="language" value="${language}"/>
@@ -264,12 +264,17 @@ Output: ${sync_state_techs_dir}/techs-manifest.json</field>
264
264
  <field name="text">DO NOT finish one platform before starting the next — this wastes time and violates the pipeline design</field>
265
265
  <field name="text">The calling Agent MUST use concurrent task dispatch (e.g., dispatch 3 workers in one turn for 3 platforms)</field>
266
266
  <field name="text">Sequential platform-by-platform execution is FORBIDDEN</field>
267
+ <field name="text">Loops S2-L1, S2-L2, S2-L3, and S2-L4 have parallel="true" and MUST execute concurrently for ALL platforms</field>
268
+ <field name="text">ALL platform status updates (S2-L1) MUST be done SIMULTANEOUSLY in PARALLEL</field>
269
+ <field name="text">ALL conventions worker task preparations (S2-L2) MUST be done SIMULTANEOUSLY in PARALLEL</field>
270
+ <field name="text">ALL UI-style worker task preparations (S2-L3) MUST be done SIMULTANEOUSLY in PARALLEL</field>
267
271
  </block>
268
272
 
269
273
  <block type="rule" id="S2-R2" level="forbidden" desc="Stage 2 forbidden actions">
270
274
  <field name="text">DO NOT process platforms sequentially — PARALLEL execution is MANDATORY</field>
271
275
  <field name="text">DO NOT wait for one platform to complete before starting another</field>
272
276
  <field name="text">DO NOT proceed to Stage 2.5 until ALL workers have completed or failed</field>
277
+ <field name="text">Sequential execution of S2-L1, S2-L2, S2-L3, or S2-L4 is STRICTLY FORBIDDEN</field>
273
278
  </block>
274
279
 
275
280
  <!-- Step 0: Ensure completed_dir exists -->
@@ -278,7 +283,7 @@ Output: ${sync_state_techs_dir}/techs-manifest.json</field>
278
283
  </block>
279
284
 
280
285
  <!-- Step 1: Update manifest status to processing -->
281
- <block type="loop" id="S2-L1" over="${techs_manifest.platforms}" as="platform" desc="Update each platform status to processing">
286
+ <block type="loop" id="S2-L1" over="${techs_manifest.platforms}" as="platform" parallel="true" max-concurrency="${techs_manifest.platforms.length}" desc="Update each platform status to processing">
282
287
  <block type="task" id="S2-B1a" action="run-script" status="pending" desc="Update platform status in manifest">
283
288
  <field name="command">node -e "
284
289
  const fs = require('fs');
@@ -304,7 +309,7 @@ if (platform) {
304
309
  <field name="message">Preparing conventions worker task plans for ${techs_manifest.platforms.length} platforms...</field>
305
310
  </block>
306
311
 
307
- <block type="loop" id="S2-L2" over="${techs_manifest.platforms}" as="platform" desc="Prepare conventions worker task for each platform">
312
+ <block type="loop" id="S2-L2" over="${techs_manifest.platforms}" as="platform" parallel="true" max-concurrency="${techs_manifest.platforms.length}" desc="Prepare conventions worker task for each platform">
308
313
  <block type="task" id="S2-B2" action="prepare-task" status="pending" desc="Prepare conventions worker task specification">
309
314
  <field name="skill_name">speccrew-knowledge-techs-generate-conventions-xml</field>
310
315
  <field name="context">
@@ -327,7 +332,7 @@ if (platform) {
327
332
  <field name="message">Preparing UI-style worker task plans for frontend platforms...</field>
328
333
  </block>
329
334
 
330
- <block type="loop" id="S2-L3" over="${techs_manifest.platforms}" as="platform" desc="Prepare UI-style worker task for frontend platforms">
335
+ <block type="loop" id="S2-L3" over="${techs_manifest.platforms}" as="platform" parallel="true" max-concurrency="${techs_manifest.platforms.length}" desc="Prepare UI-style worker task for frontend platforms">
331
336
  <block type="gateway" id="S2-G1" mode="guard" test="${platform.platform_type} IN ['web', 'mobile', 'desktop']" fail-action="skip" desc="Only for frontend platforms">
332
337
  <field name="message">Skipping UI-style for backend platform: ${platform.platform_id}</field>
333
338
  </block>
@@ -352,6 +357,12 @@ if (platform) {
352
357
  </block>
353
358
 
354
359
  <!-- Step 5: Monitor Completion Markers -->
360
+ <block type="rule" id="S2-R3" level="mandatory" desc="Platform monitoring parallel execution rule">
361
+ <field name="text">ALL platform monitoring tasks MUST be dispatched SIMULTANEOUSLY in a SINGLE orchestration turn</field>
362
+ <field name="text">DO NOT process platforms one-by-one. Sequential execution is FORBIDDEN</field>
363
+ <field name="text">ALL completion marker checks for ALL platforms MUST run in PARALLEL</field>
364
+ </block>
365
+
355
366
  <block type="loop" id="S2-L4" over="${techs_manifest.platforms}" as="platform" parallel="true" max-concurrency="${techs_manifest.platforms.length}" desc="Monitor completion markers for each platform">
356
367
 
357
368
  <!-- Check conventions worker marker -->
@@ -440,6 +451,9 @@ if (platform) {
440
451
  <field name="text">MUST scan ALL completion markers before proceeding</field>
441
452
  <field name="text">MUST verify document existence and completeness</field>
442
453
  <field name="text">MUST update stage2-status.json with verification results</field>
454
+ <field name="text">ALL platform scans MUST be executed SIMULTANEOUSLY in PARALLEL — sequential scanning is FORBIDDEN</field>
455
+ <field name="text">ALL platform integrity checks MUST be executed SIMULTANEOUSLY in PARALLEL — sequential verification is FORBIDDEN</field>
456
+ <field name="text">Stage 2.5 loops S2_5-L1 and S2_5-L2 have parallel="true" and MUST execute concurrently</field>
443
457
  </block>
444
458
 
445
459
  <!-- Step A: Scan Completion Markers -->
@@ -447,7 +461,7 @@ if (platform) {
447
461
  <field name="message">Stage 2.5 Step A: Scanning completion markers...</field>
448
462
  </block>
449
463
 
450
- <block type="loop" id="S2_5-L1" over="${techs_manifest.platforms}" as="platform" desc="Scan markers for each platform">
464
+ <block type="loop" id="S2_5-L1" over="${techs_manifest.platforms}" as="platform" parallel="true" max-concurrency="${techs_manifest.platforms.length}" desc="Scan markers for each platform">
451
465
  <block type="task" id="S2_5-B1" action="run-script" status="pending" desc="Check all markers for platform">
452
466
  <field name="command">node -e "
453
467
  const fs = require('fs');
@@ -473,7 +487,7 @@ console.log(JSON.stringify(markers));
473
487
  <field name="message">Stage 2.5 Step B: Verifying output integrity...</field>
474
488
  </block>
475
489
 
476
- <block type="loop" id="S2_5-L2" over="${techs_manifest.platforms}" as="platform" desc="Verify documents for each platform">
490
+ <block type="loop" id="S2_5-L2" over="${techs_manifest.platforms}" as="platform" parallel="true" max-concurrency="${techs_manifest.platforms.length}" desc="Verify documents for each platform">
477
491
  <block type="task" id="S2_5-B2" action="run-script" status="pending" desc="Check required documents exist">
478
492
  <field name="command">node -e "
479
493
  const fs = require('fs');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "speccrew",
3
- "version": "0.6.56",
3
+ "version": "0.6.59",
4
4
  "description": "Spec-Driven Development toolkit for AI-powered IDEs",
5
5
  "author": "charlesmu99",
6
6
  "repository": {
@@ -481,3 +481,20 @@ Team Leader executes:
481
481
  6. **`loop parallel="true"`** = dispatch all iterations concurrently
482
482
  7. **`rule level="forbidden"`** = immediate stop if violated
483
483
  8. **Input/Output blocks** define the contract — respect required parameters
484
+
485
+ ### Block Execution Announcement Protocol
486
+
487
+ Before executing each `<block>`, the agent MUST announce the block being executed. The announcement format is:
488
+
489
+ ```
490
+ 📋 Block [{block-id}] (type={block-type}, action={action}) — {block-desc}
491
+ ```
492
+
493
+ **Rules:**
494
+ 1. The announcement MUST be made BEFORE the block execution begins, not after.
495
+ 2. ALL block types require announcement: `task`, `loop`, `checkpoint`, `rule`, `gateway`, `input`, `output`.
496
+ 3. For `<block type="loop">` blocks, announce the loop entry with iteration context (e.g., "Loop [S2-L2] — Iterating over ${batch}, 5 items, parallel=true").
497
+ 4. For `<block type="checkpoint">` blocks, announce the checkpoint and its validation result.
498
+ 5. For `<block type="rule">` blocks, announce the rule being applied.
499
+ 6. This protocol applies to ALL agents executing XML workflows — both orchestrating agents (Team Leader) and worker agents.
500
+ 7. Nested blocks inside loops should also be announced for each iteration.