karajan-code 4.70.3 → 5.0.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/README.md +2 -2
- package/package.json +6 -14
- package/packages/hu-board/package.json +1 -1
- package/packages/hu-board/public/app.js +4 -18
- package/packages/hu-board/public/index.html +5 -2
- package/packages/hu-board/public/styles.css +26 -0
- package/packages/hu-board/public/utils/api.js +0 -74
- package/packages/hu-board/public/utils/board-tour.js +62 -0
- package/packages/hu-board/public/utils/board-view.js +49 -144
- package/packages/hu-board/public/utils/command-launcher.js +8 -70
- package/packages/hu-board/public/utils/formatters.js +10 -0
- package/packages/hu-board/public/utils/governance-view.js +13 -1
- package/packages/hu-board/public/utils/hu-actions.js +17 -68
- package/packages/hu-board/public/utils/init-listeners.js +7 -8
- package/packages/hu-board/public/utils/log-panel.js +3 -16
- package/packages/hu-board/public/utils/maggle-mode.js +2 -12
- package/packages/hu-board/public/utils/modals.js +7 -0
- package/packages/hu-board/public/utils/project-picker-view.js +1 -1
- package/packages/hu-board/public/utils/server-push.js +3 -22
- package/packages/hu-board/public/utils/shared-board-notice.js +33 -0
- package/packages/hu-board/public/utils/story-detail-view.js +5 -19
- package/packages/hu-board/src/cleanup-zombies.js +0 -1
- package/packages/hu-board/src/command-runner.js +36 -25
- package/packages/hu-board/src/db-connection.js +282 -0
- package/packages/hu-board/src/db-paths.js +113 -0
- package/packages/hu-board/src/db-queries.js +188 -0
- package/packages/hu-board/src/db-tombstones.js +51 -0
- package/packages/hu-board/src/db.js +28 -619
- package/packages/hu-board/src/ephemeral-cleaner.js +2 -3
- package/packages/hu-board/src/hu-zombie-reaper.js +5 -1
- package/packages/hu-board/src/plan-mutations.js +3 -153
- package/packages/hu-board/src/plans-service.js +55 -0
- package/packages/hu-board/src/routes/api/commands.js +122 -0
- package/packages/hu-board/src/routes/api/plans.js +55 -0
- package/packages/hu-board/src/routes/api/projects.js +267 -0
- package/packages/hu-board/src/routes/api/prompts.js +135 -0
- package/packages/hu-board/src/routes/api/sessions.js +95 -0
- package/packages/hu-board/src/routes/api/stories.js +183 -0
- package/packages/hu-board/src/routes/api/system.js +241 -0
- package/packages/hu-board/src/routes/api/tombstones.js +38 -0
- package/packages/hu-board/src/routes/api.js +24 -1371
- package/packages/hu-board/src/routes/governance.js +23 -1
- package/packages/hu-board/src/routes/lanes.js +40 -0
- package/packages/hu-board/src/routes/rag.js +4 -3
- package/packages/hu-board/src/server.js +20 -34
- package/packages/hu-board/src/sync-files.js +318 -0
- package/packages/hu-board/src/sync-project.js +99 -0
- package/packages/hu-board/src/sync.js +22 -566
- package/packages/hu-board/src/zombie-reaper.js +1 -1
- package/scripts/dual-publish.mjs +19 -3
- package/scripts/postinstall.js +15 -2
- package/scripts/verify-pack-mcp.mjs +11 -7
- package/scripts/verify-pack.mjs +4 -4
- package/src/audit/audit-history.js +3 -2
- package/src/audit/dead-exports.js +68 -59
- package/src/audit/deterministic-summary.js +33 -13
- package/src/audit/npm-signatures.js +41 -0
- package/src/audit/sbom.js +35 -0
- package/src/checks/agent-token.js +112 -0
- package/src/checks/ai-surface.js +9 -7
- package/src/checks/config-files.js +8 -7
- package/src/checks/dir-setup.js +34 -1
- package/src/checks/native-build.js +21 -39
- package/src/checks/runner.js +128 -142
- package/src/cli/advanced-commands.js +6 -6
- package/src/cli/register-meta.js +42 -82
- package/src/cli/register-pipeline.js +8 -140
- package/src/cli/register-plan.js +9 -100
- package/src/cli/retired-commands.js +49 -0
- package/src/cli.js +18 -5
- package/src/commands/agent-run.js +40 -26
- package/src/commands/audit.js +127 -124
- package/src/commands/board.js +5 -9
- package/src/commands/check.js +29 -16
- package/src/commands/doctor.js +2 -0
- package/src/commands/env.js +176 -148
- package/src/commands/go.js +76 -55
- package/src/commands/harden.js +105 -86
- package/src/commands/hu-start.js +52 -0
- package/src/commands/hu.js +288 -221
- package/src/commands/identity.js +111 -78
- package/src/commands/init-atril.js +47 -0
- package/src/commands/init.js +11 -40
- package/src/commands/install-tools.js +70 -85
- package/src/commands/main-guard.js +78 -0
- package/src/commands/policy.js +226 -184
- package/src/commands/qmd.js +27 -21
- package/src/commands/rag.js +75 -48
- package/src/commands/review-gate.js +422 -339
- package/src/commands/start.js +4 -5
- package/src/commands/steward.js +96 -47
- package/src/commands/tournament.js +46 -37
- package/src/commands/worktree.js +94 -25
- package/src/config/loader.js +34 -17
- package/src/config/overrides.js +2 -1
- package/src/config/secrets.js +36 -0
- package/src/config.js +1 -0
- package/src/environment/adr.js +16 -2
- package/src/environment/board-access.js +11 -9
- package/src/git/karajan-label.js +20 -8
- package/src/guards/main-guard.js +54 -0
- package/src/harden/branch-protection.js +36 -3
- package/src/harden/hook-templates.js +16 -1
- package/src/harden/sentinel/posttooluse.mjs +18 -6
- package/src/harden/sentinel/pretooluse-sentinel.mjs +81 -35
- package/src/harden/sentinel/sentinel-lib.mjs +149 -23
- package/src/harden/sentinel/sentinel-shell.mjs +64 -0
- package/src/harden/sentinel/sessionstart.mjs +3 -2
- package/src/harden/sentinel/stop.mjs +5 -4
- package/src/harden/workflow-engine.js +12 -1
- package/src/harden/workflow-templates.js +1 -1
- package/src/hu/board-lookup.js +29 -0
- package/src/hu/gh-identity.js +23 -0
- package/src/hu/github-board.js +4 -2
- package/src/hu/lane-prune.js +15 -7
- package/src/hu/lane-setup.js +65 -0
- package/src/hu/lanes.js +66 -0
- package/src/hu/team-board-mirror.js +3 -2
- package/src/infrastructure/command-runner.js +1 -1
- package/src/infrastructure/file-system-service.js +1 -1
- package/src/mcp/handlers/direct-handlers.js +2 -292
- package/src/mcp/handlers/hu-handlers.js +44 -55
- package/src/mcp/handlers/management-handlers.js +2 -42
- package/src/mcp/server-handlers.js +16 -22
- package/src/mcp/tools.js +5 -200
- package/src/plan/lane-plans.js +54 -0
- package/src/plan/plan-store.js +7 -4
- package/src/policy/add.js +26 -12
- package/src/policy/adr-number-check.js +31 -0
- package/src/prompts/audit.js +299 -339
- package/src/prompts/card-context.js +10 -5
- package/src/prompts/reviewer.js +5 -2
- package/src/rag/auto-update.js +56 -43
- package/src/rag/bootstrap-index.js +28 -54
- package/src/rag/migrate.js +1 -1
- package/src/rag/onnx-fallback.js +2 -2
- package/src/rag/pid-lease.js +70 -0
- package/src/rag/project-store.js +2 -12
- package/src/review/card-first.js +57 -46
- package/src/review/dual-review.js +5 -4
- package/src/review/gate-gitignore.js +23 -4
- package/src/review/gate-hook.js +47 -0
- package/src/review/one-shot-review.js +51 -37
- package/src/review/publish-pr.js +18 -3
- package/src/review/repo-docs.js +73 -0
- package/src/review/repo-stack.js +59 -0
- package/src/review/reviewer-fallback.js +34 -3
- package/src/review/seal-commit.js +29 -0
- package/src/review/sonar-pregate.js +59 -44
- package/src/review/third-party-review.js +90 -16
- package/src/review/third-party-size.js +43 -0
- package/src/review/third-party-skills.js +54 -0
- package/src/review/verify-pr.js +2 -26
- package/src/roles/audit-role.js +106 -125
- package/src/skills/atril-skills.js +5 -4
- package/src/sonar/scanner.js +116 -101
- package/src/steward/invariants.js +5 -4
- package/src/steward/phantom-sweep.js +66 -0
- package/src/tournament/judge.js +60 -53
- package/src/utils/agent-home.js +33 -0
- package/src/utils/gh-account.js +37 -0
- package/src/utils/git.js +0 -13
- package/src/utils/home-migration.js +7 -1
- package/src/utils/main-checkout.js +53 -0
- package/src/utils/qmd-collection.js +3 -1
- package/src/utils/welcome.js +3 -3
- package/templates/{skills → atril/commands}/kj-architect.md +5 -0
- package/templates/{skills → atril/commands}/kj-audit.md +5 -0
- package/templates/{skills → atril/commands}/kj-board.md +5 -0
- package/templates/{skills → atril/commands}/kj-code.md +5 -0
- package/templates/{skills → atril/commands}/kj-csv-transform.md +5 -0
- package/templates/{skills → atril/commands}/kj-data-report.md +5 -0
- package/templates/{skills → atril/commands}/kj-discover.md +5 -0
- package/templates/{skills → atril/commands}/kj-rag-query.md +5 -0
- package/templates/{skills → atril/commands}/kj-review.md +5 -0
- package/templates/{skills → atril/commands}/kj-security.md +5 -0
- package/templates/{skills → atril/commands}/kj-sonar.md +5 -0
- package/templates/{skills → atril/commands}/kj-sql-analysis.md +5 -0
- package/templates/{skills → atril/commands}/kj-test.md +5 -0
- package/packages/hu-board/public/utils/run-launcher.js +0 -169
- package/packages/hu-board/src/run-tracker.js +0 -121
- package/src/activity-log.js +0 -59
- package/src/agents/host-agent.js +0 -38
- package/src/agents/model-registry.js +0 -142
- package/src/audit/plan-adherence.js +0 -224
- package/src/autonomy/arbiter-role.js +0 -71
- package/src/autonomy/arbiter.js +0 -22
- package/src/autonomy/decider.js +0 -18
- package/src/autonomy/decision-resolver.js +0 -58
- package/src/autonomy/outcome-report.js +0 -55
- package/src/autonomy/stage-ask.js +0 -30
- package/src/brain/decision-tracker.js +0 -125
- package/src/brain/decisor.js +0 -243
- package/src/brain/solomon-consult.js +0 -171
- package/src/brain/standby-scheduler.js +0 -8
- package/src/budget/comparison.js +0 -96
- package/src/canvas/parse-md.js +0 -238
- package/src/canvas/schema.js +0 -101
- package/src/canvas/to-flat.js +0 -85
- package/src/canvas/validate.js +0 -163
- package/src/ci/dispatch.js +0 -99
- package/src/ci/index.js +0 -3
- package/src/ci/pr-diff.js +0 -26
- package/src/cli/register-standby.js +0 -26
- package/src/commands/architect.js +0 -111
- package/src/commands/autorun.js +0 -49
- package/src/commands/researcher.js +0 -54
- package/src/commands/resume.js +0 -72
- package/src/commands/review.js +0 -89
- package/src/commands/run.js +0 -185
- package/src/commands/standby.js +0 -117
- package/src/commands/status.js +0 -36
- package/src/commands/triage.js +0 -78
- package/src/commands/undo.js +0 -62
- package/src/domains/domain-loader.js +0 -245
- package/src/domains/domain-registry.js +0 -140
- package/src/domains/domain-synthesizer.js +0 -129
- package/src/git/automation.js +0 -433
- package/src/git/coder-commit-guard.js +0 -40
- package/src/git/hu-automation.js +0 -243
- package/src/git/hu-snapshot.js +0 -10
- package/src/git/symlink-targets.js +0 -57
- package/src/golden/asserter.js +0 -92
- package/src/golden/loader.js +0 -52
- package/src/golden/runner.js +0 -92
- package/src/golden/schema.js +0 -41
- package/src/guards/duplicate-members.js +0 -86
- package/src/guards/intent-guard.js +0 -161
- package/src/guards/perf-guard.js +0 -126
- package/src/guards/policy-guard.js +0 -90
- package/src/guards/policy-resolver.js +0 -85
- package/src/guards/secret-redactor.js +0 -71
- package/src/hu/acceptance-runner.js +0 -138
- package/src/hu/lazy-planner.js +0 -109
- package/src/hu/parallel-executor.js +0 -94
- package/src/hu/routing-metrics.js +0 -17
- package/src/hu/split-validator.js +0 -143
- package/src/hu/splitting-detector.js +0 -158
- package/src/hu/splitting-generator.js +0 -175
- package/src/hu/store.js +0 -394
- package/src/infrastructure/mocks.js +0 -182
- package/src/mcp/handlers/run-handler.js +0 -318
- package/src/mcp/sovereignty-guard.js +0 -113
- package/src/mcp/tool-arg-normalizers.js +0 -16
- package/src/mutate/reviewer-signal.js +0 -76
- package/src/orchestrator/agent-fallback.js +0 -81
- package/src/orchestrator/brain-coordinator.js +0 -186
- package/src/orchestrator/ci-integration.js +0 -139
- package/src/orchestrator/config-init.js +0 -426
- package/src/orchestrator/direct-actions.js +0 -217
- package/src/orchestrator/drivers/error-recovery.js +0 -287
- package/src/orchestrator/drivers/init-context.js +0 -238
- package/src/orchestrator/drivers/iteration-loop.js +0 -382
- package/src/orchestrator/drivers/iteration-phases/coder-and-refactorer.js +0 -69
- package/src/orchestrator/drivers/iteration-phases/guards.js +0 -91
- package/src/orchestrator/drivers/iteration-phases/handle-approved.js +0 -29
- package/src/orchestrator/drivers/iteration-phases/quality-gates.js +0 -125
- package/src/orchestrator/drivers/iteration-phases/reviewer-gate.js +0 -62
- package/src/orchestrator/drivers/post-loop.js +0 -404
- package/src/orchestrator/drivers/pre-loop-phases/auto-hu-batch.js +0 -137
- package/src/orchestrator/drivers/pre-loop-phases/config-deprecations.js +0 -47
- package/src/orchestrator/drivers/pre-loop-phases/ensure-addyosmani-skills.js +0 -84
- package/src/orchestrator/drivers/pre-loop-phases/inject-loaded-plan.js +0 -393
- package/src/orchestrator/drivers/pre-loop.js +0 -524
- package/src/orchestrator/drivers/run-hu-batch.js +0 -468
- package/src/orchestrator/feedback-enrichment.js +0 -140
- package/src/orchestrator/feedback-queue.js +0 -151
- package/src/orchestrator/flow-control.js +0 -170
- package/src/orchestrator/flow-runner.js +0 -365
- package/src/orchestrator/fs-leak-detector.js +0 -228
- package/src/orchestrator/hu-scheduler.js +0 -114
- package/src/orchestrator/hu-sub-pipeline.js +0 -642
- package/src/orchestrator/iteration-gate.js +0 -73
- package/src/orchestrator/iteration-stages.js +0 -9
- package/src/orchestrator/parallel-limiter.js +0 -74
- package/src/orchestrator/pipeline-context.js +0 -72
- package/src/orchestrator/post-loop-stages.js +0 -468
- package/src/orchestrator/pre-loop-stages.js +0 -11
- package/src/orchestrator/preflight-checks.js +0 -487
- package/src/orchestrator/repair/repair-detector.js +0 -117
- package/src/orchestrator/repair/repair-runner.js +0 -142
- package/src/orchestrator/reviewer-fallback.js +0 -45
- package/src/orchestrator/role-output-compressor.js +0 -189
- package/src/orchestrator/session-journal.js +0 -308
- package/src/orchestrator/solomon-escalation.js +0 -331
- package/src/orchestrator/solomon-rules.js +0 -212
- package/src/orchestrator/stages/acceptance-coverage-stage.js +0 -47
- package/src/orchestrator/stages/architect-stage.js +0 -172
- package/src/orchestrator/stages/coder-stage.js +0 -506
- package/src/orchestrator/stages/domain-curator-stage.js +0 -82
- package/src/orchestrator/stages/format-stage.js +0 -61
- package/src/orchestrator/stages/hu-reviewer-phases/detect-and-apply-splits.js +0 -127
- package/src/orchestrator/stages/hu-reviewer-stage.js +0 -419
- package/src/orchestrator/stages/perf-stage.js +0 -140
- package/src/orchestrator/stages/planner-stage.js +0 -85
- package/src/orchestrator/stages/rag-context-stage.js +0 -80
- package/src/orchestrator/stages/rag-preload-decisor.js +0 -58
- package/src/orchestrator/stages/research-stage.js +0 -69
- package/src/orchestrator/stages/reviewer-stage.js +0 -431
- package/src/orchestrator/stages/sonar-stage.js +0 -439
- package/src/orchestrator/stages/stage-classes.js +0 -84
- package/src/orchestrator/stages/stage-executor.js +0 -155
- package/src/orchestrator/stages/suite-green-stage.js +0 -62
- package/src/orchestrator/stages/tdd-discipline-stage.js +0 -56
- package/src/orchestrator/stages/tool-judge-stage.js +0 -53
- package/src/orchestrator/stages/triage-stage.js +0 -172
- package/src/orchestrator/standby.js +0 -71
- package/src/orchestrator/verification-gate.js +0 -183
- package/src/orchestrator.js +0 -22
- package/src/plan/adr-generator.js +0 -170
- package/src/plan/adr-loader.js +0 -60
- package/src/plan/plan-executor.js +0 -100
- package/src/plan/plan-validation.js +0 -4
- package/src/prompts/acceptance-coverage.js +0 -32
- package/src/prompts/arbiter.js +0 -38
- package/src/prompts/architect.js +0 -103
- package/src/prompts/hu-reviewer.js +0 -244
- package/src/prompts/tool-correctness-judge.js +0 -31
- package/src/prompts/triage.js +0 -57
- package/src/rag/embedder.js +0 -2
- package/src/rag/embedders/_cloud-base.js +0 -2
- package/src/rag/embedders/cohere.js +0 -2
- package/src/rag/embedders/mistral.js +0 -2
- package/src/rag/embedders/onnx.js +0 -2
- package/src/rag/embedders/openai.js +0 -2
- package/src/rag/embedders/voyage.js +0 -2
- package/src/rag/rerank.js +0 -4
- package/src/rag/where-parser.js +0 -4
- package/src/repeat-detector.js +0 -88
- package/src/review/diff-generator.js +0 -112
- package/src/review/git-stash-helper.js +0 -104
- package/src/review/schema.js +0 -31
- package/src/review/scope-filter.js +0 -152
- package/src/review/snapshot-diff.js +0 -122
- package/src/review/tdd-discipline.js +0 -90
- package/src/review/tdd-policy.js +0 -69
- package/src/roles/acceptance-coverage-role.js +0 -78
- package/src/roles/architect-role.js +0 -77
- package/src/roles/coder-role.js +0 -113
- package/src/roles/commiter-role.js +0 -113
- package/src/roles/domain-curator-role.js +0 -134
- package/src/roles/hu-reviewer-role.js +0 -64
- package/src/roles/impeccable-role.js +0 -85
- package/src/roles/index.js +0 -18
- package/src/roles/karajan-brain-role.js +0 -134
- package/src/roles/perf-role.js +0 -122
- package/src/roles/planner-role.js +0 -151
- package/src/roles/refactorer-role.js +0 -30
- package/src/roles/repairer-role.js +0 -181
- package/src/roles/researcher-role.js +0 -70
- package/src/roles/reviewer-role.js +0 -115
- package/src/roles/security-role.js +0 -73
- package/src/roles/solomon-role.js +0 -271
- package/src/roles/sonar-role.js +0 -72
- package/src/roles/tester-role.js +0 -235
- package/src/roles/tool-correctness-judge-role.js +0 -78
- package/src/roles/triage-role.js +0 -99
- package/src/session/journal/decisions-writer.js +0 -200
- package/src/session/journal/iteration-logger.js +0 -194
- package/src/session/journal/summary-writer.js +0 -320
- package/src/session/journal/tree-writer.js +0 -164
- package/src/session/journal/write-all.js +0 -89
- package/src/session-cleanup.js +0 -116
- package/src/skills/semantic-detector.js +0 -167
- package/src/skills/skill-detector.js +0 -385
- package/src/sonar/cloud-scanner.js +0 -78
- package/src/tracker-bootstrap.js +0 -44
- package/src/types/index.js +0 -38
- package/src/types/policy.js +0 -43
- package/src/types/solomon.js +0 -87
- package/src/utils/async-lock.js +0 -31
- package/src/utils/budget.js +0 -229
- package/src/utils/cwd-confirm.js +0 -39
- package/src/utils/display/event-handlers.js +0 -403
- package/src/utils/display/formatters.js +0 -132
- package/src/utils/display/header.js +0 -47
- package/src/utils/display/session-summary.js +0 -118
- package/src/utils/display/solomon.js +0 -42
- package/src/utils/pricing.js +0 -29
- package/src/utils/quota-simulator.js +0 -56
- package/src/utils/role-assigner.js +0 -137
- package/src/utils/rtk-wrapper.js +0 -87
- package/src/utils/run-registry.js +0 -9
- package/src/utils/status-dashboard.js +0 -254
- package/src/utils/tool-call-extractor.js +0 -56
- package/src/webperf/devtools-detect.js +0 -112
- package/src/webperf/webperf-role-integration.js +0 -66
- package/templates/skills/kj-run.md +0 -73
|
@@ -1,468 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HU sub-pipeline driver — extracted from flow-runner.js in PR-L
|
|
3
|
-
* (audit follow-up). Was 244 LOC inlined inside _runFlowInner, the
|
|
4
|
-
* second-worst long-function per `kj audit`.
|
|
5
|
-
*
|
|
6
|
-
* Behaviour identical to the inlined version — the closure passed to
|
|
7
|
-
* runHuSubPipeline is moved verbatim, only its surrounding context
|
|
8
|
-
* (logger, emitter, ctx, askQuestion) becomes function parameters.
|
|
9
|
-
*
|
|
10
|
-
* The extraction also fixes a latent bug: flow-runner.js called
|
|
11
|
-
* `runCoderStage`, `runSonarStage`, `generateDiff` and
|
|
12
|
-
* `runSecurityStage` without static OR dynamic imports. Those
|
|
13
|
-
* references would ReferenceError at runtime on the HU sub-pipeline
|
|
14
|
-
* path; the fact that no test caught it suggests the path was rarely
|
|
15
|
-
* exercised end-to-end (preflight failures often abort earlier).
|
|
16
|
-
* This module imports them explicitly.
|
|
17
|
-
*
|
|
18
|
-
* Returns:
|
|
19
|
-
* - { handled: true, result } — needsSubPipeline matched; caller
|
|
20
|
-
* must short-circuit and return `result`.
|
|
21
|
-
* - { handled: false } — caller falls through to the standard
|
|
22
|
-
* single-task pipeline (runIterationLoop).
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
import { emitProgress, makeEvent } from "../../utils/events.js";
|
|
26
|
-
import { needsSubPipeline, runHuSubPipeline } from "../hu-sub-pipeline.js";
|
|
27
|
-
import { runCoderStage } from "../stages/coder-stage.js";
|
|
28
|
-
import { runSonarStage } from "../stages/sonar-stage.js";
|
|
29
|
-
import { runSuiteGreenStage, suiteGreenRequired } from "../stages/suite-green-stage.js";
|
|
30
|
-
import { runIterationLoop } from "./iteration-loop.js";
|
|
31
|
-
import { classifyFailure, createFailureTracker } from "../repair/repair-detector.js";
|
|
32
|
-
import { runRepair } from "../repair/repair-runner.js";
|
|
33
|
-
import { writeHistoryRecord } from "./post-loop.js";
|
|
34
|
-
import { setReviewerFeedback } from "../../session/mutators.js";
|
|
35
|
-
import { createHuSnapshot } from "../../git/hu-snapshot.js";
|
|
36
|
-
import { generateDiff } from "../../review/diff-generator.js";
|
|
37
|
-
import { runAcceptanceCoverageStage } from "../stages/acceptance-coverage-stage.js";
|
|
38
|
-
import { criteriaOfCard } from "../../roles/acceptance-coverage-role.js";
|
|
39
|
-
import { applyCoderRoute } from "../../hu/coder-routing.js";
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* @param {object} args
|
|
43
|
-
* @param {object} args.ctx — PipelineContext from initFlowContext
|
|
44
|
-
* @param {string} args.task — original task description (for writeHistoryRecord)
|
|
45
|
-
* @param {object} args.askQuestion
|
|
46
|
-
* @param {object} args.emitter
|
|
47
|
-
* @param {object} args.logger
|
|
48
|
-
* @returns {Promise<{handled: boolean, result?: object}>}
|
|
49
|
-
*/
|
|
50
|
-
export async function runHuBatch({ ctx, task, askQuestion, emitter, logger }) {
|
|
51
|
-
if (!needsSubPipeline(ctx.stageResults.huReviewer)) {
|
|
52
|
-
return { handled: false };
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// --- HU Sub-Pipeline: run each certified HU as an independent iteration loop ---
|
|
56
|
-
logger.info(`HU sub-pipeline: ${ctx.stageResults.huReviewer.certified} certified stories — running each as a sub-pipeline`);
|
|
57
|
-
emitProgress(emitter, makeEvent("hu:sub-pipeline:start", { ...ctx.eventBase, stage: "hu-sub-pipeline" }, {
|
|
58
|
-
message: `Running ${ctx.stageResults.huReviewer.certified} HUs as sub-pipelines`,
|
|
59
|
-
detail: { total: ctx.stageResults.huReviewer.total, certified: ctx.stageResults.huReviewer.certified }
|
|
60
|
-
}));
|
|
61
|
-
|
|
62
|
-
// Per-HU pipeline: focused max_iterations, fresh Brain state, own git branch.
|
|
63
|
-
const huMaxIterations = ctx.config.hu_max_iterations ?? 3;
|
|
64
|
-
const huBranches = new Map();
|
|
65
|
-
const { prepareHuBranch, finalizeHuCommit, collectGitWarnings } = await import("../../git/hu-automation.js");
|
|
66
|
-
|
|
67
|
-
// PR F (v2.7.5): the per-HU loop needs three pieces of plan context:
|
|
68
|
-
// • ADRs (loaded once from disk, applies to every HU on this plan)
|
|
69
|
-
// • the plan-reviewer findings (rides on the huBatch)
|
|
70
|
-
// • each HU's spec_section (rides on the story)
|
|
71
|
-
// Loading ADRs once outside the loop avoids hitting the filesystem
|
|
72
|
-
// per-HU. When the plan never went through `kj plan` (legacy
|
|
73
|
-
// auto-generated batch), planId is null → loadActiveAdrs falls back
|
|
74
|
-
// to the "_loose" bucket and returns [] silently.
|
|
75
|
-
const planId = ctx.stageResults.huReviewer.planId || null;
|
|
76
|
-
const reviewerFindings = ctx.stageResults.huReviewer.review || null;
|
|
77
|
-
let planAdrs = [];
|
|
78
|
-
try {
|
|
79
|
-
const { loadActiveAdrs } = await import("../../plan/adr-loader.js");
|
|
80
|
-
planAdrs = await loadActiveAdrs(ctx.config.projectDir || process.cwd(), planId);
|
|
81
|
-
if (planAdrs.length > 0) {
|
|
82
|
-
logger.info(`Plan ${planId || "(loose)"}: loaded ${planAdrs.length} ADR(s) for coder context`);
|
|
83
|
-
}
|
|
84
|
-
} catch (err) {
|
|
85
|
-
logger.warn(`ADR loader failed (non-blocking): ${err.message}`);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const subPipelineResult = await runHuSubPipeline({
|
|
89
|
-
huReviewerResult: ctx.stageResults.huReviewer,
|
|
90
|
-
runIterationFn: async (huTask, story, laneOpts = {}) => {
|
|
91
|
-
// Apply per-HU policies based on task_type (infra skips reviewer/sonar/tdd).
|
|
92
|
-
// KJC-TSK-0400: `effectiveTaskType` mira primero story.task_type y, si
|
|
93
|
-
// no es válido, infiere del prefijo del title ([SPIKE], [DOC]…). Así
|
|
94
|
-
// un HU con title "[SPIKE] Decidir auth" entra automáticamente a las
|
|
95
|
-
// policies de spike (sin Sonar/TDD/tests).
|
|
96
|
-
const { applyPolicies, effectiveTaskType } = await import("../../guards/policy-resolver.js");
|
|
97
|
-
const resolvedTaskType = effectiveTaskType(story);
|
|
98
|
-
if (resolvedTaskType !== story.task_type) {
|
|
99
|
-
logger.info(`HU ${story.id}: task_type inferido del title → ${resolvedTaskType} (era ${story.task_type || "null"})`);
|
|
100
|
-
}
|
|
101
|
-
const huPolicies = applyPolicies({ taskType: resolvedTaskType, policies: ctx.config.policies });
|
|
102
|
-
logger.info(`HU ${story.id} (${resolvedTaskType}): policies → reviewer=${huPolicies.reviewer}, tdd=${huPolicies.tdd}, sonar=${huPolicies.sonar}, tests=${huPolicies.testsRequired}`);
|
|
103
|
-
|
|
104
|
-
// PAR-E2 PR2 (KJC-TSK-0629): every per-HU adjustment lands on LANE
|
|
105
|
-
// copies. ctx.config/pipelineFlags/session/brainCtx are shared by all
|
|
106
|
-
// lanes, so concurrent lanes must never mutate them. This also fixes a
|
|
107
|
-
// latent leak: max_iterations was mutated on ctx.config and only the
|
|
108
|
-
// fallback path restored it — the acceptance path returned early and
|
|
109
|
-
// left it clamped to hu_max_iterations for the rest of the run.
|
|
110
|
-
const worktreePath = laneOpts?.worktreePath || null;
|
|
111
|
-
const projectDir = worktreePath || ctx.config.projectDir || process.cwd();
|
|
112
|
-
// PAR-H (KJC-TSK-0631): the lane's slot travels as env vars so any
|
|
113
|
-
// service the coder or the acceptance tests start can offset its
|
|
114
|
-
// ports (offset = slot × 100; the project applies its own base).
|
|
115
|
-
const laneEnv = Number.isInteger(laneOpts?.laneSlot)
|
|
116
|
-
? { KJ_LANE_SLOT: String(laneOpts.laneSlot), KJ_PORT_OFFSET: String(laneOpts.laneSlot * 100) }
|
|
117
|
-
: null;
|
|
118
|
-
const laneConfig = { ...ctx.config, projectDir, max_iterations: huMaxIterations, lane_env: laneEnv };
|
|
119
|
-
if (!huPolicies.tdd) laneConfig.development = { ...laneConfig.development, methodology: "standard", require_test_changes: false };
|
|
120
|
-
if (!huPolicies.sonar) laneConfig.sonarqube = { ...laneConfig.sonarqube, enabled: false };
|
|
121
|
-
const laneFlags = { ...ctx.pipelineFlags };
|
|
122
|
-
if (!huPolicies.reviewer) laneFlags.reviewerEnabled = false;
|
|
123
|
-
if (!huPolicies.testsRequired) laneFlags.testerEnabled = false;
|
|
124
|
-
// Worktree lanes clone the session so reviewer feedback never leaks
|
|
125
|
-
// across concurrent HUs. Disk saves still share the session id (last
|
|
126
|
-
// writer wins) — journal noise at most; in-memory isolation is what
|
|
127
|
-
// guards correctness.
|
|
128
|
-
const laneSession = worktreePath ? { ...ctx.session } : ctx.session;
|
|
129
|
-
let laneBrain = ctx.brainCtx;
|
|
130
|
-
if (ctx.brainCtx?.enabled) {
|
|
131
|
-
const { createBrainContext } = await import("../brain-coordinator.js");
|
|
132
|
-
const fresh = createBrainContext({ enabled: true });
|
|
133
|
-
if (worktreePath) {
|
|
134
|
-
laneBrain = { ...ctx.brainCtx, extensionCount: 0, feedbackQueue: fresh.feedbackQueue, verificationTracker: fresh.verificationTracker };
|
|
135
|
-
} else {
|
|
136
|
-
ctx.brainCtx.extensionCount = 0;
|
|
137
|
-
ctx.brainCtx.feedbackQueue = fresh.feedbackQueue;
|
|
138
|
-
ctx.brainCtx.verificationTracker = fresh.verificationTracker;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
let lanePlannedTask = ctx.plannedTask;
|
|
142
|
-
|
|
143
|
-
// A worktree lane skips the main-tree checkout: `git worktree add -b`
|
|
144
|
-
// already left the worktree checked out on kj-hu-<id>.
|
|
145
|
-
let branchName;
|
|
146
|
-
if (worktreePath) {
|
|
147
|
-
branchName = `kj-hu-${story.id}`;
|
|
148
|
-
huBranches.set(story.id, branchName);
|
|
149
|
-
} else {
|
|
150
|
-
branchName = await prepareHuBranch({ story, huBranches, config: laneConfig, logger, cardId: ctx.session?.pg_task_id || null });
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// KJC-TSK-0408 step 2: snapshot del workspace ANTES de invocar al
|
|
154
|
-
// coder. Si el usuario hace Undo después, restaura este SHA. El
|
|
155
|
-
// ref se crea sobre el HEAD ya checkouteado a la rama de la HU,
|
|
156
|
-
// así que apunta al estado limpio sobre el que el coder empieza.
|
|
157
|
-
// Idempotente — si la HU ya tenía snapshot, se sobreescribe.
|
|
158
|
-
try {
|
|
159
|
-
const snap = await createHuSnapshot({ projectDir, huId: story.id });
|
|
160
|
-
if (snap.ok) {
|
|
161
|
-
story.outcome = { ...(story.outcome || {}), snapshot_sha: snap.sha, snapshot_ref: snap.ref };
|
|
162
|
-
logger.info(`HU ${story.id}: snapshot ${snap.sha.slice(0, 7)} (ref ${snap.ref})`);
|
|
163
|
-
} else {
|
|
164
|
-
logger.warn(`HU ${story.id}: snapshot falló (non-blocking): ${snap.error}`);
|
|
165
|
-
}
|
|
166
|
-
} catch (err) {
|
|
167
|
-
logger.warn(`HU ${story.id}: snapshot threw (non-blocking): ${err.message}`);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// If HU has acceptance_tests, Brain runs them as the gate instead of
|
|
171
|
-
// the standard reviewer/tester pipeline. This is the radical fix:
|
|
172
|
-
// concrete executable tests replace subjective reviewer opinions.
|
|
173
|
-
if (story.acceptance_tests?.length > 0) {
|
|
174
|
-
const { runAcceptanceTests, buildDiagnosticPrompt } = await import("../../hu/acceptance-runner.js");
|
|
175
|
-
// Issue #538 — Repairer runtime safety net. The tracker remembers
|
|
176
|
-
// each test's last failure signature; classifyFailure flags
|
|
177
|
-
// structural bugs (broken jq, shell parse error, missing tool)
|
|
178
|
-
// after 2 consecutive identical failures so we don't burn 5
|
|
179
|
-
// iterations against an irreparable test (the 2026-04-29 bug).
|
|
180
|
-
const failureTracker = createFailureTracker();
|
|
181
|
-
|
|
182
|
-
for (let attempt = 1; attempt <= laneConfig.max_iterations; attempt++) {
|
|
183
|
-
logger.info(`HU ${story.id}: coder iteration ${attempt}/${laneConfig.max_iterations}`);
|
|
184
|
-
applyCoderRoute({ laneConfig, story, attempt, config: ctx.config, logger });
|
|
185
|
-
emitProgress(emitter, makeEvent("iteration:start", { ...ctx.eventBase, stage: "iteration" }, {
|
|
186
|
-
message: `Iteration ${attempt}/${laneConfig.max_iterations}`,
|
|
187
|
-
detail: { iteration: attempt, maxIterations: laneConfig.max_iterations }
|
|
188
|
-
}));
|
|
189
|
-
|
|
190
|
-
// Coder runs with the HU task + any diagnostic feedback from previous attempt.
|
|
191
|
-
// Phase 2 of tests-first (v2.7.5): also hand over the declared
|
|
192
|
-
// acceptance_tests so the coder has the contract from turn 1,
|
|
193
|
-
// not only after the first failed run.
|
|
194
|
-
const coderResult = await runCoderStage({
|
|
195
|
-
coderRoleInstance: ctx.coderRoleInstance, coderRole: ctx.coderRole,
|
|
196
|
-
config: laneConfig, logger, emitter, eventBase: ctx.eventBase,
|
|
197
|
-
session: laneSession, plannedTask: lanePlannedTask,
|
|
198
|
-
trackBudget: ctx.trackBudget, iteration: attempt, brainCtx: laneBrain,
|
|
199
|
-
acceptanceTests: story.acceptance_tests,
|
|
200
|
-
// PR F (v2.7.5): plan-aware coder context. ADRs are
|
|
201
|
-
// shared across the plan, the rest are scoped to this
|
|
202
|
-
// HU. buildCoderPrompt skips any section whose data is
|
|
203
|
-
// null/empty so legacy paths render the same prompt.
|
|
204
|
-
adrs: planAdrs,
|
|
205
|
-
specSection: story.spec_section || null,
|
|
206
|
-
reviewerFindings,
|
|
207
|
-
huId: story.id
|
|
208
|
-
});
|
|
209
|
-
if (coderResult?.action === "standby" || coderResult?.action === "pause") {
|
|
210
|
-
return coderResult?.result || { approved: false, reason: "coder_failed" };
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Sonar quality gate (sw task_type only, when policies say sonar=true)
|
|
214
|
-
if (huPolicies.sonar && laneConfig.sonarqube?.enabled) {
|
|
215
|
-
try {
|
|
216
|
-
const sonarResult = await runSonarStage({
|
|
217
|
-
config: laneConfig, logger, emitter, eventBase: ctx.eventBase,
|
|
218
|
-
session: laneSession, trackBudget: ctx.trackBudget, iteration: attempt,
|
|
219
|
-
// sonarState is required: runSonarStage reads/writes
|
|
220
|
-
// .issuesInitial / .issuesFinal on it. Forgetting this
|
|
221
|
-
// surfaces as `Cannot read properties of undefined
|
|
222
|
-
// (reading 'issuesInitial')` and the catch below swallows
|
|
223
|
-
// it as "sonar stage failed (non-blocking)" — the gate
|
|
224
|
-
// never actually runs for the HU, every iteration burns
|
|
225
|
-
// budget for nothing. Mirrors iteration-loop.js.
|
|
226
|
-
sonarState: ctx.sonarState,
|
|
227
|
-
repeatDetector: ctx.repeatDetector,
|
|
228
|
-
budgetSummary: ctx.budgetSummary,
|
|
229
|
-
askQuestion, brainCtx: laneBrain
|
|
230
|
-
});
|
|
231
|
-
if (sonarResult?.action === "continue") {
|
|
232
|
-
// Sonar failed — add to feedback for next coder attempt
|
|
233
|
-
lanePlannedTask = `${huTask}\n\n--- SONAR FAILURE ---\n${laneSession.last_reviewer_feedback}`;
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
} catch (err) {
|
|
237
|
-
logger.warn(`HU ${story.id}: sonar stage failed (non-blocking): ${err.message}`);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// KJC-BUG-0328 (#2088): a code HU is never approved over a red project
|
|
242
|
-
// suite; it goes back to the coder, as in the iteration loop.
|
|
243
|
-
if ((huPolicies.tdd || huPolicies.testsRequired) && suiteGreenRequired(laneConfig)) {
|
|
244
|
-
const green = await runSuiteGreenStage({ config: laneConfig, logger, emitter, eventBase: ctx.eventBase, session: laneSession, iteration: attempt });
|
|
245
|
-
if (green.action === "continue") {
|
|
246
|
-
lanePlannedTask = `${huTask}\n\n--- TEST SUITE RED ---\n${laneSession.last_reviewer_feedback}`;
|
|
247
|
-
continue;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Brain runs acceptance tests
|
|
252
|
-
logger.info(`HU ${story.id}: running ${story.acceptance_tests.length} acceptance tests`);
|
|
253
|
-
emitProgress(emitter, makeEvent("hu:acceptance-start", { ...ctx.eventBase, stage: "acceptance" }, {
|
|
254
|
-
message: `Running ${story.acceptance_tests.length} acceptance tests`,
|
|
255
|
-
detail: { huId: story.id, testCount: story.acceptance_tests.length }
|
|
256
|
-
}));
|
|
257
|
-
|
|
258
|
-
const testResult = await runAcceptanceTests(story.acceptance_tests, projectDir, { env: laneEnv });
|
|
259
|
-
emitProgress(emitter, makeEvent("hu:acceptance-end", { ...ctx.eventBase, stage: "acceptance" }, {
|
|
260
|
-
status: testResult.allPassed ? "ok" : "fail",
|
|
261
|
-
message: testResult.summary,
|
|
262
|
-
detail: { allPassed: testResult.allPassed, results: testResult.results.map(r => ({ cmd: r.cmd, passed: r.passed })) }
|
|
263
|
-
}));
|
|
264
|
-
|
|
265
|
-
if (testResult.allPassed) {
|
|
266
|
-
// Phase 3 of tests-first (v2.7.5): the shell gate is green,
|
|
267
|
-
// but any Gherkin scenarios are still "pending" — they need
|
|
268
|
-
// the tester to translate them into real test files before
|
|
269
|
-
// we can call the HU done. If none are pending, approve
|
|
270
|
-
// immediately like before.
|
|
271
|
-
const pendingGherkinTests = (story.acceptance_tests || []).filter(
|
|
272
|
-
(t) => t && typeof t === "object" && t.type === "gherkin"
|
|
273
|
-
);
|
|
274
|
-
if (pendingGherkinTests.length === 0) {
|
|
275
|
-
logger.info(`HU ${story.id}: all shell acceptance tests PASSED, no Gherkin to translate — approved`);
|
|
276
|
-
const git = await finalizeHuCommit({ story, branchName, config: laneConfig, logger, cwd: worktreePath });
|
|
277
|
-
return { approved: true, sessionId: laneSession.id, reason: "acceptance_tests_passed", git };
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Shell tests passed; Gherkin needs translation. Invoke the
|
|
281
|
-
// tester with both so it writes code tests for the Gherkin,
|
|
282
|
-
// runs the suite, and returns verdict.
|
|
283
|
-
logger.info(`HU ${story.id}: shell gate passed, asking tester to translate ${pendingGherkinTests.length} Gherkin scenario(s)`);
|
|
284
|
-
const { runTesterStage } = await import("../post-loop-stages.js");
|
|
285
|
-
const shellResults = testResult.results.filter((r) => r.type !== "gherkin");
|
|
286
|
-
const testerOutcome = await runTesterStage({
|
|
287
|
-
config: laneConfig, logger, emitter, eventBase: ctx.eventBase,
|
|
288
|
-
session: laneSession, coderRole: ctx.coderRole, trackBudget: ctx.trackBudget,
|
|
289
|
-
iteration: attempt, task: huTask, diff: null, askQuestion,
|
|
290
|
-
pendingGherkinTests, shellTestResults: shellResults,
|
|
291
|
-
});
|
|
292
|
-
const testerStage = testerOutcome?.stageResult;
|
|
293
|
-
if (testerOutcome?.action !== "continue" && testerStage?.verdict === "pass") {
|
|
294
|
-
logger.info(`HU ${story.id}: tester translated Gherkin and verdict=pass — approved`);
|
|
295
|
-
const git = await finalizeHuCommit({ story, branchName, config: laneConfig, logger, cwd: worktreePath });
|
|
296
|
-
return { approved: true, sessionId: laneSession.id, reason: "acceptance_tests_passed", git };
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Tester rejected (translated tests failed or coverage
|
|
300
|
-
// inadequate). Feed its diagnostic back to the coder for
|
|
301
|
-
// another iteration.
|
|
302
|
-
const failing = testerStage?.failing_scenarios?.join("\n- ") || testerStage?.summary || "tester rejected";
|
|
303
|
-
const diagnostic = [
|
|
304
|
-
"Tester translated the Gherkin acceptance tests and some FAILED:",
|
|
305
|
-
failing ? `- ${failing}` : "",
|
|
306
|
-
"",
|
|
307
|
-
"Fix the implementation so every scenario passes. Do NOT soften the scenarios.",
|
|
308
|
-
].filter(Boolean).join("\n");
|
|
309
|
-
logger.warn(`HU ${story.id}: tester verdict=fail after Gherkin translation — sending back to coder`);
|
|
310
|
-
setReviewerFeedback(laneSession, diagnostic);
|
|
311
|
-
lanePlannedTask = `${huTask}\n\n--- GHERKIN TRANSLATION FAILURES ---\n${diagnostic}`;
|
|
312
|
-
continue;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Brain diagnoses failures and sends concrete fix to coder
|
|
316
|
-
const failed = testResult.results.filter(r => !r.passed);
|
|
317
|
-
|
|
318
|
-
// Repairer (#538): for each failing shell test, see if it's been
|
|
319
|
-
// failing the same way 2× in a row AND looks structurally broken
|
|
320
|
-
// (jq syntax, shell parse error, missing tool, etc.). If yes,
|
|
321
|
-
// invoke RepairerRole to rewrite the test in the plan + batch
|
|
322
|
-
// before iterating again. If unfixable, escalate (fail the HU
|
|
323
|
-
// immediately rather than burn the rest of max_iterations).
|
|
324
|
-
let repairedAny = false;
|
|
325
|
-
let repairEscalate = null;
|
|
326
|
-
for (const f of failed) {
|
|
327
|
-
if (f.type !== "shell") continue;
|
|
328
|
-
const consecutive = failureTracker.record(f.cmd, f.output);
|
|
329
|
-
const cls = classifyFailure({
|
|
330
|
-
cmd: f.cmd, errorOutput: f.output, consecutive,
|
|
331
|
-
});
|
|
332
|
-
if (!cls.infeasible) continue;
|
|
333
|
-
const testIndex = story.acceptance_tests.findIndex((t) => {
|
|
334
|
-
const c = typeof t === "object" ? t.content : t;
|
|
335
|
-
return c === f.cmd;
|
|
336
|
-
});
|
|
337
|
-
if (testIndex < 0) continue;
|
|
338
|
-
// Mutate `story.acceptance_tests` in place. The same story
|
|
339
|
-
// reference lives inside runHuSubPipeline's batch (passed
|
|
340
|
-
// by ref), so the next iteration's acceptance runner sees
|
|
341
|
-
// the fixed test without us needing batch+batchSessionId
|
|
342
|
-
// in this scope. The reconcile from PR #544 will sync the
|
|
343
|
-
// plan JSON on the next run.
|
|
344
|
-
const repair = await runRepair({
|
|
345
|
-
story, testIndex, errorOutput: f.output,
|
|
346
|
-
batchSessionId: ctx.stageResults.huReviewer.batchSessionId,
|
|
347
|
-
// batch unavailable in this closure scope — runRepair
|
|
348
|
-
// gates saveHuBatch on its presence and skips when
|
|
349
|
-
// missing; the in-memory mutation alone is sufficient
|
|
350
|
-
// for the next acceptance run within this same HU loop.
|
|
351
|
-
batch: null,
|
|
352
|
-
config: ctx.config, logger, emitter, eventBase: ctx.eventBase,
|
|
353
|
-
failureKind: cls.kind,
|
|
354
|
-
savePlanPatch: null,
|
|
355
|
-
});
|
|
356
|
-
if (repair.escalate) { repairEscalate = repair.reason; break; }
|
|
357
|
-
if (repair.repaired) {
|
|
358
|
-
repairedAny = true;
|
|
359
|
-
failureTracker.reset(f.cmd);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
if (repairEscalate) {
|
|
364
|
-
logger.warn(`HU ${story.id}: Repairer escalated — ${repairEscalate}`);
|
|
365
|
-
return { approved: false, sessionId: laneSession.id, reason: "repair_escalated" };
|
|
366
|
-
}
|
|
367
|
-
if (repairedAny) {
|
|
368
|
-
// Tests were rewritten — restart iteration with the fixed
|
|
369
|
-
// tests in place; do NOT increment attempt against the user
|
|
370
|
-
// because the failure was the planner's bug, not the coder's.
|
|
371
|
-
logger.info(`HU ${story.id}: Repairer fixed test(s); restarting acceptance gate without consuming iteration`);
|
|
372
|
-
attempt -= 1;
|
|
373
|
-
continue;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// No repair happened — normal coder feedback path.
|
|
377
|
-
const diagnostic = buildDiagnosticPrompt(failed);
|
|
378
|
-
logger.warn(`HU ${story.id}: ${failed.length} acceptance test(s) FAILED — sending diagnostic to coder`);
|
|
379
|
-
setReviewerFeedback(laneSession, diagnostic);
|
|
380
|
-
lanePlannedTask = `${huTask}\n\n--- ACCEPTANCE TEST FAILURES ---\n${diagnostic}`;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// All iterations exhausted
|
|
384
|
-
logger.warn(`HU ${story.id}: max iterations reached with acceptance tests still failing`);
|
|
385
|
-
return { approved: false, sessionId: laneSession.id, reason: "acceptance_tests_failed" };
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// Fallback: no acceptance_tests → standard pipeline (reviewer/tester).
|
|
389
|
-
// The loop reads everything through ctx.* — handing it a lane view
|
|
390
|
-
// keeps the shared ctx untouched (no more mutate-and-restore dance).
|
|
391
|
-
const laneCtx = { ...ctx, config: laneConfig, pipelineFlags: laneFlags, session: laneSession, brainCtx: laneBrain, plannedTask: lanePlannedTask,
|
|
392
|
-
beforeIteration: (attempt) => applyCoderRoute({ laneConfig, story, attempt, config: ctx.config, logger }) };
|
|
393
|
-
const result = await runIterationLoop(laneCtx, { task: huTask, askQuestion, emitter, logger });
|
|
394
|
-
if (result?.approved) {
|
|
395
|
-
// KJC-BUG-0248: what the HU's push or PR said joins what the loop recorded.
|
|
396
|
-
const git = await finalizeHuCommit({ story, branchName, config: laneConfig, logger, cwd: worktreePath });
|
|
397
|
-
const prev = result.git ?? {};
|
|
398
|
-
return { ...result, git: { ...prev, prUrl: prev.prUrl ?? git.prUrl, errors: [...(prev.errors ?? []), ...git.errors] } };
|
|
399
|
-
}
|
|
400
|
-
return result;
|
|
401
|
-
},
|
|
402
|
-
emitter,
|
|
403
|
-
eventBase: ctx.eventBase,
|
|
404
|
-
logger,
|
|
405
|
-
config: ctx.config,
|
|
406
|
-
// PR1 (live HU status): forward the per-HU status updater so
|
|
407
|
-
// hu-sub-pipeline can patch the plan JSON on every transition,
|
|
408
|
-
// not only at the end of the run. The board's chokidar then
|
|
409
|
-
// fires per-HU and the Kanban columns update in real time.
|
|
410
|
-
onStatusChange: ctx.session?._liveStatusUpdater || null,
|
|
411
|
-
// PR3 (per-HU outcome): same pattern, but for the rich
|
|
412
|
-
// outcome blob written ONCE per HU at the end of runSingleHu.
|
|
413
|
-
onOutcome: ctx.session?._liveOutcomeUpdater || null,
|
|
414
|
-
// Cost D (KJC-TSK-0515): the sub-pipeline slices BudgetTracker
|
|
415
|
-
// entries per HU and stamps `outcome.cost_usd` so the board can
|
|
416
|
-
// show per-HU spend. Without this the tracker is invisible to it.
|
|
417
|
-
budgetTracker: ctx.budgetTracker || null
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
emitProgress(emitter, makeEvent("hu:sub-pipeline:end", { ...ctx.eventBase, stage: "hu-sub-pipeline" }, {
|
|
421
|
-
status: subPipelineResult.approved ? "ok" : "fail",
|
|
422
|
-
message: subPipelineResult.approved
|
|
423
|
-
? "All HUs completed successfully"
|
|
424
|
-
: `Sub-pipeline finished with failures (${subPipelineResult.blockedIds.length} blocked)`,
|
|
425
|
-
detail: { results: subPipelineResult.results, blockedIds: subPipelineResult.blockedIds }
|
|
426
|
-
}));
|
|
427
|
-
|
|
428
|
-
// Sync results back to plan file if this was a plan-based run
|
|
429
|
-
if (ctx.session._syncResultsToPlan) {
|
|
430
|
-
try {
|
|
431
|
-
await ctx.session._syncResultsToPlan(subPipelineResult);
|
|
432
|
-
logger.info("Plan updated with execution results");
|
|
433
|
-
} catch (err) {
|
|
434
|
-
logger.warn(`Failed to sync results to plan: ${err.message}`);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// KJC-BUG-0248 (#1895): a push or a PR that failed is said in the result
|
|
439
|
-
// the host reads, not only in a warn line of the log.
|
|
440
|
-
const warnings = collectGitWarnings(subPipelineResult.results);
|
|
441
|
-
for (const w of warnings) logger.warn(`git automation: ${w}`);
|
|
442
|
-
// KJC-TSK-0987 (#1894): the HUs approved themselves against their own stories;
|
|
443
|
-
// the card's criteria are read against the diff of the whole run before the
|
|
444
|
-
// run says approved. Uncovered: approved:false with the list, for the host.
|
|
445
|
-
let coverage = { action: "skip" };
|
|
446
|
-
if (subPipelineResult.approved) {
|
|
447
|
-
const diff = await generateDiff({ baseRef: ctx.session.head_at_start || ctx.session.session_start_sha });
|
|
448
|
-
coverage = await runAcceptanceCoverageStage({
|
|
449
|
-
config: ctx.config, logger, emitter, eventBase: ctx.eventBase, trackBudget: ctx.trackBudget,
|
|
450
|
-
iteration: 0, task, criteria: criteriaOfCard(ctx.session.pg_card), diff,
|
|
451
|
-
});
|
|
452
|
-
if (coverage.stageResult) ctx.stageResults.acceptanceCoverage = coverage.stageResult;
|
|
453
|
-
}
|
|
454
|
-
const uncovered = coverage.action === "retry" ? { reason: "acceptance_criteria_uncovered", uncovered: coverage.stageResult.uncovered } : {};
|
|
455
|
-
const unavailable = coverage.action === "fail" ? { reason: "acceptance_coverage_unavailable", error: coverage.stageResult.summary } : {};
|
|
456
|
-
const finalResult = {
|
|
457
|
-
approved: subPipelineResult.approved && coverage.action !== "retry" && coverage.action !== "fail",
|
|
458
|
-
sessionId: ctx.session.id,
|
|
459
|
-
huResults: subPipelineResult.results,
|
|
460
|
-
blockedIds: subPipelineResult.blockedIds,
|
|
461
|
-
planId: ctx.session._planRef?.planId || null,
|
|
462
|
-
...uncovered,
|
|
463
|
-
...unavailable,
|
|
464
|
-
...(warnings.length > 0 ? { warnings } : {})
|
|
465
|
-
};
|
|
466
|
-
await writeHistoryRecord({ sessionId: ctx.session.id, task, result: finalResult, logger });
|
|
467
|
-
return { handled: true, result: finalResult };
|
|
468
|
-
}
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
// Feedback enrichment: transform vague feedback into actionable instructions.
|
|
2
|
-
// Extracts file hints, line hints, and suggests fixes from feedback text.
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Extract file path hints from feedback description.
|
|
6
|
-
* Matches patterns like: packages/server/auth.js, src/routes/*, auth/middleware.ts
|
|
7
|
-
*/
|
|
8
|
-
export function extractFileHints(description) {
|
|
9
|
-
if (!description) return [];
|
|
10
|
-
const hints = new Set();
|
|
11
|
-
|
|
12
|
-
// Explicit paths with extensions (supports bare filenames too)
|
|
13
|
-
const pathRe = /\b([\w./_-]+\.(?:js|ts|jsx|tsx|py|go|rs|java|rb|php|cs|swift|dart|html|css|vue|svelte|astro|json|yml|yaml|md|sql))\b/gi;
|
|
14
|
-
const paths = description.match(pathRe) || [];
|
|
15
|
-
for (const p of paths) hints.add(p);
|
|
16
|
-
|
|
17
|
-
// Directory references
|
|
18
|
-
const dirRe = /\b(packages\/[\w-]+|src\/[\w/-]+|tests?\/[\w/-]+|lib\/[\w/-]+)/gi;
|
|
19
|
-
const dirs = description.match(dirRe) || [];
|
|
20
|
-
for (const d of dirs) hints.add(d);
|
|
21
|
-
|
|
22
|
-
return [...hints];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Detect the category of feedback from description keywords.
|
|
27
|
-
*/
|
|
28
|
-
export function detectCategory(description) {
|
|
29
|
-
const text = (description || "").toLowerCase();
|
|
30
|
-
if (/\b(injection|xss|csrf|httponly|sql\s*injection|exposed\s*secret|auth(?:entication)?\s*bypass|authoriza(?:tion)?\s*bypass|cryptograph|token\s*expos)/i.test(text)) return "security";
|
|
31
|
-
if (/\btest|coverage|assert|spec|mock\b/.test(text)) return "tests";
|
|
32
|
-
if (/\bperf(ormance)?|slow|optimize|bottleneck|latency\b/.test(text)) return "performance";
|
|
33
|
-
if (/\b(style|format(?:ting)?|naming|rename|indent|whitespace|comment|jsdoc|cosmetic)\b/.test(text)) return "style";
|
|
34
|
-
if (/\b(bug|incorrect|wrong|fails?|error|crash|broken)\b/.test(text)) return "correctness";
|
|
35
|
-
return "other";
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Detect severity from description keywords and existing severity field.
|
|
40
|
-
*/
|
|
41
|
-
export function detectSeverity(description, existingSeverity) {
|
|
42
|
-
if (existingSeverity) return existingSeverity;
|
|
43
|
-
const text = (description || "").toLowerCase();
|
|
44
|
-
if (/\bcritical|blocker|severe|production\s*risk\b/.test(text)) return "critical";
|
|
45
|
-
if (/\b(high|security|vulnerab|breaking|regression|injection|xss|csrf)\b/.test(text)) return "high";
|
|
46
|
-
if (/\blow|minor|cosmetic|nitpick\b/.test(text)) return "low";
|
|
47
|
-
return "medium";
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Generate actionable instructions from a feedback entry.
|
|
52
|
-
* Adds concrete steps the coder can follow.
|
|
53
|
-
*/
|
|
54
|
-
export function generateActionPlan(entry) {
|
|
55
|
-
const steps = [];
|
|
56
|
-
const cat = entry.category || detectCategory(entry.description);
|
|
57
|
-
|
|
58
|
-
// Always start with locating the problem
|
|
59
|
-
if (entry.file) {
|
|
60
|
-
steps.push(`Open ${entry.file}${entry.line ? ` at line ${entry.line}` : ""}`);
|
|
61
|
-
} else {
|
|
62
|
-
const hints = extractFileHints(entry.description);
|
|
63
|
-
if (hints.length > 0) {
|
|
64
|
-
steps.push(`Look in: ${hints.join(", ")}`);
|
|
65
|
-
} else {
|
|
66
|
-
steps.push("Identify the relevant file(s) using grep/search");
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Category-specific actions
|
|
71
|
-
if (cat === "security") {
|
|
72
|
-
steps.push("Apply the security fix (validate input, escape output, use parameterized queries, etc.)");
|
|
73
|
-
} else if (cat === "tests") {
|
|
74
|
-
steps.push("Write the missing test cases covering the scenario described");
|
|
75
|
-
steps.push("Run tests to verify they pass: npm test or equivalent");
|
|
76
|
-
} else if (cat === "correctness") {
|
|
77
|
-
steps.push("Fix the bug described");
|
|
78
|
-
steps.push("Add a regression test");
|
|
79
|
-
} else if (cat === "performance") {
|
|
80
|
-
steps.push("Optimize the identified bottleneck");
|
|
81
|
-
steps.push("Verify improvement with measurements");
|
|
82
|
-
} else if (cat === "style") {
|
|
83
|
-
steps.push("Apply the style fix (run linter if available)");
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Include suggested fix if provided
|
|
87
|
-
if (entry.suggestedFix) {
|
|
88
|
-
steps.push(`Suggested approach: ${entry.suggestedFix}`);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return steps;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Enrich a single feedback entry with file hints, category, severity, and action plan.
|
|
96
|
-
*/
|
|
97
|
-
export function enrichEntry(entry) {
|
|
98
|
-
const description = entry.description || "";
|
|
99
|
-
// Re-detect category when missing or set to default "other"
|
|
100
|
-
const category = (!entry.category || entry.category === "other")
|
|
101
|
-
? detectCategory(description)
|
|
102
|
-
: entry.category;
|
|
103
|
-
const severity = detectSeverity(description, entry.severity);
|
|
104
|
-
const fileHints = entry.file ? [entry.file] : extractFileHints(description);
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
...entry,
|
|
108
|
-
category,
|
|
109
|
-
severity,
|
|
110
|
-
fileHints,
|
|
111
|
-
actionPlan: generateActionPlan({ ...entry, category })
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Enrich all entries in a queue in-place.
|
|
117
|
-
*/
|
|
118
|
-
export function enrichQueue(queue) {
|
|
119
|
-
queue.entries = queue.entries.map(enrichEntry);
|
|
120
|
-
return queue;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Format enriched entries as actionable coder prompt sections.
|
|
125
|
-
*/
|
|
126
|
-
export function formatEnrichedForCoder(enrichedEntries) {
|
|
127
|
-
if (!enrichedEntries?.length) return "";
|
|
128
|
-
|
|
129
|
-
const sections = [];
|
|
130
|
-
enrichedEntries.forEach((e, i) => {
|
|
131
|
-
const header = `### Issue ${i + 1}: [${e.severity}] ${e.category} — ${e.description}`;
|
|
132
|
-
const location = e.fileHints?.length
|
|
133
|
-
? `**Location hints:** ${e.fileHints.join(", ")}`
|
|
134
|
-
: "**Location:** search the project for relevant files";
|
|
135
|
-
const plan = `**Action plan:**\n${e.actionPlan.map(s => `- ${s}`).join("\n")}`;
|
|
136
|
-
sections.push([header, location, plan].join("\n"));
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
return sections.join("\n\n");
|
|
140
|
-
}
|