akm-cli 0.9.1 → 0.9.2-alpha.2
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/CHANGELOG.md +103 -28
- package/README.md +3 -1
- package/SECURITY.md +1 -1
- package/STABILITY.md +1 -1
- package/dist/akm +2 -2
- package/dist/akm-migrate +2 -2
- package/dist/assets/hints/cli-hints-full.md +14 -9
- package/dist/assets/improve-strategies/proactive-maintenance.json +1 -1
- package/dist/assets/improve-strategies/reflect-distill.json +1 -1
- package/dist/assets/models.json +35 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +3 -4
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +1 -3
- package/dist/assets/tasks/core/extract.yml +6 -5
- package/dist/assets/tasks/core/improve.yml +6 -5
- package/dist/assets/tasks/core/index-refresh.yml +6 -5
- package/dist/assets/tasks/core/sync.yml +6 -5
- package/dist/assets/tasks/core/version-check.yml +6 -5
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
- package/dist/cli/confirm.js +2 -2
- package/dist/cli/parse-args.js +3 -24
- package/dist/cli/retired-commands.js +1 -1
- package/dist/cli/shared.js +2 -2
- package/dist/cli.js +11 -9
- package/dist/commands/agent/agent-dispatch.js +55 -89
- package/dist/commands/agent/contribute-cli.js +12 -45
- package/dist/commands/command/builtin-action.js +32 -0
- package/dist/commands/command/command-cli.js +99 -0
- package/dist/commands/command/command-execution.js +308 -0
- package/dist/commands/command/execution-source-loader.js +176 -0
- package/dist/commands/command/portable-template.js +60 -0
- package/dist/commands/config-cli.js +10 -4
- package/dist/commands/env/env.js +4 -2
- package/dist/commands/feedback-cli.js +1 -1
- package/dist/commands/health/checks.js +241 -29
- package/dist/commands/health/html-report.js +0 -14
- package/dist/commands/health/report-view-model.js +0 -1
- package/dist/commands/health/surfaces.js +6 -7
- package/dist/commands/health/types.js +0 -2
- package/dist/commands/health.js +63 -18
- package/dist/commands/improve/collapse-detector.js +5 -6
- package/dist/commands/improve/consolidate.js +251 -214
- package/dist/commands/improve/distill/promote-memory.js +71 -34
- package/dist/commands/improve/distill/quality-gate.js +17 -5
- package/dist/commands/improve/distill.js +232 -155
- package/dist/commands/improve/eligibility.js +112 -79
- package/dist/commands/improve/execution.js +57 -0
- package/dist/commands/improve/extract-cli.js +5 -5
- package/dist/commands/improve/extract-prompt.js +64 -22
- package/dist/commands/improve/extract.js +608 -360
- package/dist/commands/improve/improve-strategies.js +43 -14
- package/dist/commands/improve/improve.js +249 -29
- package/dist/commands/improve/loop-stages.js +11 -17
- package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
- package/dist/commands/improve/outcome-loop.js +22 -38
- package/dist/commands/improve/planner.js +134 -0
- package/dist/commands/improve/preparation.js +730 -409
- package/dist/commands/improve/reflect.js +386 -223
- package/dist/commands/improve/run-context.js +3 -4
- package/dist/commands/improve/salience.js +6 -58
- package/dist/commands/improve/session-asset.js +12 -12
- package/dist/commands/lint/index.js +101 -29
- package/dist/commands/migrate-cli.js +11 -69
- package/dist/commands/migration-tool.js +6 -9
- package/dist/commands/models-cli.js +27 -0
- package/dist/commands/proposal/drain.js +258 -186
- package/dist/commands/proposal/proposal-cli.js +32 -10
- package/dist/commands/proposal/proposal.js +2 -5
- package/dist/commands/proposal/propose.js +192 -172
- package/dist/commands/proposal/repository.js +54 -91
- package/dist/commands/proposal/validators/proposal-validators.js +9 -7
- package/dist/commands/read/curate.js +53 -22
- package/dist/commands/read/registry-search.js +25 -9
- package/dist/commands/read/remember-cli.js +14 -2
- package/dist/commands/read/search.js +10 -4
- package/dist/commands/read/show.js +139 -153
- package/dist/commands/registry-cli.js +16 -7
- package/dist/commands/remember.js +33 -18
- package/dist/commands/sources/add-cli.js +19 -178
- package/dist/commands/sources/bundle-cli.js +15 -3
- package/dist/commands/sources/dangerous-env-audit.js +135 -0
- package/dist/commands/sources/info.js +2 -1
- package/dist/commands/sources/installed-stashes.js +901 -177
- package/dist/commands/sources/schema-repair.js +174 -95
- package/dist/commands/sources/self-update.js +30 -74
- package/dist/commands/sources/source-add.js +3 -5
- package/dist/commands/sources/sources-cli.js +2 -15
- package/dist/commands/sources/update-transaction.js +220 -0
- package/dist/commands/tasks/tasks-cli.js +3 -3
- package/dist/commands/tasks/tasks.js +736 -317
- package/dist/commands/workflow-cli.js +2 -2
- package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
- package/dist/core/adapter/adapters/akm-adapter.js +85 -35
- package/dist/core/adapter/adapters/akm-lint.js +54 -39
- package/dist/core/adapter/adapters/akm-metadata.js +45 -45
- package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
- package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
- package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
- package/dist/core/adapter/adapters/index.js +0 -9
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
- package/dist/core/adapter/adapters/okf-adapter.js +4 -0
- package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
- package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
- package/dist/core/adapter/execution-source.js +308 -0
- package/dist/core/adapter/recognize-match.js +36 -13
- package/dist/core/adapter/registry.js +0 -9
- package/dist/core/asset/stash-meta.js +94 -4
- package/dist/core/common.js +6 -11
- package/dist/core/config/config-io.js +3 -3
- package/dist/core/config/config-schema.js +18 -40
- package/dist/core/config/config-sources.js +11 -21
- package/dist/core/config/config-walker.js +31 -13
- package/dist/core/config/config.js +23 -26
- package/dist/core/config/schema/engines.js +8 -7
- package/dist/core/config/schema/improve-processes.js +29 -5
- package/dist/core/config/schema/index-config.js +0 -27
- package/dist/core/config/schema/primitives.js +1 -23
- package/dist/core/config/schema/sources-bundles.js +13 -16
- package/dist/core/errors.js +2 -0
- package/dist/core/events.js +68 -32
- package/dist/core/extra-params.js +1 -0
- package/dist/core/improve-result.js +315 -0
- package/dist/core/lesson-lint.js +0 -6
- package/dist/core/maintenance-barrier.js +4 -4
- package/dist/core/network-policy.js +152 -0
- package/dist/core/paths.js +1 -1
- package/dist/core/recognition-util.js +4 -4
- package/dist/core/registry-url.js +456 -0
- package/dist/core/state/migrations.js +161 -47
- package/dist/core/state-db.js +453 -80
- package/dist/core/system-error.js +32 -0
- package/dist/core/time.js +2 -12
- package/dist/core/write-source.js +0 -18
- package/dist/execution/directory-identity.js +52 -0
- package/dist/execution/executable-identity.js +107 -0
- package/dist/execution/guarded-source.js +398 -0
- package/dist/execution/json.js +95 -0
- package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
- package/dist/execution/record.js +55 -0
- package/dist/execution/resolved-request.js +730 -0
- package/dist/execution/source.js +320 -0
- package/dist/indexer/bundle-identity-guard.js +5 -4
- package/dist/indexer/db/graph-db.js +33 -0
- package/dist/indexer/graph/graph-boost.js +3 -4
- package/dist/indexer/graph/graph-extraction.js +562 -373
- package/dist/indexer/index-written-assets.js +78 -39
- package/dist/indexer/indexer.js +471 -432
- package/dist/indexer/installations.js +6 -0
- package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
- package/dist/indexer/materialize-embeddings.js +155 -0
- package/dist/indexer/passes/memory-inference.js +227 -174
- package/dist/indexer/passes/metadata.js +263 -118
- package/dist/indexer/scan/doc-to-entry.js +7 -10
- package/dist/indexer/scan/drain-dir.js +51 -23
- package/dist/indexer/search/db-search.js +156 -50
- package/dist/indexer/search/fts-query.js +40 -40
- package/dist/indexer/search/ranking.js +36 -1
- package/dist/indexer/search/search-attribution.js +3 -1
- package/dist/indexer/search/search-fields.js +23 -14
- package/dist/indexer/search/search-hit-enrichers.js +1 -1
- package/dist/indexer/search/search-source.js +7 -16
- package/dist/indexer/search/semantic-status.js +10 -1
- package/dist/indexer/usage/show-usage.js +105 -0
- package/dist/indexer/usage/usage-events.js +7 -2
- package/dist/indexer/walk/matchers.js +40 -10
- package/dist/indexer/walk/path-resolver.js +5 -2
- package/dist/indexer/walk/walker.js +20 -2
- package/dist/integrations/agent/builder-shared.js +3 -6
- package/dist/integrations/agent/conversation-fallback.js +16 -0
- package/dist/integrations/agent/engine-resolution.js +87 -87
- package/dist/integrations/agent/execution-cascade.js +566 -0
- package/dist/integrations/agent/execution-definitions.js +211 -0
- package/dist/integrations/agent/execution-lowering.js +811 -0
- package/dist/integrations/agent/execution-preparation.js +67 -0
- package/dist/integrations/agent/index.js +0 -2
- package/dist/integrations/agent/inline-execution.js +74 -0
- package/dist/integrations/agent/model-map.js +515 -0
- package/dist/integrations/agent/persona-fallback.js +30 -0
- package/dist/integrations/agent/request-lowering.js +186 -0
- package/dist/integrations/agent/runner-dispatch.js +230 -37
- package/dist/integrations/agent/runner.js +12 -83
- package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
- package/dist/integrations/harnesses/aider/index.js +0 -1
- package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
- package/dist/integrations/harnesses/amazonq/index.js +0 -1
- package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
- package/dist/integrations/harnesses/claude/index.js +1 -5
- package/dist/integrations/harnesses/claude/session-log.js +3 -33
- package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
- package/dist/integrations/harnesses/codex/index.js +0 -1
- package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
- package/dist/integrations/harnesses/copilot/index.js +0 -1
- package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
- package/dist/integrations/harnesses/gemini/index.js +0 -1
- package/dist/integrations/harnesses/index.js +4 -44
- package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
- package/dist/integrations/harnesses/opencode/index.js +0 -2
- package/dist/integrations/harnesses/opencode/session-log.js +14 -204
- package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
- package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
- package/dist/integrations/harnesses/openhands/index.js +0 -1
- package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
- package/dist/integrations/harnesses/pi/index.js +0 -1
- package/dist/integrations/harnesses/shared.js +0 -1
- package/dist/integrations/harnesses/types.js +1 -3
- package/dist/integrations/lockfile.js +82 -79
- package/dist/integrations/session-logs/index.js +6 -17
- package/dist/integrations/session-logs/provider-base.js +1 -29
- package/dist/llm/client.js +10 -5
- package/dist/llm/embedder.js +6 -7
- package/dist/llm/embedders/local.js +37 -88
- package/dist/llm/embedders/types.js +1 -1
- package/dist/llm/graph-extract.js +75 -50
- package/dist/llm/index-passes.js +43 -5
- package/dist/llm/memory-infer.js +8 -6
- package/dist/llm/metadata-enhance.js +5 -3
- package/dist/llm/structured-call.js +122 -25
- package/dist/output/format-exempt.js +1 -1
- package/dist/output/render-registry.js +0 -16
- package/dist/output/renderers.js +12 -7
- package/dist/output/shapes/curate.js +1 -0
- package/dist/output/shapes/helpers.js +10 -2
- package/dist/output/shapes/passthrough.js +2 -0
- package/dist/output/text/command-format.js +31 -33
- package/dist/output/text/health-format.js +1 -29
- package/dist/output/text/migrate.js +6 -56
- package/dist/output/text/proposal-format.js +16 -1
- package/dist/output/text/workflow-format.js +16 -0
- package/dist/registry/network.js +279 -0
- package/dist/registry/pinned-request-helper.js +247 -0
- package/dist/registry/pinned-transport.js +717 -0
- package/dist/registry/providers/skills-sh.js +18 -6
- package/dist/registry/providers/static-index.js +20 -7
- package/dist/registry/resolve.js +53 -28
- package/dist/scripts/akm-migrate-node.js +19334 -52269
- package/dist/scripts/akm-migrate.js +19270 -51612
- package/dist/setup/registry-stash-loader.js +64 -20
- package/dist/setup/semantic-assets.js +9 -34
- package/dist/setup/setup.js +12 -30
- package/dist/setup/source-identity.js +17 -0
- package/dist/setup/steps/sources.js +36 -15
- package/dist/setup/steps/tasks.js +39 -11
- package/dist/sources/providers/git-provider.js +3 -3
- package/dist/sources/providers/npm.js +2 -2
- package/dist/sources/providers/provider-utils.js +4 -3
- package/dist/sources/providers/website.js +11 -7
- package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
- package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
- package/dist/sources/website-url.js +73 -0
- package/dist/storage/engines/sqlite-migrations.js +81 -26
- package/dist/storage/managed-db.js +27 -24
- package/dist/storage/repositories/events-repository.js +3 -0
- package/dist/storage/repositories/index-connection.js +42 -10
- package/dist/storage/repositories/index-entries-repository.js +203 -229
- package/dist/storage/repositories/index-entry-mapper.js +8 -12
- package/dist/storage/repositories/index-entry-schema.js +255 -0
- package/dist/storage/repositories/index-fts-repository.js +64 -71
- package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
- package/dist/storage/repositories/index-meta-repository.js +0 -11
- package/dist/storage/repositories/index-schema.js +74 -350
- package/dist/storage/repositories/index-utility-repository.js +12 -17
- package/dist/storage/repositories/index-vec-repository.js +56 -7
- package/dist/storage/repositories/proposals-repository.js +4 -127
- package/dist/storage/repositories/registry-cache.js +2 -1
- package/dist/storage/repositories/task-history-repository.js +20 -40
- package/dist/storage/repositories/workflow-runs-repository.js +228 -129
- package/dist/storage/sqlite-read-snapshot.js +148 -0
- package/dist/tasks/backends/cron.js +170 -42
- package/dist/tasks/backends/index.js +1 -1
- package/dist/tasks/backends/launchd.js +787 -202
- package/dist/tasks/backends/schtasks.js +282 -83
- package/dist/tasks/embedded.js +7 -7
- package/dist/tasks/frozen-script.js +50 -0
- package/dist/tasks/resolve-akm-bin.js +5 -1
- package/dist/tasks/runner.js +239 -251
- package/dist/tasks/runtime-v3.js +281 -0
- package/dist/tasks/scheduler-binding.js +272 -0
- package/dist/tasks/scheduler-invocation.js +57 -43
- package/dist/tasks/scheduler-sync.js +654 -0
- package/dist/tasks/source-v3.js +752 -0
- package/dist/tasks/standalone-script-entry.js +5 -0
- package/dist/tasks/task-id.js +29 -0
- package/dist/workflows/authoring/authoring.js +15 -32
- package/dist/workflows/exec/dispatch-redaction.js +14 -8
- package/dist/workflows/exec/exec-unit.js +7 -28
- package/dist/workflows/exec/frozen-judge.js +57 -89
- package/dist/workflows/exec/lowering-notices.js +23 -0
- package/dist/workflows/exec/native-executor.js +301 -458
- package/dist/workflows/exec/param-secrets.js +4 -3
- package/dist/workflows/exec/run-workflow.js +26 -32
- package/dist/workflows/exec/step-work.js +105 -109
- package/dist/workflows/exec/unit-dispatch.js +103 -27
- package/dist/workflows/exec/unit-writer.js +3 -3
- package/dist/workflows/exec/worktree.js +2 -2
- package/dist/workflows/ir/compile.js +86 -72
- package/dist/workflows/ir/environment-v4.js +328 -0
- package/dist/workflows/ir/freeze-v4.js +122 -0
- package/dist/workflows/ir/plan-hash.js +13 -7
- package/dist/workflows/ir/schema-v4.js +525 -0
- package/dist/workflows/ir/schema.js +25 -284
- package/dist/workflows/ir/source-freeze-v4.js +506 -0
- package/dist/workflows/parser.js +27 -24
- package/dist/workflows/program/schema.js +1 -2
- package/dist/workflows/renderer.js +42 -29
- package/dist/workflows/resource-limits.js +4 -5
- package/dist/workflows/runtime/agent-identity.js +11 -13
- package/dist/workflows/runtime/plan-classifier.js +8 -8
- package/dist/workflows/runtime/runs.js +27 -43
- package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
- package/dist/workflows/source-files.js +373 -0
- package/dist/workflows/source-ir/compile.js +196 -0
- package/dist/workflows/source-ir/github-yaml.js +577 -0
- package/dist/workflows/source-ir/ordering.js +38 -0
- package/dist/workflows/source-ir/program.js +50 -0
- package/dist/workflows/source-ir/result.js +26 -0
- package/dist/workflows/source-ir/schema.js +772 -0
- package/dist/workflows/source-ir/semantics.js +242 -0
- package/dist/workflows/source-ir/uses.js +14 -0
- package/docs/README.md +2 -0
- package/docs/migration/README.md +3 -1
- package/docs/migration/release-notes/0.9.2.md +55 -0
- package/docs/migration/release-notes/README.md +5 -0
- package/docs/migration/v0.8-to-v0.9.md +76 -1077
- package/docs/migration/v0.9.0-troubleshooting.md +104 -516
- package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
- package/docs/reference/README.md +1 -0
- package/docs/reference/cli.md +230 -98
- package/docs/reference/configuration.md +159 -36
- package/docs/reference/data-and-telemetry.md +19 -1
- package/docs/reference/supported-formats.md +23 -3
- package/docs/reference/tasks.md +182 -0
- package/docs/reference/workflow-schema.md +91 -40
- package/docs/reference/workflows.md +33 -6
- package/package.json +10 -6
- package/schemas/akm-config.json +372 -224
- package/schemas/akm-task.json +324 -80
- package/schemas/akm-workflow.json +6 -9
- package/dist/core/migration-operation.js +0 -75
- package/dist/integrations/agent/model-aliases.js +0 -74
- package/dist/tasks/parser.js +0 -380
- package/dist/tasks/schema.js +0 -123
- package/dist/tasks/validator.js +0 -80
- package/dist/workflows/ir/freeze.js +0 -320
- package/dist/workflows/runtime/document-cache.js +0 -13
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
* - The judgment tier (Phase 3) adjudicates the deferred items: when a
|
|
24
24
|
* `judgment` RunnerSpec is supplied the engine pre-fetches context (the live
|
|
25
25
|
* asset + sibling pending proposals for the same ref) into a prompt,
|
|
26
|
-
* dispatches it
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
* dispatches it through the shared resolved/lowered execution boundary,
|
|
27
|
+
* and performs the resulting accept / reject *itself* (the runner only
|
|
28
|
+
* judges).
|
|
29
29
|
* Items the runner cannot resolve — and any deferred items when no runner is
|
|
30
30
|
* configured — surface a `triage_deferred` event so "enabled, no agent"
|
|
31
31
|
* never silently looks like full success.
|
|
@@ -40,13 +40,14 @@ import path from "node:path";
|
|
|
40
40
|
import { assetPathForName, stashDirFor } from "../../core/asset/asset-placement.js";
|
|
41
41
|
import { parseFrontmatter } from "../../core/asset/frontmatter.js";
|
|
42
42
|
import { parseRefInput } from "../../core/asset/resolve-ref.js";
|
|
43
|
+
import { ConfigError } from "../../core/errors.js";
|
|
43
44
|
import { appendEvent } from "../../core/events.js";
|
|
44
45
|
import { escapeJsonStringControls, stripCodeFences, stripThinkBlocks } from "../../core/parse.js";
|
|
45
46
|
import { info, warn } from "../../core/warn.js";
|
|
46
|
-
import {
|
|
47
|
-
import {
|
|
47
|
+
import { acquireLoweredExecutionDispatchLease, dispatchLoweredExecutionRequest, disposeLoweredExecutionDispatchLease, lowerResolvedExecutionRequestWithRunner, } from "../../integrations/agent/execution-lowering.js";
|
|
48
|
+
import { prepareInlineExecutionWithRunner } from "../../integrations/agent/inline-execution.js";
|
|
48
49
|
import { akmProposalAccept, akmProposalReject } from "./proposal.js";
|
|
49
|
-
import { listProposals, preflightProposalPromotion, proposalContent, recordGateDecision, } from "./repository.js";
|
|
50
|
+
import { listProposals, listProposalsReadOnly, preflightProposalPromotion, proposalContent, recordGateDecision, } from "./repository.js";
|
|
50
51
|
// ---------------------------------------------------------------------------
|
|
51
52
|
// Content helpers
|
|
52
53
|
// ---------------------------------------------------------------------------
|
|
@@ -208,39 +209,56 @@ export function parseJudgmentVerdict(raw) {
|
|
|
208
209
|
return null;
|
|
209
210
|
return { decision, reason: typeof reason === "string" ? reason : "" };
|
|
210
211
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
parseOutput: "text",
|
|
223
|
-
...(runner.timeoutMs !== undefined ? { timeoutMs: runner.timeoutMs } : {}),
|
|
224
|
-
};
|
|
225
|
-
const result = await executeRunner(runner, prompt, runOptions, {
|
|
226
|
-
llm: async (spec, p) => {
|
|
227
|
-
const messages = [{ role: "user", content: p }];
|
|
228
|
-
const raw = seams.chat
|
|
229
|
-
? await seams.chat(spec, messages)
|
|
230
|
-
: await chatCompletion(spec.connection, messages, {
|
|
231
|
-
...(spec.timeoutMs !== undefined ? { timeoutMs: spec.timeoutMs } : {}),
|
|
232
|
-
});
|
|
233
|
-
// chatCompletion has no failure envelope — a returned string is success.
|
|
234
|
-
return { ok: true, exitCode: 0, stdout: raw, stderr: "", durationMs: 0 };
|
|
235
|
-
},
|
|
212
|
+
async function dispatchJudgment(runner, prompt, seams, lease) {
|
|
213
|
+
const prepared = prepareInlineExecutionWithRunner({
|
|
214
|
+
content: prompt,
|
|
215
|
+
runner,
|
|
216
|
+
invocationKind: "direct",
|
|
217
|
+
});
|
|
218
|
+
const lowered = lowerResolvedExecutionRequestWithRunner(prepared.request, prepared.runner);
|
|
219
|
+
const chat = seams.chat;
|
|
220
|
+
const llmRunner = lowered.runner.kind === "llm" ? lowered.runner : undefined;
|
|
221
|
+
const dispatchOptions = {
|
|
222
|
+
lease,
|
|
236
223
|
...(seams.runAgentFn ? { runAgent: seams.runAgentFn } : {}),
|
|
237
224
|
...(seams.runSdkFn ? { runSdk: seams.runSdkFn } : {}),
|
|
238
|
-
|
|
225
|
+
...(chat && llmRunner
|
|
226
|
+
? {
|
|
227
|
+
chat: async (connection, messages) => chat({ ...llmRunner, connection }, messages),
|
|
228
|
+
}
|
|
229
|
+
: {}),
|
|
230
|
+
};
|
|
231
|
+
let result;
|
|
232
|
+
try {
|
|
233
|
+
result = await dispatchLoweredExecutionRequest(lowered, dispatchOptions);
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
if (error instanceof ConfigError)
|
|
237
|
+
throw error;
|
|
238
|
+
return {
|
|
239
|
+
verdict: null,
|
|
240
|
+
notices: lowered.notices,
|
|
241
|
+
error: error instanceof Error ? error.message : String(error),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
239
244
|
if (!result.ok) {
|
|
240
|
-
|
|
241
|
-
|
|
245
|
+
return {
|
|
246
|
+
verdict: null,
|
|
247
|
+
notices: lowered.notices,
|
|
248
|
+
error: result.error ?? result.reason ?? "unknown error",
|
|
249
|
+
};
|
|
242
250
|
}
|
|
243
|
-
return parseJudgmentVerdict(result.stdout);
|
|
251
|
+
return { verdict: parseJudgmentVerdict(result.stdout), notices: lowered.notices };
|
|
252
|
+
}
|
|
253
|
+
/** Validate symbolic judgment credentials without contacting a provider. */
|
|
254
|
+
async function preflightJudgmentRunner(runner) {
|
|
255
|
+
const prepared = prepareInlineExecutionWithRunner({
|
|
256
|
+
content: "Validate the selected proposal judgment runner before mutation.",
|
|
257
|
+
runner,
|
|
258
|
+
invocationKind: "direct",
|
|
259
|
+
});
|
|
260
|
+
const lowered = lowerResolvedExecutionRequestWithRunner(prepared.request, prepared.runner);
|
|
261
|
+
return acquireLoweredExecutionDispatchLease(lowered);
|
|
244
262
|
}
|
|
245
263
|
function judgedContentHash(proposal) {
|
|
246
264
|
return createHash("sha256").update(proposalContent(proposal), "utf8").digest("hex");
|
|
@@ -260,6 +278,7 @@ async function runJudgmentTier(input) {
|
|
|
260
278
|
const staged = [];
|
|
261
279
|
const skippedByCap = [];
|
|
262
280
|
const stillDeferred = [];
|
|
281
|
+
const noticesByKey = new Map();
|
|
263
282
|
// Remaining accept budget shared with the deterministic promote loop.
|
|
264
283
|
let acceptBudget = Math.max(0, input.remainingAcceptBudget);
|
|
265
284
|
for (const item of input.deferred) {
|
|
@@ -270,15 +289,28 @@ async function runJudgmentTier(input) {
|
|
|
270
289
|
}
|
|
271
290
|
const ctx = prefetchJudgmentContext(input.stashDir, proposal, input.pending);
|
|
272
291
|
const prompt = buildJudgmentPrompt(proposal, item.reason, ctx);
|
|
273
|
-
let
|
|
292
|
+
let dispatch;
|
|
274
293
|
try {
|
|
275
|
-
|
|
294
|
+
dispatch = await dispatchJudgment(input.runner, prompt, input.seams, input.lease);
|
|
276
295
|
}
|
|
277
296
|
catch (err) {
|
|
297
|
+
if (err instanceof ConfigError)
|
|
298
|
+
throw err;
|
|
278
299
|
warn(`[triage] judgment dispatch failed for ${item.id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
279
300
|
stillDeferred.push(item);
|
|
280
301
|
continue;
|
|
281
302
|
}
|
|
303
|
+
for (const notice of dispatch.notices) {
|
|
304
|
+
const key = JSON.stringify(notice);
|
|
305
|
+
if (!noticesByKey.has(key))
|
|
306
|
+
noticesByKey.set(key, notice);
|
|
307
|
+
}
|
|
308
|
+
if (dispatch.error) {
|
|
309
|
+
warn(`[triage] judgment dispatch failed for ${item.id}: ${dispatch.error}`);
|
|
310
|
+
stillDeferred.push(item);
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
const verdict = dispatch.verdict;
|
|
282
314
|
if (!verdict || verdict.decision === "defer") {
|
|
283
315
|
stillDeferred.push(item);
|
|
284
316
|
continue;
|
|
@@ -367,37 +399,35 @@ async function runJudgmentTier(input) {
|
|
|
367
399
|
stillDeferred.push(item);
|
|
368
400
|
}
|
|
369
401
|
}
|
|
370
|
-
return {
|
|
402
|
+
return {
|
|
403
|
+
promoted,
|
|
404
|
+
rejected,
|
|
405
|
+
staged,
|
|
406
|
+
skippedByCap,
|
|
407
|
+
stillDeferred,
|
|
408
|
+
notices: Object.freeze([...noticesByKey.values()]),
|
|
409
|
+
};
|
|
371
410
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
// ---------------------------------------------------------------------------
|
|
375
|
-
/**
|
|
376
|
-
* Drain the standing pending backlog under a deterministic policy.
|
|
377
|
-
*
|
|
378
|
-
* @param opts Drain options (policy, applyMode, ceilings, dry-run).
|
|
379
|
-
* @param promoteFn Injectable override for `akmProposalAccept` (test seam).
|
|
380
|
-
* @param rejectFn Injectable override for `akmProposalReject` (test seam).
|
|
381
|
-
*/
|
|
382
|
-
export async function drainProposals(opts, promoteFn = akmProposalAccept, rejectFn = akmProposalReject, judgmentSeams = {}) {
|
|
383
|
-
const result = { promoted: [], rejected: [], deferred: [], skippedByCap: [], staged: [] };
|
|
411
|
+
/** Classify the queue without mutating proposal, event, or promotion state. */
|
|
412
|
+
function classifyPendingProposals(opts) {
|
|
384
413
|
const exclude = opts.excludeIds ?? new Set();
|
|
385
|
-
|
|
386
|
-
//
|
|
414
|
+
// A configured judgment runner must be credential-validated before any live
|
|
415
|
+
// state connection or migration. Its classification pass therefore reads an
|
|
416
|
+
// isolated SQLite snapshot; deterministic-only drains retain the historical
|
|
417
|
+
// live/migrating queue read.
|
|
418
|
+
const pending = (opts.judgment ? listProposalsReadOnly : listProposals)(opts.stashDir, {
|
|
419
|
+
status: "pending",
|
|
420
|
+
}).filter((proposal) => !exclude.has(proposal.id));
|
|
387
421
|
const acceptIds = [];
|
|
388
422
|
const acceptGateReasons = new Map();
|
|
389
423
|
const rejectTargets = [];
|
|
424
|
+
const deferred = [];
|
|
425
|
+
const deferredGateDecisions = [];
|
|
390
426
|
const gateLabel = `triage:${opts.policy.name}`;
|
|
391
|
-
// Items deferred purely because they need a judge (no threshold-based reason)
|
|
392
|
-
// — these are re-stamped `no-judge-configured` when no runner resolves them.
|
|
393
427
|
const needsJudge = new Set();
|
|
394
428
|
for (const proposal of pending) {
|
|
395
|
-
//
|
|
396
|
-
//
|
|
397
|
-
// Overwriting an authoritative rejection with `auto-accepted` would corrupt
|
|
398
|
-
// the audit trail and silently promote content the gate explicitly rejected.
|
|
399
|
-
// Such proposals remain pending until the TTL expires them — the queue is
|
|
400
|
-
// audited-autonomous; no manual-review rung exists (06-M3).
|
|
429
|
+
// An authoritative rejection from another gate stays pending and is never
|
|
430
|
+
// silently overwritten by this triage policy.
|
|
401
431
|
if (proposal.gateDecision?.outcome === "auto-rejected" && !proposal.gateDecision.gate?.startsWith("triage:")) {
|
|
402
432
|
continue;
|
|
403
433
|
}
|
|
@@ -411,23 +441,19 @@ export async function drainProposals(opts, promoteFn = akmProposalAccept, reject
|
|
|
411
441
|
const decision = classifyProposal(proposal, opts.policy, opts.maxDiffLines);
|
|
412
442
|
if (decision === null)
|
|
413
443
|
continue;
|
|
414
|
-
// #577: stamp the gate's verdict onto the proposal so `akm proposal show`
|
|
415
|
-
// can explain WHY it landed here. A dry-run performs zero writes, so it
|
|
416
|
-
// records nothing.
|
|
417
444
|
if (decision.verdict === "defer") {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
445
|
+
deferredGateDecisions.push({
|
|
446
|
+
id: proposal.id,
|
|
447
|
+
decision: {
|
|
448
|
+
outcome: "deferred",
|
|
449
|
+
reason: decision.gate.reason,
|
|
450
|
+
...(decision.gate.measured !== undefined ? { measured: decision.gate.measured } : {}),
|
|
451
|
+
...(decision.gate.thresholds ? { thresholds: decision.gate.thresholds } : {}),
|
|
452
|
+
gate: gateLabel,
|
|
453
|
+
},
|
|
424
454
|
});
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
// pending only because it needs adjudication — re-stampable to
|
|
428
|
-
// `no-judge-configured`. A band-based defer keeps its specific reason.
|
|
429
|
-
if (decision.verdict === "defer" && !decision.gate.thresholds) {
|
|
430
|
-
needsJudge.add(proposal.id);
|
|
455
|
+
if (!decision.gate.thresholds)
|
|
456
|
+
needsJudge.add(proposal.id);
|
|
431
457
|
}
|
|
432
458
|
if (decision.verdict === "accept") {
|
|
433
459
|
acceptIds.push(proposal.id);
|
|
@@ -437,141 +463,187 @@ export async function drainProposals(opts, promoteFn = akmProposalAccept, reject
|
|
|
437
463
|
rejectTargets.push({ id: proposal.id, reason: decision.reason });
|
|
438
464
|
}
|
|
439
465
|
else {
|
|
440
|
-
|
|
466
|
+
deferred.push({ id: proposal.id, reason: decision.reason });
|
|
441
467
|
}
|
|
442
468
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
const
|
|
464
|
-
result
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
469
|
+
return {
|
|
470
|
+
pending,
|
|
471
|
+
acceptIds,
|
|
472
|
+
acceptGateReasons,
|
|
473
|
+
rejectTargets,
|
|
474
|
+
deferred,
|
|
475
|
+
deferredGateDecisions,
|
|
476
|
+
gateLabel,
|
|
477
|
+
needsJudge,
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Drain the standing pending backlog under a deterministic policy.
|
|
482
|
+
*
|
|
483
|
+
* @param opts Drain options (policy, applyMode, ceilings, dry-run).
|
|
484
|
+
* @param promoteFn Injectable override for `akmProposalAccept` (test seam).
|
|
485
|
+
* @param rejectFn Injectable override for `akmProposalReject` (test seam).
|
|
486
|
+
*/
|
|
487
|
+
export async function drainProposals(opts, promoteFn = akmProposalAccept, rejectFn = akmProposalReject, judgmentSeams = {}) {
|
|
488
|
+
const classification = classifyPendingProposals(opts);
|
|
489
|
+
const { pending, acceptIds, acceptGateReasons, rejectTargets, deferredGateDecisions, gateLabel, needsJudge } = classification;
|
|
490
|
+
const result = {
|
|
491
|
+
promoted: [],
|
|
492
|
+
rejected: [],
|
|
493
|
+
deferred: classification.deferred,
|
|
494
|
+
skippedByCap: [],
|
|
495
|
+
staged: [],
|
|
496
|
+
};
|
|
497
|
+
// A configured judgment runner makes every deferred item dispatch-eligible.
|
|
498
|
+
// Validate its symbolic credentials before applying any deterministic gate,
|
|
499
|
+
// reject, promote, or event mutation. Provider/runtime failures remain the
|
|
500
|
+
// judgment tier's fail-soft responsibility after this configuration fence.
|
|
501
|
+
const dispatchLease = opts.judgment && result.deferred.length > 0 ? await preflightJudgmentRunner(opts.judgment) : undefined;
|
|
502
|
+
try {
|
|
503
|
+
for (const { id, decision } of deferredGateDecisions)
|
|
504
|
+
stampGateDecision(opts, id, decision);
|
|
505
|
+
// --- Reject empties (independent of the accept ceiling / applyMode) ---
|
|
506
|
+
for (const target of rejectTargets) {
|
|
507
|
+
if (opts.dryRun) {
|
|
508
|
+
result.rejected.push(target.id);
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
475
511
|
try {
|
|
476
|
-
await
|
|
512
|
+
await rejectFn({
|
|
477
513
|
stashDir: opts.stashDir,
|
|
478
|
-
id,
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
gateDecision: {
|
|
482
|
-
outcome: "auto-accepted",
|
|
483
|
-
reason: acceptGateReasons.get(id) ?? "policy-accept",
|
|
484
|
-
gate: gateLabel,
|
|
485
|
-
},
|
|
514
|
+
id: target.id,
|
|
515
|
+
reason: target.reason,
|
|
516
|
+
gateDecision: { outcome: "auto-rejected", reason: "empty-diff", gate: gateLabel },
|
|
486
517
|
});
|
|
487
|
-
result.
|
|
488
|
-
deterministicPromoted += 1;
|
|
518
|
+
result.rejected.push(target.id);
|
|
489
519
|
}
|
|
490
520
|
catch (err) {
|
|
491
|
-
warn(`[triage]
|
|
521
|
+
warn(`[triage] reject failed for ${target.id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
492
522
|
}
|
|
493
523
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
524
|
+
// --- Accept ceiling: enforced BEFORE the promote loop ---
|
|
525
|
+
const withinCap = acceptIds.slice(0, Math.max(0, opts.maxAccepts));
|
|
526
|
+
result.skippedByCap = acceptIds.slice(Math.max(0, opts.maxAccepts));
|
|
527
|
+
if (result.skippedByCap.length > 0) {
|
|
528
|
+
info(`[triage] accept ceiling reached: ${withinCap.length} promoted, ${result.skippedByCap.length} skipped by cap (maxAccepts=${opts.maxAccepts})`);
|
|
529
|
+
}
|
|
530
|
+
// --- Promotion gate: applyMode "queue" never promotes (stage only) ---
|
|
531
|
+
// Count deterministic promotions so the judgment tier shares the same accept
|
|
532
|
+
// budget (deterministic + judgment promotions ≤ maxAccepts).
|
|
533
|
+
let deterministicPromoted = 0;
|
|
534
|
+
if (opts.applyMode === "promote" && !opts.dryRun) {
|
|
535
|
+
info(`[triage] auto-promote active: ${withinCap.length} accepts allowed this run`);
|
|
536
|
+
for (const id of withinCap) {
|
|
537
|
+
try {
|
|
538
|
+
await promoteFn({
|
|
539
|
+
stashDir: opts.stashDir,
|
|
540
|
+
id,
|
|
506
541
|
...(opts.target ? { target: opts.target } : {}),
|
|
542
|
+
...(opts.config ? { config: opts.config } : {}),
|
|
507
543
|
gateDecision: {
|
|
508
544
|
outcome: "auto-accepted",
|
|
509
545
|
reason: acceptGateReasons.get(id) ?? "policy-accept",
|
|
510
546
|
gate: gateLabel,
|
|
511
547
|
},
|
|
512
548
|
});
|
|
549
|
+
result.promoted.push(id);
|
|
550
|
+
deterministicPromoted += 1;
|
|
551
|
+
}
|
|
552
|
+
catch (err) {
|
|
553
|
+
warn(`[triage] promote failed for ${id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
513
554
|
}
|
|
514
|
-
result.promoted.push(id);
|
|
515
|
-
deterministicPromoted += 1;
|
|
516
555
|
}
|
|
517
|
-
|
|
518
|
-
|
|
556
|
+
}
|
|
557
|
+
else if (opts.applyMode === "promote" && opts.dryRun) {
|
|
558
|
+
// Exercise the same stamped candidate and lint boundary as real promotion.
|
|
559
|
+
// Tests that omit config retain the classification-only seam.
|
|
560
|
+
const byId = new Map(pending.map((proposal) => [proposal.id, proposal]));
|
|
561
|
+
for (const id of withinCap) {
|
|
562
|
+
try {
|
|
563
|
+
if (opts.config) {
|
|
564
|
+
const proposal = byId.get(id);
|
|
565
|
+
if (!proposal)
|
|
566
|
+
throw new Error(`Proposal ${id} disappeared during drain preflight.`);
|
|
567
|
+
preflightProposalPromotion(opts.config, proposal, {
|
|
568
|
+
...(opts.target ? { target: opts.target } : {}),
|
|
569
|
+
gateDecision: {
|
|
570
|
+
outcome: "auto-accepted",
|
|
571
|
+
reason: acceptGateReasons.get(id) ?? "policy-accept",
|
|
572
|
+
gate: gateLabel,
|
|
573
|
+
},
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
result.promoted.push(id);
|
|
577
|
+
deterministicPromoted += 1;
|
|
578
|
+
}
|
|
579
|
+
catch (err) {
|
|
580
|
+
warn(`[triage] preflight failed for ${id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
581
|
+
}
|
|
519
582
|
}
|
|
520
583
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
584
|
+
// applyMode "queue": leave accept candidates pending (staged). No promotion.
|
|
585
|
+
// Remaining accept budget for the judgment tier: maxAccepts minus what was
|
|
586
|
+
// actually promoted deterministically. Bounds the TOTAL promotions, not just
|
|
587
|
+
// the deterministic path. Moot in queue mode (it promotes nothing).
|
|
588
|
+
const remainingAcceptBudget = Math.max(0, Math.max(0, opts.maxAccepts) - deterministicPromoted);
|
|
589
|
+
// --- Judgment tier (Phase 3): adjudicate the deferred items ---
|
|
590
|
+
// Only runs when a RunnerSpec is configured. The runner returns a verdict; the
|
|
591
|
+
// ENGINE performs the resulting accept (respecting applyMode) / reject write.
|
|
592
|
+
if (opts.judgment && result.deferred.length > 0) {
|
|
593
|
+
if (!dispatchLease)
|
|
594
|
+
throw new TypeError("proposal judgment work requires an operation dispatch lease");
|
|
595
|
+
const tier = await runJudgmentTier({
|
|
596
|
+
stashDir: opts.stashDir,
|
|
597
|
+
applyMode: opts.applyMode,
|
|
598
|
+
dryRun: opts.dryRun,
|
|
599
|
+
runner: opts.judgment,
|
|
600
|
+
lease: dispatchLease,
|
|
601
|
+
deferred: result.deferred,
|
|
602
|
+
pending,
|
|
603
|
+
promoteFn,
|
|
604
|
+
rejectFn,
|
|
605
|
+
seams: judgmentSeams,
|
|
606
|
+
...(opts.target ? { target: opts.target } : {}),
|
|
607
|
+
...(opts.config ? { config: opts.config } : {}),
|
|
608
|
+
remainingAcceptBudget,
|
|
609
|
+
gateLabel,
|
|
610
|
+
});
|
|
611
|
+
result.promoted.push(...tier.promoted);
|
|
612
|
+
result.rejected.push(...tier.rejected);
|
|
613
|
+
result.staged.push(...tier.staged);
|
|
614
|
+
if (tier.notices.length > 0)
|
|
615
|
+
result.notices = tier.notices;
|
|
616
|
+
// Judgment-tier accepts dropped by the shared accept cap surface under
|
|
617
|
+
// skippedByCap, same as deterministic cap drops.
|
|
618
|
+
result.skippedByCap.push(...tier.skippedByCap);
|
|
619
|
+
if (tier.skippedByCap.length > 0) {
|
|
620
|
+
info(`[triage] accept ceiling reached in judgment tier: ${tier.skippedByCap.length} judged-accept items skipped by cap (maxAccepts=${opts.maxAccepts})`);
|
|
621
|
+
}
|
|
622
|
+
// Replace the deferred list with only the items the judgment tier could NOT
|
|
623
|
+
// resolve (verdict "defer", parse failure, or runner error). Staged
|
|
624
|
+
// queue-mode accepts are RESOLVED and tracked in result.staged instead.
|
|
625
|
+
result.deferred = tier.stillDeferred;
|
|
554
626
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
// (e.g. `max-diff-lines`), which is more actionable than "no judge".
|
|
567
|
-
for (const item of result.deferred) {
|
|
568
|
-
if (needsJudge.has(item.id)) {
|
|
569
|
-
stampGateDecision(opts, item.id, { outcome: "deferred", reason: "no-judge-configured", gate: gateLabel });
|
|
627
|
+
else if (result.deferred.length > 0) {
|
|
628
|
+
// #577: no judgment runner configured — items deferred *because they need a
|
|
629
|
+
// judge* (mid-band / possible-dup, no threshold reason) stay pending solely
|
|
630
|
+
// for lack of one. Re-stamp those as `no-judge-configured` so the operator
|
|
631
|
+
// sees a per-proposal reason instead of inferring it from the run-level
|
|
632
|
+
// triage_deferred aggregate. Band-deferred items keep their specific reason
|
|
633
|
+
// (e.g. `max-diff-lines`), which is more actionable than "no judge".
|
|
634
|
+
for (const item of result.deferred) {
|
|
635
|
+
if (needsJudge.has(item.id)) {
|
|
636
|
+
stampGateDecision(opts, item.id, { outcome: "deferred", reason: "no-judge-configured", gate: gateLabel });
|
|
637
|
+
}
|
|
570
638
|
}
|
|
571
639
|
}
|
|
640
|
+
emitDrainEvents(opts, result);
|
|
641
|
+
return result;
|
|
642
|
+
}
|
|
643
|
+
finally {
|
|
644
|
+
if (dispatchLease)
|
|
645
|
+
disposeLoweredExecutionDispatchLease(dispatchLease);
|
|
572
646
|
}
|
|
573
|
-
emitDrainEvents(opts, result);
|
|
574
|
-
return result;
|
|
575
647
|
}
|
|
576
648
|
/**
|
|
577
649
|
* Persist a gate decision onto a proposal, honouring the dry-run contract
|
|
@@ -18,16 +18,24 @@ import { parsePositiveIntFlag } from "../../cli/parse-args.js";
|
|
|
18
18
|
import { defineGroupCommand, defineJsonCommand, output } from "../../cli/shared.js";
|
|
19
19
|
import { resolveStashDir } from "../../core/common.js";
|
|
20
20
|
import { loadConfig } from "../../core/config/config.js";
|
|
21
|
-
import { UsageError } from "../../core/errors.js";
|
|
22
|
-
import { resolveTriageJudgmentRunner } from "../../integrations/agent/runner.js";
|
|
21
|
+
import { ConfigError, UsageError } from "../../core/errors.js";
|
|
23
22
|
import { installLlmUsagePersistenceIfAbsent } from "../../llm/usage-persist.js";
|
|
24
23
|
import { withLlmStage } from "../../llm/usage-telemetry.js";
|
|
24
|
+
import { resolveImproveExecution } from "../improve/execution.js";
|
|
25
25
|
import { extractCommand } from "../improve/extract-cli.js";
|
|
26
26
|
import { resolveImproveStrategy } from "../improve/improve-strategies.js";
|
|
27
27
|
import { drainProposals } from "./drain.js";
|
|
28
28
|
import { resolveDrainPolicy } from "./drain-policies.js";
|
|
29
29
|
import { akmProposalAccept, akmProposalDiff, akmProposalList, akmProposalReject, akmProposalRevert, akmProposalShow, bulkAdjudicateProposals, } from "./proposal.js";
|
|
30
30
|
import { proposeCommand } from "./propose-cli.js";
|
|
31
|
+
export function mergeProposalDrainNotices(resolutionNotices, dispatchNotices) {
|
|
32
|
+
const byKey = new Map();
|
|
33
|
+
for (const notice of resolutionNotices ?? [])
|
|
34
|
+
byKey.set(JSON.stringify(notice), notice);
|
|
35
|
+
for (const notice of dispatchNotices ?? [])
|
|
36
|
+
byKey.set(JSON.stringify(notice), notice);
|
|
37
|
+
return byKey.size > 0 ? Object.freeze([...byKey.values()]) : undefined;
|
|
38
|
+
}
|
|
31
39
|
/**
|
|
32
40
|
* `--source` was renamed to `--generator` on `proposal accept`/`proposal
|
|
33
41
|
* reject` in 0.9 (WS3/S8 — "Removed in 0.9.0"). citty is non-strict, so the
|
|
@@ -352,7 +360,7 @@ const proposalDrainCommand = defineJsonCommand({
|
|
|
352
360
|
},
|
|
353
361
|
judgment: {
|
|
354
362
|
type: "boolean",
|
|
355
|
-
description: "
|
|
363
|
+
description: "Explicitly enable the judgment tier for this drain (overrides judgment.enabled=false; agent/sdk per config). No-op with a logged triage_deferred summary when no runner is configured.",
|
|
356
364
|
default: false,
|
|
357
365
|
},
|
|
358
366
|
strategy: {
|
|
@@ -408,14 +416,25 @@ const proposalDrainCommand = defineJsonCommand({
|
|
|
408
416
|
})
|
|
409
417
|
.map((proposal) => proposal.id));
|
|
410
418
|
}
|
|
411
|
-
// Phase 3:
|
|
412
|
-
//
|
|
413
|
-
//
|
|
414
|
-
//
|
|
415
|
-
//
|
|
416
|
-
const
|
|
417
|
-
?
|
|
419
|
+
// Phase 3: --judgment is an invocation-level opt-in. It deliberately
|
|
420
|
+
// overrides judgment.enabled=false for this standalone drain while still
|
|
421
|
+
// reusing that block's execution overrides. Without the flag, configured
|
|
422
|
+
// judgment enablement is owned only by `akm improve`. A missing runner is
|
|
423
|
+
// a documented standalone no-op that leaves deferred items unresolved.
|
|
424
|
+
const judgmentResolution = args.judgment === true
|
|
425
|
+
? resolveImproveExecution({
|
|
426
|
+
config: cfg,
|
|
427
|
+
profile: selectedStrategy.config,
|
|
428
|
+
process: triageConfig,
|
|
429
|
+
current: triageConfig?.judgment,
|
|
430
|
+
processName: "proposal-triage-judgment",
|
|
431
|
+
})
|
|
418
432
|
: null;
|
|
433
|
+
const judgment = judgmentResolution?.runner ?? null;
|
|
434
|
+
const effectiveJudgmentLlm = triageConfig?.judgment?.llm ?? triageConfig?.llm ?? selectedStrategy.config.llm;
|
|
435
|
+
if (judgment && judgment.kind !== "llm" && effectiveJudgmentLlm) {
|
|
436
|
+
throw new ConfigError(`Triage judgment engine "${judgment.engine ?? "unknown"}" is an agent engine and cannot receive llm overrides.`, "INVALID_CONFIG_FILE");
|
|
437
|
+
}
|
|
419
438
|
// #576: persist + attribute per-call LLM usage for the standalone drain
|
|
420
439
|
// path. `IfAbsent` keeps an enclosing `akm improve` sink in charge when
|
|
421
440
|
// drain runs as a sub-step; the disposer clears only a sink we installed.
|
|
@@ -437,6 +456,8 @@ const proposalDrainCommand = defineJsonCommand({
|
|
|
437
456
|
finally {
|
|
438
457
|
disposeDrainUsageSink();
|
|
439
458
|
}
|
|
459
|
+
const dispatchNotices = result.notices;
|
|
460
|
+
const notices = mergeProposalDrainNotices(judgmentResolution?.notices, dispatchNotices);
|
|
440
461
|
output("proposal-drain", {
|
|
441
462
|
schemaVersion: 1,
|
|
442
463
|
ok: true,
|
|
@@ -446,6 +467,7 @@ const proposalDrainCommand = defineJsonCommand({
|
|
|
446
467
|
strategy: selectedStrategy.name,
|
|
447
468
|
judgmentEngine: judgment?.engine ?? null,
|
|
448
469
|
judgmentKind: judgment?.kind ?? null,
|
|
470
|
+
...(notices ? { notices } : {}),
|
|
449
471
|
promoted: result.promoted,
|
|
450
472
|
rejected: result.rejected,
|
|
451
473
|
deferred: result.deferred,
|