forge-workflow 0.0.2 → 0.0.3

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.
Files changed (147) hide show
  1. package/.claude/commands/plan.md +93 -4
  2. package/.cline/workflows/dev.md +311 -0
  3. package/.cline/workflows/plan.md +475 -0
  4. package/.cline/workflows/premerge.md +176 -0
  5. package/.cline/workflows/research.md +39 -0
  6. package/.cline/workflows/review.md +439 -0
  7. package/.cline/workflows/rollback.md +718 -0
  8. package/.cline/workflows/ship.md +131 -0
  9. package/.cline/workflows/sonarcloud.md +146 -0
  10. package/.cline/workflows/status.md +74 -0
  11. package/.cline/workflows/validate.md +234 -0
  12. package/.cline/workflows/verify.md +218 -0
  13. package/.codex/config.toml +11 -0
  14. package/.codex/skills/dev/SKILL.md +314 -0
  15. package/.codex/skills/plan/SKILL.md +478 -0
  16. package/.codex/skills/premerge/SKILL.md +179 -0
  17. package/.codex/skills/research/SKILL.md +42 -0
  18. package/.codex/skills/review/SKILL.md +442 -0
  19. package/.codex/skills/rollback/SKILL.md +721 -0
  20. package/.codex/skills/ship/SKILL.md +134 -0
  21. package/.codex/skills/sonarcloud/SKILL.md +149 -0
  22. package/.codex/skills/status/SKILL.md +77 -0
  23. package/.codex/skills/validate/SKILL.md +237 -0
  24. package/.codex/skills/verify/SKILL.md +221 -0
  25. package/.cursor/commands/dev.md +311 -0
  26. package/.cursor/commands/plan.md +475 -0
  27. package/.cursor/commands/premerge.md +176 -0
  28. package/.cursor/commands/research.md +39 -0
  29. package/.cursor/commands/review.md +439 -0
  30. package/.cursor/commands/rollback.md +718 -0
  31. package/.cursor/commands/ship.md +131 -0
  32. package/.cursor/commands/sonarcloud.md +146 -0
  33. package/.cursor/commands/status.md +74 -0
  34. package/.cursor/commands/validate.md +234 -0
  35. package/.cursor/commands/verify.md +218 -0
  36. package/.cursor/rules/permissions-guidance.mdc +37 -0
  37. package/.github/prompts/dev.prompt.md +316 -0
  38. package/.github/prompts/plan.prompt.md +480 -0
  39. package/.github/prompts/premerge.prompt.md +181 -0
  40. package/.github/prompts/research.prompt.md +44 -0
  41. package/.github/prompts/review.prompt.md +444 -0
  42. package/.github/prompts/rollback.prompt.md +723 -0
  43. package/.github/prompts/ship.prompt.md +136 -0
  44. package/.github/prompts/sonarcloud.prompt.md +151 -0
  45. package/.github/prompts/status.prompt.md +79 -0
  46. package/.github/prompts/validate.prompt.md +239 -0
  47. package/.github/prompts/verify.prompt.md +223 -0
  48. package/.kilocode/workflows/dev.md +315 -0
  49. package/.kilocode/workflows/plan.md +479 -0
  50. package/.kilocode/workflows/premerge.md +180 -0
  51. package/.kilocode/workflows/research.md +43 -0
  52. package/.kilocode/workflows/review.md +443 -0
  53. package/.kilocode/workflows/rollback.md +722 -0
  54. package/.kilocode/workflows/ship.md +135 -0
  55. package/.kilocode/workflows/sonarcloud.md +150 -0
  56. package/.kilocode/workflows/status.md +78 -0
  57. package/.kilocode/workflows/validate.md +238 -0
  58. package/.kilocode/workflows/verify.md +222 -0
  59. package/.opencode/commands/dev.md +314 -0
  60. package/.opencode/commands/plan.md +478 -0
  61. package/.opencode/commands/premerge.md +179 -0
  62. package/.opencode/commands/research.md +42 -0
  63. package/.opencode/commands/review.md +442 -0
  64. package/.opencode/commands/rollback.md +721 -0
  65. package/.opencode/commands/ship.md +134 -0
  66. package/.opencode/commands/sonarcloud.md +149 -0
  67. package/.opencode/commands/status.md +77 -0
  68. package/.opencode/commands/validate.md +237 -0
  69. package/.opencode/commands/verify.md +221 -0
  70. package/.roo/commands/dev.md +315 -0
  71. package/.roo/commands/plan.md +479 -0
  72. package/.roo/commands/premerge.md +180 -0
  73. package/.roo/commands/research.md +43 -0
  74. package/.roo/commands/review.md +443 -0
  75. package/.roo/commands/rollback.md +722 -0
  76. package/.roo/commands/ship.md +135 -0
  77. package/.roo/commands/sonarcloud.md +150 -0
  78. package/.roo/commands/status.md +78 -0
  79. package/.roo/commands/validate.md +238 -0
  80. package/.roo/commands/verify.md +222 -0
  81. package/LICENSE +21 -21
  82. package/docs/ENHANCED_ONBOARDING.md +2 -2
  83. package/docs/TOOLCHAIN.md +15 -234
  84. package/install.sh +32 -36
  85. package/lib/commands/plan.js +11 -15
  86. package/lib/commands/recommend.js +2 -2
  87. package/lib/dep-guard/analyzer.js +294 -0
  88. package/lib/dep-guard/behavior-detector.js +98 -0
  89. package/lib/dep-guard/contract-detector.js +162 -0
  90. package/lib/dep-guard/import-detector.js +498 -0
  91. package/lib/dep-guard/path-utils.js +13 -0
  92. package/lib/dep-guard/rubric.js +120 -0
  93. package/lib/dep-guard/task-parser.js +318 -0
  94. package/lib/plugin-catalog.js +18 -28
  95. package/lib/workflow-profiles.js +5 -11
  96. package/package.json +15 -4
  97. package/skills/parallel-deep-research/SKILL.md +108 -0
  98. package/skills/parallel-deep-research/evals/README.md +27 -0
  99. package/skills/parallel-deep-research/evals/evals.json +62 -0
  100. package/skills/sonarcloud-analysis/SKILL.md +171 -0
  101. package/skills/sonarcloud-analysis/evals/README.md +27 -0
  102. package/skills/sonarcloud-analysis/evals/evals.json +50 -0
  103. package/skills/sonarcloud-analysis/references/api-reference.md +466 -0
  104. package/docs/planning/PROGRESS.md +0 -396
  105. package/docs/plans/.gitkeep +0 -0
  106. package/docs/plans/2026-02-27-forge-test-suite-v2-decisions.md +0 -21
  107. package/docs/plans/2026-02-27-forge-test-suite-v2-design.md +0 -362
  108. package/docs/plans/2026-02-27-forge-test-suite-v2-tasks.md +0 -343
  109. package/docs/plans/2026-03-02-superpowers-gaps-decisions.md +0 -26
  110. package/docs/plans/2026-03-02-superpowers-gaps-design.md +0 -239
  111. package/docs/plans/2026-03-02-superpowers-gaps-tasks.md +0 -260
  112. package/docs/plans/2026-03-04-agent-command-parity-design.md +0 -163
  113. package/docs/plans/2026-03-04-verify-worktree-cleanup-decisions.md +0 -7
  114. package/docs/plans/2026-03-04-verify-worktree-cleanup-design.md +0 -165
  115. package/docs/plans/2026-03-05-forge-uto-decisions.md +0 -6
  116. package/docs/plans/2026-03-05-forge-uto-design.md +0 -116
  117. package/docs/plans/2026-03-05-forge-uto-tasks.md +0 -244
  118. package/docs/plans/2026-03-10-command-creator-and-eval-decisions.md +0 -52
  119. package/docs/plans/2026-03-10-command-creator-and-eval-design.md +0 -350
  120. package/docs/plans/2026-03-10-command-creator-and-eval-tasks.md +0 -426
  121. package/docs/plans/2026-03-10-stale-workflow-refs-decisions.md +0 -8
  122. package/docs/plans/2026-03-10-stale-workflow-refs-design.md +0 -80
  123. package/docs/plans/2026-03-10-stale-workflow-refs-tasks.md +0 -90
  124. package/docs/plans/2026-03-14-beads-plan-context-decisions.md +0 -9
  125. package/docs/plans/2026-03-14-beads-plan-context-design.md +0 -171
  126. package/docs/plans/2026-03-14-beads-plan-context-tasks.md +0 -160
  127. package/docs/plans/2026-03-14-skill-eval-loop-decisions.md +0 -33
  128. package/docs/plans/2026-03-14-skill-eval-loop-design.md +0 -118
  129. package/docs/plans/2026-03-14-skill-eval-loop-results.md +0 -78
  130. package/docs/plans/2026-03-14-skill-eval-loop-tasks.md +0 -160
  131. package/docs/plans/2026-03-15-agent-command-parity-v2-decisions.md +0 -11
  132. package/docs/plans/2026-03-15-agent-command-parity-v2-design.md +0 -145
  133. package/docs/plans/2026-03-15-agent-command-parity-v2-tasks.md +0 -211
  134. package/docs/research/TEMPLATE.md +0 -292
  135. package/docs/research/advanced-testing.md +0 -297
  136. package/docs/research/agent-permissions.md +0 -167
  137. package/docs/research/dependency-chain.md +0 -328
  138. package/docs/research/forge-workflow-v2.md +0 -550
  139. package/docs/research/plugin-architecture.md +0 -772
  140. package/docs/research/pr4-cli-automation.md +0 -326
  141. package/docs/research/premerge-verify-restructure.md +0 -205
  142. package/docs/research/skills-restructure.md +0 -508
  143. package/docs/research/sonarcloud-perfection-plan.md +0 -166
  144. package/docs/research/sonarcloud-quality-gate.md +0 -184
  145. package/docs/research/superpowers-integration.md +0 -403
  146. package/docs/research/superpowers.md +0 -319
  147. package/docs/research/test-environment.md +0 -519
