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,61 +1,22 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
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
|
-
/**
|
|
5
|
-
* The single, keyed-on-ref implementation of "is this a derived memory?" and
|
|
6
|
-
* "which parent does it derive from?" (R12).
|
|
7
|
-
*
|
|
8
|
-
* Two divergent copies previously lived side by side — the CONSUMER
|
|
9
|
-
* (`memory-improve.ts`, keyed on the memory name) and the PRODUCER
|
|
10
|
-
* (`memory-contradiction-detect.ts`, keyed on the file path). The producer's
|
|
11
|
-
* copy was strictly narrower: it ignored `derivedFrom` entirely and matched
|
|
12
|
-
* `source:` through a separate parser. That let the producer and consumer
|
|
13
|
-
* disagree on a memory's parent — the exact defect plan §6 calls out
|
|
14
|
-
* ("producer/consumer cannot disagree").
|
|
15
|
-
*
|
|
16
|
-
* Both sides now share this one impl, keyed on the memory NAME (the stash-
|
|
17
|
-
* relative path without the `.md` extension, e.g. `nested/foo.derived`). The
|
|
18
|
-
* producer converts its file path to a name via `toMemoryRef` before calling
|
|
19
|
-
* in. Adopting it on the producer side is an INTENTIONAL widening, pinned by
|
|
20
|
-
* `tests/commands/improve/derived-ref.test.ts`:
|
|
21
|
-
* - `derivedFrom`-keyed families now resolve a parent (and so participate in
|
|
22
|
-
* contradiction detection); and
|
|
23
|
-
* - `source:` is parsed through `parseRefInput` so current bundle-qualified
|
|
24
|
-
* refs resolve consistently.
|
|
25
|
-
*
|
|
26
|
-
* Resolution order (source → derivedFrom → `.derived` suffix) matches the
|
|
27
|
-
* consumer's prior behaviour exactly, so the consumer side is a pure move.
|
|
28
|
-
*/
|
|
4
|
+
/** The one implementation of "is this a derived memory, and what is its parent?" (R12), keyed on the memory name. */
|
|
29
5
|
import { conceptIdFromTypeName, parseRefInput } from "../../../core/asset/resolve-ref.js";
|
|
30
6
|
import { asNonEmptyString } from "../../../core/common.js";
|
|
31
7
|
import { DERIVED_SUFFIX } from "../../../core/recognition-util.js";
|
|
32
8
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* - `memory:<name>` — the internal identity spelling, and what belief edges
|
|
38
|
-
* written before 0.9.0 carry on disk;
|
|
39
|
-
* - `memories/<name>` / `<bundle>//memories/<name>` — the conceptId grammar
|
|
40
|
-
* the current writers emit (`akm remember --supersedes` and `akm import
|
|
41
|
-
* --supersedes` go through `writeSupersededEdge` with the write result's
|
|
42
|
-
* `ref`, which is fully qualified).
|
|
43
|
-
*
|
|
44
|
-
* Accepting only the first spelling silently reduced every edge written by the
|
|
45
|
-
* current code path to nothing, so a superseded memory read back as active.
|
|
46
|
-
* Both normalize to the bare name; the caller re-mints the internal
|
|
47
|
-
* `memory:<name>` form via {@link memoryIdentityRef}, so there is still exactly
|
|
48
|
-
* one identity spelling downstream.
|
|
9
|
+
* A belief edge (`supersededBy` / `contradictedBy` / `currentBeliefRefs`) as a
|
|
10
|
+
* bare memory name. Both spellings occur: `memory:<name>` (pre-0.9 edges) and
|
|
11
|
+
* `[bundle//]memories/<name>` (what `--supersedes` writes today); accepting only
|
|
12
|
+
* the first once read every superseded memory back as active.
|
|
49
13
|
*/
|
|
50
14
|
export function parseMemoryName(value) {
|
|
51
15
|
if (!value)
|
|
52
16
|
return undefined;
|
|
53
17
|
const trimmed = value.trim();
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const name = trimmed.slice(MEMORY_PREFIX.length);
|
|
57
|
-
return name.length > 0 ? name : undefined;
|
|
58
|
-
}
|
|
18
|
+
if (trimmed.startsWith("memory:"))
|
|
19
|
+
return trimmed.slice("memory:".length) || undefined;
|
|
59
20
|
try {
|
|
60
21
|
const parsed = parseRefInput(trimmed);
|
|
61
22
|
return parsed.type === "memory" && parsed.name.length > 0 ? parsed.name : undefined;
|
|
@@ -64,10 +25,7 @@ export function parseMemoryName(value) {
|
|
|
64
25
|
return undefined;
|
|
65
26
|
}
|
|
66
27
|
}
|
|
67
|
-
/**
|
|
68
|
-
* Parse a current `source:` backref and return its canonical memory conceptId,
|
|
69
|
-
* or `undefined` when it is empty, invalid, or not a memory ref.
|
|
70
|
-
*/
|
|
28
|
+
/** A `source:` backref as a memory conceptId, or `undefined` when it is not a memory ref. */
|
|
71
29
|
export function parseMemoryRef(value) {
|
|
72
30
|
if (!value)
|
|
73
31
|
return undefined;
|
|
@@ -79,37 +37,15 @@ export function parseMemoryRef(value) {
|
|
|
79
37
|
return undefined;
|
|
80
38
|
}
|
|
81
39
|
}
|
|
82
|
-
/**
|
|
83
|
-
* Format a bare memory name into the belief-edge IDENTITY channel ref
|
|
84
|
-
* `memory:<name>` — the ONE implementation of that spelling.
|
|
85
|
-
*
|
|
86
|
-
* Belief-edge / identity
|
|
87
|
-
* channel (`contradictedBy` / `supersededBy` / `currentBeliefRefs`, and a derived
|
|
88
|
-
* memory's own `record.ref`) uses `memory:<name>` and is compared against a
|
|
89
|
-
* derived memory's identity ref. This channel is separate from `source:` asset
|
|
90
|
-
* refs, which use `memories/<name>`. Both `memory-improve.ts` (`refArray`) and
|
|
91
|
-
* `memory-contradiction-detect.ts` (`toMemoryRef`) emit through here so the
|
|
92
|
-
* identity spelling has one implementation.
|
|
93
|
-
*/
|
|
40
|
+
/** The belief-edge identity spelling, `memory:<name>` (separate from `source:` asset refs). */
|
|
94
41
|
export function memoryIdentityRef(name) {
|
|
95
42
|
return `memory:${name}`;
|
|
96
43
|
}
|
|
97
|
-
/**
|
|
98
|
-
* True when the named memory is a derived/inferred child — either it carries
|
|
99
|
-
* `inferred: true` in its frontmatter or its name ends with the structural
|
|
100
|
-
* `.derived` suffix.
|
|
101
|
-
*/
|
|
44
|
+
/** Inferred (`inferred: true`) or named with the `.derived` suffix. */
|
|
102
45
|
export function isDerivedMemory(name, frontmatter) {
|
|
103
46
|
return frontmatter.inferred === true || name.endsWith(DERIVED_SUFFIX);
|
|
104
47
|
}
|
|
105
|
-
/**
|
|
106
|
-
* Resolve the parent (source) memory ref for a derived memory as a canonical
|
|
107
|
-
* `memories/<name>` conceptId, or `undefined` when none
|
|
108
|
-
* can be determined. Precedence:
|
|
109
|
-
* 1. `frontmatter.source` (normalised through {@link parseMemoryRef});
|
|
110
|
-
* 2. `frontmatter.derivedFrom` (a bare memory name → `memories/<name>`);
|
|
111
|
-
* 3. the `.derived` name suffix, stripped → `memories/<name>`.
|
|
112
|
-
*/
|
|
48
|
+
/** A derived memory's parent conceptId: from `source`, else `derivedFrom`, else the name minus `.derived`. */
|
|
113
49
|
export function resolveParentRef(name, frontmatter) {
|
|
114
50
|
const fromSource = parseMemoryRef(asNonEmptyString(frontmatter.source));
|
|
115
51
|
if (fromSource)
|
|
@@ -117,8 +53,7 @@ export function resolveParentRef(name, frontmatter) {
|
|
|
117
53
|
const derivedFrom = asNonEmptyString(frontmatter.derivedFrom);
|
|
118
54
|
if (derivedFrom)
|
|
119
55
|
return conceptIdFromTypeName("memory", derivedFrom);
|
|
120
|
-
if (name.endsWith(DERIVED_SUFFIX))
|
|
56
|
+
if (name.endsWith(DERIVED_SUFFIX))
|
|
121
57
|
return conceptIdFromTypeName("memory", name.slice(0, -DERIVED_SUFFIX.length));
|
|
122
|
-
}
|
|
123
58
|
return undefined;
|
|
124
59
|
}
|
|
@@ -2,141 +2,37 @@
|
|
|
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
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* #
|
|
12
|
-
*
|
|
13
|
-
* The 4-state belief lifecycle (active → superseded | contradicted → archived)
|
|
14
|
-
* was previously encoded only in `memory-improve.ts`. `akmConsolidate` used a
|
|
15
|
-
* flat merge/delete/promote model with no belief states, causing the two engines
|
|
16
|
-
* to diverge. This module:
|
|
17
|
-
*
|
|
18
|
-
* 1. Re-exports the belief-state types from `memory-improve.ts` so callers
|
|
19
|
-
* can import from one canonical location.
|
|
20
|
-
* 2. Provides `writeContradictEdge` — a shared primitive that both engines
|
|
21
|
-
* use when an LLM (or heuristic) identifies a contradiction between two
|
|
22
|
-
* memories. This is the bridge between `akmConsolidate`'s LLM-detected
|
|
23
|
-
* contradictions and `resolveFamilyContradictions`' SCC resolver.
|
|
24
|
-
*
|
|
25
|
-
* # References
|
|
26
|
-
*
|
|
27
|
-
* - Zep / Graphiti (arXiv:2501.13956 §3) — unified belief-revision pipeline
|
|
28
|
-
* - MemOS (arXiv:2507.03724) — formal archive/merge/transition with shared state model
|
|
5
|
+
* Belief-state edge writer (#382): append a `supersededBy` edge to an asset's
|
|
6
|
+
* frontmatter and demote its `beliefState`, metadata only. Idempotent (a
|
|
7
|
+
* present edge AND demotion is a no-op; an edge without its demotion is
|
|
8
|
+
* repaired) and never weakens a stronger demotion — severity is
|
|
9
|
+
* superseded > contradicted > archived, per the ranker's `beliefStateBoost`.
|
|
10
|
+
* The SCC resolver in memory-improve.ts is a state-transition writer (it
|
|
11
|
+
* replaces and clears edges) and deliberately does not use this (#885).
|
|
29
12
|
*/
|
|
30
13
|
import { mutateFrontmatter } from "../../../core/asset/frontmatter.js";
|
|
31
|
-
// ── Shared edge-list reader ───────────────────────────────────────────────────
|
|
32
14
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* Scalar edges are LIVE data: the indexer's `normalizeNonEmptyStringList`
|
|
37
|
-
* accepts them and lint deliberately never flags them. An Array.isArray-only
|
|
38
|
-
* read treats a scalar as "no existing edges" and silently destroys the edge
|
|
39
|
-
* on the next merge — mirror `mergeXrefsIntoContent`'s scalar promotion
|
|
40
|
-
* instead.
|
|
15
|
+
* An edge value as a list. A scalar string is live data (the indexer accepts
|
|
16
|
+
* it), so it is promoted rather than dropped on the next write.
|
|
41
17
|
*/
|
|
42
18
|
function readEdgeList(value) {
|
|
43
|
-
if (Array.isArray(value))
|
|
19
|
+
if (Array.isArray(value))
|
|
44
20
|
return value.filter((v) => typeof v === "string" && v.trim().length > 0);
|
|
45
|
-
}
|
|
46
21
|
if (typeof value === "string" && value.trim())
|
|
47
22
|
return [value.trim()];
|
|
48
23
|
return [];
|
|
49
24
|
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Write `contradictedBy` and `beliefState: contradicted` edges to a memory
|
|
53
|
-
* file's frontmatter (C-3 / #382).
|
|
54
|
-
*
|
|
55
|
-
* The shared primitive for APPENDING one contradiction edge. Used by
|
|
56
|
-
* `memory-contradiction-detect.ts`'s automated contradiction pass.
|
|
57
|
-
*
|
|
58
|
-
* NOT used by `persistBeliefStateTransition` in `memory-improve.ts`, and that
|
|
59
|
-
* is deliberate (#885): the SCC resolver is a state-TRANSITION writer, not an
|
|
60
|
-
* edge appender. It replaces `contradictedBy` wholesale from recomputed
|
|
61
|
-
* `currentBeliefRefs`, deletes the key when a memory transitions away from
|
|
62
|
-
* `contradicted`, and moves a memory to any target state including back to
|
|
63
|
-
* active. An append-only primitive that never weakens a demotion cannot
|
|
64
|
-
* express those, and routing it through this would break the resolver's
|
|
65
|
-
* ability to clear an edge.
|
|
66
|
-
*
|
|
67
|
-
* Idempotent: if the `contradictedByRef` is already in `contradictedBy` AND
|
|
68
|
-
* the file already carries the demotion state, the file is not rewritten. The
|
|
69
|
-
* guard is state-aware like its sibling {@link writeSupersededEdge}: an edge
|
|
70
|
-
* present WITHOUT the demotion (e.g. a hand-written `contradictedBy:` line,
|
|
71
|
-
* or a beliefState lost to a partial edit) is repaired, not skipped — an
|
|
72
|
-
* edge-only guard would make such a file a permanent no-op while
|
|
73
|
-
* contradiction writers report the contradiction as applied.
|
|
74
|
-
*
|
|
75
|
-
* Never weakens a stronger demotion: `archived` ranks BELOW `contradicted`
|
|
76
|
-
* (see `BELIEF_STATE_SCORE_CEILINGS` in
|
|
77
|
-
* src/indexer/search/ranking-contributors.ts), so contradicting an archived
|
|
78
|
-
* memory keeps `archived` and only appends the edge.
|
|
79
|
-
*
|
|
80
|
-
* @param filePath - Absolute path to the memory markdown file.
|
|
81
|
-
* @param contradictedByRef - The ref that contradicts this memory.
|
|
82
|
-
* @returns `true` when the file was rewritten, `false` when the edge and the
|
|
83
|
-
* demotion were already present (the idempotent no-op). Callers count
|
|
84
|
-
* edges written from this.
|
|
85
|
-
*/
|
|
86
|
-
export function writeContradictEdge(filePath, contradictedByRef) {
|
|
87
|
-
return mutateFrontmatter(filePath, (parsed) => {
|
|
88
|
-
const existing = readEdgeList(parsed.data.contradictedBy);
|
|
89
|
-
const currentState = parsed.data.beliefState;
|
|
90
|
-
const nextState = currentState === "archived" ? currentState : "contradicted";
|
|
91
|
-
if (existing.includes(contradictedByRef) && currentState === nextState) {
|
|
92
|
-
return null; // Already written — idempotent.
|
|
93
|
-
}
|
|
94
|
-
const nextContradictedBy = [...new Set([...existing, contradictedByRef])].sort();
|
|
95
|
-
return {
|
|
96
|
-
...parsed.data,
|
|
97
|
-
contradictedBy: nextContradictedBy,
|
|
98
|
-
beliefState: nextState,
|
|
99
|
-
};
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
// ── Supersession edge writer ─────────────────────────────────────────────────
|
|
103
|
-
/**
|
|
104
|
-
* Write `supersededBy` and `beliefState: superseded` edges to an asset file's
|
|
105
|
-
* frontmatter (SPEC-5, stash-conventions-code-spec.md).
|
|
106
|
-
*
|
|
107
|
-
* Sibling of {@link writeContradictEdge}: the shared primitive for the
|
|
108
|
-
* conventions' corrections pattern — when a new asset supersedes an old one,
|
|
109
|
-
* the old asset gets a METADATA-ONLY demotion edit (every other frontmatter
|
|
110
|
-
* key and the body are preserved) so the ranker's beliefStateBoost demotes the
|
|
111
|
-
* stale incumbent and `--belief current` hides it. Used by
|
|
112
|
-
* `akm remember --supersedes` / `akm import --supersedes`.
|
|
113
|
-
*
|
|
114
|
-
* Idempotent: if `supersededByRef` is already in `supersededBy` and the file
|
|
115
|
-
* is already demoted, the file is not rewritten. Multiple corrections
|
|
116
|
-
* sorted-set-append their refs.
|
|
117
|
-
*
|
|
118
|
-
* Never WEAKENS an existing demotion: `contradicted` and `archived` rank
|
|
119
|
-
* BELOW `superseded` (severity order deprecated > superseded > contradicted >
|
|
120
|
-
* archived — see `BELIEF_STATE_SCORE_CEILINGS` in
|
|
121
|
-
* src/indexer/search/ranking-contributors.ts), so superseding an already
|
|
122
|
-
* contradicted/archived asset keeps the stronger state and only appends the
|
|
123
|
-
* `supersededBy` edge.
|
|
124
|
-
*
|
|
125
|
-
* @param filePath - Absolute path to the asset markdown file.
|
|
126
|
-
* @param supersededByRef - The ref of the correction that supersedes this asset.
|
|
127
|
-
*/
|
|
25
|
+
/** Mark an asset superseded by a correction (`akm remember|import --supersedes`). */
|
|
128
26
|
export function writeSupersededEdge(filePath, supersededByRef) {
|
|
129
27
|
mutateFrontmatter(filePath, (parsed) => {
|
|
130
28
|
const existing = readEdgeList(parsed.data.supersededBy);
|
|
131
29
|
const currentState = parsed.data.beliefState;
|
|
132
30
|
const nextState = currentState === "contradicted" || currentState === "archived" ? currentState : "superseded";
|
|
133
|
-
if (existing.includes(supersededByRef) && currentState === nextState)
|
|
134
|
-
return null;
|
|
135
|
-
}
|
|
136
|
-
const nextSupersededBy = [...new Set([...existing, supersededByRef])].sort();
|
|
31
|
+
if (existing.includes(supersededByRef) && currentState === nextState)
|
|
32
|
+
return null;
|
|
137
33
|
return {
|
|
138
34
|
...parsed.data,
|
|
139
|
-
supersededBy:
|
|
35
|
+
supersededBy: [...new Set([...existing, supersededByRef])].sort(),
|
|
140
36
|
beliefState: nextState,
|
|
141
37
|
};
|
|
142
38
|
});
|
|
@@ -12,6 +12,7 @@ import { DERIVED_SUFFIX } from "../../../core/recognition-util.js";
|
|
|
12
12
|
import { warn } from "../../../core/warn.js";
|
|
13
13
|
import { recordWrittenPath } from "../../../core/write-provenance.js";
|
|
14
14
|
import { walkMarkdownFiles } from "../../../indexer/walk/walker.js";
|
|
15
|
+
import { contentHash } from "../content-hash.js";
|
|
15
16
|
import { isDerivedMemory, memoryIdentityRef, parseMemoryName, resolveParentRef } from "./derived-ref.js";
|
|
16
17
|
export function analyzeMemoryCleanup(stashDir, options = {}) {
|
|
17
18
|
const records = collectDerivedMemories(stashDir, options.parentRef);
|
|
@@ -676,7 +677,7 @@ function resolveBeliefState(frontmatter) {
|
|
|
676
677
|
// firstExistingRef's byRef map), so they are NORMALIZED to `memory:<name>` here.
|
|
677
678
|
// On disk they arrive in either spelling — `memory:<name>` from pre-0.9.0
|
|
678
679
|
// writes, or the `[<bundle>//]memories/<name>` conceptId that
|
|
679
|
-
// `writeSupersededEdge
|
|
680
|
+
// `writeSupersededEdge` persists today — and
|
|
680
681
|
// `parseMemoryName` accepts both. Reading only the first spelling silently
|
|
681
682
|
// dropped every edge the current write path produces.
|
|
682
683
|
function refArray(value) {
|
|
@@ -697,13 +698,13 @@ function refArray(value) {
|
|
|
697
698
|
return [...refs].sort();
|
|
698
699
|
}
|
|
699
700
|
function buildFingerprint(title, description, tags, searchHints, body) {
|
|
700
|
-
return JSON.stringify({
|
|
701
|
+
return contentHash(JSON.stringify({
|
|
701
702
|
title: normalizeSignal(title),
|
|
702
703
|
description: normalizeSignal(description),
|
|
703
704
|
tags: normalizeList(tags),
|
|
704
705
|
searchHints: normalizeList(searchHints),
|
|
705
706
|
body: normalizeBody(body),
|
|
706
|
-
});
|
|
707
|
+
}));
|
|
707
708
|
}
|
|
708
709
|
function normalizeBody(value) {
|
|
709
710
|
return value
|
|
@@ -2,85 +2,37 @@
|
|
|
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 { getAllAssetOutcomes, getAssetOutcome, getOutcomeScoresByRef, upsertAssetOutcome, } from "../../storage/repositories/outcome-repository.js";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* EMA decay factor for the expected-retrieval rolling mean (α).
|
|
8
|
-
* New expected = α × new_count + (1−α) × old_expected.
|
|
9
|
-
* At α = 0.3 the window is ≈ 3 cycles.
|
|
10
|
-
*/
|
|
5
|
+
/** EMA factor for both the score and the expected retrieval delta (≈ a 3-cycle window). */
|
|
11
6
|
export const OUTCOME_EMA_ALPHA = 0.3;
|
|
12
|
-
/**
|
|
13
|
-
* Warm-start cap: the maximum `outcome_score` a brand-new row can be seeded with
|
|
14
|
-
* (from the utility EMA). Prevents a `[0,1]`-range utility value from generating
|
|
15
|
-
* a score that the first negative delta could catastrophically invert. Clipped to
|
|
16
|
-
* the maximum plausible first differential update: `max(0, 1 − 0) = 1`, so cap
|
|
17
|
-
* at a modest 0.3 to avoid spurious rank-flip on first cycle.
|
|
18
|
-
*/
|
|
7
|
+
/** The most a new row can be seeded with, so the first negative delta cannot flip the rank. */
|
|
19
8
|
export const WARM_START_CAP = 0.3;
|
|
20
|
-
/**
|
|
21
|
-
* Penalty cap: the minimum outcome_score is capped at this value so a single
|
|
22
|
-
* very-negative run can't send the score to −∞.
|
|
23
|
-
*/
|
|
24
9
|
export const OUTCOME_SCORE_MIN = -1.0;
|
|
25
|
-
/**
|
|
26
|
-
* Saturation ceiling: the maximum outcome_score. Biological RPE saturates —
|
|
27
|
-
* a fully predicted reward produces zero response, not an ever-growing one —
|
|
28
|
-
* so a long-lived popular asset must not accrue unbounded outcome mass that
|
|
29
|
-
* would dominate ranking once the outcome weight is enabled (analysis G2).
|
|
30
|
-
* 1.5 comfortably exceeds the max plausible single-cycle raw update while
|
|
31
|
-
* keeping the normalised outcomeSalience spread meaningful.
|
|
32
|
-
*/
|
|
10
|
+
/** Saturation: a long-popular asset must not accrue unbounded outcome mass. */
|
|
33
11
|
export const OUTCOME_SCORE_MAX = 1.5;
|
|
34
|
-
/**
|
|
35
|
-
* Diversity floor: `outcomeSalience` for any asset is at least this fraction
|
|
36
|
-
* of the maximum observed `outcome_score` in the table, so rare-but-correct
|
|
37
|
-
* assets cannot be permanently outcompeted. 0 = disabled (pure competition).
|
|
38
|
-
*/
|
|
12
|
+
/** `outcomeSalience` never drops below this fraction of the stash-wide maximum. */
|
|
39
13
|
export const DIVERSITY_FLOOR_FRACTION = 0.1;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
* the live writer both call this function, so rank inputs cannot drift merely
|
|
43
|
-
* because one caller persists the projection and the other does not.
|
|
44
|
-
*/
|
|
14
|
+
export { getAllAssetOutcomes, getAssetOutcome, getOutcomeScoresByRef };
|
|
15
|
+
/** The next outcome values, without storage — dry planning and the writer share it. */
|
|
45
16
|
export function projectAssetOutcome(existing, inputs) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
outcomeScore = seedScore;
|
|
53
|
-
expectedRetrievalRate = 0;
|
|
17
|
+
if (existing === undefined) {
|
|
18
|
+
return {
|
|
19
|
+
outcomeScore: Math.min(WARM_START_CAP, Math.max(0, inputs.utilityScore ?? 0)),
|
|
20
|
+
expectedRetrievalRate: 0,
|
|
21
|
+
isNewRow: true,
|
|
22
|
+
};
|
|
54
23
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
outcomeScore = Math.min(OUTCOME_SCORE_MAX, Math.max(OUTCOME_SCORE_MIN, newScore));
|
|
64
|
-
}
|
|
65
|
-
return { outcomeScore, expectedRetrievalRate, isNewRow };
|
|
24
|
+
const retrievalDelta = Math.max(0, inputs.currentRetrievalCount - existing.retrieval_count);
|
|
25
|
+
const rawUpdate = retrievalDelta - existing.expected_retrieval_rate + (inputs.valence ?? 0);
|
|
26
|
+
const newScore = OUTCOME_EMA_ALPHA * rawUpdate + (1 - OUTCOME_EMA_ALPHA) * existing.outcome_score;
|
|
27
|
+
return {
|
|
28
|
+
outcomeScore: Math.min(OUTCOME_SCORE_MAX, Math.max(OUTCOME_SCORE_MIN, newScore)),
|
|
29
|
+
expectedRetrievalRate: OUTCOME_EMA_ALPHA * retrievalDelta + (1 - OUTCOME_EMA_ALPHA) * existing.expected_retrieval_rate,
|
|
30
|
+
isNewRow: false,
|
|
31
|
+
};
|
|
66
32
|
}
|
|
67
|
-
/**
|
|
68
|
-
* Upsert one asset's outcome row.
|
|
69
|
-
*
|
|
70
|
-
* On first call for a ref (no prior row): warm-starts from `utilityScore`.
|
|
71
|
-
* On subsequent calls: applies the differential update formula.
|
|
72
|
-
*
|
|
73
|
-
* Returns the resulting `outcome_score` so the caller can pass it to
|
|
74
|
-
* `computeSalience` without a second read.
|
|
75
|
-
*/
|
|
33
|
+
/** Upsert one asset's outcome row; returns the score for the salience vector. */
|
|
76
34
|
export function updateAssetOutcome(db, inputs) {
|
|
77
|
-
const
|
|
78
|
-
const existing = getAssetOutcome(db, inputs.ref);
|
|
79
|
-
const projection = projectAssetOutcome(existing, inputs);
|
|
80
|
-
// Upsert the row. See `upsertAssetOutcome` in
|
|
81
|
-
// storage/repositories/outcome-repository.ts (#672 part 2) for the SQL text
|
|
82
|
-
// and the `review_pressure` omission rationale — this call site is
|
|
83
|
-
// unchanged in intent, just no longer inline.
|
|
35
|
+
const projection = projectAssetOutcome(getAssetOutcome(db, inputs.ref), inputs);
|
|
84
36
|
upsertAssetOutcome(db, {
|
|
85
37
|
ref: inputs.ref,
|
|
86
38
|
lastRetrievedAt: inputs.lastRetrievedAt,
|
|
@@ -89,97 +41,17 @@ export function updateAssetOutcome(db, inputs) {
|
|
|
89
41
|
negativeFeedbackCount: inputs.negativeFeedbackCount,
|
|
90
42
|
acceptedChangeCount: inputs.acceptedChangeCount,
|
|
91
43
|
outcomeScore: projection.outcomeScore,
|
|
92
|
-
updatedAt: now,
|
|
44
|
+
updatedAt: inputs.now ?? Date.now(),
|
|
93
45
|
});
|
|
94
46
|
return { outcomeScore: projection.outcomeScore, isNewRow: projection.isNewRow };
|
|
95
47
|
}
|
|
96
|
-
// ── Reader ────────────────────────────────────────────────────────────────────
|
|
97
|
-
//
|
|
98
|
-
// getAssetOutcome / getAllAssetOutcomes / getOutcomeScoresByRef moved verbatim
|
|
99
|
-
// to storage/repositories/outcome-repository.ts (#672 part 2) — re-exported
|
|
100
|
-
// here so existing importers of this module resolve unchanged.
|
|
101
|
-
export { getAllAssetOutcomes, getAssetOutcome, getOutcomeScoresByRef };
|
|
102
|
-
// ── outcomeSalience projection ────────────────────────────────────────────────
|
|
103
48
|
/**
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* Approach:
|
|
108
|
-
* 1. Clip negative scores to 0 (a negative outcome just means "below expected",
|
|
109
|
-
* not "irrelevant"; it should not harm the base retrieval/encoding ranking).
|
|
110
|
-
* 2. Apply the diversity floor so rare-but-correct assets always retain a
|
|
111
|
-
* minimum `outcomeSalience` relative to the stash-wide maximum.
|
|
112
|
-
* 3. Normalise by `maxScore` (the stash-wide max outcome_score) so the term
|
|
113
|
-
* lives in [0, 1]. When maxScore ≤ 0 (all seeds, nothing observed yet),
|
|
114
|
-
* return the floor (or 0 if DIVERSITY_FLOOR_FRACTION = 0).
|
|
115
|
-
*
|
|
116
|
-
* @param outcomeScore - Raw outcome_score from asset_outcome.
|
|
117
|
-
* @param maxScore - Maximum outcome_score across ALL rows (≥ 0 after clip).
|
|
118
|
-
* Callers must compute this once per batch and pass it in.
|
|
49
|
+
* A raw outcome score as salience in [0, 1]: negatives clip to 0 (below
|
|
50
|
+
* expected is not irrelevant), normalized by the stash-wide maximum, never
|
|
51
|
+
* below the diversity floor.
|
|
119
52
|
*/
|
|
120
53
|
export function outcomeScoreToSalience(outcomeScore, maxScore) {
|
|
121
|
-
|
|
122
|
-
if (maxScore <= 0) {
|
|
123
|
-
// No positive scores observed yet — return diversity floor.
|
|
54
|
+
if (maxScore <= 0)
|
|
124
55
|
return DIVERSITY_FLOOR_FRACTION;
|
|
125
|
-
|
|
126
|
-
const normalised = clipped / maxScore;
|
|
127
|
-
// Apply diversity floor.
|
|
128
|
-
return Math.max(DIVERSITY_FLOOR_FRACTION, normalised);
|
|
129
|
-
}
|
|
130
|
-
// ── Proxy-adequacy tripwire ───────────────────────────────────────────────────
|
|
131
|
-
/**
|
|
132
|
-
* Dead-proxy threshold: |corr| below this means outcome_score carries no
|
|
133
|
-
* information about improvement need (pure noise).
|
|
134
|
-
*/
|
|
135
|
-
export const PROXY_DEAD_CORR_THRESHOLD = 0.1;
|
|
136
|
-
/**
|
|
137
|
-
* Minimum sample size before the dead-proxy check fires. Below this, a
|
|
138
|
-
* near-zero correlation is indistinguishable from small-sample noise.
|
|
139
|
-
*/
|
|
140
|
-
export const PROXY_DEAD_MIN_N = 500;
|
|
141
|
-
/**
|
|
142
|
-
* Compute `corr(outcome_score, accepted_change_rate)` across all asset_outcome
|
|
143
|
-
* rows. Returns `{correlation: NaN, n, isInverted: false}` when there is
|
|
144
|
-
* insufficient data (fewer than 3 rows or zero variance in either variable).
|
|
145
|
-
*
|
|
146
|
-
* A NEGATIVE correlation means: assets with a HIGH outcome_score have a LOW
|
|
147
|
-
* accepted_change_rate — i.e. the assets the proxy rates as "doing well"
|
|
148
|
-
* (frequently retrieved) are precisely the ones that rarely yield an accepted
|
|
149
|
-
* improvement. That inverts the proxy: high outcome_score ≠ "doing well", so the
|
|
150
|
-
* coarse retrieval-delta signal is no longer trustworthy and the 0.10+ rich
|
|
151
|
-
* signal is due. (`isInverted = correlation < -0.3`.)
|
|
152
|
-
*/
|
|
153
|
-
export function computeProxyAdequacy(rows) {
|
|
154
|
-
const n = rows.length;
|
|
155
|
-
if (n < 3)
|
|
156
|
-
return { correlation: Number.NaN, n, isInverted: false, isDead: false };
|
|
157
|
-
// accepted_change_rate per row.
|
|
158
|
-
const xs = rows.map((r) => r.outcome_score);
|
|
159
|
-
const ys = rows.map((r) => r.accepted_change_count / Math.max(1, r.retrieval_count));
|
|
160
|
-
const meanX = xs.reduce((a, b) => a + b, 0) / n;
|
|
161
|
-
const meanY = ys.reduce((a, b) => a + b, 0) / n;
|
|
162
|
-
let covXY = 0;
|
|
163
|
-
let varX = 0;
|
|
164
|
-
let varY = 0;
|
|
165
|
-
for (let i = 0; i < n; i++) {
|
|
166
|
-
const dx = xs[i] - meanX;
|
|
167
|
-
const dy = ys[i] - meanY;
|
|
168
|
-
covXY += dx * dy;
|
|
169
|
-
varX += dx * dx;
|
|
170
|
-
varY += dy * dy;
|
|
171
|
-
}
|
|
172
|
-
covXY /= n;
|
|
173
|
-
varX /= n;
|
|
174
|
-
varY /= n;
|
|
175
|
-
const denom = Math.sqrt(varX) * Math.sqrt(varY);
|
|
176
|
-
if (denom < 1e-12)
|
|
177
|
-
return { correlation: Number.NaN, n, isInverted: false, isDead: false };
|
|
178
|
-
const correlation = covXY / denom;
|
|
179
|
-
// Inverted proxy: negative correlation between outcome and accepted_change_rate
|
|
180
|
-
// means high-outcome assets are also high-need — the opposite of "useful".
|
|
181
|
-
const isInverted = correlation < -0.3;
|
|
182
|
-
// Dead proxy: near-zero correlation at scale — the score is noise.
|
|
183
|
-
const isDead = n >= PROXY_DEAD_MIN_N && Math.abs(correlation) < PROXY_DEAD_CORR_THRESHOLD;
|
|
184
|
-
return { correlation, n, isInverted, isDead };
|
|
56
|
+
return Math.max(DIVERSITY_FLOOR_FRACTION, Math.max(0, outcomeScore) / maxScore);
|
|
185
57
|
}
|
|
@@ -42,10 +42,7 @@ export function projectMemoryCleanup(input) {
|
|
|
42
42
|
},
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
|
-
/**
|
|
46
|
-
* Apply the final global cap to an already-ranked candidate snapshot.
|
|
47
|
-
* Replay remains additive to the ordinary cap, matching the live #610 rule.
|
|
48
|
-
*/
|
|
45
|
+
/** Apply the final global cap to an already-ranked candidate snapshot. */
|
|
49
46
|
export function selectEffectiveImproveRefs(args) {
|
|
50
47
|
const distillOnlySet = new Set(args.distillOnlyRefs.map((entry) => entry.ref));
|
|
51
48
|
const reflectAndDistill = args.rankedRefs.filter((entry) => !distillOnlySet.has(entry.ref));
|
|
@@ -53,10 +50,7 @@ export function selectEffectiveImproveRefs(args) {
|
|
|
53
50
|
// Preserve the established live ordering: ordinary reflect-path refs first,
|
|
54
51
|
// then distill-only refs, with the rank order stable inside each partition.
|
|
55
52
|
const allLoopRefs = [...reflectAndDistill, ...distillOnly];
|
|
56
|
-
const
|
|
57
|
-
const ordinary = allLoopRefs.filter((entry) => entry.eligibilitySource !== "replay");
|
|
58
|
-
const selectedOrdinary = args.limit === undefined ? ordinary : ordinary.slice(0, args.limit);
|
|
59
|
-
const loopRefs = [...selectedOrdinary, ...replay.slice(0, args.replayBudget)];
|
|
53
|
+
const loopRefs = args.limit === undefined ? allLoopRefs : allLoopRefs.slice(0, args.limit);
|
|
60
54
|
return {
|
|
61
55
|
loopRefs,
|
|
62
56
|
distillOnlyRefs: distillOnly,
|
|
@@ -82,8 +76,9 @@ export function buildImproveExecutionPlan(input) {
|
|
|
82
76
|
limits: {
|
|
83
77
|
configured: { ...input.configuredLimits },
|
|
84
78
|
...(input.effectiveLimit !== undefined ? { effective: input.effectiveLimit } : {}),
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
// The replay lane is gone; the published plan keeps a zero allowance.
|
|
80
|
+
additiveReplayAllowance: 0,
|
|
81
|
+
...(input.effectiveLimit !== undefined ? { totalCeiling: input.effectiveLimit } : {}),
|
|
87
82
|
},
|
|
88
83
|
gates: input.gates.map((gate) => ({ ...gate })),
|
|
89
84
|
effectiveRefs,
|