karajan-code 4.70.3 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/package.json +6 -14
- package/packages/hu-board/package.json +1 -1
- package/packages/hu-board/public/app.js +4 -18
- package/packages/hu-board/public/index.html +5 -2
- package/packages/hu-board/public/styles.css +26 -0
- package/packages/hu-board/public/utils/api.js +0 -74
- package/packages/hu-board/public/utils/board-tour.js +62 -0
- package/packages/hu-board/public/utils/board-view.js +49 -144
- package/packages/hu-board/public/utils/command-launcher.js +8 -70
- package/packages/hu-board/public/utils/formatters.js +10 -0
- package/packages/hu-board/public/utils/governance-view.js +13 -1
- package/packages/hu-board/public/utils/hu-actions.js +17 -68
- package/packages/hu-board/public/utils/init-listeners.js +7 -8
- package/packages/hu-board/public/utils/log-panel.js +3 -16
- package/packages/hu-board/public/utils/maggle-mode.js +2 -12
- package/packages/hu-board/public/utils/modals.js +7 -0
- package/packages/hu-board/public/utils/project-picker-view.js +1 -1
- package/packages/hu-board/public/utils/server-push.js +3 -22
- package/packages/hu-board/public/utils/shared-board-notice.js +33 -0
- package/packages/hu-board/public/utils/story-detail-view.js +5 -19
- package/packages/hu-board/src/cleanup-zombies.js +0 -1
- package/packages/hu-board/src/command-runner.js +36 -25
- package/packages/hu-board/src/db-connection.js +282 -0
- package/packages/hu-board/src/db-paths.js +113 -0
- package/packages/hu-board/src/db-queries.js +188 -0
- package/packages/hu-board/src/db-tombstones.js +51 -0
- package/packages/hu-board/src/db.js +28 -619
- package/packages/hu-board/src/ephemeral-cleaner.js +2 -3
- package/packages/hu-board/src/hu-zombie-reaper.js +5 -1
- package/packages/hu-board/src/plan-mutations.js +3 -153
- package/packages/hu-board/src/plans-service.js +55 -0
- package/packages/hu-board/src/routes/api/commands.js +122 -0
- package/packages/hu-board/src/routes/api/plans.js +55 -0
- package/packages/hu-board/src/routes/api/projects.js +267 -0
- package/packages/hu-board/src/routes/api/prompts.js +135 -0
- package/packages/hu-board/src/routes/api/sessions.js +95 -0
- package/packages/hu-board/src/routes/api/stories.js +183 -0
- package/packages/hu-board/src/routes/api/system.js +241 -0
- package/packages/hu-board/src/routes/api/tombstones.js +38 -0
- package/packages/hu-board/src/routes/api.js +24 -1371
- package/packages/hu-board/src/routes/governance.js +23 -1
- package/packages/hu-board/src/routes/lanes.js +40 -0
- package/packages/hu-board/src/routes/rag.js +4 -3
- package/packages/hu-board/src/server.js +20 -34
- package/packages/hu-board/src/sync-files.js +318 -0
- package/packages/hu-board/src/sync-project.js +99 -0
- package/packages/hu-board/src/sync.js +22 -566
- package/packages/hu-board/src/zombie-reaper.js +1 -1
- package/scripts/dual-publish.mjs +19 -3
- package/scripts/postinstall.js +15 -2
- package/scripts/verify-pack-mcp.mjs +11 -7
- package/scripts/verify-pack.mjs +4 -4
- package/src/audit/audit-history.js +3 -2
- package/src/audit/dead-exports.js +68 -59
- package/src/audit/deterministic-summary.js +33 -13
- package/src/audit/npm-signatures.js +41 -0
- package/src/audit/sbom.js +35 -0
- package/src/checks/agent-token.js +112 -0
- package/src/checks/ai-surface.js +9 -7
- package/src/checks/config-files.js +8 -7
- package/src/checks/dir-setup.js +34 -1
- package/src/checks/native-build.js +21 -39
- package/src/checks/runner.js +128 -142
- package/src/cli/advanced-commands.js +6 -6
- package/src/cli/register-meta.js +42 -82
- package/src/cli/register-pipeline.js +8 -140
- package/src/cli/register-plan.js +9 -100
- package/src/cli/retired-commands.js +49 -0
- package/src/cli.js +18 -5
- package/src/commands/agent-run.js +40 -26
- package/src/commands/audit.js +127 -124
- package/src/commands/board.js +5 -9
- package/src/commands/check.js +29 -16
- package/src/commands/doctor.js +2 -0
- package/src/commands/env.js +176 -148
- package/src/commands/go.js +76 -55
- package/src/commands/harden.js +105 -86
- package/src/commands/hu-start.js +52 -0
- package/src/commands/hu.js +288 -221
- package/src/commands/identity.js +111 -78
- package/src/commands/init-atril.js +47 -0
- package/src/commands/init.js +11 -40
- package/src/commands/install-tools.js +70 -85
- package/src/commands/main-guard.js +78 -0
- package/src/commands/policy.js +226 -184
- package/src/commands/qmd.js +27 -21
- package/src/commands/rag.js +75 -48
- package/src/commands/review-gate.js +422 -339
- package/src/commands/start.js +4 -5
- package/src/commands/steward.js +96 -47
- package/src/commands/tournament.js +46 -37
- package/src/commands/worktree.js +94 -25
- package/src/config/loader.js +34 -17
- package/src/config/overrides.js +2 -1
- package/src/config/secrets.js +36 -0
- package/src/config.js +1 -0
- package/src/environment/adr.js +16 -2
- package/src/environment/board-access.js +11 -9
- package/src/git/karajan-label.js +20 -8
- package/src/guards/main-guard.js +54 -0
- package/src/harden/branch-protection.js +36 -3
- package/src/harden/hook-templates.js +16 -1
- package/src/harden/sentinel/posttooluse.mjs +18 -6
- package/src/harden/sentinel/pretooluse-sentinel.mjs +81 -35
- package/src/harden/sentinel/sentinel-lib.mjs +149 -23
- package/src/harden/sentinel/sentinel-shell.mjs +64 -0
- package/src/harden/sentinel/sessionstart.mjs +3 -2
- package/src/harden/sentinel/stop.mjs +5 -4
- package/src/harden/workflow-engine.js +12 -1
- package/src/harden/workflow-templates.js +1 -1
- package/src/hu/board-lookup.js +29 -0
- package/src/hu/gh-identity.js +23 -0
- package/src/hu/github-board.js +4 -2
- package/src/hu/lane-prune.js +15 -7
- package/src/hu/lane-setup.js +65 -0
- package/src/hu/lanes.js +66 -0
- package/src/hu/team-board-mirror.js +3 -2
- package/src/infrastructure/command-runner.js +1 -1
- package/src/infrastructure/file-system-service.js +1 -1
- package/src/mcp/handlers/direct-handlers.js +2 -292
- package/src/mcp/handlers/hu-handlers.js +44 -55
- package/src/mcp/handlers/management-handlers.js +2 -42
- package/src/mcp/server-handlers.js +16 -22
- package/src/mcp/tools.js +5 -200
- package/src/plan/lane-plans.js +54 -0
- package/src/plan/plan-store.js +7 -4
- package/src/policy/add.js +26 -12
- package/src/policy/adr-number-check.js +31 -0
- package/src/prompts/audit.js +299 -339
- package/src/prompts/card-context.js +10 -5
- package/src/prompts/reviewer.js +5 -2
- package/src/rag/auto-update.js +56 -43
- package/src/rag/bootstrap-index.js +28 -54
- package/src/rag/migrate.js +1 -1
- package/src/rag/onnx-fallback.js +2 -2
- package/src/rag/pid-lease.js +70 -0
- package/src/rag/project-store.js +2 -12
- package/src/review/card-first.js +57 -46
- package/src/review/dual-review.js +5 -4
- package/src/review/gate-gitignore.js +23 -4
- package/src/review/gate-hook.js +47 -0
- package/src/review/one-shot-review.js +51 -37
- package/src/review/publish-pr.js +18 -3
- package/src/review/repo-docs.js +73 -0
- package/src/review/repo-stack.js +59 -0
- package/src/review/reviewer-fallback.js +34 -3
- package/src/review/seal-commit.js +29 -0
- package/src/review/sonar-pregate.js +59 -44
- package/src/review/third-party-review.js +90 -16
- package/src/review/third-party-size.js +43 -0
- package/src/review/third-party-skills.js +54 -0
- package/src/review/verify-pr.js +2 -26
- package/src/roles/audit-role.js +106 -125
- package/src/skills/atril-skills.js +5 -4
- package/src/sonar/scanner.js +116 -101
- package/src/steward/invariants.js +5 -4
- package/src/steward/phantom-sweep.js +66 -0
- package/src/tournament/judge.js +60 -53
- package/src/utils/agent-home.js +33 -0
- package/src/utils/gh-account.js +37 -0
- package/src/utils/git.js +0 -13
- package/src/utils/home-migration.js +7 -1
- package/src/utils/main-checkout.js +53 -0
- package/src/utils/qmd-collection.js +3 -1
- package/src/utils/welcome.js +3 -3
- package/templates/{skills → atril/commands}/kj-architect.md +5 -0
- package/templates/{skills → atril/commands}/kj-audit.md +5 -0
- package/templates/{skills → atril/commands}/kj-board.md +5 -0
- package/templates/{skills → atril/commands}/kj-code.md +5 -0
- package/templates/{skills → atril/commands}/kj-csv-transform.md +5 -0
- package/templates/{skills → atril/commands}/kj-data-report.md +5 -0
- package/templates/{skills → atril/commands}/kj-discover.md +5 -0
- package/templates/{skills → atril/commands}/kj-rag-query.md +5 -0
- package/templates/{skills → atril/commands}/kj-review.md +5 -0
- package/templates/{skills → atril/commands}/kj-security.md +5 -0
- package/templates/{skills → atril/commands}/kj-sonar.md +5 -0
- package/templates/{skills → atril/commands}/kj-sql-analysis.md +5 -0
- package/templates/{skills → atril/commands}/kj-test.md +5 -0
- package/packages/hu-board/public/utils/run-launcher.js +0 -169
- package/packages/hu-board/src/run-tracker.js +0 -121
- package/src/activity-log.js +0 -59
- package/src/agents/host-agent.js +0 -38
- package/src/agents/model-registry.js +0 -142
- package/src/audit/plan-adherence.js +0 -224
- package/src/autonomy/arbiter-role.js +0 -71
- package/src/autonomy/arbiter.js +0 -22
- package/src/autonomy/decider.js +0 -18
- package/src/autonomy/decision-resolver.js +0 -58
- package/src/autonomy/outcome-report.js +0 -55
- package/src/autonomy/stage-ask.js +0 -30
- package/src/brain/decision-tracker.js +0 -125
- package/src/brain/decisor.js +0 -243
- package/src/brain/solomon-consult.js +0 -171
- package/src/brain/standby-scheduler.js +0 -8
- package/src/budget/comparison.js +0 -96
- package/src/canvas/parse-md.js +0 -238
- package/src/canvas/schema.js +0 -101
- package/src/canvas/to-flat.js +0 -85
- package/src/canvas/validate.js +0 -163
- package/src/ci/dispatch.js +0 -99
- package/src/ci/index.js +0 -3
- package/src/ci/pr-diff.js +0 -26
- package/src/cli/register-standby.js +0 -26
- package/src/commands/architect.js +0 -111
- package/src/commands/autorun.js +0 -49
- package/src/commands/researcher.js +0 -54
- package/src/commands/resume.js +0 -72
- package/src/commands/review.js +0 -89
- package/src/commands/run.js +0 -185
- package/src/commands/standby.js +0 -117
- package/src/commands/status.js +0 -36
- package/src/commands/triage.js +0 -78
- package/src/commands/undo.js +0 -62
- package/src/domains/domain-loader.js +0 -245
- package/src/domains/domain-registry.js +0 -140
- package/src/domains/domain-synthesizer.js +0 -129
- package/src/git/automation.js +0 -433
- package/src/git/coder-commit-guard.js +0 -40
- package/src/git/hu-automation.js +0 -243
- package/src/git/hu-snapshot.js +0 -10
- package/src/git/symlink-targets.js +0 -57
- package/src/golden/asserter.js +0 -92
- package/src/golden/loader.js +0 -52
- package/src/golden/runner.js +0 -92
- package/src/golden/schema.js +0 -41
- package/src/guards/duplicate-members.js +0 -86
- package/src/guards/intent-guard.js +0 -161
- package/src/guards/perf-guard.js +0 -126
- package/src/guards/policy-guard.js +0 -90
- package/src/guards/policy-resolver.js +0 -85
- package/src/guards/secret-redactor.js +0 -71
- package/src/hu/acceptance-runner.js +0 -138
- package/src/hu/lazy-planner.js +0 -109
- package/src/hu/parallel-executor.js +0 -94
- package/src/hu/routing-metrics.js +0 -17
- package/src/hu/split-validator.js +0 -143
- package/src/hu/splitting-detector.js +0 -158
- package/src/hu/splitting-generator.js +0 -175
- package/src/hu/store.js +0 -394
- package/src/infrastructure/mocks.js +0 -182
- package/src/mcp/handlers/run-handler.js +0 -318
- package/src/mcp/sovereignty-guard.js +0 -113
- package/src/mcp/tool-arg-normalizers.js +0 -16
- package/src/mutate/reviewer-signal.js +0 -76
- package/src/orchestrator/agent-fallback.js +0 -81
- package/src/orchestrator/brain-coordinator.js +0 -186
- package/src/orchestrator/ci-integration.js +0 -139
- package/src/orchestrator/config-init.js +0 -426
- package/src/orchestrator/direct-actions.js +0 -217
- package/src/orchestrator/drivers/error-recovery.js +0 -287
- package/src/orchestrator/drivers/init-context.js +0 -238
- package/src/orchestrator/drivers/iteration-loop.js +0 -382
- package/src/orchestrator/drivers/iteration-phases/coder-and-refactorer.js +0 -69
- package/src/orchestrator/drivers/iteration-phases/guards.js +0 -91
- package/src/orchestrator/drivers/iteration-phases/handle-approved.js +0 -29
- package/src/orchestrator/drivers/iteration-phases/quality-gates.js +0 -125
- package/src/orchestrator/drivers/iteration-phases/reviewer-gate.js +0 -62
- package/src/orchestrator/drivers/post-loop.js +0 -404
- package/src/orchestrator/drivers/pre-loop-phases/auto-hu-batch.js +0 -137
- package/src/orchestrator/drivers/pre-loop-phases/config-deprecations.js +0 -47
- package/src/orchestrator/drivers/pre-loop-phases/ensure-addyosmani-skills.js +0 -84
- package/src/orchestrator/drivers/pre-loop-phases/inject-loaded-plan.js +0 -393
- package/src/orchestrator/drivers/pre-loop.js +0 -524
- package/src/orchestrator/drivers/run-hu-batch.js +0 -468
- package/src/orchestrator/feedback-enrichment.js +0 -140
- package/src/orchestrator/feedback-queue.js +0 -151
- package/src/orchestrator/flow-control.js +0 -170
- package/src/orchestrator/flow-runner.js +0 -365
- package/src/orchestrator/fs-leak-detector.js +0 -228
- package/src/orchestrator/hu-scheduler.js +0 -114
- package/src/orchestrator/hu-sub-pipeline.js +0 -642
- package/src/orchestrator/iteration-gate.js +0 -73
- package/src/orchestrator/iteration-stages.js +0 -9
- package/src/orchestrator/parallel-limiter.js +0 -74
- package/src/orchestrator/pipeline-context.js +0 -72
- package/src/orchestrator/post-loop-stages.js +0 -468
- package/src/orchestrator/pre-loop-stages.js +0 -11
- package/src/orchestrator/preflight-checks.js +0 -487
- package/src/orchestrator/repair/repair-detector.js +0 -117
- package/src/orchestrator/repair/repair-runner.js +0 -142
- package/src/orchestrator/reviewer-fallback.js +0 -45
- package/src/orchestrator/role-output-compressor.js +0 -189
- package/src/orchestrator/session-journal.js +0 -308
- package/src/orchestrator/solomon-escalation.js +0 -331
- package/src/orchestrator/solomon-rules.js +0 -212
- package/src/orchestrator/stages/acceptance-coverage-stage.js +0 -47
- package/src/orchestrator/stages/architect-stage.js +0 -172
- package/src/orchestrator/stages/coder-stage.js +0 -506
- package/src/orchestrator/stages/domain-curator-stage.js +0 -82
- package/src/orchestrator/stages/format-stage.js +0 -61
- package/src/orchestrator/stages/hu-reviewer-phases/detect-and-apply-splits.js +0 -127
- package/src/orchestrator/stages/hu-reviewer-stage.js +0 -419
- package/src/orchestrator/stages/perf-stage.js +0 -140
- package/src/orchestrator/stages/planner-stage.js +0 -85
- package/src/orchestrator/stages/rag-context-stage.js +0 -80
- package/src/orchestrator/stages/rag-preload-decisor.js +0 -58
- package/src/orchestrator/stages/research-stage.js +0 -69
- package/src/orchestrator/stages/reviewer-stage.js +0 -431
- package/src/orchestrator/stages/sonar-stage.js +0 -439
- package/src/orchestrator/stages/stage-classes.js +0 -84
- package/src/orchestrator/stages/stage-executor.js +0 -155
- package/src/orchestrator/stages/suite-green-stage.js +0 -62
- package/src/orchestrator/stages/tdd-discipline-stage.js +0 -56
- package/src/orchestrator/stages/tool-judge-stage.js +0 -53
- package/src/orchestrator/stages/triage-stage.js +0 -172
- package/src/orchestrator/standby.js +0 -71
- package/src/orchestrator/verification-gate.js +0 -183
- package/src/orchestrator.js +0 -22
- package/src/plan/adr-generator.js +0 -170
- package/src/plan/adr-loader.js +0 -60
- package/src/plan/plan-executor.js +0 -100
- package/src/plan/plan-validation.js +0 -4
- package/src/prompts/acceptance-coverage.js +0 -32
- package/src/prompts/arbiter.js +0 -38
- package/src/prompts/architect.js +0 -103
- package/src/prompts/hu-reviewer.js +0 -244
- package/src/prompts/tool-correctness-judge.js +0 -31
- package/src/prompts/triage.js +0 -57
- package/src/rag/embedder.js +0 -2
- package/src/rag/embedders/_cloud-base.js +0 -2
- package/src/rag/embedders/cohere.js +0 -2
- package/src/rag/embedders/mistral.js +0 -2
- package/src/rag/embedders/onnx.js +0 -2
- package/src/rag/embedders/openai.js +0 -2
- package/src/rag/embedders/voyage.js +0 -2
- package/src/rag/rerank.js +0 -4
- package/src/rag/where-parser.js +0 -4
- package/src/repeat-detector.js +0 -88
- package/src/review/diff-generator.js +0 -112
- package/src/review/git-stash-helper.js +0 -104
- package/src/review/schema.js +0 -31
- package/src/review/scope-filter.js +0 -152
- package/src/review/snapshot-diff.js +0 -122
- package/src/review/tdd-discipline.js +0 -90
- package/src/review/tdd-policy.js +0 -69
- package/src/roles/acceptance-coverage-role.js +0 -78
- package/src/roles/architect-role.js +0 -77
- package/src/roles/coder-role.js +0 -113
- package/src/roles/commiter-role.js +0 -113
- package/src/roles/domain-curator-role.js +0 -134
- package/src/roles/hu-reviewer-role.js +0 -64
- package/src/roles/impeccable-role.js +0 -85
- package/src/roles/index.js +0 -18
- package/src/roles/karajan-brain-role.js +0 -134
- package/src/roles/perf-role.js +0 -122
- package/src/roles/planner-role.js +0 -151
- package/src/roles/refactorer-role.js +0 -30
- package/src/roles/repairer-role.js +0 -181
- package/src/roles/researcher-role.js +0 -70
- package/src/roles/reviewer-role.js +0 -115
- package/src/roles/security-role.js +0 -73
- package/src/roles/solomon-role.js +0 -271
- package/src/roles/sonar-role.js +0 -72
- package/src/roles/tester-role.js +0 -235
- package/src/roles/tool-correctness-judge-role.js +0 -78
- package/src/roles/triage-role.js +0 -99
- package/src/session/journal/decisions-writer.js +0 -200
- package/src/session/journal/iteration-logger.js +0 -194
- package/src/session/journal/summary-writer.js +0 -320
- package/src/session/journal/tree-writer.js +0 -164
- package/src/session/journal/write-all.js +0 -89
- package/src/session-cleanup.js +0 -116
- package/src/skills/semantic-detector.js +0 -167
- package/src/skills/skill-detector.js +0 -385
- package/src/sonar/cloud-scanner.js +0 -78
- package/src/tracker-bootstrap.js +0 -44
- package/src/types/index.js +0 -38
- package/src/types/policy.js +0 -43
- package/src/types/solomon.js +0 -87
- package/src/utils/async-lock.js +0 -31
- package/src/utils/budget.js +0 -229
- package/src/utils/cwd-confirm.js +0 -39
- package/src/utils/display/event-handlers.js +0 -403
- package/src/utils/display/formatters.js +0 -132
- package/src/utils/display/header.js +0 -47
- package/src/utils/display/session-summary.js +0 -118
- package/src/utils/display/solomon.js +0 -42
- package/src/utils/pricing.js +0 -29
- package/src/utils/quota-simulator.js +0 -56
- package/src/utils/role-assigner.js +0 -137
- package/src/utils/rtk-wrapper.js +0 -87
- package/src/utils/run-registry.js +0 -9
- package/src/utils/status-dashboard.js +0 -254
- package/src/utils/tool-call-extractor.js +0 -56
- package/src/webperf/devtools-detect.js +0 -112
- package/src/webperf/webperf-role-integration.js +0 -66
- package/templates/skills/kj-run.md +0 -73
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* KJC-BUG-0032: detect coder writes outside projectDir.
|
|
3
|
-
*
|
|
4
|
-
* The coder subprocess runs with `cwd: projectDir`, but its Bash
|
|
5
|
-
* tool can `cd` anywhere. Real-world repro: a HU titled
|
|
6
|
-
* "Initialize project skeleton: create assistant/ directory…"
|
|
7
|
-
* had Claude run `cd /home/manu/assistant && pnpm init …`. The
|
|
8
|
-
* resulting 36 MB of code lived in $HOME/assistant — outside the
|
|
9
|
-
* projectDir, outside any git repo, lost to tracking, commits and
|
|
10
|
-
* review. The coder was happy, the user only noticed by accident.
|
|
11
|
-
*
|
|
12
|
-
* This module is a deterministic guard: snapshot $HOME's top-level
|
|
13
|
-
* directory entries before the coder runs, scan again after, and
|
|
14
|
-
* any new entry that isn't projectDir itself is a leak.
|
|
15
|
-
*
|
|
16
|
-
* Why top-level only:
|
|
17
|
-
* - Catches the realistic failure mode (coder creates a sibling
|
|
18
|
-
* `~/something` for the project skeleton).
|
|
19
|
-
* - Cheap (single readdirSync) — no `find -newer` traversal of
|
|
20
|
-
* $HOME's potentially massive tree.
|
|
21
|
-
* - Doesn't thrash on writes to existing dirs like ~/.npm or
|
|
22
|
-
* ~/.cache that legitimate node tooling touches.
|
|
23
|
-
*
|
|
24
|
-
* What it WON'T catch (acknowledged trade-offs):
|
|
25
|
-
* - Writes deep inside existing $HOME subdirs (e.g. modifying
|
|
26
|
-
* ~/.bashrc — would need a per-file mtime scan).
|
|
27
|
-
* - Writes to /tmp (high false-positive rate; /tmp is ephemeral
|
|
28
|
-
* by spec; less risky).
|
|
29
|
-
* - Writes to /etc, /var, etc. (would require root anyway).
|
|
30
|
-
*
|
|
31
|
-
* The first-pass guard is the right ROI. Subsequent iterations
|
|
32
|
-
* can extend coverage if real-world cases warrant it.
|
|
33
|
-
*/
|
|
34
|
-
|
|
35
|
-
import fs from "node:fs";
|
|
36
|
-
import os from "node:os";
|
|
37
|
-
import path from "node:path";
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Snapshot the immediate children of $HOME. Returns a Set of names
|
|
41
|
-
* (not full paths) so detectNewHomeEntries can do a quick set
|
|
42
|
-
* difference. Empty set on any FS error — treats "can't snapshot"
|
|
43
|
-
* the same as "snapshot OK with nothing".
|
|
44
|
-
*
|
|
45
|
-
* @returns {Set<string>}
|
|
46
|
-
*/
|
|
47
|
-
export function snapshotHomeTopLevel() {
|
|
48
|
-
try {
|
|
49
|
-
return new Set(fs.readdirSync(os.homedir()));
|
|
50
|
-
} catch {
|
|
51
|
-
return new Set();
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Detect $HOME top-level entries that didn't exist when `before`
|
|
57
|
-
* was snapshotted. Excludes projectDir itself in case the user
|
|
58
|
-
* keeps their projects directly under $HOME (no false positive
|
|
59
|
-
* if `projectDir = $HOME/some-project` already existed).
|
|
60
|
-
*
|
|
61
|
-
* @param {Set<string>} before — snapshot from snapshotHomeTopLevel()
|
|
62
|
-
* @param {string|null} projectDir — current run's projectDir
|
|
63
|
-
* @returns {string[]} absolute paths of new entries (potential leaks)
|
|
64
|
-
*/
|
|
65
|
-
export function detectNewHomeEntries(before, projectDir) {
|
|
66
|
-
const home = os.homedir();
|
|
67
|
-
let after;
|
|
68
|
-
try {
|
|
69
|
-
after = fs.readdirSync(home);
|
|
70
|
-
} catch {
|
|
71
|
-
return [];
|
|
72
|
-
}
|
|
73
|
-
const projectDirAbs = projectDir ? path.resolve(projectDir) : null;
|
|
74
|
-
const leaks = [];
|
|
75
|
-
for (const name of after) {
|
|
76
|
-
if (before.has(name)) continue;
|
|
77
|
-
const full = path.join(home, name);
|
|
78
|
-
// Skip projectDir itself (defensive — shouldn't happen since
|
|
79
|
-
// projectDir is pre-existing, but a paranoid check costs nothing).
|
|
80
|
-
if (projectDirAbs && path.resolve(full) === projectDirAbs) continue;
|
|
81
|
-
leaks.push(full);
|
|
82
|
-
}
|
|
83
|
-
return leaks;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Issue #546: filter potential leaks by attribution. Snapshot diff
|
|
88
|
-
* cannot tell coder writes from concurrent writes by other processes
|
|
89
|
-
* (host AI scratch files, parallel tooling, manual edits). Real-world
|
|
90
|
-
* 2026-04-29 incident: a host-side write of `~/karajan-status-snapshot.md`
|
|
91
|
-
* during a kj run aborted HU 003 with a false positive.
|
|
92
|
-
*
|
|
93
|
-
* Heuristic: if the coder genuinely created the leaked path, its
|
|
94
|
-
* transcript will mention the path or its basename — the coder
|
|
95
|
-
* narrates what it does (Bash commands, file operations, todos). If
|
|
96
|
-
* neither the absolute path nor the basename appears anywhere in
|
|
97
|
-
* the transcript, the entry was created by some other process during
|
|
98
|
-
* the iteration window and should NOT abort the HU.
|
|
99
|
-
*
|
|
100
|
-
* False-negative trade-off: a coder that creates a file silently
|
|
101
|
-
* (e.g. via a script that doesn't echo paths) escapes this filter.
|
|
102
|
-
* That trade-off is acceptable: the original leak class (coder
|
|
103
|
-
* skeleton-creating in $HOME) always emits the path in commands; the
|
|
104
|
-
* silent-write case has never been observed.
|
|
105
|
-
*
|
|
106
|
-
* Back-compat: when `transcript` is empty/null/undefined, NO filtering
|
|
107
|
-
* happens — every detected leak is passed through unchanged. This
|
|
108
|
-
* preserves the strict behaviour for callers that don't have a
|
|
109
|
-
* transcript handy.
|
|
110
|
-
*
|
|
111
|
-
* @param {string[]} leaks — output of detectNewHomeEntries()
|
|
112
|
-
* @param {string|null|undefined} transcript — coder's full output text
|
|
113
|
-
* @returns {string[]} filtered leaks (the subset attributable to coder)
|
|
114
|
-
*/
|
|
115
|
-
/**
|
|
116
|
-
* KJC-BUG-0180 — attribution is not severity. `verifyLeaksAgainstTranscript`
|
|
117
|
-
* stays strict on purpose: with no transcript it returns everything. But
|
|
118
|
-
* "the snapshot changed and there is NO evidence the coder did it" is a
|
|
119
|
-
* different fact from "the coder wrote there", and only the second one
|
|
120
|
-
* should kill the run. The flaky CI failure was exactly this: other test
|
|
121
|
-
* files writing under $HOME during the iteration window, an empty
|
|
122
|
-
* transcript from a mocked coder, and the whole flow failing and retrying.
|
|
123
|
-
*
|
|
124
|
-
* @returns {{attributed: string[], unattributed: string[]}} — `attributed`
|
|
125
|
-
* fails the run; `unattributed` is reported and the work goes on.
|
|
126
|
-
*/
|
|
127
|
-
export function splitLeakEvidence(leaks, transcript) {
|
|
128
|
-
const hasTranscript = typeof transcript === "string" && transcript.length > 0;
|
|
129
|
-
const found = verifyLeaksAgainstTranscript(leaks, transcript);
|
|
130
|
-
return hasTranscript ? { attributed: found, unattributed: [] } : { attributed: [], unattributed: found };
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export function verifyLeaksAgainstTranscript(leaks, transcript) {
|
|
134
|
-
if (!Array.isArray(leaks) || leaks.length === 0) return [];
|
|
135
|
-
if (!transcript || typeof transcript !== "string" || transcript.length === 0) {
|
|
136
|
-
return leaks; // no transcript ⇒ no filtering, original behaviour
|
|
137
|
-
}
|
|
138
|
-
return leaks.filter((p) => {
|
|
139
|
-
const base = path.basename(p);
|
|
140
|
-
// Skip artefacts whose basename is too short to be discriminating
|
|
141
|
-
// (e.g. ".cache") — a 3-char basename matched against a long
|
|
142
|
-
// transcript is meaningless. For such cases we prefer the strict
|
|
143
|
-
// behaviour (flag) since they're rare and the user can inspect.
|
|
144
|
-
if (base.length < 4) return true;
|
|
145
|
-
return transcript.includes(p) || transcript.includes(base);
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Layer 2 detection (KJC-BUG-0032): scan the coder's transcript for
|
|
151
|
-
* `cd <abs-path> && <write-cmd>` patterns where <abs-path> is OUTSIDE
|
|
152
|
-
* projectDir. Catches the bug class even when the target directory
|
|
153
|
-
* already existed (so snapshot-diff misses it) — e.g.
|
|
154
|
-
* `cd /home/manu/assistant && pnpm init -y`
|
|
155
|
-
* when `~/assistant` was pre-existing.
|
|
156
|
-
*
|
|
157
|
-
* Filters applied:
|
|
158
|
-
* - Only absolute paths (`/`, `~/`, `$HOME/`) — relative `cd subdir`
|
|
159
|
-
* is assumed inside projectDir.
|
|
160
|
-
* - Skips `/tmp/...` (ephemeral, legitimate scratch).
|
|
161
|
-
* - Skips targets inside projectDir.
|
|
162
|
-
* - Only flags if followed (same line, within 200 chars) by a
|
|
163
|
-
* write/creation command: mkdir / touch / cp / mv / git init /
|
|
164
|
-
* {pnpm,npm,yarn} init / {pnpm,npm,yarn,npx} create / cat > /
|
|
165
|
-
* echo > / shell redirect (>, >>).
|
|
166
|
-
* - Pure read commands (ls, which, cat <file>, grep) do NOT flag.
|
|
167
|
-
*
|
|
168
|
-
* @param {string|null|undefined} transcript
|
|
169
|
-
* @param {string|null} projectDir
|
|
170
|
-
* @returns {string[]} deduplicated absolute leak paths
|
|
171
|
-
*/
|
|
172
|
-
export function detectTranscriptCdLeaks(transcript, projectDir) {
|
|
173
|
-
if (!transcript || typeof transcript !== "string") return [];
|
|
174
|
-
const projectDirAbs = projectDir ? path.resolve(projectDir) : null;
|
|
175
|
-
const homeDir = os.homedir();
|
|
176
|
-
// Capture cd target + the next ~200 chars of the same line for write-cmd lookup.
|
|
177
|
-
// Two-step (S5843, complexity 31): capture ANY cd target with one simple
|
|
178
|
-
// class, then filter to the absolute/home-rooted shapes the original
|
|
179
|
-
// single alternation required (~/, $HOME/, /).
|
|
180
|
-
const cdRegex = /\bcd\s+([^\s&|;`'"\n]+)([^\n]{0,200})/g;
|
|
181
|
-
const absoluteTargetRe = /^(?:~\/|\$HOME\/|\/)/;
|
|
182
|
-
const writeCmdRegex = /\b(mkdir|touch|cp|mv|git\s+init|(?:pnpm|npm|yarn)\s+(?:init|create)|npx\s+create|cat\s*>|echo\s+[^|]*>|>>?\s*\S)/i;
|
|
183
|
-
const leaks = new Set();
|
|
184
|
-
let match;
|
|
185
|
-
while ((match = cdRegex.exec(transcript)) !== null) {
|
|
186
|
-
if (!absoluteTargetRe.test(match[1])) continue;
|
|
187
|
-
const original = match[1];
|
|
188
|
-
let target = original;
|
|
189
|
-
const rest = match[2] || "";
|
|
190
|
-
if (target.startsWith("~/")) target = path.join(homeDir, target.slice(2));
|
|
191
|
-
else if (target.startsWith("$HOME/")) target = path.join(homeDir, target.slice(6));
|
|
192
|
-
const abs = path.resolve(target);
|
|
193
|
-
// /tmp guard only applies to literal /tmp/... paths (ephemeral scratch),
|
|
194
|
-
// not to ~/ expansions that happen to land in /tmp during tests.
|
|
195
|
-
if (original === "/tmp" || original.startsWith("/tmp/")) continue;
|
|
196
|
-
if (projectDirAbs && (abs === projectDirAbs || abs.startsWith(projectDirAbs + path.sep))) continue;
|
|
197
|
-
if (!writeCmdRegex.test(rest)) continue;
|
|
198
|
-
leaks.add(abs);
|
|
199
|
-
}
|
|
200
|
-
return Array.from(leaks);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Format a leak list into a multiline plain-Spanish error message.
|
|
205
|
-
* Includes a hint about how to recover (move-or-delete) so the user
|
|
206
|
-
* isn't left guessing.
|
|
207
|
-
*
|
|
208
|
-
* @param {string[]} leaks — output of detectNewHomeEntries()
|
|
209
|
-
* @param {string} projectDir
|
|
210
|
-
* @returns {string}
|
|
211
|
-
*/
|
|
212
|
-
export function formatLeakMessage(leaks, projectDir) {
|
|
213
|
-
const list = leaks.map((p) => ` - ${p}`).join("\n");
|
|
214
|
-
return [
|
|
215
|
-
`El coder creó archivos FUERA del proyecto (${projectDir}).`,
|
|
216
|
-
"",
|
|
217
|
-
"Rutas detectadas:",
|
|
218
|
-
list,
|
|
219
|
-
"",
|
|
220
|
-
"Esto suele pasar cuando una HU usa una ruta absoluta o relativa a $HOME y el coder la sigue literalmente. Las rutas SIEMPRE deben ser relativas a la raíz del proyecto.",
|
|
221
|
-
"",
|
|
222
|
-
"Qué hacer:",
|
|
223
|
-
" 1. Inspecciona las rutas listadas. Si tienen código útil, muévelo a tu proyecto manualmente.",
|
|
224
|
-
" 2. Borra las rutas listadas si son basura.",
|
|
225
|
-
" 3. Edita el título / scope de la HU para que no contenga rutas absolutas.",
|
|
226
|
-
" 4. Vuelve a lanzar la HU.",
|
|
227
|
-
].join("\n");
|
|
228
|
-
}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* hu-scheduler — pure scheduling for parallel HU execution (KJC-TSK-0623,
|
|
3
|
-
* épica KJC-PCS-0065). Decides WHICH HUs may run concurrently; it never
|
|
4
|
-
* launches anything. Two rules, both conservative by design:
|
|
5
|
-
*
|
|
6
|
-
* 1. Dependencies: every blocked_by id must be in completedIds.
|
|
7
|
-
* 2. Scope isolation: two HUs whose scopes touch the same path prefix are
|
|
8
|
-
* never scheduled together — predictable merge conflicts cost more than
|
|
9
|
-
* the parallelism they'd buy. An HU with NO scope has an unknown blast
|
|
10
|
-
* radius, so it runs alone (exclusive with everything).
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Extract path-like tokens from a free-form scope. The planner writes scope
|
|
15
|
-
* as prose ("src/auth y tests/auth", "docs/*.md"); we keep anything that
|
|
16
|
-
* looks like a path or glob, normalized to its base directory.
|
|
17
|
-
*/
|
|
18
|
-
export function scopeTokens(scope) {
|
|
19
|
-
if (!scope || typeof scope !== "string") return [];
|
|
20
|
-
const matches = scope.match(/[\w.-]+(?:\/[\w*.{}-]+)+|[\w.-]+\/(?=\s|$|,)/g) || [];
|
|
21
|
-
return matches
|
|
22
|
-
.map((raw) => {
|
|
23
|
-
// Cut the token at its first glob segment: "src/**/*.js" → "src".
|
|
24
|
-
const segments = raw.replace(/\/+$/, "").split("/");
|
|
25
|
-
const firstGlob = segments.findIndex((s) => s.includes("*") || s.includes("{"));
|
|
26
|
-
return (firstGlob === -1 ? segments : segments.slice(0, firstGlob)).join("/");
|
|
27
|
-
})
|
|
28
|
-
.filter(Boolean);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** True when two token lists share a path-prefix relationship. */
|
|
32
|
-
function tokensOverlap(a, b) {
|
|
33
|
-
for (const ta of a) {
|
|
34
|
-
for (const tb of b) {
|
|
35
|
-
if (ta === tb || ta.startsWith(`${tb}/`) || tb.startsWith(`${ta}/`)) return true;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* True when two HUs cannot run concurrently: either one has no parseable
|
|
43
|
-
* scope (unknown blast radius ⇒ exclusive) or their scopes overlap.
|
|
44
|
-
*/
|
|
45
|
-
export function husConflict(huA, huB) {
|
|
46
|
-
const a = scopeTokens(huA.scope);
|
|
47
|
-
const b = scopeTokens(huB.scope);
|
|
48
|
-
if (a.length === 0 || b.length === 0) return true;
|
|
49
|
-
return tokensOverlap(a, b);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Split one dependency-level group into conflict-free chunks of at most
|
|
54
|
-
* `maxParallel` HUs (KJC-TSK-0626). Every chunk runs concurrently; chunks
|
|
55
|
-
* run one after another. maxParallel 1 ⇒ singletons (fully sequential).
|
|
56
|
-
* Same conservative rules as husConflict: no parseable scope ⇒ runs alone.
|
|
57
|
-
*
|
|
58
|
-
* @param {Array<{id: string, scope?: string|null}>} stories - full story list
|
|
59
|
-
* @param {string[]} groupIds - ids of one dependency-level group, in order
|
|
60
|
-
* @param {number} maxParallel
|
|
61
|
-
* @returns {string[][]} chunks of ids
|
|
62
|
-
*/
|
|
63
|
-
export function partitionConflictFree(stories, groupIds, maxParallel = 1) {
|
|
64
|
-
if (maxParallel <= 1) return groupIds.map((id) => [id]);
|
|
65
|
-
const byId = new Map(stories.map((s) => [s.id, s]));
|
|
66
|
-
const chunks = [];
|
|
67
|
-
for (const id of groupIds) {
|
|
68
|
-
const story = byId.get(id) ?? { id };
|
|
69
|
-
const fit = chunks.find(
|
|
70
|
-
(chunk) => chunk.length < maxParallel && !chunk.some((otherId) => husConflict(story, byId.get(otherId) ?? { id: otherId })),
|
|
71
|
-
);
|
|
72
|
-
if (fit) fit.push(id);
|
|
73
|
-
else chunks.push([id]);
|
|
74
|
-
}
|
|
75
|
-
return chunks;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Select the next HUs to launch, order-preserving and greedy.
|
|
80
|
-
*
|
|
81
|
-
* @param {object} args
|
|
82
|
-
* @param {Array<{id: string, blocked_by?: string[], scope?: string|null}>} args.hus - candidates, in plan order
|
|
83
|
-
* @param {string[]} [args.completedIds] - HUs already done (dependency source)
|
|
84
|
-
* @param {Array<{id: string, scope?: string|null}>} [args.inFlight] - HUs currently running
|
|
85
|
-
* @param {number} [args.maxParallel] - total concurrency cap (in-flight included)
|
|
86
|
-
* @returns {{ selected: object[], blocked: Array<{id: string, reason: string}> }}
|
|
87
|
-
*/
|
|
88
|
-
export function selectRunnableHus({ hus = [], completedIds = [], inFlight = [], maxParallel = 1 } = {}) {
|
|
89
|
-
const done = new Set(completedIds);
|
|
90
|
-
const flying = new Set(inFlight.map((h) => h.id));
|
|
91
|
-
const slots = Math.max(0, maxParallel - inFlight.length);
|
|
92
|
-
const selected = [];
|
|
93
|
-
const blocked = [];
|
|
94
|
-
|
|
95
|
-
for (const hu of hus) {
|
|
96
|
-
if (flying.has(hu.id) || done.has(hu.id)) continue;
|
|
97
|
-
const missing = (hu.blocked_by || []).filter((dep) => !done.has(dep));
|
|
98
|
-
if (missing.length > 0) {
|
|
99
|
-
blocked.push({ id: hu.id, reason: `waiting for ${missing.join(", ")}` });
|
|
100
|
-
continue;
|
|
101
|
-
}
|
|
102
|
-
if (selected.length >= slots) {
|
|
103
|
-
blocked.push({ id: hu.id, reason: "no free slot" });
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
const rival = [...inFlight, ...selected].find((other) => husConflict(hu, other));
|
|
107
|
-
if (rival) {
|
|
108
|
-
blocked.push({ id: hu.id, reason: `scope conflicts with ${rival.id}` });
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
selected.push(hu);
|
|
112
|
-
}
|
|
113
|
-
return { selected, blocked };
|
|
114
|
-
}
|