karajan-code 4.70.4 → 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/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/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 +75 -48
- package/src/commands/review-gate.js +421 -341
- 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/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 +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 +1 -35
- package/src/mcp/handlers/management-handlers.js +2 -42
- 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 +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/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-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/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
package/src/roles/tester-role.js
DELETED
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
import { AgentRole } from "./agent-role.js";
|
|
2
|
-
import { extractFirstJson } from "../utils/json-extract.js";
|
|
3
|
-
import { detectTestFramework } from "../utils/project-detect.js";
|
|
4
|
-
import { loadAvailableSkills, buildSkillSection } from "../skills/skill-loader.js";
|
|
5
|
-
|
|
6
|
-
const SUBAGENT_PREAMBLE = [
|
|
7
|
-
"IMPORTANT: You are running as a Karajan sub-agent.",
|
|
8
|
-
"Do NOT ask about using Karajan, do NOT mention Karajan, do NOT suggest orchestration.",
|
|
9
|
-
"Do NOT use any MCP tools. Focus only on evaluating test quality."
|
|
10
|
-
].join(" ");
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Map test framework to the command that runs tests with coverage.
|
|
14
|
-
*/
|
|
15
|
-
const COVERAGE_COMMANDS = {
|
|
16
|
-
vitest: "npx vitest run --coverage --reporter=verbose 2>&1",
|
|
17
|
-
jest: "npx jest --coverage --verbose 2>&1",
|
|
18
|
-
mocha: "npx c8 mocha 2>&1",
|
|
19
|
-
playwright: "npx playwright test 2>&1",
|
|
20
|
-
pytest: "pytest --cov --tb=short 2>&1",
|
|
21
|
-
"go-test": "go test -cover -v ./... 2>&1",
|
|
22
|
-
"cargo-test": "cargo test --verbose 2>&1",
|
|
23
|
-
junit: "mvn test 2>&1",
|
|
24
|
-
rspec: "bundle exec rspec 2>&1",
|
|
25
|
-
phpunit: "vendor/bin/phpunit --coverage-text 2>&1",
|
|
26
|
-
"dotnet-test": "dotnet test --collect:\"XPlat Code Coverage\" 2>&1",
|
|
27
|
-
"dart-test": "dart test 2>&1",
|
|
28
|
-
// INF-B: the basic suite ALWAYS runs first; checkov is the additive deep
|
|
29
|
-
// scan. If checkov is missing this chain fails and the brief falls back to
|
|
30
|
-
// the basic suite alone (declared degradation, never green-by-omission).
|
|
31
|
-
"terraform-validate": "terraform init -backend=false -input=false 2>&1 && terraform validate 2>&1 && checkov -d . --compact --quiet 2>&1",
|
|
32
|
-
"helm-lint": "helm lint . 2>&1 && checkov -d . --compact --quiet 2>&1",
|
|
33
|
-
"kustomize-build": "kustomize build . > /dev/null && checkov -d . --compact --quiet 2>&1",
|
|
34
|
-
"ansible-lint": "ansible-lint 2>&1 && checkov -d . --compact --quiet 2>&1"
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const TEST_COMMANDS = {
|
|
38
|
-
vitest: "npx vitest run --reporter=verbose 2>&1",
|
|
39
|
-
jest: "npx jest --verbose 2>&1",
|
|
40
|
-
mocha: "npx mocha 2>&1",
|
|
41
|
-
playwright: "npx playwright test 2>&1",
|
|
42
|
-
pytest: "pytest --tb=short 2>&1",
|
|
43
|
-
"go-test": "go test -v ./... 2>&1",
|
|
44
|
-
"cargo-test": "cargo test --verbose 2>&1",
|
|
45
|
-
junit: "mvn test 2>&1",
|
|
46
|
-
rspec: "bundle exec rspec 2>&1",
|
|
47
|
-
phpunit: "vendor/bin/phpunit 2>&1",
|
|
48
|
-
"dotnet-test": "dotnet test 2>&1",
|
|
49
|
-
"dart-test": "dart test 2>&1",
|
|
50
|
-
// INF-B (KJC-TSK-0759): infra suites — validity IS the test floor.
|
|
51
|
-
"terraform-validate": "terraform init -backend=false -input=false 2>&1 && terraform validate 2>&1",
|
|
52
|
-
"helm-lint": "helm lint . 2>&1",
|
|
53
|
-
"kustomize-build": "kustomize build . > /dev/null && echo kustomize build: OK",
|
|
54
|
-
"ansible-lint": "ansible-lint 2>&1"
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export class TesterRole extends AgentRole {
|
|
58
|
-
constructor(opts) {
|
|
59
|
-
super({ ...opts, name: "tester" });
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
extractInput(input) {
|
|
63
|
-
if (typeof input === "string") return { task: input, diff: null, sonarIssues: null, pendingGherkinTests: null, shellTestResults: null };
|
|
64
|
-
return {
|
|
65
|
-
task: input?.task || this.context?.task || "",
|
|
66
|
-
diff: input?.diff || null,
|
|
67
|
-
sonarIssues: input?.sonarIssues || null,
|
|
68
|
-
// Tests-first Phase 3 (v2.7.5): the tester now has two extra inputs.
|
|
69
|
-
// - pendingGherkinTests: HU-declared Gherkin scenarios that need
|
|
70
|
-
// translation into executable code tests in the project's
|
|
71
|
-
// framework. The tester writes the test files and includes them
|
|
72
|
-
// in the suite run before computing verdict.
|
|
73
|
-
// - shellTestResults: results of running the HU's shell-type
|
|
74
|
-
// acceptance_tests (already run upstream, passed at this point).
|
|
75
|
-
// Passed for context so the tester doesn't re-run them redundantly.
|
|
76
|
-
pendingGherkinTests: input?.pendingGherkinTests || null,
|
|
77
|
-
shellTestResults: input?.shellTestResults || null,
|
|
78
|
-
onOutput: input?.onOutput || null
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async buildPrompt({ task, diff, sonarIssues, pendingGherkinTests, shellTestResults }) {
|
|
83
|
-
const projectDir = this.config?.projectDir || process.cwd();
|
|
84
|
-
const detection = await detectTestFramework(projectDir);
|
|
85
|
-
|
|
86
|
-
const sections = [SUBAGENT_PREAMBLE];
|
|
87
|
-
if (this.instructions) sections.push(this.instructions);
|
|
88
|
-
|
|
89
|
-
sections.push(
|
|
90
|
-
"You are a test quality gate. Your job is to EXECUTE the test suite, measure REAL coverage, and evaluate quality.",
|
|
91
|
-
"You MUST run the actual test command — do NOT guess or estimate results."
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
if (detection.hasTests && detection.framework) {
|
|
95
|
-
const coverageCmd = COVERAGE_COMMANDS[detection.framework];
|
|
96
|
-
const testCmd = TEST_COMMANDS[detection.framework];
|
|
97
|
-
// INF-B: infra suites have no node_modules — the step-0 contract is
|
|
98
|
-
// "the tool exists or you SAY which one is missing", never a fake green.
|
|
99
|
-
const step0 = detection.language === "infra"
|
|
100
|
-
? "**Step 0**: Verify the SUITE tool is installed (`which terraform`/`helm`/`kustomize`/`ansible-lint`). If the suite tool is MISSING, do NOT fake a green: report it in `failures` with the exact install command and set tests_pass: false. `checkov` (the deep scan below) is OPTIONAL: if it is missing, run the basic suite as the fallback and SAY the deep scan was skipped (degraded, never green-by-omission)."
|
|
101
|
-
: "**Step 0**: If node_modules/ does not exist, run `npm install` (or `pnpm install`) first.";
|
|
102
|
-
sections.push(
|
|
103
|
-
`## Detected test framework: ${detection.framework} (${detection.language})`,
|
|
104
|
-
step0,
|
|
105
|
-
`**Step 1**: Run the test suite with coverage:`,
|
|
106
|
-
"```bash",
|
|
107
|
-
coverageCmd || testCmd,
|
|
108
|
-
"```",
|
|
109
|
-
"If the coverage command fails (missing dependency), fall back to:",
|
|
110
|
-
"```bash",
|
|
111
|
-
testCmd,
|
|
112
|
-
"```",
|
|
113
|
-
"**Step 2**: Parse the output to extract: pass/fail count, coverage percentages, any failures.",
|
|
114
|
-
"**Step 3**: Return a single JSON object with REAL numbers from the test output."
|
|
115
|
-
);
|
|
116
|
-
} else {
|
|
117
|
-
sections.push(
|
|
118
|
-
"## No test framework detected",
|
|
119
|
-
"**Step 0**: If package.json exists but node_modules/ does not, run `npm install` first.",
|
|
120
|
-
"**Step 1**: Look at package.json or project files to find test scripts.",
|
|
121
|
-
"**Step 2**: Try running `npm test` or detect the framework from config files.",
|
|
122
|
-
"**Step 3**: If tests exist, run them. If no tests exist, report tests_pass: false with verdict: 'fail'.",
|
|
123
|
-
"**Step 4**: Return a JSON object with your findings."
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Tests-first Phase 3 (v2.7.5): include the upstream shell-test results
|
|
128
|
-
// so the tester knows which part of the contract is already green.
|
|
129
|
-
if (Array.isArray(shellTestResults) && shellTestResults.length > 0) {
|
|
130
|
-
const passedCount = shellTestResults.filter((r) => r.passed).length;
|
|
131
|
-
sections.push(
|
|
132
|
-
"## Acceptance gate — shell tests",
|
|
133
|
-
`The HU's shell-type acceptance_tests already ran upstream: ${passedCount}/${shellTestResults.length} passed.`,
|
|
134
|
-
"Do NOT re-run them. Use the coverage/translation steps below to decide your verdict."
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Gherkin translation block — only when the HU carries Gherkin
|
|
139
|
-
// scenarios. Tester must produce real test files (in the detected
|
|
140
|
-
// framework's language) that encode Given/When/Then. This is how
|
|
141
|
-
// behaviour-level specs become enforceable.
|
|
142
|
-
if (Array.isArray(pendingGherkinTests) && pendingGherkinTests.length > 0) {
|
|
143
|
-
sections.push(
|
|
144
|
-
"## Gherkin acceptance tests to translate",
|
|
145
|
-
"These Gherkin scenarios are the HU's behavioural contract. They are NOT yet",
|
|
146
|
-
"executable — your job is to turn each one into a concrete test case in the",
|
|
147
|
-
"project's test framework, WRITE the test files, and INCLUDE them in the suite",
|
|
148
|
-
"run that determines your verdict.",
|
|
149
|
-
"",
|
|
150
|
-
"For each scenario below:",
|
|
151
|
-
"1. Pick the appropriate test file (use `target` hint if present; otherwise",
|
|
152
|
-
" put the test next to the module it exercises).",
|
|
153
|
-
"2. Write a test that asserts the Given/When/Then as-is. Do NOT soften the",
|
|
154
|
-
" assertion — if it's ambiguous, pick the stricter reading.",
|
|
155
|
-
"3. Make sure the test runs as part of the normal suite (no separate invocation).",
|
|
156
|
-
"",
|
|
157
|
-
"If ANY translated test fails on the current code, return verdict: \"fail\" with",
|
|
158
|
-
"a failing_scenarios array listing the scenarios that didn't pass. The coder",
|
|
159
|
-
"will iterate.",
|
|
160
|
-
""
|
|
161
|
-
);
|
|
162
|
-
pendingGherkinTests.forEach((entry, i) => {
|
|
163
|
-
const content = typeof entry === "string" ? entry : entry?.content || "";
|
|
164
|
-
const target = entry && entry.file ? ` · target: \`${entry.file}\`` : "";
|
|
165
|
-
sections.push(
|
|
166
|
-
`### Scenario ${i + 1}${target}`,
|
|
167
|
-
"```gherkin",
|
|
168
|
-
content,
|
|
169
|
-
"```",
|
|
170
|
-
""
|
|
171
|
-
);
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
sections.push(
|
|
176
|
-
"",
|
|
177
|
-
"Return ONLY a single valid JSON object:",
|
|
178
|
-
'{"tests_pass":boolean,"coverage":{"overall":number,"services":number,"utilities":number},"missing_scenarios":[string],"quality_issues":[string],"failing_scenarios":[string],"translated_scenarios":[string],"verdict":"pass"|"fail"}',
|
|
179
|
-
"",
|
|
180
|
-
"- coverage.overall MUST be a real number from the test runner output, NOT an estimate",
|
|
181
|
-
"- If coverage tool is not available, set coverage.overall to null (not 0, not a guess)",
|
|
182
|
-
"- tests_pass must reflect whether the actual test run succeeded",
|
|
183
|
-
"- failing_scenarios: when Gherkin was given, list the scenarios your translated tests CAUGHT as failing",
|
|
184
|
-
"- translated_scenarios: when Gherkin was given, list the scenarios you successfully turned into passing tests",
|
|
185
|
-
"- verdict MUST be \"fail\" if any translated scenario still fails (even if coverage is green)",
|
|
186
|
-
`## Task\n${task}`
|
|
187
|
-
);
|
|
188
|
-
if (diff) sections.push(`## Git diff\n${diff}`);
|
|
189
|
-
if (sonarIssues) sections.push(`## Sonar test issues\n${sonarIssues}`);
|
|
190
|
-
|
|
191
|
-
// Inject test-relevant skills (filtered by role=tester to only include
|
|
192
|
-
// testing patterns like pytest-patterns, vitest-patterns, etc.)
|
|
193
|
-
const skills = await loadAvailableSkills(projectDir);
|
|
194
|
-
const skillSection = buildSkillSection(skills, {
|
|
195
|
-
provider: this._resolvedProvider || (typeof this.resolveProvider === "function" ? this.resolveProvider() : null),
|
|
196
|
-
role: "tester",
|
|
197
|
-
});
|
|
198
|
-
if (skillSection) sections.push(skillSection);
|
|
199
|
-
|
|
200
|
-
return { prompt: sections.join("\n\n") };
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
parseOutput(raw) { return extractFirstJson(raw); }
|
|
204
|
-
|
|
205
|
-
isSuccessful(parsed) {
|
|
206
|
-
const verdict = parsed.verdict || (parsed.tests_pass ? "pass" : "fail");
|
|
207
|
-
return verdict === "pass";
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
buildSuccessResult(parsed, provider) {
|
|
211
|
-
const verdict = parsed.verdict || (parsed.tests_pass ? "pass" : "fail");
|
|
212
|
-
return {
|
|
213
|
-
tests_pass: Boolean(parsed.tests_pass),
|
|
214
|
-
coverage: parsed.coverage || {},
|
|
215
|
-
missing_scenarios: parsed.missing_scenarios || [],
|
|
216
|
-
quality_issues: parsed.quality_issues || [],
|
|
217
|
-
// Tests-first Phase 3: Gherkin translation outcomes.
|
|
218
|
-
failing_scenarios: Array.isArray(parsed.failing_scenarios) ? parsed.failing_scenarios : [],
|
|
219
|
-
translated_scenarios: Array.isArray(parsed.translated_scenarios) ? parsed.translated_scenarios : [],
|
|
220
|
-
verdict,
|
|
221
|
-
provider
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
buildSummary(parsed) {
|
|
226
|
-
const verdict = parsed.verdict || (parsed.tests_pass ? "pass" : "fail");
|
|
227
|
-
const coverage = parsed.coverage || {};
|
|
228
|
-
const coverageStr = coverage.overall != null ? `${coverage.overall}%` : "not measured";
|
|
229
|
-
const missingPart = parsed.missing_scenarios?.length ? `; ${parsed.missing_scenarios.length} missing scenario(s)` : "";
|
|
230
|
-
const qualityPart = parsed.quality_issues?.length ? `; ${parsed.quality_issues.length} quality issue(s)` : "";
|
|
231
|
-
const failingPart = parsed.failing_scenarios?.length ? `; ${parsed.failing_scenarios.length} failing scenario(s)` : "";
|
|
232
|
-
const translatedPart = parsed.translated_scenarios?.length ? `; ${parsed.translated_scenarios.length} scenario(s) translated` : "";
|
|
233
|
-
return `Verdict: ${verdict}; Coverage: ${coverageStr}${translatedPart}${failingPart}${missingPart}${qualityPart}`;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
// KJC-TSK-0375 — ToolCorrectnessJudgeRole.
|
|
2
|
-
// Post-iteration LLM judge scoring how well a coder picked its tool calls.
|
|
3
|
-
// PR1 ships role + tests; wiring lives in PR3.
|
|
4
|
-
|
|
5
|
-
import { AgentRole } from "./agent-role.js";
|
|
6
|
-
import { buildToolCorrectnessJudgePrompt } from "../prompts/tool-correctness-judge.js";
|
|
7
|
-
import { extractFirstJson } from "../utils/json-extract.js";
|
|
8
|
-
|
|
9
|
-
const DEFAULT_THRESHOLD = 70;
|
|
10
|
-
|
|
11
|
-
const clampScore = (n) => Number.isFinite(n) ? Math.max(0, Math.min(100, Math.round(n))) : null;
|
|
12
|
-
|
|
13
|
-
const normalizeBreakdown = (b) => Array.isArray(b) ? b.map((x) => ({
|
|
14
|
-
tool: typeof x?.tool === "string" ? x.tool : "unknown",
|
|
15
|
-
args: x?.args && typeof x.args === "object" ? x.args : {},
|
|
16
|
-
correct: Boolean(x?.correct),
|
|
17
|
-
reason: typeof x?.reason === "string" ? x.reason.trim() : "",
|
|
18
|
-
})) : [];
|
|
19
|
-
|
|
20
|
-
export class ToolCorrectnessJudgeRole extends AgentRole {
|
|
21
|
-
constructor(opts) {
|
|
22
|
-
super({ ...opts, name: "tool-correctness-judge" });
|
|
23
|
-
this.threshold = opts?.threshold ?? this.config?.pipeline?.tool_judge?.threshold ?? DEFAULT_THRESHOLD;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
resolveProvider() {
|
|
27
|
-
return this.config?.roles?.["tool-correctness-judge"]?.provider
|
|
28
|
-
|| this.config?.roles?.reviewer?.provider
|
|
29
|
-
|| this.config?.roles?.coder?.provider
|
|
30
|
-
|| "claude";
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
extractInput(input) {
|
|
34
|
-
return { ...super.extractInput(input), toolCalls: Array.isArray(input?.toolCalls) ? input.toolCalls : [] };
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async buildPrompt({ task, toolCalls }) {
|
|
38
|
-
return { prompt: buildToolCorrectnessJudgePrompt({ task, toolCalls, instructions: this.instructions }) };
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
parseOutput(raw) { return extractFirstJson(raw); }
|
|
42
|
-
|
|
43
|
-
buildSuccessResult(parsed, provider) {
|
|
44
|
-
const score = clampScore(parsed?.score);
|
|
45
|
-
return {
|
|
46
|
-
score,
|
|
47
|
-
summary: typeof parsed?.summary === "string" ? parsed.summary.trim() : "",
|
|
48
|
-
breakdown: normalizeBreakdown(parsed?.breakdown),
|
|
49
|
-
threshold: this.threshold,
|
|
50
|
-
lowQuality: score !== null && score < this.threshold,
|
|
51
|
-
provider,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
buildSummary(parsed) {
|
|
56
|
-
const score = clampScore(parsed?.score);
|
|
57
|
-
if (score === null) return "Tool-judge: unparseable score";
|
|
58
|
-
return `Tool-judge: ${score}/100 (${score < this.threshold ? "LOW" : "OK"})`;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Empty tool-call list = nothing to judge. Skip the LLM and report a
|
|
62
|
-
// neutral score so an absent transcript is never read as a failure.
|
|
63
|
-
async execute(input) {
|
|
64
|
-
const extracted = this.extractInput(input);
|
|
65
|
-
if (extracted.toolCalls.length === 0) {
|
|
66
|
-
return {
|
|
67
|
-
ok: true,
|
|
68
|
-
result: {
|
|
69
|
-
score: null, summary: "no tool calls captured (skipped)", breakdown: [],
|
|
70
|
-
threshold: this.threshold, lowQuality: false, provider: this.resolveProvider(), skipped: true,
|
|
71
|
-
},
|
|
72
|
-
summary: "Tool-judge: skipped (no calls)",
|
|
73
|
-
usage: null,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
return super.execute(extracted);
|
|
77
|
-
}
|
|
78
|
-
}
|
package/src/roles/triage-role.js
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { AgentRole } from "./agent-role.js";
|
|
2
|
-
import { buildTriagePrompt } from "../prompts/triage.js";
|
|
3
|
-
import { VALID_TASK_TYPES } from "../guards/policy-resolver.js";
|
|
4
|
-
import { extractFirstJson } from "../utils/json-extract.js";
|
|
5
|
-
|
|
6
|
-
const VALID_LEVELS = new Set(["trivial", "simple", "medium", "complex"]);
|
|
7
|
-
const VALID_ROLES = new Set(["planner", "researcher", "refactorer", "reviewer", "tester", "security", "impeccable"]);
|
|
8
|
-
const FALLBACK_TASK_TYPE = "sw";
|
|
9
|
-
const FALLBACK_RESULT = { level: "medium", roles: ["reviewer"], taskType: FALLBACK_TASK_TYPE, shouldDecompose: false };
|
|
10
|
-
|
|
11
|
-
function normalizeRoles(roles) {
|
|
12
|
-
if (!Array.isArray(roles)) return [];
|
|
13
|
-
return Array.from(new Set(roles.filter((role) => VALID_ROLES.has(role))));
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function normalizeSubtasks(subtasks) {
|
|
17
|
-
if (!Array.isArray(subtasks)) return [];
|
|
18
|
-
return subtasks.map((s) => (typeof s === "string" ? s.trim() : "")).filter(Boolean).slice(0, 5);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function normalizeDomainHints(hints) {
|
|
22
|
-
if (!Array.isArray(hints)) return [];
|
|
23
|
-
return hints.map((h) => (typeof h === "string" ? h.trim().toLowerCase() : "")).filter(Boolean);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export class TriageRole extends AgentRole {
|
|
27
|
-
constructor(opts) {
|
|
28
|
-
super({ ...opts, name: "triage" });
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async buildPrompt({ task }) {
|
|
32
|
-
return { prompt: buildTriagePrompt({ task, instructions: this.instructions }) };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
parseOutput(raw) { return extractFirstJson(raw); }
|
|
36
|
-
|
|
37
|
-
buildSuccessResult(parsed, provider) {
|
|
38
|
-
const level = VALID_LEVELS.has(parsed.level) ? parsed.level : "medium";
|
|
39
|
-
const roles = normalizeRoles(parsed.roles);
|
|
40
|
-
const reasoning = String(parsed.reasoning || "").trim() || "No reasoning provided.";
|
|
41
|
-
const shouldDecompose = Boolean(parsed.shouldDecompose);
|
|
42
|
-
const subtasks = normalizeSubtasks(parsed.subtasks);
|
|
43
|
-
const taskType = VALID_TASK_TYPES.has(parsed.taskType) ? parsed.taskType : FALLBACK_TASK_TYPE;
|
|
44
|
-
const domainHints = normalizeDomainHints(parsed.domainHints);
|
|
45
|
-
|
|
46
|
-
const result = { level, roles, reasoning, taskType, domainHints, provider };
|
|
47
|
-
if (shouldDecompose && subtasks.length > 0) {
|
|
48
|
-
result.shouldDecompose = true;
|
|
49
|
-
result.subtasks = subtasks;
|
|
50
|
-
} else {
|
|
51
|
-
result.shouldDecompose = false;
|
|
52
|
-
}
|
|
53
|
-
return result;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
buildSummary(parsed) {
|
|
57
|
-
const level = VALID_LEVELS.has(parsed.level) ? parsed.level : "medium";
|
|
58
|
-
const roles = normalizeRoles(parsed.roles);
|
|
59
|
-
const decomposeNote = parsed.shouldDecompose ? " — decomposition recommended" : "";
|
|
60
|
-
return `Triage: ${level} (${roles.length} role${roles.length === 1 ? "" : "s"})${decomposeNote}`;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
handleParseNull(agentResult, provider) {
|
|
64
|
-
return this.degradedTriageResult({
|
|
65
|
-
agentResult, provider,
|
|
66
|
-
reason: "parse-null",
|
|
67
|
-
reasoning: "Unstructured output, using safe defaults.",
|
|
68
|
-
message: "[triage] LLM output was unstructured — falling back to defaults; the pipeline may skip optional roles.",
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
handleParseError(err, agentResult, provider) {
|
|
73
|
-
return this.degradedTriageResult({
|
|
74
|
-
agentResult, provider,
|
|
75
|
-
reason: "parse-error",
|
|
76
|
-
reasoning: `Failed to parse triage output (${err?.message || "unknown error"}), using safe defaults.`,
|
|
77
|
-
message: `[triage] Could not parse LLM output — falling back to defaults: ${err?.message || "unknown"}.`,
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Triage degradation is what made a complex task quietly run as
|
|
82
|
-
// `level: "medium", roles: ["reviewer"]` — researcher / architect /
|
|
83
|
-
// security / tester silently skipped. The fallback is preserved so
|
|
84
|
-
// the pipeline still moves, but the user (and any board / kj-tail
|
|
85
|
-
// listener) now learns it happened. result.degraded = true lets
|
|
86
|
-
// downstream code react if it wants.
|
|
87
|
-
// Same shape as before so downstream cost / event accounting is
|
|
88
|
-
// byte-identical; the only change is the loud logger.warn, which is
|
|
89
|
-
// what makes the silent degradation visible to the user / kj-tail.
|
|
90
|
-
degradedTriageResult({ agentResult, provider, reason: _reason, reasoning, message }) {
|
|
91
|
-
this.logger?.warn?.(message);
|
|
92
|
-
return {
|
|
93
|
-
ok: true,
|
|
94
|
-
result: { ...FALLBACK_RESULT, reasoning, provider, raw: agentResult.output },
|
|
95
|
-
summary: "Triage complete (fallback defaults)",
|
|
96
|
-
usage: agentResult.usage,
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
}
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Build a human-readable `decisions.md` journal file summarizing every
|
|
3
|
-
* Solomon ruling and Brain decision made during a session.
|
|
4
|
-
*
|
|
5
|
-
* Acceptance criterion (KJC-TSK-0287):
|
|
6
|
-
* - Given Solomon was invoked at least once → decisions.md is written with
|
|
7
|
-
* timestamp, trigger, conflict context, ruling (action + reasoning),
|
|
8
|
-
* and resulting actions for every invocation.
|
|
9
|
-
* - Given Solomon was NOT invoked → decisions.md is NOT created.
|
|
10
|
-
*
|
|
11
|
-
* Brain routing decisions (from KJC-TSK-0322) are appended as a secondary
|
|
12
|
-
* section when present, since they share the same "decisions" semantics.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import fs from "node:fs/promises";
|
|
16
|
-
import path from "node:path";
|
|
17
|
-
import { getSessionRoot } from "../../utils/paths.js";
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* @typedef {Object} SolomonRuling
|
|
21
|
-
* @property {string} at - ISO timestamp
|
|
22
|
-
* @property {string} stage - conflict stage (reviewer, brain-routing, ...)
|
|
23
|
-
* @property {string} trigger - short reason the invocation was triggered
|
|
24
|
-
* @property {Object} conflict - conflict context (task, reason, summary)
|
|
25
|
-
* @property {string} ruling - approve / continue / escalate_human / create_subtask / ...
|
|
26
|
-
* @property {string} [reasoning]
|
|
27
|
-
* @property {string[]} [conditions]
|
|
28
|
-
* @property {string} [alternativeAgent]
|
|
29
|
-
* @property {string} [result] - what the orchestrator did with the ruling
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* @typedef {Object} BrainDecisionRecord
|
|
34
|
-
* @property {number} seq
|
|
35
|
-
* @property {string} at
|
|
36
|
-
* @property {string} intent
|
|
37
|
-
* @property {string[]} rolesOn
|
|
38
|
-
* @property {string} confidence
|
|
39
|
-
* @property {string} rationale
|
|
40
|
-
* @property {string[]} appliedOverrides
|
|
41
|
-
*/
|
|
42
|
-
|
|
43
|
-
function esc(text) {
|
|
44
|
-
return String(text ?? "").replaceAll(/\|/g, "\\|");
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function formatConditions(conditions) {
|
|
48
|
-
if (!Array.isArray(conditions) || conditions.length === 0) return "—";
|
|
49
|
-
return conditions.map((c) => `- ${esc(c)}`).join("\n");
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function formatConflict(conflict) {
|
|
53
|
-
if (!conflict) return "—";
|
|
54
|
-
const parts = [];
|
|
55
|
-
if (conflict.task) parts.push(`**Task**: ${esc(conflict.task.slice(0, 200))}${conflict.task.length > 200 ? "…" : ""}`);
|
|
56
|
-
if (conflict.reason) parts.push(`**Reason**: ${esc(conflict.reason)}`);
|
|
57
|
-
if (conflict.summary) parts.push(`**Summary**: ${esc(conflict.summary)}`);
|
|
58
|
-
return parts.length > 0 ? parts.join("\n") : "—";
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Render a Solomon ruling as a Markdown subsection.
|
|
63
|
-
* @param {SolomonRuling} ruling
|
|
64
|
-
* @param {number} index
|
|
65
|
-
* @returns {string}
|
|
66
|
-
*/
|
|
67
|
-
function renderRuling(ruling, index) {
|
|
68
|
-
const at = ruling.at || "(unknown timestamp)";
|
|
69
|
-
const header = `### ${index + 1}. Solomon @ ${at} — ${esc(ruling.ruling || "unknown")}`;
|
|
70
|
-
const lines = [
|
|
71
|
-
header,
|
|
72
|
-
"",
|
|
73
|
-
`- **Stage**: ${esc(ruling.stage || "?")}`,
|
|
74
|
-
`- **Trigger**: ${esc(ruling.trigger || "?")}`,
|
|
75
|
-
`- **Ruling**: \`${esc(ruling.ruling || "?")}\``,
|
|
76
|
-
];
|
|
77
|
-
if (ruling.alternativeAgent) lines.push(`- **Alternative agent**: ${esc(ruling.alternativeAgent)}`);
|
|
78
|
-
if (ruling.result) lines.push(`- **Applied result**: ${esc(ruling.result)}`);
|
|
79
|
-
lines.push("");
|
|
80
|
-
if (ruling.reasoning) {
|
|
81
|
-
lines.push("**Reasoning:**", "");
|
|
82
|
-
lines.push(ruling.reasoning.trim(), "");
|
|
83
|
-
}
|
|
84
|
-
if (ruling.conditions?.length) {
|
|
85
|
-
lines.push("**Conditions:**", "");
|
|
86
|
-
lines.push(formatConditions(ruling.conditions), "");
|
|
87
|
-
}
|
|
88
|
-
lines.push("**Conflict context:**", "");
|
|
89
|
-
lines.push(formatConflict(ruling.conflict), "");
|
|
90
|
-
return lines.join("\n");
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function renderBrainDecision(decision, index) {
|
|
94
|
-
return [
|
|
95
|
-
`### ${index + 1}. Brain @ ${decision.at} — ${esc(decision.intent)}`,
|
|
96
|
-
"",
|
|
97
|
-
`- **Confidence**: ${esc(decision.confidence)}`,
|
|
98
|
-
`- **Roles on**: ${(decision.rolesOn || []).map(esc).join(", ") || "—"}`,
|
|
99
|
-
`- **Overrides applied**: ${(decision.appliedOverrides || []).map(esc).join(", ") || "—"}`,
|
|
100
|
-
"",
|
|
101
|
-
`> ${esc(decision.rationale || "")}`,
|
|
102
|
-
"",
|
|
103
|
-
].join("\n");
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Build the Markdown content for a session. Returns `null` if no Solomon
|
|
108
|
-
* rulings are present (per acceptance criteria — no Solomon, no file).
|
|
109
|
-
*
|
|
110
|
-
* @param {Object} session
|
|
111
|
-
* @returns {string|null}
|
|
112
|
-
*/
|
|
113
|
-
export function buildDecisionsMarkdown(session) {
|
|
114
|
-
const rulings = Array.isArray(session?.solomonRulings) ? session.solomonRulings : [];
|
|
115
|
-
const brainDecisions = Array.isArray(session?.brainDecisions) ? session.brainDecisions : [];
|
|
116
|
-
|
|
117
|
-
// AC: no Solomon invocation → do not generate the file.
|
|
118
|
-
if (rulings.length === 0) return null;
|
|
119
|
-
|
|
120
|
-
const sessionId = session?.id || "(unknown)";
|
|
121
|
-
const header = [
|
|
122
|
-
`# Session decisions — ${sessionId}`,
|
|
123
|
-
"",
|
|
124
|
-
`_Generated at ${new Date().toISOString()}._`,
|
|
125
|
-
"",
|
|
126
|
-
`This journal records every decision the pipeline delegated to Solomon, plus Brain routing decisions for cross-reference.`,
|
|
127
|
-
"",
|
|
128
|
-
"## Summary",
|
|
129
|
-
"",
|
|
130
|
-
`- Solomon invocations: **${rulings.length}**`,
|
|
131
|
-
`- Brain routing decisions: **${brainDecisions.length}**`,
|
|
132
|
-
"",
|
|
133
|
-
"---",
|
|
134
|
-
"",
|
|
135
|
-
"## Solomon rulings",
|
|
136
|
-
"",
|
|
137
|
-
].join("\n");
|
|
138
|
-
|
|
139
|
-
const solomonSection = rulings.map((r, i) => renderRuling(r, i)).join("\n---\n\n");
|
|
140
|
-
|
|
141
|
-
const brainSection = brainDecisions.length > 0
|
|
142
|
-
? "\n---\n\n## Brain routing decisions\n\n" + brainDecisions.map((d, i) => renderBrainDecision(d, i)).join("\n")
|
|
143
|
-
: "";
|
|
144
|
-
|
|
145
|
-
return header + solomonSection + brainSection + "\n";
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Persist decisions.md to the session's journal directory. No-op when there
|
|
150
|
-
* are no Solomon rulings (per acceptance criteria).
|
|
151
|
-
*
|
|
152
|
-
* Path resolution (first match wins):
|
|
153
|
-
* 1. options.journalDir — direct path to the session journal directory
|
|
154
|
-
* 2. options.sessionRoot + id — combines with session.id
|
|
155
|
-
* 3. getSessionRoot() + id — default Karajan session root
|
|
156
|
-
*
|
|
157
|
-
* @param {Object} session
|
|
158
|
-
* @param {{ journalDir?: string, sessionRoot?: string, logger?: Object }} [options]
|
|
159
|
-
* @returns {Promise<{ written: boolean, path: string|null }>}
|
|
160
|
-
*/
|
|
161
|
-
export async function writeDecisionsJournal(session, options = {}) {
|
|
162
|
-
const content = buildDecisionsMarkdown(session);
|
|
163
|
-
if (content === null) return { written: false, path: null };
|
|
164
|
-
|
|
165
|
-
const sessionDir = options.journalDir
|
|
166
|
-
|| path.join(options.sessionRoot || getSessionRoot(), session.id);
|
|
167
|
-
const filePath = path.join(sessionDir, "decisions.md");
|
|
168
|
-
try {
|
|
169
|
-
await fs.mkdir(sessionDir, { recursive: true });
|
|
170
|
-
await fs.writeFile(filePath, content, "utf8");
|
|
171
|
-
options.logger?.info?.(`Wrote decisions journal: ${filePath}`);
|
|
172
|
-
return { written: true, path: filePath };
|
|
173
|
-
} catch (err) {
|
|
174
|
-
options.logger?.warn?.(`Failed to write decisions journal (non-blocking): ${err.message}`);
|
|
175
|
-
return { written: false, path: null };
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Append a Solomon ruling to session.solomonRulings. Creates the array if
|
|
181
|
-
* absent. Callers (invokeSolomon) pass the structured record.
|
|
182
|
-
*
|
|
183
|
-
* @param {Object} session
|
|
184
|
-
* @param {SolomonRuling} record
|
|
185
|
-
*/
|
|
186
|
-
export function recordSolomonRuling(session, record) {
|
|
187
|
-
if (!session) return;
|
|
188
|
-
if (!Array.isArray(session.solomonRulings)) session.solomonRulings = [];
|
|
189
|
-
session.solomonRulings.push({
|
|
190
|
-
at: record.at || new Date().toISOString(),
|
|
191
|
-
stage: record.stage || "unknown",
|
|
192
|
-
trigger: record.trigger || "",
|
|
193
|
-
conflict: record.conflict || {},
|
|
194
|
-
ruling: record.ruling || "unknown",
|
|
195
|
-
reasoning: record.reasoning || "",
|
|
196
|
-
conditions: Array.isArray(record.conditions) ? [...record.conditions] : [],
|
|
197
|
-
alternativeAgent: record.alternativeAgent || null,
|
|
198
|
-
result: record.result || "",
|
|
199
|
-
});
|
|
200
|
-
}
|