@fenglimg/fabric-server 2.0.0-rc.25 → 2.0.0-rc.26
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.
|
@@ -1473,10 +1473,9 @@ async function reconcileKnowledge(projectRoot, opts) {
|
|
|
1473
1473
|
// src/services/serve-lock.ts
|
|
1474
1474
|
import fs from "fs";
|
|
1475
1475
|
import path from "path";
|
|
1476
|
-
import { createTranslator,
|
|
1476
|
+
import { createTranslator, resolveFabricLocale } from "@fenglimg/fabric-shared";
|
|
1477
1477
|
import { IOFabricError as IOFabricError2 } from "@fenglimg/fabric-shared/errors";
|
|
1478
1478
|
var LOCK_FILENAME = ".serve.lock";
|
|
1479
|
-
var t = createTranslator(detectNodeLocale());
|
|
1480
1479
|
var ServeLockHeldError = class extends IOFabricError2 {
|
|
1481
1480
|
code = "SERVE_LOCK_HELD";
|
|
1482
1481
|
httpStatus = 423;
|
|
@@ -1504,6 +1503,7 @@ function acquireLock(projectRoot, opts) {
|
|
|
1504
1503
|
} catch {
|
|
1505
1504
|
}
|
|
1506
1505
|
if (state && state.pid && state.pid !== process.pid && isAlive(state.pid) && !opts?.force) {
|
|
1506
|
+
const t = createTranslator(resolveFabricLocale(projectRoot));
|
|
1507
1507
|
throw new ServeLockHeldError(
|
|
1508
1508
|
`serve lock held by live PID ${state.pid}`,
|
|
1509
1509
|
{
|
|
@@ -1554,6 +1554,7 @@ function checkLockOrThrow(projectRoot, opts) {
|
|
|
1554
1554
|
return;
|
|
1555
1555
|
}
|
|
1556
1556
|
if (opts?.force) return;
|
|
1557
|
+
const t = createTranslator(resolveFabricLocale(projectRoot));
|
|
1557
1558
|
throw new ServeLockHeldError(
|
|
1558
1559
|
`serve lock held by live PID ${state.pid}`,
|
|
1559
1560
|
{
|
|
@@ -1574,6 +1575,7 @@ import { minimatch } from "minimatch";
|
|
|
1574
1575
|
import {
|
|
1575
1576
|
agentsMetaSchema as agentsMetaSchema4,
|
|
1576
1577
|
AgentsMetaCountersSchema,
|
|
1578
|
+
createTranslator as createTranslator2,
|
|
1577
1579
|
forensicReportSchema,
|
|
1578
1580
|
parseKnowledgeId as parseKnowledgeId2,
|
|
1579
1581
|
knowledgeTestIndexSchema as knowledgeTestIndexSchema2,
|
|
@@ -1583,7 +1585,8 @@ import {
|
|
|
1583
1585
|
BOOTSTRAP_MARKER_END,
|
|
1584
1586
|
BOOTSTRAP_REGEX,
|
|
1585
1587
|
ONBOARD_SLOT_NAMES,
|
|
1586
|
-
ONBOARD_SLOT_TOTAL
|
|
1588
|
+
ONBOARD_SLOT_TOTAL,
|
|
1589
|
+
resolveFabricLocale as resolveFabricLocale2
|
|
1587
1590
|
} from "@fenglimg/fabric-shared";
|
|
1588
1591
|
import { detectFramework } from "@fenglimg/fabric-shared/node";
|
|
1589
1592
|
import { atomicWriteJson as atomicWriteJson2, atomicWriteText as atomicWriteText4 } from "@fenglimg/fabric-shared/node/atomic-write";
|
|
@@ -1669,6 +1672,7 @@ var TARGET_FILE_PATHS = [
|
|
|
1669
1672
|
];
|
|
1670
1673
|
async function runDoctorReport(target) {
|
|
1671
1674
|
const projectRoot = normalizeTarget(target);
|
|
1675
|
+
const t = createTranslator2(resolveFabricLocale2(projectRoot));
|
|
1672
1676
|
const framework = detectFramework(projectRoot);
|
|
1673
1677
|
const entryPoints = collectEntryPoints(projectRoot);
|
|
1674
1678
|
const [
|
|
@@ -1722,91 +1726,91 @@ async function runDoctorReport(target) {
|
|
|
1722
1726
|
const skillMdYamlInvalid = inspectSkillMdYamlInvalid(projectRoot);
|
|
1723
1727
|
const onboardCoverage = inspectOnboardCoverage(projectRoot);
|
|
1724
1728
|
const checks = [
|
|
1725
|
-
createBootstrapAnchorCheck(bootstrapAnchor),
|
|
1729
|
+
createBootstrapAnchorCheck(t, bootstrapAnchor),
|
|
1726
1730
|
// v2.0.0-rc.19 TASK-004: bootstrap marker migration check sits adjacent to
|
|
1727
1731
|
// the anchor check — both are bootstrap-file invariants. fixable_error
|
|
1728
1732
|
// when any of the four target paths still carries the legacy marker.
|
|
1729
|
-
createBootstrapMarkerMigrationCheck(bootstrapMarkerMigration),
|
|
1733
|
+
createBootstrapMarkerMigrationCheck(t, bootstrapMarkerMigration),
|
|
1730
1734
|
// v2.0.0-rc.19 TASK-005: L1 + L2 byte-level drift detection sit immediately
|
|
1731
1735
|
// after the marker migration check. Order: anchor existence → migration →
|
|
1732
1736
|
// L1 (canonical ↔ snapshot) → L2 (snapshot+rules ↔ three-end blocks).
|
|
1733
|
-
createL1BootstrapSnapshotDriftCheck(l1BootstrapSnapshotDrift),
|
|
1734
|
-
createL2ManagedBlockDriftCheck(l2ManagedBlockDrift),
|
|
1735
|
-
createKnowledgeDirMissingCheck(knowledgeDirMissing),
|
|
1737
|
+
createL1BootstrapSnapshotDriftCheck(t, l1BootstrapSnapshotDrift),
|
|
1738
|
+
createL2ManagedBlockDriftCheck(t, l2ManagedBlockDrift),
|
|
1739
|
+
createKnowledgeDirMissingCheck(t, knowledgeDirMissing),
|
|
1736
1740
|
// v2.0.0-rc.22 TASK-006: baseline filename format. Sits adjacent to
|
|
1737
1741
|
// knowledge_dir_missing — both are knowledge-layout invariants. manual_error
|
|
1738
1742
|
// kind; resolution is manual file deletion (rc.23 TASK-012 (F8a) removed
|
|
1739
1743
|
// the baseline-emit pipeline, so no auto-fix exists).
|
|
1740
|
-
createBaselineFilenameFormatCheck(baselineFilenameFormat),
|
|
1741
|
-
createForensicCheck(forensic, framework.kind, entryPoints.length),
|
|
1744
|
+
createBaselineFilenameFormatCheck(t, baselineFilenameFormat),
|
|
1745
|
+
createForensicCheck(t, forensic, framework.kind, entryPoints.length),
|
|
1742
1746
|
// v2.0: removed `createInitContextCheck` — `.fabric/init-context.json`
|
|
1743
1747
|
// is owned by the AI-side client init skill, not by `fabric install` CLI.
|
|
1744
1748
|
// The file's absence is a legitimate post-init state when the skill has
|
|
1745
1749
|
// not yet run, so flagging it as a doctor manual_error misrepresents
|
|
1746
1750
|
// ownership.
|
|
1747
|
-
createMetaCheck(meta),
|
|
1748
|
-
createRuleContentRefCheck(meta),
|
|
1751
|
+
createMetaCheck(t, meta),
|
|
1752
|
+
createRuleContentRefCheck(t, meta),
|
|
1749
1753
|
// v2.0 / rc.2: `createRuleSectionsCheck` removed — it parsed v1.x
|
|
1750
1754
|
// [MANDATORY_INJECTION] sections out of legacy rule files, a structural
|
|
1751
1755
|
// concept that has no v2 equivalent. rc.4 will introduce a dedicated v2
|
|
1752
1756
|
// lint suite for the new knowledge frontmatter contract.
|
|
1753
|
-
createKnowledgeTestIndexCheck(knowledgeTestIndex),
|
|
1754
|
-
createEventLedgerCheck(eventLedger),
|
|
1755
|
-
createEventLedgerPartialWriteCheck(eventLedger),
|
|
1756
|
-
createMcpConfigInWrongFileCheck(mcpConfigInWrongFile),
|
|
1757
|
-
createMetaManuallyDivergedCheck(metaManuallyDiverged),
|
|
1758
|
-
createKnowledgeDirUnindexedCheck(knowledgeDirUnindexed),
|
|
1759
|
-
createStableIdCollisionCheck(stableIdCollision),
|
|
1760
|
-
createCounterDesyncCheck(counterDesync),
|
|
1761
|
-
createFilesystemEditFallbackCheck(filesystemEditFallback),
|
|
1757
|
+
createKnowledgeTestIndexCheck(t, knowledgeTestIndex),
|
|
1758
|
+
createEventLedgerCheck(t, eventLedger),
|
|
1759
|
+
createEventLedgerPartialWriteCheck(t, eventLedger),
|
|
1760
|
+
createMcpConfigInWrongFileCheck(t, mcpConfigInWrongFile),
|
|
1761
|
+
createMetaManuallyDivergedCheck(t, metaManuallyDiverged),
|
|
1762
|
+
createKnowledgeDirUnindexedCheck(t, knowledgeDirUnindexed),
|
|
1763
|
+
createStableIdCollisionCheck(t, stableIdCollision),
|
|
1764
|
+
createCounterDesyncCheck(t, counterDesync),
|
|
1765
|
+
createFilesystemEditFallbackCheck(t, filesystemEditFallback),
|
|
1762
1766
|
// rc.4 TASK-001: read-side lint checks #16-18. Findings only — mutation
|
|
1763
1767
|
// + event emission lands in TASK-003 behind --apply-lint.
|
|
1764
|
-
createOrphanDemoteCheck(orphanDemote),
|
|
1765
|
-
createStaleArchiveCheck(staleArchive),
|
|
1766
|
-
createPendingOverdueCheck(pendingOverdue),
|
|
1768
|
+
createOrphanDemoteCheck(t, orphanDemote),
|
|
1769
|
+
createStaleArchiveCheck(t, staleArchive),
|
|
1770
|
+
createPendingOverdueCheck(t, pendingOverdue),
|
|
1767
1771
|
// rc.4 TASK-002: read-side integrity checks #19-21. Stable_id duplicate
|
|
1768
1772
|
// runs first in this trio — it is the most critical integrity break and
|
|
1769
1773
|
// surfaces ahead of layer-mismatch / index-drift in the report so a
|
|
1770
1774
|
// human operator triages the collision before reasoning about counter
|
|
1771
1775
|
// state. Index drift is the only fixable_error of the three; stable_id
|
|
1772
1776
|
// duplicate and layer mismatch require manual triage (rename / move).
|
|
1773
|
-
createStableIdDuplicateCheck(stableIdDuplicate),
|
|
1774
|
-
createLayerMismatchCheck(layerMismatch),
|
|
1775
|
-
createIndexDriftCheck(indexDrift),
|
|
1777
|
+
createStableIdDuplicateCheck(t, stableIdDuplicate),
|
|
1778
|
+
createLayerMismatchCheck(t, layerMismatch),
|
|
1779
|
+
createIndexDriftCheck(t, indexDrift),
|
|
1776
1780
|
// rc.5 TASK-010: read-side underseeded-corpus check (#22). Info kind —
|
|
1777
1781
|
// does not bump report status. Recommends running the fabric-import skill
|
|
1778
1782
|
// to backfill knowledge when the corpus is below the threshold floor.
|
|
1779
|
-
createUnderseededCheck(underseeded),
|
|
1783
|
+
createUnderseededCheck(t, underseeded),
|
|
1780
1784
|
// rc.5 TASK-013 (C4): relevance_paths hygiene checks #23/#24/#25.
|
|
1781
1785
|
// All three are flag-only in rc.5 (no apply-lint mutations).
|
|
1782
1786
|
// #23 narrow_no_paths — warning kind (silent recall risk)
|
|
1783
1787
|
// #24 relevance_paths_dangling — warning kind (glob → zero matches)
|
|
1784
1788
|
// #25 relevance_paths_drift — info kind (git-log heuristic; noisy)
|
|
1785
|
-
createNarrowNoPathsCheck(narrowNoPaths),
|
|
1786
|
-
createRelevancePathsDanglingCheck(relevancePathsDangling),
|
|
1787
|
-
createRelevancePathsDriftCheck(relevancePathsDrift),
|
|
1789
|
+
createNarrowNoPathsCheck(t, narrowNoPaths),
|
|
1790
|
+
createRelevancePathsDanglingCheck(t, relevancePathsDangling),
|
|
1791
|
+
createRelevancePathsDriftCheck(t, relevancePathsDrift),
|
|
1788
1792
|
// rc.6 TASK-023 (E6): narrow_too_few (lint #26). Info kind; both arms
|
|
1789
1793
|
// (structural + telemetry) recommend the same fabric-import action.
|
|
1790
|
-
createNarrowTooFewCheck(narrowTooFew),
|
|
1794
|
+
createNarrowTooFewCheck(t, narrowTooFew),
|
|
1791
1795
|
// rc.6 TASK-021 (E3): session-hints cache hygiene (lint #27). Info kind.
|
|
1792
|
-
createSessionHintsStaleCheck(sessionHintsStale),
|
|
1796
|
+
createSessionHintsStaleCheck(t, sessionHintsStale),
|
|
1793
1797
|
// rc.23 TASK-010 (e): stale .fabric/.serve.lock advisory. Info kind —
|
|
1794
1798
|
// does not bump report status. `--fix` unlinks the corpse and emits
|
|
1795
1799
|
// `serve_lock_cleared`.
|
|
1796
|
-
createStaleServeLockCheck(staleServeLock),
|
|
1800
|
+
createStaleServeLockCheck(t, staleServeLock),
|
|
1797
1801
|
// v2.0.0-rc.9 TASK-003 (A3): relevance fields back-fill (lint #28).
|
|
1798
1802
|
// Info kind — applies to pending entries only; canonical entries get
|
|
1799
1803
|
// the fields written verbatim by fab_review.approve/modify.
|
|
1800
|
-
createRelevanceFieldsMissingCheck(relevanceFieldsMissing),
|
|
1804
|
+
createRelevanceFieldsMissingCheck(t, relevanceFieldsMissing),
|
|
1801
1805
|
// rc.12 lint #29: skill_md_yaml_invalid. Warning kind — surfaces
|
|
1802
1806
|
// SKILL.md frontmatter that Codex CLI silently drops at load.
|
|
1803
|
-
createSkillMdYamlInvalidCheck(skillMdYamlInvalid),
|
|
1807
|
+
createSkillMdYamlInvalidCheck(t, skillMdYamlInvalid),
|
|
1804
1808
|
// v2.0.0-rc.23 TASK-014 (F8c): Onboard coverage advisory. Info kind.
|
|
1805
1809
|
// Surfaces uncovered S5 onboard slots and recommends /fabric-archive
|
|
1806
1810
|
// first-run phase. Sits adjacent to Skill markdown YAML — both are
|
|
1807
1811
|
// Skill-adjacent advisories. --fix never mutates onboard state.
|
|
1808
|
-
createOnboardCoverageCheck(onboardCoverage),
|
|
1809
|
-
createPreexistingRootFilesCheck(preexistingRootFiles)
|
|
1812
|
+
createOnboardCoverageCheck(t, onboardCoverage),
|
|
1813
|
+
createPreexistingRootFilesCheck(t, preexistingRootFiles)
|
|
1810
1814
|
// v2.0 / rc.2: `createLegacyClientPathCheck` removed. The schema now
|
|
1811
1815
|
// rejects retired clientPaths keys (windsurf/rooCode/geminiCLI) at Zod
|
|
1812
1816
|
// parse time, so the soft-deprecation warn-and-fix path no longer has a
|
|
@@ -2568,21 +2572,25 @@ async function inspectBootstrapMarkerMigration(target) {
|
|
|
2568
2572
|
}
|
|
2569
2573
|
return { filesNeedingMigration };
|
|
2570
2574
|
}
|
|
2571
|
-
function createBootstrapMarkerMigrationCheck(inspection) {
|
|
2575
|
+
function createBootstrapMarkerMigrationCheck(t, inspection) {
|
|
2572
2576
|
if (inspection.filesNeedingMigration.length === 0) {
|
|
2573
2577
|
return okCheck(
|
|
2574
|
-
"
|
|
2575
|
-
"
|
|
2578
|
+
t("doctor.check.bootstrap_marker_migration.name"),
|
|
2579
|
+
t("doctor.check.bootstrap_marker_migration.ok")
|
|
2576
2580
|
);
|
|
2577
2581
|
}
|
|
2578
2582
|
const list = inspection.filesNeedingMigration.join(", ");
|
|
2583
|
+
const count = inspection.filesNeedingMigration.length;
|
|
2579
2584
|
return issueCheck(
|
|
2580
|
-
"
|
|
2585
|
+
t("doctor.check.bootstrap_marker_migration.name"),
|
|
2581
2586
|
"error",
|
|
2582
2587
|
"fixable_error",
|
|
2583
2588
|
"bootstrap_marker_migration_required",
|
|
2584
|
-
|
|
2585
|
-
|
|
2589
|
+
t(`doctor.check.bootstrap_marker_migration.message.${count === 1 ? "singular" : "plural"}`, {
|
|
2590
|
+
count: String(count),
|
|
2591
|
+
list
|
|
2592
|
+
}),
|
|
2593
|
+
t("doctor.check.bootstrap_marker_migration.remediation")
|
|
2586
2594
|
);
|
|
2587
2595
|
}
|
|
2588
2596
|
async function inspectL1BootstrapSnapshotDrift(target) {
|
|
@@ -2601,20 +2609,20 @@ async function inspectL1BootstrapSnapshotDrift(target) {
|
|
|
2601
2609
|
}
|
|
2602
2610
|
return { status: "drift", canonical: BOOTSTRAP_CANONICAL, onDisk };
|
|
2603
2611
|
}
|
|
2604
|
-
function createL1BootstrapSnapshotDriftCheck(inspection) {
|
|
2612
|
+
function createL1BootstrapSnapshotDriftCheck(t, inspection) {
|
|
2605
2613
|
if (inspection.status === "drift") {
|
|
2606
2614
|
return issueCheck(
|
|
2607
|
-
"
|
|
2615
|
+
t("doctor.check.bootstrap_snapshot_drift.name"),
|
|
2608
2616
|
"error",
|
|
2609
2617
|
"fixable_error",
|
|
2610
2618
|
"bootstrap_snapshot_drift",
|
|
2611
|
-
".
|
|
2612
|
-
"
|
|
2619
|
+
t("doctor.check.bootstrap_snapshot_drift.message.drift"),
|
|
2620
|
+
t("doctor.check.bootstrap_snapshot_drift.remediation.drift")
|
|
2613
2621
|
);
|
|
2614
2622
|
}
|
|
2615
2623
|
return okCheck(
|
|
2616
|
-
"
|
|
2617
|
-
inspection.status === "ok" ? ".
|
|
2624
|
+
t("doctor.check.bootstrap_snapshot_drift.name"),
|
|
2625
|
+
inspection.status === "ok" ? t("doctor.check.bootstrap_snapshot_drift.ok.ok") : t("doctor.check.bootstrap_snapshot_drift.ok.missing_delegated")
|
|
2618
2626
|
);
|
|
2619
2627
|
}
|
|
2620
2628
|
async function inspectL2ManagedBlockDrift(target) {
|
|
@@ -2706,39 +2714,46 @@ ${projectRules}`;
|
|
|
2706
2714
|
}
|
|
2707
2715
|
return { status: "drift", drifted };
|
|
2708
2716
|
}
|
|
2709
|
-
function createL2ManagedBlockDriftCheck(inspection) {
|
|
2717
|
+
function createL2ManagedBlockDriftCheck(t, inspection) {
|
|
2710
2718
|
if (inspection.status === "drift") {
|
|
2711
2719
|
const list = inspection.drifted.map((d) => d.path).join(", ");
|
|
2720
|
+
const count = inspection.drifted.length;
|
|
2712
2721
|
return issueCheck(
|
|
2713
|
-
"
|
|
2722
|
+
t("doctor.check.managed_block_drift.name"),
|
|
2714
2723
|
"error",
|
|
2715
2724
|
"fixable_error",
|
|
2716
2725
|
"managed_block_drift",
|
|
2717
|
-
|
|
2718
|
-
|
|
2726
|
+
t(`doctor.check.managed_block_drift.message.${count === 1 ? "singular" : "plural"}`, {
|
|
2727
|
+
count: String(count),
|
|
2728
|
+
list
|
|
2729
|
+
}),
|
|
2730
|
+
t("doctor.check.managed_block_drift.remediation")
|
|
2719
2731
|
);
|
|
2720
2732
|
}
|
|
2721
2733
|
return okCheck(
|
|
2722
|
-
"
|
|
2723
|
-
inspection.status === "ok" ? "
|
|
2734
|
+
t("doctor.check.managed_block_drift.name"),
|
|
2735
|
+
inspection.status === "ok" ? t("doctor.check.managed_block_drift.ok.ok") : t("doctor.check.managed_block_drift.ok.no_managed_block")
|
|
2724
2736
|
);
|
|
2725
2737
|
}
|
|
2726
|
-
function createBootstrapAnchorCheck(inspection) {
|
|
2738
|
+
function createBootstrapAnchorCheck(t, inspection) {
|
|
2727
2739
|
if (!inspection.hasAgentsMd && !inspection.hasClaudeMd) {
|
|
2728
2740
|
return issueCheck(
|
|
2729
|
-
"
|
|
2741
|
+
t("doctor.check.bootstrap_anchor.name"),
|
|
2730
2742
|
"error",
|
|
2731
2743
|
"fixable_error",
|
|
2732
2744
|
"bootstrap_anchor_missing",
|
|
2733
|
-
"
|
|
2734
|
-
"
|
|
2745
|
+
t("doctor.check.bootstrap_anchor.message.missing"),
|
|
2746
|
+
t("doctor.check.bootstrap_anchor.remediation.missing")
|
|
2735
2747
|
);
|
|
2736
2748
|
}
|
|
2737
2749
|
const present = [
|
|
2738
2750
|
inspection.hasAgentsMd ? "AGENTS.md" : null,
|
|
2739
2751
|
inspection.hasClaudeMd ? "CLAUDE.md" : null
|
|
2740
2752
|
].filter((entry) => entry !== null).join(", ");
|
|
2741
|
-
return okCheck(
|
|
2753
|
+
return okCheck(
|
|
2754
|
+
t("doctor.check.bootstrap_anchor.name"),
|
|
2755
|
+
t("doctor.check.bootstrap_anchor.ok", { present })
|
|
2756
|
+
);
|
|
2742
2757
|
}
|
|
2743
2758
|
function inspectKnowledgeDirMissing(projectRoot) {
|
|
2744
2759
|
const knowledgeRoot = join6(projectRoot, ".fabric", "knowledge");
|
|
@@ -2799,154 +2814,269 @@ function inspectBaselineFilenameFormat(projectRoot) {
|
|
|
2799
2814
|
offenders.sort((a, b) => a.path.localeCompare(b.path));
|
|
2800
2815
|
return { offenders };
|
|
2801
2816
|
}
|
|
2802
|
-
function createBaselineFilenameFormatCheck(inspection) {
|
|
2817
|
+
function createBaselineFilenameFormatCheck(t, inspection) {
|
|
2803
2818
|
if (inspection.offenders.length === 0) {
|
|
2804
2819
|
return okCheck(
|
|
2805
|
-
"
|
|
2806
|
-
"
|
|
2820
|
+
t("doctor.check.baseline_filename_format.name"),
|
|
2821
|
+
t("doctor.check.baseline_filename_format.ok")
|
|
2807
2822
|
);
|
|
2808
2823
|
}
|
|
2809
2824
|
const first = inspection.offenders[0];
|
|
2810
2825
|
const detail = `${first.stable_id} at ${first.path}`;
|
|
2826
|
+
const count = inspection.offenders.length;
|
|
2811
2827
|
return issueCheck(
|
|
2812
|
-
"
|
|
2828
|
+
t("doctor.check.baseline_filename_format.name"),
|
|
2813
2829
|
"error",
|
|
2814
2830
|
"manual_error",
|
|
2815
2831
|
"lint-baseline-filename-format",
|
|
2816
|
-
|
|
2817
|
-
|
|
2832
|
+
t(`doctor.check.baseline_filename_format.message.${count === 1 ? "singular" : "plural"}`, {
|
|
2833
|
+
count: String(count),
|
|
2834
|
+
detail
|
|
2835
|
+
}),
|
|
2836
|
+
t("doctor.check.baseline_filename_format.remediation")
|
|
2818
2837
|
);
|
|
2819
2838
|
}
|
|
2820
|
-
function createKnowledgeDirMissingCheck(inspection) {
|
|
2839
|
+
function createKnowledgeDirMissingCheck(t, inspection) {
|
|
2821
2840
|
if (inspection.missingSubdirs.length > 0) {
|
|
2822
2841
|
const list = inspection.missingSubdirs.join(", ");
|
|
2842
|
+
const count = inspection.missingSubdirs.length;
|
|
2823
2843
|
return issueCheck(
|
|
2824
|
-
"
|
|
2844
|
+
t("doctor.check.knowledge_dir_missing.name"),
|
|
2825
2845
|
"error",
|
|
2826
2846
|
"fixable_error",
|
|
2827
2847
|
"knowledge_dir_missing",
|
|
2828
|
-
|
|
2829
|
-
|
|
2848
|
+
t(`doctor.check.knowledge_dir_missing.message.${count === 1 ? "singular" : "plural"}`, {
|
|
2849
|
+
count: String(count),
|
|
2850
|
+
list
|
|
2851
|
+
}),
|
|
2852
|
+
t("doctor.check.knowledge_dir_missing.remediation")
|
|
2830
2853
|
);
|
|
2831
2854
|
}
|
|
2832
2855
|
return okCheck(
|
|
2833
|
-
"
|
|
2834
|
-
|
|
2856
|
+
t("doctor.check.knowledge_dir_missing.name"),
|
|
2857
|
+
t("doctor.check.knowledge_dir_missing.ok", { count: String(KNOWLEDGE_SUBDIRS3.length) })
|
|
2835
2858
|
);
|
|
2836
2859
|
}
|
|
2837
|
-
function createForensicCheck(forensic, frameworkKind, entryPointCount) {
|
|
2860
|
+
function createForensicCheck(t, forensic, frameworkKind, entryPointCount) {
|
|
2838
2861
|
if (!forensic.present) {
|
|
2839
2862
|
return issueCheck(
|
|
2840
|
-
"
|
|
2863
|
+
t("doctor.check.forensic.name"),
|
|
2841
2864
|
"error",
|
|
2842
2865
|
"manual_error",
|
|
2843
2866
|
"forensic_missing",
|
|
2844
|
-
|
|
2845
|
-
|
|
2867
|
+
t(`doctor.check.forensic.message.missing.${entryPointCount === 1 ? "singular" : "plural"}`, {
|
|
2868
|
+
error: forensic.error ?? t("doctor.check.forensic.message.missing-default"),
|
|
2869
|
+
frameworkKind,
|
|
2870
|
+
count: String(entryPointCount)
|
|
2871
|
+
}),
|
|
2872
|
+
t("doctor.check.forensic.remediation")
|
|
2846
2873
|
);
|
|
2847
2874
|
}
|
|
2848
2875
|
if (!forensic.valid) {
|
|
2849
|
-
return issueCheck(
|
|
2876
|
+
return issueCheck(
|
|
2877
|
+
t("doctor.check.forensic.name"),
|
|
2878
|
+
"error",
|
|
2879
|
+
"manual_error",
|
|
2880
|
+
"forensic_invalid",
|
|
2881
|
+
forensic.error ?? t("doctor.check.forensic.message.invalid-default"),
|
|
2882
|
+
t("doctor.check.forensic.remediation")
|
|
2883
|
+
);
|
|
2850
2884
|
}
|
|
2851
|
-
return okCheck(
|
|
2885
|
+
return okCheck(
|
|
2886
|
+
t("doctor.check.forensic.name"),
|
|
2887
|
+
t("doctor.check.forensic.ok", { frameworkKind: forensic.report?.framework.kind ?? "unknown" })
|
|
2888
|
+
);
|
|
2852
2889
|
}
|
|
2853
|
-
function createMetaCheck(meta) {
|
|
2890
|
+
function createMetaCheck(t, meta) {
|
|
2854
2891
|
if (!meta.present) {
|
|
2855
|
-
return issueCheck(
|
|
2892
|
+
return issueCheck(
|
|
2893
|
+
t("doctor.check.agents_meta.name"),
|
|
2894
|
+
"error",
|
|
2895
|
+
"fixable_error",
|
|
2896
|
+
"agents_meta_missing",
|
|
2897
|
+
t("doctor.check.agents_meta.message.missing"),
|
|
2898
|
+
t("doctor.check.agents_meta.remediation.missing")
|
|
2899
|
+
);
|
|
2856
2900
|
}
|
|
2857
2901
|
if (!meta.valid) {
|
|
2858
|
-
return issueCheck(
|
|
2902
|
+
return issueCheck(
|
|
2903
|
+
t("doctor.check.agents_meta.name"),
|
|
2904
|
+
"error",
|
|
2905
|
+
"manual_error",
|
|
2906
|
+
"agents_meta_invalid",
|
|
2907
|
+
meta.readError ?? t("doctor.check.agents_meta.message.invalid-default"),
|
|
2908
|
+
t("doctor.check.agents_meta.remediation.invalid")
|
|
2909
|
+
);
|
|
2859
2910
|
}
|
|
2860
2911
|
if (meta.stale) {
|
|
2861
2912
|
return issueCheck(
|
|
2862
|
-
"
|
|
2913
|
+
t("doctor.check.agents_meta.name"),
|
|
2863
2914
|
"warn",
|
|
2864
2915
|
"warning",
|
|
2865
2916
|
"agents_meta_stale",
|
|
2866
|
-
|
|
2867
|
-
|
|
2917
|
+
t("doctor.check.agents_meta.message.stale", {
|
|
2918
|
+
revision: meta.revision,
|
|
2919
|
+
computedRevision: meta.computedRevision ?? "<unknown>"
|
|
2920
|
+
}),
|
|
2921
|
+
t("doctor.check.agents_meta.remediation.stale")
|
|
2868
2922
|
);
|
|
2869
2923
|
}
|
|
2870
|
-
return okCheck(
|
|
2924
|
+
return okCheck(
|
|
2925
|
+
t("doctor.check.agents_meta.name"),
|
|
2926
|
+
t("doctor.check.agents_meta.ok", { revision: meta.revision })
|
|
2927
|
+
);
|
|
2871
2928
|
}
|
|
2872
|
-
function createRuleContentRefCheck(meta) {
|
|
2929
|
+
function createRuleContentRefCheck(t, meta) {
|
|
2873
2930
|
if (!meta.valid) {
|
|
2874
|
-
return issueCheck(
|
|
2931
|
+
return issueCheck(
|
|
2932
|
+
t("doctor.check.rule_content_refs.name"),
|
|
2933
|
+
"error",
|
|
2934
|
+
"manual_error",
|
|
2935
|
+
"content_refs_unavailable",
|
|
2936
|
+
t("doctor.check.rule_content_refs.message.unavailable"),
|
|
2937
|
+
t("doctor.check.rule_content_refs.remediation.unavailable")
|
|
2938
|
+
);
|
|
2875
2939
|
}
|
|
2876
2940
|
if (meta.invalidContentRefs.length > 0) {
|
|
2941
|
+
const count = meta.invalidContentRefs.length;
|
|
2877
2942
|
return issueCheck(
|
|
2878
|
-
"
|
|
2943
|
+
t("doctor.check.rule_content_refs.name"),
|
|
2879
2944
|
"error",
|
|
2880
2945
|
"manual_error",
|
|
2881
2946
|
"content_ref_outside_rules",
|
|
2882
|
-
|
|
2883
|
-
|
|
2947
|
+
t(`doctor.check.rule_content_refs.message.outside.${count === 1 ? "singular" : "plural"}`, {
|
|
2948
|
+
count: String(count)
|
|
2949
|
+
}),
|
|
2950
|
+
t("doctor.check.rule_content_refs.remediation.outside")
|
|
2884
2951
|
);
|
|
2885
2952
|
}
|
|
2886
2953
|
if (meta.missingContentRefs.length > 0) {
|
|
2954
|
+
const count = meta.missingContentRefs.length;
|
|
2887
2955
|
return issueCheck(
|
|
2888
|
-
"
|
|
2956
|
+
t("doctor.check.rule_content_refs.name"),
|
|
2889
2957
|
"error",
|
|
2890
2958
|
"fixable_error",
|
|
2891
2959
|
"content_ref_missing",
|
|
2892
|
-
|
|
2893
|
-
|
|
2960
|
+
t(`doctor.check.rule_content_refs.message.missing.${count === 1 ? "singular" : "plural"}`, {
|
|
2961
|
+
count: String(count)
|
|
2962
|
+
}),
|
|
2963
|
+
t("doctor.check.rule_content_refs.remediation.missing")
|
|
2894
2964
|
);
|
|
2895
2965
|
}
|
|
2896
|
-
return okCheck("
|
|
2966
|
+
return okCheck(t("doctor.check.rule_content_refs.name"), t("doctor.check.rule_content_refs.ok"));
|
|
2897
2967
|
}
|
|
2898
|
-
function createKnowledgeTestIndexCheck(index) {
|
|
2968
|
+
function createKnowledgeTestIndexCheck(t, index) {
|
|
2899
2969
|
if (!index.present) {
|
|
2900
|
-
return issueCheck(
|
|
2970
|
+
return issueCheck(
|
|
2971
|
+
t("doctor.check.knowledge_test_index.name"),
|
|
2972
|
+
"error",
|
|
2973
|
+
"fixable_error",
|
|
2974
|
+
"knowledge_test_index_missing",
|
|
2975
|
+
index.error,
|
|
2976
|
+
t("doctor.check.knowledge_test_index.remediation.missing")
|
|
2977
|
+
);
|
|
2901
2978
|
}
|
|
2902
2979
|
if (!index.valid) {
|
|
2903
|
-
return issueCheck(
|
|
2980
|
+
return issueCheck(
|
|
2981
|
+
t("doctor.check.knowledge_test_index.name"),
|
|
2982
|
+
"error",
|
|
2983
|
+
"manual_error",
|
|
2984
|
+
"knowledge_test_index_invalid",
|
|
2985
|
+
index.error,
|
|
2986
|
+
t("doctor.check.knowledge_test_index.remediation.invalid")
|
|
2987
|
+
);
|
|
2904
2988
|
}
|
|
2905
2989
|
if (index.stale) {
|
|
2906
|
-
return issueCheck(
|
|
2990
|
+
return issueCheck(
|
|
2991
|
+
t("doctor.check.knowledge_test_index.name"),
|
|
2992
|
+
"error",
|
|
2993
|
+
"fixable_error",
|
|
2994
|
+
"knowledge_test_index_stale",
|
|
2995
|
+
t("doctor.check.knowledge_test_index.message.stale"),
|
|
2996
|
+
t("doctor.check.knowledge_test_index.remediation.stale")
|
|
2997
|
+
);
|
|
2907
2998
|
}
|
|
2908
|
-
return okCheck(
|
|
2999
|
+
return okCheck(
|
|
3000
|
+
t("doctor.check.knowledge_test_index.name"),
|
|
3001
|
+
t(
|
|
3002
|
+
`doctor.check.knowledge_test_index.ok.${index.linkCount === 1 ? "link_singular" : "link_plural"}.${index.orphanCount === 1 ? "orphan_singular" : "orphan_plural"}`,
|
|
3003
|
+
{ linkCount: String(index.linkCount), orphanCount: String(index.orphanCount) }
|
|
3004
|
+
)
|
|
3005
|
+
);
|
|
2909
3006
|
}
|
|
2910
|
-
function createEventLedgerCheck(ledger) {
|
|
3007
|
+
function createEventLedgerCheck(t, ledger) {
|
|
2911
3008
|
if (!ledger.exists) {
|
|
2912
|
-
return issueCheck(
|
|
3009
|
+
return issueCheck(
|
|
3010
|
+
t("doctor.check.event_ledger.name"),
|
|
3011
|
+
"error",
|
|
3012
|
+
"fixable_error",
|
|
3013
|
+
"event_ledger_missing",
|
|
3014
|
+
t("doctor.check.event_ledger.message.missing"),
|
|
3015
|
+
t("doctor.check.event_ledger.remediation.missing")
|
|
3016
|
+
);
|
|
2913
3017
|
}
|
|
2914
3018
|
if (!ledger.writable) {
|
|
2915
|
-
return issueCheck(
|
|
3019
|
+
return issueCheck(
|
|
3020
|
+
t("doctor.check.event_ledger.name"),
|
|
3021
|
+
"error",
|
|
3022
|
+
"manual_error",
|
|
3023
|
+
"event_ledger_not_writable",
|
|
3024
|
+
ledger.error ?? t("doctor.check.event_ledger.message.not_writable-default"),
|
|
3025
|
+
t("doctor.check.event_ledger.remediation.not_writable")
|
|
3026
|
+
);
|
|
2916
3027
|
}
|
|
2917
3028
|
if (!ledger.parseable) {
|
|
2918
|
-
return issueCheck(
|
|
3029
|
+
return issueCheck(
|
|
3030
|
+
t("doctor.check.event_ledger.name"),
|
|
3031
|
+
"error",
|
|
3032
|
+
"manual_error",
|
|
3033
|
+
"event_ledger_invalid",
|
|
3034
|
+
ledger.error ?? t("doctor.check.event_ledger.message.invalid-default"),
|
|
3035
|
+
t("doctor.check.event_ledger.remediation.invalid")
|
|
3036
|
+
);
|
|
2919
3037
|
}
|
|
2920
|
-
return okCheck("
|
|
3038
|
+
return okCheck(t("doctor.check.event_ledger.name"), t("doctor.check.event_ledger.ok"));
|
|
2921
3039
|
}
|
|
2922
|
-
function createMcpConfigInWrongFileCheck(inspection) {
|
|
3040
|
+
function createMcpConfigInWrongFileCheck(t, inspection) {
|
|
2923
3041
|
if (inspection.hasWrongEntry) {
|
|
2924
3042
|
return issueCheck(
|
|
2925
|
-
"
|
|
3043
|
+
t("doctor.check.mcp_config_in_wrong_file.name"),
|
|
2926
3044
|
"error",
|
|
2927
3045
|
"fixable_error",
|
|
2928
3046
|
"mcp_config_in_wrong_file",
|
|
2929
|
-
|
|
2930
|
-
"
|
|
3047
|
+
t("doctor.check.mcp_config_in_wrong_file.message"),
|
|
3048
|
+
t("doctor.check.mcp_config_in_wrong_file.remediation")
|
|
2931
3049
|
);
|
|
2932
3050
|
}
|
|
2933
|
-
return okCheck(
|
|
3051
|
+
return okCheck(
|
|
3052
|
+
t("doctor.check.mcp_config_in_wrong_file.name"),
|
|
3053
|
+
t("doctor.check.mcp_config_in_wrong_file.ok")
|
|
3054
|
+
);
|
|
2934
3055
|
}
|
|
2935
|
-
function createEventLedgerPartialWriteCheck(ledger) {
|
|
3056
|
+
function createEventLedgerPartialWriteCheck(t, ledger) {
|
|
2936
3057
|
if (!ledger.exists || !ledger.writable) {
|
|
2937
|
-
return okCheck(
|
|
3058
|
+
return okCheck(
|
|
3059
|
+
t("doctor.check.event_ledger_partial_write.name"),
|
|
3060
|
+
t("doctor.check.event_ledger_partial_write.ok.skipped")
|
|
3061
|
+
);
|
|
2938
3062
|
}
|
|
2939
3063
|
if (ledger.hasPartialWrite) {
|
|
2940
3064
|
return issueCheck(
|
|
2941
|
-
"
|
|
3065
|
+
t("doctor.check.event_ledger_partial_write.name"),
|
|
2942
3066
|
"error",
|
|
2943
3067
|
"fixable_error",
|
|
2944
3068
|
"event_ledger_partial_write",
|
|
2945
|
-
|
|
2946
|
-
|
|
3069
|
+
t("doctor.check.event_ledger_partial_write.message", {
|
|
3070
|
+
byteOffset: String(ledger.partialWriteByteOffset),
|
|
3071
|
+
byteLength: String(ledger.partialWriteByteLength)
|
|
3072
|
+
}),
|
|
3073
|
+
t("doctor.check.event_ledger_partial_write.remediation")
|
|
2947
3074
|
);
|
|
2948
3075
|
}
|
|
2949
|
-
return okCheck(
|
|
3076
|
+
return okCheck(
|
|
3077
|
+
t("doctor.check.event_ledger_partial_write.name"),
|
|
3078
|
+
t("doctor.check.event_ledger_partial_write.ok.clean")
|
|
3079
|
+
);
|
|
2950
3080
|
}
|
|
2951
3081
|
function okCheck(name, message) {
|
|
2952
3082
|
return { name, status: "ok", message };
|
|
@@ -2966,7 +3096,8 @@ function collectIssues(checks, kind) {
|
|
|
2966
3096
|
return checks.filter((check) => check.kind === kind).map((check) => ({
|
|
2967
3097
|
code: check.code ?? check.name,
|
|
2968
3098
|
name: check.name,
|
|
2969
|
-
message: check.message
|
|
3099
|
+
message: check.message,
|
|
3100
|
+
actionHint: check.actionHint
|
|
2970
3101
|
}));
|
|
2971
3102
|
}
|
|
2972
3103
|
function findIssue(issues, code) {
|
|
@@ -3053,18 +3184,24 @@ function collectMdFilesUnder(out, projectRoot, rootDir, relPrefix) {
|
|
|
3053
3184
|
}
|
|
3054
3185
|
}
|
|
3055
3186
|
}
|
|
3056
|
-
function createKnowledgeDirUnindexedCheck(inspection) {
|
|
3187
|
+
function createKnowledgeDirUnindexedCheck(t, inspection) {
|
|
3057
3188
|
if (inspection.unindexedFiles.length > 0) {
|
|
3189
|
+
const count = inspection.unindexedFiles.length;
|
|
3058
3190
|
return issueCheck(
|
|
3059
|
-
"
|
|
3191
|
+
t("doctor.check.knowledge_dir_unindexed.name"),
|
|
3060
3192
|
"error",
|
|
3061
3193
|
"fixable_error",
|
|
3062
3194
|
"knowledge_dir_unindexed",
|
|
3063
|
-
|
|
3064
|
-
|
|
3195
|
+
t(`doctor.check.knowledge_dir_unindexed.message.${count === 1 ? "singular" : "plural"}`, {
|
|
3196
|
+
count: String(count)
|
|
3197
|
+
}),
|
|
3198
|
+
t("doctor.check.knowledge_dir_unindexed.remediation")
|
|
3065
3199
|
);
|
|
3066
3200
|
}
|
|
3067
|
-
return okCheck(
|
|
3201
|
+
return okCheck(
|
|
3202
|
+
t("doctor.check.knowledge_dir_unindexed.name"),
|
|
3203
|
+
t("doctor.check.knowledge_dir_unindexed.ok")
|
|
3204
|
+
);
|
|
3068
3205
|
}
|
|
3069
3206
|
async function inspectStableIdCollisions(projectRoot) {
|
|
3070
3207
|
const found = [];
|
|
@@ -3147,7 +3284,7 @@ function inspectCounterDesync(meta) {
|
|
|
3147
3284
|
["guideline", "GLD"],
|
|
3148
3285
|
["pitfall", "PIT"],
|
|
3149
3286
|
["process", "PRO"]
|
|
3150
|
-
].find(([
|
|
3287
|
+
].find(([t]) => t === parsed.type)?.[1];
|
|
3151
3288
|
if (typeCode === void 0) {
|
|
3152
3289
|
continue;
|
|
3153
3290
|
}
|
|
@@ -3175,61 +3312,84 @@ function inspectCounterDesync(meta) {
|
|
|
3175
3312
|
correctedCounters: desyncs.length === 0 ? null : corrected
|
|
3176
3313
|
};
|
|
3177
3314
|
}
|
|
3178
|
-
function createCounterDesyncCheck(inspection) {
|
|
3315
|
+
function createCounterDesyncCheck(t, inspection) {
|
|
3179
3316
|
if (inspection.desyncs.length > 0) {
|
|
3180
3317
|
const first = inspection.desyncs[0];
|
|
3181
|
-
const
|
|
3318
|
+
const observedId = `K${first.layer === "KP" ? "P" : "T"}-${first.type}-${String(first.observed).padStart(4, "0")}`;
|
|
3319
|
+
const count = inspection.desyncs.length;
|
|
3182
3320
|
return issueCheck(
|
|
3183
|
-
"
|
|
3321
|
+
t("doctor.check.counter_desync.name"),
|
|
3184
3322
|
"error",
|
|
3185
3323
|
"fixable_error",
|
|
3186
3324
|
"counter_desync",
|
|
3187
|
-
|
|
3188
|
-
|
|
3325
|
+
t(`doctor.check.counter_desync.message.${count === 1 ? "singular" : "plural"}`, {
|
|
3326
|
+
count: String(count),
|
|
3327
|
+
counterPath: `counters.${first.layer}.${first.type}`,
|
|
3328
|
+
current: String(first.current),
|
|
3329
|
+
observedId
|
|
3330
|
+
}),
|
|
3331
|
+
t("doctor.check.counter_desync.remediation")
|
|
3189
3332
|
);
|
|
3190
3333
|
}
|
|
3191
|
-
return okCheck("
|
|
3334
|
+
return okCheck(t("doctor.check.counter_desync.name"), t("doctor.check.counter_desync.ok"));
|
|
3192
3335
|
}
|
|
3193
|
-
function createStableIdCollisionCheck(inspection) {
|
|
3336
|
+
function createStableIdCollisionCheck(t, inspection) {
|
|
3194
3337
|
if (inspection.collisions.length > 0) {
|
|
3195
3338
|
const first = inspection.collisions[0];
|
|
3196
|
-
const
|
|
3339
|
+
const count = inspection.collisions.length;
|
|
3197
3340
|
return issueCheck(
|
|
3198
|
-
"
|
|
3341
|
+
t("doctor.check.stable_id_collision.name"),
|
|
3199
3342
|
"warn",
|
|
3200
3343
|
"warning",
|
|
3201
3344
|
"stable_id_collision",
|
|
3202
|
-
|
|
3203
|
-
|
|
3345
|
+
t(`doctor.check.stable_id_collision.message.${count === 1 ? "singular" : "plural"}`, {
|
|
3346
|
+
count: String(count),
|
|
3347
|
+
stableId: first.stable_id,
|
|
3348
|
+
fileCount: String(first.files.length),
|
|
3349
|
+
files: first.files.join(", ")
|
|
3350
|
+
}),
|
|
3351
|
+
t("doctor.check.stable_id_collision.remediation")
|
|
3204
3352
|
);
|
|
3205
3353
|
}
|
|
3206
|
-
return okCheck("
|
|
3354
|
+
return okCheck(t("doctor.check.stable_id_collision.name"), t("doctor.check.stable_id_collision.ok"));
|
|
3207
3355
|
}
|
|
3208
|
-
function createMetaManuallyDivergedCheck(inspection) {
|
|
3356
|
+
function createMetaManuallyDivergedCheck(t, inspection) {
|
|
3209
3357
|
if (!inspection.readable) {
|
|
3210
|
-
return okCheck(
|
|
3358
|
+
return okCheck(
|
|
3359
|
+
t("doctor.check.meta_manually_diverged.name"),
|
|
3360
|
+
t("doctor.check.meta_manually_diverged.ok.unreadable")
|
|
3361
|
+
);
|
|
3211
3362
|
}
|
|
3212
3363
|
if (inspection.extraMetaEntries.length > 0) {
|
|
3364
|
+
const count = inspection.extraMetaEntries.length;
|
|
3213
3365
|
return issueCheck(
|
|
3214
|
-
"
|
|
3366
|
+
t("doctor.check.meta_manually_diverged.name"),
|
|
3215
3367
|
"warn",
|
|
3216
3368
|
"warning",
|
|
3217
3369
|
"meta_manually_diverged",
|
|
3218
|
-
`
|
|
3219
|
-
|
|
3370
|
+
t(`doctor.check.meta_manually_diverged.message.extra.${count === 1 ? "singular" : "plural"}`, {
|
|
3371
|
+
count: String(count)
|
|
3372
|
+
}),
|
|
3373
|
+
t("doctor.check.meta_manually_diverged.remediation.extra")
|
|
3220
3374
|
);
|
|
3221
3375
|
}
|
|
3222
3376
|
if (inspection.hashMismatchEntries.length > 0) {
|
|
3377
|
+
const count = inspection.hashMismatchEntries.length;
|
|
3223
3378
|
return issueCheck(
|
|
3224
|
-
"
|
|
3379
|
+
t("doctor.check.meta_manually_diverged.name"),
|
|
3225
3380
|
"warn",
|
|
3226
3381
|
"warning",
|
|
3227
3382
|
"meta_manually_diverged",
|
|
3228
|
-
`
|
|
3229
|
-
|
|
3383
|
+
t(`doctor.check.meta_manually_diverged.message.hash.${count === 1 ? "singular" : "plural"}`, {
|
|
3384
|
+
count: String(count)
|
|
3385
|
+
}),
|
|
3386
|
+
t("doctor.check.meta_manually_diverged.remediation.hash")
|
|
3230
3387
|
);
|
|
3231
3388
|
}
|
|
3232
|
-
return okCheck(
|
|
3389
|
+
return okCheck(
|
|
3390
|
+
t("doctor.check.meta_manually_diverged.name"),
|
|
3391
|
+
t("doctor.check.meta_manually_diverged.ok.consistent")
|
|
3392
|
+
);
|
|
3233
3393
|
}
|
|
3234
3394
|
function inspectPreexistingRootFiles(projectRoot) {
|
|
3235
3395
|
const candidates = ["CLAUDE.md", "AGENTS.md"];
|
|
@@ -3295,36 +3455,44 @@ async function inspectFilesystemEditFallback(projectRoot) {
|
|
|
3295
3455
|
}
|
|
3296
3456
|
return { synthesized: orphanIds.length, synthesizedStableIds: orphanIds };
|
|
3297
3457
|
}
|
|
3298
|
-
function createFilesystemEditFallbackCheck(inspection) {
|
|
3458
|
+
function createFilesystemEditFallbackCheck(t, inspection) {
|
|
3299
3459
|
if (inspection.synthesized === 0) {
|
|
3300
3460
|
return okCheck(
|
|
3301
|
-
"
|
|
3302
|
-
"
|
|
3461
|
+
t("doctor.check.filesystem_edit_fallback.name"),
|
|
3462
|
+
t("doctor.check.filesystem_edit_fallback.ok")
|
|
3303
3463
|
);
|
|
3304
3464
|
}
|
|
3305
3465
|
const sample = inspection.synthesizedStableIds.slice(0, 3).join(", ");
|
|
3306
3466
|
return {
|
|
3307
|
-
name: "
|
|
3467
|
+
name: t("doctor.check.filesystem_edit_fallback.name"),
|
|
3308
3468
|
status: "ok",
|
|
3309
3469
|
kind: "info",
|
|
3310
3470
|
code: "knowledge_promoted_synthesized",
|
|
3311
3471
|
fixable: false,
|
|
3312
|
-
message:
|
|
3313
|
-
|
|
3472
|
+
message: t(
|
|
3473
|
+
`doctor.check.filesystem_edit_fallback.message.synthesized.${inspection.synthesized === 1 ? "singular" : "plural"}`,
|
|
3474
|
+
{
|
|
3475
|
+
count: String(inspection.synthesized),
|
|
3476
|
+
sample,
|
|
3477
|
+
suffix: inspection.synthesizedStableIds.length > 3 ? ", ..." : "",
|
|
3478
|
+
reason: SYNTHESIZED_PROMOTED_REASON
|
|
3479
|
+
}
|
|
3480
|
+
),
|
|
3481
|
+
actionHint: t("doctor.check.filesystem_edit_fallback.remediation.synthesized")
|
|
3314
3482
|
};
|
|
3315
3483
|
}
|
|
3316
|
-
function createPreexistingRootFilesCheck(inspection) {
|
|
3484
|
+
function createPreexistingRootFilesCheck(t, inspection) {
|
|
3317
3485
|
if (inspection.detected.length === 0) {
|
|
3318
|
-
return okCheck("
|
|
3486
|
+
return okCheck(t("doctor.check.preexisting_root_files.name"), t("doctor.check.preexisting_root_files.ok"));
|
|
3319
3487
|
}
|
|
3320
3488
|
return {
|
|
3321
|
-
name: "
|
|
3489
|
+
name: t("doctor.check.preexisting_root_files.name"),
|
|
3322
3490
|
status: "ok",
|
|
3323
3491
|
kind: "info",
|
|
3324
3492
|
code: "preexisting_root_claude_md",
|
|
3325
3493
|
fixable: false,
|
|
3326
|
-
message:
|
|
3327
|
-
actionHint: "
|
|
3494
|
+
message: t("doctor.check.preexisting_root_files.message", { files: inspection.detected.join(", ") }),
|
|
3495
|
+
actionHint: t("doctor.check.preexisting_root_files.remediation")
|
|
3328
3496
|
};
|
|
3329
3497
|
}
|
|
3330
3498
|
async function buildLastConsumedIndex(projectRoot) {
|
|
@@ -3822,114 +3990,156 @@ function readUnderseedThresholdFromConfig(projectRoot) {
|
|
|
3822
3990
|
}
|
|
3823
3991
|
return DEFAULT_UNDERSEED_NODE_THRESHOLD;
|
|
3824
3992
|
}
|
|
3825
|
-
function createOrphanDemoteCheck(inspection) {
|
|
3993
|
+
function createOrphanDemoteCheck(t, inspection) {
|
|
3826
3994
|
if (inspection.candidates.length === 0) {
|
|
3827
3995
|
return okCheck(
|
|
3828
|
-
"
|
|
3829
|
-
"
|
|
3996
|
+
t("doctor.check.orphan_demote.name"),
|
|
3997
|
+
t("doctor.check.orphan_demote.ok")
|
|
3830
3998
|
);
|
|
3831
3999
|
}
|
|
3832
4000
|
const first = inspection.candidates[0];
|
|
3833
4001
|
const detail = `${first.stable_id} (${first.maturity}, ${first.age_days}d inactive at ${first.path})`;
|
|
4002
|
+
const count = inspection.candidates.length;
|
|
3834
4003
|
return issueCheck(
|
|
3835
|
-
"
|
|
4004
|
+
t("doctor.check.orphan_demote.name"),
|
|
3836
4005
|
"warn",
|
|
3837
4006
|
"warning",
|
|
3838
4007
|
"knowledge_orphan_demote_required",
|
|
3839
|
-
|
|
3840
|
-
|
|
4008
|
+
t(`doctor.check.orphan_demote.message.${count === 1 ? "singular" : "plural"}`, {
|
|
4009
|
+
count: String(count),
|
|
4010
|
+
stableDays: String(ORPHAN_DEMOTE_THRESHOLD_DAYS.stable),
|
|
4011
|
+
endorsedDays: String(ORPHAN_DEMOTE_THRESHOLD_DAYS.endorsed),
|
|
4012
|
+
draftDays: String(ORPHAN_DEMOTE_THRESHOLD_DAYS.draft),
|
|
4013
|
+
detail
|
|
4014
|
+
}),
|
|
4015
|
+
t("doctor.check.orphan_demote.remediation")
|
|
3841
4016
|
);
|
|
3842
4017
|
}
|
|
3843
|
-
function createStaleArchiveCheck(inspection) {
|
|
4018
|
+
function createStaleArchiveCheck(t, inspection) {
|
|
3844
4019
|
if (inspection.candidates.length === 0) {
|
|
3845
4020
|
return okCheck(
|
|
3846
|
-
"
|
|
3847
|
-
"
|
|
4021
|
+
t("doctor.check.stale_archive.name"),
|
|
4022
|
+
t("doctor.check.stale_archive.ok")
|
|
3848
4023
|
);
|
|
3849
4024
|
}
|
|
3850
4025
|
const first = inspection.candidates[0];
|
|
3851
4026
|
const detail = `${first.stable_id} (${first.age_days}d inactive at ${first.path}) \u2192 ${first.archive_path}`;
|
|
4027
|
+
const count = inspection.candidates.length;
|
|
3852
4028
|
return issueCheck(
|
|
3853
|
-
"
|
|
4029
|
+
t("doctor.check.stale_archive.name"),
|
|
3854
4030
|
"warn",
|
|
3855
4031
|
"warning",
|
|
3856
4032
|
"knowledge_stale_archive_required",
|
|
3857
|
-
|
|
3858
|
-
|
|
4033
|
+
t(`doctor.check.stale_archive.message.${count === 1 ? "singular" : "plural"}`, {
|
|
4034
|
+
count: String(count),
|
|
4035
|
+
additionalDays: String(STALE_ARCHIVE_ADDITIONAL_DAYS),
|
|
4036
|
+
detail
|
|
4037
|
+
}),
|
|
4038
|
+
t("doctor.check.stale_archive.remediation")
|
|
3859
4039
|
);
|
|
3860
4040
|
}
|
|
3861
|
-
function createPendingOverdueCheck(inspection) {
|
|
4041
|
+
function createPendingOverdueCheck(t, inspection) {
|
|
3862
4042
|
if (inspection.candidates.length === 0) {
|
|
3863
4043
|
return okCheck(
|
|
3864
|
-
"
|
|
3865
|
-
"
|
|
4044
|
+
t("doctor.check.pending_overdue.name"),
|
|
4045
|
+
t("doctor.check.pending_overdue.ok")
|
|
3866
4046
|
);
|
|
3867
4047
|
}
|
|
3868
4048
|
const first = inspection.candidates[0];
|
|
3869
4049
|
const detail = `${first.path} (${first.age_days}d old)`;
|
|
4050
|
+
const count = inspection.candidates.length;
|
|
3870
4051
|
return issueCheck(
|
|
3871
|
-
"
|
|
4052
|
+
t("doctor.check.pending_overdue.name"),
|
|
3872
4053
|
"warn",
|
|
3873
4054
|
"warning",
|
|
3874
4055
|
"knowledge_pending_overdue",
|
|
3875
|
-
|
|
3876
|
-
|
|
4056
|
+
t(`doctor.check.pending_overdue.message.${count === 1 ? "singular" : "plural"}`, {
|
|
4057
|
+
count: String(count),
|
|
4058
|
+
thresholdDays: String(PENDING_OVERDUE_THRESHOLD_DAYS),
|
|
4059
|
+
detail
|
|
4060
|
+
}),
|
|
4061
|
+
t("doctor.check.pending_overdue.remediation")
|
|
3877
4062
|
);
|
|
3878
4063
|
}
|
|
3879
|
-
function createUnderseededCheck(inspection) {
|
|
4064
|
+
function createUnderseededCheck(t, inspection) {
|
|
3880
4065
|
if (!inspection.underseeded) {
|
|
3881
4066
|
return okCheck(
|
|
3882
|
-
"
|
|
3883
|
-
|
|
4067
|
+
t("doctor.check.underseeded.name"),
|
|
4068
|
+
t("doctor.check.underseeded.ok", {
|
|
4069
|
+
count: String(inspection.node_count),
|
|
4070
|
+
threshold: String(inspection.threshold)
|
|
4071
|
+
})
|
|
3884
4072
|
);
|
|
3885
4073
|
}
|
|
3886
4074
|
return issueCheck(
|
|
3887
|
-
"
|
|
4075
|
+
t("doctor.check.underseeded.name"),
|
|
3888
4076
|
"ok",
|
|
3889
4077
|
"info",
|
|
3890
4078
|
"knowledge_underseeded",
|
|
3891
|
-
`
|
|
3892
|
-
|
|
4079
|
+
t(`doctor.check.underseeded.message.${inspection.node_count === 1 ? "singular" : "plural"}`, {
|
|
4080
|
+
count: String(inspection.node_count),
|
|
4081
|
+
threshold: String(inspection.threshold)
|
|
4082
|
+
}),
|
|
4083
|
+
t("doctor.check.underseeded.remediation")
|
|
3893
4084
|
);
|
|
3894
4085
|
}
|
|
3895
|
-
function createSessionHintsStaleCheck(inspection) {
|
|
4086
|
+
function createSessionHintsStaleCheck(t, inspection) {
|
|
3896
4087
|
if (inspection.candidates.length === 0) {
|
|
3897
4088
|
return okCheck(
|
|
3898
|
-
"
|
|
3899
|
-
|
|
4089
|
+
t("doctor.check.session_hints_stale.name"),
|
|
4090
|
+
t("doctor.check.session_hints_stale.ok", {
|
|
4091
|
+
days: String(SESSION_HINTS_STALE_DAYS)
|
|
4092
|
+
})
|
|
3900
4093
|
);
|
|
3901
4094
|
}
|
|
3902
4095
|
const first = inspection.candidates[0];
|
|
3903
4096
|
const detail = `${first.path} (${first.age_days}d old)`;
|
|
4097
|
+
const count = inspection.candidates.length;
|
|
3904
4098
|
return issueCheck(
|
|
3905
|
-
"
|
|
4099
|
+
t("doctor.check.session_hints_stale.name"),
|
|
3906
4100
|
"ok",
|
|
3907
4101
|
"info",
|
|
3908
4102
|
"knowledge_session_hints_stale",
|
|
3909
|
-
|
|
3910
|
-
|
|
4103
|
+
t(`doctor.check.session_hints_stale.message.${count === 1 ? "singular" : "plural"}`, {
|
|
4104
|
+
count: String(count),
|
|
4105
|
+
days: String(SESSION_HINTS_STALE_DAYS),
|
|
4106
|
+
detail
|
|
4107
|
+
}),
|
|
4108
|
+
t("doctor.check.session_hints_stale.remediation")
|
|
3911
4109
|
);
|
|
3912
4110
|
}
|
|
3913
|
-
function createStaleServeLockCheck(inspection) {
|
|
4111
|
+
function createStaleServeLockCheck(t, inspection) {
|
|
3914
4112
|
if (!inspection.present) {
|
|
3915
|
-
return okCheck(
|
|
4113
|
+
return okCheck(
|
|
4114
|
+
t("doctor.check.stale_serve_lock.name"),
|
|
4115
|
+
t("doctor.check.stale_serve_lock.ok.no_lock")
|
|
4116
|
+
);
|
|
3916
4117
|
}
|
|
3917
4118
|
if (inspection.pidAlive) {
|
|
3918
4119
|
return okCheck(
|
|
3919
|
-
"
|
|
3920
|
-
|
|
4120
|
+
t("doctor.check.stale_serve_lock.name"),
|
|
4121
|
+
t("doctor.check.stale_serve_lock.ok.live_pid", {
|
|
4122
|
+
pid: String(inspection.pid)
|
|
4123
|
+
})
|
|
3921
4124
|
);
|
|
3922
4125
|
}
|
|
3923
4126
|
const days = Math.floor(inspection.ageMs / MS_PER_DAY);
|
|
3924
4127
|
const hours = Math.floor(inspection.ageMs / (60 * 60 * 1e3));
|
|
3925
|
-
const acquiredAgo = days >= 1 ?
|
|
4128
|
+
const acquiredAgo = days >= 1 ? t(`doctor.check.stale_serve_lock.age.day.${days === 1 ? "singular" : "plural"}`, {
|
|
4129
|
+
count: String(days)
|
|
4130
|
+
}) : t(`doctor.check.stale_serve_lock.age.hour.${hours === 1 ? "singular" : "plural"}`, {
|
|
4131
|
+
count: String(hours)
|
|
4132
|
+
});
|
|
3926
4133
|
return issueCheck(
|
|
3927
|
-
"
|
|
4134
|
+
t("doctor.check.stale_serve_lock.name"),
|
|
3928
4135
|
"ok",
|
|
3929
4136
|
"info",
|
|
3930
4137
|
"stale_serve_lock",
|
|
3931
|
-
|
|
3932
|
-
|
|
4138
|
+
t("doctor.check.stale_serve_lock.message.dead_pid", {
|
|
4139
|
+
pid: String(inspection.pid),
|
|
4140
|
+
acquiredAgo
|
|
4141
|
+
}),
|
|
4142
|
+
t("doctor.check.stale_serve_lock.remediation.dead_pid")
|
|
3933
4143
|
);
|
|
3934
4144
|
}
|
|
3935
4145
|
function extractKnowledgeFrontmatterRelevanceScope(source) {
|
|
@@ -4128,64 +4338,81 @@ function readRecentGitTouchedPaths(projectRoot, windowDays) {
|
|
|
4128
4338
|
}
|
|
4129
4339
|
return Array.from(set);
|
|
4130
4340
|
}
|
|
4131
|
-
function createNarrowNoPathsCheck(inspection) {
|
|
4341
|
+
function createNarrowNoPathsCheck(t, inspection) {
|
|
4132
4342
|
if (inspection.candidates.length === 0) {
|
|
4133
4343
|
return okCheck(
|
|
4134
|
-
"
|
|
4135
|
-
"
|
|
4344
|
+
t("doctor.check.narrow_no_paths.name"),
|
|
4345
|
+
t("doctor.check.narrow_no_paths.ok")
|
|
4136
4346
|
);
|
|
4137
4347
|
}
|
|
4138
4348
|
const first = inspection.candidates[0];
|
|
4139
4349
|
const detail = `${first.stable_id} (${first.path})`;
|
|
4350
|
+
const count = inspection.candidates.length;
|
|
4140
4351
|
return issueCheck(
|
|
4141
|
-
"
|
|
4352
|
+
t("doctor.check.narrow_no_paths.name"),
|
|
4142
4353
|
"warn",
|
|
4143
4354
|
"warning",
|
|
4144
4355
|
"knowledge_narrow_no_paths",
|
|
4145
|
-
|
|
4146
|
-
|
|
4356
|
+
t(`doctor.check.narrow_no_paths.message.${count === 1 ? "singular" : "plural"}`, {
|
|
4357
|
+
count: String(count),
|
|
4358
|
+
detail
|
|
4359
|
+
}),
|
|
4360
|
+
t("doctor.check.narrow_no_paths.remediation")
|
|
4147
4361
|
);
|
|
4148
4362
|
}
|
|
4149
|
-
function createRelevancePathsDanglingCheck(inspection) {
|
|
4363
|
+
function createRelevancePathsDanglingCheck(t, inspection) {
|
|
4150
4364
|
if (inspection.entries.length === 0) {
|
|
4151
4365
|
return okCheck(
|
|
4152
|
-
"
|
|
4153
|
-
"
|
|
4366
|
+
t("doctor.check.relevance_paths_dangling.name"),
|
|
4367
|
+
t("doctor.check.relevance_paths_dangling.ok")
|
|
4154
4368
|
);
|
|
4155
4369
|
}
|
|
4156
4370
|
const first = inspection.entries[0];
|
|
4157
4371
|
const detail = `${first.stable_id} at ${first.path} \u2192 \`${first.dangling_glob}\` (0 matches)`;
|
|
4372
|
+
const count = inspection.entries.length;
|
|
4158
4373
|
return issueCheck(
|
|
4159
|
-
"
|
|
4374
|
+
t("doctor.check.relevance_paths_dangling.name"),
|
|
4160
4375
|
"warn",
|
|
4161
4376
|
"warning",
|
|
4162
4377
|
"knowledge_relevance_paths_dangling",
|
|
4163
|
-
|
|
4164
|
-
|
|
4378
|
+
t(`doctor.check.relevance_paths_dangling.message.${count === 1 ? "singular" : "plural"}`, {
|
|
4379
|
+
count: String(count),
|
|
4380
|
+
detail
|
|
4381
|
+
}),
|
|
4382
|
+
t("doctor.check.relevance_paths_dangling.remediation")
|
|
4165
4383
|
);
|
|
4166
4384
|
}
|
|
4167
|
-
function createRelevancePathsDriftCheck(inspection) {
|
|
4385
|
+
function createRelevancePathsDriftCheck(t, inspection) {
|
|
4168
4386
|
if (!inspection.git_available) {
|
|
4169
4387
|
return okCheck(
|
|
4170
|
-
"
|
|
4171
|
-
|
|
4388
|
+
t("doctor.check.relevance_paths_drift.name"),
|
|
4389
|
+
t("doctor.check.relevance_paths_drift.ok.skipped", {
|
|
4390
|
+
windowDays: String(RELEVANCE_PATHS_DRIFT_WINDOW_DAYS)
|
|
4391
|
+
})
|
|
4172
4392
|
);
|
|
4173
4393
|
}
|
|
4174
4394
|
if (inspection.candidates.length === 0) {
|
|
4175
4395
|
return okCheck(
|
|
4176
|
-
"
|
|
4177
|
-
|
|
4396
|
+
t("doctor.check.relevance_paths_drift.name"),
|
|
4397
|
+
t("doctor.check.relevance_paths_drift.ok.fresh", {
|
|
4398
|
+
windowDays: String(RELEVANCE_PATHS_DRIFT_WINDOW_DAYS)
|
|
4399
|
+
})
|
|
4178
4400
|
);
|
|
4179
4401
|
}
|
|
4180
4402
|
const first = inspection.candidates[0];
|
|
4181
4403
|
const detail = `${first.stable_id} at ${first.path} (globs: ${first.globs.join(", ")})`;
|
|
4404
|
+
const count = inspection.candidates.length;
|
|
4182
4405
|
return issueCheck(
|
|
4183
|
-
"
|
|
4406
|
+
t("doctor.check.relevance_paths_drift.name"),
|
|
4184
4407
|
"ok",
|
|
4185
4408
|
"info",
|
|
4186
4409
|
"knowledge_relevance_paths_drift",
|
|
4187
|
-
|
|
4188
|
-
|
|
4410
|
+
t(`doctor.check.relevance_paths_drift.message.${count === 1 ? "singular" : "plural"}`, {
|
|
4411
|
+
count: String(count),
|
|
4412
|
+
windowDays: String(RELEVANCE_PATHS_DRIFT_WINDOW_DAYS),
|
|
4413
|
+
detail
|
|
4414
|
+
}),
|
|
4415
|
+
t("doctor.check.relevance_paths_drift.remediation")
|
|
4189
4416
|
);
|
|
4190
4417
|
}
|
|
4191
4418
|
function inspectRelevanceFieldsMissing(projectRoot) {
|
|
@@ -4328,11 +4555,11 @@ async function applyRelevanceFieldsMissing(candidate) {
|
|
|
4328
4555
|
};
|
|
4329
4556
|
}
|
|
4330
4557
|
}
|
|
4331
|
-
function createRelevanceFieldsMissingCheck(inspection) {
|
|
4558
|
+
function createRelevanceFieldsMissingCheck(t, inspection) {
|
|
4332
4559
|
if (inspection.candidates.length === 0) {
|
|
4333
4560
|
return okCheck(
|
|
4334
|
-
"
|
|
4335
|
-
"
|
|
4561
|
+
t("doctor.check.relevance_fields_missing.name"),
|
|
4562
|
+
t("doctor.check.relevance_fields_missing.ok")
|
|
4336
4563
|
);
|
|
4337
4564
|
}
|
|
4338
4565
|
const first = inspection.candidates[0];
|
|
@@ -4340,13 +4567,17 @@ function createRelevanceFieldsMissingCheck(inspection) {
|
|
|
4340
4567
|
if (first.missing_scope) missingParts.push("relevance_scope");
|
|
4341
4568
|
if (first.missing_paths) missingParts.push("relevance_paths");
|
|
4342
4569
|
const detail = `${first.pending_path} (missing: ${missingParts.join(", ")})`;
|
|
4570
|
+
const count = inspection.candidates.length;
|
|
4343
4571
|
return issueCheck(
|
|
4344
|
-
"
|
|
4572
|
+
t("doctor.check.relevance_fields_missing.name"),
|
|
4345
4573
|
"ok",
|
|
4346
4574
|
"info",
|
|
4347
4575
|
"knowledge_relevance_fields_missing",
|
|
4348
|
-
|
|
4349
|
-
|
|
4576
|
+
t(`doctor.check.relevance_fields_missing.message.${count === 1 ? "singular" : "plural"}`, {
|
|
4577
|
+
count: String(count),
|
|
4578
|
+
detail
|
|
4579
|
+
}),
|
|
4580
|
+
t("doctor.check.relevance_fields_missing.remediation")
|
|
4350
4581
|
);
|
|
4351
4582
|
}
|
|
4352
4583
|
var SKILL_MD_FRONTMATTER_ROOTS = [".claude/skills", ".codex/skills"];
|
|
@@ -4417,23 +4648,26 @@ function extractSkillFrontmatterLines(raw) {
|
|
|
4417
4648
|
}
|
|
4418
4649
|
return null;
|
|
4419
4650
|
}
|
|
4420
|
-
function createSkillMdYamlInvalidCheck(inspection) {
|
|
4651
|
+
function createSkillMdYamlInvalidCheck(t, inspection) {
|
|
4421
4652
|
if (inspection.candidates.length === 0) {
|
|
4422
4653
|
return okCheck(
|
|
4423
|
-
"
|
|
4424
|
-
"
|
|
4654
|
+
t("doctor.check.skill_md_yaml_invalid.name"),
|
|
4655
|
+
t("doctor.check.skill_md_yaml_invalid.ok")
|
|
4425
4656
|
);
|
|
4426
4657
|
}
|
|
4427
4658
|
const first = inspection.candidates[0];
|
|
4428
4659
|
const detail = `${first.path}:${first.line} (key \`${first.key}\` value contains an unquoted ': ' \u2014 preview: \`${first.preview}\`)`;
|
|
4429
4660
|
const plural = inspection.candidates.length === 1;
|
|
4430
4661
|
return issueCheck(
|
|
4431
|
-
"
|
|
4662
|
+
t("doctor.check.skill_md_yaml_invalid.name"),
|
|
4432
4663
|
"warn",
|
|
4433
4664
|
"warning",
|
|
4434
4665
|
"skill_md_yaml_invalid",
|
|
4435
|
-
|
|
4436
|
-
|
|
4666
|
+
t(`doctor.check.skill_md_yaml_invalid.message.${plural ? "singular" : "plural"}`, {
|
|
4667
|
+
count: String(inspection.candidates.length),
|
|
4668
|
+
detail
|
|
4669
|
+
}),
|
|
4670
|
+
t("doctor.check.skill_md_yaml_invalid.remediation")
|
|
4437
4671
|
);
|
|
4438
4672
|
}
|
|
4439
4673
|
var KNOWLEDGE_CANONICAL_TYPE_DIRS_FOR_ONBOARD = [
|
|
@@ -4528,55 +4762,83 @@ function readFrontmatterScalar(content, key) {
|
|
|
4528
4762
|
}
|
|
4529
4763
|
return void 0;
|
|
4530
4764
|
}
|
|
4531
|
-
function createOnboardCoverageCheck(inspection) {
|
|
4765
|
+
function createOnboardCoverageCheck(t, inspection) {
|
|
4532
4766
|
const filledCount = ONBOARD_SLOT_NAMES.filter(
|
|
4533
4767
|
(slot) => inspection.filled[slot].length > 0
|
|
4534
4768
|
).length;
|
|
4535
4769
|
if (inspection.missing.length === 0) {
|
|
4536
4770
|
return okCheck(
|
|
4537
|
-
"
|
|
4538
|
-
|
|
4771
|
+
t("doctor.check.onboard_coverage.name"),
|
|
4772
|
+
t("doctor.check.onboard_coverage.ok.complete", {
|
|
4773
|
+
filledCount: String(filledCount),
|
|
4774
|
+
total: String(ONBOARD_SLOT_TOTAL),
|
|
4775
|
+
optedOutCount: String(inspection.opted_out.length)
|
|
4776
|
+
})
|
|
4539
4777
|
);
|
|
4540
4778
|
}
|
|
4541
4779
|
return issueCheck(
|
|
4542
|
-
"
|
|
4780
|
+
t("doctor.check.onboard_coverage.name"),
|
|
4543
4781
|
"ok",
|
|
4544
4782
|
"info",
|
|
4545
4783
|
"onboard_coverage_incomplete",
|
|
4546
|
-
|
|
4547
|
-
|
|
4784
|
+
t("doctor.check.onboard_coverage.message.incomplete", {
|
|
4785
|
+
missingSlots: inspection.missing.join(", "),
|
|
4786
|
+
filledCount: String(filledCount),
|
|
4787
|
+
total: String(ONBOARD_SLOT_TOTAL),
|
|
4788
|
+
optedOutCount: String(inspection.opted_out.length)
|
|
4789
|
+
}),
|
|
4790
|
+
t("doctor.check.onboard_coverage.remediation.incomplete")
|
|
4548
4791
|
);
|
|
4549
4792
|
}
|
|
4550
|
-
function createNarrowTooFewCheck(inspection) {
|
|
4793
|
+
function createNarrowTooFewCheck(t, inspection) {
|
|
4551
4794
|
const { structural_flagged, telemetry_flagged } = inspection;
|
|
4552
4795
|
if (!structural_flagged && !telemetry_flagged) {
|
|
4553
4796
|
const ratioPct = (inspection.narrow_ratio * 100).toFixed(0);
|
|
4554
|
-
const teleNote = inspection.telemetry_skipped ? "
|
|
4797
|
+
const teleNote = inspection.telemetry_skipped ? t("doctor.check.narrow_too_few.message.telemetry_skipped") : t("doctor.check.narrow_too_few.message.telemetry_window", {
|
|
4798
|
+
silencePct: (inspection.silence_rate * 100).toFixed(0),
|
|
4799
|
+
windowDays: String(SILENCE_WINDOW_DAYS)
|
|
4800
|
+
});
|
|
4555
4801
|
return okCheck(
|
|
4556
|
-
"
|
|
4557
|
-
|
|
4802
|
+
t("doctor.check.narrow_too_few.name"),
|
|
4803
|
+
t("doctor.check.narrow_too_few.ok", {
|
|
4804
|
+
ratioPct,
|
|
4805
|
+
narrowCount: String(inspection.narrow_with_paths_count),
|
|
4806
|
+
totalCount: String(inspection.total_canonical_entries),
|
|
4807
|
+
teleNote
|
|
4808
|
+
})
|
|
4558
4809
|
);
|
|
4559
4810
|
}
|
|
4560
4811
|
const parts = [];
|
|
4561
4812
|
if (structural_flagged) {
|
|
4562
4813
|
const ratioPct = (inspection.narrow_ratio * 100).toFixed(0);
|
|
4563
4814
|
parts.push(
|
|
4564
|
-
|
|
4815
|
+
t("doctor.check.narrow_too_few.message.structural", {
|
|
4816
|
+
ratioPct,
|
|
4817
|
+
narrowCount: String(inspection.narrow_with_paths_count),
|
|
4818
|
+
totalCount: String(inspection.total_canonical_entries),
|
|
4819
|
+
thresholdPct: (NARROW_RATIO_THRESHOLD * 100).toFixed(0)
|
|
4820
|
+
})
|
|
4565
4821
|
);
|
|
4566
4822
|
}
|
|
4567
4823
|
if (telemetry_flagged) {
|
|
4568
4824
|
const silencePct = (inspection.silence_rate * 100).toFixed(0);
|
|
4569
4825
|
parts.push(
|
|
4570
|
-
|
|
4826
|
+
t("doctor.check.narrow_too_few.message.telemetry", {
|
|
4827
|
+
silencePct,
|
|
4828
|
+
silenceFires: String(inspection.silence_fires_in_window),
|
|
4829
|
+
totalFires: String(inspection.total_edit_fires_in_window),
|
|
4830
|
+
windowDays: String(SILENCE_WINDOW_DAYS),
|
|
4831
|
+
thresholdPct: (SILENCE_RATE_THRESHOLD * 100).toFixed(0)
|
|
4832
|
+
})
|
|
4571
4833
|
);
|
|
4572
4834
|
}
|
|
4573
4835
|
return issueCheck(
|
|
4574
|
-
"
|
|
4836
|
+
t("doctor.check.narrow_too_few.name"),
|
|
4575
4837
|
"ok",
|
|
4576
4838
|
"info",
|
|
4577
4839
|
"knowledge_narrow_too_few",
|
|
4578
|
-
|
|
4579
|
-
"
|
|
4840
|
+
t("doctor.check.narrow_too_few.message.summary", { parts: parts.join("; ") }),
|
|
4841
|
+
t("doctor.check.narrow_too_few.remediation")
|
|
4580
4842
|
);
|
|
4581
4843
|
}
|
|
4582
4844
|
function resolvePersonalKnowledgeRoot() {
|
|
@@ -4713,58 +4975,70 @@ function inspectIndexDrift(projectRoot, meta) {
|
|
|
4713
4975
|
);
|
|
4714
4976
|
return { drifts };
|
|
4715
4977
|
}
|
|
4716
|
-
function createStableIdDuplicateCheck(inspection) {
|
|
4978
|
+
function createStableIdDuplicateCheck(t, inspection) {
|
|
4717
4979
|
if (inspection.duplicates.length === 0) {
|
|
4718
4980
|
return okCheck(
|
|
4719
|
-
"
|
|
4720
|
-
"
|
|
4981
|
+
t("doctor.check.stable_id_duplicate.name"),
|
|
4982
|
+
t("doctor.check.stable_id_duplicate.ok")
|
|
4721
4983
|
);
|
|
4722
4984
|
}
|
|
4723
4985
|
const first = inspection.duplicates[0];
|
|
4724
4986
|
const detail = `${first.stable_id} appears in ${first.paths.length} files: ${first.paths.join(", ")}`;
|
|
4987
|
+
const count = inspection.duplicates.length;
|
|
4725
4988
|
return issueCheck(
|
|
4726
|
-
"
|
|
4989
|
+
t("doctor.check.stable_id_duplicate.name"),
|
|
4727
4990
|
"error",
|
|
4728
4991
|
"manual_error",
|
|
4729
4992
|
"knowledge_stable_id_duplicate",
|
|
4730
|
-
|
|
4731
|
-
|
|
4993
|
+
t(`doctor.check.stable_id_duplicate.message.${count === 1 ? "singular" : "plural"}`, {
|
|
4994
|
+
count: String(count),
|
|
4995
|
+
detail
|
|
4996
|
+
}),
|
|
4997
|
+
t("doctor.check.stable_id_duplicate.remediation")
|
|
4732
4998
|
);
|
|
4733
4999
|
}
|
|
4734
|
-
function createLayerMismatchCheck(inspection) {
|
|
5000
|
+
function createLayerMismatchCheck(t, inspection) {
|
|
4735
5001
|
if (inspection.mismatches.length === 0) {
|
|
4736
5002
|
return okCheck(
|
|
4737
|
-
"
|
|
4738
|
-
"
|
|
5003
|
+
t("doctor.check.layer_mismatch.name"),
|
|
5004
|
+
t("doctor.check.layer_mismatch.ok")
|
|
4739
5005
|
);
|
|
4740
5006
|
}
|
|
4741
5007
|
const first = inspection.mismatches[0];
|
|
4742
5008
|
const detail = `${first.stable_id} at ${first.path} (located in ${first.located_in}, expected ${first.expected_layer})`;
|
|
5009
|
+
const count = inspection.mismatches.length;
|
|
4743
5010
|
return issueCheck(
|
|
4744
|
-
"
|
|
5011
|
+
t("doctor.check.layer_mismatch.name"),
|
|
4745
5012
|
"error",
|
|
4746
5013
|
"manual_error",
|
|
4747
5014
|
"knowledge_layer_mismatch",
|
|
4748
|
-
|
|
4749
|
-
|
|
5015
|
+
t(`doctor.check.layer_mismatch.message.${count === 1 ? "singular" : "plural"}`, {
|
|
5016
|
+
count: String(count),
|
|
5017
|
+
detail
|
|
5018
|
+
}),
|
|
5019
|
+
t("doctor.check.layer_mismatch.remediation")
|
|
4750
5020
|
);
|
|
4751
5021
|
}
|
|
4752
|
-
function createIndexDriftCheck(inspection) {
|
|
5022
|
+
function createIndexDriftCheck(t, inspection) {
|
|
4753
5023
|
if (inspection.drifts.length === 0) {
|
|
4754
5024
|
return okCheck(
|
|
4755
|
-
"
|
|
4756
|
-
"
|
|
5025
|
+
t("doctor.check.index_drift.name"),
|
|
5026
|
+
t("doctor.check.index_drift.ok")
|
|
4757
5027
|
);
|
|
4758
5028
|
}
|
|
4759
5029
|
const first = inspection.drifts[0];
|
|
4760
5030
|
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})`;
|
|
5031
|
+
const count = inspection.drifts.length;
|
|
4761
5032
|
return issueCheck(
|
|
4762
|
-
"
|
|
5033
|
+
t("doctor.check.index_drift.name"),
|
|
4763
5034
|
"error",
|
|
4764
5035
|
"fixable_error",
|
|
4765
5036
|
"knowledge_index_drift",
|
|
4766
|
-
|
|
4767
|
-
|
|
5037
|
+
t(`doctor.check.index_drift.message.${count === 1 ? "singular" : "plural"}`, {
|
|
5038
|
+
count: String(count),
|
|
5039
|
+
detail
|
|
5040
|
+
}),
|
|
5041
|
+
t("doctor.check.index_drift.remediation")
|
|
4768
5042
|
);
|
|
4769
5043
|
}
|
|
4770
5044
|
async function migrateBootstrapMarkers(projectRoot) {
|
|
@@ -5137,7 +5411,7 @@ async function runDoctorCiteCoverage(projectRoot, options) {
|
|
|
5137
5411
|
break;
|
|
5138
5412
|
}
|
|
5139
5413
|
}
|
|
5140
|
-
const filteredTurns = options.client === "all" ? assistantTurns : assistantTurns.filter((
|
|
5414
|
+
const filteredTurns = options.client === "all" ? assistantTurns : assistantTurns.filter((t) => t.client === options.client);
|
|
5141
5415
|
let clientSessionIds = null;
|
|
5142
5416
|
if (options.client !== "all") {
|
|
5143
5417
|
clientSessionIds = /* @__PURE__ */ new Set();
|