karajan-code 4.70.4 → 5.0.1
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/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 -143
- package/src/cli/register-plan.js +9 -100
- package/src/cli/register-roles-skills.js +1 -1
- 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 -150
- package/src/commands/go.js +71 -53
- package/src/commands/harden.js +90 -81
- 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 +121 -55
- package/src/commands/review-gate.js +421 -341
- package/src/commands/skills.js +4 -1
- 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 +107 -26
- package/src/config/defaults.js +1 -0
- package/src/config/loader.js +34 -17
- package/src/config/role-resolver.js +1 -0
- package/src/config/schema.js +1 -0
- package/src/config/secrets.js +36 -0
- package/src/config.js +1 -0
- package/src/environment/adr.js +16 -2
- package/src/git/karajan-label.js +20 -8
- package/src/guards/main-guard.js +54 -0
- package/src/harden/hook-templates.js +16 -1
- package/src/harden/sentinel/posttooluse.mjs +18 -6
- package/src/harden/sentinel/pretooluse-sentinel.mjs +64 -36
- package/src/harden/sentinel/sentinel-lib.mjs +129 -26
- package/src/harden/sentinel/sentinel-shell.mjs +5 -16
- package/src/harden/sentinel/sessionstart.mjs +3 -2
- package/src/harden/sentinel/stop.mjs +5 -4
- 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 +83 -13
- 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 +1 -35
- package/src/mcp/handlers/management-handlers.js +2 -42
- package/src/mcp/handlers/rag-handler.js +9 -1
- package/src/mcp/server-handlers.js +16 -22
- package/src/mcp/tools.js +1 -196
- 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 +71 -44
- package/src/rag/bootstrap-index.js +46 -54
- package/src/rag/indexer.js +9 -4
- package/src/rag/migrate.js +1 -1
- package/src/rag/onnx-fallback.js +2 -2
- package/src/rag/pid-lease.js +181 -0
- package/src/rag/project-store.js +2 -12
- package/src/review/card-first.js +57 -46
- package/src/review/gate-gitignore.js +23 -4
- package/src/review/gate-hook.js +12 -4
- package/src/review/one-shot-review.js +45 -35
- package/src/review/publish-pr.js +14 -2
- package/src/review/repo-docs.js +73 -0
- package/src/review/repo-stack.js +59 -0
- package/src/review/reviewer-fallback.js +19 -4
- 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 +1 -24
- package/src/roles/audit-role.js +106 -125
- package/src/skills/atril-cli.js +19 -1
- package/src/skills/atril-skills.js +5 -4
- package/src/skills/skills-search.js +72 -0
- package/src/skills/translate.js +62 -0
- 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/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,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* KJC-BUG-0298 (#1993): with TDD, a red suite never reaches the reviewer. A
|
|
3
|
-
* step whose test was added without its implementation was approved with one
|
|
4
|
-
* failing file, because nothing in the iteration RAN the tests: the
|
|
5
|
-
* verification gate counts changed lines, the TDD check asks that tests changed
|
|
6
|
-
* alongside sources, and the red-then-green discipline is opt-in. This stage
|
|
7
|
-
* runs the project's own test command (the one kj harden resolves: npm test, a
|
|
8
|
-
* known framework, pytest, go test) and hands a red suite back to the coder.
|
|
9
|
-
* Opt-out: `development.require_green_suite: false`.
|
|
10
|
-
*/
|
|
11
|
-
import { addCheckpoint, saveSession } from "../../session/store.js";
|
|
12
|
-
import { setReviewerFeedback } from "../../session/mutators.js";
|
|
13
|
-
import { emitProgress, makeEvent } from "../../utils/events.js";
|
|
14
|
-
import { runCommand } from "../../utils/process.js";
|
|
15
|
-
import { resolveCmds } from "../../commands/harden.js";
|
|
16
|
-
|
|
17
|
-
const TAIL_CHARS = 2000;
|
|
18
|
-
const DEFAULT_TIMEOUT_MS = 10 * 60 * 1000;
|
|
19
|
-
|
|
20
|
-
/** Whether the project asks for a green suite (opt-out, and off in the test harness). */
|
|
21
|
-
export function suiteGreenRequired(config) {
|
|
22
|
-
return config?.development?.require_green_suite !== false && config?.testHarness?.disableSuiteGreenStage !== true;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** @returns {Promise<{action: "ok"|"continue", result: object}>} */
|
|
26
|
-
export async function runSuiteGreenStage({ config, logger, emitter, eventBase, session, iteration, resolveCmdsImpl = resolveCmds, runTestsImpl = null }) {
|
|
27
|
-
const projectDir = config?.projectDir || process.cwd();
|
|
28
|
-
let command = null;
|
|
29
|
-
try {
|
|
30
|
-
command = (await resolveCmdsImpl(projectDir))?.test || null;
|
|
31
|
-
} catch (err) {
|
|
32
|
-
logger?.warn?.(`suite-green: could not resolve the test command (${err.message})`);
|
|
33
|
-
}
|
|
34
|
-
if (!command) {
|
|
35
|
-
const message = "suite-green: no test command for this project (package.json scripts.test or a known framework): the suite was NOT run";
|
|
36
|
-
logger?.warn?.(message);
|
|
37
|
-
emitProgress(emitter, makeEvent("tests:result", { ...eventBase, stage: "tests" }, { status: "warn", message, detail: { ran: false } }));
|
|
38
|
-
return { action: "ok", result: { ran: false } };
|
|
39
|
-
}
|
|
40
|
-
const timeout = Number(config?.development?.test_timeout_ms) > 0 ? Number(config.development.test_timeout_ms) : DEFAULT_TIMEOUT_MS;
|
|
41
|
-
const run = runTestsImpl || (async () => {
|
|
42
|
-
// KJC-BUG-0427: never a login shell, which re-reads the person's profile and may rewrite
|
|
43
|
-
// PATH (nvm drops node when npm_config_prefix is set): the suite runs in kj's own environment.
|
|
44
|
-
const res = await runCommand("bash", ["-c", command], { cwd: projectDir, timeout });
|
|
45
|
-
return { exitCode: res.exitCode ?? 1, output: `${res.stdout || ""}\n${res.stderr || ""}` };
|
|
46
|
-
});
|
|
47
|
-
const res = await run(command);
|
|
48
|
-
const ok = res.exitCode === 0;
|
|
49
|
-
const verdict = ok ? "green" : `red (exit ${res.exitCode})`;
|
|
50
|
-
emitProgress(emitter, makeEvent("tests:result", { ...eventBase, stage: "tests" }, {
|
|
51
|
-
status: ok ? "ok" : "fail",
|
|
52
|
-
message: `${command}: ${verdict}`,
|
|
53
|
-
detail: { ran: true, ok, command, exitCode: res.exitCode, executorType: "local" },
|
|
54
|
-
}));
|
|
55
|
-
await addCheckpoint(session, { stage: "suite-green", iteration, ok, command });
|
|
56
|
-
if (ok) return { action: "ok", result: { ran: true, ok: true, command } };
|
|
57
|
-
const tail = String(res.output || "").trim().slice(-TAIL_CHARS);
|
|
58
|
-
setReviewerFeedback(session, `The test suite is RED (${command}, exit ${res.exitCode}). Make it green before anything else:\n${tail}`);
|
|
59
|
-
await saveSession(session);
|
|
60
|
-
logger?.warn?.(`suite-green: ${command} exited ${res.exitCode}; back to the coder`);
|
|
61
|
-
return { action: "continue", result: { ran: true, ok: false, command, exitCode: res.exitCode } };
|
|
62
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
// TDD discipline stage (KJC-TSK-0398 PR3).
|
|
2
|
-
// Runs the red-then-green check when `development.require_red_then_green`
|
|
3
|
-
// is true. Wraps `verifyTddDiscipline` with the surgical stash helper and
|
|
4
|
-
// the project's detected test framework. Gate is opt-in; an unsupported
|
|
5
|
-
// framework reports a warning and does not block the iteration.
|
|
6
|
-
|
|
7
|
-
import { detectTestFramework } from "../../utils/project-detect.js";
|
|
8
|
-
import { verifyTddDiscipline } from "../../review/tdd-discipline.js";
|
|
9
|
-
import { stashFilesAside, restoreFiles } from "../../review/git-stash-helper.js";
|
|
10
|
-
import { runCommand } from "../../utils/process.js";
|
|
11
|
-
import { emitProgress, makeEvent } from "../../utils/events.js";
|
|
12
|
-
|
|
13
|
-
const TDD_DISCIPLINE_COMMANDS = {
|
|
14
|
-
vitest: ["npx", ["vitest", "run", "--reporter=verbose"]],
|
|
15
|
-
jest: ["npx", ["jest", "--verbose"]],
|
|
16
|
-
pytest: ["pytest", ["--tb=short"]],
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export async function runTddDisciplineStage({
|
|
20
|
-
config, logger, emitter, eventBase, sourceFiles = [], testFiles = [],
|
|
21
|
-
detectFramework = detectTestFramework,
|
|
22
|
-
runTestsImpl,
|
|
23
|
-
stashImpl = stashFilesAside,
|
|
24
|
-
restoreImpl = restoreFiles,
|
|
25
|
-
} = {}) {
|
|
26
|
-
const projectDir = config?.projectDir || process.cwd();
|
|
27
|
-
const detection = await detectFramework(projectDir);
|
|
28
|
-
const framework = detection?.framework || null;
|
|
29
|
-
|
|
30
|
-
const runTests = runTestsImpl || (async () => {
|
|
31
|
-
const cmd = TDD_DISCIPLINE_COMMANDS[framework];
|
|
32
|
-
if (!cmd) return { exitCode: 0, output: "" };
|
|
33
|
-
const res = await runCommand(cmd[0], cmd[1], { cwd: projectDir, reject: false });
|
|
34
|
-
return { exitCode: res.exitCode ?? 0, output: `${res.stdout || ""}\n${res.stderr || ""}` };
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
const result = await verifyTddDiscipline({
|
|
38
|
-
projectDir, sourceFiles, testFiles, framework,
|
|
39
|
-
runTests,
|
|
40
|
-
stashFiles: (files) => stashImpl(files, { projectDir }),
|
|
41
|
-
restoreFiles: (token) => restoreImpl(token, { projectDir }),
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
emitProgress(emitter, makeEvent("tdd:discipline", { ...eventBase, stage: "tdd" }, {
|
|
45
|
-
status: result.ok ? "ok" : "fail",
|
|
46
|
-
message: result.message || `TDD discipline: ${result.reason}`,
|
|
47
|
-
detail: { reason: result.reason, framework },
|
|
48
|
-
}));
|
|
49
|
-
|
|
50
|
-
if (result.warning) logger?.warn?.(result.warning);
|
|
51
|
-
if (!result.ok) {
|
|
52
|
-
logger?.warn?.(`TDD discipline violation: ${result.reason}`);
|
|
53
|
-
return { action: "continue", result };
|
|
54
|
-
}
|
|
55
|
-
return { action: "ok", result };
|
|
56
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
// KJC-TSK-0375 PR3 — ToolJudgeStage.
|
|
2
|
-
// Opt-in (`pipeline.tool_judge.enabled`). Scores the coder's tool calls
|
|
3
|
-
// extracted from `session.last_coder_transcript`. Non-blocking: a low
|
|
4
|
-
// score emits a warn event and surfaces in summary but never gates the
|
|
5
|
-
// iteration loop. Wiring lives in `quality-gates.js`.
|
|
6
|
-
|
|
7
|
-
import { ToolCorrectnessJudgeRole } from "../../roles/tool-correctness-judge-role.js";
|
|
8
|
-
import { extractToolCalls } from "../../utils/tool-call-extractor.js";
|
|
9
|
-
import { emitProgress, makeEvent } from "../../utils/events.js";
|
|
10
|
-
|
|
11
|
-
export async function runToolJudgeStage({
|
|
12
|
-
config, logger, emitter, eventBase, session, coderRole, trackBudget,
|
|
13
|
-
iteration, task,
|
|
14
|
-
extractFn = extractToolCalls,
|
|
15
|
-
RoleClass = ToolCorrectnessJudgeRole,
|
|
16
|
-
} = {}) {
|
|
17
|
-
logger?.setContext?.({ iteration, stage: "tool-judge" });
|
|
18
|
-
emitProgress(emitter, makeEvent("toolJudge:start", { ...eventBase, stage: "tool-judge" }, {
|
|
19
|
-
message: "Tool-judge scoring coder tool calls",
|
|
20
|
-
}));
|
|
21
|
-
|
|
22
|
-
const transcript = session?.last_coder_transcript || "";
|
|
23
|
-
const provider = coderRole?.provider || "claude";
|
|
24
|
-
const toolCalls = extractFn({ transcript, provider });
|
|
25
|
-
|
|
26
|
-
const role = new RoleClass({ config, logger, emitter });
|
|
27
|
-
const roleResult = await role.execute({ task, toolCalls });
|
|
28
|
-
|
|
29
|
-
try { trackBudget?.("tool-judge", roleResult.usage || { tokens_in: 0, tokens_out: 0, cost_usd: 0 }); } catch { /* best-effort */ }
|
|
30
|
-
|
|
31
|
-
const r = roleResult.result;
|
|
32
|
-
const status = r?.skipped ? "skip" : r?.lowQuality ? "warn" : roleResult.ok ? "ok" : "fail";
|
|
33
|
-
|
|
34
|
-
emitProgress(emitter, makeEvent("toolJudge:end", { ...eventBase, stage: "tool-judge" }, {
|
|
35
|
-
status,
|
|
36
|
-
message: roleResult.summary || "tool-judge done",
|
|
37
|
-
detail: { score: r?.score ?? null, threshold: r?.threshold ?? null, skipped: Boolean(r?.skipped), callCount: toolCalls.length },
|
|
38
|
-
}));
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
action: "ok",
|
|
42
|
-
stageResult: {
|
|
43
|
-
ok: roleResult.ok !== false,
|
|
44
|
-
score: r?.score ?? null,
|
|
45
|
-
lowQuality: Boolean(r?.lowQuality),
|
|
46
|
-
skipped: Boolean(r?.skipped),
|
|
47
|
-
threshold: r?.threshold ?? null,
|
|
48
|
-
summary: roleResult.summary || "",
|
|
49
|
-
breakdown: r?.breakdown || [],
|
|
50
|
-
callCount: toolCalls.length,
|
|
51
|
-
},
|
|
52
|
-
};
|
|
53
|
-
}
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Triage stage logic.
|
|
3
|
-
* Extracted from pre-loop-stages.js for maintainability.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { TriageRole } from "../../roles/triage-role.js";
|
|
7
|
-
import { addCheckpoint } from "../../session/store.js";
|
|
8
|
-
import { emitProgress, makeEvent, emitAgentOutput } from "../../utils/events.js";
|
|
9
|
-
import { selectModelsForRoles } from "../../utils/model-selector.js";
|
|
10
|
-
import { createStallDetector } from "../../utils/stall-detector.js";
|
|
11
|
-
|
|
12
|
-
const ROLE_NAMES = ["planner", "researcher", "architect", "refactorer", "reviewer", "tester", "security", "impeccable"];
|
|
13
|
-
|
|
14
|
-
// What each role is for. Used to explain — deterministically — why triage would
|
|
15
|
-
// or would not turn a role on. These describe the role's trigger, never a claim
|
|
16
|
-
// about the current task, so the rationale is never invented per run.
|
|
17
|
-
const ROLE_PURPOSE = {
|
|
18
|
-
planner: "planificación multi-fichero",
|
|
19
|
-
researcher: "investigación de contexto externo",
|
|
20
|
-
architect: "decisiones de arquitectura",
|
|
21
|
-
refactorer: "refactor de código existente",
|
|
22
|
-
reviewer: "revisión de cambios",
|
|
23
|
-
tester: "cobertura de tests dedicada",
|
|
24
|
-
security: "auditoría de seguridad",
|
|
25
|
-
impeccable: "auditoría de diseño"
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
function buildRoleOverrides(recommendedRoles, pipelineConfig) {
|
|
29
|
-
const overrides = {};
|
|
30
|
-
for (const role of ROLE_NAMES) {
|
|
31
|
-
overrides[`${role}Enabled`] = recommendedRoles.has(role) || Boolean(pipelineConfig[role]?.enabled);
|
|
32
|
-
}
|
|
33
|
-
return overrides;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Per-role explanation of the activate/skip decision, mirroring buildRoleOverrides.
|
|
37
|
-
// When triage failed, recommendations are unavailable: roles resolve from config
|
|
38
|
-
// alone and skipped ones get a neutral "sin datos de triage" reason.
|
|
39
|
-
function buildRoleRationale(recommendedRoles, pipelineConfig, triageOk) {
|
|
40
|
-
return ROLE_NAMES.map((role) => {
|
|
41
|
-
const forced = Boolean(pipelineConfig[role]?.enabled);
|
|
42
|
-
const recommended = triageOk && recommendedRoles.has(role);
|
|
43
|
-
const purpose = ROLE_PURPOSE[role];
|
|
44
|
-
|
|
45
|
-
if (recommended) {
|
|
46
|
-
return { role, enabled: true, source: "triage", reason: `recomendado por triage — ${purpose}` };
|
|
47
|
-
}
|
|
48
|
-
if (forced) {
|
|
49
|
-
return { role, enabled: true, source: "config", reason: `forzado por configuración — ${purpose}` };
|
|
50
|
-
}
|
|
51
|
-
const reason = triageOk
|
|
52
|
-
? `no recomendado — ${purpose} sin aplicar`
|
|
53
|
-
: `sin datos de triage — ${purpose} sin evaluar`;
|
|
54
|
-
return { role, enabled: false, source: "none", reason };
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function applyModelSelection(triageOutput, config, emitter, eventBase) {
|
|
59
|
-
if (!triageOutput.ok || !config?.model_selection?.enabled) return null;
|
|
60
|
-
const level = triageOutput.result?.level;
|
|
61
|
-
if (!level) return null;
|
|
62
|
-
|
|
63
|
-
const { modelOverrides, reasoning } = selectModelsForRoles({ level, config });
|
|
64
|
-
for (const [role, model] of Object.entries(modelOverrides)) {
|
|
65
|
-
if (config.roles?.[role] && !config.roles[role].model) {
|
|
66
|
-
config.roles[role].model = model;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
const modelSelection = { modelOverrides, reasoning };
|
|
70
|
-
emitProgress(
|
|
71
|
-
emitter,
|
|
72
|
-
makeEvent("model-selection:applied", { ...eventBase, stage: "triage" }, {
|
|
73
|
-
message: "Smart model selection applied",
|
|
74
|
-
detail: modelSelection
|
|
75
|
-
})
|
|
76
|
-
);
|
|
77
|
-
return modelSelection;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export async function runTriageStage({ config, logger, emitter, eventBase, session, coderRole, trackBudget }) {
|
|
81
|
-
logger.setContext({ iteration: 0, stage: "triage" });
|
|
82
|
-
|
|
83
|
-
const triageProvider = config?.roles?.triage?.provider || coderRole.provider;
|
|
84
|
-
emitProgress(
|
|
85
|
-
emitter,
|
|
86
|
-
makeEvent("triage:start", { ...eventBase, stage: "triage" }, {
|
|
87
|
-
message: "Triage classifying task complexity",
|
|
88
|
-
detail: { provider: triageProvider, executorType: "agent" }
|
|
89
|
-
})
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
const triageOnOutput = (payload) => emitAgentOutput(emitter, eventBase, "triage", triageProvider, payload);
|
|
93
|
-
const triageStall = createStallDetector({
|
|
94
|
-
onOutput: triageOnOutput, emitter, eventBase, stage: "triage", provider: triageProvider
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
const triage = new TriageRole({ config, logger, emitter });
|
|
98
|
-
await triage.init({ task: session.task, sessionId: session.id, iteration: 0 });
|
|
99
|
-
const triageStart = Date.now();
|
|
100
|
-
let triageOutput;
|
|
101
|
-
try {
|
|
102
|
-
triageOutput = await triage.run({ task: session.task, onOutput: triageStall.onOutput });
|
|
103
|
-
} catch (err) {
|
|
104
|
-
logger.warn(`Triage threw: ${err.message}`);
|
|
105
|
-
triageOutput = { ok: false, summary: `Triage error: ${err.message}`, result: { error: err.message } };
|
|
106
|
-
} finally {
|
|
107
|
-
triageStall.stop();
|
|
108
|
-
}
|
|
109
|
-
trackBudget({
|
|
110
|
-
role: "triage",
|
|
111
|
-
provider: config?.roles?.triage?.provider || coderRole.provider,
|
|
112
|
-
model: config?.roles?.triage?.model || coderRole.model,
|
|
113
|
-
result: triageOutput,
|
|
114
|
-
duration_ms: Date.now() - triageStart
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
await addCheckpoint(session, {
|
|
118
|
-
stage: "triage",
|
|
119
|
-
iteration: 0,
|
|
120
|
-
ok: triageOutput.ok,
|
|
121
|
-
provider: config?.roles?.triage?.provider || coderRole.provider,
|
|
122
|
-
model: config?.roles?.triage?.model || coderRole.model || null
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
const recommendedRoles = new Set(triageOutput.result?.roles || []);
|
|
126
|
-
const roleOverrides = triageOutput.ok
|
|
127
|
-
? buildRoleOverrides(recommendedRoles, config.pipeline || {})
|
|
128
|
-
: {};
|
|
129
|
-
|
|
130
|
-
const shouldDecompose = triageOutput.result?.shouldDecompose || false;
|
|
131
|
-
const subtasks = triageOutput.result?.subtasks || [];
|
|
132
|
-
|
|
133
|
-
const domainHints = triageOutput.result?.domainHints || [];
|
|
134
|
-
|
|
135
|
-
const stageResult = {
|
|
136
|
-
ok: triageOutput.ok,
|
|
137
|
-
level: triageOutput.result?.level || null,
|
|
138
|
-
roles: Array.from(recommendedRoles),
|
|
139
|
-
reasoning: triageOutput.result?.reasoning || null,
|
|
140
|
-
taskType: triageOutput.result?.taskType || "sw",
|
|
141
|
-
shouldDecompose,
|
|
142
|
-
subtasks,
|
|
143
|
-
domainHints,
|
|
144
|
-
roleRationale: buildRoleRationale(recommendedRoles, config.pipeline || {}, triageOutput.ok)
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
const modelSelection = applyModelSelection(triageOutput, config, emitter, eventBase);
|
|
148
|
-
if (modelSelection) {
|
|
149
|
-
stageResult.modelSelection = modelSelection;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (shouldDecompose && subtasks.length > 0) {
|
|
153
|
-
emitProgress(
|
|
154
|
-
emitter,
|
|
155
|
-
makeEvent("triage:decompose", { ...eventBase, stage: "triage" }, {
|
|
156
|
-
message: `Task decomposition recommended: ${subtasks.length} subtask${subtasks.length === 1 ? "" : "s"}`,
|
|
157
|
-
detail: { shouldDecompose, subtasks }
|
|
158
|
-
})
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
emitProgress(
|
|
163
|
-
emitter,
|
|
164
|
-
makeEvent("triage:end", { ...eventBase, stage: "triage" }, {
|
|
165
|
-
status: triageOutput.ok ? "ok" : "fail",
|
|
166
|
-
message: triageOutput.ok ? "Triage completed" : `Triage failed: ${triageOutput.summary}`,
|
|
167
|
-
detail: { ...stageResult, provider: triageProvider, executorType: "agent" }
|
|
168
|
-
})
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
return { roleOverrides, stageResult };
|
|
172
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { emitProgress, makeEvent } from "../utils/events.js";
|
|
2
|
-
import { setStatus } from "../session/mutators.js";
|
|
3
|
-
|
|
4
|
-
const DEFAULT_COOLDOWN_MS = 5 * 60 * 1000; // 5 minutes
|
|
5
|
-
const MAX_COOLDOWN_MS = 30 * 60 * 1000; // 30 minutes
|
|
6
|
-
const MAX_STANDBY_RETRIES = 5;
|
|
7
|
-
const HEARTBEAT_INTERVAL_MS = 30 * 1000; // 30 seconds
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Wait for a rate limit cooldown, emitting heartbeat events.
|
|
11
|
-
* Returns when the cooldown expires.
|
|
12
|
-
*
|
|
13
|
-
* @param {object} options
|
|
14
|
-
* @param {number|null} options.cooldownMs - Milliseconds to wait (null = use default)
|
|
15
|
-
* @param {string|null} options.cooldownUntil - ISO timestamp when cooldown expires
|
|
16
|
-
* @param {string} options.agent - Agent that was rate-limited
|
|
17
|
-
* @param {number} options.retryCount - Current retry attempt (for backoff)
|
|
18
|
-
* @param {object} options.emitter - Event emitter
|
|
19
|
-
* @param {object} options.eventBase - Base event fields
|
|
20
|
-
* @param {object} options.logger
|
|
21
|
-
* @param {object} options.session
|
|
22
|
-
*/
|
|
23
|
-
export async function waitForCooldown({ cooldownMs, cooldownUntil, agent, retryCount, emitter, eventBase, logger, session }) {
|
|
24
|
-
// Calculate wait time with exponential backoff for retries without known cooldown
|
|
25
|
-
let waitMs = cooldownMs || DEFAULT_COOLDOWN_MS;
|
|
26
|
-
if (!cooldownMs && retryCount > 0) {
|
|
27
|
-
waitMs = Math.min(DEFAULT_COOLDOWN_MS * Math.pow(2, retryCount), MAX_COOLDOWN_MS);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const resumeAt = cooldownUntil || new Date(Date.now() + waitMs).toISOString();
|
|
31
|
-
|
|
32
|
-
logger.info(`Standby: waiting ${Math.round(waitMs / 1000)}s for ${agent} rate limit (retry ${retryCount + 1}/${MAX_STANDBY_RETRIES})`);
|
|
33
|
-
|
|
34
|
-
// Emit standby start event
|
|
35
|
-
emitProgress(emitter, makeEvent("coder:standby", { ...eventBase, stage: "standby" }, {
|
|
36
|
-
message: `Rate limited — standby until ${resumeAt} (attempt ${retryCount + 1}/${MAX_STANDBY_RETRIES})`,
|
|
37
|
-
detail: { agent, cooldownUntil: resumeAt, cooldownMs: waitMs, retryCount: retryCount + 1, maxRetries: MAX_STANDBY_RETRIES }
|
|
38
|
-
}));
|
|
39
|
-
|
|
40
|
-
// Update session status
|
|
41
|
-
setStatus(session, "standby");
|
|
42
|
-
|
|
43
|
-
// Wait with periodic heartbeats
|
|
44
|
-
const startTime = Date.now();
|
|
45
|
-
const endTime = startTime + waitMs;
|
|
46
|
-
|
|
47
|
-
while (Date.now() < endTime) {
|
|
48
|
-
const remaining = endTime - Date.now();
|
|
49
|
-
const sleepTime = Math.min(HEARTBEAT_INTERVAL_MS, remaining);
|
|
50
|
-
|
|
51
|
-
await new Promise(resolve => setTimeout(resolve, sleepTime));
|
|
52
|
-
|
|
53
|
-
if (Date.now() < endTime) {
|
|
54
|
-
const remainingSec = Math.round((endTime - Date.now()) / 1000);
|
|
55
|
-
emitProgress(emitter, makeEvent("coder:standby_heartbeat", { ...eventBase, stage: "standby" }, {
|
|
56
|
-
message: `Standby: ${remainingSec}s remaining`,
|
|
57
|
-
detail: { agent, remainingMs: endTime - Date.now(), retryCount: retryCount + 1 }
|
|
58
|
-
}));
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Emit resume event
|
|
63
|
-
emitProgress(emitter, makeEvent("coder:standby_resume", { ...eventBase, stage: "standby" }, {
|
|
64
|
-
message: `Cooldown expired — resuming with ${agent}`,
|
|
65
|
-
detail: { agent, retryCount: retryCount + 1 }
|
|
66
|
-
}));
|
|
67
|
-
|
|
68
|
-
setStatus(session, "running");
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export { DEFAULT_COOLDOWN_MS, MAX_COOLDOWN_MS, MAX_STANDBY_RETRIES, HEARTBEAT_INTERVAL_MS };
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
// Verification gate: checks whether the coder actually produced changes.
|
|
2
|
-
// Prevents advancing the pipeline when coder iterations produce 0 file changes.
|
|
3
|
-
|
|
4
|
-
import { execFileSync } from "node:child_process";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @typedef {Object} VerificationResult
|
|
8
|
-
* @property {boolean} passed - true if coder produced real changes
|
|
9
|
-
* @property {number} filesChanged - count of modified/added/deleted files
|
|
10
|
-
* @property {number} linesChanged - total lines added/deleted
|
|
11
|
-
* @property {string[]} files - list of changed file paths
|
|
12
|
-
* @property {string} reason - explanation when not passed
|
|
13
|
-
* @property {string} [retryStrategy] - suggested retry approach when not passed
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Count files and lines changed since a base reference.
|
|
18
|
-
* Uses git diff --numstat to get both file and line counts.
|
|
19
|
-
*
|
|
20
|
-
* Audit follow-up: was using execSync with template-string interpolation
|
|
21
|
-
* of `baseRef` and `projectDir`. Both come ultimately from session
|
|
22
|
-
* state / config — not user shell input — but the shape was a known
|
|
23
|
-
* shell-injection vector. Switched to execFileSync with arg arrays so
|
|
24
|
-
* no interpolation reaches /bin/sh.
|
|
25
|
-
*/
|
|
26
|
-
export function countChangesSince(baseRef, projectDir = null) {
|
|
27
|
-
try {
|
|
28
|
-
const args = ["diff", "--numstat", String(baseRef)];
|
|
29
|
-
if (projectDir) args.push("--", String(projectDir));
|
|
30
|
-
const output = execFileSync("git", args, {
|
|
31
|
-
encoding: "utf8", stdio: ["pipe", "pipe", "pipe"],
|
|
32
|
-
}).trim();
|
|
33
|
-
|
|
34
|
-
if (!output) return { files: [], filesChanged: 0, linesAdded: 0, linesDeleted: 0 };
|
|
35
|
-
|
|
36
|
-
const lines = output.split("\n");
|
|
37
|
-
const files = [];
|
|
38
|
-
let linesAdded = 0;
|
|
39
|
-
let linesDeleted = 0;
|
|
40
|
-
|
|
41
|
-
for (const line of lines) {
|
|
42
|
-
const [added, deleted, file] = line.split("\t");
|
|
43
|
-
if (!file) continue;
|
|
44
|
-
files.push(file);
|
|
45
|
-
linesAdded += Number(added) || 0;
|
|
46
|
-
linesDeleted += Number(deleted) || 0;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return { files, filesChanged: files.length, linesAdded, linesDeleted };
|
|
50
|
-
} catch (err) {
|
|
51
|
-
// Git itself failed (baseRef invalid, repo corrupt, git binary
|
|
52
|
-
// missing). Previously we returned `filesChanged: 0` silently —
|
|
53
|
-
// indistinguishable from "the coder did nothing". Surface the
|
|
54
|
-
// error so verifyCoderOutput stops blaming the agent for an
|
|
55
|
-
// infrastructure failure.
|
|
56
|
-
return {
|
|
57
|
-
files: [], filesChanged: 0, linesAdded: 0, linesDeleted: 0,
|
|
58
|
-
gitError: err?.stderr?.toString?.() || err?.message || String(err),
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Also count untracked files (new files not yet added to git).
|
|
65
|
-
*
|
|
66
|
-
* Audit follow-up: same execSync→execFileSync change as countChangesSince.
|
|
67
|
-
*/
|
|
68
|
-
export function countUntrackedFiles(projectDir = null) {
|
|
69
|
-
try {
|
|
70
|
-
const args = ["ls-files", "--others", "--exclude-standard"];
|
|
71
|
-
if (projectDir) args.push(String(projectDir));
|
|
72
|
-
const output = execFileSync("git", args, {
|
|
73
|
-
encoding: "utf8", stdio: ["pipe", "pipe", "pipe"],
|
|
74
|
-
}).trim();
|
|
75
|
-
|
|
76
|
-
if (!output) return { files: [] };
|
|
77
|
-
return { files: output.split("\n").filter(Boolean) };
|
|
78
|
-
} catch (err) {
|
|
79
|
-
return {
|
|
80
|
-
files: [],
|
|
81
|
-
gitError: err?.stderr?.toString?.() || err?.message || String(err),
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Verify that the coder produced real changes.
|
|
88
|
-
* Counts both tracked changes and untracked files.
|
|
89
|
-
*
|
|
90
|
-
* @param {Object} params
|
|
91
|
-
* @param {string} params.baseRef - git reference to compare against
|
|
92
|
-
* @param {string} [params.projectDir] - directory scope
|
|
93
|
-
* @param {number} [params.minFiles=1] - minimum files that must change
|
|
94
|
-
* @param {number} [params.minLines=1] - minimum lines that must change
|
|
95
|
-
* @returns {VerificationResult}
|
|
96
|
-
*/
|
|
97
|
-
export function verifyCoderOutput({ baseRef, projectDir = null, minFiles = 1, minLines = 1 }) {
|
|
98
|
-
const tracked = countChangesSince(baseRef, projectDir);
|
|
99
|
-
const untracked = countUntrackedFiles(projectDir);
|
|
100
|
-
|
|
101
|
-
// Git infra failure (bad baseRef, corrupt repo, git missing) — not
|
|
102
|
-
// the coder's fault. Bail out with `gitError` so the caller can log
|
|
103
|
-
// it and skip the "retry with explicit file paths" feedback that
|
|
104
|
-
// would otherwise blame the agent.
|
|
105
|
-
const gitError = tracked.gitError || untracked.gitError;
|
|
106
|
-
if (gitError) {
|
|
107
|
-
return {
|
|
108
|
-
passed: false,
|
|
109
|
-
filesChanged: 0,
|
|
110
|
-
linesChanged: 0,
|
|
111
|
-
files: [],
|
|
112
|
-
reason: `Git verification failed: ${gitError}`,
|
|
113
|
-
retryStrategy: null,
|
|
114
|
-
gitError,
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const untrackedFiles = untracked.files || [];
|
|
119
|
-
const totalFiles = tracked.filesChanged + untrackedFiles.length;
|
|
120
|
-
const totalLines = tracked.linesAdded + tracked.linesDeleted;
|
|
121
|
-
const allFiles = [...tracked.files, ...untrackedFiles];
|
|
122
|
-
|
|
123
|
-
const passed = totalFiles >= minFiles && (untrackedFiles.length > 0 || totalLines >= minLines);
|
|
124
|
-
|
|
125
|
-
if (passed) {
|
|
126
|
-
return {
|
|
127
|
-
passed: true,
|
|
128
|
-
filesChanged: totalFiles,
|
|
129
|
-
linesChanged: totalLines,
|
|
130
|
-
files: allFiles,
|
|
131
|
-
reason: "",
|
|
132
|
-
retryStrategy: null
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return {
|
|
137
|
-
passed: false,
|
|
138
|
-
filesChanged: totalFiles,
|
|
139
|
-
linesChanged: totalLines,
|
|
140
|
-
files: allFiles,
|
|
141
|
-
reason: totalFiles === 0
|
|
142
|
-
? "Coder produced 0 file changes"
|
|
143
|
-
: `Only ${totalFiles} file(s) and ${totalLines} line(s) changed (below minimum)`,
|
|
144
|
-
retryStrategy: buildRetryStrategy(totalFiles, totalLines)
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Build a retry strategy based on the verification failure.
|
|
150
|
-
*/
|
|
151
|
-
function buildRetryStrategy(filesChanged, _linesChanged) {
|
|
152
|
-
if (filesChanged === 0) {
|
|
153
|
-
return "Rephrase the feedback with explicit file paths. List specific files to create/modify. Include example code snippets.";
|
|
154
|
-
}
|
|
155
|
-
return "Expand the prompt with more specific instructions. Break the task into smaller concrete steps.";
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Track consecutive verification failures to detect stuck loops.
|
|
160
|
-
*/
|
|
161
|
-
export class VerificationTracker {
|
|
162
|
-
constructor() {
|
|
163
|
-
this.consecutiveFailures = 0;
|
|
164
|
-
this.history = [];
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
record(result) {
|
|
168
|
-
this.history.push({ ...result, timestamp: Date.now() });
|
|
169
|
-
if (result.passed) {
|
|
170
|
-
this.consecutiveFailures = 0;
|
|
171
|
-
} else {
|
|
172
|
-
this.consecutiveFailures += 1;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
isStuck(threshold = 2) {
|
|
177
|
-
return this.consecutiveFailures >= threshold;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
getLastFailure() {
|
|
181
|
-
return [...this.history].reverse().find(h => !h.passed) || null;
|
|
182
|
-
}
|
|
183
|
-
}
|
package/src/orchestrator.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Orchestrator public API — thin barrel over the implementation in
|
|
3
|
-
* src/orchestrator/flow-runner.js.
|
|
4
|
-
*
|
|
5
|
-
* Why this file is tiny: the 2 000-line monolith that used to live here was
|
|
6
|
-
* extracted in KJC-TSK-0315 so that adding a new stage or routing rule no
|
|
7
|
-
* longer requires touching a god-module. Consumers keep importing from
|
|
8
|
-
* `src/orchestrator.js` — only the internal layout changed.
|
|
9
|
-
*
|
|
10
|
-
* See also:
|
|
11
|
-
* - src/orchestrator/flow-runner.js — runFlow + resumeFlow internals
|
|
12
|
-
* - src/orchestrator/stages/stage-executor.js — the StageExecutor contract
|
|
13
|
-
* that individual stages can implement to register themselves with
|
|
14
|
-
* StageRegistry.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
export { runFlow, resumeFlow } from "./orchestrator/flow-runner.js";
|
|
18
|
-
export { loadProductContext } from "./orchestrator/config-init.js";
|
|
19
|
-
export {
|
|
20
|
-
shouldAutoContinueCheckpoint,
|
|
21
|
-
parseCheckpointAnswer
|
|
22
|
-
} from "./orchestrator/flow-control.js";
|