package/install.sh CHANGED
@@ -342,7 +342,7 @@ create_link() {
342
342
  # Universal SKILL.md content
343
343
  SKILL_CONTENT='---
344
344
  name: forge-workflow
345
- description: 9-stage TDD-first workflow for feature development. Use when building features, fixing bugs, or shipping PRs.
345
+ description: 7-stage TDD-first workflow for feature development. Use when building features, fixing bugs, or shipping PRs.
346
346
  category: Development Workflow
347
347
  tags: [tdd, workflow, pr, git, testing]
348
348
  tools: [Bash, Read, Write, Edit, Grep, Glob]
@@ -360,24 +360,23 @@ Automatically invoke this skill when the user wants to:
360
360
  - Create a pull request
361
361
  - Run the development workflow
362
362
 
363
- ## 9 Stages
363
+ ## 7 Stages
364
364
 
365
365
  | Stage | Command | Description |
366
366
  |-------|---------|-------------|
367
- | 1 | `/status` | Check current context, active work, recent completions |
368
- | 2 | `/research` | Deep research with web search, document to docs/research/ |
369
- | 3 | `/plan` | Create implementation plan, branch, OpenSpec if strategic |
370
- | 4 | `/dev` | TDD development (RED-GREEN-REFACTOR cycles) |
371
- | 5 | `/check` | Validation (type/lint/security/tests) |
372
- | 6 | `/ship` | Create PR with full documentation |
373
- | 7 | `/review` | Address ALL PR feedback |
374
- | 8 | `/merge` | Update docs, merge PR, cleanup |
375
- | 9 | `/verify` | Final documentation verification |
367
+ | utility | `/status` | Check current context, active work, recent completions |
368
+ | 1 | `/plan` | Design intent -> research -> branch + worktree + task list |
369
+ | 2 | `/dev` | TDD development (implementer -> spec review -> quality review) |
370
+ | 3 | `/validate` | Type check, lint, security, tests - all fresh output |
371
+ | 4 | `/ship` | Push branch and create PR with full documentation |
372
+ | 5 | `/review` | Address ALL PR feedback (GitHub Actions, Greptile, SonarCloud) |
373
+ | 6 | `/premerge` | Update docs, hand off PR to user |
374
+ | 7 | `/verify` | Post-merge health check (CI on main, close Beads) |
376
375
 
377
376
  ## Workflow Flow
378
377
 
379
378
  ```
380
- /status -> /research -> /plan -> /dev -> /check -> /ship -> /review -> /merge -> /verify
379
+ /status -> /plan -> /dev -> /validate -> /ship -> /review -> /premerge -> /verify
381
380
  ```
382
381
 
383
382
  ## Core Principles
@@ -398,10 +397,10 @@ if [ "$INSTALL_CLAUDE" = true ]; then
398
397
  mkdir -p .claude/commands .claude/rules .claude/skills/forge-workflow .claude/scripts
399
398
 
400
399
  # Download commands
401
- for cmd in status research plan dev check ship review merge verify; do
400
+ for cmd in status plan dev validate ship review premerge verify research rollback sonarcloud; do
402
401
  curl -fsSL "$BASE_URL/.claude/commands/$cmd.md" -o ".claude/commands/$cmd.md" 2>/dev/null || true
403
402
  done
404
- echo -e " ${GREEN}Downloaded: 9 workflow commands${NC}"
403
+ echo -e " ${GREEN}Downloaded: 11 workflow commands${NC}"
405
404
 
406
405
  # Download rules
407
406
  curl -fsSL "$BASE_URL/.claude/rules/workflow.md" -o ".claude/rules/workflow.md" 2>/dev/null || true
@@ -458,14 +457,13 @@ alwaysApply: true
458
457
  Use these commands via `/command-name`:
459
458
 
460
459
  1. `/status` - Check current context, active work, recent completions
461
- 2. `/research` - Deep research with web search, document to docs/research/
462
- 3. `/plan` - Create implementation plan, branch, tracking
463
- 4. `/dev` - TDD development (RED-GREEN-REFACTOR cycles)
464
- 5. `/check` - Validation (type/lint/security/tests)
465
- 6. `/ship` - Create PR with full documentation
466
- 7. `/review` - Address ALL PR feedback
467
- 8. `/merge` - Update docs, merge PR, cleanup
468
- 9. `/verify` - Final documentation verification
460
+ 2. `/plan` - Design intent -> research -> branch + worktree + task list
461
+ 3. `/dev` - TDD development (implementer -> spec review -> quality review)
462
+ 4. `/validate` - Type check, lint, security, tests - all fresh output
463
+ 5. `/ship` - Push branch and create PR with full documentation
464
+ 6. `/review` - Address ALL PR feedback (GitHub Actions, Greptile, SonarCloud)
465
+ 7. `/premerge` - Update docs, hand off PR to user
466
+ 8. `/verify` - Post-merge health check (CI on main, close Beads)
469
467
 
470
468
  See AGENTS.md for full workflow details.
471
469
  MDC_EOF
@@ -495,7 +493,7 @@ if [ "$INSTALL_WINDSURF" = true ]; then
495
493
  filename=$(basename "$cmd")
496
494
  strip_frontmatter "$cmd" > ".windsurf/workflows/$filename" 2>/dev/null || cp "$cmd" ".windsurf/workflows/$filename"
497
495
  done
498
- echo -e " ${GREEN}Converted: 9 workflow commands${NC}"
496
+ echo -e " ${GREEN}Converted: 11 workflow commands${NC}"
499
497
 
500
498
  # Copy rules
501
499
  cp .claude/rules/workflow.md .windsurf/rules/workflow.md 2>/dev/null || true
@@ -525,7 +523,7 @@ if [ "$INSTALL_KILOCODE" = true ]; then
525
523
  filename=$(basename "$cmd")
526
524
  strip_frontmatter "$cmd" > ".kilocode/workflows/$filename" 2>/dev/null || cp "$cmd" ".kilocode/workflows/$filename"
527
525
  done
528
- echo -e " ${GREEN}Converted: 9 workflow commands${NC}"
526
+ echo -e " ${GREEN}Converted: 11 workflow commands${NC}"
529
527
 
530
528
  # Copy rules
531
529
  cp .claude/rules/workflow.md .kilocode/rules/workflow.md 2>/dev/null || true
@@ -552,7 +550,7 @@ if [ "$INSTALL_ANTIGRAVITY" = true ]; then
552
550
  filename=$(basename "$cmd")
553
551
  strip_frontmatter "$cmd" > ".agent/workflows/$filename" 2>/dev/null || cp "$cmd" ".agent/workflows/$filename"
554
552
  done
555
- echo -e " ${GREEN}Converted: 9 workflow commands${NC}"
553
+ echo -e " ${GREEN}Converted: 11 workflow commands${NC}"
556
554
 
557
555
  # Copy rules
558
556
  cp .claude/rules/workflow.md .agent/rules/workflow.md 2>/dev/null || true
@@ -583,7 +581,7 @@ if [ "$INSTALL_COPILOT" = true ]; then
583
581
  filename="${basename_noext}.prompt.md"
584
582
  strip_frontmatter "$cmd" > ".github/prompts/$filename" 2>/dev/null || cp "$cmd" ".github/prompts/$filename"
585
583
  done
586
- echo -e " ${GREEN}Converted: 9 workflow prompts${NC}"
584
+ echo -e " ${GREEN}Converted: 11 workflow prompts${NC}"
587
585
  fi
588
586
 
589
587
  # Link copilot-instructions.md -> AGENTS.md
@@ -615,7 +613,7 @@ if [ "$INSTALL_CONTINUE" = true ]; then
615
613
  strip_frontmatter "$cmd" 2>/dev/null || cat "$cmd"
616
614
  } > ".continue/prompts/$filename"
