akm-cli 0.9.17-alpha.3 → 0.9.17-alpha.5
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 +760 -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/lint/base-linter.js +19 -5
- 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 +4 -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/source-manage.js +9 -2
- 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/asset-placement.js +4 -13
- 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 +43940 -93320
- package/dist/scripts/akm-migrate.js +43700 -93078
- 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 +361 -751
- 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 -441
- 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
|
@@ -7,24 +7,17 @@ import { decodeTaskHistoryMetadata } from "../../storage/repositories/task-histo
|
|
|
7
7
|
export function roundRate(value) {
|
|
8
8
|
return Number(value.toFixed(4));
|
|
9
9
|
}
|
|
10
|
-
export function parseTaskMetadata(row) {
|
|
11
|
-
const metadata = decodeTaskHistoryMetadata(row.metadata_json);
|
|
12
|
-
return {
|
|
13
|
-
...(metadata.durationMs !== undefined ? { durationMs: metadata.durationMs } : {}),
|
|
14
|
-
...(metadata.detail ? { detail: metadata.detail } : {}),
|
|
15
|
-
...(metadata.engine !== undefined ? { engine: metadata.engine } : {}),
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
10
|
/**
|
|
19
|
-
* `
|
|
20
|
-
* `listStateProposals`).
|
|
21
|
-
* legacy/additive shapes; only
|
|
22
|
-
*
|
|
23
|
-
* the whole `akm health`
|
|
11
|
+
* `task_history.metadata_json`'s `detail` field, decoded and skip-and-warn on
|
|
12
|
+
* corruption instead of throwing (mirrors `listStateProposals`).
|
|
13
|
+
* `decodeTaskHistoryMetadata` already tolerates legacy/additive shapes; only
|
|
14
|
+
* genuine corruption reaches this catch, and a corrupt row must degrade the
|
|
15
|
+
* metric (excluded, not fatal) rather than abort the whole `akm health`
|
|
16
|
+
* computation.
|
|
24
17
|
*/
|
|
25
18
|
export function taskFailureDetail(row) {
|
|
26
19
|
try {
|
|
27
|
-
return
|
|
20
|
+
return decodeTaskHistoryMetadata(row.metadata_json).detail ?? undefined;
|
|
28
21
|
}
|
|
29
22
|
catch (error) {
|
|
30
23
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -37,9 +30,8 @@ export function taskFailureDetail(row) {
|
|
|
37
30
|
* that represents a prepared command (agent/LLM) result. `target_kind` is
|
|
38
31
|
* read in the current (post-D8) vocabulary — the
|
|
39
32
|
* `025-task-history-vocabulary-backfill` state migration rewrites every
|
|
40
|
-
* legacy-vocabulary row
|
|
41
|
-
*
|
|
42
|
-
* it, so a `"command"` row here is unambiguously the agent/LLM arm.
|
|
33
|
+
* legacy-vocabulary row before this ever runs against it, so a `"command"`
|
|
34
|
+
* row here is unambiguously the agent/LLM arm.
|
|
43
35
|
*/
|
|
44
36
|
export function isAgentTaskHistoryRow(row) {
|
|
45
37
|
return row.target_kind === "command";
|
|
@@ -48,8 +40,7 @@ export function isAgentTaskHistoryRow(row) {
|
|
|
48
40
|
* #943: reason-value breakdown for a set of agent (command-kind) task
|
|
49
41
|
* failure rows — how much of the observed failures are `timeout` vs
|
|
50
42
|
* `non_zero_exit` vs `spawn_failed` etc., so `akm health`'s `task-fail-rate`
|
|
51
|
-
* advisory can say "timeout-dominant" from data rather than log grep.
|
|
52
|
-
* the existing `AgentFailureReason` vocabulary verbatim (spawn.ts) — a
|
|
43
|
+
* advisory can say "timeout-dominant" from data rather than log grep. A
|
|
53
44
|
* reason-per-row read failure (already warned by {@link taskFailureDetail})
|
|
54
45
|
* still counts under `"unknown"` rather than being dropped, so the total
|
|
55
46
|
* always equals `agentFailures.length`.
|
|
@@ -62,17 +53,16 @@ export function countAgentFailureReasons(agentFailures) {
|
|
|
62
53
|
}
|
|
63
54
|
return counts;
|
|
64
55
|
}
|
|
65
|
-
|
|
56
|
+
/** A zeroed accumulator — also what health reports when it could not read state.db at all (#791). */
|
|
57
|
+
export function emptyImproveMetrics() {
|
|
66
58
|
return {
|
|
67
59
|
invoked: 0,
|
|
68
60
|
completed: 0,
|
|
69
61
|
skipped: 0,
|
|
70
62
|
skipReasons: {},
|
|
71
63
|
resultRows: { total: 0, included: 0, skipped: { invalid: 0 } },
|
|
72
|
-
plannedRefs: 0,
|
|
73
|
-
strategyFilteredRefs: 0,
|
|
74
64
|
actions: {
|
|
75
|
-
reflect: { ok: 0, failed: 0, cooldown: 0, skipped: 0
|
|
65
|
+
reflect: { ok: 0, failed: 0, cooldown: 0, skipped: 0 },
|
|
76
66
|
distill: {
|
|
77
67
|
queued: 0,
|
|
78
68
|
llmFailed: 0,
|
|
@@ -81,8 +71,6 @@ function createUnknownImproveMetrics() {
|
|
|
81
71
|
configDisabled: 0,
|
|
82
72
|
skipped: 0,
|
|
83
73
|
skippedByReason: {},
|
|
84
|
-
deferred: 0,
|
|
85
|
-
deferredByReason: {},
|
|
86
74
|
},
|
|
87
75
|
memoryPrune: 0,
|
|
88
76
|
memoryInference: 0,
|
|
@@ -90,110 +78,25 @@ function createUnknownImproveMetrics() {
|
|
|
90
78
|
error: 0,
|
|
91
79
|
},
|
|
92
80
|
autoAccept: { promoted: 0, validationFailed: 0 },
|
|
93
|
-
reflectsWithErrorContext: 0,
|
|
94
|
-
coverageGapCount: 0,
|
|
95
|
-
deadUrlCount: 0,
|
|
96
|
-
deadUrlsChecked: 0,
|
|
97
|
-
deadUrlsTotal: 0,
|
|
98
|
-
deadUrlsSkipped: 0,
|
|
99
81
|
memorySummary: { eligible: 0, derived: 0 },
|
|
100
|
-
memoryCleanup: {
|
|
101
|
-
pruneCandidates: 0,
|
|
102
|
-
contradictionCandidates: 0,
|
|
103
|
-
beliefStateTransitions: 0,
|
|
104
|
-
consolidationCandidates: 0,
|
|
105
|
-
archived: 0,
|
|
106
|
-
warnings: 0,
|
|
107
|
-
},
|
|
108
82
|
consolidation: {
|
|
109
|
-
ran: false,
|
|
110
83
|
processed: 0,
|
|
111
84
|
promoted: 0,
|
|
112
85
|
merged: 0,
|
|
113
86
|
deleted: 0,
|
|
114
87
|
contradicted: 0,
|
|
115
88
|
judgedNoAction: 0,
|
|
116
|
-
mergedSecondaries: 0,
|
|
117
|
-
failedChunkMemories: 0,
|
|
118
|
-
skipReasons: {},
|
|
119
89
|
failedChunks: 0,
|
|
120
90
|
totalChunks: 0,
|
|
121
91
|
durationMs: 0,
|
|
122
92
|
},
|
|
123
|
-
memoryInference: {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
retryAttempts: 0,
|
|
128
|
-
freshAttempts: 0,
|
|
129
|
-
splitParents: 0,
|
|
130
|
-
written: 0,
|
|
131
|
-
skippedNoFacts: 0,
|
|
132
|
-
skippedChildExists: 0,
|
|
133
|
-
skippedAborted: 0,
|
|
134
|
-
unaccounted: 0,
|
|
135
|
-
htmlErrorCount: 0,
|
|
136
|
-
yieldRate: 0,
|
|
137
|
-
durationMs: 0,
|
|
138
|
-
},
|
|
139
|
-
graphExtraction: {
|
|
140
|
-
ran: false,
|
|
141
|
-
extractedFiles: 0,
|
|
142
|
-
consideredFiles: 0,
|
|
143
|
-
entities: 0,
|
|
144
|
-
relations: 0,
|
|
145
|
-
extractionCoverage: 0,
|
|
146
|
-
cacheHits: 0,
|
|
147
|
-
cacheMisses: 0,
|
|
148
|
-
cacheHitRate: 0,
|
|
149
|
-
truncations: 0,
|
|
150
|
-
failures: 0,
|
|
151
|
-
htmlErrors: 0,
|
|
152
|
-
retryAttempts: 0,
|
|
153
|
-
nonArrayBatchFailures: 0,
|
|
154
|
-
durationMs: 0,
|
|
155
|
-
},
|
|
156
|
-
sessionExtraction: {
|
|
157
|
-
ran: false,
|
|
158
|
-
sessionsScanned: 0,
|
|
159
|
-
sessionsExtracted: 0,
|
|
160
|
-
sessionsSkipped: 0,
|
|
161
|
-
proposalsCreated: 0,
|
|
162
|
-
warnings: 0,
|
|
163
|
-
durationMs: 0,
|
|
164
|
-
},
|
|
165
|
-
wallTime: {
|
|
166
|
-
count: 0,
|
|
167
|
-
medianMs: 0,
|
|
168
|
-
p95Ms: 0,
|
|
169
|
-
minMs: 0,
|
|
170
|
-
maxMs: 0,
|
|
171
|
-
byPhase: {
|
|
172
|
-
consolidation: { count: 0, totalMs: 0, medianMs: 0, p95Ms: 0 },
|
|
173
|
-
memoryInference: { count: 0, totalMs: 0, medianMs: 0, p95Ms: 0 },
|
|
174
|
-
graphExtraction: { count: 0, totalMs: 0, medianMs: 0, p95Ms: 0 },
|
|
175
|
-
},
|
|
176
|
-
},
|
|
177
|
-
perfTelemetry: {
|
|
178
|
-
dedupPoolSize: 0,
|
|
179
|
-
llmPoolSize: 0,
|
|
180
|
-
embedMs: 0,
|
|
181
|
-
embedCacheHits: 0,
|
|
182
|
-
embedCacheMisses: 0,
|
|
183
|
-
overBudgetRuns: 0,
|
|
184
|
-
runsWithTelemetry: 0,
|
|
185
|
-
},
|
|
186
|
-
coverage: {
|
|
187
|
-
rate: Number.NaN,
|
|
188
|
-
eligibleFraction: Number.NaN,
|
|
189
|
-
acceptedProposals: 0,
|
|
190
|
-
distinctRefs: 0,
|
|
191
|
-
churnRatio: Number.NaN,
|
|
192
|
-
totalAssets: 0,
|
|
193
|
-
},
|
|
93
|
+
memoryInference: { considered: 0, freshAttempts: 0, written: 0, skippedNoFacts: 0, yieldRate: 0, durationMs: 0 },
|
|
94
|
+
graphExtraction: { extractedFiles: 0, entities: 0, relations: 0, failures: 0, durationMs: 0 },
|
|
95
|
+
wallTime: { medianMs: 0, p95Ms: 0 },
|
|
96
|
+
coverage: { acceptedProposals: 0, distinctRefs: 0 },
|
|
194
97
|
};
|
|
195
98
|
}
|
|
196
|
-
|
|
99
|
+
function toFiniteNumber(value) {
|
|
197
100
|
if (typeof value === "number" && Number.isFinite(value))
|
|
198
101
|
return value;
|
|
199
102
|
if (typeof value === "string" && value.trim()) {
|
|
@@ -203,47 +106,9 @@ export function toFiniteNumber(value) {
|
|
|
203
106
|
}
|
|
204
107
|
return 0;
|
|
205
108
|
}
|
|
206
|
-
/**
|
|
207
|
-
* Event-derived metrics. Only `completed` and skipReasons/invoked are sourced
|
|
208
|
-
* from events in v2 — the richer fields come from {@link summarizeImproveRuns}.
|
|
209
|
-
* The function still receives `improve_completed` events so that the completed
|
|
210
|
-
* count reflects the canonical event stream (it lines up 1:1 with improve_runs
|
|
211
|
-
* rows in practice, but the events table remains the system-of-record for the
|
|
212
|
-
* existence of a run).
|
|
213
|
-
*/
|
|
214
|
-
export function summarizeImproveCompleted(events) {
|
|
215
|
-
const metrics = createUnknownImproveMetrics();
|
|
216
|
-
metrics.completed = events.length;
|
|
217
|
-
return metrics;
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* Read the machine-readable reason from a distill `outcome: "skipped"` result.
|
|
221
|
-
*/
|
|
222
|
-
function classifyDistillSkipReason(r) {
|
|
223
|
-
const explicitReason = typeof r?.skipReason === "string" ? r.skipReason : undefined;
|
|
224
|
-
return explicitReason || "unknown";
|
|
225
|
-
}
|
|
226
|
-
// ── projectRunMetrics phase helpers (chunk-9 WI-9.5c; file-level decompose of
|
|
227
|
-
// the single-envelope projection — each helper mutates its own disjoint
|
|
228
|
-
// subtree of the accumulator from the raw envelope, so call order does not
|
|
229
|
-
// matter; kept in envelope-field order for readability) ──────────────────────
|
|
230
|
-
/** plannedRefs / strategyFilteredRefs ref-count accounting. */
|
|
231
|
-
function applyPlannedRefs(metrics, result) {
|
|
232
|
-
// plannedRefs (array of {ref, reason})
|
|
233
|
-
const plannedRefs = result.plannedRefs;
|
|
234
|
-
if (Array.isArray(plannedRefs))
|
|
235
|
-
metrics.plannedRefs += plannedRefs.length;
|
|
236
|
-
// strategyFilteredRefs (array of {ref, reason}) — 2026-05-27: pre-filter
|
|
237
|
-
// bucket from `collectEligibleRefs` so the metric reflects work the
|
|
238
|
-
// planner dropped before signal-delta / per-pass dispatch.
|
|
239
|
-
const strategyFilteredRefs = result.strategyFilteredRefs;
|
|
240
|
-
if (Array.isArray(strategyFilteredRefs))
|
|
241
|
-
metrics.strategyFilteredRefs += strategyFilteredRefs.length;
|
|
242
|
-
}
|
|
243
|
-
/** One `actions[]` entry → the matching counter bucket (see ImproveHealthMetrics.actions jsdoc for the taxonomy). */
|
|
109
|
+
/** One `actions[]` entry → its counter bucket; unknown modes/outcomes are not counted. */
|
|
244
110
|
function applyAction(metrics, action) {
|
|
245
|
-
|
|
246
|
-
switch (mode) {
|
|
111
|
+
switch (typeof action.mode === "string" ? action.mode : "") {
|
|
247
112
|
case "reflect":
|
|
248
113
|
metrics.actions.reflect.ok += 1;
|
|
249
114
|
break;
|
|
@@ -253,20 +118,12 @@ function applyAction(metrics, action) {
|
|
|
253
118
|
case "reflect-cooldown":
|
|
254
119
|
metrics.actions.reflect.cooldown += 1;
|
|
255
120
|
break;
|
|
256
|
-
case "reflect-skipped":
|
|
121
|
+
case "reflect-skipped":
|
|
257
122
|
metrics.actions.reflect.skipped += 1;
|
|
258
|
-
const r = action.result;
|
|
259
|
-
const reason = typeof r?.reason === "string" && r.reason.trim() ? r.reason : "unknown";
|
|
260
|
-
metrics.actions.reflect.skippedByReason[reason] = (metrics.actions.reflect.skippedByReason[reason] ?? 0) + 1;
|
|
261
|
-
break;
|
|
262
|
-
}
|
|
263
|
-
case "reflect-guard-rejected":
|
|
264
|
-
metrics.actions.reflect.guardRejected += 1;
|
|
265
123
|
break;
|
|
266
124
|
case "distill": {
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
switch (outcome) {
|
|
125
|
+
const result = action.result;
|
|
126
|
+
switch (typeof result?.outcome === "string" ? result.outcome : "") {
|
|
270
127
|
case "queued":
|
|
271
128
|
metrics.actions.distill.queued += 1;
|
|
272
129
|
break;
|
|
@@ -283,21 +140,6 @@ function applyAction(metrics, action) {
|
|
|
283
140
|
case "config_disabled":
|
|
284
141
|
metrics.actions.distill.configDisabled += 1;
|
|
285
142
|
break;
|
|
286
|
-
case "skipped": {
|
|
287
|
-
// Previously dropped on the floor. The four sub-paths that emit
|
|
288
|
-
// `outcome: "skipped"` (see distill.ts:893, 1024, 1120, 1576):
|
|
289
|
-
// - recursive_lesson_input (type guard refused a lesson input)
|
|
290
|
-
// - conflict_noop (LLM resolved destination conflict as NOOP)
|
|
291
|
-
// - proposal-skipped cooldown / dedup at persistence
|
|
292
|
-
// 465 events/7d in the user's live stack. The result message
|
|
293
|
-
// typically encodes the reason; we also accept an explicit
|
|
294
|
-
// `skipReason` field when downstream code sets it.
|
|
295
|
-
metrics.actions.distill.deferred += 1;
|
|
296
|
-
const reason = classifyDistillSkipReason(r);
|
|
297
|
-
metrics.actions.distill.deferredByReason[reason] =
|
|
298
|
-
(metrics.actions.distill.deferredByReason[reason] ?? 0) + 1;
|
|
299
|
-
break;
|
|
300
|
-
}
|
|
301
143
|
default:
|
|
302
144
|
break;
|
|
303
145
|
}
|
|
@@ -315,261 +157,89 @@ function applyAction(metrics, action) {
|
|
|
315
157
|
case "error":
|
|
316
158
|
metrics.actions.error += 1;
|
|
317
159
|
break;
|
|
160
|
+
default:
|
|
161
|
+
break;
|
|
318
162
|
}
|
|
319
163
|
}
|
|
320
|
-
/**
|
|
321
|
-
function
|
|
322
|
-
const
|
|
323
|
-
if (
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
applyAction(metrics, action);
|
|
164
|
+
/** Project one `improve_runs.result_json` envelope into a single-run accumulator. */
|
|
165
|
+
function projectRunMetrics(result) {
|
|
166
|
+
const metrics = emptyImproveMetrics();
|
|
167
|
+
if (Array.isArray(result.actions)) {
|
|
168
|
+
for (const action of result.actions)
|
|
169
|
+
applyAction(metrics, action);
|
|
327
170
|
}
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* Read the bounded `distillSkipped` aggregate into the corresponding total and
|
|
331
|
-
* per-reason health metrics.
|
|
332
|
-
*/
|
|
333
|
-
function applyDistillSkippedAggregate(metrics, result) {
|
|
334
171
|
const distillSkipped = result.distillSkipped;
|
|
335
|
-
if (
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
const byReason = distillSkipped.byReason;
|
|
339
|
-
if (byReason && typeof byReason === "object") {
|
|
340
|
-
for (const [reason, count] of Object.entries(byReason)) {
|
|
172
|
+
if (distillSkipped && typeof distillSkipped === "object") {
|
|
173
|
+
metrics.actions.distill.skipped += toFiniteNumber(distillSkipped.total);
|
|
174
|
+
for (const [reason, count] of Object.entries(distillSkipped.byReason ?? {})) {
|
|
341
175
|
metrics.actions.distill.skippedByReason[reason] =
|
|
342
176
|
(metrics.actions.distill.skippedByReason[reason] ?? 0) + toFiniteNumber(count);
|
|
343
177
|
}
|
|
344
178
|
}
|
|
345
|
-
}
|
|
346
|
-
/** autoAccept + the small envelope-level counters (reflectsWithErrorContext, coverageGaps, deadUrls). */
|
|
347
|
-
function applyMiscCounters(metrics, result) {
|
|
348
179
|
metrics.autoAccept.promoted += toFiniteNumber(result.gateAutoAcceptedCount);
|
|
349
180
|
metrics.autoAccept.validationFailed += toFiniteNumber(result.gateAutoAcceptFailedCount);
|
|
350
|
-
metrics.reflectsWithErrorContext += toFiniteNumber(result.reflectsWithErrorContext);
|
|
351
|
-
if (Array.isArray(result.coverageGaps))
|
|
352
|
-
metrics.coverageGapCount += result.coverageGaps.length;
|
|
353
|
-
if (Array.isArray(result.deadUrls))
|
|
354
|
-
metrics.deadUrlCount += result.deadUrls.length;
|
|
355
|
-
const deadUrlCoverage = result.deadUrlCoverage;
|
|
356
|
-
if (deadUrlCoverage && typeof deadUrlCoverage === "object") {
|
|
357
|
-
metrics.deadUrlsChecked += toFiniteNumber(deadUrlCoverage.checked);
|
|
358
|
-
metrics.deadUrlsTotal += toFiniteNumber(deadUrlCoverage.total);
|
|
359
|
-
metrics.deadUrlsSkipped += toFiniteNumber(deadUrlCoverage.skipped);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
function applyMemorySummary(metrics, result) {
|
|
363
181
|
const memorySummary = result.memorySummary;
|
|
364
|
-
if (
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
metrics.memorySummary.derived += toFiniteNumber(memorySummary.derived);
|
|
368
|
-
}
|
|
369
|
-
function applyMemoryCleanup(metrics, result) {
|
|
370
|
-
const memoryCleanup = result.memoryCleanup;
|
|
371
|
-
if (!memoryCleanup)
|
|
372
|
-
return;
|
|
373
|
-
if (Array.isArray(memoryCleanup.pruneCandidates))
|
|
374
|
-
metrics.memoryCleanup.pruneCandidates += memoryCleanup.pruneCandidates.length;
|
|
375
|
-
if (Array.isArray(memoryCleanup.contradictionCandidates))
|
|
376
|
-
metrics.memoryCleanup.contradictionCandidates += memoryCleanup.contradictionCandidates.length;
|
|
377
|
-
if (Array.isArray(memoryCleanup.beliefStateTransitions))
|
|
378
|
-
metrics.memoryCleanup.beliefStateTransitions += memoryCleanup.beliefStateTransitions.length;
|
|
379
|
-
if (Array.isArray(memoryCleanup.consolidationCandidates))
|
|
380
|
-
metrics.memoryCleanup.consolidationCandidates += memoryCleanup.consolidationCandidates.length;
|
|
381
|
-
if (Array.isArray(memoryCleanup.archived))
|
|
382
|
-
metrics.memoryCleanup.archived += memoryCleanup.archived.length;
|
|
383
|
-
if (Array.isArray(memoryCleanup.warnings))
|
|
384
|
-
metrics.memoryCleanup.warnings += memoryCleanup.warnings.length;
|
|
385
|
-
}
|
|
386
|
-
function applyConsolidation(metrics, result) {
|
|
387
|
-
const consolidation = result.consolidation;
|
|
388
|
-
if (!consolidation)
|
|
389
|
-
return;
|
|
390
|
-
metrics.consolidation.processed += toFiniteNumber(consolidation.processed);
|
|
391
|
-
metrics.consolidation.merged += toFiniteNumber(consolidation.merged);
|
|
392
|
-
metrics.consolidation.deleted += toFiniteNumber(consolidation.deleted);
|
|
393
|
-
metrics.consolidation.contradicted += toFiniteNumber(consolidation.contradicted);
|
|
394
|
-
if (Array.isArray(consolidation.promoted))
|
|
395
|
-
metrics.consolidation.promoted += consolidation.promoted.length;
|
|
396
|
-
metrics.consolidation.failedChunks += toFiniteNumber(consolidation.failedChunks);
|
|
397
|
-
metrics.consolidation.totalChunks += toFiniteNumber(consolidation.totalChunks);
|
|
398
|
-
metrics.consolidation.durationMs += toFiniteNumber(consolidation.durationMs);
|
|
399
|
-
metrics.consolidation.judgedNoAction += toFiniteNumber(consolidation.judgedNoAction);
|
|
400
|
-
metrics.consolidation.mergedSecondaries += toFiniteNumber(consolidation.mergedSecondaries);
|
|
401
|
-
metrics.consolidation.failedChunkMemories += toFiniteNumber(consolidation.failedChunkMemories);
|
|
402
|
-
// Structured emitter (new on this branch): consolidate.ts now pushes
|
|
403
|
-
// per-ref grouped `{ref, skips: [{op, reason}]}` entries to `skipReasons`
|
|
404
|
-
// for every deterministic post-LLM rejection. Each ref appears once but
|
|
405
|
-
// may carry multiple skips; aggregate every reason. Pre-fix envelopes have
|
|
406
|
-
// neither field, so be defensive.
|
|
407
|
-
const skipReasons = consolidation.skipReasons;
|
|
408
|
-
if (Array.isArray(skipReasons)) {
|
|
409
|
-
for (const entry of skipReasons) {
|
|
410
|
-
if (!entry || typeof entry !== "object")
|
|
411
|
-
continue;
|
|
412
|
-
const skips = entry.skips;
|
|
413
|
-
if (!Array.isArray(skips))
|
|
414
|
-
continue;
|
|
415
|
-
for (const skip of skips) {
|
|
416
|
-
if (!skip || typeof skip !== "object")
|
|
417
|
-
continue;
|
|
418
|
-
const reason = skip.reason;
|
|
419
|
-
if (typeof reason !== "string" || !reason.trim())
|
|
420
|
-
continue;
|
|
421
|
-
metrics.consolidation.skipReasons[reason] = (metrics.consolidation.skipReasons[reason] ?? 0) + 1;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
182
|
+
if (memorySummary) {
|
|
183
|
+
metrics.memorySummary.eligible += toFiniteNumber(memorySummary.eligible);
|
|
184
|
+
metrics.memorySummary.derived += toFiniteNumber(memorySummary.derived);
|
|
424
185
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
186
|
+
const consolidation = result.consolidation;
|
|
187
|
+
if (consolidation) {
|
|
188
|
+
const cons = metrics.consolidation;
|
|
189
|
+
cons.processed += toFiniteNumber(consolidation.processed);
|
|
190
|
+
if (Array.isArray(consolidation.promoted))
|
|
191
|
+
cons.promoted += consolidation.promoted.length;
|
|
192
|
+
cons.merged += toFiniteNumber(consolidation.merged);
|
|
193
|
+
cons.deleted += toFiniteNumber(consolidation.deleted);
|
|
194
|
+
cons.contradicted += toFiniteNumber(consolidation.contradicted);
|
|
195
|
+
cons.judgedNoAction += toFiniteNumber(consolidation.judgedNoAction);
|
|
196
|
+
cons.failedChunks += toFiniteNumber(consolidation.failedChunks);
|
|
197
|
+
cons.totalChunks += toFiniteNumber(consolidation.totalChunks);
|
|
198
|
+
cons.durationMs += toFiniteNumber(consolidation.durationMs);
|
|
438
199
|
}
|
|
439
|
-
}
|
|
440
|
-
function applyMemoryInference(metrics, result) {
|
|
441
200
|
const memoryInference = result.memoryInference;
|
|
442
201
|
if (memoryInference) {
|
|
443
202
|
const considered = toFiniteNumber(memoryInference.considered);
|
|
444
|
-
const
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
metrics.memoryInference.skippedChildExists += toFiniteNumber(memoryInference.skippedChildExists);
|
|
452
|
-
metrics.memoryInference.skippedAborted += toFiniteNumber(memoryInference.skippedAborted);
|
|
453
|
-
metrics.memoryInference.unaccounted += toFiniteNumber(memoryInference.unaccounted);
|
|
454
|
-
metrics.memoryInference.htmlErrorCount += toFiniteNumber(memoryInference.htmlErrorCount);
|
|
203
|
+
const mi = metrics.memoryInference;
|
|
204
|
+
mi.considered += considered;
|
|
205
|
+
// Cache hits and budget-aborted records never reached the LLM; excluding
|
|
206
|
+
// them keeps the yield rate a statement about real inference attempts.
|
|
207
|
+
mi.freshAttempts += Math.max(0, considered - toFiniteNumber(memoryInference.cacheHits) - toFiniteNumber(memoryInference.skippedAborted));
|
|
208
|
+
mi.written += toFiniteNumber(memoryInference.writtenFacts);
|
|
209
|
+
mi.skippedNoFacts += toFiniteNumber(memoryInference.skippedNoFacts);
|
|
455
210
|
}
|
|
456
211
|
metrics.memoryInference.durationMs += toFiniteNumber(result.memoryInferenceDurationMs);
|
|
457
|
-
}
|
|
458
|
-
function applyGraphExtraction(metrics, result) {
|
|
459
212
|
const graphExtraction = result.graphExtraction;
|
|
460
213
|
if (graphExtraction) {
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
214
|
+
const ge = metrics.graphExtraction;
|
|
215
|
+
// This run's counts, like entities and relations. `quality` describes the
|
|
216
|
+
// whole stored graph after the run; summing it would count each stored file
|
|
217
|
+
// once per run in the window.
|
|
218
|
+
ge.extractedFiles += toFiniteNumber(graphExtraction.extracted);
|
|
219
|
+
ge.entities += toFiniteNumber(graphExtraction.totalEntities);
|
|
220
|
+
ge.relations += toFiniteNumber(graphExtraction.totalRelations);
|
|
468
221
|
const telemetry = graphExtraction.telemetry;
|
|
469
|
-
|
|
470
|
-
metrics.graphExtraction.cacheHits += toFiniteNumber(telemetry.cacheHits);
|
|
471
|
-
metrics.graphExtraction.cacheMisses += toFiniteNumber(telemetry.cacheMisses);
|
|
472
|
-
metrics.graphExtraction.truncations += toFiniteNumber(telemetry.truncationCount);
|
|
473
|
-
metrics.graphExtraction.failures += toFiniteNumber(telemetry.failureCount);
|
|
474
|
-
metrics.graphExtraction.htmlErrors += toFiniteNumber(telemetry.htmlErrorCount);
|
|
475
|
-
metrics.graphExtraction.retryAttempts += toFiniteNumber(telemetry.retryAttempts);
|
|
476
|
-
metrics.graphExtraction.nonArrayBatchFailures += toFiniteNumber(telemetry.nonArrayBatchFailures);
|
|
477
|
-
}
|
|
222
|
+
ge.failures += toFiniteNumber(telemetry?.failureCount);
|
|
478
223
|
}
|
|
479
224
|
metrics.graphExtraction.durationMs += toFiniteNumber(result.graphExtractionDurationMs);
|
|
480
|
-
}
|
|
481
|
-
function applySessionExtraction(metrics, result) {
|
|
482
|
-
if (!Array.isArray(result.extract))
|
|
483
|
-
return;
|
|
484
|
-
for (const e of result.extract) {
|
|
485
|
-
metrics.sessionExtraction.sessionsScanned += toFiniteNumber(e.sessionsProcessed);
|
|
486
|
-
metrics.sessionExtraction.sessionsSkipped += toFiniteNumber(e.sessionsSkipped);
|
|
487
|
-
if (Array.isArray(e.sessions)) {
|
|
488
|
-
metrics.sessionExtraction.sessionsExtracted += e.sessions.filter((s) => Array.isArray(s.proposalIds) && s.proposalIds.length > 0).length;
|
|
489
|
-
}
|
|
490
|
-
metrics.sessionExtraction.proposalsCreated += Array.isArray(e.proposals) ? e.proposals.length : 0;
|
|
491
|
-
metrics.sessionExtraction.warnings += Array.isArray(e.warnings) ? e.warnings.length : 0;
|
|
492
|
-
metrics.sessionExtraction.durationMs += toFiniteNumber(e.durationMs);
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
/**
|
|
496
|
-
* Project a single `improve_runs.result_json` envelope into an accumulator-shaped
|
|
497
|
-
* ImproveHealthMetrics. The aggregator merges these per-row metrics into one
|
|
498
|
-
* window-level metric. File-level decomposed (chunk-9 WI-9.5c) into one
|
|
499
|
-
* `apply*` helper per envelope section above; each mutates its own disjoint
|
|
500
|
-
* subtree of the accumulator so the call order below is not load-bearing.
|
|
501
|
-
*/
|
|
502
|
-
function projectRunMetrics(result) {
|
|
503
|
-
const metrics = createUnknownImproveMetrics();
|
|
504
|
-
applyPlannedRefs(metrics, result);
|
|
505
|
-
applyActionsList(metrics, result);
|
|
506
|
-
applyDistillSkippedAggregate(metrics, result);
|
|
507
|
-
applyMiscCounters(metrics, result);
|
|
508
|
-
applyMemorySummary(metrics, result);
|
|
509
|
-
applyMemoryCleanup(metrics, result);
|
|
510
|
-
applyConsolidation(metrics, result);
|
|
511
|
-
applyMemoryInference(metrics, result);
|
|
512
|
-
applyGraphExtraction(metrics, result);
|
|
513
|
-
applySessionExtraction(metrics, result);
|
|
514
225
|
return metrics;
|
|
515
226
|
}
|
|
516
|
-
/**
|
|
517
|
-
* Finalize derived flags and rates on an accumulator. Used both for the
|
|
518
|
-
* window-level aggregate and for each per-run row in --detail per-run mode
|
|
519
|
-
* so the single-row metrics still expose `ran` / `yieldRate` / `cacheHitRate`.
|
|
520
|
-
*/
|
|
227
|
+
/** Derived rates on an accumulator (window aggregate or single run). */
|
|
521
228
|
function finalizeImproveMetrics(metrics) {
|
|
522
|
-
metrics.
|
|
523
|
-
|
|
524
|
-
metrics.consolidation.durationMs > 0 ||
|
|
525
|
-
metrics.consolidation.promoted > 0 ||
|
|
526
|
-
metrics.consolidation.merged > 0 ||
|
|
527
|
-
metrics.consolidation.deleted > 0 ||
|
|
528
|
-
metrics.consolidation.contradicted > 0 ||
|
|
529
|
-
metrics.consolidation.totalChunks > 0;
|
|
530
|
-
metrics.memoryInference.ran =
|
|
531
|
-
metrics.memoryInference.considered > 0 ||
|
|
532
|
-
metrics.memoryInference.written > 0 ||
|
|
533
|
-
metrics.memoryInference.durationMs > 0;
|
|
534
|
-
// Yield denominator excludes cache hits and aborted records.
|
|
535
|
-
metrics.memoryInference.freshAttempts = Math.max(0, metrics.memoryInference.considered - metrics.memoryInference.cacheHits - metrics.memoryInference.skippedAborted);
|
|
536
|
-
metrics.memoryInference.yieldRate =
|
|
537
|
-
metrics.memoryInference.freshAttempts > 0
|
|
538
|
-
? roundRate(metrics.memoryInference.written / metrics.memoryInference.freshAttempts)
|
|
539
|
-
: 0;
|
|
540
|
-
metrics.graphExtraction.ran =
|
|
541
|
-
metrics.graphExtraction.extractedFiles > 0 ||
|
|
542
|
-
metrics.graphExtraction.entities > 0 ||
|
|
543
|
-
metrics.graphExtraction.durationMs > 0;
|
|
544
|
-
const cacheTotal = metrics.graphExtraction.cacheHits + metrics.graphExtraction.cacheMisses;
|
|
545
|
-
metrics.graphExtraction.cacheHitRate = cacheTotal > 0 ? roundRate(metrics.graphExtraction.cacheHits / cacheTotal) : 0;
|
|
546
|
-
metrics.graphExtraction.extractionCoverage =
|
|
547
|
-
metrics.graphExtraction.consideredFiles > 0
|
|
548
|
-
? roundRate(metrics.graphExtraction.extractedFiles / metrics.graphExtraction.consideredFiles)
|
|
549
|
-
: 0;
|
|
550
|
-
metrics.sessionExtraction.ran =
|
|
551
|
-
metrics.sessionExtraction.sessionsScanned > 0 ||
|
|
552
|
-
metrics.sessionExtraction.proposalsCreated > 0 ||
|
|
553
|
-
metrics.sessionExtraction.durationMs > 0;
|
|
229
|
+
const mi = metrics.memoryInference;
|
|
230
|
+
mi.yieldRate = mi.freshAttempts > 0 ? roundRate(mi.written / mi.freshAttempts) : 0;
|
|
554
231
|
}
|
|
555
232
|
/**
|
|
556
|
-
* Merge per-
|
|
557
|
-
*
|
|
233
|
+
* Merge per-run metrics from `src` into accumulator `dst`. Every counter is
|
|
234
|
+
* additive; `memorySummary` is a whole-stash snapshot and is deliberately NOT
|
|
235
|
+
* merged (summing it across runs inflated it ~N× — the 1.2M-eligible bug), and
|
|
236
|
+
* the derived rate is recomputed by {@link finalizeImproveMetrics}.
|
|
558
237
|
*/
|
|
559
238
|
function mergeImproveMetrics(dst, src) {
|
|
560
|
-
dst.plannedRefs += src.plannedRefs;
|
|
561
|
-
// strategyFilteredRefs is the count of refs the planner drops up-front for the
|
|
562
|
-
// active strategy — recomputed against the (stable) stash every run, so it is a
|
|
563
|
-
// snapshot, NOT a per-run increment. Summing it re-counts the same refs each
|
|
564
|
-
// run (the ~2.4M bug). Set from the most recent run in summarizeImproveRuns.
|
|
565
239
|
dst.actions.reflect.ok += src.actions.reflect.ok;
|
|
566
240
|
dst.actions.reflect.failed += src.actions.reflect.failed;
|
|
567
241
|
dst.actions.reflect.cooldown += src.actions.reflect.cooldown;
|
|
568
242
|
dst.actions.reflect.skipped += src.actions.reflect.skipped;
|
|
569
|
-
dst.actions.reflect.guardRejected += src.actions.reflect.guardRejected;
|
|
570
|
-
for (const [reason, count] of Object.entries(src.actions.reflect.skippedByReason)) {
|
|
571
|
-
dst.actions.reflect.skippedByReason[reason] = (dst.actions.reflect.skippedByReason[reason] ?? 0) + count;
|
|
572
|
-
}
|
|
573
243
|
dst.actions.distill.queued += src.actions.distill.queued;
|
|
574
244
|
dst.actions.distill.llmFailed += src.actions.distill.llmFailed;
|
|
575
245
|
dst.actions.distill.judgeRejected += src.actions.distill.judgeRejected;
|
|
@@ -579,86 +249,31 @@ function mergeImproveMetrics(dst, src) {
|
|
|
579
249
|
for (const [reason, count] of Object.entries(src.actions.distill.skippedByReason)) {
|
|
580
250
|
dst.actions.distill.skippedByReason[reason] = (dst.actions.distill.skippedByReason[reason] ?? 0) + count;
|
|
581
251
|
}
|
|
582
|
-
dst.actions.distill.deferred += src.actions.distill.deferred;
|
|
583
|
-
for (const [reason, count] of Object.entries(src.actions.distill.deferredByReason)) {
|
|
584
|
-
dst.actions.distill.deferredByReason[reason] = (dst.actions.distill.deferredByReason[reason] ?? 0) + count;
|
|
585
|
-
}
|
|
586
252
|
dst.actions.memoryPrune += src.actions.memoryPrune;
|
|
587
253
|
dst.actions.memoryInference += src.actions.memoryInference;
|
|
588
254
|
dst.actions.graphExtraction += src.actions.graphExtraction;
|
|
589
255
|
dst.actions.error += src.actions.error;
|
|
590
256
|
dst.autoAccept.promoted += src.autoAccept.promoted;
|
|
591
257
|
dst.autoAccept.validationFailed += src.autoAccept.validationFailed;
|
|
592
|
-
dst.reflectsWithErrorContext += src.reflectsWithErrorContext;
|
|
593
|
-
dst.coverageGapCount += src.coverageGapCount;
|
|
594
|
-
dst.deadUrlCount += src.deadUrlCount;
|
|
595
|
-
dst.deadUrlsChecked += src.deadUrlsChecked;
|
|
596
|
-
dst.deadUrlsTotal += src.deadUrlsTotal;
|
|
597
|
-
dst.deadUrlsSkipped += src.deadUrlsSkipped;
|
|
598
|
-
// NOTE: memorySummary (derived/eligible) is a WHOLE-STASH snapshot recorded on
|
|
599
|
-
// every run, NOT a per-run increment — summing it across the window inflates
|
|
600
|
-
// it ~N× (the 1.2M-eligible bug). It is set from the most recent run in
|
|
601
|
-
// summarizeImproveRuns instead, so it is intentionally not merged here.
|
|
602
|
-
dst.memoryCleanup.pruneCandidates += src.memoryCleanup.pruneCandidates;
|
|
603
|
-
dst.memoryCleanup.contradictionCandidates += src.memoryCleanup.contradictionCandidates;
|
|
604
|
-
dst.memoryCleanup.beliefStateTransitions += src.memoryCleanup.beliefStateTransitions;
|
|
605
|
-
dst.memoryCleanup.consolidationCandidates += src.memoryCleanup.consolidationCandidates;
|
|
606
|
-
dst.memoryCleanup.archived += src.memoryCleanup.archived;
|
|
607
|
-
dst.memoryCleanup.warnings += src.memoryCleanup.warnings;
|
|
608
258
|
dst.consolidation.processed += src.consolidation.processed;
|
|
609
259
|
dst.consolidation.promoted += src.consolidation.promoted;
|
|
610
260
|
dst.consolidation.merged += src.consolidation.merged;
|
|
611
261
|
dst.consolidation.deleted += src.consolidation.deleted;
|
|
612
262
|
dst.consolidation.contradicted += src.consolidation.contradicted;
|
|
263
|
+
dst.consolidation.judgedNoAction += src.consolidation.judgedNoAction;
|
|
613
264
|
dst.consolidation.failedChunks += src.consolidation.failedChunks;
|
|
614
265
|
dst.consolidation.totalChunks += src.consolidation.totalChunks;
|
|
615
266
|
dst.consolidation.durationMs += src.consolidation.durationMs;
|
|
616
|
-
dst.consolidation.judgedNoAction += src.consolidation.judgedNoAction;
|
|
617
|
-
dst.consolidation.mergedSecondaries += src.consolidation.mergedSecondaries;
|
|
618
|
-
dst.consolidation.failedChunkMemories += src.consolidation.failedChunkMemories;
|
|
619
|
-
for (const [reason, count] of Object.entries(src.consolidation.skipReasons)) {
|
|
620
|
-
dst.consolidation.skipReasons[reason] = (dst.consolidation.skipReasons[reason] ?? 0) + count;
|
|
621
|
-
}
|
|
622
267
|
dst.memoryInference.considered += src.memoryInference.considered;
|
|
623
|
-
dst.memoryInference.
|
|
624
|
-
dst.memoryInference.splitParents += src.memoryInference.splitParents;
|
|
268
|
+
dst.memoryInference.freshAttempts += src.memoryInference.freshAttempts;
|
|
625
269
|
dst.memoryInference.written += src.memoryInference.written;
|
|
626
270
|
dst.memoryInference.skippedNoFacts += src.memoryInference.skippedNoFacts;
|
|
627
|
-
dst.memoryInference.skippedChildExists += src.memoryInference.skippedChildExists;
|
|
628
|
-
dst.memoryInference.skippedAborted += src.memoryInference.skippedAborted;
|
|
629
|
-
dst.memoryInference.unaccounted += src.memoryInference.unaccounted;
|
|
630
|
-
dst.memoryInference.htmlErrorCount += src.memoryInference.htmlErrorCount;
|
|
631
|
-
dst.memoryInference.retryAttempts += src.memoryInference.retryAttempts;
|
|
632
271
|
dst.memoryInference.durationMs += src.memoryInference.durationMs;
|
|
633
272
|
dst.graphExtraction.extractedFiles += src.graphExtraction.extractedFiles;
|
|
634
|
-
dst.graphExtraction.consideredFiles += src.graphExtraction.consideredFiles;
|
|
635
273
|
dst.graphExtraction.entities += src.graphExtraction.entities;
|
|
636
274
|
dst.graphExtraction.relations += src.graphExtraction.relations;
|
|
637
|
-
dst.graphExtraction.cacheHits += src.graphExtraction.cacheHits;
|
|
638
|
-
dst.graphExtraction.cacheMisses += src.graphExtraction.cacheMisses;
|
|
639
|
-
dst.graphExtraction.truncations += src.graphExtraction.truncations;
|
|
640
275
|
dst.graphExtraction.failures += src.graphExtraction.failures;
|
|
641
|
-
dst.graphExtraction.htmlErrors += src.graphExtraction.htmlErrors;
|
|
642
|
-
dst.graphExtraction.nonArrayBatchFailures += src.graphExtraction.nonArrayBatchFailures;
|
|
643
|
-
dst.graphExtraction.retryAttempts += src.graphExtraction.retryAttempts;
|
|
644
276
|
dst.graphExtraction.durationMs += src.graphExtraction.durationMs;
|
|
645
|
-
dst.sessionExtraction.sessionsScanned += src.sessionExtraction.sessionsScanned;
|
|
646
|
-
dst.sessionExtraction.sessionsExtracted += src.sessionExtraction.sessionsExtracted;
|
|
647
|
-
dst.sessionExtraction.sessionsSkipped += src.sessionExtraction.sessionsSkipped;
|
|
648
|
-
dst.sessionExtraction.proposalsCreated += src.sessionExtraction.proposalsCreated;
|
|
649
|
-
dst.sessionExtraction.warnings += src.sessionExtraction.warnings;
|
|
650
|
-
dst.sessionExtraction.durationMs += src.sessionExtraction.durationMs;
|
|
651
|
-
// WS-5: merge perf telemetry (additive sums).
|
|
652
|
-
dst.perfTelemetry.dedupPoolSize += src.perfTelemetry.dedupPoolSize;
|
|
653
|
-
dst.perfTelemetry.llmPoolSize += src.perfTelemetry.llmPoolSize;
|
|
654
|
-
dst.perfTelemetry.embedMs += src.perfTelemetry.embedMs;
|
|
655
|
-
dst.perfTelemetry.embedCacheHits += src.perfTelemetry.embedCacheHits;
|
|
656
|
-
dst.perfTelemetry.embedCacheMisses += src.perfTelemetry.embedCacheMisses;
|
|
657
|
-
dst.perfTelemetry.overBudgetRuns += src.perfTelemetry.overBudgetRuns;
|
|
658
|
-
dst.perfTelemetry.runsWithTelemetry += src.perfTelemetry.runsWithTelemetry;
|
|
659
|
-
// coverage: acceptedProposals is additive; totalAssets is a snapshot (like memorySummary).
|
|
660
|
-
// totalAssets is intentionally NOT merged here — set from the most recent run in summarizeImproveRuns.
|
|
661
|
-
dst.coverage.acceptedProposals += src.coverage.acceptedProposals;
|
|
662
277
|
}
|
|
663
278
|
function compareImproveRunRecency(a, b) {
|
|
664
279
|
const started = a.started_at.localeCompare(b.started_at);
|
|
@@ -669,101 +284,48 @@ function compareImproveRunRecency(a, b) {
|
|
|
669
284
|
return completed;
|
|
670
285
|
return a.id.localeCompare(b.id);
|
|
671
286
|
}
|
|
287
|
+
/**
|
|
288
|
+
* Aggregate the window's `improve_runs` rows. `runCount` is every non-dry-run
|
|
289
|
+
* row in the window; rows whose envelope does not decode are counted under
|
|
290
|
+
* `resultRows.skipped.invalid` and excluded from the result-derived metrics.
|
|
291
|
+
*/
|
|
672
292
|
export function summarizeImproveRuns(db, since, until) {
|
|
673
|
-
const accum =
|
|
293
|
+
const accum = emptyImproveMetrics();
|
|
674
294
|
const rows = queryImproveRuns(db, since, until);
|
|
675
|
-
|
|
676
|
-
// that phase. Phases that did not run on a given envelope are simply
|
|
677
|
-
// omitted (NOT counted as 0) so the median/p95 reflect actual phase work.
|
|
678
|
-
const phaseDurations = {
|
|
679
|
-
consolidation: [],
|
|
680
|
-
memoryInference: [],
|
|
681
|
-
graphExtraction: [],
|
|
682
|
-
};
|
|
295
|
+
const resultRows = { total: rows.length, included: 0, skipped: { invalid: 0 } };
|
|
683
296
|
// memorySummary is a whole-stash snapshot per run, so the window value is the
|
|
684
|
-
//
|
|
685
|
-
let
|
|
686
|
-
let latestMemorySummary;
|
|
687
|
-
let latestStrategyFilteredRefs = 0;
|
|
688
|
-
if (!accum.resultRows)
|
|
689
|
-
throw new Error("invariant: improve result-row accounting was not initialized");
|
|
690
|
-
accum.resultRows.total = rows.length;
|
|
297
|
+
// newest complete run's snapshot (current state) — not a sum across runs.
|
|
298
|
+
let latest;
|
|
691
299
|
for (const row of rows) {
|
|
692
|
-
let
|
|
300
|
+
let result;
|
|
693
301
|
try {
|
|
694
|
-
|
|
302
|
+
result = decodeImproveResult(row.result_json).envelope;
|
|
695
303
|
}
|
|
696
304
|
catch {
|
|
697
|
-
|
|
305
|
+
resultRows.skipped.invalid += 1;
|
|
698
306
|
continue;
|
|
699
307
|
}
|
|
700
|
-
|
|
701
|
-
const result = decoded.envelope;
|
|
308
|
+
resultRows.included += 1;
|
|
702
309
|
const perRow = projectRunMetrics(result);
|
|
703
310
|
mergeImproveMetrics(accum, perRow);
|
|
704
|
-
const startMs = new Date(row.started_at).getTime();
|
|
705
311
|
if (result.terminated === undefined &&
|
|
706
|
-
Number.isFinite(
|
|
707
|
-
(
|
|
708
|
-
|
|
709
|
-
latestMemorySummary = perRow.memorySummary;
|
|
710
|
-
latestStrategyFilteredRefs = perRow.strategyFilteredRefs;
|
|
312
|
+
Number.isFinite(new Date(row.started_at).getTime()) &&
|
|
313
|
+
(latest === undefined || compareImproveRunRecency(row, latest.row) > 0)) {
|
|
314
|
+
latest = { row, memorySummary: perRow.memorySummary };
|
|
711
315
|
}
|
|
712
|
-
// Collect per-phase durations directly off the envelope. consolidation's
|
|
713
|
-
// duration lives inside the sub-object; memoryInference and graphExtraction
|
|
714
|
-
// expose top-level *DurationMs keys (`memoryInferenceDurationMs`,
|
|
715
|
-
// `graphExtractionDurationMs`) when they actually ran on that envelope.
|
|
716
|
-
const consol = result.consolidation;
|
|
717
|
-
const consolMs = toFiniteNumber(consol?.durationMs);
|
|
718
|
-
if (consolMs > 0)
|
|
719
|
-
phaseDurations.consolidation.push(consolMs);
|
|
720
|
-
const memMs = toFiniteNumber(result.memoryInferenceDurationMs);
|
|
721
|
-
if (memMs > 0)
|
|
722
|
-
phaseDurations.memoryInference.push(memMs);
|
|
723
|
-
const graphMs = toFiniteNumber(result.graphExtractionDurationMs);
|
|
724
|
-
if (graphMs > 0)
|
|
725
|
-
phaseDurations.graphExtraction.push(graphMs);
|
|
726
316
|
}
|
|
727
317
|
finalizeImproveMetrics(accum);
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
accum.wallTime.byPhase = {
|
|
732
|
-
consolidation: summarizePhaseDurations(phaseDurations.consolidation),
|
|
733
|
-
memoryInference: summarizePhaseDurations(phaseDurations.memoryInference),
|
|
734
|
-
graphExtraction: summarizePhaseDurations(phaseDurations.graphExtraction),
|
|
735
|
-
};
|
|
318
|
+
accum.resultRows = resultRows;
|
|
319
|
+
if (latest)
|
|
320
|
+
accum.memorySummary = latest.memorySummary;
|
|
736
321
|
return { metrics: accum, runCount: rows.length };
|
|
737
322
|
}
|
|
738
|
-
/**
|
|
739
|
-
* Aggregate a list of per-envelope phase durations into the
|
|
740
|
-
* `wallTime.byPhase.*` shape: count, total, median, p95. Median/p95 use the
|
|
741
|
-
* same nearest-rank picker as the top-level wallTime stats so the two are
|
|
742
|
-
* comparable.
|
|
743
|
-
*/
|
|
744
|
-
export function summarizePhaseDurations(samples) {
|
|
745
|
-
if (samples.length === 0)
|
|
746
|
-
return { count: 0, totalMs: 0, medianMs: 0, p95Ms: 0 };
|
|
747
|
-
const sorted = [...samples].sort((a, b) => a - b);
|
|
748
|
-
const pick = (q) => sorted[Math.min(sorted.length - 1, Math.floor(q * sorted.length))] ?? 0;
|
|
749
|
-
const totalMs = sorted.reduce((acc, n) => acc + n, 0);
|
|
750
|
-
return {
|
|
751
|
-
count: sorted.length,
|
|
752
|
-
totalMs,
|
|
753
|
-
medianMs: pick(0.5),
|
|
754
|
-
p95Ms: pick(0.95),
|
|
755
|
-
};
|
|
756
|
-
}
|
|
757
|
-
/**
|
|
758
|
-
* Project an improve_runs row + wall-time lookup into a single ImproveRunSummary.
|
|
759
|
-
* Used by `akm health --detail per-run`.
|
|
760
|
-
*/
|
|
323
|
+
/** Project an improve_runs row + wall time + task attribution into one {@link ImproveRunSummary}. */
|
|
761
324
|
export function projectImproveRunSummary(row, wallTimeMs, taskId) {
|
|
762
325
|
let result = {};
|
|
763
326
|
let resultStatus = "invalid";
|
|
764
327
|
try {
|
|
765
|
-
|
|
766
|
-
result = decoded.envelope;
|
|
328
|
+
result = decodeImproveResult(row.result_json).envelope;
|
|
767
329
|
resultStatus = "valid";
|
|
768
330
|
}
|
|
769
331
|
catch {
|
|
@@ -773,10 +335,7 @@ export function projectImproveRunSummary(row, wallTimeMs, taskId) {
|
|
|
773
335
|
}
|
|
774
336
|
const perRow = projectRunMetrics(result);
|
|
775
337
|
finalizeImproveMetrics(perRow);
|
|
776
|
-
const orphansPurged = toFiniteNumber(result.orphansPurged);
|
|
777
338
|
const lintSummary = result.lintSummary;
|
|
778
|
-
const lintFixed = lintSummary ? toFiniteNumber(lintSummary.fixed) : 0;
|
|
779
|
-
const lintFlagged = lintSummary ? toFiniteNumber(lintSummary.flagged) : 0;
|
|
780
339
|
return {
|
|
781
340
|
id: row.id,
|
|
782
341
|
startedAt: row.started_at,
|
|
@@ -793,37 +352,21 @@ export function projectImproveRunSummary(row, wallTimeMs, taskId) {
|
|
|
793
352
|
taskId,
|
|
794
353
|
actions: perRow.actions,
|
|
795
354
|
memorySummary: perRow.memorySummary,
|
|
796
|
-
memoryCleanup: perRow.memoryCleanup,
|
|
797
355
|
consolidation: perRow.consolidation,
|
|
798
356
|
memoryInference: perRow.memoryInference,
|
|
799
357
|
graphExtraction: perRow.graphExtraction,
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
lintFlagged,
|
|
804
|
-
};
|
|
805
|
-
}
|
|
806
|
-
function emptyPhaseStats() {
|
|
807
|
-
return {
|
|
808
|
-
consolidation: { count: 0, totalMs: 0, medianMs: 0, p95Ms: 0 },
|
|
809
|
-
memoryInference: { count: 0, totalMs: 0, medianMs: 0, p95Ms: 0 },
|
|
810
|
-
graphExtraction: { count: 0, totalMs: 0, medianMs: 0, p95Ms: 0 },
|
|
358
|
+
orphansPurged: toFiniteNumber(result.orphansPurged),
|
|
359
|
+
lintFixed: lintSummary ? toFiniteNumber(lintSummary.fixed) : 0,
|
|
360
|
+
lintFlagged: lintSummary ? toFiniteNumber(lintSummary.flagged) : 0,
|
|
811
361
|
};
|
|
812
362
|
}
|
|
813
|
-
|
|
814
|
-
|
|
363
|
+
/** Nearest-rank median and p95 of the window's run wall times. */
|
|
364
|
+
export function computeWallTimeStats(durationsMs) {
|
|
815
365
|
if (durationsMs.length === 0)
|
|
816
|
-
return {
|
|
366
|
+
return { medianMs: 0, p95Ms: 0 };
|
|
817
367
|
const sorted = [...durationsMs].sort((a, b) => a - b);
|
|
818
368
|
const pick = (q) => sorted[Math.min(sorted.length - 1, Math.floor(q * sorted.length))] ?? 0;
|
|
819
|
-
return {
|
|
820
|
-
count: sorted.length,
|
|
821
|
-
medianMs: pick(0.5),
|
|
822
|
-
p95Ms: pick(0.95),
|
|
823
|
-
minMs: sorted[0] ?? 0,
|
|
824
|
-
maxMs: sorted[sorted.length - 1] ?? 0,
|
|
825
|
-
byPhase: phase,
|
|
826
|
-
};
|
|
369
|
+
return { medianMs: pick(0.5), p95Ms: pick(0.95) };
|
|
827
370
|
}
|
|
828
371
|
export function buildImproveSkipSummary(events) {
|
|
829
372
|
// Two kinds of skip events:
|
|
@@ -833,9 +376,9 @@ export function buildImproveSkipSummary(events) {
|
|
|
833
376
|
// (`no_new_signal`, `strategy_filtered_all_passes`). Each run re-counts the
|
|
834
377
|
// same stable set, so summing across the window re-counts it N times (the
|
|
835
378
|
// 2.7M / 3M inflation). For these we keep the MOST RECENT run's count — the
|
|
836
|
-
// current snapshot — matching how memorySummary
|
|
837
|
-
//
|
|
838
|
-
//
|
|
379
|
+
// current snapshot — matching how memorySummary is handled. Events arrive
|
|
380
|
+
// in chronological (offset) order, so the last count-bearing event per
|
|
381
|
+
// reason is the latest run's value.
|
|
839
382
|
const summed = {};
|
|
840
383
|
const latestSnapshot = {};
|
|
841
384
|
for (const event of events) {
|
|
@@ -855,3 +398,21 @@ export function buildImproveSkipSummary(events) {
|
|
|
855
398
|
const skipped = Object.values(skipReasons).reduce((a, b) => a + b, 0);
|
|
856
399
|
return { skipped, skipReasons };
|
|
857
400
|
}
|
|
401
|
+
/**
|
|
402
|
+
* Proposals accepted in `[since, until)` (by `updated_at`) and the distinct
|
|
403
|
+
* refs among them — N accepted rewrites of one asset touch one ref. A single
|
|
404
|
+
* SQL aggregate; proposal bodies are never loaded. Fails open to zeros when
|
|
405
|
+
* the table is absent.
|
|
406
|
+
*/
|
|
407
|
+
export function computeWindowProposalCoverage(db, since, until) {
|
|
408
|
+
try {
|
|
409
|
+
const row = db
|
|
410
|
+
.prepare("SELECT COUNT(*) AS accepted, COUNT(DISTINCT ref) AS refs FROM proposals " +
|
|
411
|
+
"WHERE status = 'accepted' AND updated_at >= ? AND (? IS NULL OR updated_at < ?)")
|
|
412
|
+
.get(since, until ?? null, until ?? null);
|
|
413
|
+
return { acceptedProposals: row?.accepted ?? 0, distinctRefs: row?.refs ?? 0 };
|
|
414
|
+
}
|
|
415
|
+
catch {
|
|
416
|
+
return { acceptedProposals: 0, distinctRefs: 0 };
|
|
417
|
+
}
|
|
418
|
+
}
|