@fenglimg/fabric-server 2.0.0-rc.25 → 2.0.0-rc.27
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.
|
@@ -184,6 +184,46 @@ var EVENT_LEDGER_DEFAULT_RETENTION_DAYS = 30;
|
|
|
184
184
|
var EVENT_LEDGER_SIZE_WARN_BYTES = 50 * 1024 * 1024;
|
|
185
185
|
var EVENT_LEDGER_ARCHIVE_DIR = ".fabric/events.archive";
|
|
186
186
|
var warnedOversize = false;
|
|
187
|
+
var knownEventTypesCache = null;
|
|
188
|
+
function getKnownEventTypes() {
|
|
189
|
+
if (knownEventTypesCache !== null) return knownEventTypesCache;
|
|
190
|
+
const set = /* @__PURE__ */ new Set();
|
|
191
|
+
for (const opt of eventLedgerEventSchema.options) {
|
|
192
|
+
const shape = opt.shape;
|
|
193
|
+
if (shape && typeof shape.event_type?.value === "string") {
|
|
194
|
+
set.add(shape.event_type.value);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
knownEventTypesCache = set;
|
|
198
|
+
return set;
|
|
199
|
+
}
|
|
200
|
+
function classifyRejection(line, index) {
|
|
201
|
+
let parsed;
|
|
202
|
+
try {
|
|
203
|
+
parsed = JSON.parse(line);
|
|
204
|
+
} catch {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
if (parsed === null || typeof parsed !== "object") return null;
|
|
208
|
+
if ("schema_version" in parsed && parsed.schema_version !== 1 && (typeof parsed.schema_version === "number" || parsed.schema_version === null)) {
|
|
209
|
+
return {
|
|
210
|
+
kind: "schema_version_unsupported",
|
|
211
|
+
line_index: index,
|
|
212
|
+
schema_version: parsed.schema_version,
|
|
213
|
+
snippet_first_120: line.slice(0, 120)
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
const known = getKnownEventTypes();
|
|
217
|
+
if (typeof parsed.event_type === "string" && !known.has(parsed.event_type)) {
|
|
218
|
+
return {
|
|
219
|
+
kind: "event_type_unknown",
|
|
220
|
+
line_index: index,
|
|
221
|
+
event_type: parsed.event_type,
|
|
222
|
+
snippet_first_120: line.slice(0, 120)
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
187
227
|
async function appendEventLedgerEvent(projectRoot, event) {
|
|
188
228
|
const eventPath = getEventLedgerPath(projectRoot);
|
|
189
229
|
const nextEvent = eventLedgerEventSchema.parse({
|
|
@@ -238,8 +278,20 @@ async function readEventLedger(projectRoot, options = {}) {
|
|
|
238
278
|
snippet_first_120: partialLine.slice(0, 120)
|
|
239
279
|
});
|
|
240
280
|
}
|
|
241
|
-
const
|
|
242
|
-
|
|
281
|
+
const trimmed = lines.map((line) => line.trim()).filter((line) => line.length > 0);
|
|
282
|
+
const events = [];
|
|
283
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
284
|
+
const line = trimmed[i];
|
|
285
|
+
const parsed = parseEventLedgerLine(line, i);
|
|
286
|
+
if (parsed !== null) {
|
|
287
|
+
events.push(parsed);
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
const rejection = classifyRejection(line, i);
|
|
291
|
+
if (rejection !== null) warnings.push(rejection);
|
|
292
|
+
}
|
|
293
|
+
const filtered = events.filter((entry) => options.event_type === void 0 || entry.event_type === options.event_type).filter((entry) => options.since === void 0 || entry.ts >= options.since).filter((entry) => options.correlation_id === void 0 || entry.correlation_id === options.correlation_id).filter((entry) => options.session_id === void 0 || entry.session_id === options.session_id);
|
|
294
|
+
return { events: filtered, warnings };
|
|
243
295
|
}
|
|
244
296
|
async function truncateLedgerToLastNewline(path2) {
|
|
245
297
|
const raw = await readFile2(path2);
|
|
@@ -951,16 +1003,18 @@ function extractRuleDescription(source) {
|
|
|
951
1003
|
}
|
|
952
1004
|
const heading = /^#\s+(.+?)\s*$/mu.exec(source);
|
|
953
1005
|
const summary = heading?.[1]?.trim();
|
|
954
|
-
|
|
1006
|
+
const knowledge = frontmatter !== null ? extractKnowledgeFieldsFromFrontmatter(frontmatter[1]) : void 0;
|
|
1007
|
+
const isStructurallyAKnowledgeEntry = summary !== void 0 && summary.length > 0 ? true : knowledge !== void 0 && (knowledge.id !== void 0 || knowledge.knowledge_type !== void 0 || knowledge.tags !== void 0 && knowledge.tags.length > 0);
|
|
1008
|
+
if (!isStructurallyAKnowledgeEntry) {
|
|
955
1009
|
return void 0;
|
|
956
1010
|
}
|
|
957
|
-
const
|
|
1011
|
+
const synthesizedSummary = summary !== void 0 && summary.length > 0 ? summary : knowledge?.id ?? (knowledge?.tags !== void 0 && knowledge.tags.length > 0 ? `(unnamed; tags: ${knowledge.tags.join(", ")})` : "(unnamed knowledge entry)");
|
|
958
1012
|
return {
|
|
959
|
-
summary,
|
|
1013
|
+
summary: synthesizedSummary,
|
|
960
1014
|
intent_clues: [],
|
|
961
1015
|
tech_stack: [],
|
|
962
1016
|
impact: [],
|
|
963
|
-
must_read_if:
|
|
1017
|
+
must_read_if: synthesizedSummary,
|
|
964
1018
|
// v2.0-rc.22: when frontmatter is present, merge its knowledge fields;
|
|
965
1019
|
// when fully absent (no `---` block), all knowledge fields stay
|
|
966
1020
|
// undefined, matching the original heading-only fallback contract.
|
|
@@ -1473,10 +1527,9 @@ async function reconcileKnowledge(projectRoot, opts) {
|
|
|
1473
1527
|
// src/services/serve-lock.ts
|
|
1474
1528
|
import fs from "fs";
|
|
1475
1529
|
import path from "path";
|
|
1476
|
-
import { createTranslator,
|
|
1530
|
+
import { createTranslator, resolveFabricLocale } from "@fenglimg/fabric-shared";
|
|
1477
1531
|
import { IOFabricError as IOFabricError2 } from "@fenglimg/fabric-shared/errors";
|
|
1478
1532
|
var LOCK_FILENAME = ".serve.lock";
|
|
1479
|
-
var t = createTranslator(detectNodeLocale());
|
|
1480
1533
|
var ServeLockHeldError = class extends IOFabricError2 {
|
|
1481
1534
|
code = "SERVE_LOCK_HELD";
|
|
1482
1535
|
httpStatus = 423;
|
|
@@ -1504,6 +1557,7 @@ function acquireLock(projectRoot, opts) {
|
|
|
1504
1557
|
} catch {
|
|
1505
1558
|
}
|
|
1506
1559
|
if (state && state.pid && state.pid !== process.pid && isAlive(state.pid) && !opts?.force) {
|
|
1560
|
+
const t = createTranslator(resolveFabricLocale(projectRoot));
|
|
1507
1561
|
throw new ServeLockHeldError(
|
|
1508
1562
|
`serve lock held by live PID ${state.pid}`,
|
|
1509
1563
|
{
|
|
@@ -1554,6 +1608,7 @@ function checkLockOrThrow(projectRoot, opts) {
|
|
|
1554
1608
|
return;
|
|
1555
1609
|
}
|
|
1556
1610
|
if (opts?.force) return;
|
|
1611
|
+
const t = createTranslator(resolveFabricLocale(projectRoot));
|
|
1557
1612
|
throw new ServeLockHeldError(
|
|
1558
1613
|
`serve lock held by live PID ${state.pid}`,
|
|
1559
1614
|
{
|
|
@@ -1574,6 +1629,7 @@ import { minimatch } from "minimatch";
|
|
|
1574
1629
|
import {
|
|
1575
1630
|
agentsMetaSchema as agentsMetaSchema4,
|
|
1576
1631
|
AgentsMetaCountersSchema,
|
|
1632
|
+
createTranslator as createTranslator2,
|
|
1577
1633
|
forensicReportSchema,
|
|
1578
1634
|
parseKnowledgeId as parseKnowledgeId2,
|
|
1579
1635
|
knowledgeTestIndexSchema as knowledgeTestIndexSchema2,
|
|
@@ -1583,7 +1639,8 @@ import {
|
|
|
1583
1639
|
BOOTSTRAP_MARKER_END,
|
|
1584
1640
|
BOOTSTRAP_REGEX,
|
|
1585
1641
|
ONBOARD_SLOT_NAMES,
|
|
1586
|
-
ONBOARD_SLOT_TOTAL
|
|
1642
|
+
ONBOARD_SLOT_TOTAL,
|
|
1643
|
+
resolveFabricLocale as resolveFabricLocale2
|
|
1587
1644
|
} from "@fenglimg/fabric-shared";
|
|
1588
1645
|
import { detectFramework } from "@fenglimg/fabric-shared/node";
|
|
1589
1646
|
import { atomicWriteJson as atomicWriteJson2, atomicWriteText as atomicWriteText4 } from "@fenglimg/fabric-shared/node/atomic-write";
|
|
@@ -1669,6 +1726,7 @@ var TARGET_FILE_PATHS = [
|
|
|
1669
1726
|
];
|
|
1670
1727
|
async function runDoctorReport(target) {
|
|
1671
1728
|
const projectRoot = normalizeTarget(target);
|
|
1729
|
+
const t = createTranslator2(resolveFabricLocale2(projectRoot));
|
|
1672
1730
|
const framework = detectFramework(projectRoot);
|
|
1673
1731
|
const entryPoints = collectEntryPoints(projectRoot);
|
|
1674
1732
|
const [
|
|
@@ -1722,91 +1780,95 @@ async function runDoctorReport(target) {
|
|
|
1722
1780
|
const skillMdYamlInvalid = inspectSkillMdYamlInvalid(projectRoot);
|
|
1723
1781
|
const onboardCoverage = inspectOnboardCoverage(projectRoot);
|
|
1724
1782
|
const checks = [
|
|
1725
|
-
createBootstrapAnchorCheck(bootstrapAnchor),
|
|
1783
|
+
createBootstrapAnchorCheck(t, bootstrapAnchor),
|
|
1726
1784
|
// v2.0.0-rc.19 TASK-004: bootstrap marker migration check sits adjacent to
|
|
1727
1785
|
// the anchor check — both are bootstrap-file invariants. fixable_error
|
|
1728
1786
|
// when any of the four target paths still carries the legacy marker.
|
|
1729
|
-
createBootstrapMarkerMigrationCheck(bootstrapMarkerMigration),
|
|
1787
|
+
createBootstrapMarkerMigrationCheck(t, bootstrapMarkerMigration),
|
|
1730
1788
|
// v2.0.0-rc.19 TASK-005: L1 + L2 byte-level drift detection sit immediately
|
|
1731
1789
|
// after the marker migration check. Order: anchor existence → migration →
|
|
1732
1790
|
// L1 (canonical ↔ snapshot) → L2 (snapshot+rules ↔ three-end blocks).
|
|
1733
|
-
createL1BootstrapSnapshotDriftCheck(l1BootstrapSnapshotDrift),
|
|
1734
|
-
createL2ManagedBlockDriftCheck(l2ManagedBlockDrift),
|
|
1735
|
-
createKnowledgeDirMissingCheck(knowledgeDirMissing),
|
|
1791
|
+
createL1BootstrapSnapshotDriftCheck(t, l1BootstrapSnapshotDrift),
|
|
1792
|
+
createL2ManagedBlockDriftCheck(t, l2ManagedBlockDrift),
|
|
1793
|
+
createKnowledgeDirMissingCheck(t, knowledgeDirMissing),
|
|
1736
1794
|
// v2.0.0-rc.22 TASK-006: baseline filename format. Sits adjacent to
|
|
1737
1795
|
// knowledge_dir_missing — both are knowledge-layout invariants. manual_error
|
|
1738
1796
|
// kind; resolution is manual file deletion (rc.23 TASK-012 (F8a) removed
|
|
1739
1797
|
// the baseline-emit pipeline, so no auto-fix exists).
|
|
1740
|
-
createBaselineFilenameFormatCheck(baselineFilenameFormat),
|
|
1741
|
-
createForensicCheck(forensic, framework.kind, entryPoints.length),
|
|
1798
|
+
createBaselineFilenameFormatCheck(t, baselineFilenameFormat),
|
|
1799
|
+
createForensicCheck(t, forensic, framework.kind, entryPoints.length),
|
|
1742
1800
|
// v2.0: removed `createInitContextCheck` — `.fabric/init-context.json`
|
|
1743
1801
|
// is owned by the AI-side client init skill, not by `fabric install` CLI.
|
|
1744
1802
|
// The file's absence is a legitimate post-init state when the skill has
|
|
1745
1803
|
// not yet run, so flagging it as a doctor manual_error misrepresents
|
|
1746
1804
|
// ownership.
|
|
1747
|
-
createMetaCheck(meta),
|
|
1748
|
-
createRuleContentRefCheck(meta),
|
|
1805
|
+
createMetaCheck(t, meta),
|
|
1806
|
+
createRuleContentRefCheck(t, meta),
|
|
1749
1807
|
// v2.0 / rc.2: `createRuleSectionsCheck` removed — it parsed v1.x
|
|
1750
1808
|
// [MANDATORY_INJECTION] sections out of legacy rule files, a structural
|
|
1751
1809
|
// concept that has no v2 equivalent. rc.4 will introduce a dedicated v2
|
|
1752
1810
|
// lint suite for the new knowledge frontmatter contract.
|
|
1753
|
-
createKnowledgeTestIndexCheck(knowledgeTestIndex),
|
|
1754
|
-
createEventLedgerCheck(eventLedger),
|
|
1755
|
-
createEventLedgerPartialWriteCheck(eventLedger),
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1811
|
+
createKnowledgeTestIndexCheck(t, knowledgeTestIndex),
|
|
1812
|
+
createEventLedgerCheck(t, eventLedger),
|
|
1813
|
+
createEventLedgerPartialWriteCheck(t, eventLedger),
|
|
1814
|
+
// v2.0.0-rc.27 TASK-010 (audit §2.24): forward-compat warning surface for
|
|
1815
|
+
// events.jsonl rows that fail Zod validation because of unknown
|
|
1816
|
+
// schema_version or event_type tokens. Previously silently dropped.
|
|
1817
|
+
createEventLedgerSchemaCompatCheck(t, eventLedger),
|
|
1818
|
+
createMcpConfigInWrongFileCheck(t, mcpConfigInWrongFile),
|
|
1819
|
+
createMetaManuallyDivergedCheck(t, metaManuallyDiverged),
|
|
1820
|
+
createKnowledgeDirUnindexedCheck(t, knowledgeDirUnindexed),
|
|
1821
|
+
createStableIdCollisionCheck(t, stableIdCollision),
|
|
1822
|
+
createCounterDesyncCheck(t, counterDesync),
|
|
1823
|
+
createFilesystemEditFallbackCheck(t, filesystemEditFallback),
|
|
1762
1824
|
// rc.4 TASK-001: read-side lint checks #16-18. Findings only — mutation
|
|
1763
1825
|
// + event emission lands in TASK-003 behind --apply-lint.
|
|
1764
|
-
createOrphanDemoteCheck(orphanDemote),
|
|
1765
|
-
createStaleArchiveCheck(staleArchive),
|
|
1766
|
-
createPendingOverdueCheck(pendingOverdue),
|
|
1826
|
+
createOrphanDemoteCheck(t, orphanDemote),
|
|
1827
|
+
createStaleArchiveCheck(t, staleArchive),
|
|
1828
|
+
createPendingOverdueCheck(t, pendingOverdue),
|
|
1767
1829
|
// rc.4 TASK-002: read-side integrity checks #19-21. Stable_id duplicate
|
|
1768
1830
|
// runs first in this trio — it is the most critical integrity break and
|
|
1769
1831
|
// surfaces ahead of layer-mismatch / index-drift in the report so a
|
|
1770
1832
|
// human operator triages the collision before reasoning about counter
|
|
1771
1833
|
// state. Index drift is the only fixable_error of the three; stable_id
|
|
1772
1834
|
// duplicate and layer mismatch require manual triage (rename / move).
|
|
1773
|
-
createStableIdDuplicateCheck(stableIdDuplicate),
|
|
1774
|
-
createLayerMismatchCheck(layerMismatch),
|
|
1775
|
-
createIndexDriftCheck(indexDrift),
|
|
1835
|
+
createStableIdDuplicateCheck(t, stableIdDuplicate),
|
|
1836
|
+
createLayerMismatchCheck(t, layerMismatch),
|
|
1837
|
+
createIndexDriftCheck(t, indexDrift),
|
|
1776
1838
|
// rc.5 TASK-010: read-side underseeded-corpus check (#22). Info kind —
|
|
1777
1839
|
// does not bump report status. Recommends running the fabric-import skill
|
|
1778
1840
|
// to backfill knowledge when the corpus is below the threshold floor.
|
|
1779
|
-
createUnderseededCheck(underseeded),
|
|
1841
|
+
createUnderseededCheck(t, underseeded),
|
|
1780
1842
|
// rc.5 TASK-013 (C4): relevance_paths hygiene checks #23/#24/#25.
|
|
1781
1843
|
// All three are flag-only in rc.5 (no apply-lint mutations).
|
|
1782
1844
|
// #23 narrow_no_paths — warning kind (silent recall risk)
|
|
1783
1845
|
// #24 relevance_paths_dangling — warning kind (glob → zero matches)
|
|
1784
1846
|
// #25 relevance_paths_drift — info kind (git-log heuristic; noisy)
|
|
1785
|
-
createNarrowNoPathsCheck(narrowNoPaths),
|
|
1786
|
-
createRelevancePathsDanglingCheck(relevancePathsDangling),
|
|
1787
|
-
createRelevancePathsDriftCheck(relevancePathsDrift),
|
|
1847
|
+
createNarrowNoPathsCheck(t, narrowNoPaths),
|
|
1848
|
+
createRelevancePathsDanglingCheck(t, relevancePathsDangling),
|
|
1849
|
+
createRelevancePathsDriftCheck(t, relevancePathsDrift),
|
|
1788
1850
|
// rc.6 TASK-023 (E6): narrow_too_few (lint #26). Info kind; both arms
|
|
1789
1851
|
// (structural + telemetry) recommend the same fabric-import action.
|
|
1790
|
-
createNarrowTooFewCheck(narrowTooFew),
|
|
1852
|
+
createNarrowTooFewCheck(t, narrowTooFew),
|
|
1791
1853
|
// rc.6 TASK-021 (E3): session-hints cache hygiene (lint #27). Info kind.
|
|
1792
|
-
createSessionHintsStaleCheck(sessionHintsStale),
|
|
1854
|
+
createSessionHintsStaleCheck(t, sessionHintsStale),
|
|
1793
1855
|
// rc.23 TASK-010 (e): stale .fabric/.serve.lock advisory. Info kind —
|
|
1794
1856
|
// does not bump report status. `--fix` unlinks the corpse and emits
|
|
1795
1857
|
// `serve_lock_cleared`.
|
|
1796
|
-
createStaleServeLockCheck(staleServeLock),
|
|
1858
|
+
createStaleServeLockCheck(t, staleServeLock),
|
|
1797
1859
|
// v2.0.0-rc.9 TASK-003 (A3): relevance fields back-fill (lint #28).
|
|
1798
1860
|
// Info kind — applies to pending entries only; canonical entries get
|
|
1799
1861
|
// the fields written verbatim by fab_review.approve/modify.
|
|
1800
|
-
createRelevanceFieldsMissingCheck(relevanceFieldsMissing),
|
|
1862
|
+
createRelevanceFieldsMissingCheck(t, relevanceFieldsMissing),
|
|
1801
1863
|
// rc.12 lint #29: skill_md_yaml_invalid. Warning kind — surfaces
|
|
1802
1864
|
// SKILL.md frontmatter that Codex CLI silently drops at load.
|
|
1803
|
-
createSkillMdYamlInvalidCheck(skillMdYamlInvalid),
|
|
1865
|
+
createSkillMdYamlInvalidCheck(t, skillMdYamlInvalid),
|
|
1804
1866
|
// v2.0.0-rc.23 TASK-014 (F8c): Onboard coverage advisory. Info kind.
|
|
1805
1867
|
// Surfaces uncovered S5 onboard slots and recommends /fabric-archive
|
|
1806
1868
|
// first-run phase. Sits adjacent to Skill markdown YAML — both are
|
|
1807
1869
|
// Skill-adjacent advisories. --fix never mutates onboard state.
|
|
1808
|
-
createOnboardCoverageCheck(onboardCoverage),
|
|
1809
|
-
createPreexistingRootFilesCheck(preexistingRootFiles)
|
|
1870
|
+
createOnboardCoverageCheck(t, onboardCoverage),
|
|
1871
|
+
createPreexistingRootFilesCheck(t, preexistingRootFiles)
|
|
1810
1872
|
// v2.0 / rc.2: `createLegacyClientPathCheck` removed. The schema now
|
|
1811
1873
|
// rejects retired clientPaths keys (windsurf/rooCode/geminiCLI) at Zod
|
|
1812
1874
|
// parse time, so the soft-deprecation warn-and-fix path no longer has a
|
|
@@ -1901,7 +1963,8 @@ async function runDoctorFix(target) {
|
|
|
1901
1963
|
"knowledge_test_index_missing",
|
|
1902
1964
|
"knowledge_test_index_stale",
|
|
1903
1965
|
"content_ref_missing",
|
|
1904
|
-
"knowledge_dir_unindexed"
|
|
1966
|
+
"knowledge_dir_unindexed",
|
|
1967
|
+
"meta_manually_diverged"
|
|
1905
1968
|
];
|
|
1906
1969
|
if (before.fixable_errors.some((issue) => reconcileCodes.includes(issue.code)) || before.warnings.some((issue) => reconcileCodes.includes(issue.code))) {
|
|
1907
1970
|
await reconcileKnowledge(projectRoot, { trigger: "doctor" });
|
|
@@ -2483,7 +2546,19 @@ async function inspectEventLedger(projectRoot) {
|
|
|
2483
2546
|
const path2 = getEventLedgerPath(projectRoot);
|
|
2484
2547
|
const exists = existsSync4(path2);
|
|
2485
2548
|
if (!exists) {
|
|
2486
|
-
return {
|
|
2549
|
+
return {
|
|
2550
|
+
exists: false,
|
|
2551
|
+
writable: false,
|
|
2552
|
+
parseable: false,
|
|
2553
|
+
hasPartialWrite: false,
|
|
2554
|
+
partialWriteByteOffset: 0,
|
|
2555
|
+
partialWriteByteLength: 0,
|
|
2556
|
+
schemaVersionUnsupportedCount: 0,
|
|
2557
|
+
eventTypeUnknownCount: 0,
|
|
2558
|
+
schemaVersionSamples: [],
|
|
2559
|
+
eventTypeSamples: [],
|
|
2560
|
+
path: path2
|
|
2561
|
+
};
|
|
2487
2562
|
}
|
|
2488
2563
|
try {
|
|
2489
2564
|
await access(path2, constants.W_OK);
|
|
@@ -2491,6 +2566,25 @@ async function inspectEventLedger(projectRoot) {
|
|
|
2491
2566
|
const raw = await readFile5(path2, "utf8");
|
|
2492
2567
|
const invalidLine = raw.split(/\r?\n/u).map((line) => line.trim()).filter(Boolean).find((line) => !isValidJsonLine(line));
|
|
2493
2568
|
const partialWarning = warnings.find((w) => w.kind === "partial_write_at_tail");
|
|
2569
|
+
const schemaVersionSamples = [];
|
|
2570
|
+
const eventTypeSamples = [];
|
|
2571
|
+
let schemaVersionUnsupportedCount = 0;
|
|
2572
|
+
let eventTypeUnknownCount = 0;
|
|
2573
|
+
for (const w of warnings) {
|
|
2574
|
+
if (w.kind === "schema_version_unsupported") {
|
|
2575
|
+
schemaVersionUnsupportedCount += 1;
|
|
2576
|
+
const token = String(w.schema_version);
|
|
2577
|
+
if (!schemaVersionSamples.includes(token) && schemaVersionSamples.length < 3) {
|
|
2578
|
+
schemaVersionSamples.push(token);
|
|
2579
|
+
}
|
|
2580
|
+
} else if (w.kind === "event_type_unknown") {
|
|
2581
|
+
eventTypeUnknownCount += 1;
|
|
2582
|
+
const token = String(w.event_type);
|
|
2583
|
+
if (!eventTypeSamples.includes(token) && eventTypeSamples.length < 3) {
|
|
2584
|
+
eventTypeSamples.push(token);
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2494
2588
|
return {
|
|
2495
2589
|
exists: true,
|
|
2496
2590
|
writable: true,
|
|
@@ -2498,6 +2592,10 @@ async function inspectEventLedger(projectRoot) {
|
|
|
2498
2592
|
hasPartialWrite: partialWarning !== void 0,
|
|
2499
2593
|
partialWriteByteOffset: partialWarning?.byte_offset ?? 0,
|
|
2500
2594
|
partialWriteByteLength: partialWarning?.byte_length ?? 0,
|
|
2595
|
+
schemaVersionUnsupportedCount,
|
|
2596
|
+
eventTypeUnknownCount,
|
|
2597
|
+
schemaVersionSamples,
|
|
2598
|
+
eventTypeSamples,
|
|
2501
2599
|
path: path2,
|
|
2502
2600
|
error: invalidLine === void 0 ? void 0 : "events.jsonl contains an invalid JSON line."
|
|
2503
2601
|
};
|
|
@@ -2509,6 +2607,10 @@ async function inspectEventLedger(projectRoot) {
|
|
|
2509
2607
|
hasPartialWrite: false,
|
|
2510
2608
|
partialWriteByteOffset: 0,
|
|
2511
2609
|
partialWriteByteLength: 0,
|
|
2610
|
+
schemaVersionUnsupportedCount: 0,
|
|
2611
|
+
eventTypeUnknownCount: 0,
|
|
2612
|
+
schemaVersionSamples: [],
|
|
2613
|
+
eventTypeSamples: [],
|
|
2512
2614
|
path: path2,
|
|
2513
2615
|
error: error instanceof Error ? error.message : String(error)
|
|
2514
2616
|
};
|
|
@@ -2568,21 +2670,25 @@ async function inspectBootstrapMarkerMigration(target) {
|
|
|
2568
2670
|
}
|
|
2569
2671
|
return { filesNeedingMigration };
|
|
2570
2672
|
}
|
|
2571
|
-
function createBootstrapMarkerMigrationCheck(inspection) {
|
|
2673
|
+
function createBootstrapMarkerMigrationCheck(t, inspection) {
|
|
2572
2674
|
if (inspection.filesNeedingMigration.length === 0) {
|
|
2573
2675
|
return okCheck(
|
|
2574
|
-
"
|
|
2575
|
-
"
|
|
2676
|
+
t("doctor.check.bootstrap_marker_migration.name"),
|
|
2677
|
+
t("doctor.check.bootstrap_marker_migration.ok")
|
|
2576
2678
|
);
|
|
2577
2679
|
}
|
|
2578
2680
|
const list = inspection.filesNeedingMigration.join(", ");
|
|
2681
|
+
const count = inspection.filesNeedingMigration.length;
|
|
2579
2682
|
return issueCheck(
|
|
2580
|
-
"
|
|
2683
|
+
t("doctor.check.bootstrap_marker_migration.name"),
|
|
2581
2684
|
"error",
|
|
2582
2685
|
"fixable_error",
|
|
2583
2686
|
"bootstrap_marker_migration_required",
|
|
2584
|
-
|
|
2585
|
-
|
|
2687
|
+
t(`doctor.check.bootstrap_marker_migration.message.${count === 1 ? "singular" : "plural"}`, {
|
|
2688
|
+
count: String(count),
|
|
2689
|
+
list
|
|
2690
|
+
}),
|
|
2691
|
+
t("doctor.check.bootstrap_marker_migration.remediation")
|
|
2586
2692
|
);
|
|
2587
2693
|
}
|
|
2588
2694
|
async function inspectL1BootstrapSnapshotDrift(target) {
|
|
@@ -2601,20 +2707,20 @@ async function inspectL1BootstrapSnapshotDrift(target) {
|
|
|
2601
2707
|
}
|
|
2602
2708
|
return { status: "drift", canonical: BOOTSTRAP_CANONICAL, onDisk };
|
|
2603
2709
|
}
|
|
2604
|
-
function createL1BootstrapSnapshotDriftCheck(inspection) {
|
|
2710
|
+
function createL1BootstrapSnapshotDriftCheck(t, inspection) {
|
|
2605
2711
|
if (inspection.status === "drift") {
|
|
2606
2712
|
return issueCheck(
|
|
2607
|
-
"
|
|
2713
|
+
t("doctor.check.bootstrap_snapshot_drift.name"),
|
|
2608
2714
|
"error",
|
|
2609
2715
|
"fixable_error",
|
|
2610
2716
|
"bootstrap_snapshot_drift",
|
|
2611
|
-
".
|
|
2612
|
-
"
|
|
2717
|
+
t("doctor.check.bootstrap_snapshot_drift.message.drift"),
|
|
2718
|
+
t("doctor.check.bootstrap_snapshot_drift.remediation.drift")
|
|
2613
2719
|
);
|
|
2614
2720
|
}
|
|
2615
2721
|
return okCheck(
|
|
2616
|
-
"
|
|
2617
|
-
inspection.status === "ok" ? ".
|
|
2722
|
+
t("doctor.check.bootstrap_snapshot_drift.name"),
|
|
2723
|
+
inspection.status === "ok" ? t("doctor.check.bootstrap_snapshot_drift.ok.ok") : t("doctor.check.bootstrap_snapshot_drift.ok.missing_delegated")
|
|
2618
2724
|
);
|
|
2619
2725
|
}
|
|
2620
2726
|
async function inspectL2ManagedBlockDrift(target) {
|
|
@@ -2706,39 +2812,46 @@ ${projectRules}`;
|
|
|
2706
2812
|
}
|
|
2707
2813
|
return { status: "drift", drifted };
|
|
2708
2814
|
}
|
|
2709
|
-
function createL2ManagedBlockDriftCheck(inspection) {
|
|
2815
|
+
function createL2ManagedBlockDriftCheck(t, inspection) {
|
|
2710
2816
|
if (inspection.status === "drift") {
|
|
2711
2817
|
const list = inspection.drifted.map((d) => d.path).join(", ");
|
|
2818
|
+
const count = inspection.drifted.length;
|
|
2712
2819
|
return issueCheck(
|
|
2713
|
-
"
|
|
2820
|
+
t("doctor.check.managed_block_drift.name"),
|
|
2714
2821
|
"error",
|
|
2715
2822
|
"fixable_error",
|
|
2716
2823
|
"managed_block_drift",
|
|
2717
|
-
|
|
2718
|
-
|
|
2824
|
+
t(`doctor.check.managed_block_drift.message.${count === 1 ? "singular" : "plural"}`, {
|
|
2825
|
+
count: String(count),
|
|
2826
|
+
list
|
|
2827
|
+
}),
|
|
2828
|
+
t("doctor.check.managed_block_drift.remediation")
|
|
2719
2829
|
);
|
|
2720
2830
|
}
|
|
2721
2831
|
return okCheck(
|
|
2722
|
-
"
|
|
2723
|
-
inspection.status === "ok" ? "
|
|
2832
|
+
t("doctor.check.managed_block_drift.name"),
|
|
2833
|
+
inspection.status === "ok" ? t("doctor.check.managed_block_drift.ok.ok") : t("doctor.check.managed_block_drift.ok.no_managed_block")
|
|
2724
2834
|
);
|
|
2725
2835
|
}
|
|
2726
|
-
function createBootstrapAnchorCheck(inspection) {
|
|
2836
|
+
function createBootstrapAnchorCheck(t, inspection) {
|
|
2727
2837
|
if (!inspection.hasAgentsMd && !inspection.hasClaudeMd) {
|
|
2728
2838
|
return issueCheck(
|
|
2729
|
-
"
|
|
2839
|
+
t("doctor.check.bootstrap_anchor.name"),
|
|
2730
2840
|
"error",
|
|
2731
2841
|
"fixable_error",
|
|
2732
2842
|
"bootstrap_anchor_missing",
|
|
2733
|
-
"
|
|
2734
|
-
"
|
|
2843
|
+
t("doctor.check.bootstrap_anchor.message.missing"),
|
|
2844
|
+
t("doctor.check.bootstrap_anchor.remediation.missing")
|
|
2735
2845
|
);
|
|
2736
2846
|
}
|
|
2737
2847
|
const present = [
|
|
2738
2848
|
inspection.hasAgentsMd ? "AGENTS.md" : null,
|
|
2739
2849
|
inspection.hasClaudeMd ? "CLAUDE.md" : null
|
|
2740
2850
|
].filter((entry) => entry !== null).join(", ");
|
|
2741
|
-
return okCheck(
|
|
2851
|
+
return okCheck(
|
|
2852
|
+
t("doctor.check.bootstrap_anchor.name"),
|
|
2853
|
+
t("doctor.check.bootstrap_anchor.ok", { present })
|
|
2854
|
+
);
|
|
2742
2855
|
}
|
|
2743
2856
|
function inspectKnowledgeDirMissing(projectRoot) {
|
|
2744
2857
|
const knowledgeRoot = join6(projectRoot, ".fabric", "knowledge");
|
|
@@ -2799,154 +2912,310 @@ function inspectBaselineFilenameFormat(projectRoot) {
|
|
|
2799
2912
|
offenders.sort((a, b) => a.path.localeCompare(b.path));
|
|
2800
2913
|
return { offenders };
|
|
2801
2914
|
}
|
|
2802
|
-
function createBaselineFilenameFormatCheck(inspection) {
|
|
2915
|
+
function createBaselineFilenameFormatCheck(t, inspection) {
|
|
2803
2916
|
if (inspection.offenders.length === 0) {
|
|
2804
2917
|
return okCheck(
|
|
2805
|
-
"
|
|
2806
|
-
"
|
|
2918
|
+
t("doctor.check.baseline_filename_format.name"),
|
|
2919
|
+
t("doctor.check.baseline_filename_format.ok")
|
|
2807
2920
|
);
|
|
2808
2921
|
}
|
|
2809
2922
|
const first = inspection.offenders[0];
|
|
2810
2923
|
const detail = `${first.stable_id} at ${first.path}`;
|
|
2924
|
+
const count = inspection.offenders.length;
|
|
2811
2925
|
return issueCheck(
|
|
2812
|
-
"
|
|
2926
|
+
t("doctor.check.baseline_filename_format.name"),
|
|
2813
2927
|
"error",
|
|
2814
2928
|
"manual_error",
|
|
2815
2929
|
"lint-baseline-filename-format",
|
|
2816
|
-
|
|
2817
|
-
|
|
2930
|
+
t(`doctor.check.baseline_filename_format.message.${count === 1 ? "singular" : "plural"}`, {
|
|
2931
|
+
count: String(count),
|
|
2932
|
+
detail
|
|
2933
|
+
}),
|
|
2934
|
+
t("doctor.check.baseline_filename_format.remediation")
|
|
2818
2935
|
);
|
|
2819
2936
|
}
|
|
2820
|
-
function createKnowledgeDirMissingCheck(inspection) {
|
|
2937
|
+
function createKnowledgeDirMissingCheck(t, inspection) {
|
|
2821
2938
|
if (inspection.missingSubdirs.length > 0) {
|
|
2822
2939
|
const list = inspection.missingSubdirs.join(", ");
|
|
2940
|
+
const count = inspection.missingSubdirs.length;
|
|
2823
2941
|
return issueCheck(
|
|
2824
|
-
"
|
|
2942
|
+
t("doctor.check.knowledge_dir_missing.name"),
|
|
2825
2943
|
"error",
|
|
2826
2944
|
"fixable_error",
|
|
2827
2945
|
"knowledge_dir_missing",
|
|
2828
|
-
|
|
2829
|
-
|
|
2946
|
+
t(`doctor.check.knowledge_dir_missing.message.${count === 1 ? "singular" : "plural"}`, {
|
|
2947
|
+
count: String(count),
|
|
2948
|
+
list
|
|
2949
|
+
}),
|
|
2950
|
+
t("doctor.check.knowledge_dir_missing.remediation")
|
|
2830
2951
|
);
|
|
2831
2952
|
}
|
|
2832
2953
|
return okCheck(
|
|
2833
|
-
"
|
|
2834
|
-
|
|
2954
|
+
t("doctor.check.knowledge_dir_missing.name"),
|
|
2955
|
+
t("doctor.check.knowledge_dir_missing.ok", { count: String(KNOWLEDGE_SUBDIRS3.length) })
|
|
2835
2956
|
);
|
|
2836
2957
|
}
|
|
2837
|
-
function createForensicCheck(forensic, frameworkKind, entryPointCount) {
|
|
2958
|
+
function createForensicCheck(t, forensic, frameworkKind, entryPointCount) {
|
|
2838
2959
|
if (!forensic.present) {
|
|
2839
2960
|
return issueCheck(
|
|
2840
|
-
"
|
|
2961
|
+
t("doctor.check.forensic.name"),
|
|
2841
2962
|
"error",
|
|
2842
2963
|
"manual_error",
|
|
2843
2964
|
"forensic_missing",
|
|
2844
|
-
|
|
2845
|
-
|
|
2965
|
+
t(`doctor.check.forensic.message.missing.${entryPointCount === 1 ? "singular" : "plural"}`, {
|
|
2966
|
+
error: forensic.error ?? t("doctor.check.forensic.message.missing-default"),
|
|
2967
|
+
frameworkKind,
|
|
2968
|
+
count: String(entryPointCount)
|
|
2969
|
+
}),
|
|
2970
|
+
t("doctor.check.forensic.remediation")
|
|
2846
2971
|
);
|
|
2847
2972
|
}
|
|
2848
2973
|
if (!forensic.valid) {
|
|
2849
|
-
return issueCheck(
|
|
2974
|
+
return issueCheck(
|
|
2975
|
+
t("doctor.check.forensic.name"),
|
|
2976
|
+
"error",
|
|
2977
|
+
"manual_error",
|
|
2978
|
+
"forensic_invalid",
|
|
2979
|
+
forensic.error ?? t("doctor.check.forensic.message.invalid-default"),
|
|
2980
|
+
t("doctor.check.forensic.remediation")
|
|
2981
|
+
);
|
|
2850
2982
|
}
|
|
2851
|
-
return okCheck(
|
|
2983
|
+
return okCheck(
|
|
2984
|
+
t("doctor.check.forensic.name"),
|
|
2985
|
+
t("doctor.check.forensic.ok", { frameworkKind: forensic.report?.framework.kind ?? "unknown" })
|
|
2986
|
+
);
|
|
2852
2987
|
}
|
|
2853
|
-
function createMetaCheck(meta) {
|
|
2988
|
+
function createMetaCheck(t, meta) {
|
|
2854
2989
|
if (!meta.present) {
|
|
2855
|
-
return issueCheck(
|
|
2990
|
+
return issueCheck(
|
|
2991
|
+
t("doctor.check.agents_meta.name"),
|
|
2992
|
+
"error",
|
|
2993
|
+
"fixable_error",
|
|
2994
|
+
"agents_meta_missing",
|
|
2995
|
+
t("doctor.check.agents_meta.message.missing"),
|
|
2996
|
+
t("doctor.check.agents_meta.remediation.missing")
|
|
2997
|
+
);
|
|
2856
2998
|
}
|
|
2857
2999
|
if (!meta.valid) {
|
|
2858
|
-
return issueCheck(
|
|
3000
|
+
return issueCheck(
|
|
3001
|
+
t("doctor.check.agents_meta.name"),
|
|
3002
|
+
"error",
|
|
3003
|
+
"manual_error",
|
|
3004
|
+
"agents_meta_invalid",
|
|
3005
|
+
meta.readError ?? t("doctor.check.agents_meta.message.invalid-default"),
|
|
3006
|
+
t("doctor.check.agents_meta.remediation.invalid")
|
|
3007
|
+
);
|
|
2859
3008
|
}
|
|
2860
3009
|
if (meta.stale) {
|
|
2861
3010
|
return issueCheck(
|
|
2862
|
-
"
|
|
3011
|
+
t("doctor.check.agents_meta.name"),
|
|
2863
3012
|
"warn",
|
|
2864
3013
|
"warning",
|
|
2865
3014
|
"agents_meta_stale",
|
|
2866
|
-
|
|
2867
|
-
|
|
3015
|
+
t("doctor.check.agents_meta.message.stale", {
|
|
3016
|
+
revision: meta.revision,
|
|
3017
|
+
computedRevision: meta.computedRevision ?? "<unknown>"
|
|
3018
|
+
}),
|
|
3019
|
+
t("doctor.check.agents_meta.remediation.stale")
|
|
2868
3020
|
);
|
|
2869
3021
|
}
|
|
2870
|
-
return okCheck(
|
|
3022
|
+
return okCheck(
|
|
3023
|
+
t("doctor.check.agents_meta.name"),
|
|
3024
|
+
t("doctor.check.agents_meta.ok", { revision: meta.revision })
|
|
3025
|
+
);
|
|
2871
3026
|
}
|
|
2872
|
-
function createRuleContentRefCheck(meta) {
|
|
3027
|
+
function createRuleContentRefCheck(t, meta) {
|
|
2873
3028
|
if (!meta.valid) {
|
|
2874
|
-
return issueCheck(
|
|
3029
|
+
return issueCheck(
|
|
3030
|
+
t("doctor.check.rule_content_refs.name"),
|
|
3031
|
+
"error",
|
|
3032
|
+
"manual_error",
|
|
3033
|
+
"content_refs_unavailable",
|
|
3034
|
+
t("doctor.check.rule_content_refs.message.unavailable"),
|
|
3035
|
+
t("doctor.check.rule_content_refs.remediation.unavailable")
|
|
3036
|
+
);
|
|
2875
3037
|
}
|
|
2876
3038
|
if (meta.invalidContentRefs.length > 0) {
|
|
3039
|
+
const count = meta.invalidContentRefs.length;
|
|
2877
3040
|
return issueCheck(
|
|
2878
|
-
"
|
|
3041
|
+
t("doctor.check.rule_content_refs.name"),
|
|
2879
3042
|
"error",
|
|
2880
3043
|
"manual_error",
|
|
2881
3044
|
"content_ref_outside_rules",
|
|
2882
|
-
|
|
2883
|
-
|
|
3045
|
+
t(`doctor.check.rule_content_refs.message.outside.${count === 1 ? "singular" : "plural"}`, {
|
|
3046
|
+
count: String(count)
|
|
3047
|
+
}),
|
|
3048
|
+
t("doctor.check.rule_content_refs.remediation.outside")
|
|
2884
3049
|
);
|
|
2885
3050
|
}
|
|
2886
3051
|
if (meta.missingContentRefs.length > 0) {
|
|
3052
|
+
const count = meta.missingContentRefs.length;
|
|
2887
3053
|
return issueCheck(
|
|
2888
|
-
"
|
|
3054
|
+
t("doctor.check.rule_content_refs.name"),
|
|
2889
3055
|
"error",
|
|
2890
3056
|
"fixable_error",
|
|
2891
3057
|
"content_ref_missing",
|
|
2892
|
-
|
|
2893
|
-
|
|
3058
|
+
t(`doctor.check.rule_content_refs.message.missing.${count === 1 ? "singular" : "plural"}`, {
|
|
3059
|
+
count: String(count)
|
|
3060
|
+
}),
|
|
3061
|
+
t("doctor.check.rule_content_refs.remediation.missing")
|
|
2894
3062
|
);
|
|
2895
3063
|
}
|
|
2896
|
-
return okCheck("
|
|
3064
|
+
return okCheck(t("doctor.check.rule_content_refs.name"), t("doctor.check.rule_content_refs.ok"));
|
|
2897
3065
|
}
|
|
2898
|
-
function createKnowledgeTestIndexCheck(index) {
|
|
3066
|
+
function createKnowledgeTestIndexCheck(t, index) {
|
|
2899
3067
|
if (!index.present) {
|
|
2900
|
-
return issueCheck(
|
|
3068
|
+
return issueCheck(
|
|
3069
|
+
t("doctor.check.knowledge_test_index.name"),
|
|
3070
|
+
"error",
|
|
3071
|
+
"fixable_error",
|
|
3072
|
+
"knowledge_test_index_missing",
|
|
3073
|
+
index.error,
|
|
3074
|
+
t("doctor.check.knowledge_test_index.remediation.missing")
|
|
3075
|
+
);
|
|
2901
3076
|
}
|
|
2902
3077
|
if (!index.valid) {
|
|
2903
|
-
return issueCheck(
|
|
3078
|
+
return issueCheck(
|
|
3079
|
+
t("doctor.check.knowledge_test_index.name"),
|
|
3080
|
+
"error",
|
|
3081
|
+
"manual_error",
|
|
3082
|
+
"knowledge_test_index_invalid",
|
|
3083
|
+
index.error,
|
|
3084
|
+
t("doctor.check.knowledge_test_index.remediation.invalid")
|
|
3085
|
+
);
|
|
2904
3086
|
}
|
|
2905
3087
|
if (index.stale) {
|
|
2906
|
-
return issueCheck(
|
|
3088
|
+
return issueCheck(
|
|
3089
|
+
t("doctor.check.knowledge_test_index.name"),
|
|
3090
|
+
"error",
|
|
3091
|
+
"fixable_error",
|
|
3092
|
+
"knowledge_test_index_stale",
|
|
3093
|
+
t("doctor.check.knowledge_test_index.message.stale"),
|
|
3094
|
+
t("doctor.check.knowledge_test_index.remediation.stale")
|
|
3095
|
+
);
|
|
2907
3096
|
}
|
|
2908
|
-
return okCheck(
|
|
3097
|
+
return okCheck(
|
|
3098
|
+
t("doctor.check.knowledge_test_index.name"),
|
|
3099
|
+
t(
|
|
3100
|
+
`doctor.check.knowledge_test_index.ok.${index.linkCount === 1 ? "link_singular" : "link_plural"}.${index.orphanCount === 1 ? "orphan_singular" : "orphan_plural"}`,
|
|
3101
|
+
{ linkCount: String(index.linkCount), orphanCount: String(index.orphanCount) }
|
|
3102
|
+
)
|
|
3103
|
+
);
|
|
2909
3104
|
}
|
|
2910
|
-
function createEventLedgerCheck(ledger) {
|
|
3105
|
+
function createEventLedgerCheck(t, ledger) {
|
|
2911
3106
|
if (!ledger.exists) {
|
|
2912
|
-
return issueCheck(
|
|
3107
|
+
return issueCheck(
|
|
3108
|
+
t("doctor.check.event_ledger.name"),
|
|
3109
|
+
"error",
|
|
3110
|
+
"fixable_error",
|
|
3111
|
+
"event_ledger_missing",
|
|
3112
|
+
t("doctor.check.event_ledger.message.missing"),
|
|
3113
|
+
t("doctor.check.event_ledger.remediation.missing")
|
|
3114
|
+
);
|
|
2913
3115
|
}
|
|
2914
3116
|
if (!ledger.writable) {
|
|
2915
|
-
return issueCheck(
|
|
3117
|
+
return issueCheck(
|
|
3118
|
+
t("doctor.check.event_ledger.name"),
|
|
3119
|
+
"error",
|
|
3120
|
+
"manual_error",
|
|
3121
|
+
"event_ledger_not_writable",
|
|
3122
|
+
ledger.error ?? t("doctor.check.event_ledger.message.not_writable-default"),
|
|
3123
|
+
t("doctor.check.event_ledger.remediation.not_writable")
|
|
3124
|
+
);
|
|
2916
3125
|
}
|
|
2917
3126
|
if (!ledger.parseable) {
|
|
2918
|
-
return issueCheck(
|
|
3127
|
+
return issueCheck(
|
|
3128
|
+
t("doctor.check.event_ledger.name"),
|
|
3129
|
+
"error",
|
|
3130
|
+
"manual_error",
|
|
3131
|
+
"event_ledger_invalid",
|
|
3132
|
+
ledger.error ?? t("doctor.check.event_ledger.message.invalid-default"),
|
|
3133
|
+
t("doctor.check.event_ledger.remediation.invalid")
|
|
3134
|
+
);
|
|
2919
3135
|
}
|
|
2920
|
-
return okCheck("
|
|
3136
|
+
return okCheck(t("doctor.check.event_ledger.name"), t("doctor.check.event_ledger.ok"));
|
|
2921
3137
|
}
|
|
2922
|
-
function createMcpConfigInWrongFileCheck(inspection) {
|
|
3138
|
+
function createMcpConfigInWrongFileCheck(t, inspection) {
|
|
2923
3139
|
if (inspection.hasWrongEntry) {
|
|
2924
3140
|
return issueCheck(
|
|
2925
|
-
"
|
|
3141
|
+
t("doctor.check.mcp_config_in_wrong_file.name"),
|
|
2926
3142
|
"error",
|
|
2927
3143
|
"fixable_error",
|
|
2928
3144
|
"mcp_config_in_wrong_file",
|
|
2929
|
-
|
|
2930
|
-
"
|
|
3145
|
+
t("doctor.check.mcp_config_in_wrong_file.message"),
|
|
3146
|
+
t("doctor.check.mcp_config_in_wrong_file.remediation")
|
|
2931
3147
|
);
|
|
2932
3148
|
}
|
|
2933
|
-
return okCheck(
|
|
3149
|
+
return okCheck(
|
|
3150
|
+
t("doctor.check.mcp_config_in_wrong_file.name"),
|
|
3151
|
+
t("doctor.check.mcp_config_in_wrong_file.ok")
|
|
3152
|
+
);
|
|
2934
3153
|
}
|
|
2935
|
-
function
|
|
3154
|
+
function createEventLedgerSchemaCompatCheck(t, ledger) {
|
|
2936
3155
|
if (!ledger.exists || !ledger.writable) {
|
|
2937
|
-
return okCheck(
|
|
3156
|
+
return okCheck(
|
|
3157
|
+
t("doctor.check.event_ledger_schema_compat.name"),
|
|
3158
|
+
t("doctor.check.event_ledger_schema_compat.ok.skipped")
|
|
3159
|
+
);
|
|
3160
|
+
}
|
|
3161
|
+
const hasUnsupportedVersion = ledger.schemaVersionUnsupportedCount > 0;
|
|
3162
|
+
const hasUnknownEventType = ledger.eventTypeUnknownCount > 0;
|
|
3163
|
+
if (!hasUnsupportedVersion && !hasUnknownEventType) {
|
|
3164
|
+
return okCheck(
|
|
3165
|
+
t("doctor.check.event_ledger_schema_compat.name"),
|
|
3166
|
+
t("doctor.check.event_ledger_schema_compat.ok.clean")
|
|
3167
|
+
);
|
|
3168
|
+
}
|
|
3169
|
+
const parts = [];
|
|
3170
|
+
if (hasUnsupportedVersion) {
|
|
3171
|
+
parts.push(
|
|
3172
|
+
t("doctor.check.event_ledger_schema_compat.message.schema_version", {
|
|
3173
|
+
count: String(ledger.schemaVersionUnsupportedCount),
|
|
3174
|
+
samples: ledger.schemaVersionSamples.join(", ")
|
|
3175
|
+
})
|
|
3176
|
+
);
|
|
3177
|
+
}
|
|
3178
|
+
if (hasUnknownEventType) {
|
|
3179
|
+
parts.push(
|
|
3180
|
+
t("doctor.check.event_ledger_schema_compat.message.event_type", {
|
|
3181
|
+
count: String(ledger.eventTypeUnknownCount),
|
|
3182
|
+
samples: ledger.eventTypeSamples.join(", ")
|
|
3183
|
+
})
|
|
3184
|
+
);
|
|
3185
|
+
}
|
|
3186
|
+
return issueCheck(
|
|
3187
|
+
t("doctor.check.event_ledger_schema_compat.name"),
|
|
3188
|
+
"warn",
|
|
3189
|
+
"warning",
|
|
3190
|
+
"event_ledger_schema_compat",
|
|
3191
|
+
parts.join(" "),
|
|
3192
|
+
t("doctor.check.event_ledger_schema_compat.remediation")
|
|
3193
|
+
);
|
|
3194
|
+
}
|
|
3195
|
+
function createEventLedgerPartialWriteCheck(t, ledger) {
|
|
3196
|
+
if (!ledger.exists || !ledger.writable) {
|
|
3197
|
+
return okCheck(
|
|
3198
|
+
t("doctor.check.event_ledger_partial_write.name"),
|
|
3199
|
+
t("doctor.check.event_ledger_partial_write.ok.skipped")
|
|
3200
|
+
);
|
|
2938
3201
|
}
|
|
2939
3202
|
if (ledger.hasPartialWrite) {
|
|
2940
3203
|
return issueCheck(
|
|
2941
|
-
"
|
|
3204
|
+
t("doctor.check.event_ledger_partial_write.name"),
|
|
2942
3205
|
"error",
|
|
2943
3206
|
"fixable_error",
|
|
2944
3207
|
"event_ledger_partial_write",
|
|
2945
|
-
|
|
2946
|
-
|
|
3208
|
+
t("doctor.check.event_ledger_partial_write.message", {
|
|
3209
|
+
byteOffset: String(ledger.partialWriteByteOffset),
|
|
3210
|
+
byteLength: String(ledger.partialWriteByteLength)
|
|
3211
|
+
}),
|
|
3212
|
+
t("doctor.check.event_ledger_partial_write.remediation")
|
|
2947
3213
|
);
|
|
2948
3214
|
}
|
|
2949
|
-
return okCheck(
|
|
3215
|
+
return okCheck(
|
|
3216
|
+
t("doctor.check.event_ledger_partial_write.name"),
|
|
3217
|
+
t("doctor.check.event_ledger_partial_write.ok.clean")
|
|
3218
|
+
);
|
|
2950
3219
|
}
|
|
2951
3220
|
function okCheck(name, message) {
|
|
2952
3221
|
return { name, status: "ok", message };
|
|
@@ -2966,7 +3235,8 @@ function collectIssues(checks, kind) {
|
|
|
2966
3235
|
return checks.filter((check) => check.kind === kind).map((check) => ({
|
|
2967
3236
|
code: check.code ?? check.name,
|
|
2968
3237
|
name: check.name,
|
|
2969
|
-
message: check.message
|
|
3238
|
+
message: check.message,
|
|
3239
|
+
actionHint: check.actionHint
|
|
2970
3240
|
}));
|
|
2971
3241
|
}
|
|
2972
3242
|
function findIssue(issues, code) {
|
|
@@ -3053,18 +3323,24 @@ function collectMdFilesUnder(out, projectRoot, rootDir, relPrefix) {
|
|
|
3053
3323
|
}
|
|
3054
3324
|
}
|
|
3055
3325
|
}
|
|
3056
|
-
function createKnowledgeDirUnindexedCheck(inspection) {
|
|
3326
|
+
function createKnowledgeDirUnindexedCheck(t, inspection) {
|
|
3057
3327
|
if (inspection.unindexedFiles.length > 0) {
|
|
3328
|
+
const count = inspection.unindexedFiles.length;
|
|
3058
3329
|
return issueCheck(
|
|
3059
|
-
"
|
|
3330
|
+
t("doctor.check.knowledge_dir_unindexed.name"),
|
|
3060
3331
|
"error",
|
|
3061
3332
|
"fixable_error",
|
|
3062
3333
|
"knowledge_dir_unindexed",
|
|
3063
|
-
|
|
3064
|
-
|
|
3334
|
+
t(`doctor.check.knowledge_dir_unindexed.message.${count === 1 ? "singular" : "plural"}`, {
|
|
3335
|
+
count: String(count)
|
|
3336
|
+
}),
|
|
3337
|
+
t("doctor.check.knowledge_dir_unindexed.remediation")
|
|
3065
3338
|
);
|
|
3066
3339
|
}
|
|
3067
|
-
return okCheck(
|
|
3340
|
+
return okCheck(
|
|
3341
|
+
t("doctor.check.knowledge_dir_unindexed.name"),
|
|
3342
|
+
t("doctor.check.knowledge_dir_unindexed.ok")
|
|
3343
|
+
);
|
|
3068
3344
|
}
|
|
3069
3345
|
async function inspectStableIdCollisions(projectRoot) {
|
|
3070
3346
|
const found = [];
|
|
@@ -3147,7 +3423,7 @@ function inspectCounterDesync(meta) {
|
|
|
3147
3423
|
["guideline", "GLD"],
|
|
3148
3424
|
["pitfall", "PIT"],
|
|
3149
3425
|
["process", "PRO"]
|
|
3150
|
-
].find(([
|
|
3426
|
+
].find(([t]) => t === parsed.type)?.[1];
|
|
3151
3427
|
if (typeCode === void 0) {
|
|
3152
3428
|
continue;
|
|
3153
3429
|
}
|
|
@@ -3175,61 +3451,84 @@ function inspectCounterDesync(meta) {
|
|
|
3175
3451
|
correctedCounters: desyncs.length === 0 ? null : corrected
|
|
3176
3452
|
};
|
|
3177
3453
|
}
|
|
3178
|
-
function createCounterDesyncCheck(inspection) {
|
|
3454
|
+
function createCounterDesyncCheck(t, inspection) {
|
|
3179
3455
|
if (inspection.desyncs.length > 0) {
|
|
3180
3456
|
const first = inspection.desyncs[0];
|
|
3181
|
-
const
|
|
3457
|
+
const observedId = `K${first.layer === "KP" ? "P" : "T"}-${first.type}-${String(first.observed).padStart(4, "0")}`;
|
|
3458
|
+
const count = inspection.desyncs.length;
|
|
3182
3459
|
return issueCheck(
|
|
3183
|
-
"
|
|
3460
|
+
t("doctor.check.counter_desync.name"),
|
|
3184
3461
|
"error",
|
|
3185
3462
|
"fixable_error",
|
|
3186
3463
|
"counter_desync",
|
|
3187
|
-
|
|
3188
|
-
|
|
3464
|
+
t(`doctor.check.counter_desync.message.${count === 1 ? "singular" : "plural"}`, {
|
|
3465
|
+
count: String(count),
|
|
3466
|
+
counterPath: `counters.${first.layer}.${first.type}`,
|
|
3467
|
+
current: String(first.current),
|
|
3468
|
+
observedId
|
|
3469
|
+
}),
|
|
3470
|
+
t("doctor.check.counter_desync.remediation")
|
|
3189
3471
|
);
|
|
3190
3472
|
}
|
|
3191
|
-
return okCheck("
|
|
3473
|
+
return okCheck(t("doctor.check.counter_desync.name"), t("doctor.check.counter_desync.ok"));
|
|
3192
3474
|
}
|
|
3193
|
-
function createStableIdCollisionCheck(inspection) {
|
|
3475
|
+
function createStableIdCollisionCheck(t, inspection) {
|
|
3194
3476
|
if (inspection.collisions.length > 0) {
|
|
3195
3477
|
const first = inspection.collisions[0];
|
|
3196
|
-
const
|
|
3478
|
+
const count = inspection.collisions.length;
|
|
3197
3479
|
return issueCheck(
|
|
3198
|
-
"
|
|
3480
|
+
t("doctor.check.stable_id_collision.name"),
|
|
3199
3481
|
"warn",
|
|
3200
3482
|
"warning",
|
|
3201
3483
|
"stable_id_collision",
|
|
3202
|
-
|
|
3203
|
-
|
|
3484
|
+
t(`doctor.check.stable_id_collision.message.${count === 1 ? "singular" : "plural"}`, {
|
|
3485
|
+
count: String(count),
|
|
3486
|
+
stableId: first.stable_id,
|
|
3487
|
+
fileCount: String(first.files.length),
|
|
3488
|
+
files: first.files.join(", ")
|
|
3489
|
+
}),
|
|
3490
|
+
t("doctor.check.stable_id_collision.remediation")
|
|
3204
3491
|
);
|
|
3205
3492
|
}
|
|
3206
|
-
return okCheck("
|
|
3493
|
+
return okCheck(t("doctor.check.stable_id_collision.name"), t("doctor.check.stable_id_collision.ok"));
|
|
3207
3494
|
}
|
|
3208
|
-
function createMetaManuallyDivergedCheck(inspection) {
|
|
3495
|
+
function createMetaManuallyDivergedCheck(t, inspection) {
|
|
3209
3496
|
if (!inspection.readable) {
|
|
3210
|
-
return okCheck(
|
|
3497
|
+
return okCheck(
|
|
3498
|
+
t("doctor.check.meta_manually_diverged.name"),
|
|
3499
|
+
t("doctor.check.meta_manually_diverged.ok.unreadable")
|
|
3500
|
+
);
|
|
3211
3501
|
}
|
|
3212
3502
|
if (inspection.extraMetaEntries.length > 0) {
|
|
3503
|
+
const count = inspection.extraMetaEntries.length;
|
|
3213
3504
|
return issueCheck(
|
|
3214
|
-
"
|
|
3505
|
+
t("doctor.check.meta_manually_diverged.name"),
|
|
3215
3506
|
"warn",
|
|
3216
3507
|
"warning",
|
|
3217
3508
|
"meta_manually_diverged",
|
|
3218
|
-
`
|
|
3219
|
-
|
|
3509
|
+
t(`doctor.check.meta_manually_diverged.message.extra.${count === 1 ? "singular" : "plural"}`, {
|
|
3510
|
+
count: String(count)
|
|
3511
|
+
}),
|
|
3512
|
+
t("doctor.check.meta_manually_diverged.remediation.extra")
|
|
3220
3513
|
);
|
|
3221
3514
|
}
|
|
3222
3515
|
if (inspection.hashMismatchEntries.length > 0) {
|
|
3516
|
+
const count = inspection.hashMismatchEntries.length;
|
|
3223
3517
|
return issueCheck(
|
|
3224
|
-
"
|
|
3518
|
+
t("doctor.check.meta_manually_diverged.name"),
|
|
3225
3519
|
"warn",
|
|
3226
3520
|
"warning",
|
|
3227
3521
|
"meta_manually_diverged",
|
|
3228
|
-
`
|
|
3229
|
-
|
|
3522
|
+
t(`doctor.check.meta_manually_diverged.message.hash.${count === 1 ? "singular" : "plural"}`, {
|
|
3523
|
+
count: String(count)
|
|
3524
|
+
}),
|
|
3525
|
+
t("doctor.check.meta_manually_diverged.remediation.hash")
|
|
3230
3526
|
);
|
|
3231
3527
|
}
|
|
3232
|
-
return okCheck(
|
|
3528
|
+
return okCheck(
|
|
3529
|
+
t("doctor.check.meta_manually_diverged.name"),
|
|
3530
|
+
t("doctor.check.meta_manually_diverged.ok.consistent")
|
|
3531
|
+
);
|
|
3233
3532
|
}
|
|
3234
3533
|
function inspectPreexistingRootFiles(projectRoot) {
|
|
3235
3534
|
const candidates = ["CLAUDE.md", "AGENTS.md"];
|
|
@@ -3295,36 +3594,44 @@ async function inspectFilesystemEditFallback(projectRoot) {
|
|
|
3295
3594
|
}
|
|
3296
3595
|
return { synthesized: orphanIds.length, synthesizedStableIds: orphanIds };
|
|
3297
3596
|
}
|
|
3298
|
-
function createFilesystemEditFallbackCheck(inspection) {
|
|
3597
|
+
function createFilesystemEditFallbackCheck(t, inspection) {
|
|
3299
3598
|
if (inspection.synthesized === 0) {
|
|
3300
3599
|
return okCheck(
|
|
3301
|
-
"
|
|
3302
|
-
"
|
|
3600
|
+
t("doctor.check.filesystem_edit_fallback.name"),
|
|
3601
|
+
t("doctor.check.filesystem_edit_fallback.ok")
|
|
3303
3602
|
);
|
|
3304
3603
|
}
|
|
3305
3604
|
const sample = inspection.synthesizedStableIds.slice(0, 3).join(", ");
|
|
3306
3605
|
return {
|
|
3307
|
-
name: "
|
|
3606
|
+
name: t("doctor.check.filesystem_edit_fallback.name"),
|
|
3308
3607
|
status: "ok",
|
|
3309
3608
|
kind: "info",
|
|
3310
3609
|
code: "knowledge_promoted_synthesized",
|
|
3311
3610
|
fixable: false,
|
|
3312
|
-
message:
|
|
3313
|
-
|
|
3611
|
+
message: t(
|
|
3612
|
+
`doctor.check.filesystem_edit_fallback.message.synthesized.${inspection.synthesized === 1 ? "singular" : "plural"}`,
|
|
3613
|
+
{
|
|
3614
|
+
count: String(inspection.synthesized),
|
|
3615
|
+
sample,
|
|
3616
|
+
suffix: inspection.synthesizedStableIds.length > 3 ? ", ..." : "",
|
|
3617
|
+
reason: SYNTHESIZED_PROMOTED_REASON
|
|
3618
|
+
}
|
|
3619
|
+
),
|
|
3620
|
+
actionHint: t("doctor.check.filesystem_edit_fallback.remediation.synthesized")
|
|
3314
3621
|
};
|
|
3315
3622
|
}
|
|
3316
|
-
function createPreexistingRootFilesCheck(inspection) {
|
|
3623
|
+
function createPreexistingRootFilesCheck(t, inspection) {
|
|
3317
3624
|
if (inspection.detected.length === 0) {
|
|
3318
|
-
return okCheck("
|
|
3625
|
+
return okCheck(t("doctor.check.preexisting_root_files.name"), t("doctor.check.preexisting_root_files.ok"));
|
|
3319
3626
|
}
|
|
3320
3627
|
return {
|
|
3321
|
-
name: "
|
|
3628
|
+
name: t("doctor.check.preexisting_root_files.name"),
|
|
3322
3629
|
status: "ok",
|
|
3323
3630
|
kind: "info",
|
|
3324
3631
|
code: "preexisting_root_claude_md",
|
|
3325
3632
|
fixable: false,
|
|
3326
|
-
message:
|
|
3327
|
-
actionHint: "
|
|
3633
|
+
message: t("doctor.check.preexisting_root_files.message", { files: inspection.detected.join(", ") }),
|
|
3634
|
+
actionHint: t("doctor.check.preexisting_root_files.remediation")
|
|
3328
3635
|
};
|
|
3329
3636
|
}
|
|
3330
3637
|
async function buildLastConsumedIndex(projectRoot) {
|
|
@@ -3822,114 +4129,156 @@ function readUnderseedThresholdFromConfig(projectRoot) {
|
|
|
3822
4129
|
}
|
|
3823
4130
|
return DEFAULT_UNDERSEED_NODE_THRESHOLD;
|
|
3824
4131
|
}
|
|
3825
|
-
function createOrphanDemoteCheck(inspection) {
|
|
4132
|
+
function createOrphanDemoteCheck(t, inspection) {
|
|
3826
4133
|
if (inspection.candidates.length === 0) {
|
|
3827
4134
|
return okCheck(
|
|
3828
|
-
"
|
|
3829
|
-
"
|
|
4135
|
+
t("doctor.check.orphan_demote.name"),
|
|
4136
|
+
t("doctor.check.orphan_demote.ok")
|
|
3830
4137
|
);
|
|
3831
4138
|
}
|
|
3832
4139
|
const first = inspection.candidates[0];
|
|
3833
4140
|
const detail = `${first.stable_id} (${first.maturity}, ${first.age_days}d inactive at ${first.path})`;
|
|
4141
|
+
const count = inspection.candidates.length;
|
|
3834
4142
|
return issueCheck(
|
|
3835
|
-
"
|
|
4143
|
+
t("doctor.check.orphan_demote.name"),
|
|
3836
4144
|
"warn",
|
|
3837
4145
|
"warning",
|
|
3838
4146
|
"knowledge_orphan_demote_required",
|
|
3839
|
-
|
|
3840
|
-
|
|
4147
|
+
t(`doctor.check.orphan_demote.message.${count === 1 ? "singular" : "plural"}`, {
|
|
4148
|
+
count: String(count),
|
|
4149
|
+
stableDays: String(ORPHAN_DEMOTE_THRESHOLD_DAYS.stable),
|
|
4150
|
+
endorsedDays: String(ORPHAN_DEMOTE_THRESHOLD_DAYS.endorsed),
|
|
4151
|
+
draftDays: String(ORPHAN_DEMOTE_THRESHOLD_DAYS.draft),
|
|
4152
|
+
detail
|
|
4153
|
+
}),
|
|
4154
|
+
t("doctor.check.orphan_demote.remediation")
|
|
3841
4155
|
);
|
|
3842
4156
|
}
|
|
3843
|
-
function createStaleArchiveCheck(inspection) {
|
|
4157
|
+
function createStaleArchiveCheck(t, inspection) {
|
|
3844
4158
|
if (inspection.candidates.length === 0) {
|
|
3845
4159
|
return okCheck(
|
|
3846
|
-
"
|
|
3847
|
-
"
|
|
4160
|
+
t("doctor.check.stale_archive.name"),
|
|
4161
|
+
t("doctor.check.stale_archive.ok")
|
|
3848
4162
|
);
|
|
3849
4163
|
}
|
|
3850
4164
|
const first = inspection.candidates[0];
|
|
3851
4165
|
const detail = `${first.stable_id} (${first.age_days}d inactive at ${first.path}) \u2192 ${first.archive_path}`;
|
|
4166
|
+
const count = inspection.candidates.length;
|
|
3852
4167
|
return issueCheck(
|
|
3853
|
-
"
|
|
4168
|
+
t("doctor.check.stale_archive.name"),
|
|
3854
4169
|
"warn",
|
|
3855
4170
|
"warning",
|
|
3856
4171
|
"knowledge_stale_archive_required",
|
|
3857
|
-
|
|
3858
|
-
|
|
4172
|
+
t(`doctor.check.stale_archive.message.${count === 1 ? "singular" : "plural"}`, {
|
|
4173
|
+
count: String(count),
|
|
4174
|
+
additionalDays: String(STALE_ARCHIVE_ADDITIONAL_DAYS),
|
|
4175
|
+
detail
|
|
4176
|
+
}),
|
|
4177
|
+
t("doctor.check.stale_archive.remediation")
|
|
3859
4178
|
);
|
|
3860
4179
|
}
|
|
3861
|
-
function createPendingOverdueCheck(inspection) {
|
|
4180
|
+
function createPendingOverdueCheck(t, inspection) {
|
|
3862
4181
|
if (inspection.candidates.length === 0) {
|
|
3863
4182
|
return okCheck(
|
|
3864
|
-
"
|
|
3865
|
-
"
|
|
4183
|
+
t("doctor.check.pending_overdue.name"),
|
|
4184
|
+
t("doctor.check.pending_overdue.ok")
|
|
3866
4185
|
);
|
|
3867
4186
|
}
|
|
3868
4187
|
const first = inspection.candidates[0];
|
|
3869
4188
|
const detail = `${first.path} (${first.age_days}d old)`;
|
|
4189
|
+
const count = inspection.candidates.length;
|
|
3870
4190
|
return issueCheck(
|
|
3871
|
-
"
|
|
4191
|
+
t("doctor.check.pending_overdue.name"),
|
|
3872
4192
|
"warn",
|
|
3873
4193
|
"warning",
|
|
3874
4194
|
"knowledge_pending_overdue",
|
|
3875
|
-
|
|
3876
|
-
|
|
4195
|
+
t(`doctor.check.pending_overdue.message.${count === 1 ? "singular" : "plural"}`, {
|
|
4196
|
+
count: String(count),
|
|
4197
|
+
thresholdDays: String(PENDING_OVERDUE_THRESHOLD_DAYS),
|
|
4198
|
+
detail
|
|
4199
|
+
}),
|
|
4200
|
+
t("doctor.check.pending_overdue.remediation")
|
|
3877
4201
|
);
|
|
3878
4202
|
}
|
|
3879
|
-
function createUnderseededCheck(inspection) {
|
|
4203
|
+
function createUnderseededCheck(t, inspection) {
|
|
3880
4204
|
if (!inspection.underseeded) {
|
|
3881
4205
|
return okCheck(
|
|
3882
|
-
"
|
|
3883
|
-
|
|
4206
|
+
t("doctor.check.underseeded.name"),
|
|
4207
|
+
t("doctor.check.underseeded.ok", {
|
|
4208
|
+
count: String(inspection.node_count),
|
|
4209
|
+
threshold: String(inspection.threshold)
|
|
4210
|
+
})
|
|
3884
4211
|
);
|
|
3885
4212
|
}
|
|
3886
4213
|
return issueCheck(
|
|
3887
|
-
"
|
|
4214
|
+
t("doctor.check.underseeded.name"),
|
|
3888
4215
|
"ok",
|
|
3889
4216
|
"info",
|
|
3890
4217
|
"knowledge_underseeded",
|
|
3891
|
-
`
|
|
3892
|
-
|
|
4218
|
+
t(`doctor.check.underseeded.message.${inspection.node_count === 1 ? "singular" : "plural"}`, {
|
|
4219
|
+
count: String(inspection.node_count),
|
|
4220
|
+
threshold: String(inspection.threshold)
|
|
4221
|
+
}),
|
|
4222
|
+
t("doctor.check.underseeded.remediation")
|
|
3893
4223
|
);
|
|
3894
4224
|
}
|
|
3895
|
-
function createSessionHintsStaleCheck(inspection) {
|
|
4225
|
+
function createSessionHintsStaleCheck(t, inspection) {
|
|
3896
4226
|
if (inspection.candidates.length === 0) {
|
|
3897
4227
|
return okCheck(
|
|
3898
|
-
"
|
|
3899
|
-
|
|
4228
|
+
t("doctor.check.session_hints_stale.name"),
|
|
4229
|
+
t("doctor.check.session_hints_stale.ok", {
|
|
4230
|
+
days: String(SESSION_HINTS_STALE_DAYS)
|
|
4231
|
+
})
|
|
3900
4232
|
);
|
|
3901
4233
|
}
|
|
3902
4234
|
const first = inspection.candidates[0];
|
|
3903
4235
|
const detail = `${first.path} (${first.age_days}d old)`;
|
|
4236
|
+
const count = inspection.candidates.length;
|
|
3904
4237
|
return issueCheck(
|
|
3905
|
-
"
|
|
4238
|
+
t("doctor.check.session_hints_stale.name"),
|
|
3906
4239
|
"ok",
|
|
3907
4240
|
"info",
|
|
3908
4241
|
"knowledge_session_hints_stale",
|
|
3909
|
-
|
|
3910
|
-
|
|
4242
|
+
t(`doctor.check.session_hints_stale.message.${count === 1 ? "singular" : "plural"}`, {
|
|
4243
|
+
count: String(count),
|
|
4244
|
+
days: String(SESSION_HINTS_STALE_DAYS),
|
|
4245
|
+
detail
|
|
4246
|
+
}),
|
|
4247
|
+
t("doctor.check.session_hints_stale.remediation")
|
|
3911
4248
|
);
|
|
3912
4249
|
}
|
|
3913
|
-
function createStaleServeLockCheck(inspection) {
|
|
4250
|
+
function createStaleServeLockCheck(t, inspection) {
|
|
3914
4251
|
if (!inspection.present) {
|
|
3915
|
-
return okCheck(
|
|
4252
|
+
return okCheck(
|
|
4253
|
+
t("doctor.check.stale_serve_lock.name"),
|
|
4254
|
+
t("doctor.check.stale_serve_lock.ok.no_lock")
|
|
4255
|
+
);
|
|
3916
4256
|
}
|
|
3917
4257
|
if (inspection.pidAlive) {
|
|
3918
4258
|
return okCheck(
|
|
3919
|
-
"
|
|
3920
|
-
|
|
4259
|
+
t("doctor.check.stale_serve_lock.name"),
|
|
4260
|
+
t("doctor.check.stale_serve_lock.ok.live_pid", {
|
|
4261
|
+
pid: String(inspection.pid)
|
|
4262
|
+
})
|
|
3921
4263
|
);
|
|
3922
4264
|
}
|
|
3923
4265
|
const days = Math.floor(inspection.ageMs / MS_PER_DAY);
|
|
3924
4266
|
const hours = Math.floor(inspection.ageMs / (60 * 60 * 1e3));
|
|
3925
|
-
const acquiredAgo = days >= 1 ?
|
|
4267
|
+
const acquiredAgo = days >= 1 ? t(`doctor.check.stale_serve_lock.age.day.${days === 1 ? "singular" : "plural"}`, {
|
|
4268
|
+
count: String(days)
|
|
4269
|
+
}) : t(`doctor.check.stale_serve_lock.age.hour.${hours === 1 ? "singular" : "plural"}`, {
|
|
4270
|
+
count: String(hours)
|
|
4271
|
+
});
|
|
3926
4272
|
return issueCheck(
|
|
3927
|
-
"
|
|
4273
|
+
t("doctor.check.stale_serve_lock.name"),
|
|
3928
4274
|
"ok",
|
|
3929
4275
|
"info",
|
|
3930
4276
|
"stale_serve_lock",
|
|
3931
|
-
|
|
3932
|
-
|
|
4277
|
+
t("doctor.check.stale_serve_lock.message.dead_pid", {
|
|
4278
|
+
pid: String(inspection.pid),
|
|
4279
|
+
acquiredAgo
|
|
4280
|
+
}),
|
|
4281
|
+
t("doctor.check.stale_serve_lock.remediation.dead_pid")
|
|
3933
4282
|
);
|
|
3934
4283
|
}
|
|
3935
4284
|
function extractKnowledgeFrontmatterRelevanceScope(source) {
|
|
@@ -4128,64 +4477,81 @@ function readRecentGitTouchedPaths(projectRoot, windowDays) {
|
|
|
4128
4477
|
}
|
|
4129
4478
|
return Array.from(set);
|
|
4130
4479
|
}
|
|
4131
|
-
function createNarrowNoPathsCheck(inspection) {
|
|
4480
|
+
function createNarrowNoPathsCheck(t, inspection) {
|
|
4132
4481
|
if (inspection.candidates.length === 0) {
|
|
4133
4482
|
return okCheck(
|
|
4134
|
-
"
|
|
4135
|
-
"
|
|
4483
|
+
t("doctor.check.narrow_no_paths.name"),
|
|
4484
|
+
t("doctor.check.narrow_no_paths.ok")
|
|
4136
4485
|
);
|
|
4137
4486
|
}
|
|
4138
4487
|
const first = inspection.candidates[0];
|
|
4139
4488
|
const detail = `${first.stable_id} (${first.path})`;
|
|
4489
|
+
const count = inspection.candidates.length;
|
|
4140
4490
|
return issueCheck(
|
|
4141
|
-
"
|
|
4491
|
+
t("doctor.check.narrow_no_paths.name"),
|
|
4142
4492
|
"warn",
|
|
4143
4493
|
"warning",
|
|
4144
4494
|
"knowledge_narrow_no_paths",
|
|
4145
|
-
|
|
4146
|
-
|
|
4495
|
+
t(`doctor.check.narrow_no_paths.message.${count === 1 ? "singular" : "plural"}`, {
|
|
4496
|
+
count: String(count),
|
|
4497
|
+
detail
|
|
4498
|
+
}),
|
|
4499
|
+
t("doctor.check.narrow_no_paths.remediation")
|
|
4147
4500
|
);
|
|
4148
4501
|
}
|
|
4149
|
-
function createRelevancePathsDanglingCheck(inspection) {
|
|
4502
|
+
function createRelevancePathsDanglingCheck(t, inspection) {
|
|
4150
4503
|
if (inspection.entries.length === 0) {
|
|
4151
4504
|
return okCheck(
|
|
4152
|
-
"
|
|
4153
|
-
"
|
|
4505
|
+
t("doctor.check.relevance_paths_dangling.name"),
|
|
4506
|
+
t("doctor.check.relevance_paths_dangling.ok")
|
|
4154
4507
|
);
|
|
4155
4508
|
}
|
|
4156
4509
|
const first = inspection.entries[0];
|
|
4157
4510
|
const detail = `${first.stable_id} at ${first.path} \u2192 \`${first.dangling_glob}\` (0 matches)`;
|
|
4511
|
+
const count = inspection.entries.length;
|
|
4158
4512
|
return issueCheck(
|
|
4159
|
-
"
|
|
4513
|
+
t("doctor.check.relevance_paths_dangling.name"),
|
|
4160
4514
|
"warn",
|
|
4161
4515
|
"warning",
|
|
4162
4516
|
"knowledge_relevance_paths_dangling",
|
|
4163
|
-
|
|
4164
|
-
|
|
4517
|
+
t(`doctor.check.relevance_paths_dangling.message.${count === 1 ? "singular" : "plural"}`, {
|
|
4518
|
+
count: String(count),
|
|
4519
|
+
detail
|
|
4520
|
+
}),
|
|
4521
|
+
t("doctor.check.relevance_paths_dangling.remediation")
|
|
4165
4522
|
);
|
|
4166
4523
|
}
|
|
4167
|
-
function createRelevancePathsDriftCheck(inspection) {
|
|
4524
|
+
function createRelevancePathsDriftCheck(t, inspection) {
|
|
4168
4525
|
if (!inspection.git_available) {
|
|
4169
4526
|
return okCheck(
|
|
4170
|
-
"
|
|
4171
|
-
|
|
4527
|
+
t("doctor.check.relevance_paths_drift.name"),
|
|
4528
|
+
t("doctor.check.relevance_paths_drift.ok.skipped", {
|
|
4529
|
+
windowDays: String(RELEVANCE_PATHS_DRIFT_WINDOW_DAYS)
|
|
4530
|
+
})
|
|
4172
4531
|
);
|
|
4173
4532
|
}
|
|
4174
4533
|
if (inspection.candidates.length === 0) {
|
|
4175
4534
|
return okCheck(
|
|
4176
|
-
"
|
|
4177
|
-
|
|
4535
|
+
t("doctor.check.relevance_paths_drift.name"),
|
|
4536
|
+
t("doctor.check.relevance_paths_drift.ok.fresh", {
|
|
4537
|
+
windowDays: String(RELEVANCE_PATHS_DRIFT_WINDOW_DAYS)
|
|
4538
|
+
})
|
|
4178
4539
|
);
|
|
4179
4540
|
}
|
|
4180
4541
|
const first = inspection.candidates[0];
|
|
4181
4542
|
const detail = `${first.stable_id} at ${first.path} (globs: ${first.globs.join(", ")})`;
|
|
4543
|
+
const count = inspection.candidates.length;
|
|
4182
4544
|
return issueCheck(
|
|
4183
|
-
"
|
|
4545
|
+
t("doctor.check.relevance_paths_drift.name"),
|
|
4184
4546
|
"ok",
|
|
4185
4547
|
"info",
|
|
4186
4548
|
"knowledge_relevance_paths_drift",
|
|
4187
|
-
|
|
4188
|
-
|
|
4549
|
+
t(`doctor.check.relevance_paths_drift.message.${count === 1 ? "singular" : "plural"}`, {
|
|
4550
|
+
count: String(count),
|
|
4551
|
+
windowDays: String(RELEVANCE_PATHS_DRIFT_WINDOW_DAYS),
|
|
4552
|
+
detail
|
|
4553
|
+
}),
|
|
4554
|
+
t("doctor.check.relevance_paths_drift.remediation")
|
|
4189
4555
|
);
|
|
4190
4556
|
}
|
|
4191
4557
|
function inspectRelevanceFieldsMissing(projectRoot) {
|
|
@@ -4328,11 +4694,11 @@ async function applyRelevanceFieldsMissing(candidate) {
|
|
|
4328
4694
|
};
|
|
4329
4695
|
}
|
|
4330
4696
|
}
|
|
4331
|
-
function createRelevanceFieldsMissingCheck(inspection) {
|
|
4697
|
+
function createRelevanceFieldsMissingCheck(t, inspection) {
|
|
4332
4698
|
if (inspection.candidates.length === 0) {
|
|
4333
4699
|
return okCheck(
|
|
4334
|
-
"
|
|
4335
|
-
"
|
|
4700
|
+
t("doctor.check.relevance_fields_missing.name"),
|
|
4701
|
+
t("doctor.check.relevance_fields_missing.ok")
|
|
4336
4702
|
);
|
|
4337
4703
|
}
|
|
4338
4704
|
const first = inspection.candidates[0];
|
|
@@ -4340,13 +4706,17 @@ function createRelevanceFieldsMissingCheck(inspection) {
|
|
|
4340
4706
|
if (first.missing_scope) missingParts.push("relevance_scope");
|
|
4341
4707
|
if (first.missing_paths) missingParts.push("relevance_paths");
|
|
4342
4708
|
const detail = `${first.pending_path} (missing: ${missingParts.join(", ")})`;
|
|
4709
|
+
const count = inspection.candidates.length;
|
|
4343
4710
|
return issueCheck(
|
|
4344
|
-
"
|
|
4711
|
+
t("doctor.check.relevance_fields_missing.name"),
|
|
4345
4712
|
"ok",
|
|
4346
4713
|
"info",
|
|
4347
4714
|
"knowledge_relevance_fields_missing",
|
|
4348
|
-
|
|
4349
|
-
|
|
4715
|
+
t(`doctor.check.relevance_fields_missing.message.${count === 1 ? "singular" : "plural"}`, {
|
|
4716
|
+
count: String(count),
|
|
4717
|
+
detail
|
|
4718
|
+
}),
|
|
4719
|
+
t("doctor.check.relevance_fields_missing.remediation")
|
|
4350
4720
|
);
|
|
4351
4721
|
}
|
|
4352
4722
|
var SKILL_MD_FRONTMATTER_ROOTS = [".claude/skills", ".codex/skills"];
|
|
@@ -4417,23 +4787,26 @@ function extractSkillFrontmatterLines(raw) {
|
|
|
4417
4787
|
}
|
|
4418
4788
|
return null;
|
|
4419
4789
|
}
|
|
4420
|
-
function createSkillMdYamlInvalidCheck(inspection) {
|
|
4790
|
+
function createSkillMdYamlInvalidCheck(t, inspection) {
|
|
4421
4791
|
if (inspection.candidates.length === 0) {
|
|
4422
4792
|
return okCheck(
|
|
4423
|
-
"
|
|
4424
|
-
"
|
|
4793
|
+
t("doctor.check.skill_md_yaml_invalid.name"),
|
|
4794
|
+
t("doctor.check.skill_md_yaml_invalid.ok")
|
|
4425
4795
|
);
|
|
4426
4796
|
}
|
|
4427
4797
|
const first = inspection.candidates[0];
|
|
4428
4798
|
const detail = `${first.path}:${first.line} (key \`${first.key}\` value contains an unquoted ': ' \u2014 preview: \`${first.preview}\`)`;
|
|
4429
4799
|
const plural = inspection.candidates.length === 1;
|
|
4430
4800
|
return issueCheck(
|
|
4431
|
-
"
|
|
4801
|
+
t("doctor.check.skill_md_yaml_invalid.name"),
|
|
4432
4802
|
"warn",
|
|
4433
4803
|
"warning",
|
|
4434
4804
|
"skill_md_yaml_invalid",
|
|
4435
|
-
|
|
4436
|
-
|
|
4805
|
+
t(`doctor.check.skill_md_yaml_invalid.message.${plural ? "singular" : "plural"}`, {
|
|
4806
|
+
count: String(inspection.candidates.length),
|
|
4807
|
+
detail
|
|
4808
|
+
}),
|
|
4809
|
+
t("doctor.check.skill_md_yaml_invalid.remediation")
|
|
4437
4810
|
);
|
|
4438
4811
|
}
|
|
4439
4812
|
var KNOWLEDGE_CANONICAL_TYPE_DIRS_FOR_ONBOARD = [
|
|
@@ -4528,55 +4901,83 @@ function readFrontmatterScalar(content, key) {
|
|
|
4528
4901
|
}
|
|
4529
4902
|
return void 0;
|
|
4530
4903
|
}
|
|
4531
|
-
function createOnboardCoverageCheck(inspection) {
|
|
4904
|
+
function createOnboardCoverageCheck(t, inspection) {
|
|
4532
4905
|
const filledCount = ONBOARD_SLOT_NAMES.filter(
|
|
4533
4906
|
(slot) => inspection.filled[slot].length > 0
|
|
4534
4907
|
).length;
|
|
4535
4908
|
if (inspection.missing.length === 0) {
|
|
4536
4909
|
return okCheck(
|
|
4537
|
-
"
|
|
4538
|
-
|
|
4910
|
+
t("doctor.check.onboard_coverage.name"),
|
|
4911
|
+
t("doctor.check.onboard_coverage.ok.complete", {
|
|
4912
|
+
filledCount: String(filledCount),
|
|
4913
|
+
total: String(ONBOARD_SLOT_TOTAL),
|
|
4914
|
+
optedOutCount: String(inspection.opted_out.length)
|
|
4915
|
+
})
|
|
4539
4916
|
);
|
|
4540
4917
|
}
|
|
4541
4918
|
return issueCheck(
|
|
4542
|
-
"
|
|
4919
|
+
t("doctor.check.onboard_coverage.name"),
|
|
4543
4920
|
"ok",
|
|
4544
4921
|
"info",
|
|
4545
4922
|
"onboard_coverage_incomplete",
|
|
4546
|
-
|
|
4547
|
-
|
|
4923
|
+
t("doctor.check.onboard_coverage.message.incomplete", {
|
|
4924
|
+
missingSlots: inspection.missing.join(", "),
|
|
4925
|
+
filledCount: String(filledCount),
|
|
4926
|
+
total: String(ONBOARD_SLOT_TOTAL),
|
|
4927
|
+
optedOutCount: String(inspection.opted_out.length)
|
|
4928
|
+
}),
|
|
4929
|
+
t("doctor.check.onboard_coverage.remediation.incomplete")
|
|
4548
4930
|
);
|
|
4549
4931
|
}
|
|
4550
|
-
function createNarrowTooFewCheck(inspection) {
|
|
4932
|
+
function createNarrowTooFewCheck(t, inspection) {
|
|
4551
4933
|
const { structural_flagged, telemetry_flagged } = inspection;
|
|
4552
4934
|
if (!structural_flagged && !telemetry_flagged) {
|
|
4553
4935
|
const ratioPct = (inspection.narrow_ratio * 100).toFixed(0);
|
|
4554
|
-
const teleNote = inspection.telemetry_skipped ? "
|
|
4936
|
+
const teleNote = inspection.telemetry_skipped ? t("doctor.check.narrow_too_few.message.telemetry_skipped") : t("doctor.check.narrow_too_few.message.telemetry_window", {
|
|
4937
|
+
silencePct: (inspection.silence_rate * 100).toFixed(0),
|
|
4938
|
+
windowDays: String(SILENCE_WINDOW_DAYS)
|
|
4939
|
+
});
|
|
4555
4940
|
return okCheck(
|
|
4556
|
-
"
|
|
4557
|
-
|
|
4941
|
+
t("doctor.check.narrow_too_few.name"),
|
|
4942
|
+
t("doctor.check.narrow_too_few.ok", {
|
|
4943
|
+
ratioPct,
|
|
4944
|
+
narrowCount: String(inspection.narrow_with_paths_count),
|
|
4945
|
+
totalCount: String(inspection.total_canonical_entries),
|
|
4946
|
+
teleNote
|
|
4947
|
+
})
|
|
4558
4948
|
);
|
|
4559
4949
|
}
|
|
4560
4950
|
const parts = [];
|
|
4561
4951
|
if (structural_flagged) {
|
|
4562
4952
|
const ratioPct = (inspection.narrow_ratio * 100).toFixed(0);
|
|
4563
4953
|
parts.push(
|
|
4564
|
-
|
|
4954
|
+
t("doctor.check.narrow_too_few.message.structural", {
|
|
4955
|
+
ratioPct,
|
|
4956
|
+
narrowCount: String(inspection.narrow_with_paths_count),
|
|
4957
|
+
totalCount: String(inspection.total_canonical_entries),
|
|
4958
|
+
thresholdPct: (NARROW_RATIO_THRESHOLD * 100).toFixed(0)
|
|
4959
|
+
})
|
|
4565
4960
|
);
|
|
4566
4961
|
}
|
|
4567
4962
|
if (telemetry_flagged) {
|
|
4568
4963
|
const silencePct = (inspection.silence_rate * 100).toFixed(0);
|
|
4569
4964
|
parts.push(
|
|
4570
|
-
|
|
4965
|
+
t("doctor.check.narrow_too_few.message.telemetry", {
|
|
4966
|
+
silencePct,
|
|
4967
|
+
silenceFires: String(inspection.silence_fires_in_window),
|
|
4968
|
+
totalFires: String(inspection.total_edit_fires_in_window),
|
|
4969
|
+
windowDays: String(SILENCE_WINDOW_DAYS),
|
|
4970
|
+
thresholdPct: (SILENCE_RATE_THRESHOLD * 100).toFixed(0)
|
|
4971
|
+
})
|
|
4571
4972
|
);
|
|
4572
4973
|
}
|
|
4573
4974
|
return issueCheck(
|
|
4574
|
-
"
|
|
4975
|
+
t("doctor.check.narrow_too_few.name"),
|
|
4575
4976
|
"ok",
|
|
4576
4977
|
"info",
|
|
4577
4978
|
"knowledge_narrow_too_few",
|
|
4578
|
-
|
|
4579
|
-
"
|
|
4979
|
+
t("doctor.check.narrow_too_few.message.summary", { parts: parts.join("; ") }),
|
|
4980
|
+
t("doctor.check.narrow_too_few.remediation")
|
|
4580
4981
|
);
|
|
4581
4982
|
}
|
|
4582
4983
|
function resolvePersonalKnowledgeRoot() {
|
|
@@ -4713,58 +5114,70 @@ function inspectIndexDrift(projectRoot, meta) {
|
|
|
4713
5114
|
);
|
|
4714
5115
|
return { drifts };
|
|
4715
5116
|
}
|
|
4716
|
-
function createStableIdDuplicateCheck(inspection) {
|
|
5117
|
+
function createStableIdDuplicateCheck(t, inspection) {
|
|
4717
5118
|
if (inspection.duplicates.length === 0) {
|
|
4718
5119
|
return okCheck(
|
|
4719
|
-
"
|
|
4720
|
-
"
|
|
5120
|
+
t("doctor.check.stable_id_duplicate.name"),
|
|
5121
|
+
t("doctor.check.stable_id_duplicate.ok")
|
|
4721
5122
|
);
|
|
4722
5123
|
}
|
|
4723
5124
|
const first = inspection.duplicates[0];
|
|
4724
5125
|
const detail = `${first.stable_id} appears in ${first.paths.length} files: ${first.paths.join(", ")}`;
|
|
5126
|
+
const count = inspection.duplicates.length;
|
|
4725
5127
|
return issueCheck(
|
|
4726
|
-
"
|
|
5128
|
+
t("doctor.check.stable_id_duplicate.name"),
|
|
4727
5129
|
"error",
|
|
4728
5130
|
"manual_error",
|
|
4729
5131
|
"knowledge_stable_id_duplicate",
|
|
4730
|
-
|
|
4731
|
-
|
|
5132
|
+
t(`doctor.check.stable_id_duplicate.message.${count === 1 ? "singular" : "plural"}`, {
|
|
5133
|
+
count: String(count),
|
|
5134
|
+
detail
|
|
5135
|
+
}),
|
|
5136
|
+
t("doctor.check.stable_id_duplicate.remediation")
|
|
4732
5137
|
);
|
|
4733
5138
|
}
|
|
4734
|
-
function createLayerMismatchCheck(inspection) {
|
|
5139
|
+
function createLayerMismatchCheck(t, inspection) {
|
|
4735
5140
|
if (inspection.mismatches.length === 0) {
|
|
4736
5141
|
return okCheck(
|
|
4737
|
-
"
|
|
4738
|
-
"
|
|
5142
|
+
t("doctor.check.layer_mismatch.name"),
|
|
5143
|
+
t("doctor.check.layer_mismatch.ok")
|
|
4739
5144
|
);
|
|
4740
5145
|
}
|
|
4741
5146
|
const first = inspection.mismatches[0];
|
|
4742
5147
|
const detail = `${first.stable_id} at ${first.path} (located in ${first.located_in}, expected ${first.expected_layer})`;
|
|
5148
|
+
const count = inspection.mismatches.length;
|
|
4743
5149
|
return issueCheck(
|
|
4744
|
-
"
|
|
5150
|
+
t("doctor.check.layer_mismatch.name"),
|
|
4745
5151
|
"error",
|
|
4746
5152
|
"manual_error",
|
|
4747
5153
|
"knowledge_layer_mismatch",
|
|
4748
|
-
|
|
4749
|
-
|
|
5154
|
+
t(`doctor.check.layer_mismatch.message.${count === 1 ? "singular" : "plural"}`, {
|
|
5155
|
+
count: String(count),
|
|
5156
|
+
detail
|
|
5157
|
+
}),
|
|
5158
|
+
t("doctor.check.layer_mismatch.remediation")
|
|
4750
5159
|
);
|
|
4751
5160
|
}
|
|
4752
|
-
function createIndexDriftCheck(inspection) {
|
|
5161
|
+
function createIndexDriftCheck(t, inspection) {
|
|
4753
5162
|
if (inspection.drifts.length === 0) {
|
|
4754
5163
|
return okCheck(
|
|
4755
|
-
"
|
|
4756
|
-
"
|
|
5164
|
+
t("doctor.check.index_drift.name"),
|
|
5165
|
+
t("doctor.check.index_drift.ok")
|
|
4757
5166
|
);
|
|
4758
5167
|
}
|
|
4759
5168
|
const first = inspection.drifts[0];
|
|
4760
5169
|
const detail = `${first.layer}.${first.type} counter=${first.counter} but max_observed=${first.max_observed} (would propose counters.${first.layer}.${first.type}=${first.proposed_after})`;
|
|
5170
|
+
const count = inspection.drifts.length;
|
|
4761
5171
|
return issueCheck(
|
|
4762
|
-
"
|
|
5172
|
+
t("doctor.check.index_drift.name"),
|
|
4763
5173
|
"error",
|
|
4764
5174
|
"fixable_error",
|
|
4765
5175
|
"knowledge_index_drift",
|
|
4766
|
-
|
|
4767
|
-
|
|
5176
|
+
t(`doctor.check.index_drift.message.${count === 1 ? "singular" : "plural"}`, {
|
|
5177
|
+
count: String(count),
|
|
5178
|
+
detail
|
|
5179
|
+
}),
|
|
5180
|
+
t("doctor.check.index_drift.remediation")
|
|
4768
5181
|
);
|
|
4769
5182
|
}
|
|
4770
5183
|
async function migrateBootstrapMarkers(projectRoot) {
|
|
@@ -5137,7 +5550,7 @@ async function runDoctorCiteCoverage(projectRoot, options) {
|
|
|
5137
5550
|
break;
|
|
5138
5551
|
}
|
|
5139
5552
|
}
|
|
5140
|
-
const filteredTurns = options.client === "all" ? assistantTurns : assistantTurns.filter((
|
|
5553
|
+
const filteredTurns = options.client === "all" ? assistantTurns : assistantTurns.filter((t) => t.client === options.client);
|
|
5141
5554
|
let clientSessionIds = null;
|
|
5142
5555
|
if (options.client !== "all") {
|
|
5143
5556
|
clientSessionIds = /* @__PURE__ */ new Set();
|