akm-cli 0.9.17-alpha.7 → 0.9.17-alpha.9
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 +473 -0
- package/STABILITY.md +9 -8
- package/dist/akm +55 -22
- package/dist/akm-migrate +38 -19
- package/dist/assets/hints/cli-hints-full.md +6 -7
- package/dist/assets/improve-strategies/catchup.json +0 -3
- package/dist/assets/improve-strategies/consolidate.json +0 -1
- package/dist/assets/improve-strategies/default.json +1 -2
- package/dist/assets/improve-strategies/proactive-maintenance.json +1 -2
- package/dist/assets/improve-strategies/quick.json +1 -2
- package/dist/assets/improve-strategies/reflect-distill.json +1 -2
- package/dist/assets/improve-strategies/thorough.json +0 -3
- package/dist/assets/prompts/consolidate-pair.md +20 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +20 -20
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -2
- package/dist/assets/templates/html/health.html +3 -5
- package/dist/cli/retired-commands.js +1 -1
- package/dist/commands/health/archive-usage.js +98 -0
- package/dist/commands/health/data-dir-usage.js +25 -13
- package/dist/commands/health/html-report.js +1 -4
- package/dist/commands/health/improve-metrics.js +0 -25
- package/dist/commands/health/md-report.js +1 -6
- package/dist/commands/health/report-view-model.js +4 -14
- package/dist/commands/health/windows.js +0 -1
- package/dist/commands/health.js +13 -0
- package/dist/commands/improve/consolidate/continuity-check.js +137 -0
- package/dist/commands/improve/consolidate/pair-pass.js +791 -0
- package/dist/commands/improve/consolidate.js +38 -63
- package/dist/commands/improve/extract-prompt.js +1 -2
- package/dist/commands/improve/improve-cli.js +1 -1
- package/dist/commands/improve/improve-strategies.js +23 -5
- package/dist/commands/improve/improve.js +19 -30
- package/dist/commands/improve/ledger.js +3 -2
- package/dist/commands/improve/loop-stages.js +5 -84
- package/dist/commands/improve/memory/memory-belief.js +3 -1
- package/dist/commands/improve/memory/memory-improve.js +269 -11
- package/dist/commands/improve/planner.js +0 -5
- package/dist/commands/improve/preparation.js +20 -135
- package/dist/commands/improve/retrieval-scope.js +19 -4
- package/dist/commands/improve/salience.js +1 -14
- package/dist/commands/improve/stage.js +0 -1
- package/dist/commands/lint/base-linter.js +19 -11
- package/dist/commands/proposal/drain.js +8 -1
- package/dist/commands/proposal/proposal-cli.js +16 -2
- package/dist/commands/proposal/proposal-types.js +7 -0
- package/dist/commands/proposal/proposal.js +37 -6
- package/dist/commands/proposal/repository.js +613 -4
- package/dist/commands/proposal/validators/proposals.js +9 -0
- package/dist/commands/read/curate.js +40 -13
- package/dist/commands/read/knowledge.js +3 -2
- package/dist/commands/read/show.js +55 -16
- package/dist/commands/sources/info.js +3 -0
- package/dist/commands/sources/stash-cli.js +2 -2
- package/dist/core/adapter/adapters/akm-adapter.js +2 -0
- package/dist/core/adapter/adapters/akm-metadata.js +31 -0
- package/dist/core/bundle-rename.js +1 -7
- package/dist/core/config/config-schema.js +8 -1
- package/dist/core/config/config.js +23 -48
- package/dist/core/config/engine-semantics.js +0 -2
- package/dist/core/config/schema/improve-processes.js +17 -42
- package/dist/core/config/schema/index-config.js +5 -25
- package/dist/core/file-change.js +13 -5
- package/dist/core/improve-result.js +16 -5
- package/dist/core/improve-types.js +0 -1
- package/dist/core/loopback.js +7 -12
- package/dist/core/parse.js +13 -16
- package/dist/core/state/migrations.js +15 -0
- package/dist/core/time.js +0 -20
- package/dist/indexer/db/llm-cache.js +2 -2
- package/dist/indexer/ensure-index.js +2 -2
- package/dist/indexer/index-written-assets.js +2 -3
- package/dist/indexer/indexer.js +18 -418
- package/dist/indexer/links/declared-links.js +90 -0
- package/dist/indexer/passes/metadata.js +0 -19
- package/dist/indexer/scan/doc-to-entry.js +1 -0
- package/dist/indexer/walk/walker.js +3 -4
- package/dist/llm/client.js +8 -10
- package/dist/llm/embedders/remote.js +1 -2
- package/dist/llm/feature-gate.js +0 -5
- package/dist/output/shapes/helpers.js +23 -4
- package/dist/output/text/command-format.js +0 -8
- package/dist/output/text/proposal-format.js +47 -1
- package/dist/output/text/show-format.js +13 -17
- package/dist/scripts/akm-migrate-node.js +2754 -2836
- package/dist/scripts/akm-migrate.js +2754 -2836
- package/dist/setup/steps/connection.js +5 -6
- package/dist/setup/steps/platforms.js +2 -2
- package/dist/sources/providers/git-stash.js +55 -4
- package/dist/storage/repositories/improve-ledger-repository.js +48 -7
- package/dist/storage/repositories/index-entries-repository.js +16 -13
- package/dist/storage/repositories/index-entry-schema.js +22 -3
- package/dist/storage/repositories/index-links-repository.js +143 -0
- package/dist/storage/repositories/index-llm-cache-repository.js +7 -26
- package/dist/storage/repositories/index-schema.js +82 -104
- package/dist/storage/repositories/proposals-repository.js +61 -0
- package/dist/storage/repositories/salience-repository.js +1 -19
- package/dist/tasks/source/task-to-v4.js +462 -74
- package/docs/migration/release-notes/0.9.17.md +7 -5
- package/docs/reference/cli.md +33 -21
- package/docs/reference/configuration.md +21 -12
- package/docs/reference/data-and-telemetry.md +0 -1
- package/package.json +1 -1
- package/schemas/akm-config.json +0 -342
- package/dist/assets/improve-strategies/graph-refresh.json +0 -15
- package/dist/assets/prompts/contradiction-judge.md +0 -33
- package/dist/assets/prompts/graph-extract-system.md +0 -1
- package/dist/assets/prompts/graph-extract-user-prompt.md +0 -35
- package/dist/assets/prompts/metadata-enhance-system.md +0 -1
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +0 -4
- package/dist/indexer/db/graph-db.js +0 -431
- package/dist/indexer/graph/graph-extraction.js +0 -807
- package/dist/indexer/graph/graph-related.js +0 -131
- package/dist/indexer/graph/graph-types.js +0 -4
- package/dist/llm/graph-extract.js +0 -903
- package/dist/llm/metadata-enhance.js +0 -95
- package/dist/tasks/source/task-to-v3.js +0 -453
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
import { stashDirFor } from "../../core/asset/asset-placement.js";
|
|
5
|
+
import { isBundleSlug, parseBundleRef } from "../../core/asset/asset-ref.js";
|
|
6
|
+
/** Channel order is the order links are stored and shown in. */
|
|
7
|
+
const CHANNELS = [
|
|
8
|
+
["xref", (doc) => doc.xrefs],
|
|
9
|
+
["superseded_by", (doc) => doc.supersededBy],
|
|
10
|
+
["contradicted_by", (doc) => doc.contradictedBy],
|
|
11
|
+
["belief_peer", (doc) => doc.currentBeliefRefs],
|
|
12
|
+
["derived_from", (doc) => doc.derivedFrom],
|
|
13
|
+
["cites", (doc) => doc.sources],
|
|
14
|
+
["links_to", (doc) => doc.links],
|
|
15
|
+
["uses", (doc) => doc.uses],
|
|
16
|
+
];
|
|
17
|
+
/** The links an indexed document declares, in channel then authored order; one per (kind, target). */
|
|
18
|
+
export function declaredLinks(doc, owner) {
|
|
19
|
+
const links = [];
|
|
20
|
+
const seen = new Set();
|
|
21
|
+
for (const [kind, read] of CHANNELS) {
|
|
22
|
+
for (const raw of stringValues(read(doc))) {
|
|
23
|
+
const target = linkTarget(raw, owner.conceptId);
|
|
24
|
+
if (!target)
|
|
25
|
+
continue;
|
|
26
|
+
const bundle = target.bundle ?? owner.bundleId;
|
|
27
|
+
if (bundle === owner.bundleId && target.conceptId === owner.conceptId)
|
|
28
|
+
continue;
|
|
29
|
+
const key = `${kind}\0${bundle}\0${target.conceptId}`;
|
|
30
|
+
if (seen.has(key))
|
|
31
|
+
continue;
|
|
32
|
+
seen.add(key);
|
|
33
|
+
links.push({ kind, raw, ...target });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return links;
|
|
37
|
+
}
|
|
38
|
+
function stringValues(value) {
|
|
39
|
+
if (typeof value === "string")
|
|
40
|
+
return [value];
|
|
41
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The target a token names, or `undefined` when the token is not an asset ref
|
|
45
|
+
* (a session id, a URL, prose, a template placeholder). Retired spellings
|
|
46
|
+
* convert in memory: `<type>:<name>` (`memory:x`), `wiki:<wiki>/<path>` (0.8
|
|
47
|
+
* wikis, now knowledge under `wikis/`), a `.md` suffix, and a `#fragment`.
|
|
48
|
+
* Inside a wiki page, `raw/…` and `pages/…` are relative to the wiki root.
|
|
49
|
+
*/
|
|
50
|
+
function linkTarget(raw, ownerConceptId) {
|
|
51
|
+
const token = raw.trim();
|
|
52
|
+
if (!token || /[\s<*]/.test(token) || token.includes("$(") || token.includes("${"))
|
|
53
|
+
return;
|
|
54
|
+
let bundle;
|
|
55
|
+
let body = token;
|
|
56
|
+
const boundary = token.indexOf("//");
|
|
57
|
+
if (boundary >= 0) {
|
|
58
|
+
bundle = token.slice(0, boundary);
|
|
59
|
+
body = token.slice(boundary + 2);
|
|
60
|
+
if (!isBundleSlug(bundle))
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const conceptId = legacyConceptId(body.split("#", 1)[0] ?? "", ownerConceptId);
|
|
64
|
+
if (conceptId === undefined || conceptId.length <= 1 || conceptId.includes("//"))
|
|
65
|
+
return;
|
|
66
|
+
try {
|
|
67
|
+
return { ...(bundle ? { bundle } : {}), conceptId: parseBundleRef(conceptId).conceptId };
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function legacyConceptId(body, ownerConceptId) {
|
|
74
|
+
const stripped = body.replace(/\.md$/i, "");
|
|
75
|
+
const colon = stripped.indexOf(":");
|
|
76
|
+
if (colon >= 0) {
|
|
77
|
+
const type = stripped.slice(0, colon);
|
|
78
|
+
const name = stripped.slice(colon + 1);
|
|
79
|
+
if (!name || name.includes(":"))
|
|
80
|
+
return;
|
|
81
|
+
if (type === "wiki")
|
|
82
|
+
return `knowledge/wikis/${name}`;
|
|
83
|
+
const stashDir = stashDirFor(type);
|
|
84
|
+
return stashDir === undefined ? undefined : `${stashDir}/${name}`;
|
|
85
|
+
}
|
|
86
|
+
const wikiRoot = /^((?:[^/]+\/)*?wikis\/[^/]+\/)/.exec(ownerConceptId)?.[1];
|
|
87
|
+
if (wikiRoot && /^(raw|pages)\//.test(stripped))
|
|
88
|
+
return `${wikiRoot}${stripped}`;
|
|
89
|
+
return stripped;
|
|
90
|
+
}
|
|
@@ -743,25 +743,6 @@ function mergeAliases(existing, generated) {
|
|
|
743
743
|
const merged = normalizeTerms([...(existing ?? []), ...generated]);
|
|
744
744
|
return merged.length > 0 ? merged : undefined;
|
|
745
745
|
}
|
|
746
|
-
// ── Enrichment Completeness ─────────────────────────────────────────────────
|
|
747
|
-
/**
|
|
748
|
-
* Returns `true` when a stash entry already has enough LLM-quality metadata
|
|
749
|
-
* that calling the LLM would produce no meaningful improvement.
|
|
750
|
-
*
|
|
751
|
-
* An entry is considered complete when ALL of the following hold:
|
|
752
|
-
* - `description` is a non-empty string
|
|
753
|
-
* - `tags` is a non-empty array
|
|
754
|
-
* - `searchHints` is a non-empty array
|
|
755
|
-
*
|
|
756
|
-
* This predicate is used by `enhanceDirsWithLlm` to skip the LLM call for
|
|
757
|
-
* entries that were previously enriched and already carry all three fields.
|
|
758
|
-
*/
|
|
759
|
-
export function isEnrichmentComplete(entry) {
|
|
760
|
-
const hasDescription = typeof entry.description === "string" && entry.description.trim().length > 0;
|
|
761
|
-
const hasTags = Array.isArray(entry.tags) && entry.tags.length > 0;
|
|
762
|
-
const hasSearchHints = Array.isArray(entry.searchHints) && entry.searchHints.length > 0;
|
|
763
|
-
return hasDescription && hasTags && hasSearchHints;
|
|
764
|
-
}
|
|
765
746
|
// ── Native Markdown search projection ──────────────────────────────────────
|
|
766
747
|
/**
|
|
767
748
|
* Maximum native Markdown prose carried by the low-weight `content` field.
|
|
@@ -138,6 +138,7 @@ export function indexDocumentToStashEntry(doc) {
|
|
|
138
138
|
entry.wikiRole = dj.wikiRole;
|
|
139
139
|
assignStringList(entry, "sources", dj.sources);
|
|
140
140
|
assignStringList(entry, "evidenceSources", dj.evidenceSources);
|
|
141
|
+
assignStringList(entry, "uses", dj.uses);
|
|
141
142
|
// D2 (#730): OKF v0.2 provenance promoteProposal stamps onto AKM-native
|
|
142
143
|
// writes, carried via DOCUMENT_JSON_CARRIED_FIELDS (akm-adapter.ts) —
|
|
143
144
|
// unpacked back to a first-class member here exactly like `sources`/
|
|
@@ -169,10 +169,9 @@ function isInsideGitRepo(dir) {
|
|
|
169
169
|
/**
|
|
170
170
|
* Recursively collect every `.md` file under `root`.
|
|
171
171
|
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
* instead of silently looking like a clean, empty scan.
|
|
172
|
+
* Used by memory-inference. Mirrors the `complete` tracking of the other
|
|
173
|
+
* walkers in this file: a directory that cannot be read makes the result
|
|
174
|
+
* incomplete instead of silently looking like a clean, empty scan.
|
|
176
175
|
*/
|
|
177
176
|
export function walkMarkdownFiles(root) {
|
|
178
177
|
const files = [];
|
package/dist/llm/client.js
CHANGED
|
@@ -101,10 +101,9 @@ function retryBackoffMs() {
|
|
|
101
101
|
* narrating about a document) does not get misclassified as a provider
|
|
102
102
|
* context-limit error (#496).
|
|
103
103
|
*
|
|
104
|
-
* Canonical home: `
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* must never be retried.
|
|
104
|
+
* Canonical home: `structured-call.ts`'s failure classifier and the retry
|
|
105
|
+
* classifier (`isRetryable`) here share one definition — retrying a context
|
|
106
|
+
* overflow cannot shrink the input, so it must never be retried.
|
|
108
107
|
*/
|
|
109
108
|
export function isContextSizeError(message) {
|
|
110
109
|
const lower = message.toLowerCase();
|
|
@@ -120,13 +119,12 @@ export function isContextSizeError(message) {
|
|
|
120
119
|
/**
|
|
121
120
|
* Codes describing a failure to reach or get a usable response from the
|
|
122
121
|
* provider transport itself, as opposed to a malformed-but-received response
|
|
123
|
-
* (`parse_error`) or a request-shape rejection (`rate_limited`).
|
|
124
|
-
* {@link isRetryable}
|
|
125
|
-
* transient
|
|
126
|
-
*
|
|
127
|
-
* per-asset") so the two classifications cannot drift apart.
|
|
122
|
+
* (`parse_error`) or a request-shape rejection (`rate_limited`). Used by
|
|
123
|
+
* {@link isRetryable}, which additionally requires evidence the failure is
|
|
124
|
+
* transient. Not exported — its one external caller (batched LLM
|
|
125
|
+
* entity-graph extraction) was retired in 0.9.17-alpha.9.
|
|
128
126
|
*/
|
|
129
|
-
|
|
127
|
+
function isTransportFailure(err) {
|
|
130
128
|
return err.code === "provider_error" || err.code === "network_error" || err.code === "provider_html_error";
|
|
131
129
|
}
|
|
132
130
|
/**
|
|
@@ -187,8 +187,7 @@ export function isContextExceededResponse(status, body) {
|
|
|
187
187
|
* Resolve the effective in-flight request window for `RemoteEmbedder.embedBatch`.
|
|
188
188
|
* Default (unset `embedding.concurrency`): 1 for a loopback endpoint, 2 for a
|
|
189
189
|
* remote one, via the shared `defaultConcurrencyForEndpoint`
|
|
190
|
-
* (`src/core/loopback.ts`)
|
|
191
|
-
* `getDefaultLlmConcurrency` (`src/indexer/indexer.ts`) uses.
|
|
190
|
+
* (`src/core/loopback.ts`).
|
|
192
191
|
*
|
|
193
192
|
* `embedding.concurrency` (#954) overrides this default in
|
|
194
193
|
* either direction, bounded 1-16 at the config schema — added after field
|
package/dist/llm/feature-gate.js
CHANGED
|
@@ -7,8 +7,6 @@
|
|
|
7
7
|
*/
|
|
8
8
|
const FEATURE_LOCATION = {
|
|
9
9
|
memory_inference: (cfg) => cfg.index?.memory?.enabled ?? true,
|
|
10
|
-
graph_extraction: (cfg) => cfg.index?.graph?.enabled ?? true,
|
|
11
|
-
metadata_enhance: (cfg) => cfg.index?.metadataEnhance?.enabled ?? false,
|
|
12
10
|
// #951: a real implementation of the dead `curate_rerank` key removed in
|
|
13
11
|
// 0.8.0. Off by default — it requires a `search.curateRerank.endpoint` a
|
|
14
12
|
// caller must explicitly configure.
|
|
@@ -82,9 +80,6 @@ export function isProcessEnabled(section, processName, config) {
|
|
|
82
80
|
if (processName === "memory_inference" || processName === "memoryInference") {
|
|
83
81
|
return isLlmFeatureEnabled(config, "memory_inference");
|
|
84
82
|
}
|
|
85
|
-
if (processName === "graph_extraction" || processName === "graphExtraction") {
|
|
86
|
-
return isLlmFeatureEnabled(config, "graph_extraction");
|
|
87
|
-
}
|
|
88
83
|
}
|
|
89
84
|
return false;
|
|
90
85
|
}
|
|
@@ -56,11 +56,25 @@ export function shapeProposalProducerOutput(result, detail) {
|
|
|
56
56
|
}
|
|
57
57
|
export function shapeProposalEntry(entry, detail) {
|
|
58
58
|
if (detail === "brief") {
|
|
59
|
-
|
|
59
|
+
// `proposal list`'s default detail level IS "brief" (no `--detail` flag
|
|
60
|
+
// given projects here, not "normal") — `retirement` itself is not
|
|
61
|
+
// projected until "normal", so a flagged retire proposal's risk would
|
|
62
|
+
// otherwise be invisible by default. A minimal boolean marker, not the
|
|
63
|
+
// full risk object, keeps brief's shape small while still letting a
|
|
64
|
+
// reviewer see it without an extra flag.
|
|
65
|
+
const retirement = entry.retirement;
|
|
66
|
+
return {
|
|
67
|
+
...pickFields(entry, ["id", "ref", "status", "source", "createdAt"]),
|
|
68
|
+
...(retirement?.continuityRisk ? { continuityRisk: true } : {}),
|
|
69
|
+
};
|
|
60
70
|
}
|
|
61
71
|
if (detail === "normal") {
|
|
62
72
|
// `confidence` and `gateDecision` (#577) explain why a proposal is pending,
|
|
63
73
|
// so they are projected at `normal` for `akm proposal list/show` when present.
|
|
74
|
+
// `retirement` (alpha.9) is the same kind of field for a consolidate
|
|
75
|
+
// `retire` proposal: without it, "list/show" carries no reason a reviewer
|
|
76
|
+
// could act on (its `payload.content` is empty by design — a retire
|
|
77
|
+
// proposal deletes, it does not write).
|
|
64
78
|
return pickFields(entry, [
|
|
65
79
|
"id",
|
|
66
80
|
"ref",
|
|
@@ -72,6 +86,7 @@ export function shapeProposalEntry(entry, detail) {
|
|
|
72
86
|
"confidence",
|
|
73
87
|
"gateDecision",
|
|
74
88
|
"review",
|
|
89
|
+
"retirement",
|
|
75
90
|
]);
|
|
76
91
|
}
|
|
77
92
|
// full: project everything including the payload.
|
|
@@ -87,6 +102,10 @@ export function shapeProposalEntry(entry, detail) {
|
|
|
87
102
|
"gateDecision",
|
|
88
103
|
"payload",
|
|
89
104
|
"review",
|
|
105
|
+
"retirement",
|
|
106
|
+
"retiredArchive",
|
|
107
|
+
"promotionSource",
|
|
108
|
+
"promotionSourceHash",
|
|
90
109
|
]);
|
|
91
110
|
}
|
|
92
111
|
export function shapeProposalListOutput(result, detail) {
|
|
@@ -357,7 +376,7 @@ export function shapeShowOutput(result, detail, shape = "human") {
|
|
|
357
376
|
"workflowParameters",
|
|
358
377
|
"steps",
|
|
359
378
|
"keys",
|
|
360
|
-
"
|
|
379
|
+
"links",
|
|
361
380
|
...FRAGMENT_PROVENANCE_FIELDS,
|
|
362
381
|
...FRAGMENT_CONTEXT_FIELDS,
|
|
363
382
|
]);
|
|
@@ -381,7 +400,7 @@ export function shapeShowOutput(result, detail, shape = "human") {
|
|
|
381
400
|
"run",
|
|
382
401
|
"origin",
|
|
383
402
|
"keys",
|
|
384
|
-
"
|
|
403
|
+
"links",
|
|
385
404
|
...FRAGMENT_PROVENANCE_FIELDS,
|
|
386
405
|
...FRAGMENT_CONTEXT_FIELDS,
|
|
387
406
|
]);
|
|
@@ -410,7 +429,7 @@ export function shapeShowOutput(result, detail, shape = "human") {
|
|
|
410
429
|
"cwd",
|
|
411
430
|
"activeRun",
|
|
412
431
|
"keys",
|
|
413
|
-
"
|
|
432
|
+
"links",
|
|
414
433
|
...FRAGMENT_PROVENANCE_FIELDS,
|
|
415
434
|
...FRAGMENT_CONTEXT_FIELDS,
|
|
416
435
|
// ref, path, and editable are always projected — at every --detail level,
|
|
@@ -407,14 +407,6 @@ export function formatIndexPlain(r) {
|
|
|
407
407
|
for (const message of warnings)
|
|
408
408
|
out += `\n - ${String(message)}`;
|
|
409
409
|
}
|
|
410
|
-
const notices = Array.isArray(indexResult.notices) ? indexResult.notices : [];
|
|
411
|
-
for (const notice of notices) {
|
|
412
|
-
const severity = notice.severity === "info" ? "info" : "warning";
|
|
413
|
-
const field = typeof notice.field === "string" ? ` field=${notice.field}` : "";
|
|
414
|
-
out +=
|
|
415
|
-
`\n notice[${severity}] ${notice.code} adapter=${notice.adapter}${field}` +
|
|
416
|
-
(notice.message ? `: ${notice.message}` : "");
|
|
417
|
-
}
|
|
418
410
|
const verification = indexResult.verification;
|
|
419
411
|
if (verification?.ok === false && verification.message) {
|
|
420
412
|
out += `\nVerification: ${String(verification.message)}`;
|
|
@@ -100,7 +100,16 @@ export function formatProposalListPlain(r) {
|
|
|
100
100
|
// #577: surface the gate verdict inline so the queue explains itself.
|
|
101
101
|
const gate = formatGateDecisionSummary(p.gateDecision);
|
|
102
102
|
const gateSuffix = gate ? ` ${gate}` : "";
|
|
103
|
-
|
|
103
|
+
// A retire proposal's continuityRisk excludes it from bulk accept —
|
|
104
|
+
// visible in the default list, not just `proposal show`, since the owner
|
|
105
|
+
// reviews the backlog list-first. `list`'s default (brief) shape
|
|
106
|
+
// projects a `continuityRisk: true` marker instead of the full
|
|
107
|
+
// `retirement` object (only present from `--detail normal` up), so both
|
|
108
|
+
// shapes are checked here.
|
|
109
|
+
const retirement = p.retirement;
|
|
110
|
+
const flagged = p.continuityRisk === true || Boolean(retirement?.continuityRisk);
|
|
111
|
+
const continuitySuffix = flagged ? " ⚠ continuity-risk" : "";
|
|
112
|
+
lines.push(`${id} [${status}] ${ref} source=${source} ${created}${gateSuffix}${continuitySuffix}`);
|
|
104
113
|
}
|
|
105
114
|
return lines.join("\n").trimEnd();
|
|
106
115
|
}
|
|
@@ -141,6 +150,43 @@ export function formatProposalShowPlain(r) {
|
|
|
141
150
|
if (review.decidedAt)
|
|
142
151
|
lines.push(`review.decidedAt: ${String(review.decidedAt)}`);
|
|
143
152
|
}
|
|
153
|
+
// alpha.9: a consolidate retire proposal writes no content (`payload.content`
|
|
154
|
+
// is empty by design) — this is the reason a reviewer needs instead. `diff`
|
|
155
|
+
// shows the body being retired.
|
|
156
|
+
const retirement = p.retirement;
|
|
157
|
+
if (retirement) {
|
|
158
|
+
lines.push(`retire: ${String(retirement.retiredRef)} -> ${String(retirement.successorRef)}`);
|
|
159
|
+
lines.push(`retire.label: ${String(retirement.judgeLabel)} (cosine=${String(retirement.cosine)})`);
|
|
160
|
+
lines.push(`retire.reason: ${String(retirement.judgeReason)}`);
|
|
161
|
+
// Item 1 (continuity check): flagged, but still minted — never swept by a
|
|
162
|
+
// bulk accept, only acceptable by id, so a reviewer must see it here.
|
|
163
|
+
const continuityRisk = retirement.continuityRisk;
|
|
164
|
+
if (continuityRisk) {
|
|
165
|
+
const failingQueries = typeof continuityRisk.failingQueries === "number" ? continuityRisk.failingQueries : 0;
|
|
166
|
+
const unverifiedQueries = typeof continuityRisk.unverifiedQueries === "number" ? continuityRisk.unverifiedQueries : 0;
|
|
167
|
+
const summary = [];
|
|
168
|
+
if (failingQueries > 0) {
|
|
169
|
+
summary.push(`${failingQueries} of the retired asset's own quer${failingQueries === 1 ? "y" : "ies"} would not have found the successor top 10`);
|
|
170
|
+
}
|
|
171
|
+
// S2: a query the search call never ran, or that fell back to
|
|
172
|
+
// keyword-only ranking, is never silently trusted OR silently
|
|
173
|
+
// dropped — it excludes the proposal from bulk accept on its own.
|
|
174
|
+
if (unverifiedQueries > 0) {
|
|
175
|
+
summary.push(`${unverifiedQueries} quer${unverifiedQueries === 1 ? "y" : "ies"} unverified (search failed or used the keyword-only fallback)`);
|
|
176
|
+
}
|
|
177
|
+
lines.push(`retire.continuityRisk: ${summary.join("; ")} — excluded from bulk accept`);
|
|
178
|
+
// N3 / S4: the actual failing query text, not just the count — a
|
|
179
|
+
// reviewer deciding whether to accept by id needs to see what would
|
|
180
|
+
// stop resolving, not just how many queries.
|
|
181
|
+
const ranks = Array.isArray(continuityRisk.ranks) ? continuityRisk.ranks : [];
|
|
182
|
+
for (const rank of ranks) {
|
|
183
|
+
const successorRank = rank.successorRank === null || rank.successorRank === undefined
|
|
184
|
+
? "absent from top 10"
|
|
185
|
+
: `#${String(rank.successorRank)}`;
|
|
186
|
+
lines.push(` - "${String(rank.query)}": retired #${String(rank.retiredRank)}, successor ${successorRank}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
144
190
|
const validation = r.validation;
|
|
145
191
|
if (validation) {
|
|
146
192
|
const findings = Array.isArray(validation.findings) ? validation.findings : [];
|
|
@@ -54,17 +54,20 @@ export function formatShowPlain(r, detail) {
|
|
|
54
54
|
if (r.schemaVersion !== undefined)
|
|
55
55
|
lines.push(`schemaVersion: ${String(r.schemaVersion)}`);
|
|
56
56
|
}
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
if (related) {
|
|
57
|
+
const links = typeof r.links === "object" && r.links !== null ? r.links : undefined;
|
|
58
|
+
if (links) {
|
|
60
59
|
lines.push("");
|
|
61
|
-
lines.push(
|
|
62
|
-
for (const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
lines.push("links:");
|
|
61
|
+
for (const part of ["outgoing", "incoming", "unresolved"]) {
|
|
62
|
+
const groups = links[part];
|
|
63
|
+
if (typeof groups !== "object" || groups === null)
|
|
64
|
+
continue;
|
|
65
|
+
lines.push(` ${part}:`);
|
|
66
|
+
for (const [kind, group] of Object.entries(groups)) {
|
|
67
|
+
const refs = Array.isArray(group.refs) ? group.refs.map(String) : [];
|
|
68
|
+
const more = (group.total ?? refs.length) > refs.length ? ` (+${(group.total ?? 0) - refs.length} more)` : "";
|
|
69
|
+
lines.push(` ${kind}: ${refs.join(", ")}${more}`);
|
|
70
|
+
}
|
|
68
71
|
}
|
|
69
72
|
}
|
|
70
73
|
const payloads = [r.content, r.template, r.prompt].filter((value) => value != null).map(String);
|
|
@@ -98,10 +101,3 @@ export function formatShowPlain(r, detail) {
|
|
|
98
101
|
appendShowDirectives(lines, r);
|
|
99
102
|
return lines.length > 0 ? lines.join("\n") : null;
|
|
100
103
|
}
|
|
101
|
-
function formatRelatedLabel(hit) {
|
|
102
|
-
const ref = typeof hit.ref === "string" ? hit.ref : undefined;
|
|
103
|
-
if (ref)
|
|
104
|
-
return ref;
|
|
105
|
-
const pathValue = typeof hit.path === "string" ? hit.path : "?";
|
|
106
|
-
return pathValue.split("/").pop() ?? pathValue;
|
|
107
|
-
}
|