617
615
  done
618
- echo -e " ${GREEN}Converted: 9 workflow prompts${NC}"
616
+ echo -e " ${GREEN}Converted: 11 workflow prompts${NC}"
619
617
  fi
620
618
 
621
619
  # Create SKILL.md
@@ -660,7 +658,7 @@ if [ "$INSTALL_OPENCODE" = true ]; then
660
658
  # Copy commands as-is (same YAML format)
661
659
  if [ "$INSTALL_CLAUDE" = true ]; then
662
660
  cp .claude/commands/*.md .opencode/commands/ 2>/dev/null || true
663
- echo -e " ${GREEN}Copied: 9 workflow commands${NC}"
661
+ echo -e " ${GREEN}Copied: 11 workflow commands${NC}"
664
662
  fi
665
663
 
666
664
  # Create SKILL.md
@@ -701,7 +699,7 @@ if [ "$INSTALL_ROO" = true ]; then
701
699
  filename=$(basename "$cmd")
702
700
  strip_frontmatter "$cmd" > ".roo/commands/$filename" 2>/dev/null || cp "$cmd" ".roo/commands/$filename"
703
701
  done
704
- echo -e " ${GREEN}Converted: 9 workflow commands${NC}"
702
+ echo -e " ${GREEN}Converted: 11 workflow commands${NC}"
705
703
  fi
706
704
 
707
705
  # Link .roorules -> AGENTS.md
@@ -1043,20 +1041,18 @@ echo -e " GET STARTED"
1043
1041
  echo -e "==============================================${NC}"
1044
1042
  echo ""
1045
1043
  echo " /status - Check current context"
1046
- echo " /research - Start researching a feature"
1047
- echo " /plan - Create implementation plan"
1048
- echo " /dev - Start TDD development"
1049
- echo " /check - Run validation"
1044
+ echo " /plan - Design intent, research, branch + task list"
1045
+ echo " /dev - TDD development"
1046
+ echo " /validate - Type check, lint, tests, security"
1050
1047
  echo " /ship - Create pull request"
1051
1048
  echo " /review - Address PR feedback"
1052
- echo " /merge - Merge and cleanup"
1053
- echo " /verify - Final documentation check"
1049
+ echo " /premerge - Update docs, hand off PR"
1050
+ echo " /verify - Post-merge health check"
1054
1051
  echo ""
1055
1052
  echo " Full guide: docs/WORKFLOW.md"
1056
1053
  echo ""
1057
1054
  echo "Optional tools:"
1058
1055
  echo " - Beads: $PKG_MANAGER install -g @beads/bd && bd init"
1059
- echo " - OpenSpec: $PKG_MANAGER install -g @fission-ai/openspec"
1060
1056
  echo ""
1061
1057
  echo -e "${CYAN}Package manager detected: $PKG_MANAGER${NC}"
1062
1058
  echo ""
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Plan Command - OpenSpec & Beads Integration
2
+ * Plan Command - Beads Integration
3
3
  * Creates implementation plan after research is complete
4
4
  *
5
5
  * Security: Uses execFileSync instead of exec/execSync to prevent command injection
@@ -123,18 +123,18 @@ function readResearchDoc(featureSlug) {
123
123
  * 3. Default to tactical if no clear signals
124
124
  *
125
125
  * @param {string} researchContent - Research document markdown content
126
- * @returns {{type: 'tactical'|'strategic', requiresOpenSpec: boolean, reason: string}} Scope analysis result
126
+ * @returns {{type: 'tactical'|'strategic', requiresDesignDoc: boolean, reason: string}} Scope analysis result
127
127
  * @example
128
128
  * const scope = detectScope(researchMarkdown);
129
129
  * if (scope.type === 'strategic') {
130
- * console.log('Requires OpenSpec proposal:', scope.reason);
130
+ * console.log('Requires design doc:', scope.reason);
131
131
  * }
132
132
  */
