akm-cli 0.9.0-beta.9 → 0.9.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +715 -0
- package/README.md +12 -4
- package/dist/akm +38 -0
- package/dist/akm-migrate-storage +38 -0
- package/dist/assets/help/help-improve.md +9 -6
- package/dist/assets/hints/cli-hints-full.md +6 -5
- package/dist/assets/profiles/default.json +9 -4
- package/dist/assets/profiles/frequent.json +1 -1
- package/dist/assets/profiles/memory-focus.json +1 -1
- package/dist/assets/profiles/proactive-maintenance.json +25 -0
- package/dist/assets/profiles/quick.json +1 -1
- package/dist/assets/profiles/recombine-only.json +21 -0
- package/dist/assets/profiles/reflect-distill.json +30 -0
- package/dist/assets/profiles/synthesize.json +15 -0
- package/dist/assets/profiles/thorough.json +1 -1
- package/dist/assets/prompts/consolidate-system.md +23 -0
- package/dist/assets/prompts/contradiction-judge.md +33 -0
- package/dist/assets/prompts/distill-knowledge-system.md +22 -0
- package/dist/assets/prompts/distill-lesson-system.md +36 -0
- package/dist/assets/prompts/extract-session.md +11 -3
- package/dist/assets/prompts/graph-extract-system.md +1 -0
- package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
- package/dist/assets/prompts/memory-infer-system.md +1 -0
- package/dist/assets/prompts/memory-infer-user.md +5 -0
- package/dist/assets/prompts/metadata-enhance-system.md +1 -0
- package/dist/assets/prompts/procedural-system.md +44 -0
- package/dist/assets/prompts/recombine-system.md +40 -0
- package/dist/assets/prompts/staleness-detect-system.md +6 -0
- package/dist/assets/prompts/validate-summary-judge.md +1 -0
- package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
- package/dist/assets/templates/html/health.html +281 -111
- package/dist/assets/wiki/ingest-workflow-template.md +45 -16
- package/dist/assets/wiki/schema-template.md +4 -4
- package/dist/cli/clack.js +56 -0
- package/dist/cli/config-migrate.js +7 -1
- package/dist/cli/confirm.js +1 -1
- package/dist/cli/parse-args.js +46 -1
- package/dist/cli/shared.js +28 -0
- package/dist/cli.js +25 -21
- package/dist/commands/agent/agent-dispatch.js +3 -2
- package/dist/commands/agent/agent-support.js +0 -7
- package/dist/commands/agent/contribute-cli.js +26 -7
- package/dist/commands/config-cli.js +26 -13
- package/dist/commands/env/child-env.js +47 -0
- package/dist/commands/env/env-binding.js +95 -0
- package/dist/commands/env/env-cli.js +228 -292
- package/dist/commands/env/env.js +14 -67
- package/dist/commands/env/secret-cli.js +140 -138
- package/dist/commands/feedback-cli.js +156 -155
- package/dist/commands/graph/graph-cli.js +5 -13
- package/dist/commands/graph/graph.js +3 -3
- package/dist/commands/health/advisories.js +151 -0
- package/dist/commands/health/checks.js +103 -16
- package/dist/commands/health/html-report.js +447 -81
- package/dist/commands/health/improve-metrics.js +771 -0
- package/dist/commands/health/llm-usage.js +65 -0
- package/dist/commands/health/md-report.js +103 -0
- package/dist/commands/health/metrics.js +278 -0
- package/dist/commands/health/stash-exposure.js +46 -0
- package/dist/commands/health/surfaces.js +216 -0
- package/dist/commands/health/task-runs.js +135 -0
- package/dist/commands/health/types.js +26 -0
- package/dist/commands/health/windows.js +195 -0
- package/dist/commands/health.js +91 -1091
- package/dist/commands/improve/anti-collapse.js +170 -0
- package/dist/commands/improve/calibration.js +161 -0
- package/dist/commands/improve/collapse-detector.js +421 -0
- package/dist/commands/improve/consolidate/chunking.js +141 -0
- package/dist/commands/improve/consolidate/eligibility.js +64 -0
- package/dist/commands/improve/consolidate/merge.js +145 -0
- package/dist/commands/improve/consolidate/sanitize.js +231 -0
- package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
- package/dist/commands/improve/consolidate.js +1295 -1277
- package/dist/commands/improve/dedup.js +482 -0
- package/dist/commands/improve/distill/content-repair.js +202 -0
- package/dist/commands/improve/distill/promote-memory.js +229 -0
- package/dist/commands/improve/distill/quality-gate.js +236 -0
- package/dist/commands/improve/distill-guards.js +127 -0
- package/dist/commands/improve/distill-promotion-policy.js +826 -167
- package/dist/commands/improve/distill.js +228 -605
- package/dist/commands/improve/eligibility.js +434 -0
- package/dist/commands/improve/encoding-salience.js +205 -0
- package/dist/commands/improve/extract-cli.js +179 -59
- package/dist/commands/improve/extract-prompt.js +54 -3
- package/dist/commands/improve/extract-watch.js +140 -0
- package/dist/commands/improve/extract.js +409 -43
- package/dist/commands/improve/feedback-valence.js +54 -0
- package/dist/commands/improve/hot-probation.js +45 -0
- package/dist/commands/improve/improve-auto-accept.js +157 -10
- package/dist/commands/improve/improve-cli.js +115 -73
- package/dist/commands/improve/improve-profiles.js +28 -8
- package/dist/commands/improve/improve-result-file.js +15 -25
- package/dist/commands/improve/improve-session.js +58 -0
- package/dist/commands/improve/improve.js +485 -2764
- package/dist/commands/improve/locks.js +154 -0
- package/dist/commands/improve/loop-stages.js +1100 -0
- package/dist/commands/improve/memory/memory-belief.js +14 -15
- package/dist/commands/improve/memory/memory-contradiction-detect.js +83 -60
- package/dist/commands/improve/memory/memory-improve.js +27 -27
- package/dist/commands/improve/outcome-loop.js +270 -0
- package/dist/commands/improve/preparation.js +2002 -0
- package/dist/commands/improve/proactive-maintenance.js +37 -35
- package/dist/commands/improve/procedural.js +398 -0
- package/dist/commands/improve/recombine.js +818 -0
- package/dist/commands/improve/reflect-noise.js +0 -0
- package/dist/commands/improve/reflect.js +206 -45
- package/dist/commands/improve/salience.js +455 -0
- package/dist/commands/improve/schema-similarity-gate.js +168 -0
- package/dist/commands/improve/shared.js +51 -0
- package/dist/commands/improve/triage.js +93 -0
- package/dist/commands/lint/agent-linter.js +19 -24
- package/dist/commands/lint/base-linter.js +173 -60
- package/dist/commands/lint/command-linter.js +19 -24
- package/dist/commands/lint/env-key-rules.js +38 -1
- package/dist/commands/lint/fact-linter.js +39 -0
- package/dist/commands/lint/index.js +31 -13
- package/dist/commands/lint/memory-linter.js +1 -1
- package/dist/commands/lint/registry.js +7 -2
- package/dist/commands/lint/task-linter.js +3 -3
- package/dist/commands/lint/workflow-linter.js +26 -1
- package/dist/commands/observability-cli.js +4 -4
- package/dist/commands/proposal/drain-policies.js +13 -4
- package/dist/commands/proposal/drain.js +45 -51
- package/dist/commands/proposal/legacy-import.js +115 -0
- package/dist/commands/proposal/proposal-cli.js +24 -34
- package/dist/commands/proposal/proposal.js +2 -1
- package/dist/commands/proposal/propose.js +8 -3
- package/dist/commands/proposal/repository.js +829 -0
- package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
- package/dist/commands/proposal/validators/proposals.js +93 -895
- package/dist/commands/read/curate.js +410 -111
- package/dist/commands/read/knowledge.js +10 -3
- package/dist/commands/read/remember-cli.js +133 -138
- package/dist/commands/read/search-cli.js +15 -8
- package/dist/commands/read/search.js +22 -11
- package/dist/commands/read/show.js +106 -14
- package/dist/commands/registry-cli.js +76 -87
- package/dist/commands/remember.js +11 -12
- package/dist/commands/sources/add-cli.js +91 -95
- package/dist/commands/sources/history.js +1 -1
- package/dist/commands/sources/init.js +66 -18
- package/dist/commands/sources/installed-stashes.js +11 -3
- package/dist/commands/sources/migration-help.js +7 -4
- package/dist/commands/sources/schema-repair.js +44 -46
- package/dist/commands/sources/self-update.js +2 -2
- package/dist/commands/sources/source-add.js +7 -3
- package/dist/commands/sources/sources-cli.js +3 -3
- package/dist/commands/sources/stash-cli.js +19 -39
- package/dist/commands/sources/stash-skeleton.js +57 -8
- package/dist/commands/tasks/default-tasks.js +15 -2
- package/dist/commands/tasks/tasks-cli.js +20 -29
- package/dist/commands/tasks/tasks.js +39 -11
- package/dist/commands/wiki-cli.js +23 -38
- package/dist/commands/workflow-cli.js +291 -13
- package/dist/core/asset/asset-registry.js +3 -1
- package/dist/core/asset/asset-spec.js +79 -5
- package/dist/core/asset/frontmatter.js +188 -167
- package/dist/core/asset/markdown.js +8 -0
- package/dist/core/authoring-rules.js +92 -0
- package/dist/core/common.js +4 -23
- package/dist/core/concurrent.js +10 -1
- package/dist/core/config/config-io.js +10 -1
- package/dist/core/config/config-migration.js +18 -40
- package/dist/core/config/config-schema.js +403 -62
- package/dist/core/config/config-types.js +3 -3
- package/dist/core/config/config.js +67 -22
- package/dist/core/deep-merge.js +38 -0
- package/dist/core/errors.js +1 -0
- package/dist/core/eval/rank-metrics.js +113 -0
- package/dist/core/events.js +4 -7
- package/dist/core/improve-types.js +47 -8
- package/dist/core/json-schema.js +142 -0
- package/dist/core/logs-db.js +14 -75
- package/dist/core/parse.js +36 -16
- package/dist/core/paths.js +18 -18
- package/dist/core/standards/resolve-standards-context.js +87 -0
- package/dist/core/standards/resolve-stash-standards.js +99 -0
- package/dist/core/standards/resolve-type-conventions.js +66 -0
- package/dist/core/state/migrations.js +770 -0
- package/dist/core/state-db.js +132 -1126
- package/dist/core/structured.js +69 -0
- package/dist/core/time.js +53 -0
- package/dist/core/warn.js +21 -0
- package/dist/core/write-source.js +37 -0
- package/dist/indexer/db/db.js +261 -770
- package/dist/indexer/db/entry-mapper.js +41 -0
- package/dist/indexer/db/graph-db.js +129 -86
- package/dist/indexer/db/llm-cache.js +2 -2
- package/dist/indexer/db/schema.js +516 -0
- package/dist/indexer/ensure-index.js +36 -92
- package/dist/indexer/feedback/utility-policy.js +75 -0
- package/dist/indexer/graph/graph-boost.js +51 -41
- package/dist/indexer/graph/graph-extraction.js +207 -4
- package/dist/indexer/index-writer-lock.js +18 -11
- package/dist/indexer/index-written-assets.js +105 -0
- package/dist/indexer/indexer.js +182 -204
- package/dist/indexer/passes/dir-staleness.js +114 -0
- package/dist/indexer/passes/memory-inference.js +13 -5
- package/dist/indexer/passes/metadata.js +20 -0
- package/dist/indexer/read-preflight.js +23 -0
- package/dist/indexer/search/db-search.js +89 -13
- package/dist/indexer/search/fts-query.js +51 -0
- package/dist/indexer/search/ranking-contributors.js +95 -9
- package/dist/indexer/search/ranking.js +79 -3
- package/dist/indexer/search/search-fields.js +6 -0
- package/dist/indexer/search/search-source.js +32 -21
- package/dist/indexer/search/semantic-status.js +4 -0
- package/dist/indexer/walk/matchers.js +48 -0
- package/dist/indexer/walk/walker.js +21 -13
- package/dist/integrations/agent/builders.js +41 -13
- package/dist/integrations/agent/config.js +20 -59
- package/dist/integrations/agent/detect.js +9 -0
- package/dist/integrations/agent/index.js +3 -19
- package/dist/integrations/agent/model-aliases.js +16 -2
- package/dist/integrations/agent/profiles.js +79 -6
- package/dist/integrations/agent/prompts.js +75 -9
- package/dist/integrations/agent/runner-dispatch.js +83 -0
- package/dist/integrations/agent/runner.js +13 -9
- package/dist/integrations/agent/spawn.js +206 -81
- package/dist/integrations/harnesses/aider/agent-builder.js +113 -0
- package/dist/integrations/harnesses/aider/index.js +58 -0
- package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
- package/dist/integrations/harnesses/amazonq/agent-builder.js +153 -0
- package/dist/integrations/harnesses/amazonq/index.js +59 -0
- package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
- package/dist/integrations/harnesses/claude/agent-builder.js +46 -7
- package/dist/integrations/harnesses/claude/index.js +27 -23
- package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
- package/dist/integrations/harnesses/claude/session-log.js +10 -0
- package/dist/integrations/harnesses/codex/agent-builder.js +137 -0
- package/dist/integrations/harnesses/codex/index.js +63 -0
- package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
- package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
- package/dist/integrations/harnesses/copilot/index.js +60 -0
- package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
- package/dist/integrations/harnesses/gemini/agent-builder.js +121 -0
- package/dist/integrations/harnesses/gemini/index.js +60 -0
- package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
- package/dist/integrations/harnesses/index.js +28 -7
- package/dist/integrations/harnesses/opencode/agent-builder.js +1 -1
- package/dist/integrations/harnesses/opencode/index.js +17 -16
- package/dist/integrations/harnesses/opencode/session-log.js +173 -3
- package/dist/integrations/harnesses/opencode-sdk/harness.js +65 -0
- package/dist/integrations/harnesses/opencode-sdk/index.js +10 -34
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +642 -71
- package/dist/integrations/harnesses/openhands/agent-builder.js +126 -0
- package/dist/integrations/harnesses/openhands/index.js +58 -0
- package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
- package/dist/integrations/harnesses/pi/agent-builder.js +104 -0
- package/dist/integrations/harnesses/pi/index.js +58 -0
- package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
- package/dist/integrations/harnesses/types.js +8 -0
- package/dist/integrations/session-logs/index.js +40 -11
- package/dist/llm/call-ai.js +2 -2
- package/dist/llm/client.js +34 -11
- package/dist/llm/embedder.js +67 -4
- package/dist/llm/embedders/cache.js +3 -1
- package/dist/llm/embedders/deterministic.js +66 -0
- package/dist/llm/embedders/local.js +73 -3
- package/dist/llm/feature-gate.js +16 -15
- package/dist/llm/graph-extract.js +67 -44
- package/dist/llm/memory-infer-impl.js +138 -0
- package/dist/llm/memory-infer.js +1 -127
- package/dist/llm/metadata-enhance.js +44 -31
- package/dist/llm/structured-call.js +49 -0
- package/dist/migrate-storage-node.mjs +8 -0
- package/dist/output/context.js +5 -5
- package/dist/output/renderers.js +87 -15
- package/dist/output/shapes/curate.js +14 -2
- package/dist/output/shapes/helpers.js +0 -3
- package/dist/output/shapes/passthrough.js +6 -1
- package/dist/output/text/helpers.js +241 -2
- package/dist/output/text/workflow.js +4 -1
- package/dist/registry/providers/skills-sh.js +21 -147
- package/dist/registry/providers/static-index.js +15 -157
- package/dist/registry/resolve.js +27 -9
- package/dist/runtime.js +25 -1
- package/dist/schemas/akm-config.json +14225 -0
- package/dist/schemas/akm-workflow.json +328 -0
- package/dist/scripts/migrate-storage.js +2743 -8390
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +1652 -607
- package/dist/setup/detect.js +9 -0
- package/dist/setup/legacy-config.js +106 -0
- package/dist/setup/prompt.js +57 -0
- package/dist/setup/providers.js +14 -0
- package/dist/setup/registry-stash-loader.js +12 -0
- package/dist/setup/semantic-assets.js +124 -0
- package/dist/setup/setup.js +52 -1614
- package/dist/setup/steps/connection.js +734 -0
- package/dist/setup/steps/output.js +31 -0
- package/dist/setup/steps/platforms.js +124 -0
- package/dist/setup/steps/semantic.js +27 -0
- package/dist/setup/steps/sources.js +222 -0
- package/dist/setup/steps/stashdir.js +42 -0
- package/dist/setup/steps/tasks.js +152 -0
- package/dist/sources/include.js +6 -2
- package/dist/sources/providers/filesystem.js +0 -1
- package/dist/sources/providers/git-install.js +210 -0
- package/dist/sources/providers/git-provider.js +234 -0
- package/dist/sources/providers/git-stash.js +248 -0
- package/dist/sources/providers/git.js +10 -661
- package/dist/sources/providers/npm.js +2 -6
- package/dist/sources/providers/provider-utils.js +13 -7
- package/dist/sources/providers/sync-from-ref.js +9 -1
- package/dist/sources/providers/website.js +9 -5
- package/dist/sources/website-ingest.js +187 -29
- package/dist/sources/wiki-fetchers/registry.js +53 -0
- package/dist/sources/wiki-fetchers/youtube.js +239 -0
- package/dist/storage/database.js +45 -10
- package/dist/storage/managed-db.js +82 -0
- package/dist/storage/repositories/canaries-repository.js +107 -0
- package/dist/storage/repositories/consolidation-repository.js +38 -0
- package/dist/storage/repositories/embeddings-repository.js +72 -0
- package/dist/storage/repositories/events-repository.js +187 -0
- package/dist/storage/repositories/extract-sessions-repository.js +96 -0
- package/dist/storage/repositories/improve-runs-repository.js +146 -0
- package/dist/storage/repositories/index-db.js +14 -8
- package/dist/storage/repositories/proposals-repository.js +220 -0
- package/dist/storage/repositories/recombine-repository.js +213 -0
- package/dist/storage/repositories/registry-cache.js +93 -0
- package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
- package/dist/storage/repositories/task-history-repository.js +93 -0
- package/dist/storage/repositories/workflow-runs-repository.js +189 -1
- package/dist/storage/sqlite-pragmas.js +146 -0
- package/dist/tasks/backends/cron.js +1 -1
- package/dist/tasks/backends/index.js +9 -0
- package/dist/tasks/backends/launchd.js +1 -1
- package/dist/tasks/backends/schtasks.js +1 -1
- package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
- package/dist/tasks/runner.js +15 -13
- package/dist/text-import-hook.mjs +1 -1
- package/dist/wiki/wiki.js +52 -11
- package/dist/workflows/authoring/authoring.js +123 -10
- package/dist/workflows/authoring/workflow-program-template.yaml +31 -0
- package/dist/workflows/cli.js +5 -0
- package/dist/workflows/db.js +138 -4
- package/dist/workflows/exec/brief.js +484 -0
- package/dist/workflows/exec/native-executor.js +975 -0
- package/dist/workflows/exec/param-secrets.js +115 -0
- package/dist/workflows/exec/report.js +1295 -0
- package/dist/workflows/exec/run-workflow.js +596 -0
- package/dist/workflows/exec/scheduler.js +100 -0
- package/dist/workflows/exec/step-work.js +1156 -0
- package/dist/workflows/exec/unit-writer.js +23 -0
- package/dist/workflows/exec/watch.js +116 -0
- package/dist/workflows/exec/worktree.js +171 -0
- package/dist/workflows/ir/compile.js +388 -0
- package/dist/workflows/ir/params.js +54 -0
- package/dist/workflows/ir/plan-hash.js +33 -0
- package/dist/workflows/ir/schema.js +4 -0
- package/dist/workflows/parser.js +3 -1
- package/dist/workflows/program/expressions.js +369 -0
- package/dist/workflows/program/parser.js +760 -0
- package/dist/workflows/program/project.js +105 -0
- package/dist/workflows/program/schema.js +54 -0
- package/dist/workflows/renderer.js +82 -5
- package/dist/workflows/runtime/agent-identity.js +59 -14
- package/dist/workflows/runtime/runs.js +248 -153
- package/dist/workflows/runtime/unit-checkin.js +45 -0
- package/dist/workflows/runtime/workflow-asset-loader.js +188 -0
- package/dist/workflows/validate-summary.js +26 -10
- package/dist/workflows/validator.js +1 -1
- package/docs/README.md +69 -18
- package/docs/data-and-telemetry.md +7 -5
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.9.0-beta.60.md +19 -0
- package/docs/migration/release-notes/0.9.0.md +39 -0
- package/package.json +10 -10
- package/dist/assets/tasks/core/update-stashes.yml +0 -4
- package/dist/commands/db-cli.js +0 -23
- package/dist/indexer/db/db-backup.js +0 -376
- package/dist/indexer/passes/staleness-detect.js +0 -488
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import fs from "node:fs";
|
|
5
|
-
import {
|
|
6
|
-
import { output, parseAllFlagValues, runWithJsonErrors } from "../cli/shared.js";
|
|
5
|
+
import { defineJsonCommand, output, parseAllFlagValues } from "../cli/shared.js";
|
|
7
6
|
import { parseAssetRef } from "../core/asset/asset-ref.js";
|
|
8
7
|
import { assembleAsset } from "../core/asset/asset-serialize.js";
|
|
9
8
|
import { parseFrontmatter, parseFrontmatterBlock } from "../core/asset/frontmatter.js";
|
|
@@ -11,11 +10,9 @@ import { writeFileAtomic } from "../core/common.js";
|
|
|
11
10
|
import { FEEDBACK_FAILURE_MODES, loadConfig } from "../core/config/config.js";
|
|
12
11
|
import { UsageError } from "../core/errors.js";
|
|
13
12
|
import { appendEvent } from "../core/events.js";
|
|
13
|
+
import { getDbPath } from "../core/paths.js";
|
|
14
14
|
import { warn } from "../core/warn.js";
|
|
15
15
|
import { applyFeedbackToUtilityScore, closeDatabase, findEntryIdByRef, getEntryFilePathById, openExistingDatabase, } from "../indexer/db/db.js";
|
|
16
|
-
import { ensureIndex } from "../indexer/ensure-index.js";
|
|
17
|
-
import { withIndexWriterLease } from "../indexer/index-writer-lock.js";
|
|
18
|
-
import { resolveSourceEntries } from "../indexer/search/search-source.js";
|
|
19
16
|
import { countFeedbackSignals, insertUsageEvent } from "../indexer/usage/usage-events.js";
|
|
20
17
|
// ── Tag validation ────────────────────────────────────────────────────────────
|
|
21
18
|
const TAG_KEY_RE = /^[a-z_][a-z0-9_]*$/;
|
|
@@ -110,7 +107,7 @@ function appendLessonStrength(type, name, feedbackRef) {
|
|
|
110
107
|
return { strength: strengthList.length };
|
|
111
108
|
}
|
|
112
109
|
// ── Command definition ────────────────────────────────────────────────────────
|
|
113
|
-
export const feedbackCommand =
|
|
110
|
+
export const feedbackCommand = defineJsonCommand({
|
|
114
111
|
meta: {
|
|
115
112
|
name: "feedback",
|
|
116
113
|
description: "Record positive or negative feedback for any indexed stash asset.\n\n" +
|
|
@@ -154,167 +151,171 @@ export const feedbackCommand = defineCommand({
|
|
|
154
151
|
"`lessonStrength[]` frontmatter array (dedup, idempotent). Ignored on non-lesson targets.",
|
|
155
152
|
},
|
|
156
153
|
},
|
|
157
|
-
run({ args }) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
154
|
+
async run({ args }) {
|
|
155
|
+
const ref = (args.ref ?? "").trim();
|
|
156
|
+
if (!ref) {
|
|
157
|
+
throw new UsageError("Asset ref is required. Usage: akm feedback <ref> --positive|--negative", "MISSING_REQUIRED_ARGUMENT", "Pass a ref like `skill:deploy` and either --positive or --negative.");
|
|
158
|
+
}
|
|
159
|
+
parseAssetRef(ref);
|
|
160
|
+
if (args.positive && args.negative) {
|
|
161
|
+
throw new UsageError("Specify either --positive or --negative, not both.");
|
|
162
|
+
}
|
|
163
|
+
if (!args.positive && !args.negative) {
|
|
164
|
+
throw new UsageError("Specify --positive or --negative.");
|
|
165
|
+
}
|
|
166
|
+
const signal = args.positive ? "positive" : "negative";
|
|
167
|
+
const reason = args.reason;
|
|
168
|
+
// F-3 / #384: Validate --failure-mode against the curated enum.
|
|
169
|
+
const failureMode = args["failure-mode"]?.trim() || undefined;
|
|
170
|
+
if (failureMode) {
|
|
171
|
+
if (args.positive) {
|
|
172
|
+
throw new UsageError("--failure-mode is only valid for negative feedback.", "INVALID_FLAG_VALUE", "Remove --failure-mode or switch to --negative.");
|
|
162
173
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
174
|
+
const cfg = loadConfig();
|
|
175
|
+
const allowedModes = cfg.feedback?.allowedFailureModes ?? FEEDBACK_FAILURE_MODES;
|
|
176
|
+
if (allowedModes.length > 0 && !allowedModes.includes(failureMode)) {
|
|
177
|
+
throw new UsageError(`Invalid --failure-mode "${failureMode}". Accepted values: ${allowedModes.join(", ")}.`, "INVALID_FLAG_VALUE", `Use one of: ${allowedModes.join(", ")}`);
|
|
166
178
|
}
|
|
167
|
-
|
|
168
|
-
|
|
179
|
+
}
|
|
180
|
+
if (args.negative === true && !reason?.trim()) {
|
|
181
|
+
// F-3 / #384: Default requireReason is now true. Load config to allow
|
|
182
|
+
// operators to opt out via feedback.requireReason: false in akm.json.
|
|
183
|
+
const cfg = loadConfig();
|
|
184
|
+
const requireReason = cfg.feedback?.requireReason ?? true; // Default: true (F-3 / #384)
|
|
185
|
+
if (requireReason) {
|
|
186
|
+
throw new UsageError("Negative feedback requires --reason (structured failure signals are needed for distillation). " +
|
|
187
|
+
"Use --failure-mode for a curated taxonomy or --reason for free text. " +
|
|
188
|
+
"Set feedback.requireReason: false in akm.json to downgrade to a warning.", "MISSING_REQUIRED_ARGUMENT", `Hint: akm feedback ${ref} --negative --reason "..." [--failure-mode incorrect|outdated|dangerous|incomplete|redundant]`);
|
|
169
189
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
// F-3 / #384: Validate --failure-mode against the curated enum.
|
|
173
|
-
const failureMode = args["failure-mode"]?.trim() || undefined;
|
|
174
|
-
if (failureMode) {
|
|
175
|
-
if (args.positive) {
|
|
176
|
-
throw new UsageError("--failure-mode is only valid for negative feedback.", "INVALID_FLAG_VALUE", "Remove --failure-mode or switch to --negative.");
|
|
177
|
-
}
|
|
178
|
-
const cfg = loadConfig();
|
|
179
|
-
const allowedModes = cfg.feedback?.allowedFailureModes ?? FEEDBACK_FAILURE_MODES;
|
|
180
|
-
if (allowedModes.length > 0 && !allowedModes.includes(failureMode)) {
|
|
181
|
-
throw new UsageError(`Invalid --failure-mode "${failureMode}". Accepted values: ${allowedModes.join(", ")}.`, "INVALID_FLAG_VALUE", `Use one of: ${allowedModes.join(", ")}`);
|
|
182
|
-
}
|
|
190
|
+
else {
|
|
191
|
+
warn("Warning: negative feedback without --reason provides less distillation signal.");
|
|
183
192
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
193
|
+
}
|
|
194
|
+
const rawTags = parseAllFlagValues("--tag");
|
|
195
|
+
const validatedTags = validateFeedbackTags(rawTags);
|
|
196
|
+
const metadataObj = {
|
|
197
|
+
signal,
|
|
198
|
+
...(reason?.trim() ? { reason: reason.trim() } : {}),
|
|
199
|
+
...(failureMode ? { failureMode } : {}),
|
|
200
|
+
...(validatedTags.length > 0 ? { tags: validatedTags } : {}),
|
|
201
|
+
};
|
|
202
|
+
const metadataStr = Object.keys(metadataObj).length > 1 ? JSON.stringify(metadataObj) : undefined;
|
|
203
|
+
// Feedback only needs the index to exist, not to be current. A stale index
|
|
204
|
+
// is fine — the ref lookup works against any populated DB. We do NOT call
|
|
205
|
+
// ensureIndex here: it either blocks (3+ min inline reindex) or spawns a
|
|
206
|
+
// background process that holds the writer lock, causing the feedback write
|
|
207
|
+
// to spin-wait for the full reindex duration. If the DB is absent we give a
|
|
208
|
+
// clear error below rather than silently triggering a rebuild.
|
|
209
|
+
if (!fs.existsSync(getDbPath())) {
|
|
210
|
+
throw new UsageError("Index not found. Run 'akm index' first to build the index before recording feedback.", "MISSING_REQUIRED_ARGUMENT", "akm index");
|
|
211
|
+
}
|
|
212
|
+
// Feedback writes exactly 2 rows (usage_events + utility_score). SQLite
|
|
213
|
+
// WAL mode + busy_timeout=30s handles concurrent access with an ongoing
|
|
214
|
+
// `akm improve` run without needing the application-level writer lock.
|
|
215
|
+
// The lock was originally needed to prevent feedback from racing a
|
|
216
|
+
// background reindex it spawned — now that ensureIndex is removed, holding
|
|
217
|
+
// the lock only causes feedback to block for the full improve run duration.
|
|
218
|
+
let utilityResult;
|
|
219
|
+
const db = openExistingDatabase();
|
|
220
|
+
try {
|
|
221
|
+
const entryId = findEntryIdByRef(db, ref);
|
|
222
|
+
if (entryId === undefined) {
|
|
223
|
+
throw new UsageError(`Ref "${ref}" is not in the index. ` +
|
|
224
|
+
"Run 'akm search' to verify the asset exists, then 'akm index' if it was recently added.");
|
|
197
225
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
226
|
+
// Persist the feedback signal into usage_events. For positive signals,
|
|
227
|
+
// the EMA utility score is updated immediately on the next read path.
|
|
228
|
+
// For negative signals, the score is adjusted the next time `akm index`
|
|
229
|
+
// runs — the signal is durable in the DB but does NOT suppress ranking
|
|
230
|
+
// in search results until after reindexing.
|
|
231
|
+
insertUsageEvent(db, {
|
|
232
|
+
event_type: "feedback",
|
|
233
|
+
entry_ref: ref,
|
|
234
|
+
entry_id: entryId,
|
|
201
235
|
signal,
|
|
202
|
-
|
|
203
|
-
...(failureMode ? { failureMode } : {}),
|
|
204
|
-
...(validatedTags.length > 0 ? { tags: validatedTags } : {}),
|
|
205
|
-
};
|
|
206
|
-
const metadataStr = Object.keys(metadataObj).length > 1 ? JSON.stringify(metadataObj) : undefined;
|
|
207
|
-
const utilityResult = await withIndexWriterLease({ purpose: "feedback-write" }, async () => {
|
|
208
|
-
// Feedback is itself an index.db writer, so it must not spawn a detached
|
|
209
|
-
// reindex and then compete with it for the same database file.
|
|
210
|
-
const sources = resolveSourceEntries();
|
|
211
|
-
if (sources.length > 0) {
|
|
212
|
-
await ensureIndex(sources[0].path, { mode: "blocking" });
|
|
213
|
-
}
|
|
214
|
-
let scopedUtilityResult;
|
|
215
|
-
const db = openExistingDatabase();
|
|
216
|
-
try {
|
|
217
|
-
const entryId = findEntryIdByRef(db, ref);
|
|
218
|
-
if (entryId === undefined) {
|
|
219
|
-
throw new UsageError(`Ref "${ref}" is not in the index. ` +
|
|
220
|
-
"Run 'akm search' to verify the asset exists, then 'akm index' if it was recently added.");
|
|
221
|
-
}
|
|
222
|
-
// Persist the feedback signal into usage_events. For positive signals,
|
|
223
|
-
// the EMA utility score is updated immediately on the next read path.
|
|
224
|
-
// For negative signals, the score is adjusted the next time `akm index`
|
|
225
|
-
// runs — the signal is durable in the DB but does NOT suppress ranking
|
|
226
|
-
// in search results until after reindexing.
|
|
227
|
-
insertUsageEvent(db, {
|
|
228
|
-
event_type: "feedback",
|
|
229
|
-
entry_ref: ref,
|
|
230
|
-
entry_id: entryId,
|
|
231
|
-
signal,
|
|
232
|
-
metadata: metadataStr,
|
|
233
|
-
});
|
|
234
|
-
// Apply feedback-derived utility score adjustment immediately so that
|
|
235
|
-
// positive/negative signals influence search ranking without requiring
|
|
236
|
-
// a full reindex. We query the total accumulated feedback counts from
|
|
237
|
-
// usage_events so the delta reflects the entire signal history.
|
|
238
|
-
// Uses MemRL bounded-step EMA (F-5 / #386, arXiv:2601.03192).
|
|
239
|
-
try {
|
|
240
|
-
const { pos, neg } = countFeedbackSignals(db, entryId);
|
|
241
|
-
scopedUtilityResult = applyFeedbackToUtilityScore(db, entryId, pos, neg);
|
|
242
|
-
}
|
|
243
|
-
catch {
|
|
244
|
-
// best-effort — feedback recording succeeds even if utility update fails
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
finally {
|
|
248
|
-
closeDatabase(db);
|
|
249
|
-
}
|
|
250
|
-
return scopedUtilityResult;
|
|
236
|
+
metadata: metadataStr,
|
|
251
237
|
});
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
238
|
+
// Apply feedback-derived utility score adjustment immediately so that
|
|
239
|
+
// positive/negative signals influence search ranking without requiring
|
|
240
|
+
// a full reindex. We query the total accumulated feedback counts from
|
|
241
|
+
// usage_events so the delta reflects the entire signal history.
|
|
242
|
+
// Uses MemRL bounded-step EMA (F-5 / #386, arXiv:2601.03192).
|
|
243
|
+
try {
|
|
244
|
+
const { pos, neg } = countFeedbackSignals(db, entryId);
|
|
245
|
+
utilityResult = applyFeedbackToUtilityScore(db, entryId, pos, neg);
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
// best-effort — feedback recording succeeds even if utility update fails
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
finally {
|
|
252
|
+
closeDatabase(db);
|
|
253
|
+
}
|
|
254
|
+
appendEvent({
|
|
255
|
+
eventType: "feedback",
|
|
256
|
+
ref,
|
|
257
|
+
metadata: metadataObj,
|
|
258
|
+
});
|
|
259
|
+
// F-5 / #386: When a high-utility asset crosses below the review threshold,
|
|
260
|
+
// auto-create a review-needed escalation proposal so a human can confirm
|
|
261
|
+
// whether the negative feedback is valid before the asset falls out of
|
|
262
|
+
// the improve loop. Best-effort — failure is logged but does not fail the
|
|
263
|
+
// feedback command.
|
|
264
|
+
// Emit a structured event rather than a proposal so the review-needed
|
|
265
|
+
// signal is queryable via `akm events list --type improve_review_needed`
|
|
266
|
+
// without risking accidental asset overwrite if the proposal is accepted.
|
|
267
|
+
if (utilityResult?.crossedReviewThreshold) {
|
|
268
|
+
try {
|
|
269
|
+
appendEvent({
|
|
270
|
+
eventType: "improve_review_needed",
|
|
271
|
+
ref,
|
|
272
|
+
metadata: {
|
|
273
|
+
previousUtility: utilityResult.previousUtility,
|
|
274
|
+
nextUtility: utilityResult.nextUtility,
|
|
275
|
+
reason: reason?.trim() ?? null,
|
|
276
|
+
failureMode: failureMode ?? null,
|
|
277
|
+
},
|
|
278
|
+
});
|
|
281
279
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
280
|
+
catch (escalationErr) {
|
|
281
|
+
warn(`[feedback] Could not emit review-needed event for ${ref}: ${escalationErr instanceof Error ? escalationErr.message : String(escalationErr)}`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// Phase 7A / Advantage D4b: --applied-to credits a lesson. When the
|
|
285
|
+
// target is a `lesson:<name>` ref and the signal is positive, append
|
|
286
|
+
// the feedback ref to the target lesson's `lessonStrength[]`
|
|
287
|
+
// frontmatter array (dedup, idempotent). Non-lesson targets are
|
|
288
|
+
// ignored. Failures here are warnings — feedback recording is the
|
|
289
|
+
// primary contract and must not regress on lesson-write errors.
|
|
290
|
+
const appliedToRaw = args["applied-to"]?.trim();
|
|
291
|
+
let appliedToResult = null;
|
|
292
|
+
if (appliedToRaw && signal === "positive") {
|
|
293
|
+
try {
|
|
294
|
+
const parsedApplied = parseAssetRef(appliedToRaw);
|
|
295
|
+
if (parsedApplied.type === "lesson") {
|
|
296
|
+
const updated = appendLessonStrength(parsedApplied.type, parsedApplied.name, ref);
|
|
297
|
+
if (updated) {
|
|
298
|
+
appliedToResult = { lessonRef: appliedToRaw, strength: updated.strength };
|
|
298
299
|
}
|
|
299
300
|
}
|
|
300
|
-
catch (err) {
|
|
301
|
-
warn(`[feedback] --applied-to failed for ${appliedToRaw}: ${err instanceof Error ? err.message : String(err)}`);
|
|
302
|
-
}
|
|
303
301
|
}
|
|
304
|
-
|
|
305
|
-
warn(
|
|
302
|
+
catch (err) {
|
|
303
|
+
warn(`[feedback] --applied-to failed for ${appliedToRaw}: ${err instanceof Error ? err.message : String(err)}`);
|
|
306
304
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
305
|
+
}
|
|
306
|
+
else if (appliedToRaw && signal !== "positive") {
|
|
307
|
+
warn("[feedback] --applied-to is ignored without --positive; lesson credit is only recorded on positive signals.");
|
|
308
|
+
}
|
|
309
|
+
output("feedback", {
|
|
310
|
+
ok: true,
|
|
311
|
+
ref,
|
|
312
|
+
signal,
|
|
313
|
+
reason: reason?.trim() ?? null,
|
|
314
|
+
failureMode: failureMode ?? null,
|
|
315
|
+
tags: validatedTags,
|
|
316
|
+
...(appliedToResult
|
|
317
|
+
? { appliedTo: { ref: appliedToResult.lessonRef, lessonStrength: appliedToResult.strength } }
|
|
318
|
+
: {}),
|
|
318
319
|
});
|
|
319
320
|
},
|
|
320
321
|
});
|
|
@@ -9,12 +9,9 @@
|
|
|
9
9
|
* same JSON envelope (stdout/stderr/exit-code) as the inline `runWithJsonErrors`
|
|
10
10
|
* form it replaces.
|
|
11
11
|
*/
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { defineJsonCommand, output, runWithJsonErrors } from "../../cli/shared.js";
|
|
12
|
+
import { parsePositiveIntFlag } from "../../cli/parse-args.js";
|
|
13
|
+
import { defineGroupCommand, defineJsonCommand, output } from "../../cli/shared.js";
|
|
15
14
|
import { akmGraphEntities, akmGraphEntity, akmGraphExport, akmGraphOrphans, akmGraphRelated, akmGraphRelations, akmGraphSummary, akmGraphUpdate, } from "./graph.js";
|
|
16
|
-
// Single source of truth: the routing set is derived from the subCommands keys
|
|
17
|
-
// (M10) so adding a subcommand can never silently desync from `hasSubcommand`.
|
|
18
15
|
const graphSubCommands = {
|
|
19
16
|
summary: defineJsonCommand({
|
|
20
17
|
meta: { name: "summary", description: "Show entity-graph counts and quality telemetry" },
|
|
@@ -118,15 +115,10 @@ const graphSubCommands = {
|
|
|
118
115
|
},
|
|
119
116
|
}),
|
|
120
117
|
};
|
|
121
|
-
const
|
|
122
|
-
export const graphCommand = defineCommand({
|
|
118
|
+
export const graphCommand = defineGroupCommand({
|
|
123
119
|
meta: { name: "graph", description: "Inspect the indexed entity graph stored in SQLite" },
|
|
124
120
|
subCommands: graphSubCommands,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (hasSubcommand(args, GRAPH_SUBCOMMAND_SET))
|
|
128
|
-
return;
|
|
129
|
-
output("graph-summary", akmGraphSummary());
|
|
130
|
-
});
|
|
121
|
+
defaultRun() {
|
|
122
|
+
output("graph-summary", akmGraphSummary());
|
|
131
123
|
},
|
|
132
124
|
});
|
|
@@ -8,7 +8,7 @@ import { loadConfig } from "../../core/config/config.js";
|
|
|
8
8
|
import { NotFoundError, UsageError } from "../../core/errors.js";
|
|
9
9
|
import { getDbPath } from "../../core/paths.js";
|
|
10
10
|
import { warn } from "../../core/warn.js";
|
|
11
|
-
import { closeDatabase, findEntryIdByRef, getEntryById, getEntryRefRowsForStashRoot,
|
|
11
|
+
import { closeDatabase, findEntryIdByRef, getEntryById, getEntryRefRowsForStashRoot, openExistingDatabase, openIndexDatabase, } from "../../indexer/db/db.js";
|
|
12
12
|
import { loadStoredGraphSnapshot } from "../../indexer/db/graph-db.js";
|
|
13
13
|
import { listRelatedPathsForFile } from "../../indexer/graph/graph-boost.js";
|
|
14
14
|
import { runGraphExtractionPass } from "../../indexer/graph/graph-extraction.js";
|
|
@@ -385,7 +385,7 @@ export async function akmGraphUpdate(options) {
|
|
|
385
385
|
let db;
|
|
386
386
|
const resolvedPaths = new Set();
|
|
387
387
|
try {
|
|
388
|
-
db =
|
|
388
|
+
db = openIndexDatabase(dbPath);
|
|
389
389
|
for (const ref of options.refs) {
|
|
390
390
|
const trimmed = ref.trim();
|
|
391
391
|
if (!trimmed)
|
|
@@ -426,7 +426,7 @@ export async function akmGraphUpdate(options) {
|
|
|
426
426
|
let db;
|
|
427
427
|
const startMs = Date.now();
|
|
428
428
|
try {
|
|
429
|
-
db =
|
|
429
|
+
db = openIndexDatabase(getDbPath());
|
|
430
430
|
const onProgress = (event) => {
|
|
431
431
|
if (!event.currentPath)
|
|
432
432
|
return;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* Improve-pipeline advisories for `akm health`: projects the computed
|
|
6
|
+
* {@link ImproveHealthMetrics} plus a few direct event reads into the
|
|
7
|
+
* ordered advisory list.
|
|
8
|
+
*/
|
|
9
|
+
import { readEvents } from "../../core/events.js";
|
|
10
|
+
import { getLatestCycleMetrics } from "../../storage/repositories/canaries-repository.js";
|
|
11
|
+
import { ENRICHMENT_MINTED_FAIL_SHARE, ENRICHMENT_MINTED_WARN_SHARE, } from "./types.js";
|
|
12
|
+
/**
|
|
13
|
+
* Build the improve-pipeline advisories for the health window from the already
|
|
14
|
+
* computed {@link ImproveHealthMetrics} plus a few direct event reads. Pure
|
|
15
|
+
* projection of state → advisories; emission order is preserved so the health
|
|
16
|
+
* report is byte-identical to the previous inline construction.
|
|
17
|
+
*/
|
|
18
|
+
export function collectImproveAdvisories(db, stateDbPath, since, improveSummary) {
|
|
19
|
+
const advisories = [];
|
|
20
|
+
// WS-2 proxy-adequacy tripwire: surface any outcome_proxy_inverted events
|
|
21
|
+
// in the health window as an advisory so operators know when the 0.10+
|
|
22
|
+
// rich in-session signal is no longer deferrable.
|
|
23
|
+
const proxyInvertedEvents = readEvents({ since, type: "outcome_proxy_inverted" }, { dbPath: stateDbPath }).events;
|
|
24
|
+
if (proxyInvertedEvents.length > 0) {
|
|
25
|
+
const lastEvent = proxyInvertedEvents[proxyInvertedEvents.length - 1];
|
|
26
|
+
const correlation = typeof lastEvent.metadata?.correlation === "number" ? lastEvent.metadata.correlation.toFixed(3) : "unknown";
|
|
27
|
+
advisories.push({
|
|
28
|
+
name: "outcome-proxy-adequacy",
|
|
29
|
+
status: "warn",
|
|
30
|
+
kind: "deterministic",
|
|
31
|
+
confidence: "high",
|
|
32
|
+
message: `WS-2 outcome proxy inverted (${proxyInvertedEvents.length} event(s) in window). ` +
|
|
33
|
+
`corr(outcome_score, accepted_change_rate) = ${correlation} < −0.3. ` +
|
|
34
|
+
"Popular assets are also the most-needing-improvement assets — " +
|
|
35
|
+
"the retrieval-based proxy is inverted. " +
|
|
36
|
+
"The 0.10+ rich in-session outcome signal is no longer deferrable. See plan §WS-2.",
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
// Two-tailed companion: a proxy that decays to noise (|corr| < 0.1 at scale)
|
|
40
|
+
// is as much a failure as an inverted one — it just fails silently.
|
|
41
|
+
const proxyDeadEvents = readEvents({ since, type: "outcome_proxy_dead" }, { dbPath: stateDbPath, db }).events;
|
|
42
|
+
if (proxyDeadEvents.length > 0) {
|
|
43
|
+
const lastEvent = proxyDeadEvents[proxyDeadEvents.length - 1];
|
|
44
|
+
const correlation = typeof lastEvent.metadata?.correlation === "number" ? lastEvent.metadata.correlation.toFixed(3) : "unknown";
|
|
45
|
+
advisories.push({
|
|
46
|
+
name: "outcome-proxy-dead",
|
|
47
|
+
status: "warn",
|
|
48
|
+
kind: "deterministic",
|
|
49
|
+
confidence: "high",
|
|
50
|
+
message: `WS-2 outcome proxy is DEAD (${proxyDeadEvents.length} event(s) in window). ` +
|
|
51
|
+
`|corr(outcome_score, accepted_change_rate)| = ${correlation} < 0.1 at n ≥ 500. ` +
|
|
52
|
+
"outcome_score is statistically unrelated to improvement outcomes — " +
|
|
53
|
+
"treat outcome-derived rank contributions as noise until a real usage/outcome signal lands.",
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// Salience-distribution collapse: Gini below the uniform baseline means
|
|
57
|
+
// ranking no longer discriminates between assets.
|
|
58
|
+
if (improveSummary.degradation?.salienceUniformityFlagged) {
|
|
59
|
+
advisories.push({
|
|
60
|
+
name: "salience-uniformity-collapse",
|
|
61
|
+
status: "warn",
|
|
62
|
+
kind: "deterministic",
|
|
63
|
+
confidence: "high",
|
|
64
|
+
message: `Salience distribution collapsed toward uniform: top-100 retrieval_salience Gini = ` +
|
|
65
|
+
`${improveSummary.degradation.corpusCentroidDistance} < 0.08 (uniform baseline ≈ 0.1). ` +
|
|
66
|
+
"Ranking currently carries little to no discrimination between assets.",
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
// Enrichment-vs-minting policy: enrichment lanes edit existing assets;
|
|
70
|
+
// a rising minted share means a lane is generating new content instead.
|
|
71
|
+
const minting = improveSummary.enrichmentMinting;
|
|
72
|
+
if (minting && Number.isFinite(minting.share) && minting.share > ENRICHMENT_MINTED_WARN_SHARE) {
|
|
73
|
+
advisories.push({
|
|
74
|
+
name: "enrichment-lane-minting",
|
|
75
|
+
status: minting.share > ENRICHMENT_MINTED_FAIL_SHARE ? "fail" : "warn",
|
|
76
|
+
kind: "deterministic",
|
|
77
|
+
confidence: "high",
|
|
78
|
+
message: `Enrichment lanes minted ${minting.minted} NEW asset(s) vs ${minting.updated} update(s) ` +
|
|
79
|
+
`(${Math.round(minting.share * 100)}% minted, threshold ${Math.round(ENRICHMENT_MINTED_WARN_SHARE * 100)}%). ` +
|
|
80
|
+
"Enrichment-classed lanes (proactive/high-salience/high-retrieval/signal-delta) are ratified to edit " +
|
|
81
|
+
"existing assets only — new-asset generation belongs to the signal-gated minting lanes.",
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
// Churn: accepted proposals far exceeding distinct touched refs means the
|
|
85
|
+
// loop is repeatedly rewriting the same assets, not covering the corpus.
|
|
86
|
+
if (Number.isFinite(improveSummary.coverage.churnRatio) && improveSummary.coverage.churnRatio > 1.5) {
|
|
87
|
+
advisories.push({
|
|
88
|
+
name: "improve-churn-ratio",
|
|
89
|
+
status: "warn",
|
|
90
|
+
kind: "deterministic",
|
|
91
|
+
confidence: "high",
|
|
92
|
+
message: `Improve churn ratio ${improveSummary.coverage.churnRatio} > 1.5: ` +
|
|
93
|
+
`${improveSummary.coverage.acceptedProposals} accepted proposals touched only ` +
|
|
94
|
+
`${improveSummary.coverage.distinctRefs} distinct assets in the window — ` +
|
|
95
|
+
"repeated rewrites of the same refs count as churn, not coverage.",
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
// R5 collapse/churn detector: surface any collapse_detector_alert events
|
|
99
|
+
// in the health window, plus the latest cycle row's headline numbers so
|
|
100
|
+
// the operator can act without opening the DB. `unknown` when the detector
|
|
101
|
+
// has never produced a cycle row (no consolidate/recombine work yet).
|
|
102
|
+
try {
|
|
103
|
+
// Reuse the already-open state.db handle (readEvents supports a
|
|
104
|
+
// borrowed connection) — no extra open/migrate/close per health call.
|
|
105
|
+
const collapseAlertEvents = readEvents({ since, type: "collapse_detector_alert" }, { dbPath: stateDbPath, db }).events;
|
|
106
|
+
const latestCycle = getLatestCycleMetrics(db);
|
|
107
|
+
const cycleSummary = latestCycle
|
|
108
|
+
? `Latest cycle (${latestCycle.ts}, ${latestCycle.pass}): mean canary recall ${latestCycle.mean_recall.toFixed(3)}, ` +
|
|
109
|
+
`distinct-content ratio ${latestCycle.distinct_content_ratio.toFixed(3)}, ` +
|
|
110
|
+
`${latestCycle.accepted_actions} accepted action(s).`
|
|
111
|
+
: "";
|
|
112
|
+
if (collapseAlertEvents.length > 0) {
|
|
113
|
+
const kinds = [...new Set(collapseAlertEvents.map((e) => String(e.metadata?.kind ?? "unknown")))];
|
|
114
|
+
const collapseKinds = kinds.filter((k) => k.startsWith("collapse"));
|
|
115
|
+
advisories.push({
|
|
116
|
+
name: "collapse-churn-detector",
|
|
117
|
+
status: "warn",
|
|
118
|
+
kind: "deterministic",
|
|
119
|
+
// Collapse kinds are measured, not inferred; churn/merge-floor
|
|
120
|
+
// volume thresholds are still being tuned (design doc §7).
|
|
121
|
+
confidence: collapseKinds.length > 0 ? "high" : "medium",
|
|
122
|
+
message: `R5 detector fired ${collapseAlertEvents.length} alert(s) in window (kinds: ${kinds.join(", ")}). ` +
|
|
123
|
+
`${cycleSummary} See docs/design/improve-collapse-churn-detector-design.md §6.3 runbook queries.`,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
else if (latestCycle) {
|
|
127
|
+
advisories.push({
|
|
128
|
+
name: "collapse-churn-detector",
|
|
129
|
+
status: "pass",
|
|
130
|
+
kind: "deterministic",
|
|
131
|
+
confidence: "high",
|
|
132
|
+
message: `No collapse/churn alerts in window. ${cycleSummary}`,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
advisories.push({
|
|
137
|
+
name: "collapse-churn-detector",
|
|
138
|
+
status: "unknown",
|
|
139
|
+
kind: "deterministic",
|
|
140
|
+
confidence: "high",
|
|
141
|
+
message: "No detector cycle rows yet — the collapse/churn detector runs only on improve cycles " +
|
|
142
|
+
"where consolidate/recombine did work (synthesis lanes may be idle).",
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
// Table may predate migration 016 in odd mixed-version setups — advisory
|
|
148
|
+
// is best-effort and must never fail the health command.
|
|
149
|
+
}
|
|
150
|
+
return advisories;
|
|
151
|
+
}
|