@fenglimg/fabric-server 2.0.0-rc.23 → 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.
|
@@ -428,6 +428,27 @@ import {
|
|
|
428
428
|
parseKnowledgeId
|
|
429
429
|
} from "@fenglimg/fabric-shared";
|
|
430
430
|
import { atomicWriteText as atomicWriteText3 } from "@fenglimg/fabric-shared/node/atomic-write";
|
|
431
|
+
async function loadKbIdTypeMap(projectRootInput) {
|
|
432
|
+
const projectRoot = normalizeProjectRoot(projectRootInput);
|
|
433
|
+
const metaPath = join4(projectRoot, ".fabric", "agents.meta.json");
|
|
434
|
+
const meta = await readExistingMeta(metaPath);
|
|
435
|
+
const map = /* @__PURE__ */ new Map();
|
|
436
|
+
if (meta === void 0) {
|
|
437
|
+
return map;
|
|
438
|
+
}
|
|
439
|
+
for (const node of Object.values(meta.nodes)) {
|
|
440
|
+
const stableId = node.stable_id;
|
|
441
|
+
if (stableId === void 0 || !isKnowledgeStableId(stableId)) {
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
const knowledgeType = node.description?.knowledge_type;
|
|
445
|
+
if (knowledgeType === void 0) {
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
map.set(stableId, knowledgeType);
|
|
449
|
+
}
|
|
450
|
+
return map;
|
|
451
|
+
}
|
|
431
452
|
async function buildKnowledgeMeta(projectRootInput) {
|
|
432
453
|
const projectRoot = normalizeProjectRoot(projectRootInput);
|
|
433
454
|
assertExistingDirectory(projectRoot);
|
|
@@ -1452,10 +1473,9 @@ async function reconcileKnowledge(projectRoot, opts) {
|
|
|
1452
1473
|
// src/services/serve-lock.ts
|
|
1453
1474
|
import fs from "fs";
|
|
1454
1475
|
import path from "path";
|
|
1455
|
-
import { createTranslator,
|
|
1476
|
+
import { createTranslator, resolveFabricLocale } from "@fenglimg/fabric-shared";
|
|
1456
1477
|
import { IOFabricError as IOFabricError2 } from "@fenglimg/fabric-shared/errors";
|
|
1457
1478
|
var LOCK_FILENAME = ".serve.lock";
|
|
1458
|
-
var t = createTranslator(detectNodeLocale());
|
|
1459
1479
|
var ServeLockHeldError = class extends IOFabricError2 {
|
|
1460
1480
|
code = "SERVE_LOCK_HELD";
|
|
1461
1481
|
httpStatus = 423;
|
|
@@ -1483,6 +1503,7 @@ function acquireLock(projectRoot, opts) {
|
|
|
1483
1503
|
} catch {
|
|
1484
1504
|
}
|
|
1485
1505
|
if (state && state.pid && state.pid !== process.pid && isAlive(state.pid) && !opts?.force) {
|
|
1506
|
+
const t = createTranslator(resolveFabricLocale(projectRoot));
|
|
1486
1507
|
throw new ServeLockHeldError(
|
|
1487
1508
|
`serve lock held by live PID ${state.pid}`,
|
|
1488
1509
|
{
|
|
@@ -1533,6 +1554,7 @@ function checkLockOrThrow(projectRoot, opts) {
|
|
|
1533
1554
|
return;
|
|
1534
1555
|
}
|
|
1535
1556
|
if (opts?.force) return;
|
|
1557
|
+
const t = createTranslator(resolveFabricLocale(projectRoot));
|
|
1536
1558
|
throw new ServeLockHeldError(
|
|
1537
1559
|
`serve lock held by live PID ${state.pid}`,
|
|
1538
1560
|
{
|
|
@@ -1553,6 +1575,7 @@ import { minimatch } from "minimatch";
|
|
|
1553
1575
|
import {
|
|
1554
1576
|
agentsMetaSchema as agentsMetaSchema4,
|
|
1555
1577
|
AgentsMetaCountersSchema,
|
|
1578
|
+
createTranslator as createTranslator2,
|
|
1556
1579
|
forensicReportSchema,
|
|
1557
1580
|
parseKnowledgeId as parseKnowledgeId2,
|
|
1558
1581
|
knowledgeTestIndexSchema as knowledgeTestIndexSchema2,
|
|
@@ -1562,7 +1585,8 @@ import {
|
|
|
1562
1585
|
BOOTSTRAP_MARKER_END,
|
|
1563
1586
|
BOOTSTRAP_REGEX,
|
|
1564
1587
|
ONBOARD_SLOT_NAMES,
|
|
1565
|
-
ONBOARD_SLOT_TOTAL
|
|
1588
|
+
ONBOARD_SLOT_TOTAL,
|
|
1589
|
+
resolveFabricLocale as resolveFabricLocale2
|
|
1566
1590
|
} from "@fenglimg/fabric-shared";
|
|
1567
1591
|
import { detectFramework } from "@fenglimg/fabric-shared/node";
|
|
1568
1592
|
import { atomicWriteJson as atomicWriteJson2, atomicWriteText as atomicWriteText4 } from "@fenglimg/fabric-shared/node/atomic-write";
|
|
@@ -1648,6 +1672,7 @@ var TARGET_FILE_PATHS = [
|
|
|
1648
1672
|
];
|
|
1649
1673
|
async function runDoctorReport(target) {
|
|
1650
1674
|
const projectRoot = normalizeTarget(target);
|
|
1675
|
+
const t = createTranslator2(resolveFabricLocale2(projectRoot));
|
|
1651
1676
|
const framework = detectFramework(projectRoot);
|
|
1652
1677
|
const entryPoints = collectEntryPoints(projectRoot);
|
|
1653
1678
|
const [
|
|
@@ -1701,91 +1726,91 @@ async function runDoctorReport(target) {
|
|
|
1701
1726
|
const skillMdYamlInvalid = inspectSkillMdYamlInvalid(projectRoot);
|
|
1702
1727
|
const onboardCoverage = inspectOnboardCoverage(projectRoot);
|
|
1703
1728
|
const checks = [
|
|
1704
|
-
createBootstrapAnchorCheck(bootstrapAnchor),
|
|
1729
|
+
createBootstrapAnchorCheck(t, bootstrapAnchor),
|
|
1705
1730
|
// v2.0.0-rc.19 TASK-004: bootstrap marker migration check sits adjacent to
|
|
1706
1731
|
// the anchor check — both are bootstrap-file invariants. fixable_error
|
|
1707
1732
|
// when any of the four target paths still carries the legacy marker.
|
|
1708
|
-
createBootstrapMarkerMigrationCheck(bootstrapMarkerMigration),
|
|
1733
|
+
createBootstrapMarkerMigrationCheck(t, bootstrapMarkerMigration),
|
|
1709
1734
|
// v2.0.0-rc.19 TASK-005: L1 + L2 byte-level drift detection sit immediately
|
|
1710
1735
|
// after the marker migration check. Order: anchor existence → migration →
|
|
1711
1736
|
// L1 (canonical ↔ snapshot) → L2 (snapshot+rules ↔ three-end blocks).
|
|
1712
|
-
createL1BootstrapSnapshotDriftCheck(l1BootstrapSnapshotDrift),
|
|
1713
|
-
createL2ManagedBlockDriftCheck(l2ManagedBlockDrift),
|
|
1714
|
-
createKnowledgeDirMissingCheck(knowledgeDirMissing),
|
|
1737
|
+
createL1BootstrapSnapshotDriftCheck(t, l1BootstrapSnapshotDrift),
|
|
1738
|
+
createL2ManagedBlockDriftCheck(t, l2ManagedBlockDrift),
|
|
1739
|
+
createKnowledgeDirMissingCheck(t, knowledgeDirMissing),
|
|
1715
1740
|
// v2.0.0-rc.22 TASK-006: baseline filename format. Sits adjacent to
|
|
1716
1741
|
// knowledge_dir_missing — both are knowledge-layout invariants. manual_error
|
|
1717
1742
|
// kind; resolution is manual file deletion (rc.23 TASK-012 (F8a) removed
|
|
1718
1743
|
// the baseline-emit pipeline, so no auto-fix exists).
|
|
1719
|
-
createBaselineFilenameFormatCheck(baselineFilenameFormat),
|
|
1720
|
-
createForensicCheck(forensic, framework.kind, entryPoints.length),
|
|
1744
|
+
createBaselineFilenameFormatCheck(t, baselineFilenameFormat),
|
|
1745
|
+
createForensicCheck(t, forensic, framework.kind, entryPoints.length),
|
|
1721
1746
|
// v2.0: removed `createInitContextCheck` — `.fabric/init-context.json`
|
|
1722
1747
|
// is owned by the AI-side client init skill, not by `fabric install` CLI.
|
|
1723
1748
|
// The file's absence is a legitimate post-init state when the skill has
|
|
1724
1749
|
// not yet run, so flagging it as a doctor manual_error misrepresents
|
|
1725
1750
|
// ownership.
|
|
1726
|
-
createMetaCheck(meta),
|
|
1727
|
-
createRuleContentRefCheck(meta),
|
|
1751
|
+
createMetaCheck(t, meta),
|
|
1752
|
+
createRuleContentRefCheck(t, meta),
|
|
1728
1753
|
// v2.0 / rc.2: `createRuleSectionsCheck` removed — it parsed v1.x
|
|
1729
1754
|
// [MANDATORY_INJECTION] sections out of legacy rule files, a structural
|
|
1730
1755
|
// concept that has no v2 equivalent. rc.4 will introduce a dedicated v2
|
|
1731
1756
|
// lint suite for the new knowledge frontmatter contract.
|
|
1732
|
-
createKnowledgeTestIndexCheck(knowledgeTestIndex),
|
|
1733
|
-
createEventLedgerCheck(eventLedger),
|
|
1734
|
-
createEventLedgerPartialWriteCheck(eventLedger),
|
|
1735
|
-
createMcpConfigInWrongFileCheck(mcpConfigInWrongFile),
|
|
1736
|
-
createMetaManuallyDivergedCheck(metaManuallyDiverged),
|
|
1737
|
-
createKnowledgeDirUnindexedCheck(knowledgeDirUnindexed),
|
|
1738
|
-
createStableIdCollisionCheck(stableIdCollision),
|
|
1739
|
-
createCounterDesyncCheck(counterDesync),
|
|
1740
|
-
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),
|
|
1741
1766
|
// rc.4 TASK-001: read-side lint checks #16-18. Findings only — mutation
|
|
1742
1767
|
// + event emission lands in TASK-003 behind --apply-lint.
|
|
1743
|
-
createOrphanDemoteCheck(orphanDemote),
|
|
1744
|
-
createStaleArchiveCheck(staleArchive),
|
|
1745
|
-
createPendingOverdueCheck(pendingOverdue),
|
|
1768
|
+
createOrphanDemoteCheck(t, orphanDemote),
|
|
1769
|
+
createStaleArchiveCheck(t, staleArchive),
|
|
1770
|
+
createPendingOverdueCheck(t, pendingOverdue),
|
|
1746
1771
|
// rc.4 TASK-002: read-side integrity checks #19-21. Stable_id duplicate
|
|
1747
1772
|
// runs first in this trio — it is the most critical integrity break and
|
|
1748
1773
|
// surfaces ahead of layer-mismatch / index-drift in the report so a
|
|
1749
1774
|
// human operator triages the collision before reasoning about counter
|
|
1750
1775
|
// state. Index drift is the only fixable_error of the three; stable_id
|
|
1751
1776
|
// duplicate and layer mismatch require manual triage (rename / move).
|
|
1752
|
-
createStableIdDuplicateCheck(stableIdDuplicate),
|
|
1753
|
-
createLayerMismatchCheck(layerMismatch),
|
|
1754
|
-
createIndexDriftCheck(indexDrift),
|
|
1777
|
+
createStableIdDuplicateCheck(t, stableIdDuplicate),
|
|
1778
|
+
createLayerMismatchCheck(t, layerMismatch),
|
|
1779
|
+
createIndexDriftCheck(t, indexDrift),
|
|
1755
1780
|
// rc.5 TASK-010: read-side underseeded-corpus check (#22). Info kind —
|
|
1756
1781
|
// does not bump report status. Recommends running the fabric-import skill
|
|
1757
1782
|
// to backfill knowledge when the corpus is below the threshold floor.
|
|
1758
|
-
createUnderseededCheck(underseeded),
|
|
1783
|
+
createUnderseededCheck(t, underseeded),
|
|
1759
1784
|
// rc.5 TASK-013 (C4): relevance_paths hygiene checks #23/#24/#25.
|
|
1760
1785
|
// All three are flag-only in rc.5 (no apply-lint mutations).
|
|
1761
1786
|
// #23 narrow_no_paths — warning kind (silent recall risk)
|
|
1762
1787
|
// #24 relevance_paths_dangling — warning kind (glob → zero matches)
|
|
1763
1788
|
// #25 relevance_paths_drift — info kind (git-log heuristic; noisy)
|
|
1764
|
-
createNarrowNoPathsCheck(narrowNoPaths),
|
|
1765
|
-
createRelevancePathsDanglingCheck(relevancePathsDangling),
|
|
1766
|
-
createRelevancePathsDriftCheck(relevancePathsDrift),
|
|
1789
|
+
createNarrowNoPathsCheck(t, narrowNoPaths),
|
|
1790
|
+
createRelevancePathsDanglingCheck(t, relevancePathsDangling),
|
|
1791
|
+
createRelevancePathsDriftCheck(t, relevancePathsDrift),
|
|
1767
1792
|
// rc.6 TASK-023 (E6): narrow_too_few (lint #26). Info kind; both arms
|
|
1768
1793
|
// (structural + telemetry) recommend the same fabric-import action.
|
|
1769
|
-
createNarrowTooFewCheck(narrowTooFew),
|
|
1794
|
+
createNarrowTooFewCheck(t, narrowTooFew),
|
|
1770
1795
|
// rc.6 TASK-021 (E3): session-hints cache hygiene (lint #27). Info kind.
|
|
1771
|
-
createSessionHintsStaleCheck(sessionHintsStale),
|
|
1796
|
+
createSessionHintsStaleCheck(t, sessionHintsStale),
|
|
1772
1797
|
// rc.23 TASK-010 (e): stale .fabric/.serve.lock advisory. Info kind —
|
|
1773
1798
|
// does not bump report status. `--fix` unlinks the corpse and emits
|
|
1774
1799
|
// `serve_lock_cleared`.
|
|
1775
|
-
createStaleServeLockCheck(staleServeLock),
|
|
1800
|
+
createStaleServeLockCheck(t, staleServeLock),
|
|
1776
1801
|
// v2.0.0-rc.9 TASK-003 (A3): relevance fields back-fill (lint #28).
|
|
1777
1802
|
// Info kind — applies to pending entries only; canonical entries get
|
|
1778
1803
|
// the fields written verbatim by fab_review.approve/modify.
|
|
1779
|
-
createRelevanceFieldsMissingCheck(relevanceFieldsMissing),
|
|
1804
|
+
createRelevanceFieldsMissingCheck(t, relevanceFieldsMissing),
|
|
1780
1805
|
// rc.12 lint #29: skill_md_yaml_invalid. Warning kind — surfaces
|
|
1781
1806
|
// SKILL.md frontmatter that Codex CLI silently drops at load.
|
|
1782
|
-
createSkillMdYamlInvalidCheck(skillMdYamlInvalid),
|
|
1807
|
+
createSkillMdYamlInvalidCheck(t, skillMdYamlInvalid),
|
|
1783
1808
|
// v2.0.0-rc.23 TASK-014 (F8c): Onboard coverage advisory. Info kind.
|
|
1784
1809
|
// Surfaces uncovered S5 onboard slots and recommends /fabric-archive
|
|
1785
1810
|
// first-run phase. Sits adjacent to Skill markdown YAML — both are
|
|
1786
1811
|
// Skill-adjacent advisories. --fix never mutates onboard state.
|
|
1787
|
-
createOnboardCoverageCheck(onboardCoverage),
|
|
1788
|
-
createPreexistingRootFilesCheck(preexistingRootFiles)
|
|
1812
|
+
createOnboardCoverageCheck(t, onboardCoverage),
|
|
1813
|
+
createPreexistingRootFilesCheck(t, preexistingRootFiles)
|
|
1789
1814
|
// v2.0 / rc.2: `createLegacyClientPathCheck` removed. The schema now
|
|
1790
1815
|
// rejects retired clientPaths keys (windsurf/rooCode/geminiCLI) at Zod
|
|
1791
1816
|
// parse time, so the soft-deprecation warn-and-fix path no longer has a
|
|
@@ -2547,21 +2572,25 @@ async function inspectBootstrapMarkerMigration(target) {
|
|
|
2547
2572
|
}
|
|
2548
2573
|
return { filesNeedingMigration };
|
|
2549
2574
|
}
|
|
2550
|
-
function createBootstrapMarkerMigrationCheck(inspection) {
|
|
2575
|
+
function createBootstrapMarkerMigrationCheck(t, inspection) {
|
|
2551
2576
|
if (inspection.filesNeedingMigration.length === 0) {
|
|
2552
2577
|
return okCheck(
|
|
2553
|
-
"
|
|
2554
|
-
"
|
|
2578
|
+
t("doctor.check.bootstrap_marker_migration.name"),
|
|
2579
|
+
t("doctor.check.bootstrap_marker_migration.ok")
|
|
2555
2580
|
);
|
|
2556
2581
|
}
|
|
2557
2582
|
const list = inspection.filesNeedingMigration.join(", ");
|
|
2583
|
+
const count = inspection.filesNeedingMigration.length;
|
|
2558
2584
|
return issueCheck(
|
|
2559
|
-
"
|
|
2585
|
+
t("doctor.check.bootstrap_marker_migration.name"),
|
|
2560
2586
|
"error",
|
|
2561
2587
|
"fixable_error",
|
|
2562
2588
|
"bootstrap_marker_migration_required",
|
|
2563
|
-
|
|
2564
|
-
|
|
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")
|
|
2565
2594
|
);
|
|
2566
2595
|
}
|
|
2567
2596
|
async function inspectL1BootstrapSnapshotDrift(target) {
|
|
@@ -2580,20 +2609,20 @@ async function inspectL1BootstrapSnapshotDrift(target) {
|
|
|
2580
2609
|
}
|
|
2581
2610
|
return { status: "drift", canonical: BOOTSTRAP_CANONICAL, onDisk };
|
|
2582
2611
|
}
|
|
2583
|
-
function createL1BootstrapSnapshotDriftCheck(inspection) {
|
|
2612
|
+
function createL1BootstrapSnapshotDriftCheck(t, inspection) {
|
|
2584
2613
|
if (inspection.status === "drift") {
|
|
2585
2614
|
return issueCheck(
|
|
2586
|
-
"
|
|
2615
|
+
t("doctor.check.bootstrap_snapshot_drift.name"),
|
|
2587
2616
|
"error",
|
|
2588
2617
|
"fixable_error",
|
|
2589
2618
|
"bootstrap_snapshot_drift",
|
|
2590
|
-
".
|
|
2591
|
-
"
|
|
2619
|
+
t("doctor.check.bootstrap_snapshot_drift.message.drift"),
|
|
2620
|
+
t("doctor.check.bootstrap_snapshot_drift.remediation.drift")
|
|
2592
2621
|
);
|
|
2593
2622
|
}
|
|
2594
2623
|
return okCheck(
|
|
2595
|
-
"
|
|
2596
|
-
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")
|
|
2597
2626
|
);
|
|
2598
2627
|
}
|
|
2599
2628
|
async function inspectL2ManagedBlockDrift(target) {
|
|
@@ -2685,39 +2714,46 @@ ${projectRules}`;
|
|
|
2685
2714
|
}
|
|
2686
2715
|
return { status: "drift", drifted };
|
|
2687
2716
|
}
|
|
2688
|
-
function createL2ManagedBlockDriftCheck(inspection) {
|
|
2717
|
+
function createL2ManagedBlockDriftCheck(t, inspection) {
|
|
2689
2718
|
if (inspection.status === "drift") {
|
|
2690
2719
|
const list = inspection.drifted.map((d) => d.path).join(", ");
|
|
2720
|
+
const count = inspection.drifted.length;
|
|
2691
2721
|
return issueCheck(
|
|
2692
|
-
"
|
|
2722
|
+
t("doctor.check.managed_block_drift.name"),
|
|
2693
2723
|
"error",
|
|
2694
2724
|
"fixable_error",
|
|
2695
2725
|
"managed_block_drift",
|
|
2696
|
-
|
|
2697
|
-
|
|
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")
|
|
2698
2731
|
);
|
|
2699
2732
|
}
|
|
2700
2733
|
return okCheck(
|
|
2701
|
-
"
|
|
2702
|
-
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")
|
|
2703
2736
|
);
|
|
2704
2737
|
}
|
|
2705
|
-
function createBootstrapAnchorCheck(inspection) {
|
|
2738
|
+
function createBootstrapAnchorCheck(t, inspection) {
|
|
2706
2739
|
if (!inspection.hasAgentsMd && !inspection.hasClaudeMd) {
|
|
2707
2740
|
return issueCheck(
|
|
2708
|
-
"
|
|
2741
|
+
t("doctor.check.bootstrap_anchor.name"),
|
|
2709
2742
|
"error",
|
|
2710
2743
|
"fixable_error",
|
|
2711
2744
|
"bootstrap_anchor_missing",
|
|
2712
|
-
"
|
|
2713
|
-
"
|
|
2745
|
+
t("doctor.check.bootstrap_anchor.message.missing"),
|
|
2746
|
+
t("doctor.check.bootstrap_anchor.remediation.missing")
|
|
2714
2747
|
);
|
|
2715
2748
|
}
|
|
2716
2749
|
const present = [
|
|
2717
2750
|
inspection.hasAgentsMd ? "AGENTS.md" : null,
|
|
2718
2751
|
inspection.hasClaudeMd ? "CLAUDE.md" : null
|
|
2719
2752
|
].filter((entry) => entry !== null).join(", ");
|
|
2720
|
-
return okCheck(
|
|
2753
|
+
return okCheck(
|
|
2754
|
+
t("doctor.check.bootstrap_anchor.name"),
|
|
2755
|
+
t("doctor.check.bootstrap_anchor.ok", { present })
|
|
2756
|
+
);
|
|
2721
2757
|
}
|
|
2722
2758
|
function inspectKnowledgeDirMissing(projectRoot) {
|
|
2723
2759
|
const knowledgeRoot = join6(projectRoot, ".fabric", "knowledge");
|
|
@@ -2778,154 +2814,269 @@ function inspectBaselineFilenameFormat(projectRoot) {
|
|
|
2778
2814
|
offenders.sort((a, b) => a.path.localeCompare(b.path));
|
|
2779
2815
|
return { offenders };
|
|
2780
2816
|
}
|
|
2781
|
-
function createBaselineFilenameFormatCheck(inspection) {
|
|
2817
|
+
function createBaselineFilenameFormatCheck(t, inspection) {
|
|
2782
2818
|
if (inspection.offenders.length === 0) {
|
|
2783
2819
|
return okCheck(
|
|
2784
|
-
"
|
|
2785
|
-
"
|
|
2820
|
+
t("doctor.check.baseline_filename_format.name"),
|
|
2821
|
+
t("doctor.check.baseline_filename_format.ok")
|
|
2786
2822
|
);
|
|
2787
2823
|
}
|
|
2788
2824
|
const first = inspection.offenders[0];
|
|
2789
2825
|
const detail = `${first.stable_id} at ${first.path}`;
|
|
2826
|
+
const count = inspection.offenders.length;
|
|
2790
2827
|
return issueCheck(
|
|
2791
|
-
"
|
|
2828
|
+
t("doctor.check.baseline_filename_format.name"),
|
|
2792
2829
|
"error",
|
|
2793
2830
|
"manual_error",
|
|
2794
2831
|
"lint-baseline-filename-format",
|
|
2795
|
-
|
|
2796
|
-
|
|
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")
|
|
2797
2837
|
);
|
|
2798
2838
|
}
|
|
2799
|
-
function createKnowledgeDirMissingCheck(inspection) {
|
|
2839
|
+
function createKnowledgeDirMissingCheck(t, inspection) {
|
|
2800
2840
|
if (inspection.missingSubdirs.length > 0) {
|
|
2801
2841
|
const list = inspection.missingSubdirs.join(", ");
|
|
2842
|
+
const count = inspection.missingSubdirs.length;
|
|
2802
2843
|
return issueCheck(
|
|
2803
|
-
"
|
|
2844
|
+
t("doctor.check.knowledge_dir_missing.name"),
|
|
2804
2845
|
"error",
|
|
2805
2846
|
"fixable_error",
|
|
2806
2847
|
"knowledge_dir_missing",
|
|
2807
|
-
|
|
2808
|
-
|
|
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")
|
|
2809
2853
|
);
|
|
2810
2854
|
}
|
|
2811
2855
|
return okCheck(
|
|
2812
|
-
"
|
|
2813
|
-
|
|
2856
|
+
t("doctor.check.knowledge_dir_missing.name"),
|
|
2857
|
+
t("doctor.check.knowledge_dir_missing.ok", { count: String(KNOWLEDGE_SUBDIRS3.length) })
|
|
2814
2858
|
);
|
|
2815
2859
|
}
|
|
2816
|
-
function createForensicCheck(forensic, frameworkKind, entryPointCount) {
|
|
2860
|
+
function createForensicCheck(t, forensic, frameworkKind, entryPointCount) {
|
|
2817
2861
|
if (!forensic.present) {
|
|
2818
2862
|
return issueCheck(
|
|
2819
|
-
"
|
|
2863
|
+
t("doctor.check.forensic.name"),
|
|
2820
2864
|
"error",
|
|
2821
2865
|
"manual_error",
|
|
2822
2866
|
"forensic_missing",
|
|
2823
|
-
|
|
2824
|
-
|
|
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")
|
|
2825
2873
|
);
|
|
2826
2874
|
}
|
|
2827
2875
|
if (!forensic.valid) {
|
|
2828
|
-
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
|
+
);
|
|
2829
2884
|
}
|
|
2830
|
-
return okCheck(
|
|
2885
|
+
return okCheck(
|
|
2886
|
+
t("doctor.check.forensic.name"),
|
|
2887
|
+
t("doctor.check.forensic.ok", { frameworkKind: forensic.report?.framework.kind ?? "unknown" })
|
|
2888
|
+
);
|
|
2831
2889
|
}
|
|
2832
|
-
function createMetaCheck(meta) {
|
|
2890
|
+
function createMetaCheck(t, meta) {
|
|
2833
2891
|
if (!meta.present) {
|
|
2834
|
-
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
|
+
);
|
|
2835
2900
|
}
|
|
2836
2901
|
if (!meta.valid) {
|
|
2837
|
-
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
|
+
);
|
|
2838
2910
|
}
|
|
2839
2911
|
if (meta.stale) {
|
|
2840
2912
|
return issueCheck(
|
|
2841
|
-
"
|
|
2913
|
+
t("doctor.check.agents_meta.name"),
|
|
2842
2914
|
"warn",
|
|
2843
2915
|
"warning",
|
|
2844
2916
|
"agents_meta_stale",
|
|
2845
|
-
|
|
2846
|
-
|
|
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")
|
|
2847
2922
|
);
|
|
2848
2923
|
}
|
|
2849
|
-
return okCheck(
|
|
2924
|
+
return okCheck(
|
|
2925
|
+
t("doctor.check.agents_meta.name"),
|
|
2926
|
+
t("doctor.check.agents_meta.ok", { revision: meta.revision })
|
|
2927
|
+
);
|
|
2850
2928
|
}
|
|
2851
|
-
function createRuleContentRefCheck(meta) {
|
|
2929
|
+
function createRuleContentRefCheck(t, meta) {
|
|
2852
2930
|
if (!meta.valid) {
|
|
2853
|
-
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
|
+
);
|
|
2854
2939
|
}
|
|
2855
2940
|
if (meta.invalidContentRefs.length > 0) {
|
|
2941
|
+
const count = meta.invalidContentRefs.length;
|
|
2856
2942
|
return issueCheck(
|
|
2857
|
-
"
|
|
2943
|
+
t("doctor.check.rule_content_refs.name"),
|
|
2858
2944
|
"error",
|
|
2859
2945
|
"manual_error",
|
|
2860
2946
|
"content_ref_outside_rules",
|
|
2861
|
-
|
|
2862
|
-
|
|
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")
|
|
2863
2951
|
);
|
|
2864
2952
|
}
|
|
2865
2953
|
if (meta.missingContentRefs.length > 0) {
|
|
2954
|
+
const count = meta.missingContentRefs.length;
|
|
2866
2955
|
return issueCheck(
|
|
2867
|
-
"
|
|
2956
|
+
t("doctor.check.rule_content_refs.name"),
|
|
2868
2957
|
"error",
|
|
2869
2958
|
"fixable_error",
|
|
2870
2959
|
"content_ref_missing",
|
|
2871
|
-
|
|
2872
|
-
|
|
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")
|
|
2873
2964
|
);
|
|
2874
2965
|
}
|
|
2875
|
-
return okCheck("
|
|
2966
|
+
return okCheck(t("doctor.check.rule_content_refs.name"), t("doctor.check.rule_content_refs.ok"));
|
|
2876
2967
|
}
|
|
2877
|
-
function createKnowledgeTestIndexCheck(index) {
|
|
2968
|
+
function createKnowledgeTestIndexCheck(t, index) {
|
|
2878
2969
|
if (!index.present) {
|
|
2879
|
-
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
|
+
);
|
|
2880
2978
|
}
|
|
2881
2979
|
if (!index.valid) {
|
|
2882
|
-
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
|
+
);
|
|
2883
2988
|
}
|
|
2884
2989
|
if (index.stale) {
|
|
2885
|
-
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
|
+
);
|
|
2886
2998
|
}
|
|
2887
|
-
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
|
+
);
|
|
2888
3006
|
}
|
|
2889
|
-
function createEventLedgerCheck(ledger) {
|
|
3007
|
+
function createEventLedgerCheck(t, ledger) {
|
|
2890
3008
|
if (!ledger.exists) {
|
|
2891
|
-
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
|
+
);
|
|
2892
3017
|
}
|
|
2893
3018
|
if (!ledger.writable) {
|
|
2894
|
-
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
|
+
);
|
|
2895
3027
|
}
|
|
2896
3028
|
if (!ledger.parseable) {
|
|
2897
|
-
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
|
+
);
|
|
2898
3037
|
}
|
|
2899
|
-
return okCheck("
|
|
3038
|
+
return okCheck(t("doctor.check.event_ledger.name"), t("doctor.check.event_ledger.ok"));
|
|
2900
3039
|
}
|
|
2901
|
-
function createMcpConfigInWrongFileCheck(inspection) {
|
|
3040
|
+
function createMcpConfigInWrongFileCheck(t, inspection) {
|
|
2902
3041
|
if (inspection.hasWrongEntry) {
|
|
2903
3042
|
return issueCheck(
|
|
2904
|
-
"
|
|
3043
|
+
t("doctor.check.mcp_config_in_wrong_file.name"),
|
|
2905
3044
|
"error",
|
|
2906
3045
|
"fixable_error",
|
|
2907
3046
|
"mcp_config_in_wrong_file",
|
|
2908
|
-
|
|
2909
|
-
"
|
|
3047
|
+
t("doctor.check.mcp_config_in_wrong_file.message"),
|
|
3048
|
+
t("doctor.check.mcp_config_in_wrong_file.remediation")
|
|
2910
3049
|
);
|
|
2911
3050
|
}
|
|
2912
|
-
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
|
+
);
|
|
2913
3055
|
}
|
|
2914
|
-
function createEventLedgerPartialWriteCheck(ledger) {
|
|
3056
|
+
function createEventLedgerPartialWriteCheck(t, ledger) {
|
|
2915
3057
|
if (!ledger.exists || !ledger.writable) {
|
|
2916
|
-
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
|
+
);
|
|
2917
3062
|
}
|
|
2918
3063
|
if (ledger.hasPartialWrite) {
|
|
2919
3064
|
return issueCheck(
|
|
2920
|
-
"
|
|
3065
|
+
t("doctor.check.event_ledger_partial_write.name"),
|
|
2921
3066
|
"error",
|
|
2922
3067
|
"fixable_error",
|
|
2923
3068
|
"event_ledger_partial_write",
|
|
2924
|
-
|
|
2925
|
-
|
|
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")
|
|
2926
3074
|
);
|
|
2927
3075
|
}
|
|
2928
|
-
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
|
+
);
|
|
2929
3080
|
}
|
|
2930
3081
|
function okCheck(name, message) {
|
|
2931
3082
|
return { name, status: "ok", message };
|
|
@@ -2945,7 +3096,8 @@ function collectIssues(checks, kind) {
|
|
|
2945
3096
|
return checks.filter((check) => check.kind === kind).map((check) => ({
|
|
2946
3097
|
code: check.code ?? check.name,
|
|
2947
3098
|
name: check.name,
|
|
2948
|
-
message: check.message
|
|
3099
|
+
message: check.message,
|
|
3100
|
+
actionHint: check.actionHint
|
|
2949
3101
|
}));
|
|
2950
3102
|
}
|
|
2951
3103
|
function findIssue(issues, code) {
|
|
@@ -3032,18 +3184,24 @@ function collectMdFilesUnder(out, projectRoot, rootDir, relPrefix) {
|
|
|
3032
3184
|
}
|
|
3033
3185
|
}
|
|
3034
3186
|
}
|
|
3035
|
-
function createKnowledgeDirUnindexedCheck(inspection) {
|
|
3187
|
+
function createKnowledgeDirUnindexedCheck(t, inspection) {
|
|
3036
3188
|
if (inspection.unindexedFiles.length > 0) {
|
|
3189
|
+
const count = inspection.unindexedFiles.length;
|
|
3037
3190
|
return issueCheck(
|
|
3038
|
-
"
|
|
3191
|
+
t("doctor.check.knowledge_dir_unindexed.name"),
|
|
3039
3192
|
"error",
|
|
3040
3193
|
"fixable_error",
|
|
3041
3194
|
"knowledge_dir_unindexed",
|
|
3042
|
-
|
|
3043
|
-
|
|
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")
|
|
3044
3199
|
);
|
|
3045
3200
|
}
|
|
3046
|
-
return okCheck(
|
|
3201
|
+
return okCheck(
|
|
3202
|
+
t("doctor.check.knowledge_dir_unindexed.name"),
|
|
3203
|
+
t("doctor.check.knowledge_dir_unindexed.ok")
|
|
3204
|
+
);
|
|
3047
3205
|
}
|
|
3048
3206
|
async function inspectStableIdCollisions(projectRoot) {
|
|
3049
3207
|
const found = [];
|
|
@@ -3126,7 +3284,7 @@ function inspectCounterDesync(meta) {
|
|
|
3126
3284
|
["guideline", "GLD"],
|
|
3127
3285
|
["pitfall", "PIT"],
|
|
3128
3286
|
["process", "PRO"]
|
|
3129
|
-
].find(([
|
|
3287
|
+
].find(([t]) => t === parsed.type)?.[1];
|
|
3130
3288
|
if (typeCode === void 0) {
|
|
3131
3289
|
continue;
|
|
3132
3290
|
}
|
|
@@ -3154,61 +3312,84 @@ function inspectCounterDesync(meta) {
|
|
|
3154
3312
|
correctedCounters: desyncs.length === 0 ? null : corrected
|
|
3155
3313
|
};
|
|
3156
3314
|
}
|
|
3157
|
-
function createCounterDesyncCheck(inspection) {
|
|
3315
|
+
function createCounterDesyncCheck(t, inspection) {
|
|
3158
3316
|
if (inspection.desyncs.length > 0) {
|
|
3159
3317
|
const first = inspection.desyncs[0];
|
|
3160
|
-
const
|
|
3318
|
+
const observedId = `K${first.layer === "KP" ? "P" : "T"}-${first.type}-${String(first.observed).padStart(4, "0")}`;
|
|
3319
|
+
const count = inspection.desyncs.length;
|
|
3161
3320
|
return issueCheck(
|
|
3162
|
-
"
|
|
3321
|
+
t("doctor.check.counter_desync.name"),
|
|
3163
3322
|
"error",
|
|
3164
3323
|
"fixable_error",
|
|
3165
3324
|
"counter_desync",
|
|
3166
|
-
|
|
3167
|
-
|
|
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")
|
|
3168
3332
|
);
|
|
3169
3333
|
}
|
|
3170
|
-
return okCheck("
|
|
3334
|
+
return okCheck(t("doctor.check.counter_desync.name"), t("doctor.check.counter_desync.ok"));
|
|
3171
3335
|
}
|
|
3172
|
-
function createStableIdCollisionCheck(inspection) {
|
|
3336
|
+
function createStableIdCollisionCheck(t, inspection) {
|
|
3173
3337
|
if (inspection.collisions.length > 0) {
|
|
3174
3338
|
const first = inspection.collisions[0];
|
|
3175
|
-
const
|
|
3339
|
+
const count = inspection.collisions.length;
|
|
3176
3340
|
return issueCheck(
|
|
3177
|
-
"
|
|
3341
|
+
t("doctor.check.stable_id_collision.name"),
|
|
3178
3342
|
"warn",
|
|
3179
3343
|
"warning",
|
|
3180
3344
|
"stable_id_collision",
|
|
3181
|
-
|
|
3182
|
-
|
|
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")
|
|
3183
3352
|
);
|
|
3184
3353
|
}
|
|
3185
|
-
return okCheck("
|
|
3354
|
+
return okCheck(t("doctor.check.stable_id_collision.name"), t("doctor.check.stable_id_collision.ok"));
|
|
3186
3355
|
}
|
|
3187
|
-
function createMetaManuallyDivergedCheck(inspection) {
|
|
3356
|
+
function createMetaManuallyDivergedCheck(t, inspection) {
|
|
3188
3357
|
if (!inspection.readable) {
|
|
3189
|
-
return okCheck(
|
|
3358
|
+
return okCheck(
|
|
3359
|
+
t("doctor.check.meta_manually_diverged.name"),
|
|
3360
|
+
t("doctor.check.meta_manually_diverged.ok.unreadable")
|
|
3361
|
+
);
|
|
3190
3362
|
}
|
|
3191
3363
|
if (inspection.extraMetaEntries.length > 0) {
|
|
3364
|
+
const count = inspection.extraMetaEntries.length;
|
|
3192
3365
|
return issueCheck(
|
|
3193
|
-
"
|
|
3366
|
+
t("doctor.check.meta_manually_diverged.name"),
|
|
3194
3367
|
"warn",
|
|
3195
3368
|
"warning",
|
|
3196
3369
|
"meta_manually_diverged",
|
|
3197
|
-
`
|
|
3198
|
-
|
|
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")
|
|
3199
3374
|
);
|
|
3200
3375
|
}
|
|
3201
3376
|
if (inspection.hashMismatchEntries.length > 0) {
|
|
3377
|
+
const count = inspection.hashMismatchEntries.length;
|
|
3202
3378
|
return issueCheck(
|
|
3203
|
-
"
|
|
3379
|
+
t("doctor.check.meta_manually_diverged.name"),
|
|
3204
3380
|
"warn",
|
|
3205
3381
|
"warning",
|
|
3206
3382
|
"meta_manually_diverged",
|
|
3207
|
-
`
|
|
3208
|
-
|
|
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")
|
|
3209
3387
|
);
|
|
3210
3388
|
}
|
|
3211
|
-
return okCheck(
|
|
3389
|
+
return okCheck(
|
|
3390
|
+
t("doctor.check.meta_manually_diverged.name"),
|
|
3391
|
+
t("doctor.check.meta_manually_diverged.ok.consistent")
|
|
3392
|
+
);
|
|
3212
3393
|
}
|
|
3213
3394
|
function inspectPreexistingRootFiles(projectRoot) {
|
|
3214
3395
|
const candidates = ["CLAUDE.md", "AGENTS.md"];
|
|
@@ -3274,36 +3455,44 @@ async function inspectFilesystemEditFallback(projectRoot) {
|
|
|
3274
3455
|
}
|
|
3275
3456
|
return { synthesized: orphanIds.length, synthesizedStableIds: orphanIds };
|
|
3276
3457
|
}
|
|
3277
|
-
function createFilesystemEditFallbackCheck(inspection) {
|
|
3458
|
+
function createFilesystemEditFallbackCheck(t, inspection) {
|
|
3278
3459
|
if (inspection.synthesized === 0) {
|
|
3279
3460
|
return okCheck(
|
|
3280
|
-
"
|
|
3281
|
-
"
|
|
3461
|
+
t("doctor.check.filesystem_edit_fallback.name"),
|
|
3462
|
+
t("doctor.check.filesystem_edit_fallback.ok")
|
|
3282
3463
|
);
|
|
3283
3464
|
}
|
|
3284
3465
|
const sample = inspection.synthesizedStableIds.slice(0, 3).join(", ");
|
|
3285
3466
|
return {
|
|
3286
|
-
name: "
|
|
3467
|
+
name: t("doctor.check.filesystem_edit_fallback.name"),
|
|
3287
3468
|
status: "ok",
|
|
3288
3469
|
kind: "info",
|
|
3289
3470
|
code: "knowledge_promoted_synthesized",
|
|
3290
3471
|
fixable: false,
|
|
3291
|
-
message:
|
|
3292
|
-
|
|
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")
|
|
3293
3482
|
};
|
|
3294
3483
|
}
|
|
3295
|
-
function createPreexistingRootFilesCheck(inspection) {
|
|
3484
|
+
function createPreexistingRootFilesCheck(t, inspection) {
|
|
3296
3485
|
if (inspection.detected.length === 0) {
|
|
3297
|
-
return okCheck("
|
|
3486
|
+
return okCheck(t("doctor.check.preexisting_root_files.name"), t("doctor.check.preexisting_root_files.ok"));
|
|
3298
3487
|
}
|
|
3299
3488
|
return {
|
|
3300
|
-
name: "
|
|
3489
|
+
name: t("doctor.check.preexisting_root_files.name"),
|
|
3301
3490
|
status: "ok",
|
|
3302
3491
|
kind: "info",
|
|
3303
3492
|
code: "preexisting_root_claude_md",
|
|
3304
3493
|
fixable: false,
|
|
3305
|
-
message:
|
|
3306
|
-
actionHint: "
|
|
3494
|
+
message: t("doctor.check.preexisting_root_files.message", { files: inspection.detected.join(", ") }),
|
|
3495
|
+
actionHint: t("doctor.check.preexisting_root_files.remediation")
|
|
3307
3496
|
};
|
|
3308
3497
|
}
|
|
3309
3498
|
async function buildLastConsumedIndex(projectRoot) {
|
|
@@ -3801,114 +3990,156 @@ function readUnderseedThresholdFromConfig(projectRoot) {
|
|
|
3801
3990
|
}
|
|
3802
3991
|
return DEFAULT_UNDERSEED_NODE_THRESHOLD;
|
|
3803
3992
|
}
|
|
3804
|
-
function createOrphanDemoteCheck(inspection) {
|
|
3993
|
+
function createOrphanDemoteCheck(t, inspection) {
|
|
3805
3994
|
if (inspection.candidates.length === 0) {
|
|
3806
3995
|
return okCheck(
|
|
3807
|
-
"
|
|
3808
|
-
"
|
|
3996
|
+
t("doctor.check.orphan_demote.name"),
|
|
3997
|
+
t("doctor.check.orphan_demote.ok")
|
|
3809
3998
|
);
|
|
3810
3999
|
}
|
|
3811
4000
|
const first = inspection.candidates[0];
|
|
3812
4001
|
const detail = `${first.stable_id} (${first.maturity}, ${first.age_days}d inactive at ${first.path})`;
|
|
4002
|
+
const count = inspection.candidates.length;
|
|
3813
4003
|
return issueCheck(
|
|
3814
|
-
"
|
|
4004
|
+
t("doctor.check.orphan_demote.name"),
|
|
3815
4005
|
"warn",
|
|
3816
4006
|
"warning",
|
|
3817
4007
|
"knowledge_orphan_demote_required",
|
|
3818
|
-
|
|
3819
|
-
|
|
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")
|
|
3820
4016
|
);
|
|
3821
4017
|
}
|
|
3822
|
-
function createStaleArchiveCheck(inspection) {
|
|
4018
|
+
function createStaleArchiveCheck(t, inspection) {
|
|
3823
4019
|
if (inspection.candidates.length === 0) {
|
|
3824
4020
|
return okCheck(
|
|
3825
|
-
"
|
|
3826
|
-
"
|
|
4021
|
+
t("doctor.check.stale_archive.name"),
|
|
4022
|
+
t("doctor.check.stale_archive.ok")
|
|
3827
4023
|
);
|
|
3828
4024
|
}
|
|
3829
4025
|
const first = inspection.candidates[0];
|
|
3830
4026
|
const detail = `${first.stable_id} (${first.age_days}d inactive at ${first.path}) \u2192 ${first.archive_path}`;
|
|
4027
|
+
const count = inspection.candidates.length;
|
|
3831
4028
|
return issueCheck(
|
|
3832
|
-
"
|
|
4029
|
+
t("doctor.check.stale_archive.name"),
|
|
3833
4030
|
"warn",
|
|
3834
4031
|
"warning",
|
|
3835
4032
|
"knowledge_stale_archive_required",
|
|
3836
|
-
|
|
3837
|
-
|
|
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")
|
|
3838
4039
|
);
|
|
3839
4040
|
}
|
|
3840
|
-
function createPendingOverdueCheck(inspection) {
|
|
4041
|
+
function createPendingOverdueCheck(t, inspection) {
|
|
3841
4042
|
if (inspection.candidates.length === 0) {
|
|
3842
4043
|
return okCheck(
|
|
3843
|
-
"
|
|
3844
|
-
"
|
|
4044
|
+
t("doctor.check.pending_overdue.name"),
|
|
4045
|
+
t("doctor.check.pending_overdue.ok")
|
|
3845
4046
|
);
|
|
3846
4047
|
}
|
|
3847
4048
|
const first = inspection.candidates[0];
|
|
3848
4049
|
const detail = `${first.path} (${first.age_days}d old)`;
|
|
4050
|
+
const count = inspection.candidates.length;
|
|
3849
4051
|
return issueCheck(
|
|
3850
|
-
"
|
|
4052
|
+
t("doctor.check.pending_overdue.name"),
|
|
3851
4053
|
"warn",
|
|
3852
4054
|
"warning",
|
|
3853
4055
|
"knowledge_pending_overdue",
|
|
3854
|
-
|
|
3855
|
-
|
|
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")
|
|
3856
4062
|
);
|
|
3857
4063
|
}
|
|
3858
|
-
function createUnderseededCheck(inspection) {
|
|
4064
|
+
function createUnderseededCheck(t, inspection) {
|
|
3859
4065
|
if (!inspection.underseeded) {
|
|
3860
4066
|
return okCheck(
|
|
3861
|
-
"
|
|
3862
|
-
|
|
4067
|
+
t("doctor.check.underseeded.name"),
|
|
4068
|
+
t("doctor.check.underseeded.ok", {
|
|
4069
|
+
count: String(inspection.node_count),
|
|
4070
|
+
threshold: String(inspection.threshold)
|
|
4071
|
+
})
|
|
3863
4072
|
);
|
|
3864
4073
|
}
|
|
3865
4074
|
return issueCheck(
|
|
3866
|
-
"
|
|
4075
|
+
t("doctor.check.underseeded.name"),
|
|
3867
4076
|
"ok",
|
|
3868
4077
|
"info",
|
|
3869
4078
|
"knowledge_underseeded",
|
|
3870
|
-
`
|
|
3871
|
-
|
|
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")
|
|
3872
4084
|
);
|
|
3873
4085
|
}
|
|
3874
|
-
function createSessionHintsStaleCheck(inspection) {
|
|
4086
|
+
function createSessionHintsStaleCheck(t, inspection) {
|
|
3875
4087
|
if (inspection.candidates.length === 0) {
|
|
3876
4088
|
return okCheck(
|
|
3877
|
-
"
|
|
3878
|
-
|
|
4089
|
+
t("doctor.check.session_hints_stale.name"),
|
|
4090
|
+
t("doctor.check.session_hints_stale.ok", {
|
|
4091
|
+
days: String(SESSION_HINTS_STALE_DAYS)
|
|
4092
|
+
})
|
|
3879
4093
|
);
|
|
3880
4094
|
}
|
|
3881
4095
|
const first = inspection.candidates[0];
|
|
3882
4096
|
const detail = `${first.path} (${first.age_days}d old)`;
|
|
4097
|
+
const count = inspection.candidates.length;
|
|
3883
4098
|
return issueCheck(
|
|
3884
|
-
"
|
|
4099
|
+
t("doctor.check.session_hints_stale.name"),
|
|
3885
4100
|
"ok",
|
|
3886
4101
|
"info",
|
|
3887
4102
|
"knowledge_session_hints_stale",
|
|
3888
|
-
|
|
3889
|
-
|
|
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")
|
|
3890
4109
|
);
|
|
3891
4110
|
}
|
|
3892
|
-
function createStaleServeLockCheck(inspection) {
|
|
4111
|
+
function createStaleServeLockCheck(t, inspection) {
|
|
3893
4112
|
if (!inspection.present) {
|
|
3894
|
-
return okCheck(
|
|
4113
|
+
return okCheck(
|
|
4114
|
+
t("doctor.check.stale_serve_lock.name"),
|
|
4115
|
+
t("doctor.check.stale_serve_lock.ok.no_lock")
|
|
4116
|
+
);
|
|
3895
4117
|
}
|
|
3896
4118
|
if (inspection.pidAlive) {
|
|
3897
4119
|
return okCheck(
|
|
3898
|
-
"
|
|
3899
|
-
|
|
4120
|
+
t("doctor.check.stale_serve_lock.name"),
|
|
4121
|
+
t("doctor.check.stale_serve_lock.ok.live_pid", {
|
|
4122
|
+
pid: String(inspection.pid)
|
|
4123
|
+
})
|
|
3900
4124
|
);
|
|
3901
4125
|
}
|
|
3902
4126
|
const days = Math.floor(inspection.ageMs / MS_PER_DAY);
|
|
3903
4127
|
const hours = Math.floor(inspection.ageMs / (60 * 60 * 1e3));
|
|
3904
|
-
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
|
+
});
|
|
3905
4133
|
return issueCheck(
|
|
3906
|
-
"
|
|
4134
|
+
t("doctor.check.stale_serve_lock.name"),
|
|
3907
4135
|
"ok",
|
|
3908
4136
|
"info",
|
|
3909
4137
|
"stale_serve_lock",
|
|
3910
|
-
|
|
3911
|
-
|
|
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")
|
|
3912
4143
|
);
|
|
3913
4144
|
}
|
|
3914
4145
|
function extractKnowledgeFrontmatterRelevanceScope(source) {
|
|
@@ -4107,64 +4338,81 @@ function readRecentGitTouchedPaths(projectRoot, windowDays) {
|
|
|
4107
4338
|
}
|
|
4108
4339
|
return Array.from(set);
|
|
4109
4340
|
}
|
|
4110
|
-
function createNarrowNoPathsCheck(inspection) {
|
|
4341
|
+
function createNarrowNoPathsCheck(t, inspection) {
|
|
4111
4342
|
if (inspection.candidates.length === 0) {
|
|
4112
4343
|
return okCheck(
|
|
4113
|
-
"
|
|
4114
|
-
"
|
|
4344
|
+
t("doctor.check.narrow_no_paths.name"),
|
|
4345
|
+
t("doctor.check.narrow_no_paths.ok")
|
|
4115
4346
|
);
|
|
4116
4347
|
}
|
|
4117
4348
|
const first = inspection.candidates[0];
|
|
4118
4349
|
const detail = `${first.stable_id} (${first.path})`;
|
|
4350
|
+
const count = inspection.candidates.length;
|
|
4119
4351
|
return issueCheck(
|
|
4120
|
-
"
|
|
4352
|
+
t("doctor.check.narrow_no_paths.name"),
|
|
4121
4353
|
"warn",
|
|
4122
4354
|
"warning",
|
|
4123
4355
|
"knowledge_narrow_no_paths",
|
|
4124
|
-
|
|
4125
|
-
|
|
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")
|
|
4126
4361
|
);
|
|
4127
4362
|
}
|
|
4128
|
-
function createRelevancePathsDanglingCheck(inspection) {
|
|
4363
|
+
function createRelevancePathsDanglingCheck(t, inspection) {
|
|
4129
4364
|
if (inspection.entries.length === 0) {
|
|
4130
4365
|
return okCheck(
|
|
4131
|
-
"
|
|
4132
|
-
"
|
|
4366
|
+
t("doctor.check.relevance_paths_dangling.name"),
|
|
4367
|
+
t("doctor.check.relevance_paths_dangling.ok")
|
|
4133
4368
|
);
|
|
4134
4369
|
}
|
|
4135
4370
|
const first = inspection.entries[0];
|
|
4136
4371
|
const detail = `${first.stable_id} at ${first.path} \u2192 \`${first.dangling_glob}\` (0 matches)`;
|
|
4372
|
+
const count = inspection.entries.length;
|
|
4137
4373
|
return issueCheck(
|
|
4138
|
-
"
|
|
4374
|
+
t("doctor.check.relevance_paths_dangling.name"),
|
|
4139
4375
|
"warn",
|
|
4140
4376
|
"warning",
|
|
4141
4377
|
"knowledge_relevance_paths_dangling",
|
|
4142
|
-
|
|
4143
|
-
|
|
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")
|
|
4144
4383
|
);
|
|
4145
4384
|
}
|
|
4146
|
-
function createRelevancePathsDriftCheck(inspection) {
|
|
4385
|
+
function createRelevancePathsDriftCheck(t, inspection) {
|
|
4147
4386
|
if (!inspection.git_available) {
|
|
4148
4387
|
return okCheck(
|
|
4149
|
-
"
|
|
4150
|
-
|
|
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
|
+
})
|
|
4151
4392
|
);
|
|
4152
4393
|
}
|
|
4153
4394
|
if (inspection.candidates.length === 0) {
|
|
4154
4395
|
return okCheck(
|
|
4155
|
-
"
|
|
4156
|
-
|
|
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
|
+
})
|
|
4157
4400
|
);
|
|
4158
4401
|
}
|
|
4159
4402
|
const first = inspection.candidates[0];
|
|
4160
4403
|
const detail = `${first.stable_id} at ${first.path} (globs: ${first.globs.join(", ")})`;
|
|
4404
|
+
const count = inspection.candidates.length;
|
|
4161
4405
|
return issueCheck(
|
|
4162
|
-
"
|
|
4406
|
+
t("doctor.check.relevance_paths_drift.name"),
|
|
4163
4407
|
"ok",
|
|
4164
4408
|
"info",
|
|
4165
4409
|
"knowledge_relevance_paths_drift",
|
|
4166
|
-
|
|
4167
|
-
|
|
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")
|
|
4168
4416
|
);
|
|
4169
4417
|
}
|
|
4170
4418
|
function inspectRelevanceFieldsMissing(projectRoot) {
|
|
@@ -4307,11 +4555,11 @@ async function applyRelevanceFieldsMissing(candidate) {
|
|
|
4307
4555
|
};
|
|
4308
4556
|
}
|
|
4309
4557
|
}
|
|
4310
|
-
function createRelevanceFieldsMissingCheck(inspection) {
|
|
4558
|
+
function createRelevanceFieldsMissingCheck(t, inspection) {
|
|
4311
4559
|
if (inspection.candidates.length === 0) {
|
|
4312
4560
|
return okCheck(
|
|
4313
|
-
"
|
|
4314
|
-
"
|
|
4561
|
+
t("doctor.check.relevance_fields_missing.name"),
|
|
4562
|
+
t("doctor.check.relevance_fields_missing.ok")
|
|
4315
4563
|
);
|
|
4316
4564
|
}
|
|
4317
4565
|
const first = inspection.candidates[0];
|
|
@@ -4319,13 +4567,17 @@ function createRelevanceFieldsMissingCheck(inspection) {
|
|
|
4319
4567
|
if (first.missing_scope) missingParts.push("relevance_scope");
|
|
4320
4568
|
if (first.missing_paths) missingParts.push("relevance_paths");
|
|
4321
4569
|
const detail = `${first.pending_path} (missing: ${missingParts.join(", ")})`;
|
|
4570
|
+
const count = inspection.candidates.length;
|
|
4322
4571
|
return issueCheck(
|
|
4323
|
-
"
|
|
4572
|
+
t("doctor.check.relevance_fields_missing.name"),
|
|
4324
4573
|
"ok",
|
|
4325
4574
|
"info",
|
|
4326
4575
|
"knowledge_relevance_fields_missing",
|
|
4327
|
-
|
|
4328
|
-
|
|
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")
|
|
4329
4581
|
);
|
|
4330
4582
|
}
|
|
4331
4583
|
var SKILL_MD_FRONTMATTER_ROOTS = [".claude/skills", ".codex/skills"];
|
|
@@ -4396,23 +4648,26 @@ function extractSkillFrontmatterLines(raw) {
|
|
|
4396
4648
|
}
|
|
4397
4649
|
return null;
|
|
4398
4650
|
}
|
|
4399
|
-
function createSkillMdYamlInvalidCheck(inspection) {
|
|
4651
|
+
function createSkillMdYamlInvalidCheck(t, inspection) {
|
|
4400
4652
|
if (inspection.candidates.length === 0) {
|
|
4401
4653
|
return okCheck(
|
|
4402
|
-
"
|
|
4403
|
-
"
|
|
4654
|
+
t("doctor.check.skill_md_yaml_invalid.name"),
|
|
4655
|
+
t("doctor.check.skill_md_yaml_invalid.ok")
|
|
4404
4656
|
);
|
|
4405
4657
|
}
|
|
4406
4658
|
const first = inspection.candidates[0];
|
|
4407
4659
|
const detail = `${first.path}:${first.line} (key \`${first.key}\` value contains an unquoted ': ' \u2014 preview: \`${first.preview}\`)`;
|
|
4408
4660
|
const plural = inspection.candidates.length === 1;
|
|
4409
4661
|
return issueCheck(
|
|
4410
|
-
"
|
|
4662
|
+
t("doctor.check.skill_md_yaml_invalid.name"),
|
|
4411
4663
|
"warn",
|
|
4412
4664
|
"warning",
|
|
4413
4665
|
"skill_md_yaml_invalid",
|
|
4414
|
-
|
|
4415
|
-
|
|
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")
|
|
4416
4671
|
);
|
|
4417
4672
|
}
|
|
4418
4673
|
var KNOWLEDGE_CANONICAL_TYPE_DIRS_FOR_ONBOARD = [
|
|
@@ -4507,55 +4762,83 @@ function readFrontmatterScalar(content, key) {
|
|
|
4507
4762
|
}
|
|
4508
4763
|
return void 0;
|
|
4509
4764
|
}
|
|
4510
|
-
function createOnboardCoverageCheck(inspection) {
|
|
4765
|
+
function createOnboardCoverageCheck(t, inspection) {
|
|
4511
4766
|
const filledCount = ONBOARD_SLOT_NAMES.filter(
|
|
4512
4767
|
(slot) => inspection.filled[slot].length > 0
|
|
4513
4768
|
).length;
|
|
4514
4769
|
if (inspection.missing.length === 0) {
|
|
4515
4770
|
return okCheck(
|
|
4516
|
-
"
|
|
4517
|
-
|
|
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
|
+
})
|
|
4518
4777
|
);
|
|
4519
4778
|
}
|
|
4520
4779
|
return issueCheck(
|
|
4521
|
-
"
|
|
4780
|
+
t("doctor.check.onboard_coverage.name"),
|
|
4522
4781
|
"ok",
|
|
4523
4782
|
"info",
|
|
4524
4783
|
"onboard_coverage_incomplete",
|
|
4525
|
-
|
|
4526
|
-
|
|
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")
|
|
4527
4791
|
);
|
|
4528
4792
|
}
|
|
4529
|
-
function createNarrowTooFewCheck(inspection) {
|
|
4793
|
+
function createNarrowTooFewCheck(t, inspection) {
|
|
4530
4794
|
const { structural_flagged, telemetry_flagged } = inspection;
|
|
4531
4795
|
if (!structural_flagged && !telemetry_flagged) {
|
|
4532
4796
|
const ratioPct = (inspection.narrow_ratio * 100).toFixed(0);
|
|
4533
|
-
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
|
+
});
|
|
4534
4801
|
return okCheck(
|
|
4535
|
-
"
|
|
4536
|
-
|
|
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
|
+
})
|
|
4537
4809
|
);
|
|
4538
4810
|
}
|
|
4539
4811
|
const parts = [];
|
|
4540
4812
|
if (structural_flagged) {
|
|
4541
4813
|
const ratioPct = (inspection.narrow_ratio * 100).toFixed(0);
|
|
4542
4814
|
parts.push(
|
|
4543
|
-
|
|
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
|
+
})
|
|
4544
4821
|
);
|
|
4545
4822
|
}
|
|
4546
4823
|
if (telemetry_flagged) {
|
|
4547
4824
|
const silencePct = (inspection.silence_rate * 100).toFixed(0);
|
|
4548
4825
|
parts.push(
|
|
4549
|
-
|
|
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
|
+
})
|
|
4550
4833
|
);
|
|
4551
4834
|
}
|
|
4552
4835
|
return issueCheck(
|
|
4553
|
-
"
|
|
4836
|
+
t("doctor.check.narrow_too_few.name"),
|
|
4554
4837
|
"ok",
|
|
4555
4838
|
"info",
|
|
4556
4839
|
"knowledge_narrow_too_few",
|
|
4557
|
-
|
|
4558
|
-
"
|
|
4840
|
+
t("doctor.check.narrow_too_few.message.summary", { parts: parts.join("; ") }),
|
|
4841
|
+
t("doctor.check.narrow_too_few.remediation")
|
|
4559
4842
|
);
|
|
4560
4843
|
}
|
|
4561
4844
|
function resolvePersonalKnowledgeRoot() {
|
|
@@ -4692,58 +4975,70 @@ function inspectIndexDrift(projectRoot, meta) {
|
|
|
4692
4975
|
);
|
|
4693
4976
|
return { drifts };
|
|
4694
4977
|
}
|
|
4695
|
-
function createStableIdDuplicateCheck(inspection) {
|
|
4978
|
+
function createStableIdDuplicateCheck(t, inspection) {
|
|
4696
4979
|
if (inspection.duplicates.length === 0) {
|
|
4697
4980
|
return okCheck(
|
|
4698
|
-
"
|
|
4699
|
-
"
|
|
4981
|
+
t("doctor.check.stable_id_duplicate.name"),
|
|
4982
|
+
t("doctor.check.stable_id_duplicate.ok")
|
|
4700
4983
|
);
|
|
4701
4984
|
}
|
|
4702
4985
|
const first = inspection.duplicates[0];
|
|
4703
4986
|
const detail = `${first.stable_id} appears in ${first.paths.length} files: ${first.paths.join(", ")}`;
|
|
4987
|
+
const count = inspection.duplicates.length;
|
|
4704
4988
|
return issueCheck(
|
|
4705
|
-
"
|
|
4989
|
+
t("doctor.check.stable_id_duplicate.name"),
|
|
4706
4990
|
"error",
|
|
4707
4991
|
"manual_error",
|
|
4708
4992
|
"knowledge_stable_id_duplicate",
|
|
4709
|
-
|
|
4710
|
-
|
|
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")
|
|
4711
4998
|
);
|
|
4712
4999
|
}
|
|
4713
|
-
function createLayerMismatchCheck(inspection) {
|
|
5000
|
+
function createLayerMismatchCheck(t, inspection) {
|
|
4714
5001
|
if (inspection.mismatches.length === 0) {
|
|
4715
5002
|
return okCheck(
|
|
4716
|
-
"
|
|
4717
|
-
"
|
|
5003
|
+
t("doctor.check.layer_mismatch.name"),
|
|
5004
|
+
t("doctor.check.layer_mismatch.ok")
|
|
4718
5005
|
);
|
|
4719
5006
|
}
|
|
4720
5007
|
const first = inspection.mismatches[0];
|
|
4721
5008
|
const detail = `${first.stable_id} at ${first.path} (located in ${first.located_in}, expected ${first.expected_layer})`;
|
|
5009
|
+
const count = inspection.mismatches.length;
|
|
4722
5010
|
return issueCheck(
|
|
4723
|
-
"
|
|
5011
|
+
t("doctor.check.layer_mismatch.name"),
|
|
4724
5012
|
"error",
|
|
4725
5013
|
"manual_error",
|
|
4726
5014
|
"knowledge_layer_mismatch",
|
|
4727
|
-
|
|
4728
|
-
|
|
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")
|
|
4729
5020
|
);
|
|
4730
5021
|
}
|
|
4731
|
-
function createIndexDriftCheck(inspection) {
|
|
5022
|
+
function createIndexDriftCheck(t, inspection) {
|
|
4732
5023
|
if (inspection.drifts.length === 0) {
|
|
4733
5024
|
return okCheck(
|
|
4734
|
-
"
|
|
4735
|
-
"
|
|
5025
|
+
t("doctor.check.index_drift.name"),
|
|
5026
|
+
t("doctor.check.index_drift.ok")
|
|
4736
5027
|
);
|
|
4737
5028
|
}
|
|
4738
5029
|
const first = inspection.drifts[0];
|
|
4739
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;
|
|
4740
5032
|
return issueCheck(
|
|
4741
|
-
"
|
|
5033
|
+
t("doctor.check.index_drift.name"),
|
|
4742
5034
|
"error",
|
|
4743
5035
|
"fixable_error",
|
|
4744
5036
|
"knowledge_index_drift",
|
|
4745
|
-
|
|
4746
|
-
|
|
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")
|
|
4747
5042
|
);
|
|
4748
5043
|
}
|
|
4749
5044
|
async function migrateBootstrapMarkers(projectRoot) {
|
|
@@ -4963,6 +5258,40 @@ async function ensureCitePolicyActivatedMarker(projectRoot) {
|
|
|
4963
5258
|
return { marker_ts: 0, emitted_now: false };
|
|
4964
5259
|
}
|
|
4965
5260
|
}
|
|
5261
|
+
async function ensureCiteContractPolicyActivatedMarker(projectRoot) {
|
|
5262
|
+
let driftStatus;
|
|
5263
|
+
try {
|
|
5264
|
+
const inspection = await inspectL1BootstrapSnapshotDrift(projectRoot);
|
|
5265
|
+
driftStatus = inspection.status;
|
|
5266
|
+
} catch {
|
|
5267
|
+
driftStatus = "drift";
|
|
5268
|
+
}
|
|
5269
|
+
if (driftStatus !== "ok") {
|
|
5270
|
+
return { marker_ts: 0, emitted_now: false, blocked_by: "bootstrap_drift" };
|
|
5271
|
+
}
|
|
5272
|
+
let existing;
|
|
5273
|
+
try {
|
|
5274
|
+
const { events } = await readEventLedger(projectRoot, {
|
|
5275
|
+
event_type: "cite_contract_policy_activated"
|
|
5276
|
+
});
|
|
5277
|
+
if (events.length > 0) {
|
|
5278
|
+
existing = events[0];
|
|
5279
|
+
}
|
|
5280
|
+
} catch {
|
|
5281
|
+
return { marker_ts: 0, emitted_now: false, blocked_by: null };
|
|
5282
|
+
}
|
|
5283
|
+
if (existing !== void 0) {
|
|
5284
|
+
return { marker_ts: existing.ts, emitted_now: false, blocked_by: null };
|
|
5285
|
+
}
|
|
5286
|
+
try {
|
|
5287
|
+
const stored = await appendEventLedgerEvent(projectRoot, {
|
|
5288
|
+
event_type: "cite_contract_policy_activated"
|
|
5289
|
+
});
|
|
5290
|
+
return { marker_ts: stored.ts, emitted_now: true, blocked_by: null };
|
|
5291
|
+
} catch {
|
|
5292
|
+
return { marker_ts: 0, emitted_now: false, blocked_by: null };
|
|
5293
|
+
}
|
|
5294
|
+
}
|
|
4966
5295
|
function parseNoneSentinel(kbLineRaw) {
|
|
4967
5296
|
if (typeof kbLineRaw !== "string" || kbLineRaw.length === 0) return "unspecified";
|
|
4968
5297
|
const m = kbLineRaw.match(/^KB:\s*none\b\s*(?:\[([^\]]*)\])?\s*$/i);
|
|
@@ -5000,7 +5329,10 @@ function matchesRelevancePath(editPath, relevancePaths) {
|
|
|
5000
5329
|
return false;
|
|
5001
5330
|
}
|
|
5002
5331
|
async function runDoctorCiteCoverage(projectRoot, options) {
|
|
5332
|
+
const layerFilter = options.layer ?? "all";
|
|
5003
5333
|
const marker = await ensureCitePolicyActivatedMarker(projectRoot);
|
|
5334
|
+
const contractMarker = await ensureCiteContractPolicyActivatedMarker(projectRoot);
|
|
5335
|
+
const idTypeMap = await loadKbIdTypeMap(projectRoot);
|
|
5004
5336
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5005
5337
|
const zeroMetrics = {
|
|
5006
5338
|
edits_touched: 0,
|
|
@@ -5009,6 +5341,20 @@ async function runDoctorCiteCoverage(projectRoot, options) {
|
|
|
5009
5341
|
expected_but_missed: 0,
|
|
5010
5342
|
total_turns: 0
|
|
5011
5343
|
};
|
|
5344
|
+
const contractStatus = contractMarker.blocked_by === "bootstrap_drift" ? "skipped:bootstrap_drift" : contractMarker.marker_ts === 0 ? "awaiting_marker" : "ok";
|
|
5345
|
+
const zeroContractMetrics = {
|
|
5346
|
+
decisions_cited: 0,
|
|
5347
|
+
pitfalls_cited: 0,
|
|
5348
|
+
contract_with: 0,
|
|
5349
|
+
contract_missing: 0,
|
|
5350
|
+
hard_violated: 0,
|
|
5351
|
+
cite_id_unresolved: 0,
|
|
5352
|
+
skip_count: {}
|
|
5353
|
+
};
|
|
5354
|
+
const zeroLayerType = {
|
|
5355
|
+
team: {},
|
|
5356
|
+
personal: {}
|
|
5357
|
+
};
|
|
5012
5358
|
if (marker.marker_ts === 0) {
|
|
5013
5359
|
return {
|
|
5014
5360
|
status: "skipped",
|
|
@@ -5016,11 +5362,17 @@ async function runDoctorCiteCoverage(projectRoot, options) {
|
|
|
5016
5362
|
marker_emitted_now: false,
|
|
5017
5363
|
since_ts: options.since,
|
|
5018
5364
|
client_filter: options.client,
|
|
5365
|
+
layer_filter: layerFilter,
|
|
5019
5366
|
metrics: zeroMetrics,
|
|
5367
|
+
contract_metrics_status: contractStatus,
|
|
5368
|
+
contract_metrics: zeroContractMetrics,
|
|
5369
|
+
per_layer_type: zeroLayerType,
|
|
5370
|
+
contract_marker_ts: contractMarker.marker_ts,
|
|
5020
5371
|
generated_at: generatedAt
|
|
5021
5372
|
};
|
|
5022
5373
|
}
|
|
5023
5374
|
const effectiveSince = Math.max(marker.marker_ts, options.since);
|
|
5375
|
+
const contractEffectiveSince = contractStatus === "ok" ? Math.max(contractMarker.marker_ts, options.since) : Number.POSITIVE_INFINITY;
|
|
5024
5376
|
let ledgerEvents = [];
|
|
5025
5377
|
try {
|
|
5026
5378
|
const result = await readEventLedger(projectRoot, { since: effectiveSince });
|
|
@@ -5032,7 +5384,12 @@ async function runDoctorCiteCoverage(projectRoot, options) {
|
|
|
5032
5384
|
marker_emitted_now: marker.emitted_now,
|
|
5033
5385
|
since_ts: effectiveSince,
|
|
5034
5386
|
client_filter: options.client,
|
|
5387
|
+
layer_filter: layerFilter,
|
|
5035
5388
|
metrics: zeroMetrics,
|
|
5389
|
+
contract_metrics_status: contractStatus,
|
|
5390
|
+
contract_metrics: zeroContractMetrics,
|
|
5391
|
+
per_layer_type: zeroLayerType,
|
|
5392
|
+
contract_marker_ts: contractMarker.marker_ts,
|
|
5036
5393
|
generated_at: generatedAt
|
|
5037
5394
|
};
|
|
5038
5395
|
}
|
|
@@ -5054,7 +5411,7 @@ async function runDoctorCiteCoverage(projectRoot, options) {
|
|
|
5054
5411
|
break;
|
|
5055
5412
|
}
|
|
5056
5413
|
}
|
|
5057
|
-
const filteredTurns = options.client === "all" ? assistantTurns : assistantTurns.filter((
|
|
5414
|
+
const filteredTurns = options.client === "all" ? assistantTurns : assistantTurns.filter((t) => t.client === options.client);
|
|
5058
5415
|
let clientSessionIds = null;
|
|
5059
5416
|
if (options.client !== "all") {
|
|
5060
5417
|
clientSessionIds = /* @__PURE__ */ new Set();
|
|
@@ -5125,6 +5482,80 @@ async function runDoctorCiteCoverage(projectRoot, options) {
|
|
|
5125
5482
|
perClientAccum.set(client, existing);
|
|
5126
5483
|
};
|
|
5127
5484
|
const sessionCitedKbs = /* @__PURE__ */ new Map();
|
|
5485
|
+
const sessionEditPaths = /* @__PURE__ */ new Map();
|
|
5486
|
+
for (const edit of editEvents) {
|
|
5487
|
+
const sid = edit.session_id;
|
|
5488
|
+
if (typeof sid !== "string" || sid.length === 0) continue;
|
|
5489
|
+
const list = sessionEditPaths.get(sid) ?? [];
|
|
5490
|
+
list.push(normalizePath(edit.path));
|
|
5491
|
+
sessionEditPaths.set(sid, list);
|
|
5492
|
+
}
|
|
5493
|
+
let decisionsCited = 0;
|
|
5494
|
+
let pitfallsCited = 0;
|
|
5495
|
+
let contractWith = 0;
|
|
5496
|
+
let contractMissing = 0;
|
|
5497
|
+
let hardViolated = 0;
|
|
5498
|
+
let citeIdUnresolved = 0;
|
|
5499
|
+
const skipCount = {};
|
|
5500
|
+
const layerTypeAccum = { team: {}, personal: {} };
|
|
5501
|
+
const bumpLayerType = (citeId, type) => {
|
|
5502
|
+
const layer = citeId.startsWith("KP-") ? "personal" : citeId.startsWith("KT-") ? "team" : null;
|
|
5503
|
+
if (layer === null) return;
|
|
5504
|
+
layerTypeAccum[layer][type] = (layerTypeAccum[layer][type] ?? 0) + 1;
|
|
5505
|
+
};
|
|
5506
|
+
const passesLayerFilter = (citeId) => {
|
|
5507
|
+
if (layerFilter === "all") return true;
|
|
5508
|
+
if (layerFilter === "team") return citeId.startsWith("KT-");
|
|
5509
|
+
return citeId.startsWith("KP-");
|
|
5510
|
+
};
|
|
5511
|
+
const evaluateOperatorViolation = (sessionId, operators) => {
|
|
5512
|
+
const editPaths = typeof sessionId === "string" && sessionId.length > 0 ? sessionEditPaths.get(sessionId) ?? [] : [];
|
|
5513
|
+
for (const op of operators) {
|
|
5514
|
+
switch (op.kind) {
|
|
5515
|
+
case "edit": {
|
|
5516
|
+
let matched = false;
|
|
5517
|
+
for (const p of editPaths) {
|
|
5518
|
+
if (minimatch(p, op.target, { dot: true, matchBase: false })) {
|
|
5519
|
+
matched = true;
|
|
5520
|
+
break;
|
|
5521
|
+
}
|
|
5522
|
+
}
|
|
5523
|
+
if (!matched) return true;
|
|
5524
|
+
break;
|
|
5525
|
+
}
|
|
5526
|
+
case "not_edit": {
|
|
5527
|
+
for (const p of editPaths) {
|
|
5528
|
+
if (minimatch(p, op.target, { dot: true, matchBase: false })) {
|
|
5529
|
+
return true;
|
|
5530
|
+
}
|
|
5531
|
+
}
|
|
5532
|
+
break;
|
|
5533
|
+
}
|
|
5534
|
+
case "require": {
|
|
5535
|
+
let found = false;
|
|
5536
|
+
for (const p of editPaths) {
|
|
5537
|
+
if (p.includes(op.target)) {
|
|
5538
|
+
found = true;
|
|
5539
|
+
break;
|
|
5540
|
+
}
|
|
5541
|
+
}
|
|
5542
|
+
if (!found) return true;
|
|
5543
|
+
break;
|
|
5544
|
+
}
|
|
5545
|
+
case "forbid": {
|
|
5546
|
+
for (const p of editPaths) {
|
|
5547
|
+
if (p.includes(op.target)) {
|
|
5548
|
+
return true;
|
|
5549
|
+
}
|
|
5550
|
+
}
|
|
5551
|
+
break;
|
|
5552
|
+
}
|
|
5553
|
+
default:
|
|
5554
|
+
break;
|
|
5555
|
+
}
|
|
5556
|
+
}
|
|
5557
|
+
return false;
|
|
5558
|
+
};
|
|
5128
5559
|
let totalTurns = 0;
|
|
5129
5560
|
let qualifyingCites = 0;
|
|
5130
5561
|
let recalledUnverified = 0;
|
|
@@ -5174,6 +5605,40 @@ async function runDoctorCiteCoverage(projectRoot, options) {
|
|
|
5174
5605
|
m.recalled_unverified += 1;
|
|
5175
5606
|
});
|
|
5176
5607
|
}
|
|
5608
|
+
if (contractStatus === "ok" && turn.ts >= contractEffectiveSince) {
|
|
5609
|
+
const commitments = turn.cite_commitments ?? [];
|
|
5610
|
+
for (let i = 0; i < turn.cite_ids.length; i += 1) {
|
|
5611
|
+
const citeId = turn.cite_ids[i];
|
|
5612
|
+
if (typeof citeId !== "string" || citeId.length === 0) continue;
|
|
5613
|
+
if (!passesLayerFilter(citeId)) continue;
|
|
5614
|
+
const kbType = idTypeMap.get(citeId);
|
|
5615
|
+
if (kbType === void 0) {
|
|
5616
|
+
citeIdUnresolved += 1;
|
|
5617
|
+
bumpLayerType(citeId, "unresolved");
|
|
5618
|
+
continue;
|
|
5619
|
+
}
|
|
5620
|
+
bumpLayerType(citeId, kbType);
|
|
5621
|
+
if (kbType === "decision" || kbType === "pitfall") {
|
|
5622
|
+
if (kbType === "decision") decisionsCited += 1;
|
|
5623
|
+
else pitfallsCited += 1;
|
|
5624
|
+
const commitment = commitments[i];
|
|
5625
|
+
const operators = commitment?.operators ?? [];
|
|
5626
|
+
const skipReason = commitment?.skip_reason ?? null;
|
|
5627
|
+
if (skipReason !== null) {
|
|
5628
|
+
skipCount[skipReason] = (skipCount[skipReason] ?? 0) + 1;
|
|
5629
|
+
continue;
|
|
5630
|
+
}
|
|
5631
|
+
if (operators.length === 0) {
|
|
5632
|
+
contractMissing += 1;
|
|
5633
|
+
continue;
|
|
5634
|
+
}
|
|
5635
|
+
contractWith += 1;
|
|
5636
|
+
if (evaluateOperatorViolation(sid, operators)) {
|
|
5637
|
+
hardViolated += 1;
|
|
5638
|
+
}
|
|
5639
|
+
}
|
|
5640
|
+
}
|
|
5641
|
+
}
|
|
5177
5642
|
}
|
|
5178
5643
|
let editsTouched = 0;
|
|
5179
5644
|
let expectedButMissed = 0;
|
|
@@ -5208,19 +5673,96 @@ async function runDoctorCiteCoverage(projectRoot, options) {
|
|
|
5208
5673
|
perClient[client] = m;
|
|
5209
5674
|
}
|
|
5210
5675
|
}
|
|
5676
|
+
const contractMetrics = {
|
|
5677
|
+
decisions_cited: decisionsCited,
|
|
5678
|
+
pitfalls_cited: pitfallsCited,
|
|
5679
|
+
contract_with: contractWith,
|
|
5680
|
+
contract_missing: contractMissing,
|
|
5681
|
+
hard_violated: hardViolated,
|
|
5682
|
+
cite_id_unresolved: citeIdUnresolved,
|
|
5683
|
+
skip_count: skipCount
|
|
5684
|
+
};
|
|
5211
5685
|
return {
|
|
5212
5686
|
status: "ok",
|
|
5213
5687
|
marker_ts: marker.marker_ts,
|
|
5214
5688
|
marker_emitted_now: marker.emitted_now,
|
|
5215
5689
|
since_ts: effectiveSince,
|
|
5216
5690
|
client_filter: options.client,
|
|
5691
|
+
layer_filter: layerFilter,
|
|
5217
5692
|
metrics,
|
|
5218
5693
|
...perClient !== void 0 ? { per_client: perClient } : {},
|
|
5219
5694
|
...Object.keys(dismissedHistogram).length > 0 ? { dismissed_reason_histogram: dismissedHistogram } : {},
|
|
5220
5695
|
...Object.keys(noneHistogram).length > 0 ? { none_reason_histogram: noneHistogram } : {},
|
|
5696
|
+
contract_metrics_status: contractStatus,
|
|
5697
|
+
contract_metrics: contractMetrics,
|
|
5698
|
+
per_layer_type: layerTypeAccum,
|
|
5699
|
+
contract_marker_ts: contractMarker.marker_ts,
|
|
5700
|
+
generated_at: generatedAt
|
|
5701
|
+
};
|
|
5702
|
+
}
|
|
5703
|
+
async function runDoctorArchiveHistory(projectRoot, options) {
|
|
5704
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5705
|
+
const nowMs = Date.now();
|
|
5706
|
+
let events = [];
|
|
5707
|
+
try {
|
|
5708
|
+
const result = await readEventLedger(projectRoot, {
|
|
5709
|
+
event_type: "session_archive_attempted",
|
|
5710
|
+
since: options.since
|
|
5711
|
+
});
|
|
5712
|
+
events = result.events;
|
|
5713
|
+
} catch {
|
|
5714
|
+
return {
|
|
5715
|
+
entries: [],
|
|
5716
|
+
total: 0,
|
|
5717
|
+
since_ms: options.since,
|
|
5718
|
+
generated_at: generatedAt
|
|
5719
|
+
};
|
|
5720
|
+
}
|
|
5721
|
+
const mostRecentBySession = /* @__PURE__ */ new Map();
|
|
5722
|
+
for (const event of events) {
|
|
5723
|
+
if (event.event_type !== "session_archive_attempted") {
|
|
5724
|
+
continue;
|
|
5725
|
+
}
|
|
5726
|
+
const sessionId = event.session_id;
|
|
5727
|
+
if (typeof sessionId !== "string" || sessionId.length === 0) {
|
|
5728
|
+
continue;
|
|
5729
|
+
}
|
|
5730
|
+
const prior = mostRecentBySession.get(sessionId);
|
|
5731
|
+
if (prior === void 0 || event.ts > prior.ts) {
|
|
5732
|
+
mostRecentBySession.set(sessionId, event);
|
|
5733
|
+
}
|
|
5734
|
+
}
|
|
5735
|
+
const entries = [];
|
|
5736
|
+
for (const [sessionId, event] of mostRecentBySession.entries()) {
|
|
5737
|
+
const ageHours = Math.max(
|
|
5738
|
+
0,
|
|
5739
|
+
Math.floor((nowMs - event.covered_through_ts) / 36e5)
|
|
5740
|
+
);
|
|
5741
|
+
entries.push({
|
|
5742
|
+
session_id_short: truncateSessionId(sessionId),
|
|
5743
|
+
last_attempted_at: new Date(event.ts).toISOString(),
|
|
5744
|
+
outcome: event.outcome,
|
|
5745
|
+
candidates_proposed: event.candidates_proposed,
|
|
5746
|
+
covered_through_ts: event.covered_through_ts,
|
|
5747
|
+
age_since_covered_hours: ageHours
|
|
5748
|
+
});
|
|
5749
|
+
}
|
|
5750
|
+
entries.sort(
|
|
5751
|
+
(a, b) => a.last_attempted_at < b.last_attempted_at ? 1 : a.last_attempted_at > b.last_attempted_at ? -1 : 0
|
|
5752
|
+
);
|
|
5753
|
+
return {
|
|
5754
|
+
entries,
|
|
5755
|
+
total: entries.length,
|
|
5756
|
+
since_ms: options.since,
|
|
5221
5757
|
generated_at: generatedAt
|
|
5222
5758
|
};
|
|
5223
5759
|
}
|
|
5760
|
+
function truncateSessionId(sessionId) {
|
|
5761
|
+
if (sessionId.length <= 8) {
|
|
5762
|
+
return sessionId;
|
|
5763
|
+
}
|
|
5764
|
+
return `${sessionId.slice(0, 8)}...`;
|
|
5765
|
+
}
|
|
5224
5766
|
function createFixMessage(fixed, report) {
|
|
5225
5767
|
const fixedText = fixed.length === 0 ? "No deterministic doctor fixes were needed." : `Applied ${fixed.length} deterministic doctor fix${fixed.length === 1 ? "" : "es"}.`;
|
|
5226
5768
|
const manualText = report.manual_errors.length === 0 ? "No manual errors remain." : `${report.manual_errors.length} manual error${report.manual_errors.length === 1 ? "" : "s"} remain.`;
|
|
@@ -5734,6 +6276,7 @@ export {
|
|
|
5734
6276
|
appendEventLedgerEvent,
|
|
5735
6277
|
readEventLedger,
|
|
5736
6278
|
flushAndSyncEventLedger,
|
|
6279
|
+
loadKbIdTypeMap,
|
|
5737
6280
|
buildKnowledgeMeta,
|
|
5738
6281
|
writeKnowledgeMeta,
|
|
5739
6282
|
computeKnowledgeBasedAgentsMeta,
|
|
@@ -5758,5 +6301,6 @@ export {
|
|
|
5758
6301
|
runDoctorFix,
|
|
5759
6302
|
runDoctorApplyLint,
|
|
5760
6303
|
runDoctorCiteCoverage,
|
|
6304
|
+
runDoctorArchiveHistory,
|
|
5761
6305
|
enrichDescriptions
|
|
5762
6306
|
};
|