akm-cli 0.9.17-alpha.2 → 0.9.17-alpha.4
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 +756 -0
- package/dist/akm +94 -196
- package/dist/cli/shared.js +6 -2
- package/dist/cli.js +22 -9
- package/dist/commands/agent/agent-dispatch.js +1 -1
- package/dist/commands/command/command-execution.js +24 -62
- package/dist/commands/feedback-cli.js +0 -1
- package/dist/commands/health/accept-rate.js +2 -2
- package/dist/commands/health/checks.js +30 -75
- package/dist/commands/health/config-skew.js +38 -0
- package/dist/commands/health/egress.js +54 -0
- package/dist/commands/health/html-report.js +0 -38
- package/dist/commands/health/improve-metrics.js +123 -562
- package/dist/commands/health/plugin-staleness.js +53 -3
- package/dist/commands/health/renderers.js +12 -4
- package/dist/commands/health/report-view-model.js +11 -106
- package/dist/commands/health/types-improve.js +4 -19
- package/dist/commands/health/windows.js +64 -73
- package/dist/commands/health.js +122 -143
- package/dist/commands/improve/consolidate/chunking.js +25 -100
- package/dist/commands/improve/consolidate/sanitize.js +54 -149
- package/dist/commands/improve/consolidate.js +538 -1075
- package/dist/commands/improve/content-hash.js +16 -24
- package/dist/commands/improve/distill/content-repair.js +18 -100
- package/dist/commands/improve/distill-guards.js +20 -81
- package/dist/commands/improve/distill-promotion-policy.js +23 -243
- package/dist/commands/improve/distill.js +608 -1075
- package/dist/commands/improve/eligibility.js +126 -400
- package/dist/commands/improve/execution.js +3 -5
- package/dist/commands/improve/extract.js +487 -1046
- package/dist/commands/improve/feedback-valence.js +0 -25
- package/dist/commands/improve/improve-cli.js +29 -166
- package/dist/commands/improve/improve-result-file.js +10 -66
- package/dist/commands/improve/improve-strategies.js +12 -7
- package/dist/commands/improve/improve-usage-report.js +18 -64
- package/dist/commands/improve/improve.js +443 -1063
- package/dist/commands/improve/ledger.js +114 -0
- package/dist/commands/improve/locks.js +2 -8
- package/dist/commands/improve/loop-stages.js +459 -1172
- package/dist/commands/improve/memory/derived-ref.js +12 -77
- package/dist/commands/improve/memory/memory-belief.js +14 -118
- package/dist/commands/improve/memory/memory-improve.js +4 -3
- package/dist/commands/improve/outcome-loop.js +28 -156
- package/dist/commands/improve/planner.js +5 -10
- package/dist/commands/improve/preparation.js +851 -2339
- package/dist/commands/improve/proactive-maintenance.js +34 -101
- package/dist/commands/improve/reflect-noise.js +104 -280
- package/dist/commands/improve/reflect.js +621 -1367
- package/dist/commands/improve/salience.js +46 -232
- package/dist/commands/improve/session-asset.js +19 -100
- package/dist/commands/improve/stage.js +323 -0
- package/dist/commands/proposal/drain.js +251 -644
- package/dist/commands/proposal/proposal-cli.js +3 -18
- package/dist/commands/proposal/proposal-types.js +20 -41
- package/dist/commands/proposal/proposal.js +1 -2
- package/dist/commands/proposal/propose.js +134 -160
- package/dist/commands/proposal/repository.js +502 -1487
- package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
- package/dist/commands/proposal/validators/proposal-validators.js +1 -1
- package/dist/commands/proposal/validators/proposals.js +13 -89
- package/dist/commands/read/curate.js +63 -413
- package/dist/commands/read/search-cli.js +16 -33
- package/dist/commands/read/search.js +17 -23
- package/dist/commands/read/show.js +2 -13
- package/dist/commands/sources/bundle-cli.js +25 -2
- package/dist/commands/sources/bundle-config-ops.js +7 -0
- package/dist/commands/sources/dangerous-env-audit.js +1 -2
- package/dist/commands/sources/info.js +2 -11
- package/dist/commands/sources/installed-stashes.js +197 -746
- package/dist/commands/sources/schema-repair.js +98 -129
- package/dist/commands/sources/source-add.js +62 -12
- package/dist/commands/sources/stash-cli.js +1 -1
- package/dist/commands/tasks/explain.js +10 -13
- package/dist/commands/tasks/tasks-cli.js +9 -8
- package/dist/commands/tasks/tasks.js +326 -930
- package/dist/commands/tasks/validate.js +42 -21
- package/dist/commands/workflow/plan.js +22 -29
- package/dist/commands/workflow-cli.js +4 -4
- package/dist/core/adapter/adapters/akm-adapter.js +0 -1
- package/dist/core/adapter/adapters/akm-lint.js +2 -3
- package/dist/core/adapter/adapters/akm-metadata.js +11 -12
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
- package/dist/core/adapter/execution-source.js +17 -29
- package/dist/core/asset/resolve-ref.js +1 -1
- package/dist/core/bundle-id.js +42 -5
- package/dist/core/bundle-rename.js +291 -0
- package/dist/core/config/config-io.js +1 -2
- package/dist/core/config/config-schema.js +1 -33
- package/dist/core/config/config-walker.js +1 -1
- package/dist/core/config/config.js +163 -68
- package/dist/core/config/legacy-source-shape-shim.js +38 -9
- package/dist/core/config/schema/embedding.js +20 -5
- package/dist/core/config/schema/engines.js +5 -0
- package/dist/core/config/schema/execution.js +1 -1
- package/dist/core/config/schema/experimental.js +1 -1
- package/dist/core/config/schema/improve-processes.js +21 -95
- package/dist/core/config/schema/improve.js +4 -42
- package/dist/core/config/schema/scheduler.js +12 -12
- package/dist/core/config/schema/search.js +6 -22
- package/dist/core/env-secret-ref.js +0 -1
- package/dist/core/errors.js +8 -9
- package/dist/core/file-lock.js +76 -173
- package/dist/core/logs-db.js +2 -2
- package/dist/core/paths.js +0 -27
- package/dist/core/redaction.js +109 -2
- package/dist/core/run-lock.js +2 -5
- package/dist/core/spawn-env.js +1 -1
- package/dist/core/state/migrations.js +108 -61
- package/dist/core/state-db-scope.js +2 -4
- package/dist/core/state-db.js +126 -692
- package/dist/core/type-presentation.js +1 -9
- package/dist/core/write-source.js +293 -1012
- package/dist/execution/input-contract.js +1 -1
- package/dist/execution/resolved-request.js +135 -689
- package/dist/execution/source.js +63 -257
- package/dist/execution/target-ref.js +1 -1
- package/dist/indexer/bundle-identity-guard.js +2 -2
- package/dist/indexer/db/graph-db.js +106 -46
- package/dist/indexer/ensure-index.js +44 -85
- package/dist/indexer/graph/graph-extraction.js +340 -562
- package/dist/indexer/graph/graph-related.js +130 -0
- package/dist/indexer/index-rebuild-lock.js +3 -11
- package/dist/indexer/index-writer-lock.js +8 -17
- package/dist/indexer/index-written-assets.js +139 -151
- package/dist/indexer/indexer.js +524 -846
- package/dist/indexer/materialize-embeddings.js +60 -397
- package/dist/indexer/passes/memory-inference.js +81 -90
- package/dist/indexer/passes/metadata.js +132 -200
- package/dist/indexer/read-preflight.js +0 -7
- package/dist/indexer/scan/doc-to-entry.js +1 -3
- package/dist/indexer/scan/drain-dir.js +1 -1
- package/dist/indexer/search/db-search.js +181 -590
- package/dist/indexer/search/fts-query.js +30 -41
- package/dist/indexer/search/ranking.js +28 -154
- package/dist/indexer/search/search-attribution.js +12 -32
- package/dist/indexer/search/search-fields.js +11 -15
- package/dist/indexer/search/search-hit-enrichers.js +54 -85
- package/dist/indexer/search/search-source.js +1 -4
- package/dist/indexer/usage/usage-events.js +2 -7
- package/dist/integrations/agent/engine-fallback.js +23 -40
- package/dist/integrations/agent/engine-resolution.js +93 -183
- package/dist/integrations/agent/execution.js +507 -0
- package/dist/integrations/agent/model-map.js +28 -156
- package/dist/integrations/agent/request-lowering.js +66 -141
- package/dist/integrations/agent/runner-dispatch.js +143 -321
- package/dist/integrations/agent/runner.js +54 -14
- package/dist/integrations/lockfile.js +53 -101
- package/dist/llm/embedders/deterministic.js +2 -3
- package/dist/llm/embedders/profile.js +71 -0
- package/dist/llm/embedders/remote.js +10 -15
- package/dist/llm/graph-extract.js +3 -12
- package/dist/llm/index-passes.js +3 -5
- package/dist/llm/memory-infer.js +1 -2
- package/dist/llm/metadata-enhance.js +1 -2
- package/dist/llm/structured-call.js +5 -24
- package/dist/output/generic-render.js +23 -11
- package/dist/output/html-render.js +13 -10
- package/dist/output/render-registry.js +3 -32
- package/dist/output/shapes/helpers.js +2 -34
- package/dist/output/shapes/passthrough.js +1 -9
- package/dist/{indexer/search/ranking-types.js → output/text/bundle-rename.js} +4 -1
- package/dist/output/text/command-format.js +60 -23
- package/dist/output/text/helpers.js +1 -1
- package/dist/output/text/migrate.js +5 -14
- package/dist/output/text/proposal-format.js +1 -2
- package/dist/output/text/workflow-format.js +0 -32
- package/dist/output/text.js +2 -0
- package/dist/registry/factory.js +4 -19
- package/dist/registry/network.js +66 -220
- package/dist/registry/providers/index.js +0 -2
- package/dist/registry/providers/skills-sh.js +3 -14
- package/dist/registry/providers/static-index.js +24 -26
- package/dist/registry/resolve.js +55 -131
- package/dist/scripts/akm-migrate-node.js +43937 -93313
- package/dist/scripts/akm-migrate.js +43697 -93071
- package/dist/setup/registry-stash-loader.js +4 -13
- package/dist/setup/semantic-assets.js +3 -44
- package/dist/setup/setup.js +1 -1
- package/dist/setup/steps/tasks.js +25 -15
- package/dist/sources/provider-factory.js +17 -18
- package/dist/sources/providers/filesystem.js +2 -3
- package/dist/sources/providers/git-install.js +7 -1
- package/dist/sources/providers/git-provider.js +0 -3
- package/dist/sources/providers/git-stash.js +0 -17
- package/dist/sources/providers/npm.js +2 -4
- package/dist/sources/providers/provider-utils.js +5 -10
- package/dist/sources/providers/website.js +0 -2
- package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
- package/dist/sources/website-url.js +2 -2
- package/dist/storage/database.js +9 -35
- package/dist/storage/repositories/improve-ledger-repository.js +168 -0
- package/dist/storage/repositories/index-connection.js +34 -70
- package/dist/storage/repositories/index-entries-repository.js +69 -111
- package/dist/storage/repositories/index-entry-mapper.js +1 -2
- package/dist/storage/repositories/index-entry-schema.js +83 -269
- package/dist/storage/repositories/index-fts-repository.js +86 -256
- package/dist/storage/repositories/index-llm-cache-repository.js +17 -0
- package/dist/storage/repositories/index-meta-repository.js +6 -4
- package/dist/storage/repositories/index-schema.js +192 -220
- package/dist/storage/repositories/index-utility-repository.js +8 -29
- package/dist/storage/repositories/index-vec-repository.js +133 -414
- package/dist/storage/repositories/outcome-repository.js +2 -1
- package/dist/storage/repositories/proposals-repository.js +35 -0
- package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
- package/dist/storage/repositories/task-history-repository.js +26 -4
- package/dist/storage/repositories/workflow-runs-repository.js +53 -244
- package/dist/storage/sqlite-migrations.js +136 -0
- package/dist/storage/sqlite-pragmas.js +11 -9
- package/dist/storage/sqlite-transaction.js +170 -0
- package/dist/storage/state-db-integrity.js +34 -27
- package/dist/tasks/activation-config.js +134 -62
- package/dist/tasks/backends/cron.js +129 -277
- package/dist/tasks/backends/exec-utils.js +2 -5
- package/dist/tasks/backends/launchd.js +125 -745
- package/dist/tasks/backends/schtasks.js +101 -620
- package/dist/tasks/prepare/prepare-support.js +5 -15
- package/dist/tasks/prepare/prepare.js +0 -2
- package/dist/tasks/resolve-akm-bin.js +20 -79
- package/dist/tasks/run/attempt-lifecycle.js +0 -1
- package/dist/tasks/scheduler-binding.js +18 -238
- package/dist/tasks/scheduler-invocation.js +52 -52
- package/dist/tasks/scheduler-lock.js +53 -0
- package/dist/tasks/scheduler-sync.js +363 -679
- package/dist/tasks/source/parse-task-source.js +160 -10
- package/dist/tasks/source/task-source-v3-frozen.js +3 -4
- package/dist/tasks/source/task-to-v4.js +2 -2
- package/dist/workflows/authoring/authoring.js +3 -12
- package/dist/workflows/compile.js +211 -0
- package/dist/workflows/concurrency-policy.js +13 -74
- package/dist/workflows/exec/child-invocation.js +3 -17
- package/dist/workflows/exec/child-workflow.js +32 -141
- package/dist/workflows/exec/dispatch-redaction.js +13 -53
- package/dist/workflows/exec/environment.js +98 -0
- package/dist/workflows/exec/exec-unit.js +33 -140
- package/dist/workflows/exec/frozen-judge.js +7 -59
- package/dist/workflows/exec/native-executor.js +82 -341
- package/dist/workflows/exec/param-secrets.js +29 -47
- package/dist/workflows/exec/run-workflow.js +154 -387
- package/dist/workflows/exec/scheduler.js +9 -36
- package/dist/workflows/exec/step-work.js +127 -430
- package/dist/workflows/exec/unit-dispatch.js +11 -63
- package/dist/workflows/exec/unit-writer.js +8 -52
- package/dist/workflows/exec/worktree.js +39 -273
- package/dist/workflows/freeze/child-output-references.js +4 -15
- package/dist/workflows/freeze/environment.js +99 -92
- package/dist/workflows/freeze/freeze.js +172 -0
- package/dist/workflows/freeze/step-values.js +19 -21
- package/dist/workflows/freeze/targets/child-workflow.js +23 -92
- package/dist/workflows/freeze/targets/command.js +10 -33
- package/dist/workflows/freeze/targets/script.js +5 -12
- package/dist/workflows/freeze/targets/shell.js +3 -6
- package/dist/workflows/freeze/targets/task.js +25 -80
- package/dist/workflows/freeze/task-bindings.js +20 -67
- package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
- package/dist/workflows/ir/params.js +6 -51
- package/dist/workflows/ir/plan-hash.js +2 -34
- package/dist/workflows/parser.js +140 -43
- package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
- package/dist/workflows/renderer.js +36 -69
- package/dist/workflows/resource-limits.js +12 -120
- package/dist/workflows/runtime/agent-identity.js +8 -40
- package/dist/workflows/runtime/run-outputs.js +3 -6
- package/dist/workflows/runtime/run-plan.js +316 -0
- package/dist/workflows/runtime/runs.js +48 -200
- package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
- package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
- package/dist/workflows/validate-summary.js +2 -7
- package/docs/integration/bundling-akm.md +49 -42
- package/docs/migration/README.md +1 -0
- package/docs/migration/release-notes/0.9.17.md +41 -0
- package/docs/migration/v0.9.1-to-v0.9.2.md +19 -7
- package/docs/reference/cli.md +182 -125
- package/docs/reference/configuration.md +49 -56
- package/docs/reference/data-and-telemetry.md +19 -20
- package/docs/reference/tasks.md +86 -38
- package/docs/reference/workflow-schema.md +14 -18
- package/docs/reference/workflows.md +6 -9
- package/package.json +1 -1
- package/schemas/akm-config.json +87 -406
- package/dist/commands/health/advisories.js +0 -150
- package/dist/commands/health/metrics.js +0 -329
- package/dist/commands/health/surfaces.js +0 -102
- package/dist/commands/improve/anti-collapse.js +0 -83
- package/dist/commands/improve/collapse-detector.js +0 -432
- package/dist/commands/improve/consolidate/eligibility.js +0 -48
- package/dist/commands/improve/consolidate/merge.js +0 -146
- package/dist/commands/improve/distill/promote-memory.js +0 -329
- package/dist/commands/improve/distill/quality-gate.js +0 -500
- package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
- package/dist/commands/improve/proposal-envelope.js +0 -31
- package/dist/commands/improve/run-context.js +0 -123
- package/dist/commands/improve/shared.js +0 -21
- package/dist/commands/improve/source-identity.js +0 -28
- package/dist/commands/improve/triage.js +0 -96
- package/dist/commands/proposal/drain-policies.js +0 -151
- package/dist/commands/sources/update-transaction.js +0 -220
- package/dist/core/action-contributors.js +0 -28
- package/dist/core/config/config-version-shim.js +0 -101
- package/dist/core/config/retired-experimental-keys-shim.js +0 -62
- package/dist/core/fs-txn.js +0 -405
- package/dist/core/lexical-score.js +0 -25
- package/dist/core/maintenance-barrier.js +0 -167
- package/dist/execution/executable-identity.js +0 -105
- package/dist/execution/guarded-source.js +0 -427
- package/dist/indexer/graph/graph-boost.js +0 -427
- package/dist/indexer/graph/graph-dedup.js +0 -95
- package/dist/indexer/search/name-match.js +0 -35
- package/dist/indexer/search/ranking-contributors.js +0 -515
- package/dist/indexer/walk/project-context.js +0 -192
- package/dist/integrations/agent/execution-cascade.js +0 -566
- package/dist/integrations/agent/execution-definitions.js +0 -202
- package/dist/integrations/agent/execution-lowering.js +0 -841
- package/dist/integrations/agent/execution-preparation.js +0 -98
- package/dist/integrations/agent/inline-execution.js +0 -74
- package/dist/registry/create-provider-registry.js +0 -29
- package/dist/registry/pinned-request-helper.js +0 -247
- package/dist/registry/pinned-transport.js +0 -717
- package/dist/sources/providers/index.js +0 -14
- package/dist/storage/engines/sqlite-migrations.js +0 -271
- package/dist/storage/repositories/canaries-repository.js +0 -107
- package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
- package/dist/storage/repositories/registry-cache.js +0 -113
- package/dist/tasks/scheduler-sync-preview.js +0 -52
- package/dist/workflows/freeze/resolve-steps.js +0 -86
- package/dist/workflows/freeze/source-freeze.js +0 -64
- package/dist/workflows/ir/compile.js +0 -321
- package/dist/workflows/ir/environment-v4.js +0 -330
- package/dist/workflows/ir/freeze-v4.js +0 -153
- package/dist/workflows/ir/schema-v4.js +0 -745
- package/dist/workflows/ir/schema.js +0 -354
- package/dist/workflows/program/schema.js +0 -78
- package/dist/workflows/runtime/checkin.js +0 -57
- package/dist/workflows/runtime/plan-classifier.js +0 -196
- package/dist/workflows/runtime/unit-checkin.js +0 -45
- package/dist/workflows/runtime/unit-phases.js +0 -20
- package/dist/workflows/schema.js +0 -4
- package/dist/workflows/source-ir/compile.js +0 -200
- package/dist/workflows/source-ir/program.js +0 -50
- package/dist/workflows/source-ir/result.js +0 -26
- package/dist/workflows/source-ir/schema.js +0 -786
- package/dist/workflows/source-ir/triggers.js +0 -79
- package/dist/workflows/source-ir/uses.js +0 -40
- package/dist/workflows/validator.js +0 -60
|
@@ -1,150 +0,0 @@
|
|
|
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 across all assets with retrieval evidence.
|
|
57
|
-
if (improveSummary.degradation?.salienceUniformityFlagged) {
|
|
58
|
-
const sampleSize = improveSummary.degradation.retrievalSalienceSampleSize;
|
|
59
|
-
advisories.push({
|
|
60
|
-
name: "salience-uniformity-collapse",
|
|
61
|
-
status: "warn",
|
|
62
|
-
kind: "deterministic",
|
|
63
|
-
confidence: "high",
|
|
64
|
-
message: `Salience distribution collapsed toward uniform: retrieval_salience Gini = ` +
|
|
65
|
-
`${improveSummary.degradation.corpusCentroidDistance} < 0.08 across ${sampleSize} ` +
|
|
66
|
-
"observed, resolvable assets. Ranking carries little discrimination among assets with retrieval evidence.",
|
|
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/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 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
|
-
: "";
|
|
111
|
-
if (collapseAlertEvents.length > 0) {
|
|
112
|
-
const kinds = [...new Set(collapseAlertEvents.map((e) => String(e.metadata?.kind ?? "unknown")))];
|
|
113
|
-
const collapseKinds = kinds.filter((k) => k.startsWith("collapse"));
|
|
114
|
-
advisories.push({
|
|
115
|
-
name: "collapse-churn-detector",
|
|
116
|
-
status: "warn",
|
|
117
|
-
kind: "deterministic",
|
|
118
|
-
// Collapse kinds are measured, not inferred; the merge-floor
|
|
119
|
-
// volume threshold is still being tuned (design doc §7).
|
|
120
|
-
confidence: collapseKinds.length > 0 ? "high" : "medium",
|
|
121
|
-
message: `R5 detector fired ${collapseAlertEvents.length} alert(s) in window (kinds: ${kinds.join(", ")}). ` +
|
|
122
|
-
`${cycleSummary} See docs/architecture/specs/improve-collapse-churn-detector-design.md §6.3 runbook queries.`,
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
else if (latestCycle) {
|
|
126
|
-
advisories.push({
|
|
127
|
-
name: "collapse-churn-detector",
|
|
128
|
-
status: "pass",
|
|
129
|
-
kind: "deterministic",
|
|
130
|
-
confidence: "high",
|
|
131
|
-
message: `No collapse/churn alerts in window. ${cycleSummary}`,
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
advisories.push({
|
|
136
|
-
name: "collapse-churn-detector",
|
|
137
|
-
status: "unknown",
|
|
138
|
-
kind: "deterministic",
|
|
139
|
-
confidence: "high",
|
|
140
|
-
message: "No detector cycle rows yet — the collapse/churn detector runs only on improve cycles " +
|
|
141
|
-
"where consolidate did work.",
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
// Table may predate migration 016 in odd mixed-version setups — advisory
|
|
147
|
-
// is best-effort and must never fail the health command.
|
|
148
|
-
}
|
|
149
|
-
return advisories;
|
|
150
|
-
}
|
|
@@ -1,329 +0,0 @@
|
|
|
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
|
-
* State.db-backed health metrics for `akm health`: the round-trip probe,
|
|
6
|
-
* denominator-fixed coverage, the enrichment-vs-minting rollup, and the WS-5
|
|
7
|
-
* per-run degradation metrics.
|
|
8
|
-
*/
|
|
9
|
-
import { rethrowIfTestIsolationError } from "../../core/errors.js";
|
|
10
|
-
import { decodeImproveResult } from "../../core/improve-result.js";
|
|
11
|
-
import { withStateDb } from "../../core/state-db.js";
|
|
12
|
-
import { insertEvent } from "../../storage/repositories/events-repository.js";
|
|
13
|
-
import { queryImproveRuns } from "../../storage/repositories/improve-runs-repository.js";
|
|
14
|
-
import { listStateProposals } from "../../storage/repositories/proposals-repository.js";
|
|
15
|
-
import { getObservedRetrievalSalience } from "../../storage/repositories/salience-repository.js";
|
|
16
|
-
import { roundRate, toFiniteNumber } from "./improve-metrics.js";
|
|
17
|
-
import { ENRICHMENT_LANES, } from "./types.js";
|
|
18
|
-
/** Event type appended + read back by the state.db round-trip probe. */
|
|
19
|
-
const HEALTH_PROBE_EVENT = "health_probe";
|
|
20
|
-
/**
|
|
21
|
-
* Retrieval-salience Gini guardrails.
|
|
22
|
-
*
|
|
23
|
-
* These are distribution-shape thresholds, not quantiles tied to a corpus
|
|
24
|
-
* size. Synthetic anchors pinned in monitor-liveness.test.ts are ~0.01 for a
|
|
25
|
-
* near-uniform two-band distribution, 0.25 for a balanced 0.25/0.75 spread,
|
|
26
|
-
* and ~0.82 for one dominant value among nine 0.01 values. Full-observation
|
|
27
|
-
* production snapshots also sit stably in the neutral band: 0.2613 at n=1,209
|
|
28
|
-
* (2026-07-12) and 0.2506 at n=1,454 (2026-08-17, missing rows excluded).
|
|
29
|
-
*/
|
|
30
|
-
const SALIENCE_UNIFORMITY_GINI_THRESHOLD = 0.08;
|
|
31
|
-
const SALIENCE_ENTRENCHMENT_GINI_THRESHOLD = 0.35;
|
|
32
|
-
/** Synthetic sentinel ref (ref-grammar decision D-R3): a colon-free
|
|
33
|
-
* `<subsystem>/_<marker>` label. `health` has no asset stash-subdir, so
|
|
34
|
-
* `health/_probe` names the subsystem. */
|
|
35
|
-
const HEALTH_PROBE_REF = "health/_probe";
|
|
36
|
-
/**
|
|
37
|
-
* Verify state.db can accept a write and read it back — WITHOUT leaving any
|
|
38
|
-
* permanent trace (R-030). Earlier versions appended a `health_probe` event
|
|
39
|
-
* on every `akm health` invocation and never removed it: a read-only health
|
|
40
|
-
* check ran on a cron would grow state.db without bound (the only purge is
|
|
41
|
-
* `improve`'s retention pass, which a health-only user never runs). The probe
|
|
42
|
-
* row inserted here is deleted again inside the SAME connection once the
|
|
43
|
-
* round trip is confirmed, so the net effect on the `events` table is always
|
|
44
|
-
* zero rows — the round trip still genuinely exercises append + read against
|
|
45
|
-
* the real table, it just doesn't accumulate.
|
|
46
|
-
*/
|
|
47
|
-
export function probeStateDbRoundTrip(stateDbPath) {
|
|
48
|
-
const started = Date.now();
|
|
49
|
-
try {
|
|
50
|
-
return withStateDb((db) => {
|
|
51
|
-
const ts = new Date().toISOString();
|
|
52
|
-
const insertedId = insertEvent(db, {
|
|
53
|
-
eventType: HEALTH_PROBE_EVENT,
|
|
54
|
-
ts,
|
|
55
|
-
ref: HEALTH_PROBE_REF,
|
|
56
|
-
metadata: { source: "akm health" },
|
|
57
|
-
});
|
|
58
|
-
const durationMs = Date.now() - started;
|
|
59
|
-
if (insertedId === undefined) {
|
|
60
|
-
return { ok: false, durationMs, error: "probe event insert did not return a row id" };
|
|
61
|
-
}
|
|
62
|
-
// The round-trip matches on the exact (id, eventType, ref) triple
|
|
63
|
-
// written above, then removes the row regardless of outcome — a
|
|
64
|
-
// failed round trip must not leak a row any more than a successful
|
|
65
|
-
// one should.
|
|
66
|
-
let roundTripOk = false;
|
|
67
|
-
try {
|
|
68
|
-
const row = db
|
|
69
|
-
.prepare("SELECT 1 AS present FROM events WHERE id = ? AND event_type = ? AND ref = ?")
|
|
70
|
-
.get(insertedId, HEALTH_PROBE_EVENT, HEALTH_PROBE_REF);
|
|
71
|
-
roundTripOk = row !== undefined;
|
|
72
|
-
}
|
|
73
|
-
finally {
|
|
74
|
-
db.prepare("DELETE FROM events WHERE id = ?").run(insertedId);
|
|
75
|
-
}
|
|
76
|
-
if (!roundTripOk) {
|
|
77
|
-
return { ok: false, durationMs, error: "probe event was not readable after append" };
|
|
78
|
-
}
|
|
79
|
-
return { ok: true, durationMs };
|
|
80
|
-
}, { path: stateDbPath });
|
|
81
|
-
}
|
|
82
|
-
catch (err) {
|
|
83
|
-
rethrowIfTestIsolationError(err);
|
|
84
|
-
return { ok: false, durationMs: Date.now() - started, error: err instanceof Error ? err.message : String(err) };
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
// ── WS-5 Observability helpers ───────────────────────────────────────────────
|
|
88
|
-
/**
|
|
89
|
-
* Compute WS-5 denominator-fixed coverage metrics.
|
|
90
|
-
*
|
|
91
|
-
* `coverage = accepted_proposals / total_assets` (Part V §3).
|
|
92
|
-
* The denominator is the TOTAL stash size (not the moving eligible set) so
|
|
93
|
-
* more-inclusive WS-1 ranking cannot spuriously inflate coverage.
|
|
94
|
-
* `eligibleFraction = eligible_assets / total_assets` is reported separately.
|
|
95
|
-
*
|
|
96
|
-
* Proposals are counted only when their `updatedAt` falls within `[since, until)`
|
|
97
|
-
* so the rate is genuinely window-scoped (matching the JSDoc on the type).
|
|
98
|
-
*
|
|
99
|
-
* @param db - Open state.db connection.
|
|
100
|
-
* @param totalAssets - Total stash asset count (eligible + derived) from the
|
|
101
|
-
* most recent run's memorySummary. 0 = denominator unknown, returns NaN rates.
|
|
102
|
-
* @param eligibleAssets - Eligible (non-derived) asset count from the most recent run.
|
|
103
|
-
* @param since - Window start (ISO-8601). Proposals accepted before this are excluded.
|
|
104
|
-
* @param until - Window end (ISO-8601, exclusive). Absent = open-ended (up to now).
|
|
105
|
-
* @param stashDir - Optional: scope accepted proposals to one stash. Absent = all stashes.
|
|
106
|
-
*/
|
|
107
|
-
export function computeDenominatorFixedCoverage(db, totalAssets, eligibleAssets, since, until, stashDir) {
|
|
108
|
-
let acceptedProposals = 0;
|
|
109
|
-
let distinctRefs = 0;
|
|
110
|
-
try {
|
|
111
|
-
const proposals = listStateProposals(db, {
|
|
112
|
-
status: "accepted",
|
|
113
|
-
...(stashDir ? { stashDir } : {}),
|
|
114
|
-
}).filter((p) => {
|
|
115
|
-
const updatedAt = p.updatedAt ?? "";
|
|
116
|
-
if (updatedAt < since)
|
|
117
|
-
return false;
|
|
118
|
-
if (until !== undefined && updatedAt >= until)
|
|
119
|
-
return false;
|
|
120
|
-
return true;
|
|
121
|
-
});
|
|
122
|
-
acceptedProposals = proposals.length;
|
|
123
|
-
// Coverage counts DISTINCT refs: N accepted rewrites of one asset are
|
|
124
|
-
// churn, not coverage. The raw proposal count is kept alongside so the
|
|
125
|
-
// churn ratio (proposals ÷ distinct refs) stays visible.
|
|
126
|
-
distinctRefs = new Set(proposals.map((p) => p.ref)).size;
|
|
127
|
-
}
|
|
128
|
-
catch {
|
|
129
|
-
// Fail open: table may not exist on older installs.
|
|
130
|
-
}
|
|
131
|
-
const churnRatio = distinctRefs > 0 ? roundRate(acceptedProposals / distinctRefs) : Number.NaN;
|
|
132
|
-
if (totalAssets === 0) {
|
|
133
|
-
return {
|
|
134
|
-
rate: Number.NaN,
|
|
135
|
-
eligibleFraction: Number.NaN,
|
|
136
|
-
acceptedProposals,
|
|
137
|
-
distinctRefs,
|
|
138
|
-
churnRatio,
|
|
139
|
-
totalAssets: 0,
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
return {
|
|
143
|
-
rate: roundRate(distinctRefs / totalAssets),
|
|
144
|
-
eligibleFraction: roundRate(eligibleAssets / totalAssets),
|
|
145
|
-
acceptedProposals,
|
|
146
|
-
distinctRefs,
|
|
147
|
-
churnRatio,
|
|
148
|
-
totalAssets,
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Compute the enrichment-vs-minting rollup over the window's accepted,
|
|
153
|
-
* lane-attributed proposals (reporting-only; see {@link EnrichmentMintingRollup}).
|
|
154
|
-
*
|
|
155
|
-
* SQL-side `json_extract` keeps the (potentially large) `backupContent` blobs
|
|
156
|
-
* out of process memory. Pre-Phase-6C rows without an `eligibilitySource`
|
|
157
|
-
* cannot be lane-classified and are excluded. Fails open (undefined) when the
|
|
158
|
-
* proposals table is absent.
|
|
159
|
-
*/
|
|
160
|
-
export function computeEnrichmentMintingRollup(db, since, until) {
|
|
161
|
-
try {
|
|
162
|
-
const rows = db
|
|
163
|
-
.prepare(`SELECT
|
|
164
|
-
json_extract(metadata_json, '$.eligibilitySource') AS lane,
|
|
165
|
-
CASE WHEN json_extract(metadata_json, '$.backupContent') IS NULL THEN 1 ELSE 0 END AS is_minted,
|
|
166
|
-
COUNT(*) AS cnt
|
|
167
|
-
FROM proposals
|
|
168
|
-
WHERE status = 'accepted'
|
|
169
|
-
AND updated_at >= ?
|
|
170
|
-
AND (? IS NULL OR updated_at < ?)
|
|
171
|
-
AND json_extract(metadata_json, '$.eligibilitySource') IS NOT NULL
|
|
172
|
-
AND json_extract(metadata_json, '$.eligibilitySource') != ''
|
|
173
|
-
GROUP BY lane, is_minted`)
|
|
174
|
-
.all(since, until ?? null, until ?? null);
|
|
175
|
-
if (rows.length === 0)
|
|
176
|
-
return undefined;
|
|
177
|
-
const byLane = {};
|
|
178
|
-
for (const row of rows) {
|
|
179
|
-
byLane[row.lane] ??= { minted: 0, updated: 0 };
|
|
180
|
-
const entry = byLane[row.lane];
|
|
181
|
-
if (row.is_minted === 1)
|
|
182
|
-
entry.minted += row.cnt;
|
|
183
|
-
else
|
|
184
|
-
entry.updated += row.cnt;
|
|
185
|
-
}
|
|
186
|
-
let minted = 0;
|
|
187
|
-
let updated = 0;
|
|
188
|
-
for (const lane of ENRICHMENT_LANES) {
|
|
189
|
-
const entry = byLane[lane];
|
|
190
|
-
if (!entry)
|
|
191
|
-
continue;
|
|
192
|
-
minted += entry.minted;
|
|
193
|
-
updated += entry.updated;
|
|
194
|
-
}
|
|
195
|
-
const decided = minted + updated;
|
|
196
|
-
return {
|
|
197
|
-
minted,
|
|
198
|
-
updated,
|
|
199
|
-
share: decided > 0 ? roundRate(minted / decided) : Number.NaN,
|
|
200
|
-
byLane,
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
catch {
|
|
204
|
-
// Fail open: proposals table may not exist on older installs.
|
|
205
|
-
return undefined;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Compute WS-5 per-run degradation metrics (Part V §4).
|
|
210
|
-
*
|
|
211
|
-
* Health VIEWS only — reads from state.db tables populated by prior improve
|
|
212
|
-
* runs. Gracefully returns partial data when tables are absent (pre-WS-1/2).
|
|
213
|
-
*
|
|
214
|
-
* @param db - Open state.db connection.
|
|
215
|
-
* @param since - Window start (ISO-8601).
|
|
216
|
-
* @param until - Window end (ISO-8601).
|
|
217
|
-
*/
|
|
218
|
-
export function computeDegradationMetrics(db, since, until) {
|
|
219
|
-
// (a) Corpus diversity — distribution of every observed retrieval-salience
|
|
220
|
-
// value for a currently resolvable asset. Zero is the no-observation floor
|
|
221
|
-
// and is excluded: the diagnostic measures discrimination among assets that
|
|
222
|
-
// have a retrieval signal, not corpus coverage.
|
|
223
|
-
//
|
|
224
|
-
// Do not preselect by rank_score here. The old top-100 sample truncated the
|
|
225
|
-
// distribution before measuring it, producing a low Gini even when the full
|
|
226
|
-
// observed corpus had a healthy spread.
|
|
227
|
-
let corpusCentroidDistance = Number.NaN;
|
|
228
|
-
let retrievalSalienceSampleSize = 0;
|
|
229
|
-
let entrenchmentFlagged;
|
|
230
|
-
let salienceUniformityFlagged;
|
|
231
|
-
try {
|
|
232
|
-
// Fail-open: the asset_salience table may not exist yet (pre-WS-1 install).
|
|
233
|
-
const rows = getObservedRetrievalSalience(db);
|
|
234
|
-
retrievalSalienceSampleSize = rows.length;
|
|
235
|
-
if (rows.length >= 5) {
|
|
236
|
-
// The repository returns ascending values. Keep a defensive sort so the
|
|
237
|
-
// O(n log n) closed-form Gini remains correct if that contract changes.
|
|
238
|
-
const vals = rows.map((r) => r.retrieval_salience).sort((a, b) => a - b);
|
|
239
|
-
const n = vals.length;
|
|
240
|
-
const sum = vals.reduce((acc, value) => acc + value, 0);
|
|
241
|
-
const weightedSum = vals.reduce((acc, value, index) => acc + (index + 1) * value, 0);
|
|
242
|
-
// Closed-form Gini for sorted non-negative values. This is O(n log n)
|
|
243
|
-
// including the defensive sort; the previous pairwise implementation
|
|
244
|
-
// was O(n²) and only safe because the sample was capped at 100.
|
|
245
|
-
const gini = sum > 0 ? (2 * weightedSum) / (n * sum) - (n + 1) / n : 0;
|
|
246
|
-
// Re-express as a diversity proxy in [0,1]: high gini = low diversity.
|
|
247
|
-
// corpusCentroidDistance approximation: gini is "distance from uniform".
|
|
248
|
-
// Two-tailed: high concentration flags entrenchment; very low spread
|
|
249
|
-
// flags near-uniformity. Calibration provenance is documented with the
|
|
250
|
-
// constants above and pinned by synthetic distribution tests.
|
|
251
|
-
corpusCentroidDistance = roundRate(gini);
|
|
252
|
-
entrenchmentFlagged = gini > SALIENCE_ENTRENCHMENT_GINI_THRESHOLD;
|
|
253
|
-
salienceUniformityFlagged = gini < SALIENCE_UNIFORMITY_GINI_THRESHOLD;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
catch {
|
|
257
|
-
// Table not present (pre-WS-1 install) — leave NaN.
|
|
258
|
-
}
|
|
259
|
-
// (b) Merge fidelity — fraction of consolidate accepted proposals in the window
|
|
260
|
-
// whose ref also has a consolidate skip-reason of "contradict_target_missing"
|
|
261
|
-
// or an event indicating contradiction. Uses the improve_runs result_json
|
|
262
|
-
// consolidation.contradicted count as a proxy.
|
|
263
|
-
// Simple implementation: contradictionRate = total_contradicted / max(1, total_processed)
|
|
264
|
-
// sourced from the window's consolidation envelope.
|
|
265
|
-
// (The full "merge proposal → later contradiction" correlation requires cross-run
|
|
266
|
-
// history; this is the available proxy.)
|
|
267
|
-
let mergeFidelityContradictionRate = 0;
|
|
268
|
-
try {
|
|
269
|
-
const runs = queryImproveRuns(db, since, until);
|
|
270
|
-
let totalContradicted = 0;
|
|
271
|
-
let totalProcessed = 0;
|
|
272
|
-
for (const row of runs) {
|
|
273
|
-
try {
|
|
274
|
-
const result = decodeImproveResult(row.result_json).envelope;
|
|
275
|
-
const cons = result.consolidation;
|
|
276
|
-
if (cons) {
|
|
277
|
-
totalContradicted += toFiniteNumber(cons.contradicted);
|
|
278
|
-
totalProcessed += toFiniteNumber(cons.processed);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
catch {
|
|
282
|
-
// Skip malformed rows.
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
if (totalProcessed > 0) {
|
|
286
|
-
mergeFidelityContradictionRate = roundRate(totalContradicted / totalProcessed);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
catch {
|
|
290
|
-
// Fail open.
|
|
291
|
-
}
|
|
292
|
-
// (c) highGenerationFraction was DELETED (meta-review 05 DRIFT-3): it
|
|
293
|
-
// approximated "LLM-merge generations" from consecutive_no_ops — which counts
|
|
294
|
-
// the opposite condition (cycles where nothing was changed) — and its own
|
|
295
|
-
// in-code TODO admitted the proxy. Display-only, never actionable; removed
|
|
296
|
-
// rather than instrumented.
|
|
297
|
-
// (d) Oracle spot-check — up to 5 recently accepted proposals in the window.
|
|
298
|
-
const oracleSpotCheck = [];
|
|
299
|
-
try {
|
|
300
|
-
const accepted = listStateProposals(db, { status: "accepted" }).filter((p) => {
|
|
301
|
-
const updatedAt = p.updatedAt ?? "";
|
|
302
|
-
return updatedAt >= since && updatedAt < until;
|
|
303
|
-
});
|
|
304
|
-
// Sample up to 5: pick evenly spaced (not just the first 5).
|
|
305
|
-
const step = Math.max(1, Math.floor(accepted.length / 5));
|
|
306
|
-
for (let i = 0; i < accepted.length && oracleSpotCheck.length < 5; i += step) {
|
|
307
|
-
const p = accepted[i];
|
|
308
|
-
if (p) {
|
|
309
|
-
oracleSpotCheck.push({
|
|
310
|
-
proposalId: p.id,
|
|
311
|
-
ref: p.ref,
|
|
312
|
-
source: p.source ?? "unknown",
|
|
313
|
-
acceptedAt: p.updatedAt ?? p.createdAt ?? "",
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
catch {
|
|
319
|
-
// Fail open.
|
|
320
|
-
}
|
|
321
|
-
return {
|
|
322
|
-
corpusCentroidDistance,
|
|
323
|
-
retrievalSalienceSampleSize,
|
|
324
|
-
entrenchmentFlagged,
|
|
325
|
-
salienceUniformityFlagged,
|
|
326
|
-
mergeFidelityContradictionRate,
|
|
327
|
-
oracleSpotCheck,
|
|
328
|
-
};
|
|
329
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
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
|
-
* The remaining `surfaces` advisory group for `akm health` (meta-review 08).
|
|
6
|
-
* `stash-git-exposure` (08-F1) shipped first in ./stash-exposure.ts; this
|
|
7
|
-
* module adds the other two read-only checks the adjudication approved:
|
|
8
|
-
*
|
|
9
|
-
* - `binary-config-skew` — config.json written by a NEWER akm than this binary (F3)
|
|
10
|
-
* - `egress-endpoints` — the remote-destination list, for eyeball diff (surfaces 3/9)
|
|
11
|
-
*
|
|
12
|
-
* Every collector is a pure projection over injected paths/config (no
|
|
13
|
-
* process.env reads) and is silent when there is nothing to report, matching
|
|
14
|
-
* the stash-exposure pattern. `egress-endpoints` is the one informational
|
|
15
|
-
* (pass-status) entry: it emits whenever any remote endpoint is configured.
|
|
16
|
-
*/
|
|
17
|
-
import { readTextFile } from "../../core/common.js";
|
|
18
|
-
import { CURRENT_CONFIG_VERSION } from "../../core/config/config-schema.js";
|
|
19
|
-
import { compareConfigVersion } from "../../core/config/config-version.js";
|
|
20
|
-
import { formatRegistryUrl } from "../../core/registry-url.js";
|
|
21
|
-
/**
|
|
22
|
-
* `binary-config-skew` (08-F3): warn when config.json carries a configVersion
|
|
23
|
-
* NEWER than (or unorderable against) this binary's CURRENT_CONFIG_VERSION —
|
|
24
|
-
* i.e. a newer/foreign akm wrote the shared config and this install is stale.
|
|
25
|
-
* That is the proven multi-install incident class. Silent for current or older
|
|
26
|
-
* versions and unreadable configs (config loading surfaces its own errors).
|
|
27
|
-
*/
|
|
28
|
-
export function collectConfigSkewAdvisory(configPath) {
|
|
29
|
-
let raw;
|
|
30
|
-
try {
|
|
31
|
-
raw = JSON.parse(readTextFile(configPath, "Config file"));
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
return undefined;
|
|
35
|
-
}
|
|
36
|
-
const onDisk = raw.configVersion;
|
|
37
|
-
const order = compareConfigVersion(onDisk, CURRENT_CONFIG_VERSION);
|
|
38
|
-
const skewed = order === 1 || (onDisk !== undefined && order === undefined);
|
|
39
|
-
if (!skewed)
|
|
40
|
-
return undefined;
|
|
41
|
-
return {
|
|
42
|
-
name: "binary-config-skew",
|
|
43
|
-
kind: "deterministic",
|
|
44
|
-
status: "warn",
|
|
45
|
-
confidence: "high",
|
|
46
|
-
message: `config.json has configVersion ${JSON.stringify(onDisk)} but this binary knows ${CURRENT_CONFIG_VERSION} — ` +
|
|
47
|
-
"a newer akm wrote the shared config, so this install is stale. Upgrade this install; do not keep a stale " +
|
|
48
|
-
"binary against the shared config/DBs.",
|
|
49
|
-
evidence: { onDiskConfigVersion: onDisk, binaryConfigVersion: CURRENT_CONFIG_VERSION },
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* `egress-endpoints` (08 surfaces 3/9): the full list of remote destinations
|
|
54
|
-
* akm can talk to under the effective config — registries, remote sources,
|
|
55
|
-
* LLM endpoints, embedding endpoint — as one pass-status informational entry
|
|
56
|
-
* for eyeball diff against expectations. Silent only when nothing remote is
|
|
57
|
-
* configured at all.
|
|
58
|
-
*/
|
|
59
|
-
export function collectEgressAdvisory(config) {
|
|
60
|
-
if (!config)
|
|
61
|
-
return undefined;
|
|
62
|
-
const endpoints = [];
|
|
63
|
-
for (const reg of config.registries ?? []) {
|
|
64
|
-
if (reg.enabled === false || !reg.url)
|
|
65
|
-
continue;
|
|
66
|
-
endpoints.push(`registry ${reg.name ?? "(unnamed)"}: ${formatRegistryUrl(reg.url)}`);
|
|
67
|
-
}
|
|
68
|
-
for (const [key, bundle] of Object.entries(config.bundles ?? {})) {
|
|
69
|
-
if (!bundle)
|
|
70
|
-
continue;
|
|
71
|
-
const url = bundle.git ?? bundle.website?.url;
|
|
72
|
-
if (!url)
|
|
73
|
-
continue;
|
|
74
|
-
endpoints.push(`source ${key} (${bundle.git ? "git" : "website"}): ${url}`);
|
|
75
|
-
}
|
|
76
|
-
for (const [name, engine] of Object.entries(config.engines ?? {})) {
|
|
77
|
-
if (engine?.kind !== "llm" || !engine.endpoint)
|
|
78
|
-
continue;
|
|
79
|
-
endpoints.push(`llm ${name}: ${engine.endpoint}`);
|
|
80
|
-
}
|
|
81
|
-
if (config.embedding?.endpoint)
|
|
82
|
-
endpoints.push(`embedding: ${config.embedding.endpoint}`);
|
|
83
|
-
if (endpoints.length === 0)
|
|
84
|
-
return undefined;
|
|
85
|
-
return {
|
|
86
|
-
name: "egress-endpoints",
|
|
87
|
-
kind: "deterministic",
|
|
88
|
-
status: "pass",
|
|
89
|
-
confidence: "high",
|
|
90
|
-
message: `${endpoints.length} remote endpoint(s) in the effective config (registries/sources/LLM/embedding) — ` +
|
|
91
|
-
"review the evidence list for unexpected destinations.",
|
|
92
|
-
evidence: { endpoints },
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Aggregate the two collectors into the advisories array shape `akmHealth`
|
|
97
|
-
* consumes. Order is fixed: skew → egress.
|
|
98
|
-
*/
|
|
99
|
-
export function collectSurfacesAdvisories(input) {
|
|
100
|
-
const results = [collectConfigSkewAdvisory(input.configPath), collectEgressAdvisory(input.config)];
|
|
101
|
-
return results.filter((r) => r !== undefined);
|
|
102
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
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
|
-
* WS-3b Step 8 — Anti-collapse merge guards.
|
|
6
|
-
*
|
|
7
|
-
* (a) Generation counter: merged.generation = max(sources)+1; merges cite
|
|
8
|
-
* sources. `over_generation_count` (collapse-detector.ts) tracks assets
|
|
9
|
-
* above the generation threshold as an advisory metric only — there is
|
|
10
|
-
* no merge-refusal path wired in.
|
|
11
|
-
* (b) Lexical-diversity check: low n-gram diversity ⇒ raise merge threshold.
|
|
12
|
-
* (d) Occasional random non-similar cluster in the pool.
|
|
13
|
-
*
|
|
14
|
-
* @module anti-collapse
|
|
15
|
-
*/
|
|
16
|
-
/** Default max generation depth before merge is refused. */
|
|
17
|
-
export const DEFAULT_MAX_GENERATION = 2;
|
|
18
|
-
/** Default fraction of pool to fill with random (non-similar) clusters. */
|
|
19
|
-
export const DEFAULT_RANDOM_CLUSTER_FRACTION = 0.05;
|
|
20
|
-
/**
|
|
21
|
-
* Read the `generation` field from an asset's frontmatter.
|
|
22
|
-
* Returns 0 when absent (no generation metadata = original asset).
|
|
23
|
-
*/
|
|
24
|
-
export function readAssetGeneration(frontmatterData) {
|
|
25
|
-
const gen = frontmatterData.generation;
|
|
26
|
-
if (typeof gen === "number" && Number.isFinite(gen) && gen >= 0) {
|
|
27
|
-
return Math.floor(gen);
|
|
28
|
-
}
|
|
29
|
-
return 0;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Compute the new generation for a merged asset.
|
|
33
|
-
* Rule: `merged.generation = max(source generations) + 1`.
|
|
34
|
-
*/
|
|
35
|
-
export function computeMergedGeneration(sourceGenerations) {
|
|
36
|
-
if (sourceGenerations.length === 0)
|
|
37
|
-
return 1;
|
|
38
|
-
return Math.max(...sourceGenerations) + 1;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Compute the bigram n-gram diversity of a text string.
|
|
42
|
-
* Returns a value in [0, 1] where 0 = all identical bigrams, 1 = all unique.
|
|
43
|
-
* Used by the lexical-diversity check to detect correlated-extraction artifacts.
|
|
44
|
-
*/
|
|
45
|
-
export function computeBigramDiversity(text) {
|
|
46
|
-
const words = text
|
|
47
|
-
.toLowerCase()
|
|
48
|
-
.split(/\s+/)
|
|
49
|
-
.filter((w) => w.length > 0);
|
|
50
|
-
if (words.length < 2)
|
|
51
|
-
return 1; // too short to have bigrams; treat as diverse
|
|
52
|
-
const total = words.length - 1;
|
|
53
|
-
const unique = new Set();
|
|
54
|
-
for (let i = 0; i < total; i++) {
|
|
55
|
-
unique.add(`${words[i]}\t${words[i + 1]}`);
|
|
56
|
-
}
|
|
57
|
-
return unique.size / total;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Check whether a cluster of memories exhibits suspiciously low lexical diversity.
|
|
61
|
-
* When true, the cluster is likely a correlated-extraction artifact; the merge
|
|
62
|
-
* threshold should be raised.
|
|
63
|
-
*
|
|
64
|
-
* @param bodies - The stripped body texts of the cluster members.
|
|
65
|
-
* @param config - Anti-collapse config.
|
|
66
|
-
* @returns `{ lowDiversity: true, diversity }` when the cluster diversity is
|
|
67
|
-
* below the 0.3 threshold; `{ lowDiversity: false }` otherwise.
|
|
68
|
-
*/
|
|
69
|
-
export function checkLexicalDiversity(bodies, config) {
|
|
70
|
-
// R5: default ON — only an explicit opt-out disables the check.
|
|
71
|
-
if (config.enabled === false || config.lexicalDiversityCheck === false) {
|
|
72
|
-
return { lowDiversity: false };
|
|
73
|
-
}
|
|
74
|
-
if (bodies.length === 0)
|
|
75
|
-
return { lowDiversity: false };
|
|
76
|
-
// Average bigram diversity across all bodies in the cluster.
|
|
77
|
-
const avg = bodies.reduce((sum, b) => sum + computeBigramDiversity(b), 0) / bodies.length;
|
|
78
|
-
const DIVERSITY_FLOOR = 0.3;
|
|
79
|
-
if (avg < DIVERSITY_FLOOR) {
|
|
80
|
-
return { lowDiversity: true, diversity: avg };
|
|
81
|
-
}
|
|
82
|
-
return { lowDiversity: false };
|
|
83
|
-
}
|