@pennyfarthing/core 7.4.0 → 7.5.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/dist/cli/utils/files.d.ts +0 -1
- package/dist/cli/utils/files.js +73 -56
- package/dist/cli/utils/manifest.d.ts +0 -1
- package/dist/cli/utils/manifest.js +48 -45
- package/dist/cli/utils/version.d.ts +0 -1
- package/dist/cli/utils/version.js +38 -32
- package/dist/index.d.ts +0 -1
- package/dist/index.js +39 -11
- package/dist/permissions/index.d.ts +0 -1
- package/dist/permissions/index.js +7 -4
- package/dist/permissions/permission-schema.d.ts +0 -1
- package/dist/permissions/permission-schema.js +13 -9
- package/dist/scripts/job-fair-aggregator.d.ts +0 -1
- package/dist/scripts/job-fair-aggregator.js +484 -341
- package/dist/workflow/index.d.ts +0 -1
- package/dist/workflow/index.js +12 -5
- package/dist/workflow/workflow-loader.d.ts +0 -1
- package/dist/workflow/workflow-loader.js +40 -34
- package/dist/workflow/workflow-permissions.d.ts +0 -1
- package/dist/workflow/workflow-permissions.js +14 -8
- package/dist/workflow/workflow-router.d.ts +0 -1
- package/dist/workflow/workflow-router.js +70 -51
- package/dist/workflow/workflow-schema.d.ts +0 -1
- package/dist/workflow/workflow-schema.js +62 -59
- package/package.json +1 -1
- package/dist/bmad/context-reader.d.ts +0 -71
- package/dist/bmad/context-reader.d.ts.map +0 -1
- package/dist/bmad/context-reader.js +0 -369
- package/dist/bmad/context-reader.js.map +0 -1
- package/dist/bmad/context-reader.test.d.ts +0 -71
- package/dist/bmad/context-reader.test.d.ts.map +0 -1
- package/dist/bmad/context-reader.test.js +0 -878
- package/dist/bmad/context-reader.test.js.map +0 -1
- package/dist/bmad/epics-parser.d.ts +0 -61
- package/dist/bmad/epics-parser.d.ts.map +0 -1
- package/dist/bmad/epics-parser.js +0 -331
- package/dist/bmad/epics-parser.js.map +0 -1
- package/dist/bmad/epics-parser.test.d.ts +0 -7
- package/dist/bmad/epics-parser.test.d.ts.map +0 -1
- package/dist/bmad/epics-parser.test.js +0 -449
- package/dist/bmad/epics-parser.test.js.map +0 -1
- package/dist/bmad/index.d.ts +0 -11
- package/dist/bmad/index.d.ts.map +0 -1
- package/dist/bmad/index.js +0 -24
- package/dist/bmad/index.js.map +0 -1
- package/dist/bmad/status-sync.d.ts +0 -173
- package/dist/bmad/status-sync.d.ts.map +0 -1
- package/dist/bmad/status-sync.js +0 -463
- package/dist/bmad/status-sync.js.map +0 -1
- package/dist/bmad/status-sync.test.d.ts +0 -7
- package/dist/bmad/status-sync.test.d.ts.map +0 -1
- package/dist/bmad/status-sync.test.js +0 -702
- package/dist/bmad/status-sync.test.js.map +0 -1
- package/dist/bmad/story-exporter.d.ts +0 -55
- package/dist/bmad/story-exporter.d.ts.map +0 -1
- package/dist/bmad/story-exporter.js +0 -170
- package/dist/bmad/story-exporter.js.map +0 -1
- package/dist/bmad/story-exporter.test.d.ts +0 -51
- package/dist/bmad/story-exporter.test.d.ts.map +0 -1
- package/dist/bmad/story-exporter.test.js +0 -603
- package/dist/bmad/story-exporter.test.js.map +0 -1
- package/dist/bmad/story-parser.d.ts +0 -44
- package/dist/bmad/story-parser.d.ts.map +0 -1
- package/dist/bmad/story-parser.js +0 -307
- package/dist/bmad/story-parser.js.map +0 -1
- package/dist/bmad/story-parser.test.d.ts +0 -44
- package/dist/bmad/story-parser.test.d.ts.map +0 -1
- package/dist/bmad/story-parser.test.js +0 -693
- package/dist/bmad/story-parser.test.js.map +0 -1
- package/dist/cli/commands/command.d.ts +0 -28
- package/dist/cli/commands/command.d.ts.map +0 -1
- package/dist/cli/commands/command.js +0 -399
- package/dist/cli/commands/command.js.map +0 -1
- package/dist/cli/commands/cyclist.d.ts +0 -46
- package/dist/cli/commands/cyclist.d.ts.map +0 -1
- package/dist/cli/commands/cyclist.js +0 -196
- package/dist/cli/commands/cyclist.js.map +0 -1
- package/dist/cli/commands/cyclist.test.d.ts +0 -13
- package/dist/cli/commands/cyclist.test.d.ts.map +0 -1
- package/dist/cli/commands/cyclist.test.js +0 -245
- package/dist/cli/commands/cyclist.test.js.map +0 -1
- package/dist/cli/commands/doctor.d.ts +0 -9
- package/dist/cli/commands/doctor.d.ts.map +0 -1
- package/dist/cli/commands/doctor.js +0 -652
- package/dist/cli/commands/doctor.js.map +0 -1
- package/dist/cli/commands/init.d.ts +0 -8
- package/dist/cli/commands/init.d.ts.map +0 -1
- package/dist/cli/commands/init.js +0 -524
- package/dist/cli/commands/init.js.map +0 -1
- package/dist/cli/commands/skill.d.ts +0 -28
- package/dist/cli/commands/skill.d.ts.map +0 -1
- package/dist/cli/commands/skill.js +0 -416
- package/dist/cli/commands/skill.js.map +0 -1
- package/dist/cli/commands/theme.d.ts +0 -21
- package/dist/cli/commands/theme.d.ts.map +0 -1
- package/dist/cli/commands/theme.js +0 -201
- package/dist/cli/commands/theme.js.map +0 -1
- package/dist/cli/commands/uninstall.d.ts +0 -8
- package/dist/cli/commands/uninstall.d.ts.map +0 -1
- package/dist/cli/commands/uninstall.js +0 -237
- package/dist/cli/commands/uninstall.js.map +0 -1
- package/dist/cli/commands/update.d.ts +0 -9
- package/dist/cli/commands/update.d.ts.map +0 -1
- package/dist/cli/commands/update.js +0 -418
- package/dist/cli/commands/update.js.map +0 -1
- package/dist/cli/commands/version.d.ts +0 -2
- package/dist/cli/commands/version.d.ts.map +0 -1
- package/dist/cli/commands/version.js +0 -28
- package/dist/cli/commands/version.js.map +0 -1
- package/dist/cli/customization.test.d.ts +0 -12
- package/dist/cli/customization.test.d.ts.map +0 -1
- package/dist/cli/customization.test.js +0 -84
- package/dist/cli/customization.test.js.map +0 -1
- package/dist/cli/cyclist-migration.test.d.ts +0 -16
- package/dist/cli/cyclist-migration.test.d.ts.map +0 -1
- package/dist/cli/cyclist-migration.test.js +0 -225
- package/dist/cli/cyclist-migration.test.js.map +0 -1
- package/dist/cli/index.d.ts +0 -3
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js +0 -174
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/ocean-profiles.test.d.ts +0 -13
- package/dist/cli/ocean-profiles.test.d.ts.map +0 -1
- package/dist/cli/ocean-profiles.test.js +0 -134
- package/dist/cli/ocean-profiles.test.js.map +0 -1
- package/dist/cli/theme-maker.test.d.ts +0 -11
- package/dist/cli/theme-maker.test.d.ts.map +0 -1
- package/dist/cli/theme-maker.test.js +0 -356
- package/dist/cli/theme-maker.test.js.map +0 -1
- package/dist/cli/utils/constants.d.ts +0 -66
- package/dist/cli/utils/constants.d.ts.map +0 -1
- package/dist/cli/utils/constants.js +0 -54
- package/dist/cli/utils/constants.js.map +0 -1
- package/dist/cli/utils/files.d.ts.map +0 -1
- package/dist/cli/utils/files.js.map +0 -1
- package/dist/cli/utils/logger.d.ts +0 -26
- package/dist/cli/utils/logger.d.ts.map +0 -1
- package/dist/cli/utils/logger.js +0 -88
- package/dist/cli/utils/logger.js.map +0 -1
- package/dist/cli/utils/manifest.d.ts.map +0 -1
- package/dist/cli/utils/manifest.js.map +0 -1
- package/dist/cli/utils/node-modules.d.ts +0 -6
- package/dist/cli/utils/node-modules.d.ts.map +0 -1
- package/dist/cli/utils/node-modules.js +0 -31
- package/dist/cli/utils/node-modules.js.map +0 -1
- package/dist/cli/utils/prompts.d.ts +0 -34
- package/dist/cli/utils/prompts.d.ts.map +0 -1
- package/dist/cli/utils/prompts.js +0 -93
- package/dist/cli/utils/prompts.js.map +0 -1
- package/dist/cli/utils/symlinks.d.ts +0 -29
- package/dist/cli/utils/symlinks.d.ts.map +0 -1
- package/dist/cli/utils/symlinks.js +0 -181
- package/dist/cli/utils/symlinks.js.map +0 -1
- package/dist/cli/utils/themes.d.ts +0 -101
- package/dist/cli/utils/themes.d.ts.map +0 -1
- package/dist/cli/utils/themes.js +0 -373
- package/dist/cli/utils/themes.js.map +0 -1
- package/dist/cli/utils/themes.test.d.ts +0 -12
- package/dist/cli/utils/themes.test.d.ts.map +0 -1
- package/dist/cli/utils/themes.test.js +0 -147
- package/dist/cli/utils/themes.test.js.map +0 -1
- package/dist/cli/utils/version.d.ts.map +0 -1
- package/dist/cli/utils/version.js.map +0 -1
- package/dist/cli/workspace.test.d.ts +0 -8
- package/dist/cli/workspace.test.d.ts.map +0 -1
- package/dist/cli/workspace.test.js +0 -151
- package/dist/cli/workspace.test.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/jira/jira-epic-creation.d.ts +0 -109
- package/dist/jira/jira-epic-creation.d.ts.map +0 -1
- package/dist/jira/jira-epic-creation.js +0 -253
- package/dist/jira/jira-epic-creation.js.map +0 -1
- package/dist/jira/jira-epic-creation.test.d.ts +0 -16
- package/dist/jira/jira-epic-creation.test.d.ts.map +0 -1
- package/dist/jira/jira-epic-creation.test.js +0 -387
- package/dist/jira/jira-epic-creation.test.js.map +0 -1
- package/dist/jira/jira-sprint-sync.d.ts +0 -247
- package/dist/jira/jira-sprint-sync.d.ts.map +0 -1
- package/dist/jira/jira-sprint-sync.js +0 -670
- package/dist/jira/jira-sprint-sync.js.map +0 -1
- package/dist/jira/jira-sprint-sync.test.d.ts +0 -16
- package/dist/jira/jira-sprint-sync.test.d.ts.map +0 -1
- package/dist/jira/jira-sprint-sync.test.js +0 -845
- package/dist/jira/jira-sprint-sync.test.js.map +0 -1
- package/dist/permissions/index.d.ts.map +0 -1
- package/dist/permissions/index.js.map +0 -1
- package/dist/permissions/permission-schema.d.ts.map +0 -1
- package/dist/permissions/permission-schema.js.map +0 -1
- package/dist/permissions/permission-schema.test.d.ts +0 -40
- package/dist/permissions/permission-schema.test.d.ts.map +0 -1
- package/dist/permissions/permission-schema.test.js +0 -367
- package/dist/permissions/permission-schema.test.js.map +0 -1
- package/dist/scripts/add-ocean-profiles.d.ts +0 -9
- package/dist/scripts/add-ocean-profiles.d.ts.map +0 -1
- package/dist/scripts/add-ocean-profiles.js +0 -695
- package/dist/scripts/add-ocean-profiles.js.map +0 -1
- package/dist/scripts/benchmark-integration.d.ts +0 -182
- package/dist/scripts/benchmark-integration.d.ts.map +0 -1
- package/dist/scripts/benchmark-integration.js +0 -691
- package/dist/scripts/benchmark-integration.js.map +0 -1
- package/dist/scripts/benchmark-integration.test.d.ts +0 -13
- package/dist/scripts/benchmark-integration.test.d.ts.map +0 -1
- package/dist/scripts/benchmark-integration.test.js +0 -680
- package/dist/scripts/benchmark-integration.test.js.map +0 -1
- package/dist/scripts/debugging-scenarios.test.d.ts +0 -18
- package/dist/scripts/debugging-scenarios.test.d.ts.map +0 -1
- package/dist/scripts/debugging-scenarios.test.js +0 -317
- package/dist/scripts/debugging-scenarios.test.js.map +0 -1
- package/dist/scripts/generate-all-spiders.d.ts +0 -10
- package/dist/scripts/generate-all-spiders.d.ts.map +0 -1
- package/dist/scripts/generate-all-spiders.js +0 -306
- package/dist/scripts/generate-all-spiders.js.map +0 -1
- package/dist/scripts/generate-report.d.ts +0 -65
- package/dist/scripts/generate-report.d.ts.map +0 -1
- package/dist/scripts/generate-report.js +0 -378
- package/dist/scripts/generate-report.js.map +0 -1
- package/dist/scripts/generate-report.test.d.ts +0 -13
- package/dist/scripts/generate-report.test.d.ts.map +0 -1
- package/dist/scripts/generate-report.test.js +0 -363
- package/dist/scripts/generate-report.test.js.map +0 -1
- package/dist/scripts/generate-spider-report.d.ts +0 -65
- package/dist/scripts/generate-spider-report.d.ts.map +0 -1
- package/dist/scripts/generate-spider-report.js +0 -366
- package/dist/scripts/generate-spider-report.js.map +0 -1
- package/dist/scripts/generate-spider-report.test.d.ts +0 -13
- package/dist/scripts/generate-spider-report.test.d.ts.map +0 -1
- package/dist/scripts/generate-spider-report.test.js +0 -367
- package/dist/scripts/generate-spider-report.test.js.map +0 -1
- package/dist/scripts/generate-spider.d.ts +0 -47
- package/dist/scripts/generate-spider.d.ts.map +0 -1
- package/dist/scripts/generate-spider.js +0 -338
- package/dist/scripts/generate-spider.js.map +0 -1
- package/dist/scripts/generate-spider.test.d.ts +0 -14
- package/dist/scripts/generate-spider.test.d.ts.map +0 -1
- package/dist/scripts/generate-spider.test.js +0 -271
- package/dist/scripts/generate-spider.test.js.map +0 -1
- package/dist/scripts/job-fair-aggregator.d.ts.map +0 -1
- package/dist/scripts/job-fair-aggregator.js.map +0 -1
- package/dist/scripts/job-fair-aggregator.test.d.ts +0 -14
- package/dist/scripts/job-fair-aggregator.test.d.ts.map +0 -1
- package/dist/scripts/job-fair-aggregator.test.js +0 -616
- package/dist/scripts/job-fair-aggregator.test.js.map +0 -1
- package/dist/scripts/run-ci.test.d.ts +0 -20
- package/dist/scripts/run-ci.test.d.ts.map +0 -1
- package/dist/scripts/run-ci.test.js +0 -127
- package/dist/scripts/run-ci.test.js.map +0 -1
- package/dist/scripts/theme-detail.test.d.ts +0 -10
- package/dist/scripts/theme-detail.test.d.ts.map +0 -1
- package/dist/scripts/theme-detail.test.js +0 -199
- package/dist/scripts/theme-detail.test.js.map +0 -1
- package/dist/scripts/validate-ocean-profiles.d.ts +0 -9
- package/dist/scripts/validate-ocean-profiles.d.ts.map +0 -1
- package/dist/scripts/validate-ocean-profiles.js +0 -130
- package/dist/scripts/validate-ocean-profiles.js.map +0 -1
- package/dist/workflow/gate-handler.d.ts +0 -94
- package/dist/workflow/gate-handler.d.ts.map +0 -1
- package/dist/workflow/gate-handler.js +0 -189
- package/dist/workflow/gate-handler.js.map +0 -1
- package/dist/workflow/gate-handler.test.d.ts +0 -14
- package/dist/workflow/gate-handler.test.d.ts.map +0 -1
- package/dist/workflow/gate-handler.test.js +0 -543
- package/dist/workflow/gate-handler.test.js.map +0 -1
- package/dist/workflow/generic-handoff.d.ts +0 -281
- package/dist/workflow/generic-handoff.d.ts.map +0 -1
- package/dist/workflow/generic-handoff.js +0 -411
- package/dist/workflow/generic-handoff.js.map +0 -1
- package/dist/workflow/generic-handoff.test.d.ts +0 -21
- package/dist/workflow/generic-handoff.test.d.ts.map +0 -1
- package/dist/workflow/generic-handoff.test.js +0 -499
- package/dist/workflow/generic-handoff.test.js.map +0 -1
- package/dist/workflow/generic-sm-finish.d.ts +0 -89
- package/dist/workflow/generic-sm-finish.d.ts.map +0 -1
- package/dist/workflow/generic-sm-finish.js +0 -157
- package/dist/workflow/generic-sm-finish.js.map +0 -1
- package/dist/workflow/generic-sm-setup.d.ts +0 -138
- package/dist/workflow/generic-sm-setup.d.ts.map +0 -1
- package/dist/workflow/generic-sm-setup.js +0 -382
- package/dist/workflow/generic-sm-setup.js.map +0 -1
- package/dist/workflow/index.d.ts.map +0 -1
- package/dist/workflow/index.js.map +0 -1
- package/dist/workflow/session-state.d.ts +0 -92
- package/dist/workflow/session-state.d.ts.map +0 -1
- package/dist/workflow/session-state.js +0 -198
- package/dist/workflow/session-state.js.map +0 -1
- package/dist/workflow/session-state.test.d.ts +0 -8
- package/dist/workflow/session-state.test.d.ts.map +0 -1
- package/dist/workflow/session-state.test.js +0 -551
- package/dist/workflow/session-state.test.js.map +0 -1
- package/dist/workflow/sm-subagents.test.d.ts +0 -23
- package/dist/workflow/sm-subagents.test.d.ts.map +0 -1
- package/dist/workflow/sm-subagents.test.js +0 -727
- package/dist/workflow/sm-subagents.test.js.map +0 -1
- package/dist/workflow/step-parser.d.ts +0 -45
- package/dist/workflow/step-parser.d.ts.map +0 -1
- package/dist/workflow/step-parser.js +0 -147
- package/dist/workflow/step-parser.js.map +0 -1
- package/dist/workflow/step-parser.test.d.ts +0 -14
- package/dist/workflow/step-parser.test.d.ts.map +0 -1
- package/dist/workflow/step-parser.test.js +0 -470
- package/dist/workflow/step-parser.test.js.map +0 -1
- package/dist/workflow/story-workflow-routing.test.d.ts +0 -17
- package/dist/workflow/story-workflow-routing.test.d.ts.map +0 -1
- package/dist/workflow/story-workflow-routing.test.js +0 -559
- package/dist/workflow/story-workflow-routing.test.js.map +0 -1
- package/dist/workflow/test-cache.d.ts +0 -131
- package/dist/workflow/test-cache.d.ts.map +0 -1
- package/dist/workflow/test-cache.js +0 -226
- package/dist/workflow/test-cache.js.map +0 -1
- package/dist/workflow/test-cache.test.d.ts +0 -17
- package/dist/workflow/test-cache.test.d.ts.map +0 -1
- package/dist/workflow/test-cache.test.js +0 -438
- package/dist/workflow/test-cache.test.js.map +0 -1
- package/dist/workflow/trimodal.d.ts +0 -86
- package/dist/workflow/trimodal.d.ts.map +0 -1
- package/dist/workflow/trimodal.js +0 -118
- package/dist/workflow/trimodal.js.map +0 -1
- package/dist/workflow/trimodal.test.d.ts +0 -11
- package/dist/workflow/trimodal.test.d.ts.map +0 -1
- package/dist/workflow/trimodal.test.js +0 -395
- package/dist/workflow/trimodal.test.js.map +0 -1
- package/dist/workflow/variable-resolver.d.ts +0 -67
- package/dist/workflow/variable-resolver.d.ts.map +0 -1
- package/dist/workflow/variable-resolver.js +0 -156
- package/dist/workflow/variable-resolver.js.map +0 -1
- package/dist/workflow/variable-resolver.test.d.ts +0 -14
- package/dist/workflow/variable-resolver.test.d.ts.map +0 -1
- package/dist/workflow/variable-resolver.test.js +0 -400
- package/dist/workflow/variable-resolver.test.js.map +0 -1
- package/dist/workflow/workflow-executor.d.ts +0 -163
- package/dist/workflow/workflow-executor.d.ts.map +0 -1
- package/dist/workflow/workflow-executor.js +0 -197
- package/dist/workflow/workflow-executor.js.map +0 -1
- package/dist/workflow/workflow-executor.test.d.ts +0 -8
- package/dist/workflow/workflow-executor.test.d.ts.map +0 -1
- package/dist/workflow/workflow-executor.test.js +0 -444
- package/dist/workflow/workflow-executor.test.js.map +0 -1
- package/dist/workflow/workflow-loader.d.ts.map +0 -1
- package/dist/workflow/workflow-loader.js.map +0 -1
- package/dist/workflow/workflow-loader.test.d.ts +0 -15
- package/dist/workflow/workflow-loader.test.d.ts.map +0 -1
- package/dist/workflow/workflow-loader.test.js +0 -354
- package/dist/workflow/workflow-loader.test.js.map +0 -1
- package/dist/workflow/workflow-migration.test.d.ts +0 -17
- package/dist/workflow/workflow-migration.test.d.ts.map +0 -1
- package/dist/workflow/workflow-migration.test.js +0 -371
- package/dist/workflow/workflow-migration.test.js.map +0 -1
- package/dist/workflow/workflow-permissions.d.ts.map +0 -1
- package/dist/workflow/workflow-permissions.js.map +0 -1
- package/dist/workflow/workflow-permissions.test.d.ts +0 -15
- package/dist/workflow/workflow-permissions.test.d.ts.map +0 -1
- package/dist/workflow/workflow-permissions.test.js +0 -301
- package/dist/workflow/workflow-permissions.test.js.map +0 -1
- package/dist/workflow/workflow-router.d.ts.map +0 -1
- package/dist/workflow/workflow-router.js.map +0 -1
- package/dist/workflow/workflow-router.test.d.ts +0 -20
- package/dist/workflow/workflow-router.test.d.ts.map +0 -1
- package/dist/workflow/workflow-router.test.js +0 -607
- package/dist/workflow/workflow-router.test.js.map +0 -1
- package/dist/workflow/workflow-schema.d.ts.map +0 -1
- package/dist/workflow/workflow-schema.js.map +0 -1
- package/dist/workflow/workflow-schema.test.d.ts +0 -45
- package/dist/workflow/workflow-schema.test.d.ts.map +0 -1
- package/dist/workflow/workflow-schema.test.js +0 -512
- package/dist/workflow/workflow-schema.test.js.map +0 -1
- package/dist/workflow/workflow-stepped-schema.test.d.ts +0 -18
- package/dist/workflow/workflow-stepped-schema.test.d.ts.map +0 -1
- package/dist/workflow/workflow-stepped-schema.test.js +0 -608
- package/dist/workflow/workflow-stepped-schema.test.js.map +0 -1
|
@@ -1,727 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for Story 31-11: Consolidate SM Bookkeeping Subagents
|
|
3
|
-
*
|
|
4
|
-
* These tests define the contract for consolidating 6 SM subagents into 3:
|
|
5
|
-
*
|
|
6
|
-
* 1. generic-sm-setup - Combines sm-story-setup + sm-work-research
|
|
7
|
-
* - Mode: 'research' | 'setup'
|
|
8
|
-
* - Research: scan backlog, batch Jira query, recommend stories
|
|
9
|
-
* - Setup: claim Jira, create branches, write session file
|
|
10
|
-
*
|
|
11
|
-
* 2. generic-sm-finish - Combines sm-finish-bookkeeping + sm-finish-execution
|
|
12
|
-
* - Phase: 'preflight' | 'execute'
|
|
13
|
-
* - Preflight: PR check, lint fix, Jira status → JSON report
|
|
14
|
-
* - Execute: archive, Jira transition, cleanup → completion flags
|
|
15
|
-
*
|
|
16
|
-
* 3. generic-handoff with setup phase support
|
|
17
|
-
* - Add setup→red transition to generic-handoff
|
|
18
|
-
* - Gate type: manual (verifies context exists)
|
|
19
|
-
*
|
|
20
|
-
* Run with: npm test
|
|
21
|
-
*/
|
|
22
|
-
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
23
|
-
import assert from 'node:assert';
|
|
24
|
-
import { mkdirSync, rmSync, existsSync, writeFileSync, readFileSync } from 'node:fs';
|
|
25
|
-
import { join, dirname } from 'node:path';
|
|
26
|
-
import { fileURLToPath } from 'node:url';
|
|
27
|
-
// Get directory for test fixtures
|
|
28
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
29
|
-
const TEST_DIR = join(__dirname, '__test_sm_subagents__');
|
|
30
|
-
// Import the generic-sm-setup module
|
|
31
|
-
import { researchBacklog, setupStory, checkEpicContext, createEpicContext } from './generic-sm-setup.js';
|
|
32
|
-
// SetupParams interface now includes checkEpicContext and contextDir fields
|
|
33
|
-
// Import the generic-sm-finish module
|
|
34
|
-
import { preflightCheck, executeFinish } from './generic-sm-finish.js';
|
|
35
|
-
// Import extended generic-handoff for setup phase
|
|
36
|
-
import { findCurrentPhase, getNextPhase, checkGate } from './generic-handoff.js';
|
|
37
|
-
// Test fixture: TDD workflow with setup phase
|
|
38
|
-
const TDD_WORKFLOW = {
|
|
39
|
-
name: 'tdd',
|
|
40
|
-
description: 'Test-driven development with code review',
|
|
41
|
-
version: '1.0.0',
|
|
42
|
-
phases: [
|
|
43
|
-
{ name: 'setup', agent: 'sm', output: ['session_file', 'branches', 'story_context'] },
|
|
44
|
-
{
|
|
45
|
-
name: 'red',
|
|
46
|
-
agent: 'tea',
|
|
47
|
-
input: ['session_file', 'story_context'],
|
|
48
|
-
output: ['failing_tests'],
|
|
49
|
-
gate: { type: 'tests_fail', condition: 'All acceptance criteria have test coverage' }
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
name: 'green',
|
|
53
|
-
agent: 'dev',
|
|
54
|
-
input: ['failing_tests', 'story_context'],
|
|
55
|
-
output: ['implementation', 'passing_tests'],
|
|
56
|
-
gate: { type: 'tests_pass', condition: 'All tests passing, no skipped tests' }
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
name: 'review',
|
|
60
|
-
agent: 'reviewer',
|
|
61
|
-
input: ['implementation', 'passing_tests'],
|
|
62
|
-
output: ['approval'],
|
|
63
|
-
gate: { type: 'approval', condition: 'Code review approved, no blocking issues' }
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
name: 'finish',
|
|
67
|
-
agent: 'sm',
|
|
68
|
-
input: ['approval'],
|
|
69
|
-
output: ['archived_session', 'story_summary']
|
|
70
|
-
}
|
|
71
|
-
],
|
|
72
|
-
triggers: { types: ['feature', 'enhancement'], points: { min: 3 }, default: true }
|
|
73
|
-
};
|
|
74
|
-
describe('Generic SM Setup (31-11)', () => {
|
|
75
|
-
beforeEach(() => {
|
|
76
|
-
if (existsSync(TEST_DIR)) {
|
|
77
|
-
rmSync(TEST_DIR, { recursive: true });
|
|
78
|
-
}
|
|
79
|
-
mkdirSync(TEST_DIR, { recursive: true });
|
|
80
|
-
});
|
|
81
|
-
afterEach(() => {
|
|
82
|
-
if (existsSync(TEST_DIR)) {
|
|
83
|
-
rmSync(TEST_DIR, { recursive: true });
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
describe('researchBacklog() - Research mode', () => {
|
|
87
|
-
it('should return available stories from sprint YAML', async () => {
|
|
88
|
-
// AC: generic-sm-setup.md created combining setup + research
|
|
89
|
-
// Research mode scans backlog and returns available stories
|
|
90
|
-
const sprintYaml = `
|
|
91
|
-
sprint:
|
|
92
|
-
number: 10
|
|
93
|
-
goal: "Customizable workflows"
|
|
94
|
-
epics:
|
|
95
|
-
- id: 31
|
|
96
|
-
stories:
|
|
97
|
-
- id: "31-11"
|
|
98
|
-
title: "Consolidate SM subagents"
|
|
99
|
-
status: backlog
|
|
100
|
-
points: 3
|
|
101
|
-
- id: "31-10"
|
|
102
|
-
title: "Activate workflow handoffs"
|
|
103
|
-
status: done
|
|
104
|
-
assigned_to: "Keith"
|
|
105
|
-
`;
|
|
106
|
-
const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
|
|
107
|
-
writeFileSync(sprintPath, sprintYaml);
|
|
108
|
-
const result = await researchBacklog({ sprintPath });
|
|
109
|
-
assert.strictEqual(result.success, true, 'Research should succeed');
|
|
110
|
-
assert.ok(result.availableStories, 'Should have available stories');
|
|
111
|
-
assert.strictEqual(result.availableStories.length, 1, 'Should have 1 available story');
|
|
112
|
-
assert.strictEqual(result.availableStories[0].id, '31-11');
|
|
113
|
-
});
|
|
114
|
-
it('should exclude assigned stories from available list', async () => {
|
|
115
|
-
// Stories with assigned_to should be excluded from research results
|
|
116
|
-
const sprintYaml = `
|
|
117
|
-
sprint:
|
|
118
|
-
number: 10
|
|
119
|
-
epics:
|
|
120
|
-
- id: 31
|
|
121
|
-
stories:
|
|
122
|
-
- id: "31-11"
|
|
123
|
-
title: "Story A"
|
|
124
|
-
status: in_progress
|
|
125
|
-
assigned_to: "Someone"
|
|
126
|
-
- id: "31-12"
|
|
127
|
-
title: "Story B"
|
|
128
|
-
status: backlog
|
|
129
|
-
`;
|
|
130
|
-
const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
|
|
131
|
-
writeFileSync(sprintPath, sprintYaml);
|
|
132
|
-
const result = await researchBacklog({ sprintPath });
|
|
133
|
-
assert.strictEqual(result.availableStories.length, 1);
|
|
134
|
-
assert.strictEqual(result.availableStories[0].id, '31-12');
|
|
135
|
-
});
|
|
136
|
-
it('should sort stories by priority then points', async () => {
|
|
137
|
-
const sprintYaml = `
|
|
138
|
-
sprint:
|
|
139
|
-
number: 10
|
|
140
|
-
epics:
|
|
141
|
-
- id: 31
|
|
142
|
-
stories:
|
|
143
|
-
- id: "31-a"
|
|
144
|
-
title: "Low priority"
|
|
145
|
-
status: backlog
|
|
146
|
-
points: 2
|
|
147
|
-
priority: P2
|
|
148
|
-
- id: "31-b"
|
|
149
|
-
title: "High priority small"
|
|
150
|
-
status: backlog
|
|
151
|
-
points: 2
|
|
152
|
-
priority: P1
|
|
153
|
-
- id: "31-c"
|
|
154
|
-
title: "High priority large"
|
|
155
|
-
status: backlog
|
|
156
|
-
points: 5
|
|
157
|
-
priority: P1
|
|
158
|
-
`;
|
|
159
|
-
const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
|
|
160
|
-
writeFileSync(sprintPath, sprintYaml);
|
|
161
|
-
const result = await researchBacklog({ sprintPath });
|
|
162
|
-
// P1 stories first, then sorted by points ascending
|
|
163
|
-
assert.strictEqual(result.availableStories[0].id, '31-b', 'P1 2pt should be first');
|
|
164
|
-
assert.strictEqual(result.availableStories[1].id, '31-c', 'P1 5pt should be second');
|
|
165
|
-
assert.strictEqual(result.availableStories[2].id, '31-a', 'P2 should be last');
|
|
166
|
-
});
|
|
167
|
-
it('should include sprint metadata in result', async () => {
|
|
168
|
-
const sprintYaml = `
|
|
169
|
-
sprint:
|
|
170
|
-
number: 10
|
|
171
|
-
goal: "Test sprint goal"
|
|
172
|
-
summary:
|
|
173
|
-
completed_points: 37
|
|
174
|
-
total_points: 60
|
|
175
|
-
epics: []
|
|
176
|
-
`;
|
|
177
|
-
const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
|
|
178
|
-
writeFileSync(sprintPath, sprintYaml);
|
|
179
|
-
const result = await researchBacklog({ sprintPath });
|
|
180
|
-
assert.strictEqual(result.sprintNumber, 10);
|
|
181
|
-
assert.strictEqual(result.sprintGoal, 'Test sprint goal');
|
|
182
|
-
assert.strictEqual(result.completedPoints, 37);
|
|
183
|
-
assert.strictEqual(result.totalPoints, 60);
|
|
184
|
-
});
|
|
185
|
-
it('should handle missing sprint file gracefully', async () => {
|
|
186
|
-
const result = await researchBacklog({
|
|
187
|
-
sprintPath: join(TEST_DIR, 'nonexistent.yaml')
|
|
188
|
-
});
|
|
189
|
-
assert.strictEqual(result.success, false);
|
|
190
|
-
assert.ok(result.error, 'Should have error message');
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
describe('setupStory() - Setup mode', () => {
|
|
194
|
-
it('should create session file with story context', async () => {
|
|
195
|
-
// AC: generic-sm-setup.md created combining setup + research
|
|
196
|
-
// Setup mode creates session file, branches, claims Jira
|
|
197
|
-
const sessionDir = join(TEST_DIR, '.session');
|
|
198
|
-
mkdirSync(sessionDir, { recursive: true });
|
|
199
|
-
const result = await setupStory({
|
|
200
|
-
storyId: '31-11',
|
|
201
|
-
title: 'Consolidate SM subagents',
|
|
202
|
-
points: 3,
|
|
203
|
-
epic: 31,
|
|
204
|
-
repos: 'pennyfarthing',
|
|
205
|
-
sessionDir,
|
|
206
|
-
workflow: 'tdd',
|
|
207
|
-
assignee: 'Keith',
|
|
208
|
-
jiraKey: 'MSSCI-11616'
|
|
209
|
-
});
|
|
210
|
-
assert.strictEqual(result.success, true, 'Setup should succeed');
|
|
211
|
-
assert.ok(result.sessionFile, 'Should return session file path');
|
|
212
|
-
const sessionPath = join(sessionDir, '31-11-session.md');
|
|
213
|
-
assert.ok(existsSync(sessionPath), 'Session file should exist');
|
|
214
|
-
const content = readFileSync(sessionPath, 'utf-8');
|
|
215
|
-
assert.ok(content.includes('31-11'), 'Should contain story ID');
|
|
216
|
-
assert.ok(content.includes('Consolidate SM subagents'), 'Should contain title');
|
|
217
|
-
assert.ok(content.includes('## Workflow Tracking'), 'Should have workflow section');
|
|
218
|
-
assert.ok(content.includes('**Phase:** setup'), 'Should start in setup phase');
|
|
219
|
-
});
|
|
220
|
-
it('should include acceptance criteria in session file', async () => {
|
|
221
|
-
const sessionDir = join(TEST_DIR, '.session');
|
|
222
|
-
mkdirSync(sessionDir, { recursive: true });
|
|
223
|
-
const result = await setupStory({
|
|
224
|
-
storyId: '31-11',
|
|
225
|
-
title: 'Test story',
|
|
226
|
-
points: 3,
|
|
227
|
-
epic: 31,
|
|
228
|
-
repos: 'pennyfarthing',
|
|
229
|
-
sessionDir,
|
|
230
|
-
workflow: 'tdd',
|
|
231
|
-
acceptanceCriteria: [
|
|
232
|
-
'AC1: First criterion',
|
|
233
|
-
'AC2: Second criterion'
|
|
234
|
-
]
|
|
235
|
-
});
|
|
236
|
-
const content = readFileSync(result.sessionFile, 'utf-8');
|
|
237
|
-
assert.ok(content.includes('## Acceptance Criteria'), 'Should have AC section');
|
|
238
|
-
assert.ok(content.includes('AC1: First criterion'), 'Should include first AC');
|
|
239
|
-
assert.ok(content.includes('AC2: Second criterion'), 'Should include second AC');
|
|
240
|
-
});
|
|
241
|
-
it('should calculate branch name from story ID and slug', async () => {
|
|
242
|
-
const sessionDir = join(TEST_DIR, '.session');
|
|
243
|
-
mkdirSync(sessionDir, { recursive: true });
|
|
244
|
-
const result = await setupStory({
|
|
245
|
-
storyId: '31-11',
|
|
246
|
-
title: 'Consolidate SM subagents',
|
|
247
|
-
points: 3,
|
|
248
|
-
epic: 31,
|
|
249
|
-
repos: 'pennyfarthing',
|
|
250
|
-
sessionDir,
|
|
251
|
-
workflow: 'tdd'
|
|
252
|
-
});
|
|
253
|
-
assert.ok(result.branchName, 'Should return branch name');
|
|
254
|
-
assert.strictEqual(result.branchName, 'feat/31-11-consolidate-sm-subagents', 'Branch name should follow pattern');
|
|
255
|
-
});
|
|
256
|
-
it('should fail if session file already exists', async () => {
|
|
257
|
-
const sessionDir = join(TEST_DIR, '.session');
|
|
258
|
-
mkdirSync(sessionDir, { recursive: true });
|
|
259
|
-
// Create existing session file
|
|
260
|
-
const existingPath = join(sessionDir, '31-11-session.md');
|
|
261
|
-
writeFileSync(existingPath, '# Existing session');
|
|
262
|
-
const result = await setupStory({
|
|
263
|
-
storyId: '31-11',
|
|
264
|
-
title: 'Test story',
|
|
265
|
-
points: 3,
|
|
266
|
-
epic: 31,
|
|
267
|
-
repos: 'pennyfarthing',
|
|
268
|
-
sessionDir,
|
|
269
|
-
workflow: 'tdd'
|
|
270
|
-
});
|
|
271
|
-
assert.strictEqual(result.success, false);
|
|
272
|
-
assert.ok(result.error?.includes('exists'), 'Should mention existing file');
|
|
273
|
-
});
|
|
274
|
-
it('should include workflow tracking with Phase History table', async () => {
|
|
275
|
-
const sessionDir = join(TEST_DIR, '.session');
|
|
276
|
-
mkdirSync(sessionDir, { recursive: true });
|
|
277
|
-
const result = await setupStory({
|
|
278
|
-
storyId: '31-11',
|
|
279
|
-
title: 'Test story',
|
|
280
|
-
points: 3,
|
|
281
|
-
epic: 31,
|
|
282
|
-
repos: 'pennyfarthing',
|
|
283
|
-
sessionDir,
|
|
284
|
-
workflow: 'tdd'
|
|
285
|
-
});
|
|
286
|
-
const content = readFileSync(result.sessionFile, 'utf-8');
|
|
287
|
-
assert.ok(content.includes('### Phase History'), 'Should have Phase History');
|
|
288
|
-
assert.ok(content.includes('| Phase | Started | Ended | Duration |'), 'Should have table header');
|
|
289
|
-
assert.ok(content.includes('| setup |'), 'Should have setup phase row');
|
|
290
|
-
});
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
describe('Generic SM Finish (31-11)', () => {
|
|
294
|
-
beforeEach(() => {
|
|
295
|
-
if (existsSync(TEST_DIR)) {
|
|
296
|
-
rmSync(TEST_DIR, { recursive: true });
|
|
297
|
-
}
|
|
298
|
-
mkdirSync(TEST_DIR, { recursive: true });
|
|
299
|
-
});
|
|
300
|
-
afterEach(() => {
|
|
301
|
-
if (existsSync(TEST_DIR)) {
|
|
302
|
-
rmSync(TEST_DIR, { recursive: true });
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
describe('preflightCheck() - Preflight phase', () => {
|
|
306
|
-
it('should return JSON report with PR status', async () => {
|
|
307
|
-
// AC: generic-sm-finish.md created combining bookkeeping + execution
|
|
308
|
-
// Preflight phase checks PR, lint, Jira status
|
|
309
|
-
const result = await preflightCheck({
|
|
310
|
-
storyId: '31-11',
|
|
311
|
-
repos: 'pennyfarthing',
|
|
312
|
-
branch: 'feat/31-11-consolidate-sm-subagents',
|
|
313
|
-
jiraKey: 'MSSCI-11616',
|
|
314
|
-
projectRoot: TEST_DIR
|
|
315
|
-
});
|
|
316
|
-
assert.ok(result, 'Should return result');
|
|
317
|
-
assert.ok('prStatus' in result, 'Should have prStatus');
|
|
318
|
-
assert.ok('lintStatus' in result, 'Should have lintStatus');
|
|
319
|
-
assert.ok('jiraStatus' in result, 'Should have jiraStatus');
|
|
320
|
-
assert.ok('readyToFinish' in result, 'Should have readyToFinish flag');
|
|
321
|
-
});
|
|
322
|
-
it('should report merged PR as ready', async () => {
|
|
323
|
-
// Mock a scenario where PR is merged
|
|
324
|
-
const result = await preflightCheck({
|
|
325
|
-
storyId: '31-11',
|
|
326
|
-
repos: 'pennyfarthing',
|
|
327
|
-
branch: 'feat/31-11-test',
|
|
328
|
-
jiraKey: 'MSSCI-11616',
|
|
329
|
-
projectRoot: TEST_DIR,
|
|
330
|
-
// For testing: inject mock PR status
|
|
331
|
-
_mockPrStatus: 'merged'
|
|
332
|
-
});
|
|
333
|
-
assert.strictEqual(result.prStatus.pennyfarthing, 'merged');
|
|
334
|
-
});
|
|
335
|
-
it('should report open PR as warning', async () => {
|
|
336
|
-
const result = await preflightCheck({
|
|
337
|
-
storyId: '31-11',
|
|
338
|
-
repos: 'pennyfarthing',
|
|
339
|
-
branch: 'feat/31-11-test',
|
|
340
|
-
jiraKey: 'MSSCI-11616',
|
|
341
|
-
projectRoot: TEST_DIR,
|
|
342
|
-
_mockPrStatus: 'open'
|
|
343
|
-
});
|
|
344
|
-
assert.strictEqual(result.prStatus.pennyfarthing, 'open');
|
|
345
|
-
assert.ok(result.warnings?.some(w => w.includes('PR')), 'Should warn about open PR');
|
|
346
|
-
});
|
|
347
|
-
it('should check acceptance criteria completion', async () => {
|
|
348
|
-
const sessionDir = join(TEST_DIR, '.session');
|
|
349
|
-
mkdirSync(sessionDir, { recursive: true });
|
|
350
|
-
// Create session file with ACs
|
|
351
|
-
writeFileSync(join(sessionDir, '31-11-session.md'), `
|
|
352
|
-
# Story 31-11
|
|
353
|
-
|
|
354
|
-
## Acceptance Criteria
|
|
355
|
-
- [x] AC1: First done
|
|
356
|
-
- [x] AC2: Second done
|
|
357
|
-
- [ ] AC3: Third not done
|
|
358
|
-
`);
|
|
359
|
-
const result = await preflightCheck({
|
|
360
|
-
storyId: '31-11',
|
|
361
|
-
repos: 'pennyfarthing',
|
|
362
|
-
branch: 'feat/31-11-test',
|
|
363
|
-
projectRoot: TEST_DIR
|
|
364
|
-
});
|
|
365
|
-
assert.strictEqual(result.acceptanceCriteria.total, 3);
|
|
366
|
-
assert.strictEqual(result.acceptanceCriteria.checked, 2);
|
|
367
|
-
assert.strictEqual(result.acceptanceCriteria.complete, false);
|
|
368
|
-
});
|
|
369
|
-
it('should return structured issues array', async () => {
|
|
370
|
-
const result = await preflightCheck({
|
|
371
|
-
storyId: '31-11',
|
|
372
|
-
repos: 'pennyfarthing',
|
|
373
|
-
branch: 'feat/31-11-test',
|
|
374
|
-
projectRoot: TEST_DIR,
|
|
375
|
-
_mockPrStatus: 'NO_PR'
|
|
376
|
-
});
|
|
377
|
-
assert.ok(Array.isArray(result.issues), 'Should have issues array');
|
|
378
|
-
if (result.issues.length > 0) {
|
|
379
|
-
assert.ok(result.issues[0].type, 'Issue should have type');
|
|
380
|
-
assert.ok(result.issues[0].message, 'Issue should have message');
|
|
381
|
-
}
|
|
382
|
-
});
|
|
383
|
-
});
|
|
384
|
-
describe('executeFinish() - Execute phase', () => {
|
|
385
|
-
it('should archive session file to sprint/archive', async () => {
|
|
386
|
-
// AC: generic-sm-finish.md created combining bookkeeping + execution
|
|
387
|
-
// Execute phase archives session, transitions Jira, cleans up
|
|
388
|
-
const sessionDir = join(TEST_DIR, '.session');
|
|
389
|
-
const archiveDir = join(TEST_DIR, 'sprint', 'archive');
|
|
390
|
-
mkdirSync(sessionDir, { recursive: true });
|
|
391
|
-
mkdirSync(archiveDir, { recursive: true });
|
|
392
|
-
// Create session file
|
|
393
|
-
const sessionContent = '# Story 31-11 Session\n\nContent here';
|
|
394
|
-
writeFileSync(join(sessionDir, '31-11-session.md'), sessionContent);
|
|
395
|
-
const result = await executeFinish({
|
|
396
|
-
storyId: '31-11',
|
|
397
|
-
storyTitle: 'Consolidate SM subagents',
|
|
398
|
-
sessionDir,
|
|
399
|
-
archiveDir,
|
|
400
|
-
summaryContent: '## Summary\n\nStory complete.'
|
|
401
|
-
});
|
|
402
|
-
assert.strictEqual(result.success, true);
|
|
403
|
-
assert.ok(result.archivePath, 'Should return archive path');
|
|
404
|
-
assert.ok(existsSync(result.archivePath), 'Archive file should exist');
|
|
405
|
-
});
|
|
406
|
-
it('should remove session file after archiving', async () => {
|
|
407
|
-
const sessionDir = join(TEST_DIR, '.session');
|
|
408
|
-
const archiveDir = join(TEST_DIR, 'sprint', 'archive');
|
|
409
|
-
mkdirSync(sessionDir, { recursive: true });
|
|
410
|
-
mkdirSync(archiveDir, { recursive: true });
|
|
411
|
-
const sessionPath = join(sessionDir, '31-11-session.md');
|
|
412
|
-
writeFileSync(sessionPath, '# Session content');
|
|
413
|
-
await executeFinish({
|
|
414
|
-
storyId: '31-11',
|
|
415
|
-
storyTitle: 'Test',
|
|
416
|
-
sessionDir,
|
|
417
|
-
archiveDir,
|
|
418
|
-
summaryContent: '## Summary'
|
|
419
|
-
});
|
|
420
|
-
assert.ok(!existsSync(sessionPath), 'Session file should be removed');
|
|
421
|
-
});
|
|
422
|
-
it('should write summary file to sprint/context', async () => {
|
|
423
|
-
const sessionDir = join(TEST_DIR, '.session');
|
|
424
|
-
const archiveDir = join(TEST_DIR, 'sprint', 'archive');
|
|
425
|
-
const contextDir = join(TEST_DIR, 'sprint', 'context');
|
|
426
|
-
mkdirSync(sessionDir, { recursive: true });
|
|
427
|
-
mkdirSync(archiveDir, { recursive: true });
|
|
428
|
-
mkdirSync(contextDir, { recursive: true });
|
|
429
|
-
writeFileSync(join(sessionDir, '31-11-session.md'), '# Session');
|
|
430
|
-
const result = await executeFinish({
|
|
431
|
-
storyId: '31-11',
|
|
432
|
-
storyTitle: 'Consolidate SM subagents',
|
|
433
|
-
sessionDir,
|
|
434
|
-
archiveDir,
|
|
435
|
-
contextDir,
|
|
436
|
-
summaryContent: '## Summary\n\nKey learnings here.'
|
|
437
|
-
});
|
|
438
|
-
assert.ok(result.summaryPath, 'Should return summary path');
|
|
439
|
-
const summaryPath = join(contextDir, 'story-31-11-summary.md');
|
|
440
|
-
assert.ok(existsSync(summaryPath), 'Summary file should exist');
|
|
441
|
-
});
|
|
442
|
-
it('should return completion flags', async () => {
|
|
443
|
-
const sessionDir = join(TEST_DIR, '.session');
|
|
444
|
-
const archiveDir = join(TEST_DIR, 'sprint', 'archive');
|
|
445
|
-
mkdirSync(sessionDir, { recursive: true });
|
|
446
|
-
mkdirSync(archiveDir, { recursive: true });
|
|
447
|
-
writeFileSync(join(sessionDir, '31-11-session.md'), '# Session');
|
|
448
|
-
const result = await executeFinish({
|
|
449
|
-
storyId: '31-11',
|
|
450
|
-
storyTitle: 'Test',
|
|
451
|
-
sessionDir,
|
|
452
|
-
archiveDir,
|
|
453
|
-
summaryContent: '## Summary'
|
|
454
|
-
});
|
|
455
|
-
assert.ok('archived' in result, 'Should have archived flag');
|
|
456
|
-
assert.ok('sessionCleared' in result, 'Should have sessionCleared flag');
|
|
457
|
-
assert.strictEqual(result.archived, true);
|
|
458
|
-
assert.strictEqual(result.sessionCleared, true);
|
|
459
|
-
});
|
|
460
|
-
it('should include timestamp in archive filename', async () => {
|
|
461
|
-
const sessionDir = join(TEST_DIR, '.session');
|
|
462
|
-
const archiveDir = join(TEST_DIR, 'sprint', 'archive');
|
|
463
|
-
mkdirSync(sessionDir, { recursive: true });
|
|
464
|
-
mkdirSync(archiveDir, { recursive: true });
|
|
465
|
-
writeFileSync(join(sessionDir, '31-11-session.md'), '# Session');
|
|
466
|
-
const result = await executeFinish({
|
|
467
|
-
storyId: '31-11',
|
|
468
|
-
storyTitle: 'Test',
|
|
469
|
-
sessionDir,
|
|
470
|
-
archiveDir,
|
|
471
|
-
summaryContent: '## Summary'
|
|
472
|
-
});
|
|
473
|
-
// Archive path should be like: story-31-11-20260114.md
|
|
474
|
-
assert.ok(result.archivePath?.match(/story-31-11-\d{8}\.md$/), `Archive path should include date: ${result.archivePath}`);
|
|
475
|
-
});
|
|
476
|
-
});
|
|
477
|
-
});
|
|
478
|
-
describe('Generic Handoff - Setup Phase Support (31-11)', () => {
|
|
479
|
-
describe('setup phase in TDD workflow', () => {
|
|
480
|
-
it('should find setup phase with no gate', () => {
|
|
481
|
-
// AC: sm-handoff folded into generic-handoff with setup phase support
|
|
482
|
-
const phase = findCurrentPhase(TDD_WORKFLOW, 'setup');
|
|
483
|
-
assert.ok(phase, 'Should find setup phase');
|
|
484
|
-
assert.strictEqual(phase.name, 'setup');
|
|
485
|
-
assert.strictEqual(phase.agent, 'sm');
|
|
486
|
-
assert.strictEqual(phase.gate, undefined, 'Setup has no gate');
|
|
487
|
-
});
|
|
488
|
-
it('should transition from setup to red (TEA)', () => {
|
|
489
|
-
// AC: sm-handoff folded into generic-handoff with setup phase support
|
|
490
|
-
const next = getNextPhase(TDD_WORKFLOW, 'setup');
|
|
491
|
-
assert.ok(next, 'Should find next phase');
|
|
492
|
-
assert.strictEqual(next.name, 'red');
|
|
493
|
-
assert.strictEqual(next.agent, 'tea');
|
|
494
|
-
});
|
|
495
|
-
it('should pass gate check for setup phase (no gate = pass)', () => {
|
|
496
|
-
// Setup phase has no gate - should always pass
|
|
497
|
-
const result = checkGate(TDD_WORKFLOW, 'setup', {});
|
|
498
|
-
assert.strictEqual(result.passed, true, 'Setup gate should pass');
|
|
499
|
-
assert.strictEqual(result.gateType, undefined, 'No gate type for setup');
|
|
500
|
-
});
|
|
501
|
-
it('should support manual gate type for explicit setup gates', () => {
|
|
502
|
-
// For workflows that want explicit manual gate on setup
|
|
503
|
-
const workflowWithManualSetup = {
|
|
504
|
-
name: 'explicit-setup',
|
|
505
|
-
phases: [
|
|
506
|
-
{
|
|
507
|
-
name: 'setup',
|
|
508
|
-
agent: 'sm',
|
|
509
|
-
gate: { type: 'manual', condition: 'Context prepared' }
|
|
510
|
-
},
|
|
511
|
-
{ name: 'implement', agent: 'dev' }
|
|
512
|
-
]
|
|
513
|
-
};
|
|
514
|
-
const result = checkGate(workflowWithManualSetup, 'setup', {});
|
|
515
|
-
assert.strictEqual(result.passed, true, 'Manual gate should pass');
|
|
516
|
-
assert.strictEqual(result.gateType, 'manual');
|
|
517
|
-
});
|
|
518
|
-
});
|
|
519
|
-
describe('finish phase in TDD workflow', () => {
|
|
520
|
-
it('should find finish phase with no gate', () => {
|
|
521
|
-
const phase = findCurrentPhase(TDD_WORKFLOW, 'finish');
|
|
522
|
-
assert.ok(phase, 'Should find finish phase');
|
|
523
|
-
assert.strictEqual(phase.name, 'finish');
|
|
524
|
-
assert.strictEqual(phase.agent, 'sm');
|
|
525
|
-
assert.strictEqual(phase.gate, undefined, 'Finish has no gate');
|
|
526
|
-
});
|
|
527
|
-
it('should return null for next phase from finish', () => {
|
|
528
|
-
const next = getNextPhase(TDD_WORKFLOW, 'finish');
|
|
529
|
-
assert.strictEqual(next, null, 'No next phase after finish');
|
|
530
|
-
});
|
|
531
|
-
});
|
|
532
|
-
describe('full TDD workflow transitions', () => {
|
|
533
|
-
it('should complete setup → red → green → review → finish', () => {
|
|
534
|
-
// AC: Both new-work and finish-story flows work end-to-end
|
|
535
|
-
// setup → red
|
|
536
|
-
let next = getNextPhase(TDD_WORKFLOW, 'setup');
|
|
537
|
-
assert.strictEqual(next?.name, 'red');
|
|
538
|
-
// red → green (after tests written)
|
|
539
|
-
next = getNextPhase(TDD_WORKFLOW, 'red');
|
|
540
|
-
assert.strictEqual(next?.name, 'green');
|
|
541
|
-
// green → review (after tests pass)
|
|
542
|
-
next = getNextPhase(TDD_WORKFLOW, 'green');
|
|
543
|
-
assert.strictEqual(next?.name, 'review');
|
|
544
|
-
// review → finish (after approval)
|
|
545
|
-
next = getNextPhase(TDD_WORKFLOW, 'review', { verdict: 'approved' });
|
|
546
|
-
assert.strictEqual(next?.name, 'finish');
|
|
547
|
-
// finish → null (done)
|
|
548
|
-
next = getNextPhase(TDD_WORKFLOW, 'finish');
|
|
549
|
-
assert.strictEqual(next, null);
|
|
550
|
-
});
|
|
551
|
-
});
|
|
552
|
-
});
|
|
553
|
-
describe('Deprecated File Removal (31-11)', () => {
|
|
554
|
-
it('should verify deprecated handoff files are marked for removal', () => {
|
|
555
|
-
// AC: Deprecated handoff files removed (tea-handoff, dev-handoff, reviewer-handoff-*)
|
|
556
|
-
const deprecatedFiles = [
|
|
557
|
-
'tea-handoff.md',
|
|
558
|
-
'dev-handoff.md',
|
|
559
|
-
'reviewer-handoff-approve.md',
|
|
560
|
-
'reviewer-handoff-reject.md'
|
|
561
|
-
];
|
|
562
|
-
// These files should NOT exist after Dev implements
|
|
563
|
-
// This test documents the requirement
|
|
564
|
-
for (const file of deprecatedFiles) {
|
|
565
|
-
assert.ok(deprecatedFiles.includes(file), `${file} is marked for removal`);
|
|
566
|
-
}
|
|
567
|
-
});
|
|
568
|
-
});
|
|
569
|
-
/**
|
|
570
|
-
* Story 38-10: SM Gate for Epic Technical Context
|
|
571
|
-
*
|
|
572
|
-
* Tests for the epic context gate that ensures stories don't start
|
|
573
|
-
* without understanding their epic's technical landscape.
|
|
574
|
-
*/
|
|
575
|
-
describe('Epic Context Gate (38-10)', () => {
|
|
576
|
-
beforeEach(() => {
|
|
577
|
-
if (existsSync(TEST_DIR)) {
|
|
578
|
-
rmSync(TEST_DIR, { recursive: true });
|
|
579
|
-
}
|
|
580
|
-
mkdirSync(TEST_DIR, { recursive: true });
|
|
581
|
-
});
|
|
582
|
-
afterEach(() => {
|
|
583
|
-
if (existsSync(TEST_DIR)) {
|
|
584
|
-
rmSync(TEST_DIR, { recursive: true });
|
|
585
|
-
}
|
|
586
|
-
});
|
|
587
|
-
describe('checkEpicContext() - Context validation', () => {
|
|
588
|
-
it('should return true when epic context file exists', async () => {
|
|
589
|
-
// AC1: SM checks for sprint/context/context-epic-{N}.md before story setup
|
|
590
|
-
const contextDir = join(TEST_DIR, 'sprint', 'context');
|
|
591
|
-
mkdirSync(contextDir, { recursive: true });
|
|
592
|
-
// Create epic context file
|
|
593
|
-
writeFileSync(join(contextDir, 'context-epic-38.md'), '# Epic 38 Context\n\nTechnical overview here.');
|
|
594
|
-
const result = await checkEpicContext({
|
|
595
|
-
epicId: 38,
|
|
596
|
-
contextDir
|
|
597
|
-
});
|
|
598
|
-
assert.strictEqual(result.exists, true, 'Should detect existing context');
|
|
599
|
-
assert.ok(result.path, 'Should return file path');
|
|
600
|
-
});
|
|
601
|
-
it('should return false with message when epic context missing', async () => {
|
|
602
|
-
// AC2: Missing epic context blocks story setup with clear message
|
|
603
|
-
const contextDir = join(TEST_DIR, 'sprint', 'context');
|
|
604
|
-
mkdirSync(contextDir, { recursive: true });
|
|
605
|
-
// No context file created
|
|
606
|
-
const result = await checkEpicContext({
|
|
607
|
-
epicId: 38,
|
|
608
|
-
contextDir
|
|
609
|
-
});
|
|
610
|
-
assert.strictEqual(result.exists, false, 'Should detect missing context');
|
|
611
|
-
assert.ok(result.message, 'Should provide a message');
|
|
612
|
-
assert.ok(result.message?.includes('38'), 'Message should reference epic ID');
|
|
613
|
-
});
|
|
614
|
-
it('should provide path hint for missing context', async () => {
|
|
615
|
-
// AC2: Missing epic context blocks with clear message showing expected path
|
|
616
|
-
const contextDir = join(TEST_DIR, 'sprint', 'context');
|
|
617
|
-
mkdirSync(contextDir, { recursive: true });
|
|
618
|
-
const result = await checkEpicContext({
|
|
619
|
-
epicId: 42,
|
|
620
|
-
contextDir
|
|
621
|
-
});
|
|
622
|
-
assert.strictEqual(result.exists, false);
|
|
623
|
-
assert.ok(result.expectedPath, 'Should provide expected path');
|
|
624
|
-
assert.ok(result.expectedPath?.includes('context-epic-42.md'), 'Expected path should include filename');
|
|
625
|
-
});
|
|
626
|
-
});
|
|
627
|
-
describe('createEpicContext() - Context creation', () => {
|
|
628
|
-
it('should create epic context file from template', async () => {
|
|
629
|
-
// AC3: SM can create epic context (researches epic, writes file)
|
|
630
|
-
const contextDir = join(TEST_DIR, 'sprint', 'context');
|
|
631
|
-
mkdirSync(contextDir, { recursive: true });
|
|
632
|
-
const result = await createEpicContext({
|
|
633
|
-
epicId: 38,
|
|
634
|
-
epicTitle: 'Agent File Modernization',
|
|
635
|
-
contextDir,
|
|
636
|
-
content: '## Technical Landscape\n\nThis epic modernizes agent files.'
|
|
637
|
-
});
|
|
638
|
-
assert.strictEqual(result.success, true, 'Creation should succeed');
|
|
639
|
-
assert.ok(result.path, 'Should return file path');
|
|
640
|
-
const filePath = join(contextDir, 'context-epic-38.md');
|
|
641
|
-
assert.ok(existsSync(filePath), 'File should exist');
|
|
642
|
-
const content = readFileSync(filePath, 'utf-8');
|
|
643
|
-
assert.ok(content.includes('Agent File Modernization'), 'Should include epic title');
|
|
644
|
-
});
|
|
645
|
-
it('should use template structure when creating context', async () => {
|
|
646
|
-
// AC4: Epic context template exists and is documented
|
|
647
|
-
const contextDir = join(TEST_DIR, 'sprint', 'context');
|
|
648
|
-
mkdirSync(contextDir, { recursive: true });
|
|
649
|
-
const result = await createEpicContext({
|
|
650
|
-
epicId: 38,
|
|
651
|
-
epicTitle: 'Agent File Modernization',
|
|
652
|
-
contextDir
|
|
653
|
-
// No content provided - should use template
|
|
654
|
-
});
|
|
655
|
-
assert.strictEqual(result.success, true);
|
|
656
|
-
const content = readFileSync(result.path, 'utf-8');
|
|
657
|
-
// Template should have standard sections
|
|
658
|
-
assert.ok(content.includes('# Epic 38'), 'Should have epic header');
|
|
659
|
-
assert.ok(content.includes('## Epic Overview') || content.includes('## Technical Landscape'), 'Should have overview section');
|
|
660
|
-
});
|
|
661
|
-
it('should not overwrite existing epic context', async () => {
|
|
662
|
-
// Safety check: don't clobber existing context
|
|
663
|
-
const contextDir = join(TEST_DIR, 'sprint', 'context');
|
|
664
|
-
mkdirSync(contextDir, { recursive: true });
|
|
665
|
-
// Create existing context
|
|
666
|
-
const existingContent = '# Existing Epic 38 Context\n\nValuable information here.';
|
|
667
|
-
writeFileSync(join(contextDir, 'context-epic-38.md'), existingContent);
|
|
668
|
-
const result = await createEpicContext({
|
|
669
|
-
epicId: 38,
|
|
670
|
-
epicTitle: 'New Title',
|
|
671
|
-
contextDir
|
|
672
|
-
});
|
|
673
|
-
assert.strictEqual(result.success, false, 'Should fail when file exists');
|
|
674
|
-
assert.ok(result.error?.includes('exists'), 'Error should mention existing file');
|
|
675
|
-
// Verify original content preserved
|
|
676
|
-
const content = readFileSync(join(contextDir, 'context-epic-38.md'), 'utf-8');
|
|
677
|
-
assert.ok(content.includes('Valuable information'), 'Original content should be preserved');
|
|
678
|
-
});
|
|
679
|
-
});
|
|
680
|
-
describe('setupStory() with epic context gate', () => {
|
|
681
|
-
it('should check epic context before setup when gate enabled', async () => {
|
|
682
|
-
// AC1: SM checks for context file before story setup
|
|
683
|
-
const sessionDir = join(TEST_DIR, '.session');
|
|
684
|
-
const contextDir = join(TEST_DIR, 'sprint', 'context');
|
|
685
|
-
mkdirSync(sessionDir, { recursive: true });
|
|
686
|
-
mkdirSync(contextDir, { recursive: true });
|
|
687
|
-
// No epic context file - should warn/block
|
|
688
|
-
const result = await setupStory({
|
|
689
|
-
storyId: '38-10',
|
|
690
|
-
title: 'SM gate for epic technical context',
|
|
691
|
-
points: 2,
|
|
692
|
-
epic: 38,
|
|
693
|
-
repos: 'pennyfarthing',
|
|
694
|
-
sessionDir,
|
|
695
|
-
workflow: 'tdd',
|
|
696
|
-
// New parameter to enable gate
|
|
697
|
-
checkEpicContext: true,
|
|
698
|
-
contextDir
|
|
699
|
-
});
|
|
700
|
-
// With gate enabled and no context, should warn (not hard fail initially)
|
|
701
|
-
assert.ok(result.warnings?.some((w) => w.includes('epic context')), 'Should warn about missing epic context');
|
|
702
|
-
});
|
|
703
|
-
it('should proceed when epic context exists', async () => {
|
|
704
|
-
// AC1: Gate passes when context file exists
|
|
705
|
-
const sessionDir = join(TEST_DIR, '.session');
|
|
706
|
-
const contextDir = join(TEST_DIR, 'sprint', 'context');
|
|
707
|
-
mkdirSync(sessionDir, { recursive: true });
|
|
708
|
-
mkdirSync(contextDir, { recursive: true });
|
|
709
|
-
// Create epic context
|
|
710
|
-
writeFileSync(join(contextDir, 'context-epic-38.md'), '# Epic 38\n\nContext here.');
|
|
711
|
-
const result = await setupStory({
|
|
712
|
-
storyId: '38-10-test',
|
|
713
|
-
title: 'SM gate for epic technical context',
|
|
714
|
-
points: 2,
|
|
715
|
-
epic: 38,
|
|
716
|
-
repos: 'pennyfarthing',
|
|
717
|
-
sessionDir,
|
|
718
|
-
workflow: 'tdd',
|
|
719
|
-
checkEpicContext: true,
|
|
720
|
-
contextDir
|
|
721
|
-
});
|
|
722
|
-
assert.strictEqual(result.success, true, 'Should succeed with context present');
|
|
723
|
-
assert.ok(!result.warnings?.some((w) => w.includes('epic context')), 'Should not warn when context exists');
|
|
724
|
-
});
|
|
725
|
-
});
|
|
726
|
-
});
|
|
727
|
-
//# sourceMappingURL=sm-subagents.test.js.map
|