akm-cli 0.9.0-rc.8 → 0.9.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/CHANGELOG.md +1063 -44
- package/README.md +51 -25
- package/SECURITY.md +14 -1
- package/STABILITY.md +497 -0
- package/dist/akm +148 -35
- package/dist/{akm-migrate-storage → akm-migrate} +6 -9
- package/dist/assets/hints/cli-hints-full.md +223 -95
- package/dist/assets/hints/cli-hints-short.md +85 -22
- package/dist/assets/improve-strategies/default.json +1 -1
- package/dist/assets/improve-strategies/reflect-distill.json +1 -1
- package/dist/assets/prompts/memory-infer-user.md +2 -3
- package/dist/assets/stash-skeleton/README.md +6 -5
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +20 -9
- package/dist/assets/tasks/core/extract.yml +1 -1
- package/dist/assets/tasks/core/version-check.yml +1 -1
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
- package/dist/assets/templates/html/health.html +1 -3
- package/dist/assets/workflows/workflow-template.md +32 -15
- package/dist/cli/invocation.js +40 -15
- package/dist/cli/parse-args.js +0 -22
- package/dist/cli/retired-commands.js +121 -0
- package/dist/cli/shared.js +154 -22
- package/dist/cli/unknown-flags.js +236 -0
- package/dist/cli-node.mjs +2 -1
- package/dist/cli.js +696 -258
- package/dist/commands/agent/agent-dispatch.js +14 -3
- package/dist/commands/agent/contribute-cli.js +73 -88
- package/dist/commands/completions.js +79 -22
- package/dist/commands/config-cli.js +17 -150
- package/dist/commands/env/env-cli.js +59 -143
- package/dist/commands/env/env.js +12 -163
- package/dist/commands/env/marker-path.js +6 -0
- package/dist/commands/env/secret-cli.js +36 -66
- package/dist/commands/env/secret.js +24 -57
- package/dist/commands/feedback-cli.js +141 -87
- package/dist/commands/health/accept-rate.js +58 -0
- package/dist/commands/health/advisories.js +3 -4
- package/dist/commands/health/checks.js +85 -23
- package/dist/commands/health/html-report.js +7 -10
- package/dist/commands/health/improve-metrics.js +25 -83
- package/dist/commands/health/md-report.js +5 -9
- package/dist/commands/health/metrics.js +62 -20
- package/dist/commands/health/renderers.js +47 -0
- package/dist/commands/health/report-view-model.js +4 -5
- package/dist/commands/health/stash-exposure.js +1 -1
- package/dist/commands/health/surfaces.js +3 -48
- package/dist/commands/health/task-runs.js +3 -67
- package/dist/commands/health/types-improve.js +7 -0
- package/dist/commands/health.js +99 -28
- package/dist/commands/improve/anti-collapse.js +2 -2
- package/dist/commands/improve/autonomy-gate.js +68 -0
- package/dist/commands/improve/collapse-detector.js +41 -40
- package/dist/commands/improve/consolidate/eligibility.js +1 -23
- package/dist/commands/improve/consolidate/merge.js +4 -0
- package/dist/commands/improve/consolidate.js +140 -1000
- package/dist/commands/improve/distill/promote-memory.js +12 -12
- package/dist/commands/improve/distill/quality-gate.js +6 -6
- package/dist/commands/improve/distill.js +58 -69
- package/dist/commands/improve/eligibility.js +105 -57
- package/dist/commands/improve/extract-cli.js +14 -133
- package/dist/commands/improve/improve-cli.js +98 -114
- package/dist/commands/improve/improve-result-file.js +1 -28
- package/dist/commands/improve/improve-strategies.js +8 -5
- package/dist/commands/improve/improve.js +128 -91
- package/dist/commands/improve/loop-stages.js +182 -20
- package/dist/commands/improve/memory/derived-ref.js +45 -43
- package/dist/commands/improve/memory/memory-belief.js +1 -1
- package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
- package/dist/commands/improve/memory/memory-improve.js +6 -5
- package/dist/commands/improve/outcome-loop.js +22 -65
- package/dist/commands/improve/preparation.js +114 -123
- package/dist/commands/improve/proactive-maintenance.js +2 -5
- package/dist/commands/improve/reflect.js +56 -160
- package/dist/commands/improve/salience.js +11 -122
- package/dist/commands/improve/source-identity.js +10 -38
- package/dist/commands/lint/base-linter.js +20 -124
- package/dist/commands/lint/env-key-rules.js +31 -47
- package/dist/commands/lint/index.js +249 -43
- package/dist/commands/{events.js → log.js} +33 -38
- package/dist/commands/migrate-cli.js +92 -12
- package/dist/commands/migration-tool.js +46 -0
- package/dist/commands/observability-cli.js +70 -209
- package/dist/commands/proposal/drain.js +101 -29
- package/dist/commands/proposal/proposal-cli.js +76 -48
- package/dist/commands/proposal/proposal.js +54 -18
- package/dist/commands/proposal/propose-cli.js +88 -0
- package/dist/commands/proposal/propose.js +23 -15
- package/dist/commands/proposal/repository.js +701 -278
- package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
- package/dist/commands/proposal/validators/proposal-validators.js +55 -7
- package/dist/commands/proposal/validators/proposals.js +4 -7
- package/dist/commands/read/curate.js +34 -53
- package/dist/commands/read/knowledge.js +150 -95
- package/dist/commands/read/registry-search.js +2 -2
- package/dist/commands/read/remember-cli.js +42 -15
- package/dist/commands/read/search-cli.js +180 -78
- package/dist/commands/read/search.js +58 -43
- package/dist/commands/read/show.js +197 -141
- package/dist/commands/registry-cli.js +12 -51
- package/dist/commands/remember.js +14 -57
- package/dist/commands/sources/add-cli.js +100 -31
- package/dist/commands/sources/bundle-cli.js +166 -0
- package/dist/commands/sources/bundle-config-ops.js +7 -2
- package/dist/commands/sources/info.js +18 -5
- package/dist/commands/sources/init.js +12 -12
- package/dist/commands/sources/installed-stashes.js +382 -98
- package/dist/commands/sources/schema-repair.js +3 -2
- package/dist/commands/sources/self-update.js +131 -38
- package/dist/commands/sources/source-add.js +72 -17
- package/dist/commands/sources/source-clone.js +129 -45
- package/dist/commands/sources/source-manage.js +43 -23
- package/dist/commands/sources/sources-cli.js +57 -208
- package/dist/commands/sources/stash-cli.js +46 -53
- package/dist/commands/tasks/tasks-cli.js +91 -97
- package/dist/commands/tasks/tasks.js +276 -421
- package/dist/commands/workflow-cli.js +175 -450
- package/dist/core/adapter/adapters/akm-adapter.js +47 -28
- package/dist/core/adapter/adapters/akm-lint.js +42 -27
- package/dist/core/adapter/adapters/akm-metadata.js +15 -44
- package/dist/core/adapter/adapters/akm-task-adapter.js +15 -13
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
- package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
- package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
- package/dist/core/adapter/adapters/index.js +6 -6
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
- package/dist/core/adapter/adapters/okf-adapter.js +187 -19
- package/dist/core/adapter/adapters/shared.js +3 -19
- package/dist/core/adapter/adapters/tool-dir-shared.js +8 -3
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
- package/dist/core/adapter/detect-adapter.js +17 -0
- package/dist/core/adapter/recognize-match.js +6 -4
- package/dist/core/adapter/validate-context.js +214 -0
- package/dist/core/asset/akm-markdown.js +63 -0
- package/dist/core/asset/asset-placement.js +20 -6
- package/dist/core/asset/asset-ref.js +11 -9
- package/dist/core/asset/frontmatter-lint.js +30 -0
- package/dist/core/asset/frontmatter.js +37 -9
- package/dist/core/asset/markdown.js +40 -51
- package/dist/core/asset/resolve-ref.js +89 -18
- package/dist/core/asset/stash-meta.js +1 -1
- package/dist/core/bundle-id.js +51 -0
- package/dist/core/common.js +152 -38
- package/dist/core/config/config-io.js +12 -1
- package/dist/core/config/config-schema.js +35 -8
- package/dist/core/config/config-sources.js +55 -11
- package/dist/core/config/config-walker.js +25 -9
- package/dist/core/config/config.js +9 -48
- package/dist/core/config/experimental.js +21 -0
- package/dist/core/config/schema/embedding.js +5 -1
- package/dist/core/config/schema/experimental.js +30 -0
- package/dist/core/config/schema/improve-processes.js +0 -6
- package/dist/core/config/schema/improve.js +21 -3
- package/dist/core/config/schema/index-config.js +8 -15
- package/dist/core/config/schema/output.js +4 -1
- package/dist/core/config/schema/setup.js +9 -18
- package/dist/core/config/schema/sources-bundles.js +49 -33
- package/dist/core/config/schema/workflow.js +3 -3
- package/dist/core/env-secret-ref.js +76 -46
- package/dist/core/errors.js +18 -12
- package/dist/core/events.js +46 -128
- package/dist/core/file-change.js +6 -5
- package/dist/core/fs-txn.js +83 -7
- package/dist/core/git-message.js +2 -2
- package/dist/core/improve-result.js +1 -100
- package/dist/core/lesson-lint.js +1 -17
- package/dist/core/logs-db.js +2 -1
- package/dist/core/migration-operation.js +16 -0
- package/dist/core/mutation-target.js +78 -0
- package/dist/core/parse.js +4 -1
- package/dist/core/paths.js +17 -20
- package/dist/core/recognition-util.js +12 -14
- package/dist/core/redaction.js +34 -0
- package/dist/core/standards/resolve-standards-context.js +2 -14
- package/dist/core/standards/resolve-stash-standards.js +2 -2
- package/dist/core/standards/resolve-type-conventions.js +2 -2
- package/dist/core/state/migrations.js +41 -18
- package/dist/core/state-db.js +5 -14
- package/dist/core/structured.js +1 -1
- package/dist/core/subprocess.js +6 -4
- package/dist/core/text-truncation.js +9 -5
- package/dist/core/type-presentation.js +3 -3
- package/dist/core/warn.js +0 -3
- package/dist/core/write-source.js +771 -95
- package/dist/indexer/bundle-identity-guard.js +3 -2
- package/dist/indexer/db/graph-db.js +0 -24
- package/dist/indexer/ensure-index.js +1 -0
- package/dist/indexer/graph/graph-boost.js +9 -34
- package/dist/indexer/graph/graph-extraction.js +8 -5
- package/dist/indexer/index-writer-lock.js +53 -17
- package/dist/indexer/index-written-assets.js +16 -22
- package/dist/indexer/indexer.js +497 -239
- package/dist/indexer/installations.js +14 -96
- package/dist/indexer/passes/dir-staleness.js +16 -9
- package/dist/indexer/passes/memory-inference.js +11 -9
- package/dist/indexer/passes/metadata.js +113 -47
- package/dist/indexer/scan/doc-to-entry.js +38 -1
- package/dist/indexer/scan/drain-dir.js +13 -23
- package/dist/indexer/search/db-search.js +99 -54
- package/dist/indexer/search/fts-query.js +47 -24
- package/dist/indexer/search/ranking-contributors.js +42 -20
- package/dist/indexer/search/ranking.js +18 -99
- package/dist/indexer/search/search-fields.js +7 -2
- package/dist/indexer/search/search-source.js +82 -93
- package/dist/indexer/usage/usage-events.js +0 -89
- package/dist/indexer/walk/file-context.js +2 -1
- package/dist/indexer/walk/matchers.js +30 -43
- package/dist/indexer/walk/path-resolver.js +7 -2
- package/dist/indexer/walk/walker.js +38 -12
- package/dist/integrations/agent/builders.js +0 -6
- package/dist/integrations/agent/config.js +2 -2
- package/dist/integrations/agent/detect.js +49 -19
- package/dist/integrations/agent/engine-fallback.js +76 -0
- package/dist/integrations/agent/profiles.js +14 -0
- package/dist/integrations/agent/prompts.js +12 -8
- package/dist/integrations/agent/runner-dispatch.js +4 -2
- package/dist/integrations/agent/runner.js +0 -1
- package/dist/integrations/agent/spawn.js +5 -6
- package/dist/integrations/github.js +1 -1
- package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
- package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
- package/dist/integrations/harnesses/claude/session-log.js +0 -10
- package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
- package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
- package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
- package/dist/integrations/harnesses/index.js +3 -7
- package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
- package/dist/integrations/harnesses/opencode/session-log.js +0 -15
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
- package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
- package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
- package/dist/integrations/lockfile.js +101 -6
- package/dist/integrations/session-logs/index.js +3 -28
- package/dist/llm/client.js +136 -100
- package/dist/llm/embedders/remote.js +13 -5
- package/dist/llm/feature-gate.js +4 -12
- package/dist/llm/graph-extract.js +5 -11
- package/dist/llm/memory-infer.js +144 -1
- package/dist/llm/metadata-enhance.js +5 -7
- package/dist/llm/structured-call.js +1 -1
- package/dist/llm/usage-persist.js +26 -5
- package/dist/llm/usage-telemetry.js +25 -2
- package/dist/output/cli-hints.js +1 -2
- package/dist/output/context.js +22 -7
- package/dist/output/format-exempt.js +80 -0
- package/dist/output/generic-render.js +259 -0
- package/dist/output/render-registry.js +57 -0
- package/dist/output/renderers.js +14 -36
- package/dist/output/shapes/curate.js +10 -1
- package/dist/output/shapes/events.js +12 -7
- package/dist/output/shapes/helpers.js +56 -83
- package/dist/output/shapes/migrate.js +8 -0
- package/dist/output/shapes/passthrough.js +7 -41
- package/dist/output/shapes/proposal/producer.js +15 -7
- package/dist/output/shapes.js +2 -9
- package/dist/output/text/{init.js → bundle-create.js} +3 -1
- package/dist/output/text/bundle-show.js +7 -0
- package/dist/output/text/command-format.js +164 -96
- package/dist/output/text/env.js +1 -3
- package/dist/output/text/events.js +8 -7
- package/dist/output/text/health-format.js +103 -0
- package/dist/output/text/health.js +7 -0
- package/dist/output/text/helpers.js +10 -8
- package/dist/output/text/lint-format.js +43 -0
- package/dist/output/text/{save.js → lint.js} +2 -2
- package/dist/output/text/migrate.js +88 -0
- package/dist/output/text/proposal/producer.js +4 -2
- package/dist/output/text/proposal-format.js +44 -72
- package/dist/output/text/registry-commands.js +1 -2
- package/dist/output/text/show-directives.js +15 -7
- package/dist/output/text/status-list.js +32 -0
- package/dist/output/text/sync.js +5 -0
- package/dist/output/text/workflow-format.js +24 -203
- package/dist/output/text/workflow.js +1 -7
- package/dist/output/text.js +16 -17
- package/dist/registry/factory.js +4 -6
- package/dist/registry/origin-resolve.js +16 -27
- package/dist/registry/providers/skills-sh.js +3 -3
- package/dist/registry/providers/static-index.js +13 -23
- package/dist/registry/resolve.js +42 -7
- package/dist/registry/semver.js +34 -84
- package/dist/runtime.js +2 -23
- package/dist/scripts/akm-migrate-node.js +60290 -0
- package/dist/scripts/akm-migrate.js +59628 -0
- package/dist/setup/detect.js +42 -15
- package/dist/setup/registry-stash-loader.js +2 -2
- package/dist/setup/setup.js +236 -136
- package/dist/setup/steps/connection.js +7 -9
- package/dist/setup/steps/platforms.js +9 -9
- package/dist/setup/steps/semantic.js +15 -3
- package/dist/setup/steps/sources.js +12 -13
- package/dist/setup/steps/stashdir.js +2 -3
- package/dist/setup/steps/tasks.js +237 -120
- package/dist/sources/freshness.js +1 -1
- package/dist/sources/provider-factory.js +11 -17
- package/dist/sources/providers/filesystem.js +2 -3
- package/dist/sources/providers/git-install.js +278 -34
- package/dist/sources/providers/git-provider.js +25 -23
- package/dist/sources/providers/git-stash.js +395 -106
- package/dist/sources/providers/git.js +2 -2
- package/dist/sources/providers/npm.js +16 -19
- package/dist/sources/providers/provider-utils.js +7 -4
- package/dist/sources/providers/sync-from-ref.js +3 -9
- package/dist/sources/providers/website.js +6 -1
- package/dist/sources/resolve.js +6 -5
- package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
- package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
- package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
- package/dist/sources/snapshot-fetchers/github.js +100 -0
- package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
- package/dist/sources/snapshot-fetchers/registry.js +17 -1
- package/dist/sources/snapshot-fetchers/robots.js +348 -0
- package/dist/sources/snapshot-fetchers/rss.js +282 -0
- package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
- package/dist/sources/snapshot-fetchers/website-ingest.js +566 -268
- package/dist/sources/snapshot-fetchers/x.js +910 -0
- package/dist/storage/database.js +7 -0
- package/dist/storage/engines/sqlite-migrations.js +23 -111
- package/dist/storage/managed-db.js +2 -2
- package/dist/storage/repositories/canaries-repository.js +1 -1
- package/dist/storage/repositories/events-repository.js +27 -11
- package/dist/storage/repositories/improve-runs-repository.js +6 -12
- package/dist/storage/repositories/index-connection.js +17 -6
- package/dist/storage/repositories/index-entries-repository.js +151 -240
- package/dist/storage/repositories/index-entry-mapper.js +15 -11
- package/dist/storage/repositories/index-fts-repository.js +5 -2
- package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
- package/dist/storage/repositories/index-meta-repository.js +2 -3
- package/dist/storage/repositories/index-schema.js +10 -25
- package/dist/storage/repositories/index-utility-repository.js +15 -28
- package/dist/storage/repositories/index-vec-repository.js +6 -1
- package/dist/storage/repositories/outcome-repository.js +119 -0
- package/dist/storage/repositories/proposals-repository.js +296 -59
- package/dist/storage/repositories/registry-cache.js +19 -0
- package/dist/storage/repositories/salience-repository.js +172 -0
- package/dist/storage/repositories/task-history-repository.js +15 -13
- package/dist/storage/repositories/workflow-runs-repository.js +52 -40
- package/dist/tasks/backends/cron.js +105 -15
- package/dist/tasks/backends/index.js +1 -1
- package/dist/tasks/backends/launchd.js +85 -38
- package/dist/tasks/backends/schtasks.js +135 -15
- package/dist/tasks/embedded.js +56 -40
- package/dist/tasks/parser.js +7 -157
- package/dist/tasks/resolve-akm-bin.js +137 -59
- package/dist/tasks/runner.js +79 -42
- package/dist/tasks/scheduler-invocation.js +220 -10
- package/dist/tasks/schema.js +24 -1
- package/dist/tasks/task-id.js +1 -3
- package/dist/tasks/validator.js +20 -6
- package/dist/workflows/authoring/authoring.js +94 -143
- package/dist/workflows/authoring/scope-key.js +1 -1
- package/dist/workflows/exec/frozen-judge.js +28 -2
- package/dist/workflows/exec/native-executor.js +77 -57
- package/dist/workflows/exec/param-secrets.js +9 -9
- package/dist/workflows/exec/run-workflow.js +133 -79
- package/dist/workflows/exec/step-work.js +219 -346
- package/dist/{migrate-storage-node.mjs → workflows/exec/unit-dispatch.js} +1 -5
- package/dist/workflows/ir/compile.js +141 -270
- package/dist/workflows/ir/freeze.js +40 -30
- package/dist/workflows/ir/params.js +135 -11
- package/dist/workflows/ir/plan-hash.js +1 -1
- package/dist/workflows/ir/schema.js +25 -26
- package/dist/workflows/parser.js +872 -307
- package/dist/workflows/program/expressions.js +20 -208
- package/dist/workflows/program/schema.js +7 -10
- package/dist/workflows/renderer.js +95 -68
- package/dist/workflows/resource-limits.js +2 -0
- package/dist/workflows/runtime/checkin.js +3 -3
- package/dist/workflows/runtime/plan-classifier.js +16 -75
- package/dist/workflows/runtime/runs.js +186 -127
- package/dist/workflows/runtime/unit-checkin.js +1 -1
- package/dist/workflows/runtime/unit-phases.js +2 -2
- package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
- package/dist/workflows/schema.js +1 -11
- package/dist/workflows/validate-summary.js +30 -36
- package/dist/workflows/validator.js +21 -62
- package/docs/README.md +68 -0
- package/docs/migration/README.md +8 -0
- package/docs/migration/release-notes/0.7.0.md +11 -11
- package/docs/migration/release-notes/0.9.0.md +208 -27
- package/docs/migration/v0.7-to-v0.8.md +46 -47
- package/docs/migration/v0.8-to-v0.9.md +564 -208
- package/docs/migration/v0.9.0-troubleshooting.md +561 -0
- package/docs/reference/README.md +12 -0
- package/docs/reference/cli.md +2253 -0
- package/docs/reference/configuration.md +358 -0
- package/docs/reference/data-and-telemetry.md +105 -42
- package/docs/reference/workflows.md +647 -0
- package/package.json +22 -11
- package/schemas/akm-asset-envelope.json +93 -0
- package/schemas/akm-config.json +81 -128
- package/schemas/akm-workflow.json +74 -73
- package/dist/assets/tasks/core/backup.yml +0 -5
- package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
- package/dist/cli/config-migrate.js +0 -1806
- package/dist/cli/config-validate.js +0 -41
- package/dist/commands/backup-cli.js +0 -56
- package/dist/commands/bundle/bundle-cli.js +0 -68
- package/dist/commands/bundle/bundle.js +0 -219
- package/dist/commands/graph/graph-cli.js +0 -124
- package/dist/commands/graph/graph.js +0 -489
- package/dist/commands/improve/extract-watch.js +0 -140
- package/dist/commands/mv-cli.js +0 -1221
- package/dist/commands/sources/history.js +0 -201
- package/dist/commands/tasks/default-tasks.js +0 -186
- package/dist/core/migration-backup.js +0 -1234
- package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
- package/dist/llm/memory-infer-impl.js +0 -138
- package/dist/migrate/legacy/config-source-migration.js +0 -223
- package/dist/migrate/legacy/content-migration.js +0 -305
- package/dist/migrate/legacy/legacy-layout.js +0 -779
- package/dist/migrate/legacy/legacy-paths.js +0 -25
- package/dist/migrate/legacy/legacy-stash-json.js +0 -72
- package/dist/migrate/legacy/proposal-fs-import.js +0 -168
- package/dist/migrate/legacy/task-target-ref-migration.js +0 -272
- package/dist/migrate/legacy/three-db-cutover.js +0 -841
- package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
- package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
- package/dist/migrate/legacy-ref-grammar.js +0 -214
- package/dist/output/shapes/distill.js +0 -14
- package/dist/output/shapes/history.js +0 -11
- package/dist/output/text/distill.js +0 -6
- package/dist/output/text/enable-disable.js +0 -8
- package/dist/output/text/history.js +0 -6
- package/dist/registry/build-index.js +0 -382
- package/dist/schemas/akm-config.json +0 -4704
- package/dist/schemas/akm-task.json +0 -87
- package/dist/schemas/akm-workflow.json +0 -372
- package/dist/scripts/migrate-storage.js +0 -3816
- package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
- package/dist/workflows/cli.js +0 -53
- package/dist/workflows/exec/brief.js +0 -481
- package/dist/workflows/exec/report.js +0 -1460
- package/dist/workflows/exec/watch.js +0 -116
- package/dist/workflows/program/parser.js +0 -813
- package/dist/workflows/program/project.js +0 -104
|
@@ -3,14 +3,13 @@
|
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
/**
|
|
5
5
|
* Shared step semantics — the ONE implementation of a step's orchestration
|
|
6
|
-
* decisions, consumed by
|
|
7
|
-
* `native-executor.ts`)
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* run of the same frozen plan produce byte-identical unit graphs.
|
|
6
|
+
* decisions, consumed by the engine loop (`run-workflow.ts` +
|
|
7
|
+
* `native-executor.ts`) on both the fresh-execution and the resume/replay path.
|
|
8
|
+
* The cardinal rule here is *no duplicated semantics*: work-list computation,
|
|
9
|
+
* prompt assembly, reducer/artifact promotion, output-schema validation,
|
|
10
|
+
* artifact-judged gate summaries, gate-feedback recovery, and route evaluation
|
|
11
|
+
* live here so a first run and a resumed run of the same frozen plan produce
|
|
12
|
+
* byte-identical unit graphs.
|
|
14
13
|
*
|
|
15
14
|
* ## What is PURE here
|
|
16
15
|
*
|
|
@@ -19,8 +18,9 @@
|
|
|
19
18
|
* recovered feedback) — is a pure function: same inputs ⇒ same unit ids, input
|
|
20
19
|
* hashes, and fully-resolved prompts. It takes NO clock, NO IO, and NO journal
|
|
21
20
|
* (journal-derived state, i.e. the recovered gate feedback, is passed in). This
|
|
22
|
-
* is the load-bearing guarantee that
|
|
23
|
-
*
|
|
21
|
+
* is the load-bearing guarantee that a resumed run recomputes exactly the units
|
|
22
|
+
* the original run dispatched, so journaled rows can be reused instead of
|
|
23
|
+
* re-executed. So are the reducer/artifact helpers
|
|
24
24
|
* ({@link buildEvidence}, {@link projectStepOutput}, {@link validateStepArtifact},
|
|
25
25
|
* {@link buildArtifactSummary}), the gate-feedback recovery
|
|
26
26
|
* ({@link recoverGateFeedback} / {@link activeGateLoop}), and route evaluation
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
* The gate-evaluation journaling ({@link journalGateEvaluationStart} /
|
|
32
32
|
* {@link journalGateEvaluationFinish}) writes `workflow_run_units` rows through
|
|
33
33
|
* the serialized writer queue — an engine-driven judge call is an LLM call and
|
|
34
|
-
* is journaled like a unit. It lives here (not in the engine loop) so
|
|
35
|
-
*
|
|
34
|
+
* is journaled like a unit. It lives here (not in the engine loop) so every
|
|
35
|
+
* caller journals gate evaluations through the identical writer.
|
|
36
36
|
*
|
|
37
37
|
* This module NEVER dispatches a unit and NEVER writes step rows: dispatch is
|
|
38
38
|
* the executor's job (`native-executor.ts`), advancing the gated spine is the
|
|
@@ -44,19 +44,17 @@ import { UsageError } from "../../core/errors.js";
|
|
|
44
44
|
import { appendEvent } from "../../core/events.js";
|
|
45
45
|
import { validateJsonSchemaSubset } from "../../core/json-schema.js";
|
|
46
46
|
import { withWorkflowRunsRepo } from "../../storage/repositories/workflow-runs-repository.js";
|
|
47
|
-
import { canonicalJson as canonicalJsonString
|
|
48
|
-
import {
|
|
47
|
+
import { canonicalJson as canonicalJsonString } from "../ir/plan-hash.js";
|
|
48
|
+
import { resolveReferenceString } from "../program/expressions.js";
|
|
49
49
|
import { WORKFLOW_MAX_MAP_EXPANSION } from "../resource-limits.js";
|
|
50
50
|
import { requireExecutableWorkflowPlan } from "../runtime/plan-classifier.js";
|
|
51
51
|
import { completeWorkflowStep } from "../runtime/runs.js";
|
|
52
52
|
import { GATE_EVALUATION_PHASE } from "../runtime/unit-phases.js";
|
|
53
53
|
import { enqueueUnitWrite } from "./unit-writer.js";
|
|
54
54
|
/**
|
|
55
|
-
* Default per-unit timeout
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* (`tryLlmFeature`). A unit's `timeout` declaration overrides this; `none`
|
|
59
|
-
* disables.
|
|
55
|
+
* Default per-unit timeout for workflow units. A unit's `timeout` declaration
|
|
56
|
+
* overrides this; `none` disables it. Direct agent dispatch has no timeout by
|
|
57
|
+
* default, while workflow units retain an independent safety ceiling.
|
|
60
58
|
*/
|
|
61
59
|
export const DEFAULT_UNIT_TIMEOUT_MS = 600_000;
|
|
62
60
|
/** How much raw unit output is retained in step evidence (full text lives on the unit row). */
|
|
@@ -68,14 +66,54 @@ const GATE_ARTIFACT_CLIP = 4_000;
|
|
|
68
66
|
* inputs: resolve the fan-out list, derive content-derived unit ids, assemble
|
|
69
67
|
* each unit's prompt (preamble + interpolated instructions + gate feedback +
|
|
70
68
|
* schema directive), and hash the resolved input. Same inputs ⇒ byte-identical
|
|
71
|
-
* ids/hashes/prompts — the invariant
|
|
69
|
+
* ids/hashes/prompts — the invariant resume/replay relies on to recognize the
|
|
70
|
+
* units an earlier run already journaled.
|
|
72
71
|
*
|
|
73
|
-
* Whole-list failures (missing subgraph,
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
72
|
+
* Whole-list failures (missing subgraph, unresolvable / non-array `over`,
|
|
73
|
+
* null or duplicate fan-out items) return `{ ok: false }`. The per-unit
|
|
74
|
+
* `resolved: { ok: false }` branch is STRUCTURALLY UNREACHABLE in the unified
|
|
75
|
+
* format — prose is never scanned for references, and everything that CAN
|
|
76
|
+
* fail (map.over / route.input / inputs:) resolves once per step, failing the
|
|
77
|
+
* whole list above. The branch is retained because every consumer of the work
|
|
78
|
+
* list shares the shape and defensively handles it; if a future unit kind
|
|
79
|
+
* reintroduces per-unit resolution (e.g. an exec/shell unit with real
|
|
80
|
+
* substitution), the failure plumbing is already in place.
|
|
78
81
|
*/
|
|
82
|
+
/**
|
|
83
|
+
* Validate a fan-out item list BEFORE any identity/dispatch work: expansion
|
|
84
|
+
* within the resource limit, no null/undefined items, no canonical duplicates.
|
|
85
|
+
* Returns the failure message, or undefined when the list is dispatchable.
|
|
86
|
+
*
|
|
87
|
+
* Null items: producer garbage — there is nothing to hand the unit as its work
|
|
88
|
+
* item. The pre-unification format rejected them incidentally (substituting
|
|
89
|
+
* `${{ item }}` failed); with items attached as context instead of spliced,
|
|
90
|
+
* nothing later would stop a unit from being dispatched with "Item: null", so
|
|
91
|
+
* the rejection is explicit here. Duplicates: content-derived unit identity
|
|
92
|
+
* makes canonical duplicates collide on id — an authoring error caught
|
|
93
|
+
* deterministically, before dispatch.
|
|
94
|
+
*/
|
|
95
|
+
function validateFanOutItems(stepId, items) {
|
|
96
|
+
if (items.length > WORKFLOW_MAX_MAP_EXPANSION) {
|
|
97
|
+
return `Step "${stepId}" fan-out expands to ${items.length} units, exceeding the ${WORKFLOW_MAX_MAP_EXPANSION}-unit resource limit.`;
|
|
98
|
+
}
|
|
99
|
+
const nullIndex = items.findIndex((item) => item === null || item === undefined);
|
|
100
|
+
if (nullIndex !== -1) {
|
|
101
|
+
return (`Step "${stepId}" fan-out list contains a null item (index ${nullIndex}). ` +
|
|
102
|
+
`Every item must be a concrete value — fix the producing step's output.`);
|
|
103
|
+
}
|
|
104
|
+
const firstIndexByCanonical = new Map();
|
|
105
|
+
for (let i = 0; i < items.length; i++) {
|
|
106
|
+
const canonical = canonicalJson(items[i]) ?? "null";
|
|
107
|
+
const firstIndex = firstIndexByCanonical.get(canonical);
|
|
108
|
+
if (firstIndex !== undefined) {
|
|
109
|
+
return (`Step "${stepId}" fan-out list contains duplicate items (indices ${firstIndex} and ${i}: ` +
|
|
110
|
+
`${clip(canonical, 200)}). Content-derived unit identity requires distinct items — ` +
|
|
111
|
+
`deduplicate the list this workflow fans out over.`);
|
|
112
|
+
}
|
|
113
|
+
firstIndexByCanonical.set(canonical, i);
|
|
114
|
+
}
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
79
117
|
export function computeStepWorkList(plan, input) {
|
|
80
118
|
const root = plan.root;
|
|
81
119
|
// Route-only steps (YAML `route:`) carry no execution subgraph.
|
|
@@ -88,30 +126,29 @@ export function computeStepWorkList(plan, input) {
|
|
|
88
126
|
const template = root.kind === "map" ? root.template : root;
|
|
89
127
|
const reducer = root.kind === "map" ? root.reducer : "collect";
|
|
90
128
|
const scope = { params: input.params, stepOutputs: input.stepOutputs };
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
129
|
+
// Instructions are ALWAYS the step's body prose, byte-exact — never
|
|
130
|
+
// templated, never scanned for reference syntax (workflow-format-
|
|
131
|
+
// unification, spec §2.3). Only `map.over` / `route.input` / `inputs[]`
|
|
132
|
+
// carry the closed reference grammar.
|
|
133
|
+
// Resolve the step's declared `inputs:` ONCE (shared by every unit in this
|
|
134
|
+
// step — map items differ, declared inputs do not): prior-step artifacts
|
|
135
|
+
// attached to every dispatched unit as structured context.
|
|
136
|
+
const resolvedInputs = [];
|
|
137
|
+
for (const reference of template.inputs ?? []) {
|
|
138
|
+
const resolved = resolveReferenceString(reference, scope);
|
|
139
|
+
if (!resolved.ok) {
|
|
99
140
|
return {
|
|
100
141
|
ok: false,
|
|
101
|
-
error: `Step "${plan.stepId}"
|
|
102
|
-
parsedInstructions.errors.map((e) => e.message).join(" "),
|
|
142
|
+
error: `Step "${plan.stepId}" declared input "${reference}" failed to resolve: ${resolved.error.message}`,
|
|
103
143
|
};
|
|
104
144
|
}
|
|
105
|
-
|
|
145
|
+
resolvedInputs.push({ reference, value: resolved.value });
|
|
106
146
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
// Resolve fan-out items: `over` is a single whole-value `${{ … }}` reference
|
|
111
|
-
// naming its producer explicitly — no ambient key search.
|
|
147
|
+
// Resolve fan-out items: `over` is a single whole-value reference naming
|
|
148
|
+
// its producer explicitly — no ambient key search.
|
|
112
149
|
let items;
|
|
113
150
|
if (root.kind === "map") {
|
|
114
|
-
const source =
|
|
151
|
+
const source = resolveReferenceString(root.over, scope);
|
|
115
152
|
if (!source.ok) {
|
|
116
153
|
return {
|
|
117
154
|
ok: false,
|
|
@@ -130,31 +167,12 @@ export function computeStepWorkList(plan, input) {
|
|
|
130
167
|
items = [undefined];
|
|
131
168
|
}
|
|
132
169
|
const isFanOut = root.kind === "map";
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
// Content-derived unit identity: compute every id up front. Duplicate items
|
|
140
|
-
// collide on identity — an authoring error caught HERE, deterministically.
|
|
170
|
+
const fanOutProblem = isFanOut ? validateFanOutItems(plan.stepId, items) : undefined;
|
|
171
|
+
if (fanOutProblem)
|
|
172
|
+
return { ok: false, error: fanOutProblem };
|
|
173
|
+
// Content-derived unit identity: compute every id up front (duplicate items
|
|
174
|
+
// were rejected above — identity requires distinct items).
|
|
141
175
|
const unitIds = items.map((item) => unitIdFor(template.id, item, isFanOut));
|
|
142
|
-
if (isFanOut) {
|
|
143
|
-
const firstIndexByCanonical = new Map();
|
|
144
|
-
for (let i = 0; i < items.length; i++) {
|
|
145
|
-
const canonical = canonicalJson(items[i]) ?? "null";
|
|
146
|
-
const firstIndex = firstIndexByCanonical.get(canonical);
|
|
147
|
-
if (firstIndex !== undefined) {
|
|
148
|
-
return {
|
|
149
|
-
ok: false,
|
|
150
|
-
error: `Step "${plan.stepId}" fan-out list contains duplicate items (indices ${firstIndex} and ${i}: ` +
|
|
151
|
-
`${clip(canonical, 200)}). Content-derived unit identity requires distinct items — ` +
|
|
152
|
-
`deduplicate the list this workflow fans out over.`,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
firstIndexByCanonical.set(canonical, i);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
176
|
const gateLoop = input.gateLoop ?? 1;
|
|
159
177
|
const frozenInvocation = template.invocation;
|
|
160
178
|
if (!frozenInvocation)
|
|
@@ -171,66 +189,80 @@ export function computeStepWorkList(plan, input) {
|
|
|
171
189
|
// never clobbered; the content-derived identity (and the prompt's
|
|
172
190
|
// {{UNIT_ID}}) stays the base id.
|
|
173
191
|
const journalBaseId = gateLoop > 1 ? `${unitId}~l${gateLoop}` : unitId;
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
192
|
+
// Context attachment (workflow-format-unification, spec §4): every unit
|
|
193
|
+
// receives the run params (already in the preamble), its item + index if
|
|
194
|
+
// it is a map unit, and the artifacts named by its step's `inputs:`.
|
|
195
|
+
// Instructions reach the unit byte-exact — never interpolated.
|
|
196
|
+
const prompt = buildUnitPrompt({
|
|
197
|
+
runId: input.runId,
|
|
198
|
+
stepId: plan.stepId,
|
|
199
|
+
unitId,
|
|
200
|
+
params: input.params,
|
|
201
|
+
...(isFanOut ? { item, itemIndex: index } : {}),
|
|
202
|
+
...(resolvedInputs.length > 0 ? { inputs: resolvedInputs } : {}),
|
|
203
|
+
...(input.gateFeedback ? { gateFeedback: input.gateFeedback } : {}),
|
|
204
|
+
...(template.schema ? { schema: template.schema } : {}),
|
|
205
|
+
instructions: template.instructions,
|
|
206
|
+
});
|
|
207
|
+
// Canonical dispatch-input envelope (reviewer finding #1). Every field
|
|
208
|
+
// here is a PLAN-FROZEN input that changes what the backend is actually
|
|
209
|
+
// asked to do, so a completed unit is reused ONLY when all of them match;
|
|
210
|
+
// a change to any of them re-dispatches. Key order is FIXED — it is the
|
|
211
|
+
// hash preimage (JSON.stringify preserves insertion order) — and this is
|
|
212
|
+
// the ONE place a unit's inputHash is computed (every caller goes through
|
|
213
|
+
// computeStepWorkList), so a hash that is byte-identical across a fresh
|
|
214
|
+
// run and a resume is structural, not coincidental.
|
|
215
|
+
//
|
|
216
|
+
// Unit identity (workflow-format-unification, spec §2.3/§4) hashes the
|
|
217
|
+
// FROZEN TEMPLATE BYTES (`template.instructions`, byte-exact, never an
|
|
218
|
+
// instantiated/interpolated string) + the canonical item JSON + the
|
|
219
|
+
// declared-input artifact hashes + the params snapshot — instead of a
|
|
220
|
+
// resolved/spliced prompt string, since there is no more splicing. The
|
|
221
|
+
// assembled `prompt` above is what the harness SEES; the hash is over the
|
|
222
|
+
// plan-frozen INPUTS that determine it, which is the same replay contract
|
|
223
|
+
// the old resolved-prompt hash gave (same inputs ⇒ same hash) with the
|
|
224
|
+
// interpolation step removed.
|
|
225
|
+
//
|
|
226
|
+
// Included beyond the R4 baseline (template/runner/model/schema): resolved
|
|
227
|
+
// timeoutMs, the env asset ref NAMES, and isolation — each reaches
|
|
228
|
+
// dispatch (native-executor's UnitDispatchRequest) and a changed one
|
|
229
|
+
// yields a materially different call. `env` carries NAMES ONLY, never
|
|
230
|
+
// resolved values: hashing a resolved secret would leak it into a durable
|
|
231
|
+
// hash oracle and would spuriously re-dispatch on every secret rotation.
|
|
232
|
+
// `retry`/`onError` are DELIBERATELY excluded — they govern failed-unit
|
|
233
|
+
// re-dispatch and step-level failure reduction, not a COMPLETED unit's
|
|
234
|
+
// inputs/output, so a completed row stays valid across policy changes.
|
|
235
|
+
//
|
|
236
|
+
// `gateFeedback` IS included (conditionally, so a no-feedback unit's
|
|
237
|
+
// preimage is byte-identical to before): it is appended to the prompt by
|
|
238
|
+
// `buildUnitPrompt`, so a gate loop's retry is materially a different ask
|
|
239
|
+
// than the rejected attempt — omitting it made loop 1 and loop 2 journal
|
|
240
|
+
// identical hashes for different prompts, breaking the "changed inputs ⇒
|
|
241
|
+
// changed hash" audit contract. Replay-safe: feedback is re-derived from
|
|
242
|
+
// the journaled gate decision, so a resumed retry re-hashes identically.
|
|
243
|
+
//
|
|
244
|
+
// Ambient config is DELIBERATELY excluded — the model-alias table, the
|
|
245
|
+
// resolved backend/connection, and the working directory (`ctx.workDir` /
|
|
246
|
+
// process.cwd()) are NOT plan-frozen. The frozen plan is the identity
|
|
247
|
+
// boundary (redesign addendum determinism bar #2): config drift under an
|
|
248
|
+
// in-flight run is out of scope by design.
|
|
249
|
+
const dispatch = transitiveDispatchSnapshot(frozenEngine, input.engines ?? {});
|
|
250
|
+
const inputHash = createHash("sha256")
|
|
251
|
+
.update(canonicalJsonString({
|
|
252
|
+
hashVersion: 4,
|
|
253
|
+
template: template.instructions,
|
|
254
|
+
item: isFanOut ? (item ?? null) : null,
|
|
255
|
+
inputs: resolvedInputs,
|
|
256
|
+
params: input.params,
|
|
257
|
+
dispatch,
|
|
258
|
+
invocation: frozenInvocation,
|
|
259
|
+
schema: template.schema ?? null,
|
|
260
|
+
env: template.env ?? null,
|
|
261
|
+
isolation: template.isolation ?? "none",
|
|
262
|
+
...(input.gateFeedback ? { gateFeedback: input.gateFeedback } : {}),
|
|
263
|
+
}))
|
|
264
|
+
.digest("hex");
|
|
265
|
+
const resolved = { ok: true, prompt, inputHash };
|
|
234
266
|
return {
|
|
235
267
|
unitId,
|
|
236
268
|
nodeId: template.id,
|
|
@@ -265,13 +297,16 @@ export function computeStepWorkList(plan, input) {
|
|
|
265
297
|
};
|
|
266
298
|
}
|
|
267
299
|
/**
|
|
268
|
-
* Assemble the final prompt: engine preamble +
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
300
|
+
* Assemble the final prompt: engine preamble (run params + item/index +
|
|
301
|
+
* declared-input artifacts, all as structured JSON context) + the step's
|
|
302
|
+
* BYTE-EXACT prose instructions (+ gate feedback on loop re-executions, +
|
|
303
|
+
* schema directive). Instructions are NEVER interpolated (workflow-format-
|
|
304
|
+
* unification, spec §2.3) — data reaches the unit as attached context, not
|
|
305
|
+
* string splices; only the ENGINE's own preamble placeholders are substituted
|
|
306
|
+
* here.
|
|
272
307
|
*/
|
|
273
308
|
export function buildUnitPrompt(input) {
|
|
274
|
-
const { runId, stepId, unitId, params, gateFeedback, schema, instructions } = input;
|
|
309
|
+
const { runId, stepId, unitId, params, itemIndex, item, inputs, gateFeedback, schema, instructions } = input;
|
|
275
310
|
// Function replacements throughout: a string replacement would interpret
|
|
276
311
|
// GetSubstitution patterns ($&, $$, $', $`) inside VALUES and silently
|
|
277
312
|
// corrupt the prompt (e.g. a param value containing "$&").
|
|
@@ -280,6 +315,15 @@ export function buildUnitPrompt(input) {
|
|
|
280
315
|
.replaceAll("{{STEP_ID}}", () => stepId)
|
|
281
316
|
.replaceAll("{{UNIT_ID}}", () => unitId)
|
|
282
317
|
.replaceAll("{{PARAMS_JSON}}", () => safeJson(params));
|
|
318
|
+
// Map-unit context: the item this unit was given, plus its index. Attached
|
|
319
|
+
// as structured JSON — the engine never splices it into the instructions.
|
|
320
|
+
const itemBlock = itemIndex !== undefined
|
|
321
|
+
? `\n\n## Item (index ${itemIndex})\nYou were given this item from the fan-out list:\n${safeJson(item)}`
|
|
322
|
+
: "";
|
|
323
|
+
// Declared `inputs:` context: the prior-step artifacts this step named.
|
|
324
|
+
const inputsBlock = inputs && inputs.length > 0
|
|
325
|
+
? `\n\n## Declared inputs\n${inputs.map((i) => `### ${i.reference}\n${safeJson(i.value)}`).join("\n\n")}`
|
|
326
|
+
: "";
|
|
283
327
|
// Gate-loop feedback (R2 max_loops): the judge's rejection is appended so
|
|
284
328
|
// the re-executed unit can address it — and so the input hash changes,
|
|
285
329
|
// making the loop's re-dispatch natural instead of a durable-row reuse.
|
|
@@ -294,7 +338,7 @@ export function buildUnitPrompt(input) {
|
|
|
294
338
|
const schemaDirective = schema
|
|
295
339
|
? `\n\nRespond with ONLY a JSON value matching this JSON Schema (no prose, no code fences):\n${safeJson(schema)}`
|
|
296
340
|
: "";
|
|
297
|
-
return `${preamble}\n${instructions}${gateBlock}${schemaDirective}`;
|
|
341
|
+
return `${preamble}\n${instructions}${itemBlock}${inputsBlock}${gateBlock}${schemaDirective}`;
|
|
298
342
|
}
|
|
299
343
|
/**
|
|
300
344
|
* Content-derived unit identity (module doc): `<node_id>:<hash12>` for a
|
|
@@ -376,18 +420,17 @@ function unitOutputValue(unit) {
|
|
|
376
420
|
return unit.text ?? null;
|
|
377
421
|
}
|
|
378
422
|
export function buildEvidence(units, reducer, isFanOut) {
|
|
379
|
-
// Per-unit evidence is the DURABLE
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
// surfaces can reproduce from the journal:
|
|
423
|
+
// Per-unit evidence is the DURABLE projection of the unit graph — a fresh run
|
|
424
|
+
// and a resumed run of the same plan must agree on it byte-for-byte. It
|
|
425
|
+
// therefore carries ONLY fields that can be reproduced from the journal alone:
|
|
383
426
|
// - a SUCCESS keeps its promoted contribution (structured `result` or clipped
|
|
384
|
-
// `text`) — the
|
|
427
|
+
// `text`) — the reuse path rehydrates exactly these from the unit row;
|
|
385
428
|
// - a FAILURE keeps only its `failureReason` (the durable, journaled failure
|
|
386
|
-
// vocabulary). The
|
|
387
|
-
//
|
|
388
|
-
//
|
|
389
|
-
//
|
|
390
|
-
// the unit row for
|
|
429
|
+
// vocabulary). The in-memory dispatch diagnostic (`error`) and any residual
|
|
430
|
+
// `text` on a failed unit are NOT persisted here: they do not survive a
|
|
431
|
+
// restart, so persisting them on the live-dispatch path alone would make
|
|
432
|
+
// the durable graph depend on WHEN it was built. The full raw text/reason
|
|
433
|
+
// still lives on the unit row for diagnostics; this is the shared graph.
|
|
391
434
|
const collected = units.map((u) => u.ok
|
|
392
435
|
? {
|
|
393
436
|
unitId: u.unitId,
|
|
@@ -478,13 +521,11 @@ export function reduceStepOutcomes(plan, reducer, isFanOut, onError, units) {
|
|
|
478
521
|
* resolution rather than silently reading the envelope). Even the degenerate
|
|
479
522
|
* artifact must honor the step's declared `outputSchema` before it can complete.
|
|
480
523
|
*
|
|
481
|
-
*
|
|
482
|
-
*
|
|
483
|
-
*
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
* an empty step has no successful results to count, and a vote-tie "failure"
|
|
487
|
-
* would diverge from the engine's long-standing empty-list semantics.
|
|
524
|
+
* Used by native dispatch (`executeStepPlan`'s `items.length === 0` branch): a
|
|
525
|
+
* zero-unit step can never be advanced by a unit completion, so it is promoted
|
|
526
|
+
* here instead. Deliberately does NOT run the reducer/vote-tie logic: an empty
|
|
527
|
+
* step has no successful results to count, and a vote-tie "failure" would
|
|
528
|
+
* diverge from the engine's long-standing empty-list semantics.
|
|
488
529
|
*/
|
|
489
530
|
export function reduceEmptyStep(plan, reducer) {
|
|
490
531
|
const evidence = { units: [], itemCount: 0, output: reducer === "collect" ? [] : null };
|
|
@@ -498,12 +539,13 @@ export function reduceEmptyStep(plan, reducer) {
|
|
|
498
539
|
};
|
|
499
540
|
}
|
|
500
541
|
/**
|
|
501
|
-
* Rehydrate a journaled unit row into a {@link UnitOutcome}.
|
|
502
|
-
*
|
|
503
|
-
*
|
|
504
|
-
* journal
|
|
505
|
-
*
|
|
506
|
-
* `failure_reason`; any journaled
|
|
542
|
+
* Rehydrate a journaled unit row into a {@link UnitOutcome}. The executor's
|
|
543
|
+
* durable-row reuse (`native-executor.ts`) calls it for completed rows; the
|
|
544
|
+
* failed-row branch keeps the mapping TOTAL, so any reduction driven off the
|
|
545
|
+
* journal yields the same outcome the live dispatch produced. A completed row's
|
|
546
|
+
* text unit journals its output as a JSON string; a schema unit journals the
|
|
547
|
+
* validated structure. A failed row carries its `failure_reason`; any journaled
|
|
548
|
+
* text is surfaced too.
|
|
507
549
|
*/
|
|
508
550
|
export function unitOutcomeFromRow(unitId, row, hasSchema) {
|
|
509
551
|
let parsed;
|
|
@@ -536,97 +578,6 @@ export function unitOutcomeFromRow(unitId, row, hasSchema) {
|
|
|
536
578
|
...(row.tokens !== null ? { tokens: row.tokens } : {}),
|
|
537
579
|
};
|
|
538
580
|
}
|
|
539
|
-
/**
|
|
540
|
-
* Select the journaled attempt row that determines a unit's TERMINAL outcome on
|
|
541
|
-
* a REPLAY surface — the engine's durable-row reuse AND the harness-neutral
|
|
542
|
-
* brief/report driver protocol — given the run's dispatch rows indexed by
|
|
543
|
-
* unit_id. This is the ONE place all surfaces resolve "which journaled row IS
|
|
544
|
-
* this unit's outcome," so they cannot drift from each other or from the engine.
|
|
545
|
-
*
|
|
546
|
-
* It mirrors the executor's {@link classifyUnitReuse} attempt scan
|
|
547
|
-
* (native-executor.ts): among the base attempt and its `~r<n>` retries — all
|
|
548
|
-
* stacked on `journalBaseId`, which already carries the active `~l<loop>` gate
|
|
549
|
-
* suffix — the FIRST completed attempt is the effective result. So a unit whose
|
|
550
|
-
* base attempt FAILED but whose later retry COMPLETED reduces as COMPLETED,
|
|
551
|
-
* exactly like an engine resume reusing the `~r1` row (Codex round-3 finding C);
|
|
552
|
-
* reading only the base row would reduce it as failed and diverge the two
|
|
553
|
-
* surfaces. With no completed attempt the HIGHEST journaled attempt stands (a
|
|
554
|
-
* terminal failure, or a still-running row); no attempt row at all ⇒ `undefined`
|
|
555
|
-
* (the unit is still outstanding).
|
|
556
|
-
*/
|
|
557
|
-
export function selectUnitAttemptRow(workUnit, dispatchRows) {
|
|
558
|
-
const base = workUnit.journalBaseId;
|
|
559
|
-
const maxAttempts = 1 + Math.max(0, workUnit.retry?.max ?? 0);
|
|
560
|
-
let fallback;
|
|
561
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
562
|
-
const row = dispatchRows.get(attempt === 0 ? base : `${base}~r${attempt}`);
|
|
563
|
-
if (!row)
|
|
564
|
-
continue;
|
|
565
|
-
if (row.status === "completed")
|
|
566
|
-
return row;
|
|
567
|
-
fallback = row; // remember the highest journaled (non-completed) attempt
|
|
568
|
-
}
|
|
569
|
-
return fallback;
|
|
570
|
-
}
|
|
571
|
-
/**
|
|
572
|
-
* Is a FAILED unit still RETRY-ELIGIBLE — i.e. NOT terminal, because a driver
|
|
573
|
-
* could still re-run it via the `--rerun` form (the engine's automatic
|
|
574
|
-
* `<baseId>~r<n>` retry)? A unit whose declared `retry.on` matches the recorded
|
|
575
|
-
* failure reason AND whose attempt budget (`1 + retry.max`) is not yet spent can
|
|
576
|
-
* still be re-run. No `retry`, an off-list reason, or an exhausted attempt budget
|
|
577
|
-
* ⇒ the failure IS terminal. Shared by the report fail-fast decision, the
|
|
578
|
-
* `--settle` refusal, and `brief`'s fully-terminal detection so all three agree
|
|
579
|
-
* on when a failed unit is genuinely done vs. still re-runnable. The normalized
|
|
580
|
-
* failure reason is compared against `retry.on` directly (a canonical taxonomy
|
|
581
|
-
* reason is stored verbatim; an `external:*` reason is by construction outside
|
|
582
|
-
* the taxonomy `retry.on` lists).
|
|
583
|
-
*/
|
|
584
|
-
export function isRetryEligibleFailure(workUnit, row, failureReason) {
|
|
585
|
-
const retry = workUnit.retry;
|
|
586
|
-
if (!retry || failureReason === null || !retry.on.includes(failureReason))
|
|
587
|
-
return false;
|
|
588
|
-
const attempts = row?.attempts ?? 1;
|
|
589
|
-
return attempts < 1 + Math.max(0, retry.max);
|
|
590
|
-
}
|
|
591
|
-
/**
|
|
592
|
-
* Does a resolvable unit still need a driver to execute + report it (or re-run
|
|
593
|
-
* it)? True for a unit with no terminal row (pending), a still-`running` row (a
|
|
594
|
-
* live/stale claim another driver holds), or a FAILED row that is still
|
|
595
|
-
* retry-eligible. False for a COMPLETED row, a terminal non-retry-eligible
|
|
596
|
-
* FAILURE, or an UNRESOLVABLE unit (the engine's immediate `expression_error` —
|
|
597
|
-
* never reportable). The best terminal attempt (base + `~r<n>` retries) is the
|
|
598
|
-
* one consulted, the SAME reuse the engine and reducer apply.
|
|
599
|
-
*/
|
|
600
|
-
export function unitStillNeedsReport(workUnit, dispatchRows) {
|
|
601
|
-
if (!workUnit.resolved.ok)
|
|
602
|
-
return false;
|
|
603
|
-
const row = selectUnitAttemptRow(workUnit, dispatchRows);
|
|
604
|
-
if (!row)
|
|
605
|
-
return true; // no journal row → pending
|
|
606
|
-
if (row.status === "running")
|
|
607
|
-
return true; // a live/stale claim is still in flight
|
|
608
|
-
if (row.status === "failed")
|
|
609
|
-
return isRetryEligibleFailure(workUnit, row, row.failure_reason);
|
|
610
|
-
return false; // completed (or a non-retry-eligible failure) → terminal
|
|
611
|
-
}
|
|
612
|
-
/**
|
|
613
|
-
* Is the active step's work-list FULLY TERMINAL — every resolvable unit run to a
|
|
614
|
-
* terminal (done, or non-retry-eligible failed) state with nothing left to
|
|
615
|
-
* execute or per-unit report — yet still needing finalization? This is the
|
|
616
|
-
* driver-recovery state after a required-gate block is resumed, or a crash
|
|
617
|
-
* between the last unit write and the step's completion (owner manual-validation
|
|
618
|
-
* finding 3): the work-list is done but the step never advanced. `brief`
|
|
619
|
-
* surfaces it with a single `report --settle` command and `--settle` runs the
|
|
620
|
-
* shared completion path for it. A list with ANY outstanding unit (pending,
|
|
621
|
-
* in-flight, or retry-eligible failed) is NOT fully terminal — the driver
|
|
622
|
-
* `report --unit`s those. A route-only / empty / all-unresolvable list (no
|
|
623
|
-
* resolvable units) is a DIFFERENT non-dispatching state, handled separately.
|
|
624
|
-
*/
|
|
625
|
-
export function isWorkListFullyTerminal(workList, dispatchRows) {
|
|
626
|
-
if (!workList.units.some((u) => u.resolved.ok))
|
|
627
|
-
return false;
|
|
628
|
-
return workList.units.every((u) => !unitStillNeedsReport(u, dispatchRows));
|
|
629
|
-
}
|
|
630
581
|
/** Stable stringify (sorted object keys, recursively) so equal values vote together. */
|
|
631
582
|
export function canonicalJson(value) {
|
|
632
583
|
return JSON.stringify(sortKeys(value));
|
|
@@ -647,9 +598,9 @@ function sortKeys(value) {
|
|
|
647
598
|
// `{ complete: false, missing, feedback }` (see journalGateEvaluationFinish).
|
|
648
599
|
// The feedback stored there is BYTE-IDENTICAL to what the engine threads into
|
|
649
600
|
// the next loop's prompts — both are the same `rejection.feedback`/`.missing`.
|
|
650
|
-
//
|
|
651
|
-
//
|
|
652
|
-
// asserts the round-trip identity.
|
|
601
|
+
// A resume recovers it from the journal so its loop-N work-list (and therefore
|
|
602
|
+
// every unit id and input hash in it) matches the one the original run built.
|
|
603
|
+
// `native-executor.test.ts` asserts the round-trip identity.
|
|
653
604
|
// GATE_EVALUATION_PHASE moved to ../runtime/unit-phases.ts (leaf) so
|
|
654
605
|
// unit-checkin can key on it without closing the exec ↔ runtime cycle.
|
|
655
606
|
/** The unit id of a step's gate-evaluation row for a given 1-based loop. */
|
|
@@ -714,9 +665,10 @@ function gateLoopOf(unitId, stepId) {
|
|
|
714
665
|
/**
|
|
715
666
|
* Classify a gate-evaluation row's journaled verdict, failing LOUDLY on a
|
|
716
667
|
* corrupt one (reviewer #17). A NULL `result_json` is the LEGITIMATE
|
|
717
|
-
*
|
|
718
|
-
*
|
|
719
|
-
* `empty`. But a PRESENT `result_json` that does
|
|
668
|
+
* completion-error / in-flight shape (`journalGateEvaluationFinish` writes null
|
|
669
|
+
* if completion itself throws after judge invocation, and a `running` row has no
|
|
670
|
+
* verdict yet) and classifies as `empty`. But a PRESENT `result_json` that does
|
|
671
|
+
* not parse as JSON, or parses to
|
|
720
672
|
* anything other than an object with a boolean `complete` field, is corruption —
|
|
721
673
|
* a truncated or hand-edited row — and MUST NOT be silently treated as absent
|
|
722
674
|
* (which would reset an active step's gate loop to 1 and re-dispatch work whose
|
|
@@ -764,7 +716,7 @@ export async function journalGateEvaluationStart(gate) {
|
|
|
764
716
|
// Marks the row as a judge call, NOT a dispatch: the budget/lifetime
|
|
765
717
|
// seed in `driveRun` skips these so resume accounting matches live.
|
|
766
718
|
phase: GATE_EVALUATION_PHASE,
|
|
767
|
-
runner:
|
|
719
|
+
runner: gate.runner,
|
|
768
720
|
engine: gate.invocation.engine,
|
|
769
721
|
model: gate.invocation.model,
|
|
770
722
|
inputHash: gate.inputHash,
|
|
@@ -780,17 +732,14 @@ export async function journalGateEvaluationStart(gate) {
|
|
|
780
732
|
* Finish the gate-evaluation unit row with the verdict as observed from the
|
|
781
733
|
* completion outcome: a rejection journals `{ complete: false, missing,
|
|
782
734
|
* feedback }`; a pass journals `{ complete: true, missing: [] }`; a judge that
|
|
783
|
-
* threw
|
|
784
|
-
* failed row with a NULL verdict. A NON-required errored gate then fails OPEN
|
|
785
|
-
* inside `validateStepSummary`; a REQUIRED errored gate BLOCKS the step
|
|
786
|
-
* (`finalizeExecutedStep`, Codex round-3 finding A).
|
|
735
|
+
* threw journals a failed row with the synthesized fail-closed rejection.
|
|
787
736
|
*/
|
|
788
737
|
export async function journalGateEvaluationFinish(gate, errored, rejection) {
|
|
789
738
|
const unitId = gateUnitId(gate.stepId, gate.loop);
|
|
790
|
-
const verdict =
|
|
791
|
-
?
|
|
792
|
-
:
|
|
793
|
-
?
|
|
739
|
+
const verdict = rejection
|
|
740
|
+
? { complete: false, missing: rejection.missing, feedback: rejection.feedback }
|
|
741
|
+
: errored
|
|
742
|
+
? null
|
|
794
743
|
: { complete: true, missing: [] };
|
|
795
744
|
const status = errored ? "failed" : "completed";
|
|
796
745
|
await enqueueUnitWrite(() => withWorkflowRunsRepo((repo) => repo.finishUnit({
|
|
@@ -814,7 +763,7 @@ export async function journalGateEvaluationFinish(gate, errored, rejection) {
|
|
|
814
763
|
* comparison is exact string equality against the declared `when:` matches.
|
|
815
764
|
*/
|
|
816
765
|
export function evaluateRoute(route, scope) {
|
|
817
|
-
const resolved =
|
|
766
|
+
const resolved = resolveReferenceString(route.input, scope);
|
|
818
767
|
if (!resolved.ok) {
|
|
819
768
|
return { ok: false, error: `route input ${route.input} failed to resolve: ${resolved.error.message}` };
|
|
820
769
|
}
|
|
@@ -910,26 +859,6 @@ function assertRouteTargetDeclared(route, stepId, selected, runId) {
|
|
|
910
859
|
`apply a bogus route decision that would skip the real branch targets. Start a new run.`);
|
|
911
860
|
}
|
|
912
861
|
}
|
|
913
|
-
/**
|
|
914
|
-
* Validate every COMPLETED route step's journaled selection against its declared
|
|
915
|
-
* targets (reviewer #7). Read-only: it throws on a PRESENT-but-invalid selection
|
|
916
|
-
* and is silent on an absent one, so it never false-positives on a healthy run —
|
|
917
|
-
* making it safe to call from the read-only `brief` surface as well as the
|
|
918
|
-
* resume/report surfaces that already re-apply the decisions.
|
|
919
|
-
*/
|
|
920
|
-
export function assertJournaledRouteSelectionsValid(plan, state) {
|
|
921
|
-
for (const stepPlan of plan.steps) {
|
|
922
|
-
if (!stepPlan.route)
|
|
923
|
-
continue;
|
|
924
|
-
const stepState = state.workflow.steps.find((s) => s.id === stepPlan.stepId);
|
|
925
|
-
if (!stepState || stepState.status !== "completed")
|
|
926
|
-
continue;
|
|
927
|
-
const selected = journaledRouteSelection(stepState.evidence);
|
|
928
|
-
if (selected !== undefined) {
|
|
929
|
-
assertRouteTargetDeclared(stepPlan.route, stepPlan.stepId, selected, state.run.id);
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
862
|
/**
|
|
934
863
|
* Replay journaled route decisions into the skip bookkeeping (resume path).
|
|
935
864
|
* For every COMPLETED route step of the frozen plan, in spine order: the
|
|
@@ -972,7 +901,7 @@ export function seedJournaledRouteDecisions(plan, state, routeSelected, routeUns
|
|
|
972
901
|
if (selected === undefined) {
|
|
973
902
|
throw new UsageError(`Workflow run ${state.run.id} has a completed route step "${stepPlan.stepId}" with no journaled route ` +
|
|
974
903
|
`decision, and the decision cannot be re-derived from the journaled evidence. Refusing to guess which ` +
|
|
975
|
-
`branch was selected
|
|
904
|
+
`branch was selected. The run journal is inconsistent; abandon this run and start a new one.`);
|
|
976
905
|
}
|
|
977
906
|
applyRouteDecision(stepPlan.route, stepPlan.stepId, selected, routeSelected, routeUnselected);
|
|
978
907
|
}
|
|
@@ -1020,23 +949,6 @@ export async function finalizeExecutedStep(input) {
|
|
|
1020
949
|
// A frozen plan either supplies its judge at the dispatch boundary or has no
|
|
1021
950
|
// judge. Re-selecting defaults here would let config drift change a run.
|
|
1022
951
|
const innerJudge = input.summaryJudge ?? null;
|
|
1023
|
-
// Reviewer #18: a REQUIRED completion gate must actually be judged. When the
|
|
1024
|
-
// gate carries criteria but no judge is available, `validateStepSummary` would
|
|
1025
|
-
// fail OPEN and silently pass the gate — exactly the offline/misconfigured
|
|
1026
|
-
// bypass a required gate exists to prevent. BLOCK the step instead (a human
|
|
1027
|
-
// resolves it via the documented manual path), rather than advance the spine
|
|
1028
|
-
// on an unjudged gate. `gate.required` rides the frozen plan (both surfaces);
|
|
1029
|
-
// `requireGates` is the engine's run-wide `--require-gates` override. Checked
|
|
1030
|
-
// BEFORE route evaluation so a blocked step journals no route decision.
|
|
1031
|
-
const gateRequired = stepPlan.gate.required === true || input.requireGates === true;
|
|
1032
|
-
if (gateRequired && completionCriteria.length > 0 && innerJudge === null) {
|
|
1033
|
-
const notes = `Step "${stepId}" has a REQUIRED completion gate but no summary-validation judge is available ` +
|
|
1034
|
-
`(no LLM is configured, or default LLM resolution failed). A required gate must be judged — refusing to fail ` +
|
|
1035
|
-
`open and silently pass it. The step is BLOCKED: configure an LLM, then \`akm workflow resume ${runId}\` to ` +
|
|
1036
|
-
`re-evaluate the gate, or advance the step manually with \`akm workflow complete\`.`;
|
|
1037
|
-
await completeWorkflowStep({ runId, stepId, status: "blocked", notes, evidence: result.evidence, ...lease });
|
|
1038
|
-
return { kind: "blocked", summary: notes };
|
|
1039
|
-
}
|
|
1040
952
|
// Route evaluation BEFORE completion: an unroutable value is an
|
|
1041
953
|
// authoring/config failure that must fail the step deterministically.
|
|
1042
954
|
let summaryOverride;
|
|
@@ -1065,9 +977,9 @@ export async function finalizeExecutedStep(input) {
|
|
|
1065
977
|
const summary = stepPlan.root && completionCriteria.length > 0
|
|
1066
978
|
? buildArtifactSummary(stepId, result.units, result.evidence)
|
|
1067
979
|
: (summaryOverride ?? result.summary);
|
|
1068
|
-
// Journal engine-driven judge calls as unit rows
|
|
1069
|
-
//
|
|
1070
|
-
//
|
|
980
|
+
// Journal engine-driven judge calls as unit rows. With no criteria there is
|
|
981
|
+
// no judge invocation or row; a criteria-bearing plan without a judge is a
|
|
982
|
+
// configuration error rather than a silent bypass.
|
|
1071
983
|
const frozenGate = innerJudge
|
|
1072
984
|
? await withWorkflowRunsRepo((repo) => {
|
|
1073
985
|
const row = repo.getRunById(runId);
|
|
@@ -1091,6 +1003,7 @@ export async function finalizeExecutedStep(input) {
|
|
|
1091
1003
|
stepId,
|
|
1092
1004
|
loop: gateLoop,
|
|
1093
1005
|
invocation: gateInvocation,
|
|
1006
|
+
runner: frozenGate?.engine?.kind === "agent" ? frozenGate.engine.runnerKind : "llm",
|
|
1094
1007
|
inputHash: createHash("sha256")
|
|
1095
1008
|
.update(canonicalJsonString({
|
|
1096
1009
|
hashVersion: 3,
|
|
@@ -1114,8 +1027,7 @@ export async function finalizeExecutedStep(input) {
|
|
|
1114
1027
|
// Reviewer #6: once the judge is invoked, its gate row is journaled `running`
|
|
1115
1028
|
// (journalGateEvaluationStart) and MUST be finished on every exit. The
|
|
1116
1029
|
// already-fixed window is the judge itself throwing (caught inside
|
|
1117
|
-
// validateStepSummary — `judgeState.errored` records it
|
|
1118
|
-
// fails open, a required gate blocks below). The remaining
|
|
1030
|
+
// validateStepSummary — `judgeState.errored` records it). The remaining
|
|
1119
1031
|
// window is `completeWorkflowStep` throwing AFTER the judge ran — a stolen
|
|
1120
1032
|
// lease, a concurrent state change, a DB error — which would otherwise skip the
|
|
1121
1033
|
// finish and strand the gate row in `running`. Finish it as an errored row (the
|
|
@@ -1129,10 +1041,7 @@ export async function finalizeExecutedStep(input) {
|
|
|
1129
1041
|
summary,
|
|
1130
1042
|
evidence: result.evidence,
|
|
1131
1043
|
summaryJudge,
|
|
1132
|
-
|
|
1133
|
-
// `validateStepSummary` does NOT fail open when the judge throws / is
|
|
1134
|
-
// unreachable / returns garbage — it flags `errored` and we block below.
|
|
1135
|
-
...(gateRequired ? { requireGate: true } : {}),
|
|
1044
|
+
...(input.signal ? { signal: input.signal } : {}),
|
|
1136
1045
|
...lease,
|
|
1137
1046
|
});
|
|
1138
1047
|
}
|
|
@@ -1142,25 +1051,8 @@ export async function finalizeExecutedStep(input) {
|
|
|
1142
1051
|
throw err;
|
|
1143
1052
|
}
|
|
1144
1053
|
const rejection = "ok" in completion && completion.ok === false ? completion : undefined;
|
|
1145
|
-
// A required gate whose judge could not be evaluated is an errored gate, not a
|
|
1146
|
-
// real rejection: journal the gate row as errored (verdict null) so the
|
|
1147
|
-
// observed outcome is honest, driven by EITHER the wrapper catching a throw OR
|
|
1148
|
-
// validateStepSummary flagging an unparseable verdict.
|
|
1149
|
-
const gateErrored = judgeState.errored || rejection?.errored === true;
|
|
1150
1054
|
if (gateUnit) {
|
|
1151
|
-
await journalGateEvaluationFinish(gateUnit,
|
|
1152
|
-
}
|
|
1153
|
-
// Codex round-3 finding A: a REQUIRED gate that could not be judged (the judge
|
|
1154
|
-
// threw, was unreachable, or returned an unparseable verdict) must NOT fail
|
|
1155
|
-
// open and advance. The gate row is journaled errored above; BLOCK the step (a
|
|
1156
|
-
// human resolves it) instead of silently passing an unjudged required gate.
|
|
1157
|
-
if (rejection?.errored) {
|
|
1158
|
-
const notes = `Step "${stepId}" has a REQUIRED completion gate but its summary-validation judge failed to return a verdict ` +
|
|
1159
|
-
`(the LLM threw, was unreachable, or returned an unparseable response). A required gate must be judged — refusing ` +
|
|
1160
|
-
`to fail open and silently pass it. The step is BLOCKED: fix the LLM/connection, then \`akm workflow resume ${runId}\` ` +
|
|
1161
|
-
`to re-evaluate the gate, or advance the step manually with \`akm workflow complete\`.`;
|
|
1162
|
-
await completeWorkflowStep({ runId, stepId, status: "blocked", notes, evidence: result.evidence, ...lease });
|
|
1163
|
-
return { kind: "blocked", summary: notes };
|
|
1055
|
+
await journalGateEvaluationFinish(gateUnit, judgeState.errored, rejection);
|
|
1164
1056
|
}
|
|
1165
1057
|
if (!rejection) {
|
|
1166
1058
|
return { kind: "advanced", ...(summaryOverride !== undefined ? { summaryOverride } : {}) };
|
|
@@ -1173,25 +1065,6 @@ export async function finalizeExecutedStep(input) {
|
|
|
1173
1065
|
gateRejection: { stepId, missing: rejection.missing, feedback: rejection.feedback },
|
|
1174
1066
|
};
|
|
1175
1067
|
}
|
|
1176
|
-
// ── Frozen plan parse + integrity check (shared) ─────────────────────────────
|
|
1177
|
-
/**
|
|
1178
|
-
* Parse and integrity-check a run's frozen plan JSON (migration 006). Shared by
|
|
1179
|
-
* the engine loop's plan loader (`run-workflow.ts`) and the R3 brief/report
|
|
1180
|
-
* surfaces so all three apply the SAME corruption + hash checks — the frozen
|
|
1181
|
-
* plan the engine executes is the exact plan brief describes and report
|
|
1182
|
-
* validates against. A NULL `plan_json` is the CALLER's decision (the engine
|
|
1183
|
-
* warns and compiles from the asset; brief/report error), so this helper only
|
|
1184
|
-
* handles a PRESENT plan string.
|
|
1185
|
-
*/
|
|
1186
|
-
export function parseFrozenPlan(runId, planJson, planHash) {
|
|
1187
|
-
try {
|
|
1188
|
-
return decodeCanonicalPlan(runId, planJson, planHash);
|
|
1189
|
-
}
|
|
1190
|
-
catch (cause) {
|
|
1191
|
-
throw new UsageError(`Workflow run ${runId} has a corrupt frozen plan: ${cause instanceof Error ? cause.message : String(cause)}. ` +
|
|
1192
|
-
`The journaled plan cannot be executed — abandon it and start a new run.`);
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
1068
|
// ── Small helpers ────────────────────────────────────────────────────────────
|
|
1196
1069
|
function safeJson(value) {
|
|
1197
1070
|
try {
|