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,125 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Iteration phase: quality gates (TDD check, SonarQube local + cloud,
|
|
3
|
-
* Impeccable design audit).
|
|
4
|
-
*
|
|
5
|
-
* Extracted verbatim from `src/orchestrator/drivers/iteration-loop.js` in
|
|
6
|
-
* the v2.7.x audit follow-up to keep the iteration-loop driver under the
|
|
7
|
-
* 600-LOC ceiling. Behaviour is byte-for-byte identical to the inlined
|
|
8
|
-
* version.
|
|
9
|
-
*
|
|
10
|
-
* Returns { action: "ok" | "return" | "continue", result? }.
|
|
11
|
-
* - "ok" → caller continues to the next phase.
|
|
12
|
-
* - "return" → caller returns `result` (pause / stalled / blocked).
|
|
13
|
-
* - "continue" → caller continues the iteration loop (next iteration).
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { generateDiff } from "../../../review/diff-generator.js";
|
|
17
|
-
import { withLock } from "../../../utils/async-lock.js";
|
|
18
|
-
import {
|
|
19
|
-
runTddCheckStage, runSonarStage, runSonarCloudStage,
|
|
20
|
-
} from "../../iteration-stages.js";
|
|
21
|
-
import { runImpeccableStage } from "../../post-loop-stages.js";
|
|
22
|
-
import { runPerfStage } from "../../stages/perf-stage.js";
|
|
23
|
-
import { runTddDisciplineStage } from "../../stages/tdd-discipline-stage.js";
|
|
24
|
-
import { runSuiteGreenStage, suiteGreenRequired } from "../../stages/suite-green-stage.js";
|
|
25
|
-
import { runToolJudgeStage } from "../../stages/tool-judge-stage.js";
|
|
26
|
-
import { runFormatStage } from "../../stages/format-stage.js";
|
|
27
|
-
import { tryCiComment } from "../../ci-integration.js";
|
|
28
|
-
|
|
29
|
-
export async function runQualityGateStages({ config, logger, emitter, eventBase, session, trackBudget, i, askQuestion, repeatDetector, budgetSummary, sonarState, task, stageResults, coderRole, pipelineFlags, brainCtx }) {
|
|
30
|
-
// KJC-TSK-1237 (#2565): the coder's files reach every gate formatted.
|
|
31
|
-
await runFormatStage({ config, logger, emitter, eventBase, session });
|
|
32
|
-
|
|
33
|
-
const tddResult = await runTddCheckStage({ config, logger, emitter, eventBase, session, trackBudget, iteration: i, askQuestion, task, brainCtx });
|
|
34
|
-
if (tddResult.action === "pause") return { action: "return", result: tddResult.result };
|
|
35
|
-
if (tddResult.action === "continue") return { action: "continue" };
|
|
36
|
-
|
|
37
|
-
// KJC-BUG-0298 (#1993): with TDD, a red suite never reaches the reviewer.
|
|
38
|
-
if (config.development?.methodology === "tdd" && suiteGreenRequired(config)) {
|
|
39
|
-
const green = await runSuiteGreenStage({ config, logger, emitter, eventBase, session, iteration: i });
|
|
40
|
-
if (green.action === "continue") return { action: "continue" };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// KJC-TSK-0398 PR3: opt-in red-then-green check.
|
|
44
|
-
if (config.development?.require_red_then_green) {
|
|
45
|
-
const disc = await runTddDisciplineStage({
|
|
46
|
-
config, logger, emitter, eventBase,
|
|
47
|
-
sourceFiles: tddResult.sourceFiles || [],
|
|
48
|
-
testFiles: tddResult.testFiles || [],
|
|
49
|
-
});
|
|
50
|
-
if (disc.action === "continue") return { action: "continue" };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Sonar runs for code tasks per policy. Since v2.7.4 it is NOT
|
|
54
|
-
// toggleable via config — that's intrinsic to Karajan. The taskType
|
|
55
|
-
// policy (resolved_policies.sonar) is the single source of truth.
|
|
56
|
-
// Solomon may skip a single iteration via rule alerts; that's a
|
|
57
|
-
// runtime decision, not a config option.
|
|
58
|
-
//
|
|
59
|
-
// Test-harness escape hatch via config.testHarness.disableSonarStage
|
|
60
|
-
// — production code reads `config?.testHarness?.disableSonarStage`,
|
|
61
|
-
// never `globalThis.*`. The legacy override surface is documented
|
|
62
|
-
// (and exclusively read) in src/config/test-harness.js. ESLint rule
|
|
63
|
-
// (#557) blocks any re-introduction of `globalThis.__KJ_*` outside
|
|
64
|
-
// that one file.
|
|
65
|
-
const sonarStageDisabledForTest = config?.testHarness?.disableSonarStage === true;
|
|
66
|
-
if (!sonarStageDisabledForTest && session.resolved_policies?.sonar !== false) {
|
|
67
|
-
// Serialize across parallel HU lanes (KJC-TSK-0625): the SonarQube
|
|
68
|
-
// server scans one project key at a time. No-op on sequential runs.
|
|
69
|
-
const sonarResult = await withLock("sonar-scan", () => runSonarStage({
|
|
70
|
-
config, logger, emitter, eventBase, session, trackBudget, iteration: i,
|
|
71
|
-
repeatDetector, budgetSummary, sonarState, askQuestion, task, brainCtx
|
|
72
|
-
}));
|
|
73
|
-
if (sonarResult.action === "stalled" || sonarResult.action === "pause") return { action: "return", result: sonarResult.result };
|
|
74
|
-
if (sonarResult.action === "continue") return { action: "continue" };
|
|
75
|
-
if (sonarResult.stageResult) {
|
|
76
|
-
stageResults.sonar = sonarResult.stageResult;
|
|
77
|
-
await tryCiComment({ config, session, logger, agent: "Sonar", body: `SonarQube scan: ${sonarResult.stageResult.summary || "completed"}` });
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (config.sonarcloud?.enabled) {
|
|
82
|
-
const cloudResult = await runSonarCloudStage({
|
|
83
|
-
config, logger, emitter, eventBase, session, trackBudget, iteration: i
|
|
84
|
-
});
|
|
85
|
-
if (cloudResult.stageResult) {
|
|
86
|
-
stageResults.sonarcloud = cloudResult.stageResult;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (pipelineFlags?.impeccableEnabled) {
|
|
91
|
-
const diff = await generateDiff({ baseRef: session.session_start_sha, projectDir: config?.projectDir || null });
|
|
92
|
-
const impeccableMode = pipelineFlags?.impeccableMode || "audit";
|
|
93
|
-
const impeccableResult = await runImpeccableStage({
|
|
94
|
-
config, logger, emitter, eventBase, session, coderRole, trackBudget,
|
|
95
|
-
iteration: i, task, diff, mode: impeccableMode
|
|
96
|
-
});
|
|
97
|
-
if (impeccableResult.stageResult) {
|
|
98
|
-
stageResults.impeccable = impeccableResult.stageResult;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (pipelineFlags?.perfEnabled) {
|
|
103
|
-
const perfResult = await runPerfStage({
|
|
104
|
-
config, logger, emitter, eventBase, session, trackBudget,
|
|
105
|
-
iteration: i, task, brainCtx
|
|
106
|
-
});
|
|
107
|
-
if (perfResult.stageResult) {
|
|
108
|
-
stageResults.perf = perfResult.stageResult;
|
|
109
|
-
}
|
|
110
|
-
if (perfResult.action === "continue") return { action: "continue" };
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// KJC-TSK-0375 PR3: opt-in tool-call quality judge. Non-blocking.
|
|
114
|
-
if (pipelineFlags?.toolJudgeEnabled) {
|
|
115
|
-
const judgeResult = await runToolJudgeStage({
|
|
116
|
-
config, logger, emitter, eventBase, session, coderRole, trackBudget,
|
|
117
|
-
iteration: i, task,
|
|
118
|
-
});
|
|
119
|
-
if (judgeResult.stageResult) {
|
|
120
|
-
stageResults.toolJudge = judgeResult.stageResult;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return { action: "ok" };
|
|
125
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Iteration phase: reviewer stage with standby handling.
|
|
3
|
-
*
|
|
4
|
-
* Extracted verbatim from `src/orchestrator/drivers/iteration-loop.js` in
|
|
5
|
-
* the v2.7.x audit follow-up to keep the iteration-loop driver under the
|
|
6
|
-
* 600-LOC ceiling. Behaviour is byte-for-byte identical to the inlined
|
|
7
|
-
* version.
|
|
8
|
-
*
|
|
9
|
-
* Returns { action: "ok" | "return" | "retry", review?, result? }.
|
|
10
|
-
* - "ok" → caller proceeds with `review` (approved or with blocking
|
|
11
|
-
* issues).
|
|
12
|
-
* - "return" → caller returns `result` (pause / stalled).
|
|
13
|
-
* - "retry" → caller decrements its iteration counter and retries.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { stageRegistry } from "../../stages/stage-classes.js";
|
|
17
|
-
import { runStage } from "../../stages/stage-executor.js";
|
|
18
|
-
import { handleStandbyResult } from "../error-recovery.js";
|
|
19
|
-
|
|
20
|
-
export async function runReviewerGateStage({ pipelineFlags, reviewerRole, config, logger, emitter, eventBase, session, trackBudget, i, reviewRules, task, repeatDetector, budgetSummary, askQuestion, brainCtx }) {
|
|
21
|
-
// Reviewer via StageRegistry (TSK-0336). ReviewerStage.canRun returns
|
|
22
|
-
// `reviewerEnabled !== false`; when false, runStage returns null and we
|
|
23
|
-
// synthesize the "disabled-by-pipeline" stub (same shape as the previous
|
|
24
|
-
// early-return). Otherwise execute() runs runReviewerStage.
|
|
25
|
-
const reviewerCtx = {
|
|
26
|
-
reviewerRole, config, logger, emitter, eventBase, session, trackBudget,
|
|
27
|
-
iteration: i, reviewRules, task, repeatDetector, budgetSummary, askQuestion,
|
|
28
|
-
brainCtx, pipelineFlags,
|
|
29
|
-
};
|
|
30
|
-
const reviewerResult = await runStage(stageRegistry.get("reviewer"), reviewerCtx);
|
|
31
|
-
if (reviewerResult === null) {
|
|
32
|
-
// KJC-BUG-0297 (#1993): `reviewed: false` so the pipeline never stamps a
|
|
33
|
-
// verdict nobody gave nor commits on it (finalizeGitAutomation).
|
|
34
|
-
return {
|
|
35
|
-
action: "ok",
|
|
36
|
-
review: { approved: true, reviewed: false, blocking_issues: [], non_blocking_suggestions: [], summary: "Reviewer disabled by pipeline: nothing was reviewed", confidence: 0 }
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
if (reviewerResult.action === "pause") return { action: "return", result: reviewerResult.result };
|
|
40
|
-
const revStandby = await handleStandbyResult({ stageResult: reviewerResult, session, emitter, eventBase, i, stage: "reviewer", logger, config, askQuestion });
|
|
41
|
-
if (revStandby.handled) {
|
|
42
|
-
if (revStandby.action === "return") return { action: "return", result: revStandby.result };
|
|
43
|
-
if (revStandby.action === "skip") {
|
|
44
|
-
// Solomon said skip review — treat as approved
|
|
45
|
-
return { action: "ok", review: { approved: true, blocking_issues: [], non_blocking_suggestions: [], summary: "Review skipped (agent rate-limited, Solomon approved)", confidence: 0.7 } };
|
|
46
|
-
}
|
|
47
|
-
if (revStandby.action === "retry_reviewer_only") {
|
|
48
|
-
// Retry just the reviewer — use alternative agent if Solomon recommended one
|
|
49
|
-
let retryReviewerRole = reviewerRole;
|
|
50
|
-
const alt = session._alternative_agent;
|
|
51
|
-
if (alt?.stage === "reviewer" && alt?.provider) {
|
|
52
|
-
retryReviewerRole = { provider: alt.provider, model: null };
|
|
53
|
-
logger.info(`Retrying reviewer with alternative agent: ${alt.provider}`);
|
|
54
|
-
delete session._alternative_agent;
|
|
55
|
-
}
|
|
56
|
-
return runReviewerGateStage({ pipelineFlags: { reviewerEnabled: true }, reviewerRole: retryReviewerRole, config, logger, emitter, eventBase, session, trackBudget, i, reviewRules, task, repeatDetector, budgetSummary, askQuestion });
|
|
57
|
-
}
|
|
58
|
-
return { action: "retry" };
|
|
59
|
-
}
|
|
60
|
-
if (reviewerResult.stalled) return { action: "return", result: reviewerResult.stalledResult };
|
|
61
|
-
return { action: "ok", review: reviewerResult.review };
|
|
62
|
-
}
|
|
@@ -1,404 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Post-loop driver — extracted from src/orchestrator/flow-runner.js in
|
|
3
|
-
* TSK-0335 (Oleada 3 of the v2.7.4 audit refactor).
|
|
4
|
-
*
|
|
5
|
-
* Handlers for everything that happens AFTER the iteration loop decides
|
|
6
|
-
* an outcome (approved, max-iterations, or simple finalization):
|
|
7
|
-
*
|
|
8
|
-
* - handlePostLoopStages: run tester → security → final audit
|
|
9
|
-
* after the reviewer has approved; any of
|
|
10
|
-
* them can demand one more coder pass.
|
|
11
|
-
* - finalizeApprovedSession: commit/push/PR + PG card transition +
|
|
12
|
-
* full session journal + session:end event.
|
|
13
|
-
* - handleMaxIterationsReached: Brain-owned decision at the iteration
|
|
14
|
-
* cap (extend vs finalize vs escalate).
|
|
15
|
-
* Legacy path delegates to Solomon.
|
|
16
|
-
* - tryAutoStartBoard: auto-launch HU Board when config opts in.
|
|
17
|
-
* - writeHistoryRecord: non-blocking HU history write on session end.
|
|
18
|
-
*
|
|
19
|
-
* Extracted verbatim; no behavior change. flow-runner.js now imports these
|
|
20
|
-
* instead of defining them. Part of breaking up the 2254-LOC god-module —
|
|
21
|
-
* see docs/ARCHITECTURE.md and the TSK-0335 acceptance criteria.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
import { generateDiff } from "../../review/diff-generator.js";
|
|
25
|
-
import { finalizeGitAutomation } from "../../git/automation.js";
|
|
26
|
-
import { listCommitsBetween, listFilesChangedSince } from "../../utils/git.js";
|
|
27
|
-
import { loadPlan } from "../../plan/plan-store.js";
|
|
28
|
-
import { computePlanAdherenceScore } from "../../audit/plan-adherence.js";
|
|
29
|
-
import { saveSession, markSessionStatus } from "../../session/store.js";
|
|
30
|
-
import {
|
|
31
|
-
setReviewerFeedback, setBudget, setRtkSavings, resetRetryCount,
|
|
32
|
-
} from "../../session/mutators.js";
|
|
33
|
-
import { emitProgress, makeEvent } from "../../utils/events.js";
|
|
34
|
-
import { runTesterStage, runSecurityStage, runFinalAuditStage } from "../post-loop-stages.js";
|
|
35
|
-
import { runAcceptanceCoverageStage } from "../stages/acceptance-coverage-stage.js";
|
|
36
|
-
import { criteriaOfCard } from "../../roles/acceptance-coverage-role.js";
|
|
37
|
-
import { invokeSolomon } from "../solomon-escalation.js";
|
|
38
|
-
import { tryCiComment } from "../ci-integration.js";
|
|
39
|
-
import { getIntegration } from "../integrations.js";
|
|
40
|
-
|
|
41
|
-
export async function handlePostLoopStages({ config, session, emitter, eventBase, coderRole, trackBudget, i, task, stageResults, ciEnabled: _ciEnabled, testerEnabled, securityEnabled, askQuestion, logger, brainCtx }) {
|
|
42
|
-
const postLoopDiff = await generateDiff({ baseRef: session.session_start_sha });
|
|
43
|
-
|
|
44
|
-
if (testerEnabled) {
|
|
45
|
-
const testerResult = await runTesterStage({
|
|
46
|
-
config, logger, emitter, eventBase, session, coderRole, trackBudget,
|
|
47
|
-
iteration: i, task, diff: postLoopDiff, askQuestion
|
|
48
|
-
});
|
|
49
|
-
if (testerResult.action === "pause") return { action: "return", result: testerResult.result };
|
|
50
|
-
if (testerResult.action === "continue") {
|
|
51
|
-
const summary = testerResult.stageResult?.summary || "Tester found issues";
|
|
52
|
-
setReviewerFeedback(session, `Tester FAILED — fix these issues:\n${summary}`);
|
|
53
|
-
await saveSession(session);
|
|
54
|
-
if (testerResult.stageResult) stageResults.tester = testerResult.stageResult;
|
|
55
|
-
// Brain: push tester failure into feedback queue + compress for next coder iteration
|
|
56
|
-
if (brainCtx?.enabled) {
|
|
57
|
-
const { processRoleOutput } = await import("../brain-coordinator.js");
|
|
58
|
-
processRoleOutput(brainCtx, { roleName: "tester", output: testerResult.stageResult, iteration: i });
|
|
59
|
-
}
|
|
60
|
-
return { action: "continue" };
|
|
61
|
-
}
|
|
62
|
-
if (testerResult.stageResult) {
|
|
63
|
-
stageResults.tester = testerResult.stageResult;
|
|
64
|
-
await tryCiComment({ config, session, logger, agent: "Tester", body: `Tests: ${testerResult.stageResult.summary || "completed"}` });
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (securityEnabled) {
|
|
69
|
-
const securityResult = await runSecurityStage({
|
|
70
|
-
config, logger, emitter, eventBase, session, coderRole, trackBudget,
|
|
71
|
-
iteration: i, task, diff: postLoopDiff, askQuestion, brainCtx
|
|
72
|
-
});
|
|
73
|
-
if (securityResult.action === "pause") return { action: "return", result: securityResult.result };
|
|
74
|
-
if (securityResult.action === "continue") {
|
|
75
|
-
const summary = securityResult.stageResult?.summary || "Security found issues";
|
|
76
|
-
setReviewerFeedback(session, `Security FAILED — fix these issues:\n${summary}`);
|
|
77
|
-
await saveSession(session);
|
|
78
|
-
if (securityResult.stageResult) stageResults.security = securityResult.stageResult;
|
|
79
|
-
// Brain: push security findings into feedback queue + compress for next coder iteration
|
|
80
|
-
if (brainCtx?.enabled) {
|
|
81
|
-
const { processRoleOutput } = await import("../brain-coordinator.js");
|
|
82
|
-
processRoleOutput(brainCtx, { roleName: "security", output: securityResult.stageResult, iteration: i });
|
|
83
|
-
}
|
|
84
|
-
return { action: "continue" };
|
|
85
|
-
}
|
|
86
|
-
if (securityResult.stageResult) {
|
|
87
|
-
stageResults.security = securityResult.stageResult;
|
|
88
|
-
await tryCiComment({ config, session, logger, agent: "Security", body: `Security scan: ${securityResult.stageResult.summary || "completed"}` });
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Final audit — last quality gate before declaring success
|
|
93
|
-
const auditResult = await runFinalAuditStage({
|
|
94
|
-
config, logger, emitter, eventBase, session, coderRole, trackBudget,
|
|
95
|
-
iteration: i, task, diff: postLoopDiff
|
|
96
|
-
});
|
|
97
|
-
if (auditResult.stageResult) {
|
|
98
|
-
stageResults.audit = auditResult.stageResult;
|
|
99
|
-
await tryCiComment({ config, session, logger, agent: "Audit", body: `Final audit: ${auditResult.stageResult.summary || "completed"}` });
|
|
100
|
-
}
|
|
101
|
-
if (auditResult.action === "retry") {
|
|
102
|
-
// Audit found actionable issues — loop back to coder
|
|
103
|
-
setReviewerFeedback(session, auditResult.feedback);
|
|
104
|
-
await saveSession(session);
|
|
105
|
-
return { action: "continue" };
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// KJC-TSK-0987 (#1894): with a card, each acceptance criterion is read against
|
|
109
|
-
// the diff by a different AI before the run is approved. An uncovered one goes
|
|
110
|
-
// back to the coder; a judge with no verdict stops the approval, never grants it.
|
|
111
|
-
const coverage = await runAcceptanceCoverageStage({
|
|
112
|
-
config, logger, emitter, eventBase, trackBudget, iteration: i, task,
|
|
113
|
-
criteria: criteriaOfCard(session.pg_card), diff: postLoopDiff,
|
|
114
|
-
});
|
|
115
|
-
if (coverage.stageResult) stageResults.acceptanceCoverage = coverage.stageResult;
|
|
116
|
-
if (coverage.action === "retry") {
|
|
117
|
-
setReviewerFeedback(session, coverage.feedback);
|
|
118
|
-
await saveSession(session);
|
|
119
|
-
return { action: "continue" };
|
|
120
|
-
}
|
|
121
|
-
if (coverage.action === "fail") {
|
|
122
|
-
return { action: "return", result: { approved: false, sessionId: session.id, reason: "acceptance_coverage_unavailable", error: coverage.stageResult.summary } };
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return { action: "proceed" };
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export async function finalizeApprovedSession({ config, gitCtx, task, logger, session, stageResults, emitter, eventBase, budgetSummary, pgCard, pgProject, review, i, rtkTracker }) {
|
|
129
|
-
const gitResult = await finalizeGitAutomation({ config, gitCtx, task, logger, session, stageResults, review });
|
|
130
|
-
|
|
131
|
-
// Accumulate final commits for PG card lifecycle tracking
|
|
132
|
-
if (gitResult?.commits?.length) {
|
|
133
|
-
const tracker = getIntegration("tracker");
|
|
134
|
-
for (const c of gitResult.commits) tracker?.onCommit?.(session, c);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (stageResults.planner?.ok) {
|
|
138
|
-
stageResults.planner.completedSteps = [...(stageResults.planner.steps ?? [])];
|
|
139
|
-
}
|
|
140
|
-
setBudget(session, budgetSummary());
|
|
141
|
-
await markSessionStatus(session, "approved");
|
|
142
|
-
|
|
143
|
-
await getIntegration("tracker")?.onSessionApproved?.({ pgCard, pgProject, config, session, gitResult, logger });
|
|
144
|
-
|
|
145
|
-
const deferredIssues = session.deferred_issues || [];
|
|
146
|
-
const rtkSavings = rtkTracker?.hasData() ? rtkTracker.summary() : undefined;
|
|
147
|
-
if (rtkSavings) setRtkSavings(session, rtkSavings);
|
|
148
|
-
await saveSession(session);
|
|
149
|
-
|
|
150
|
-
// --- Journal: write final files (shared writer, KJC-BUG-0084) ---
|
|
151
|
-
const journalDir = session._journalDir;
|
|
152
|
-
if (journalDir) {
|
|
153
|
-
// 2026-05-07 dogfooding: gitResult.commits only carries the post-loop
|
|
154
|
-
// commit (scaffold). Compute the full range from session_start_sha so
|
|
155
|
-
// every commit the run produced shows up in execution order.
|
|
156
|
-
let allCommits;
|
|
157
|
-
try { allCommits = (await listCommitsBetween(session.head_at_start || session.session_start_sha)) || []; }
|
|
158
|
-
catch { allCommits = gitResult?.commits || []; }
|
|
159
|
-
const summaryCommits = allCommits.length > 0 ? allCommits : (gitResult?.commits || []);
|
|
160
|
-
|
|
161
|
-
// KJC-TSK-0376 — plan adherence metric (deepeval-inspired). When the
|
|
162
|
-
// run executed against a known plan (`kj run --plan <id>`), score how
|
|
163
|
-
// faithfully the coder followed it. Pure offline; safe-by-default
|
|
164
|
-
// (try/catch swallows missing helper or unreadable plan).
|
|
165
|
-
let planAdherence = null;
|
|
166
|
-
try {
|
|
167
|
-
const planId = session._planRef?.planId
|
|
168
|
-
|| stageResults?.huReviewer?.planId
|
|
169
|
-
|| null;
|
|
170
|
-
if (planId) {
|
|
171
|
-
const projectDir = config?.projectDir
|
|
172
|
-
|| session.config_snapshot?.projectDir
|
|
173
|
-
|| process.cwd();
|
|
174
|
-
const plan = await loadPlan(projectDir, planId).catch(() => null);
|
|
175
|
-
if (plan && Array.isArray(plan.hus) && plan.hus.length > 0) {
|
|
176
|
-
const filesChanged = await listFilesChangedSince(
|
|
177
|
-
session.head_at_start || session.session_start_sha,
|
|
178
|
-
).catch(() => []);
|
|
179
|
-
planAdherence = computePlanAdherenceScore({
|
|
180
|
-
commits: summaryCommits,
|
|
181
|
-
plan,
|
|
182
|
-
filesChanged,
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
} catch (err) {
|
|
187
|
-
logger?.warn?.(`Plan adherence calculation skipped: ${err.message}`);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const { writeSessionJournal } = await import("../../session/journal/write-all.js");
|
|
191
|
-
await writeSessionJournal({
|
|
192
|
-
session, logger,
|
|
193
|
-
result: "APPROVED",
|
|
194
|
-
iterations: i,
|
|
195
|
-
budget: budgetSummary(),
|
|
196
|
-
stages: stageResults,
|
|
197
|
-
commits: summaryCommits,
|
|
198
|
-
planAdherence,
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Mirror the same enriched commit list into the session:end event so the
|
|
203
|
-
// terminal `🏁 Result` banner (printSessionGit) shows every commit the
|
|
204
|
-
// run produced, not just the post-loop scaffold one. We compute again
|
|
205
|
-
// (cheap: one git log call) rather than threading a variable through
|
|
206
|
-
// because the journal block is wrapped in try/catch and may have set
|
|
207
|
-
// `summaryCommits` to a fallback we don't want for the event detail.
|
|
208
|
-
// try/catch tolerates both promise rejection and sync undefined returns
|
|
209
|
-
// (see the journal block above for the detailed rationale).
|
|
210
|
-
let eventCommits = null;
|
|
211
|
-
try { eventCommits = await listCommitsBetween(session.head_at_start || session.session_start_sha); }
|
|
212
|
-
catch { /* leave null — we'll use gitResult unchanged */ }
|
|
213
|
-
const gitForEvent = (Array.isArray(eventCommits) && eventCommits.length > 0)
|
|
214
|
-
? { ...gitResult, commits: eventCommits }
|
|
215
|
-
: gitResult;
|
|
216
|
-
const endDetail = { approved: true, iterations: i, stages: stageResults, git: gitForEvent, budget: budgetSummary(), deferredIssues };
|
|
217
|
-
if (rtkSavings) endDetail.rtk_savings = rtkSavings;
|
|
218
|
-
emitProgress(
|
|
219
|
-
emitter,
|
|
220
|
-
makeEvent("session:end", { ...eventBase, stage: "done" }, {
|
|
221
|
-
message: deferredIssues.length > 0
|
|
222
|
-
? `Session approved (${deferredIssues.length} deferred issue(s) tracked as tech debt)`
|
|
223
|
-
: "Session approved",
|
|
224
|
-
detail: endDetail
|
|
225
|
-
})
|
|
226
|
-
);
|
|
227
|
-
const result = { approved: true, sessionId: session.id, review, git: gitResult, deferredIssues };
|
|
228
|
-
if (rtkSavings) result.rtk_savings = rtkSavings;
|
|
229
|
-
return result;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
export async function handleMaxIterationsReached({ session, budgetSummary, emitter, eventBase, config, stageResults, logger, askQuestion, task, rtkTracker, brainCtx }) {
|
|
233
|
-
const budget = budgetSummary();
|
|
234
|
-
|
|
235
|
-
// Brain-owned decision: max_iterations is guidance, not a hard rule.
|
|
236
|
-
// Brain evaluates the feedback queue state to decide extend / finalize / escalate.
|
|
237
|
-
// Solomon is only consulted if Brain cannot decide on its own.
|
|
238
|
-
if (brainCtx?.enabled) {
|
|
239
|
-
const entries = brainCtx.feedbackQueue?.entries || [];
|
|
240
|
-
const pending = entries.map(e => ({ source: e.source, category: e.category, severity: e.severity, description: e.description }));
|
|
241
|
-
const hasSecurity = entries.some(e => e.category === "security" || e.source === "security");
|
|
242
|
-
const hasCorrectness = entries.some(e => ["correctness", "tests"].includes(e.category));
|
|
243
|
-
|
|
244
|
-
if (hasSecurity) {
|
|
245
|
-
// Brain: security issues unresolved → cannot finalize, escalate
|
|
246
|
-
logger.warn(`Brain: max_iterations reached with ${entries.filter(e => e.category === "security" || e.source === "security").length} unresolved security issue(s) — cannot finalize`);
|
|
247
|
-
return { paused: true, sessionId: session.id, question: "Brain: unresolved security issues at max_iterations. Review manually or extend pipeline.", context: "brain_security_block", pending };
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (hasCorrectness) {
|
|
251
|
-
// Brain: correctness/test issues pending. Cap at MAX_EXTENSIONS to avoid infinite extensions.
|
|
252
|
-
const MAX_EXTENSIONS = 2;
|
|
253
|
-
if (brainCtx.extensionCount >= MAX_EXTENSIONS) {
|
|
254
|
-
logger.warn(`Brain: ${brainCtx.extensionCount} extensions exhausted with correctness issues still pending — escalating to human`);
|
|
255
|
-
return { paused: true, sessionId: session.id, question: `Brain exhausted ${MAX_EXTENSIONS} extensions with correctness/tests still pending. Manual intervention needed.`, context: "brain_extension_cap", pending };
|
|
256
|
-
}
|
|
257
|
-
brainCtx.extensionCount += 1;
|
|
258
|
-
logger.info(`Brain: max_iterations reached with ${entries.filter(e => ["correctness", "tests"].includes(e.category)).length} correctness issue(s) pending — extending iterations (extension ${brainCtx.extensionCount}/${MAX_EXTENSIONS})`);
|
|
259
|
-
resetRetryCount(session, "reviewer");
|
|
260
|
-
await saveSession(session);
|
|
261
|
-
return { approved: false, sessionId: session.id, reason: "max_iterations_extended", extraIterations: Math.ceil(config.max_iterations / 2) };
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (entries.length === 0) {
|
|
265
|
-
// Brain: no pending feedback → last reviewer approved, finalize
|
|
266
|
-
logger.info("Brain: max_iterations reached with clean feedback queue — finalizing as approved");
|
|
267
|
-
return { approved: true, sessionId: session.id, reason: "brain_approved" };
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// hasStyleOnly: genuine dilemma → Brain consults Solomon
|
|
271
|
-
logger.info(`Brain: max_iterations with ${entries.length} style-only issue(s) — consulting Solomon on dilemma`);
|
|
272
|
-
const solomonResult = await invokeSolomon({
|
|
273
|
-
config, logger, emitter, eventBase, stage: "max_iterations", askQuestion, session,
|
|
274
|
-
iteration: config.max_iterations,
|
|
275
|
-
conflict: {
|
|
276
|
-
stage: "brain-max-iterations",
|
|
277
|
-
task,
|
|
278
|
-
iterationCount: config.max_iterations,
|
|
279
|
-
maxIterations: config.max_iterations,
|
|
280
|
-
budget_usd: budget?.total_cost_usd || 0,
|
|
281
|
-
dilemma: `Max iterations reached with ${entries.length} style-only issue(s) pending. Accept as-is or request more work?`,
|
|
282
|
-
pendingIssues: pending,
|
|
283
|
-
history: [{ agent: "pipeline", feedback: session.last_reviewer_feedback || "Max iterations reached" }]
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
// Brain applies Solomon's decision
|
|
287
|
-
if (solomonResult.action === "approve") {
|
|
288
|
-
logger.info("Brain: Solomon advised approve for style-only pending — finalizing");
|
|
289
|
-
return { approved: true, sessionId: session.id, reason: "brain_solomon_approved" };
|
|
290
|
-
}
|
|
291
|
-
if (solomonResult.action === "continue") {
|
|
292
|
-
return { approved: false, sessionId: session.id, reason: "max_iterations_extended", extraIterations: solomonResult.extraIterations || Math.ceil(config.max_iterations / 2) };
|
|
293
|
-
}
|
|
294
|
-
if (solomonResult.action === "pause") {
|
|
295
|
-
return { paused: true, sessionId: session.id, question: solomonResult.question, context: "brain_solomon_dilemma" };
|
|
296
|
-
}
|
|
297
|
-
// Fallback: escalate to human
|
|
298
|
-
return { paused: true, sessionId: session.id, question: `Brain+Solomon cannot resolve: ${entries.length} pending issue(s) at max_iterations`, context: "max_iterations" };
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// Legacy path (Brain disabled): original Solomon-driven flow
|
|
302
|
-
const solomonResult = await invokeSolomon({
|
|
303
|
-
config, logger, emitter, eventBase, stage: "max_iterations", askQuestion, session,
|
|
304
|
-
iteration: config.max_iterations,
|
|
305
|
-
conflict: {
|
|
306
|
-
stage: "max_iterations",
|
|
307
|
-
task,
|
|
308
|
-
iterationCount: config.max_iterations,
|
|
309
|
-
maxIterations: config.max_iterations,
|
|
310
|
-
budget_usd: budget?.total_cost_usd || 0,
|
|
311
|
-
history: [{ agent: "pipeline", feedback: session.last_reviewer_feedback || "Max iterations reached without reviewer approval" }]
|
|
312
|
-
}
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
if (solomonResult.action === "approve") {
|
|
316
|
-
logger.info("Solomon approved coder's work at max_iterations checkpoint");
|
|
317
|
-
return { approved: true, sessionId: session.id, reason: "solomon_approved" };
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
if (solomonResult.action === "continue") {
|
|
321
|
-
if (solomonResult.humanGuidance) {
|
|
322
|
-
setReviewerFeedback(session, `Solomon guidance: ${solomonResult.humanGuidance}`);
|
|
323
|
-
}
|
|
324
|
-
resetRetryCount(session, "reviewer");
|
|
325
|
-
await saveSession(session);
|
|
326
|
-
const extraIterations = solomonResult.extraIterations || config.max_iterations;
|
|
327
|
-
return { approved: false, sessionId: session.id, reason: "max_iterations_extended", humanGuidance: solomonResult.humanGuidance, extraIterations };
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
if (solomonResult.action === "pause") {
|
|
331
|
-
return { paused: true, sessionId: session.id, question: solomonResult.question, context: "max_iterations" };
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (solomonResult.action === "subtask") {
|
|
335
|
-
return { paused: true, sessionId: session.id, subtask: solomonResult.subtask, context: "max_iterations_subtask" };
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// Solomon couldn't resolve — fail
|
|
339
|
-
setBudget(session, budgetSummary());
|
|
340
|
-
const rtkSavings = rtkTracker?.hasData() ? rtkTracker.summary() : undefined;
|
|
341
|
-
if (rtkSavings) setRtkSavings(session, rtkSavings);
|
|
342
|
-
await markSessionStatus(session, "failed");
|
|
343
|
-
const failDetail = { approved: false, reason: "max_iterations", iterations: config.max_iterations, stages: stageResults, budget: budgetSummary() };
|
|
344
|
-
if (rtkSavings) failDetail.rtk_savings = rtkSavings;
|
|
345
|
-
emitProgress(
|
|
346
|
-
emitter,
|
|
347
|
-
makeEvent("session:end", { ...eventBase, stage: "done" }, {
|
|
348
|
-
status: "fail",
|
|
349
|
-
message: "Max iterations reached (Solomon could not resolve)",
|
|
350
|
-
detail: failDetail
|
|
351
|
-
})
|
|
352
|
-
);
|
|
353
|
-
return { approved: false, sessionId: session.id, reason: "max_iterations" };
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* KJC-TSK-0884 (ADR 0011): the board starts by itself, like Sonar. Only a
|
|
358
|
-
* declared false on either switch opts out; nothing declared means on.
|
|
359
|
-
*/
|
|
360
|
-
export const boardAutoStartWanted = (config) => config?.hu_board?.auto_start !== false && config?.hu_board?.enabled !== false;
|
|
361
|
-
|
|
362
|
-
export async function tryAutoStartBoard(config, logger, emitter, eventBase) {
|
|
363
|
-
if (!boardAutoStartWanted(config)) return;
|
|
364
|
-
// Never auto-start during vitest: races the PID file and starts a detached
|
|
365
|
-
// process that outlives the test run.
|
|
366
|
-
if (process.env.VITEST || process.env.NODE_ENV === "test") return;
|
|
367
|
-
|
|
368
|
-
try {
|
|
369
|
-
const { startBoard, renderBoardBanner, boardSlug } = await import("../../commands/board.js");
|
|
370
|
-
const boardPort = config.hu_board?.port || 4000;
|
|
371
|
-
// KJC-TSK-0884: the project's scoped view (/p/<slug>), the parent repo's
|
|
372
|
-
// even from a worktree lane.
|
|
373
|
-
const slug = config.projectDir ? boardSlug(config.projectDir) : null;
|
|
374
|
-
const boardResult = await startBoard(boardPort, { projectSlug: slug });
|
|
375
|
-
const status = boardResult.alreadyRunning ? "already running" : "started";
|
|
376
|
-
// Highlighted URL box — visible regardless of log level (matches the
|
|
377
|
-
// post-planner auto-HU banner so the user gets the same signal either
|
|
378
|
-
// way the board got started).
|
|
379
|
-
console.log(renderBoardBanner({ url: boardResult.url, status }));
|
|
380
|
-
logger.info(`HU Board ${status} at ${boardResult.url}`);
|
|
381
|
-
emitProgress(emitter, makeEvent("board:started", eventBase, {
|
|
382
|
-
message: `HU Board running at ${boardResult.url}`,
|
|
383
|
-
detail: { pid: boardResult.pid, port: boardPort, url: boardResult.url }
|
|
384
|
-
}));
|
|
385
|
-
} catch (err) {
|
|
386
|
-
logger.warn(`HU Board auto-start failed (non-blocking): ${err.message}`);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
export async function writeHistoryRecord({ sessionId, task, result, logger }) {
|
|
391
|
-
try {
|
|
392
|
-
const { createHistoryRecord } = await import("../../hu/store.js");
|
|
393
|
-
const approved = Boolean(result?.approved);
|
|
394
|
-
const summary = result?.review?.summary || result?.reason || null;
|
|
395
|
-
await createHistoryRecord(sessionId, {
|
|
396
|
-
task,
|
|
397
|
-
result: JSON.stringify(result),
|
|
398
|
-
approved,
|
|
399
|
-
summary
|
|
400
|
-
});
|
|
401
|
-
} catch (err) {
|
|
402
|
-
logger.warn(`HU history record failed (non-blocking): ${err.message}`);
|
|
403
|
-
}
|
|
404
|
-
}
|