133
133
  function detectScope(researchContent) {
134
134
  if (!researchContent || typeof researchContent !== 'string') {
135
135
  return {
136
136
  type: 'tactical',
137
- requiresOpenSpec: false,
137
+ requiresDesignDoc: false,
138
138
  reason: 'Invalid research content (defaulting to tactical)',
139
139
  };
140
140
  }
@@ -166,7 +166,7 @@ function detectScope(researchContent) {
166
166
  const type = scopeMatch[1].toLowerCase();
167
167
  return {
168
168
  type,
169
- requiresOpenSpec: type === 'strategic',
169
+ requiresDesignDoc: type === 'strategic',
170
170
  reason: `Explicit scope declaration: ${scopeMatch[1]}`,
171
171
  };
172
172
  }
@@ -190,7 +190,7 @@ function detectScope(researchContent) {
190
190
  if (foundKeywords.length > 0) {
191
191
  return {
192
192
  type: 'strategic',
193
- requiresOpenSpec: true,
193
+ requiresDesignDoc: true,
194
194
  reason: `Strategic keywords detected: ${foundKeywords.join(', ')}`,
195
195
  };
196
196
  }
@@ -198,7 +198,7 @@ function detectScope(researchContent) {
198
198
  // Default to tactical (safe fallback)
199
199
  return {
200
200
  type: 'tactical',
201
- requiresOpenSpec: false,
201
+ requiresDesignDoc: false,
202
202
  reason: 'No strategic indicators found (tactical by default)',
203
203
  };
204
204
  }
@@ -244,7 +244,7 @@ function createBeadsIssue(featureName, researchPath, scope) {
244
244
  if (!slugValidation.valid) {
245
245
  return { success: false, error: `Cannot generate valid slug from feature name: ${slugValidation.error}` };
246
246
  }
247
- description += `\n\nOpenSpec: openspec/changes/${featureSlug}/`;
247
+ description += `\n\nDesign: docs/plans/${featureSlug}-design.md`;
248
248
  }
249
249
 
250
250
  // Execute bd create command using execFileSync for safety (OWASP A03)
@@ -585,12 +585,9 @@ function applyYAGNIFilter({ task, tasks, designDoc } = {}) {
585
585
  * Strategic workflow (architecture changes, >1 day):
586
586
  * 1. Read research document
587
587
  * 2. Detect scope (strategic)
588
- * 3. Create Beads issue with OpenSpec link
588
+ * 3. Create Beads issue with design doc link
589
589
  * 4. Create feature branch
590
- * 5. Generate OpenSpec proposal (proposal.md, tasks.md, design.md)
591
- * 6. Commit and push proposal
592
- * 7. Create proposal PR
593
- * → Next: Wait for proposal approval
590
+ * Next: Create design doc, then /dev command
594
591
  *
595
592
  * @param {string} featureName - Feature name (human-readable, e.g., "Payment Integration")
596
593
  * @returns {Promise<{
@@ -598,8 +595,7 @@ function applyYAGNIFilter({ task, tasks, designDoc } = {}) {
598
595
  * scope?: 'tactical'|'strategic',
599
596
  * beadsIssueId?: string,
600
597
  * branchName?: string,
601
- * openSpecCreated?: boolean,
602
- * proposalPR?: {url: string, number: number},
598
+ *
603
599
  * summary?: string,
604
600
  * nextCommand?: string,
605
601
  * error?: string
@@ -20,10 +20,10 @@ const STAGE_NAMES = {
20
20
  research: 'Research',
21
21
  plan: 'Plan',
22
22
  dev: 'Dev',
23
- check: 'Check',
23
+ validate: 'Validate',
24
24
  ship: 'Ship',
25
25
  review: 'Review',
26
- merge: 'Merge',
26
+ premerge: 'Premerge',
27
27
  };
28
28
 
29
29
  /**
@@ -0,0 +1,294 @@
1
+ const { scoreImportDependencies } = require('./import-detector.js');
2
+ const { parseTaskFile } = require('./task-parser.js');
3
+ const { scoreContractDependencies } = require('./contract-detector.js');
4
+ const { scoreBehavioralDependencies } = require('./behavior-detector.js');
5
+ const { WEIGHTS: RUBRIC_WEIGHTS, aggregateRubricScores, needsUserEscalation } = require('./rubric.js');
6
+ const { normalizeRepoPath } = require('./path-utils.js');
7
+
8
+ const CONFIDENCE_THRESHOLD = 0.7;
9
+ const DETECTOR_KEYS = Object.freeze([
10
+ 'importCallChain',
11
+ 'contractDependencies',
12
+ 'behavioralDependencies',
13
+ ]);
14
+
15
+ function createZeroedDetectorMap() {
16
+ return DETECTOR_KEYS.reduce((result, key) => {
17
+ result[key] = 0;
18
+ return result;
19
+ }, {});
20
+ }
21
+
22
+ function createEmptyDetectorEvidence() {
23
+ return DETECTOR_KEYS.reduce((result, key) => {
24
+ result[key] = [];
25
+ return result;
26
+ }, {});
27
+ }
28
+
29
+ function normalizeContractToken(contract) {
30
+ if (typeof contract !== 'string') {
31
+ return null;
32
+ }
33
+
34
+ const separatorIndex = contract.indexOf(':');
35
+ if (separatorIndex === -1) {
36
+ return contract;
37
+ }
38
+
39
+ return `${normalizeRepoPath(contract.slice(0, separatorIndex))}${contract.slice(separatorIndex)}`;
40
+ }
41
+
42
+ function normalizeContractNotes(notes = '') {
43
+ return notes
44
+ .split(/\r?\n/)
45
+ .map((line) => {
46
+ const marker = ': ';
47
+ if (!line.startsWith('contracts@') || !line.includes(marker)) {
48
+ return line;
49
+ }
50
+
51
+ const separatorIndex = line.indexOf(marker);
52
+ const prefix = line.slice(0, separatorIndex + marker.length);
53
+ const payload = line.slice(separatorIndex + marker.length);
54
+ const normalizedPayload = payload
55
+ .split(/\s+/)
56
+ .map(normalizeContractToken)
57
+ .join(' ');
58
+ return `${prefix}${normalizedPayload}`;
59
+ })
60
+ .join('\n');
61
+ }
62
+
63
+ function normalizeIssue(issue, label) {
64
+ if (!issue || typeof issue !== 'object') {
65
+ throw new Error(`${label} is required`);
66
+ }
67
+
68
+ if (!issue.id || typeof issue.id !== 'string') {
69
+ throw new Error(`${label} id is required`);
70
+ }
71
+
72
+ return {
73
+ id: issue.id,
74
+ title: typeof issue.title === 'string' ? issue.title : '',
75
+ description: typeof issue.description === 'string' ? issue.description : '',
76
+ status: typeof issue.status === 'string' ? issue.status : '',
77
+ contracts: Array.isArray(issue.contracts)
78
+ ? issue.contracts.map(normalizeContractToken).filter((contract) => typeof contract === 'string')
79
+ : [],
80
+ files: Array.isArray(issue.files)
81
+ ? issue.files
82
+ .filter((file) => typeof file === 'string')
83
+ .map((file) => normalizeRepoPath(file))
84
+ : [],
85
+ notes: typeof issue.notes === 'string' ? normalizeContractNotes(issue.notes) : '',
86
+ };
87
+ }
88
+
89
+ function normalizePhase3Input(input = {}) {
90
+ const currentIssue = normalizeIssue(input.currentIssue, 'Current issue');
91
+ const openIssues = Array.isArray(input.openIssues)
92
+ ? input.openIssues.map((issue) => normalizeIssue(issue, 'Open issue'))
93
+ : [];
94
+ const taskContext = input.taskContext ?? parseTaskFile(input.taskFile);
95
+
96
+ return {
97
+ currentIssue,
98
+ openIssues,
99
+ taskContext,
100
+ repositoryRoot: typeof input.repositoryRoot === 'string' && input.repositoryRoot
101
+ ? input.repositoryRoot
102
+ : process.cwd(),
103
+ thresholds: {
104
+ confidence: CONFIDENCE_THRESHOLD,
105
+ },
106
+ };
107
+ }
108
+
109
+ function createScaffoldResult(normalizedInput) {
110
+ const detectorScores = createZeroedDetectorMap();
111
+
112
+ return {
113
+ currentIssue: normalizedInput.currentIssue,
114
+ taskContext: normalizedInput.taskContext,
115
+ issues: [],
116
+ scores: {
117
+ ...detectorScores,
118
+ rubric: 0,
119
+ },
120
+ rubric: {
121
+ score: 0,
122
+ threshold: normalizedInput.thresholds.confidence,
123
+ summary: 'No detector findings yet.',
124
+ weights: RUBRIC_WEIGHTS,
125
+ reasons: [],
126
+ },
127
+ detectorEvidence: createEmptyDetectorEvidence(),
128
+ detectorConflicts: [],
129
+ importCallChain: {
130
+ score: 0,
131
+ evidence: [],
132
+ findings: [],
133
+ },
134
+ contractDependencies: {
135
+ score: 0,
136
+ evidence: [],
137
+ findings: [],
138
+ },
139
+ behavioralDependencies: {
140
+ score: 0,
141
+ evidence: [],
142
+ findings: [],
143
+ uncertain: false,
144
+ },
145
+ confidence: {
146
+ score: 1,
147
+ threshold: normalizedInput.thresholds.confidence,
148
+ belowThreshold: false,
149
+ reasons: [],
150
+ },
151
+ proposals: [],
152
+ needsUserDecision: false,
153
+ metadata: {
154
+ analyzerVersion: 'phase3-v1-scaffold',
155
+ parser: '@babel/parser',
156
+ },
157
+ };
158
+ }
159
+
160
+ function inferDetectorType(finding) {
161
+ if (finding.behavioralSignal) {
162
+ return 'behavioralDependencies';
163
+ }
164
+
165
+ const firstEvidenceType = finding.evidence?.[0]?.type;
166
+ if (firstEvidenceType === 'contract') {
167
+ return 'contractDependencies';
168
+ }
169
+
170
+ return 'importCallChain';
171
+ }
172
+
173
+ function formatDetectorList(detectorTypes) {
174
+ const labels = detectorTypes.map((type) => {
175
+ if (type === 'importCallChain') {
176
+ return 'import/call-chain evidence';
177
+ }
178
+ if (type === 'contractDependencies') {
179
+ return 'contract evidence';
180
+ }
181
+ return 'behavioral evidence';
182
+ });
183
+
184
+ return labels.join(', ');
185
+ }
186
+
187
+ function buildDependencyProposals(normalizedInput, findings, rubricResult) {
188
+ if (findings.length === 0) {
189
+ return [];
190
+ }
191
+
192
+ const groupedFindings = new Map();
193
+ for (const finding of findings) {
194
+ const entry = groupedFindings.get(finding.targetIssueId) ?? {
195
+ targetIssueId: finding.targetIssueId,
196
+ detectorTypes: new Set(),
197
+ hasBehavioralUncertainty: false,
198
+ };
199
+ entry.detectorTypes.add(inferDetectorType(finding));
200
+ entry.hasBehavioralUncertainty = entry.hasBehavioralUncertainty || Boolean(finding.uncertain);
201
+ groupedFindings.set(finding.targetIssueId, entry);
202
+ }
203
+
204
+ return Array.from(groupedFindings.values()).map((entry) => {
205
+ const detectorTypes = Array.from(entry.detectorTypes);
206
+ const reasons = [
207
+ `${entry.targetIssueId} appears to consume or rely on logic being changed in ${normalizedInput.currentIssue.id} through ${formatDetectorList(detectorTypes)}.`,
208
+ ];
209
+ const cons = [
210
+ 'Adds coordination overhead and may delay the dependent issue.',
211
+ ];
212
+
213
+ if (entry.hasBehavioralUncertainty) {
214
+ reasons.push('Behavioral evidence is heuristic and should be confirmed before applying the dependency.');
215
+ cons.push('Behavioral evidence is heuristic and may be noisy until a human confirms the dependency.');
216
+ }
217
+
218
+ if (rubricResult.confidence.belowThreshold) {
219
+ reasons.push(`Overall confidence ${rubricResult.confidence.score.toFixed(2)} is below the review threshold.`);
220
+ }
221
+
222
+ return {
223
+ action: 'add-dependency',
224
+ dependentIssueId: entry.targetIssueId,
225
+ dependsOnIssueId: normalizedInput.currentIssue.id,
226
+ detectorTypes,
227
+ requiresApproval: true,
228
+ confidence: rubricResult.confidence.score,
229
+ reasons,
230
+ pros: [
231
+ 'Preserves issue independence claims by sequencing affected work explicitly.',
232
+ 'Reduces merge-order surprises for downstream logic consumers.',
233
+ ],
234
+ cons,
235
+ };
236
+ });
237
+ }
238
+
239
+ function analyzePhase3Dependencies(input = {}) {
240
+ const normalizedInput = normalizePhase3Input(input);
241
+ const result = createScaffoldResult(normalizedInput);
242
+ const importResult = scoreImportDependencies(normalizedInput);
243
+ const contractResult = scoreContractDependencies(normalizedInput);
244
+ const behavioralResult = scoreBehavioralDependencies(normalizedInput);
245
+ const rubricResult = aggregateRubricScores({
246
+ importCallChain: importResult,
247
+ contractDependencies: contractResult,
248
+ behavioralDependencies: behavioralResult,
249
+ }, {
250
+ confidenceThreshold: normalizedInput.thresholds.confidence,
251
+ });
252
+
253
+ result.issues = [
254
+ ...importResult.findings,
255
+ ...contractResult.findings,
256
+ ...behavioralResult.findings,
257
+ ];
258
+ result.scores.importCallChain = importResult.score;
259
+ result.scores.contractDependencies = contractResult.score;
260
+ result.scores.behavioralDependencies = behavioralResult.score;
261
+ result.scores.rubric = rubricResult.total;
262
+ result.rubric = rubricResult;
263
+ result.detectorEvidence.importCallChain = importResult.evidence;
264
+ result.detectorEvidence.contractDependencies = contractResult.evidence;
265
+ result.detectorEvidence.behavioralDependencies = behavioralResult.evidence;
266
+ result.detectorConflicts = rubricResult.detectorConflicts;
267
+ result.confidence = rubricResult.confidence;
268
+ result.needsUserDecision = needsUserEscalation(rubricResult);
269
+ result.proposals = buildDependencyProposals(normalizedInput, result.issues, rubricResult);
270
+ result.importCallChain = {
271
+ score: importResult.score,
272
+ evidence: importResult.evidence,
273
+ findings: importResult.findings,
274
+ };
275
+ result.contractDependencies = {
276
+ score: contractResult.score,
277
+ evidence: contractResult.evidence,
278
+ findings: contractResult.findings,
279
+ };
280
+ result.behavioralDependencies = {
281
+ score: behavioralResult.score,
282
+ evidence: behavioralResult.evidence,
283
+ findings: behavioralResult.findings,
284
+ uncertain: behavioralResult.uncertain,
285
+ };
286
+
287
+ return result;
288
+ }
289
+
290
+ module.exports = {
291
+ analyzePhase3Dependencies,
292
+ DETECTOR_KEYS,
293
+ normalizePhase3Input,
294
+ };
@@ -0,0 +1,98 @@
1
+ const STOP_WORDS = new Set([
2
+ 'the', 'a', 'an', 'and', 'or', 'is', 'in', 'to', 'for', 'of', 'with', 'on',
3
+ 'at', 'by', 'from', 'add', 'fix', 'update', 'implement', 'create', 'remove',
4
+ 'delete', 'make', 'use', 'get', 'set', 'run', 'test', 'check', 'this', 'that',
5
+ 'it', 'be', 'as', 'not', 'no', 'but', 'if', 'do', 'we', 'they', 'are', 'was',
6
+ 'were', 'been', 'have', 'has', 'had', 'will', 'would', 'could', 'should',
7
+ 'may', 'can', 'each', 'every', 'both', 'also', 'into', 'than', 'then', 'when',
8
+ 'where', 'which', 'what', 'how', 'why', 'who', 'its', 'new', 'first', 'last',
9
+ 'same', 'other',
10
+ ]);
11
+
12
+ const BEHAVIOR_TERMS = new Set([
13
+ 'approval', 'behavior', 'confidence', 'default', 'manual', 'ordering',
14
+ 'output', 'policy', 'review', 'rules', 'rule', 'threshold', 'validation',
15
+ 'workflow', 'state',
16
+ ]);
17
+ const STRONG_SIGNAL_TERMS = new Set([
18
+ 'approval',
19
+ 'confidence',
20
+ 'manual',
21
+ 'policy',
22
+ 'review',
23
+ 'rule',
24
+ 'rules',
25
+ 'threshold',
26
+ ]);
27
+
28
+ function tokenize(text) {
29
+ return Array.from(new Set(
30
+ text
31
+ .toLowerCase()
32
+ .split(/[^a-z0-9]+/)
33
+ .filter((term) => term.length >= 3 && !STOP_WORDS.has(term)),
34
+ ));
35
+ }
36
+
37
+ function scoreBehavioralDependencies(normalizedInput) {
38
+ const taskText = normalizedInput.taskContext.tasks
39
+ .map((task) => task.whatToImplement)
40
+ .join(' ');
41
+ const taskTerms = tokenize(taskText).filter((term) => BEHAVIOR_TERMS.has(term));
42
+
43
+ if (taskTerms.length === 0) {
44
+ return {
45
+ score: 0,
46
+ findings: [],
47
+ evidence: [],
48
+ uncertain: false,
49
+ };
50
+ }
51
+
52
+ const findings = [];
53
+ const evidence = [];
54
+
55
+ for (const issue of normalizedInput.openIssues) {
56
+ const issueText = [issue.title, issue.description, issue.notes].filter(Boolean).join(' ');
57
+ const issueTerms = new Set(tokenize(issueText));
58
+ const sharedTerms = taskTerms.filter((term) => issueTerms.has(term));
59
+
60
+ if (sharedTerms.length < 2) {
61
+ continue;
62
+ }
63
+
64
+ const strongSignalCount = sharedTerms.filter((term) => STRONG_SIGNAL_TERMS.has(term)).length;
65
+ const isUncertain = strongSignalCount < 2 || sharedTerms.length < 3;
66
+ const signal = sharedTerms.some((term) => ['rule', 'rules', 'policy', 'approval'].includes(term))
67
+ ? 'rule-change-overlap'
68
+ : 'behavior-change-overlap';
69
+ const issueEvidence = sharedTerms.map((term) => ({
70
+ type: 'behavior',
71
+ targetIssueId: issue.id,
72
+ sharedTerms: sharedTerms,
73
+ term,
74
+ scoreContribution: 1,
75
+ }));
76
+
77
+ findings.push({
78
+ sourceIssueId: normalizedInput.currentIssue.id,
79
+ targetIssueId: issue.id,
80
+ score: sharedTerms.length,
81
+ behavioralSignal: signal,
82
+ uncertain: isUncertain,
83
+ evidence: issueEvidence,
84
+ });
85
+ evidence.push(...issueEvidence);
86
+ }
87
+
88
+ return {
89
+ score: findings.reduce((total, finding) => total + finding.score, 0),
90
+ findings,
91
+ evidence,
92
+ uncertain: findings.some((finding) => finding.uncertain),
93
+ };
94
+ }
95
+
96
+ module.exports = {
97
+ scoreBehavioralDependencies,
98
+ };