@kaddo/mcp 3.120.0 → 3.121.1

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.
Files changed (2) hide show
  1. package/dist/index.js +1358 -187
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -389,7 +389,7 @@ function join(...parts) {
389
389
  }
390
390
 
391
391
  // ../cli/src/core/impact-report.ts
392
- import matter11 from "gray-matter";
392
+ import matter12 from "gray-matter";
393
393
 
394
394
  // ../cli/src/core/config.ts
395
395
  import { z } from "zod";
@@ -499,8 +499,8 @@ function loadConfig(dir) {
499
499
  const parsed = configSchema.safeParse(raw ?? {});
500
500
  if (!parsed.success) {
501
501
  const issues = parsed.error.issues.map((i) => {
502
- const path9 = i.path.join(".");
503
- return path9 ? ` - ${path9}: ${i.message}` : ` - ${i.message}`;
502
+ const path10 = i.path.join(".");
503
+ return path10 ? ` - ${path10}: ${i.message}` : ` - ${i.message}`;
504
504
  }).join("\n");
505
505
  throw new ConfigError(`Invalid .kaddo/config.yml:
506
506
  ${issues}`);
@@ -941,11 +941,11 @@ function moduleArtifactCoverage(dir, id) {
941
941
  };
942
942
  }
943
943
  function loadMappedModules(dir) {
944
- const path9 = join(dir, DESCRIPTOR_PATH);
945
- if (!exists(path9)) return [];
944
+ const path10 = join(dir, DESCRIPTOR_PATH);
945
+ if (!exists(path10)) return [];
946
946
  let parsed;
947
947
  try {
948
- parsed = parseYaml4(readFile(path9));
948
+ parsed = parseYaml4(readFile(path10));
949
949
  } catch {
950
950
  return [];
951
951
  }
@@ -2452,7 +2452,7 @@ function sectionParagraph2(md, title) {
2452
2452
  }
2453
2453
  return "";
2454
2454
  }
2455
- function parseCapsule(id, path9, md) {
2455
+ function parseCapsule(id, path10, md) {
2456
2456
  const { data } = matter4(md);
2457
2457
  const updatedAt = data.updated_at ? String(data.updated_at) : void 0;
2458
2458
  let ageDays = null;
@@ -2462,7 +2462,7 @@ function parseCapsule(id, path9, md) {
2462
2462
  }
2463
2463
  return {
2464
2464
  id,
2465
- path: path9,
2465
+ path: path10,
2466
2466
  system: data.system ? String(data.system) : id,
2467
2467
  owner: data.owner ? String(data.owner) : void 0,
2468
2468
  updatedAt,
@@ -2735,6 +2735,252 @@ function parseWorkItemResources(fm) {
2735
2735
  return out;
2736
2736
  }
2737
2737
 
2738
+ // ../cli/src/core/deprecation.ts
2739
+ import matter7 from "gray-matter";
2740
+ var DEPRECATION_ARTIFACT_TYPE = "deprecation-plan";
2741
+ var DEPRECATION_PLANS_DIR = "knowledge/legacy/deprecations";
2742
+ var TARGET_TYPES = [
2743
+ "module",
2744
+ "route",
2745
+ "function",
2746
+ "ui",
2747
+ "job",
2748
+ "database-object",
2749
+ "integration",
2750
+ "config",
2751
+ "other"
2752
+ ];
2753
+ var LIFECYCLE_STATES2 = [
2754
+ "candidate",
2755
+ "usage-validated",
2756
+ "soft-deprecated",
2757
+ "disabled",
2758
+ "removed-devtest",
2759
+ "production-ready",
2760
+ "production-changed",
2761
+ "observing",
2762
+ "completed",
2763
+ "deprecated-retained"
2764
+ ];
2765
+ var ENVIRONMENTS = ["development", "test", "production"];
2766
+ var ENVIRONMENT_STATES = ["active", "soft-deprecated", "disabled", "removed"];
2767
+ var EVIDENCE_SIGNALS = [
2768
+ "static",
2769
+ "runtime",
2770
+ "database",
2771
+ "external_consumers",
2772
+ "human_review"
2773
+ ];
2774
+ function str2(v) {
2775
+ return typeof v === "string" && v.trim() ? v.trim() : null;
2776
+ }
2777
+ function parseWorkItemDeprecationTargets(fm) {
2778
+ const v = fm.deprecation_targets;
2779
+ if (!Array.isArray(v)) return [];
2780
+ const out = [];
2781
+ for (const raw of v) {
2782
+ if (typeof raw === "string") {
2783
+ const s = raw.trim();
2784
+ if (!s) continue;
2785
+ const slash = s.indexOf("/");
2786
+ if (slash > 0) out.push({ plan: s.slice(0, slash).trim(), target: s.slice(slash + 1).trim() });
2787
+ else out.push({ plan: null, target: s });
2788
+ continue;
2789
+ }
2790
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
2791
+ const o2 = raw;
2792
+ const target = str2(o2.target) ?? str2(o2.id);
2793
+ if (!target) continue;
2794
+ out.push({ plan: str2(o2.plan), target });
2795
+ }
2796
+ }
2797
+ return out;
2798
+ }
2799
+ function strArray2(v) {
2800
+ if (Array.isArray(v)) return v.map((x2) => String(x2).trim()).filter(Boolean);
2801
+ if (typeof v === "string" && v.trim()) return [v.trim()];
2802
+ return [];
2803
+ }
2804
+ function isValidTargetType(t) {
2805
+ return TARGET_TYPES.includes(t);
2806
+ }
2807
+ function isValidLifecycleState(s) {
2808
+ return LIFECYCLE_STATES2.includes(s);
2809
+ }
2810
+ function isValidEnvironmentState(s) {
2811
+ return ENVIRONMENT_STATES.includes(s);
2812
+ }
2813
+ function parseAssessment(v) {
2814
+ if (!v || typeof v !== "object" || Array.isArray(v)) {
2815
+ return { unusedConfidence: null, blastRadius: null, reversibility: null };
2816
+ }
2817
+ const o2 = v;
2818
+ return {
2819
+ unusedConfidence: str2(o2.unused_confidence),
2820
+ blastRadius: str2(o2.blast_radius),
2821
+ reversibility: str2(o2.reversibility)
2822
+ };
2823
+ }
2824
+ function parseUsageEvidence(v) {
2825
+ if (!v || typeof v !== "object" || Array.isArray(v)) return {};
2826
+ const out = {};
2827
+ for (const signal of EVIDENCE_SIGNALS) {
2828
+ const raw = v[signal];
2829
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
2830
+ const o2 = raw;
2831
+ out[signal] = {
2832
+ status: str2(o2.status) ?? "unknown",
2833
+ quality: str2(o2.quality) ?? "unknown",
2834
+ detail: str2(o2.detail)
2835
+ };
2836
+ }
2837
+ return out;
2838
+ }
2839
+ function parseSoftDeprecation(v) {
2840
+ if (!v || typeof v !== "object" || Array.isArray(v)) return null;
2841
+ const o2 = v;
2842
+ const windowRaw = o2.observation_window_days;
2843
+ const window = typeof windowRaw === "number" && Number.isFinite(windowRaw) && windowRaw >= 0 ? Math.floor(windowRaw) : null;
2844
+ const exitCriteria = Array.isArray(o2.exit_criteria) ? o2.exit_criteria.map((c) => {
2845
+ if (typeof c === "string") return c.trim() ? { description: c.trim(), met: false } : null;
2846
+ if (c && typeof c === "object" && !Array.isArray(c)) {
2847
+ const co = c;
2848
+ const description = str2(co.description) ?? str2(co.criterion);
2849
+ if (!description) return null;
2850
+ return { description, met: co.met === true };
2851
+ }
2852
+ return null;
2853
+ }).filter((c) => Boolean(c)) : [];
2854
+ return {
2855
+ strategy: str2(o2.strategy),
2856
+ mechanism: str2(o2.mechanism),
2857
+ startedAt: str2(o2.started_at),
2858
+ observationWindowDays: window,
2859
+ exitCriteria
2860
+ };
2861
+ }
2862
+ function parseEnvironments(v) {
2863
+ if (!v || typeof v !== "object" || Array.isArray(v)) return {};
2864
+ const out = {};
2865
+ for (const env of ENVIRONMENTS) {
2866
+ const s = str2(v[env]);
2867
+ if (s) out[env] = s;
2868
+ }
2869
+ return out;
2870
+ }
2871
+ function parseTarget(raw) {
2872
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
2873
+ const o2 = raw;
2874
+ const id = str2(o2.id);
2875
+ if (!id) return null;
2876
+ return {
2877
+ id,
2878
+ type: str2(o2.type) ?? "other",
2879
+ locator: str2(o2.locator),
2880
+ title: str2(o2.title),
2881
+ lifecycle: str2(o2.lifecycle) ?? "candidate",
2882
+ environments: parseEnvironments(o2.environments),
2883
+ assessment: parseAssessment(o2.assessment),
2884
+ usageEvidence: parseUsageEvidence(o2.usage_evidence),
2885
+ dependsOnRemoval: strArray2(o2.depends_on_removal),
2886
+ resource: str2(o2.resource),
2887
+ module: str2(o2.module),
2888
+ softDeprecation: parseSoftDeprecation(o2.soft_deprecation)
2889
+ };
2890
+ }
2891
+ function parseTargets(v) {
2892
+ if (!Array.isArray(v)) return [];
2893
+ const out = [];
2894
+ for (const raw of v) {
2895
+ const t = parseTarget(raw);
2896
+ if (t) out.push(t);
2897
+ }
2898
+ return out;
2899
+ }
2900
+ function lifecycleCountsOf(targets) {
2901
+ const counts = {};
2902
+ for (const t of targets) counts[t.lifecycle] = (counts[t.lifecycle] ?? 0) + 1;
2903
+ return counts;
2904
+ }
2905
+ function purposeFromBody2(body) {
2906
+ const m2 = body.match(/^#{1,2}\s+Purpose\s*\n+([\s\S]*?)(?=\n#{1,2}\s|\s*$)/m);
2907
+ const text3 = m2 ? m2[1].trim() : "";
2908
+ return text3 || null;
2909
+ }
2910
+ function getDeprecationPlans(dir) {
2911
+ return discoverKnowledge(dir).filter((a2) => a2.type === DEPRECATION_ARTIFACT_TYPE).map((a2) => {
2912
+ const fm = a2.rawFrontmatter;
2913
+ const targets = parseTargets(fm.targets);
2914
+ return {
2915
+ id: a2.id || str2(fm.id) || a2.title,
2916
+ title: a2.title || str2(fm.title) || a2.id,
2917
+ path: a2.relPath,
2918
+ status: str2(fm.status),
2919
+ targetCount: targets.length,
2920
+ lifecycleCounts: lifecycleCountsOf(targets)
2921
+ };
2922
+ }).sort((x2, y) => x2.id.localeCompare(y.id));
2923
+ }
2924
+ function getDeprecationPlan(dir, id) {
2925
+ const match = discoverKnowledge(dir).find(
2926
+ (a2) => a2.type === DEPRECATION_ARTIFACT_TYPE && (a2.id === id || a2.title === id)
2927
+ );
2928
+ if (!match) return null;
2929
+ const raw = readFile(match.filePath);
2930
+ const parsed = matter7(raw);
2931
+ const fm = parsed.data ?? {};
2932
+ const body = parsed.content.trim();
2933
+ const targets = parseTargets(fm.targets);
2934
+ return {
2935
+ id: str2(fm.id) ?? match.id,
2936
+ title: str2(fm.title) ?? match.title,
2937
+ path: match.relPath,
2938
+ status: str2(fm.status),
2939
+ targetCount: targets.length,
2940
+ lifecycleCounts: lifecycleCountsOf(targets),
2941
+ purpose: str2(fm.purpose) ?? purposeFromBody2(body),
2942
+ targets,
2943
+ markdownBody: body
2944
+ };
2945
+ }
2946
+ function validateTarget(target) {
2947
+ const findings = [];
2948
+ const at = target.id;
2949
+ if (!isValidTargetType(String(target.type))) {
2950
+ findings.push({ severity: "error", target: at, message: `Unknown target type: ${target.type}` });
2951
+ }
2952
+ if (!isValidLifecycleState(String(target.lifecycle))) {
2953
+ findings.push({ severity: "error", target: at, message: `Unknown lifecycle state: ${target.lifecycle}` });
2954
+ }
2955
+ if (!target.locator) {
2956
+ findings.push({ severity: "warning", target: at, message: "Target has no locator \u2014 it may be ambiguous." });
2957
+ }
2958
+ for (const [env, state] of Object.entries(target.environments)) {
2959
+ if (!isValidEnvironmentState(String(state))) {
2960
+ findings.push({ severity: "error", target: at, message: `Unknown environment state for ${env}: ${state}` });
2961
+ }
2962
+ }
2963
+ const prodActive = target.environments.production === "active";
2964
+ if (target.lifecycle === "completed") {
2965
+ const stillActive = ENVIRONMENTS.filter((e) => target.environments[e] === "active");
2966
+ if (stillActive.length > 0) {
2967
+ findings.push({
2968
+ severity: "error",
2969
+ target: at,
2970
+ message: `Target is 'completed' but still active in: ${stillActive.join(", ")}.`
2971
+ });
2972
+ }
2973
+ }
2974
+ if ((target.lifecycle === "production-changed" || target.lifecycle === "observing") && prodActive) {
2975
+ findings.push({
2976
+ severity: "error",
2977
+ target: at,
2978
+ message: `Target is '${target.lifecycle}' but production is still 'active'.`
2979
+ });
2980
+ }
2981
+ return findings;
2982
+ }
2983
+
2738
2984
  // ../cli/src/core/graph.ts
2739
2985
  var KNOWLEDGE3 = "knowledge";
2740
2986
  var RESOURCE_ROLE_EDGE = {
@@ -2779,9 +3025,9 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
2779
3025
  ];
2780
3026
  const presentLayers = [];
2781
3027
  for (const layer2 of layerDocs) {
2782
- const path9 = layer2.files.map((f3) => `${KNOWLEDGE3}/${f3}`).find((rel) => exists(join(dir, rel)));
2783
- if (path9) {
2784
- addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path: path9 });
3028
+ const path10 = layer2.files.map((f3) => `${KNOWLEDGE3}/${f3}`).find((rel) => exists(join(dir, rel)));
3029
+ if (path10) {
3030
+ addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path: path10 });
2785
3031
  presentLayers.push(layer2.id);
2786
3032
  }
2787
3033
  }
@@ -2839,6 +3085,33 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
2839
3085
  addEdge(modId, resId, "depends_on");
2840
3086
  }
2841
3087
  }
3088
+ for (const summary of getDeprecationPlans(dir)) {
3089
+ const plan = getDeprecationPlan(dir, summary.id);
3090
+ if (!plan) continue;
3091
+ const planId = `deprecation-plan:${plan.id}`;
3092
+ addNode({ id: planId, type: "deprecation-plan", label: `${plan.id} ${plan.title}`.trim(), path: plan.path, status: plan.status ?? void 0 });
3093
+ for (const target of plan.targets) {
3094
+ const targetId = `deprecation-target:${target.id}`;
3095
+ addNode({ id: targetId, type: "deprecation-target", label: target.title ? `${target.id} ${target.title}` : target.id, path: plan.path, status: target.lifecycle });
3096
+ addEdge(planId, targetId, "targets");
3097
+ if (target.module) {
3098
+ const moduleId = `module:${target.module}`;
3099
+ addNode({ id: moduleId, type: "module", label: target.module });
3100
+ addEdge(targetId, moduleId, "targets");
3101
+ }
3102
+ if (target.resource) {
3103
+ const resourceId = `resource:${target.resource}`;
3104
+ const title = resourceTitleById.get(target.resource);
3105
+ addNode({ id: resourceId, type: "project-resource", label: title ? `${target.resource} ${title}` : target.resource });
3106
+ addEdge(targetId, resourceId, "targets");
3107
+ }
3108
+ for (const dependency of target.dependsOnRemoval) {
3109
+ const dependencyId = `deprecation-target:${dependency}`;
3110
+ addNode({ id: dependencyId, type: "deprecation-target", label: dependency });
3111
+ addEdge(targetId, dependencyId, "depends_on_removal");
3112
+ }
3113
+ }
3114
+ }
2842
3115
  for (const wi of selectedWIs) {
2843
3116
  const id = wi.id || wi.title;
2844
3117
  if (!id || !id.trim()) continue;
@@ -2869,6 +3142,11 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
2869
3142
  addNode({ id: resId, type: "project-resource", label: title ? `${rel.id} ${title}`.trim() : rel.id });
2870
3143
  addEdge(wiNodeId, resId, RESOURCE_ROLE_EDGE[rel.role] ?? "uses");
2871
3144
  }
3145
+ for (const ref of parseWorkItemDeprecationTargets(wi.rawFrontmatter)) {
3146
+ const targetId = `deprecation-target:${ref.target}`;
3147
+ addNode({ id: targetId, type: "deprecation-target", label: ref.target });
3148
+ addEdge(wiNodeId, targetId, "targets");
3149
+ }
2872
3150
  for (const dec of wi.decisions) {
2873
3151
  if (!dec || !dec.trim()) continue;
2874
3152
  const adrId = `adr:${dec}`;
@@ -3247,7 +3525,7 @@ function loadGraphHints(dir) {
3247
3525
  }
3248
3526
 
3249
3527
  // ../cli/src/services/installed-skills.ts
3250
- import matter7 from "gray-matter";
3528
+ import matter8 from "gray-matter";
3251
3529
  var SKILLS_DIR = join("knowledge", "skills");
3252
3530
  function discoverInstalledSkills(dir) {
3253
3531
  const base = join(dir, SKILLS_DIR);
@@ -3257,7 +3535,7 @@ function discoverInstalledSkills(dir) {
3257
3535
  const skillFile = join(base, entry, "skill.md");
3258
3536
  if (!isFile(skillFile)) continue;
3259
3537
  try {
3260
- const { data } = matter7(readFile(skillFile));
3538
+ const { data } = matter8(readFile(skillFile));
3261
3539
  if (data.type && String(data.type) !== "skill") continue;
3262
3540
  const id = String(data.id ?? entry);
3263
3541
  out.push({
@@ -4337,13 +4615,13 @@ function meaningful(text3) {
4337
4615
  });
4338
4616
  }
4339
4617
  function readPocSummary(dir) {
4340
- const path9 = join(dir, POC_ARTIFACT_PATH);
4341
- if (!exists(path9)) {
4618
+ const path10 = join(dir, POC_ARTIFACT_PATH);
4619
+ if (!exists(path10)) {
4342
4620
  return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
4343
4621
  }
4344
4622
  let markdown = "";
4345
4623
  try {
4346
- markdown = readFile(path9);
4624
+ markdown = readFile(path10);
4347
4625
  } catch {
4348
4626
  return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
4349
4627
  }
@@ -4362,7 +4640,7 @@ function readPocSummary(dir) {
4362
4640
 
4363
4641
  // ../cli/src/core/poc-report.ts
4364
4642
  import { createHash } from "crypto";
4365
- import matter8 from "gray-matter";
4643
+ import matter9 from "gray-matter";
4366
4644
  var POC_REPORT_PREFIX = "poc-report-v";
4367
4645
  var SECRET_LINE = /(^|\s)(?:[A-Z][A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|API_KEY|PRIVATE_KEY)|(?:token|secret|password|api[_-]?key))\s*[:=]\s*\S+/i;
4368
4646
  var reportName = /^poc-report-v(\d{3,})\.md$/;
@@ -4378,11 +4656,11 @@ function redactSecrets(value) {
4378
4656
  return line.replace(/([:=]\s*)\S+.*/, "$1[redacted]");
4379
4657
  }).join("\n");
4380
4658
  }
4381
- function source(dir, path9, kind) {
4382
- const absolute = join(dir, path9);
4659
+ function source(dir, path10, kind) {
4660
+ const absolute = join(dir, path10);
4383
4661
  if (!exists(absolute)) return null;
4384
4662
  const raw = readFile(absolute);
4385
- return { path: posix(path9), kind, digest: sha(raw), content: redactSecrets(raw) };
4663
+ return { path: posix(path10), kind, digest: sha(raw), content: redactSecrets(raw) };
4386
4664
  }
4387
4665
  function reportFiles(dir) {
4388
4666
  const delivery = join(dir, "knowledge", "delivery");
@@ -4390,7 +4668,7 @@ function reportFiles(dir) {
4390
4668
  const match = name.match(reportName);
4391
4669
  if (!match) return [];
4392
4670
  try {
4393
- const parsed = matter8(readFile(join(delivery, name)));
4671
+ const parsed = matter9(readFile(join(delivery, name)));
4394
4672
  const fingerprint = typeof parsed.data.source_fingerprint === "string" ? parsed.data.source_fingerprint : "";
4395
4673
  const manifest = parsed.data.source_manifest;
4396
4674
  const sourceManifest = manifest && typeof manifest === "object" && !Array.isArray(manifest) ? Object.fromEntries(Object.entries(manifest).filter(([, digest]) => typeof digest === "string")) : {};
@@ -4422,9 +4700,9 @@ function buildPocReportContext(dir) {
4422
4700
  };
4423
4701
  }
4424
4702
  const sources = [];
4425
- const add = (path9, kind) => {
4426
- if (sources.some((item2) => item2.path === posix(path9))) return;
4427
- const item = source(dir, path9, kind);
4703
+ const add = (path10, kind) => {
4704
+ if (sources.some((item2) => item2.path === posix(path10))) return;
4705
+ const item = source(dir, path10, kind);
4428
4706
  if (item) sources.push(item);
4429
4707
  };
4430
4708
  add(POC_ARTIFACT_PATH, "poc");
@@ -4452,7 +4730,7 @@ function buildPocReportContext(dir) {
4452
4730
  const sourceFingerprint = sha(sources.map((item) => `${item.path}\0${item.digest}`).join("\n"));
4453
4731
  const status = !latestReport ? "missing" : latestReport.sourceFingerprint === sourceFingerprint ? "current" : "stale";
4454
4732
  const currentManifest = Object.fromEntries(sources.map((item) => [item.path, item.digest]));
4455
- const changesSinceLatest = status === "stale" ? [.../* @__PURE__ */ new Set([...Object.keys(currentManifest), ...Object.keys(latestReport?.sourceManifest ?? {})])].filter((path9) => currentManifest[path9] !== latestReport?.sourceManifest[path9]).sort() : [];
4733
+ const changesSinceLatest = status === "stale" ? [.../* @__PURE__ */ new Set([...Object.keys(currentManifest), ...Object.keys(latestReport?.sourceManifest ?? {})])].filter((path10) => currentManifest[path10] !== latestReport?.sourceManifest[path10]).sort() : [];
4456
4734
  const sourceList = sources.map((item) => `- ${item.path} (${item.kind})`).join("\n") || "- No sources selected.";
4457
4735
  return {
4458
4736
  eligible: true,
@@ -4468,6 +4746,9 @@ function buildPocReportContext(dir) {
4468
4746
  `Canonical conclusion: ${poc.conclusion}. Do not contradict it.`,
4469
4747
  "Use the 15 canonical sections. Mark missing evidence as Not evaluated, Not applicable, or No evidence available.",
4470
4748
  "Distinguish measured, calculated, estimated, and projected values. Never include secret values.",
4749
+ "Choose the representation that best communicates each grounded fact: Markdown tables for structured comparisons, Mermaid for grounded architecture, flows, or relationships, and prose for explanation and interpretation.",
4750
+ "Do not invent data, entities, relationships, priorities, or metrics to create a table or diagram. Do not use ASCII diagrams when Mermaid can represent the same grounded flow.",
4751
+ "In Traceability, use repository-relative Markdown links or paths. Never use file:/// URLs or local absolute filesystem paths.",
4471
4752
  "",
4472
4753
  "Selected sources:",
4473
4754
  sourceList
@@ -4600,10 +4881,10 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
4600
4881
  const q3 = (rel) => analyzeKnowledgeArtifact(dir, rel);
4601
4882
  const resolveAgent = (agent) => {
4602
4883
  const file = agent.endsWith(".md") ? agent : `${agent}.md`;
4603
- const path9 = agentInstallPath(file);
4604
- const installed = isFile(join(dir, path9));
4884
+ const path10 = agentInstallPath(file);
4885
+ const installed = isFile(join(dir, path10));
4605
4886
  const group = agentGroupOf(file);
4606
- return { agentPath: path9, agentInstalled: installed, installCommand: installed ? void 0 : `kaddo add agents --group ${group}` };
4887
+ return { agentPath: path10, agentInstalled: installed, installCommand: installed ? void 0 : `kaddo add agents --group ${group}` };
4607
4888
  };
4608
4889
  const moduleRepo = isModule(config);
4609
4890
  const coreRepo = isCore(config);
@@ -5033,7 +5314,7 @@ function deriveDeliveryCompletedStatus(dir) {
5033
5314
  }
5034
5315
 
5035
5316
  // ../cli/src/core/assets.ts
5036
- import matter9 from "gray-matter";
5317
+ import matter10 from "gray-matter";
5037
5318
 
5038
5319
  // ../cli/src/core/version.ts
5039
5320
  import { fileURLToPath } from "url";
@@ -5734,6 +6015,49 @@ After a POC has a canonical conclusion and Core has prepared its deterministic r
5734
6015
  - Use \`Not evaluated in this POC.\`, \`Not applicable.\`, or \`No evidence available.\` when sources do not support a section.
5735
6016
  - Return a proposal for human review; Core persists it only after confirmation.
5736
6017
 
6018
+ ## Representation selection
6019
+
6020
+ - Use prose for context, interpretation, rationale, findings, and conclusions.
6021
+ - Use a Markdown table when supplied evidence has comparable rows, such as success criteria and
6022
+ outcomes, resources, experiments, measurements, costs, risks, recommendations, or traceability.
6023
+ - Use Mermaid only for architecture, topology, or flow relationships explicitly supported by the
6024
+ supplied sources. Prefer it to an ASCII diagram when both can represent the same grounded flow.
6025
+ - Tables and diagrams are communication tools, not completeness requirements. Do not produce one
6026
+ when the context does not support a useful representation.
6027
+ - Never invent table rows, diagram nodes or edges, statuses, priorities, metrics, or relationships
6028
+ to make a report look more complete.
6029
+ - Preserve measured, calculated, estimated, and projected semantics in every representation.
6030
+ - In Traceability, use repository-relative Markdown links or paths; never emit \`file:///\` URLs or
6031
+ local absolute filesystem paths.
6032
+
6033
+ ## Section representation guide
6034
+
6035
+ | Section | Preferred representation when supported by evidence |
6036
+ |---|---|
6037
+ | Executive Summary | Snapshot table and short prose |
6038
+ | Problem and Objective | Prose |
6039
+ | Hypothesis and Success Criteria | Evaluation matrix |
6040
+ | Scope, Constraints and Non-goals | Compact bullets or table |
6041
+ | Technical Approach and Architecture | Mermaid and prose |
6042
+ | Infrastructure and Project Resources | Table, with optional Mermaid |
6043
+ | Implementation and Relevant Technical Decisions | Table or concise prose |
6044
+ | Experiments and Validation | Validation matrix |
6045
+ | Results and Measurements | Measurement tables |
6046
+ | Cost and Efficiency Analysis | Comparison table |
6047
+ | Observability and Operational Findings | Table or prose |
6048
+ | Findings, Learnings, Limitations and Risks | Structured matrix |
6049
+ | Conclusion | Status and concise prose |
6050
+ | Recommendation and Next Steps | Recommendation matrix only when priority is evidenced |
6051
+ | Traceability and Sources | Relative-link table or list |
6052
+
6053
+ ## Quality checklist
6054
+
6055
+ - Structured evidence uses tables when clearer than prose.
6056
+ - Grounded architecture or flows use Mermaid when useful; no decorative or unsupported diagrams exist.
6057
+ - Tables and Mermaid preserve source grounding and measurement semantics.
6058
+ - Traceability contains repository-portable paths.
6059
+ - The report is scannable before reading the full narrative.
6060
+
5737
6061
  ## Required structure
5738
6062
 
5739
6063
  1. Executive Summary
@@ -8301,7 +8625,7 @@ function canonicalSkills() {
8301
8625
  }
8302
8626
  function versionOf(content) {
8303
8627
  try {
8304
- const v = matter9(content).data.version;
8628
+ const v = matter10(content).data.version;
8305
8629
  return v === void 0 || v === null ? null : String(v);
8306
8630
  } catch {
8307
8631
  return null;
@@ -9141,7 +9465,7 @@ function renderRouteCompact(route) {
9141
9465
  }
9142
9466
 
9143
9467
  // ../cli/src/core/metadata-health.ts
9144
- import matter10 from "gray-matter";
9468
+ import matter11 from "gray-matter";
9145
9469
  var KNOWLEDGE_FILES2 = [
9146
9470
  "knowledge/business/business.md",
9147
9471
  "knowledge/product/product.md",
@@ -9161,7 +9485,7 @@ function analyzeMetadataHealth(dir) {
9161
9485
  let data;
9162
9486
  try {
9163
9487
  const raw = readFile(p);
9164
- data = matter10(raw).data;
9488
+ data = matter11(raw).data;
9165
9489
  } catch {
9166
9490
  continue;
9167
9491
  }
@@ -9193,7 +9517,7 @@ function analyzeMetadataHealth(dir) {
9193
9517
  if (isFile(p) && entry.endsWith(".md")) {
9194
9518
  try {
9195
9519
  const raw = readFile(p);
9196
- const fm = matter10(raw).data;
9520
+ const fm = matter11(raw).data;
9197
9521
  if (fm.status === "done") {
9198
9522
  const rel = p.replace(dir + "/", "").replace(dir + "\\", "").replace(/\\/g, "/");
9199
9523
  findings.push({
@@ -10061,7 +10385,7 @@ function buildGuardHistory(dir) {
10061
10385
  const d3 = hotspotDir(t.code_path);
10062
10386
  hotspotMap.set(d3, (hotspotMap.get(d3) ?? 0) + 1);
10063
10387
  }
10064
- const hotspots = [...hotspotMap.entries()].map(([path9, warnings]) => ({ path: path9, warnings })).sort((a2, b2) => b2.warnings - a2.warnings);
10388
+ const hotspots = [...hotspotMap.entries()].map(([path10, warnings]) => ({ path: path10, warnings })).sort((a2, b2) => b2.warnings - a2.warnings);
10065
10389
  return {
10066
10390
  available: true,
10067
10391
  total_runs: sorted.length,
@@ -10148,7 +10472,7 @@ function buildImpactReport(dir, opts = {}, now = /* @__PURE__ */ new Date()) {
10148
10472
  }
10149
10473
  }
10150
10474
  try {
10151
- const body = matter11(readFile(wi.filePath)).content;
10475
+ const body = matter12(readFile(wi.filePath)).content;
10152
10476
  if (hasSection(body, /acceptance|criterios de aceptaci/i)) withAcceptance++;
10153
10477
  else gaps.missing_acceptance_criteria.push(gapItem(wi, "Add an `## Acceptance Criteria` section."));
10154
10478
  if (hasSection(body, /definition of done|^#{1,6}\s*dod\b|definici[oó]n de (terminado|hecho)/i)) withDoD++;
@@ -10861,7 +11185,7 @@ function serializeDriftJson(r2) {
10861
11185
  }
10862
11186
 
10863
11187
  // src/workitems.ts
10864
- import matter12 from "gray-matter";
11188
+ import matter13 from "gray-matter";
10865
11189
  var VALID_SOURCES2 = /* @__PURE__ */ new Set(["manual", "roadmap", "jira", "github", "notion", "xlsx", "csv", "api", "external", "unknown"]);
10866
11190
  function optStr3(v) {
10867
11191
  return typeof v === "string" && v.trim() ? v.trim() : void 0;
@@ -10884,7 +11208,7 @@ function parseSource(data) {
10884
11208
  return { type: "unknown", inferred: true };
10885
11209
  }
10886
11210
  var ACTIVE_STATES2 = /* @__PURE__ */ new Set(["draft", "ready", "in-progress", "blocked"]);
10887
- function strArray2(v) {
11211
+ function strArray3(v) {
10888
11212
  return Array.isArray(v) ? v.map((x2) => String(x2)).filter(Boolean) : [];
10889
11213
  }
10890
11214
  function lifecycleOf(data, relPath) {
@@ -10952,15 +11276,15 @@ function listWorkItems(root) {
10952
11276
  if (raw === null) continue;
10953
11277
  let parsed;
10954
11278
  try {
10955
- parsed = matter12(raw);
11279
+ parsed = matter13(raw);
10956
11280
  } catch {
10957
11281
  continue;
10958
11282
  }
10959
11283
  const data = parsed.data;
10960
11284
  if (!data.type) continue;
10961
11285
  const status = lifecycleOf(data, rel);
10962
- const code = strArray2(data.code);
10963
- const domains = strArray2(data.domains);
11286
+ const code = strArray3(data.code);
11287
+ const domains = strArray3(data.domains);
10964
11288
  const source3 = parseSource(data);
10965
11289
  const readiness = status === "draft" ? assessReadiness(data, parsed.content, code, domains, source3) : void 0;
10966
11290
  items.push({
@@ -10972,9 +11296,9 @@ function listWorkItems(root) {
10972
11296
  path: rel,
10973
11297
  summary: String(data.summary ?? "") || firstParagraph2(parsed.content),
10974
11298
  code,
10975
- capabilities: strArray2(data.capabilities),
10976
- decisions: strArray2(data.decisions),
10977
- capsules: strArray2(data.capsules),
11299
+ capabilities: strArray3(data.capabilities),
11300
+ decisions: strArray3(data.decisions),
11301
+ capsules: strArray3(data.capsules),
10978
11302
  source: source3,
10979
11303
  domains,
10980
11304
  readiness,
@@ -11451,14 +11775,14 @@ function getPrompt(root, name) {
11451
11775
  }
11452
11776
 
11453
11777
  // src/tools.ts
11454
- import matter16 from "gray-matter";
11778
+ import matter20 from "gray-matter";
11455
11779
 
11456
11780
  // ../cli/dist/core.js
11457
11781
  import fs3 from "fs";
11458
11782
  import path3 from "path";
11459
11783
  import { z as z3 } from "zod";
11460
11784
  import { parse as parseYaml10, stringify as stringifyYaml5 } from "yaml";
11461
- import matter13 from "gray-matter";
11785
+ import matter14 from "gray-matter";
11462
11786
  import matter22 from "gray-matter";
11463
11787
  import { parse as parseYaml62, stringify as stringifyYaml42 } from "yaml";
11464
11788
  import { parse as parseYaml22 } from "yaml";
@@ -11468,28 +11792,31 @@ import { parse as parseYaml42, stringify as stringifyYaml22 } from "yaml";
11468
11792
  import matter32 from "gray-matter";
11469
11793
  import matter42 from "gray-matter";
11470
11794
  import matter52 from "gray-matter";
11795
+ import matter62 from "gray-matter";
11471
11796
  import { parse as parseYaml72 } from "yaml";
11472
11797
  import { createHash as createHash2 } from "crypto";
11473
- import matter62 from "gray-matter";
11474
11798
  import matter72 from "gray-matter";
11799
+ import matter82 from "gray-matter";
11475
11800
  import { fileURLToPath as fileURLToPath2 } from "url";
11476
11801
  import { dirname as dirname2, join as join22, parse as parse2 } from "path";
11477
11802
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
11478
- import matter82 from "gray-matter";
11479
11803
  import matter92 from "gray-matter";
11480
11804
  import matter102 from "gray-matter";
11805
+ import matter112 from "gray-matter";
11481
11806
  import { parse as parseYaml82, stringify as stringifyYaml52 } from "yaml";
11482
11807
  import fs4 from "fs";
11483
11808
  import path4 from "path";
11484
11809
  import crypto2 from "crypto";
11485
- import matter112 from "gray-matter";
11810
+ import matter122 from "gray-matter";
11486
11811
  import crypto3 from "crypto";
11487
11812
  import fs5 from "fs";
11488
- import matter122 from "gray-matter";
11489
11813
  import matter132 from "gray-matter";
11814
+ import matter142 from "gray-matter";
11490
11815
  import { parse as parseYaml92, stringify as stringifyYaml6 } from "yaml";
11491
11816
  import { parse as parseYaml102, stringify as stringifyYaml7 } from "yaml";
11492
- import matter14 from "gray-matter";
11817
+ import matter15 from "gray-matter";
11818
+ import matter16 from "gray-matter";
11819
+ import matter17 from "gray-matter";
11493
11820
  function exists2(p) {
11494
11821
  return fs3.existsSync(p);
11495
11822
  }
@@ -11608,8 +11935,8 @@ function loadConfig2(dir) {
11608
11935
  const parsed = configSchema2.safeParse(raw ?? {});
11609
11936
  if (!parsed.success) {
11610
11937
  const issues = parsed.error.issues.map((i) => {
11611
- const path52 = i.path.join(".");
11612
- return path52 ? ` - ${path52}: ${i.message}` : ` - ${i.message}`;
11938
+ const path62 = i.path.join(".");
11939
+ return path62 ? ` - ${path62}: ${i.message}` : ` - ${i.message}`;
11613
11940
  }).join("\n");
11614
11941
  throw new ConfigError2(`Invalid .kaddo/config.yml:
11615
11942
  ${issues}`);
@@ -11665,7 +11992,7 @@ function parseImpactAnalysis2(data) {
11665
11992
  }
11666
11993
  function parseArtifact2(filePath, raw) {
11667
11994
  try {
11668
- const { data } = matter13(raw);
11995
+ const { data } = matter14(raw);
11669
11996
  const globs = Array.isArray(data.code) ? data.code.filter(Boolean) : [];
11670
11997
  return {
11671
11998
  filePath,
@@ -11718,7 +12045,7 @@ function readArtifacts2(archDir) {
11718
12045
  walkDir(archDir);
11719
12046
  return artifacts;
11720
12047
  }
11721
- var LIFECYCLE_STATES2 = [
12048
+ var LIFECYCLE_STATES3 = [
11722
12049
  "draft",
11723
12050
  "ready",
11724
12051
  "in-progress",
@@ -11733,7 +12060,7 @@ var LEGACY_STATUS_MAP2 = {
11733
12060
  canceled: "archived",
11734
12061
  "in-progress": "in-progress"
11735
12062
  };
11736
- var STATE_SET2 = new Set(LIFECYCLE_STATES2);
12063
+ var STATE_SET2 = new Set(LIFECYCLE_STATES3);
11737
12064
  function isLifecycleState2(s) {
11738
12065
  return STATE_SET2.has(s);
11739
12066
  }
@@ -11755,7 +12082,7 @@ function lifecycleStateOf2(item) {
11755
12082
  return "ready";
11756
12083
  }
11757
12084
  function emptyLifecycleCounts2() {
11758
- return Object.fromEntries(LIFECYCLE_STATES2.map((s) => [s, 0]));
12085
+ return Object.fromEntries(LIFECYCLE_STATES3.map((s) => [s, 0]));
11759
12086
  }
11760
12087
  var ARCH_DIR3 = "knowledge";
11761
12088
  function toPosix7(p) {
@@ -11813,11 +12140,11 @@ function moduleArtifactCoverage2(dir, id) {
11813
12140
  };
11814
12141
  }
11815
12142
  function loadMappedModules2(dir) {
11816
- const path52 = join3(dir, DESCRIPTOR_PATH2);
11817
- if (!exists2(path52)) return [];
12143
+ const path62 = join3(dir, DESCRIPTOR_PATH2);
12144
+ if (!exists2(path62)) return [];
11818
12145
  let parsed;
11819
12146
  try {
11820
- parsed = parseYaml32(readFile2(path52));
12147
+ parsed = parseYaml32(readFile2(path62));
11821
12148
  } catch {
11822
12149
  return [];
11823
12150
  }
@@ -13220,10 +13547,10 @@ function suggestInitiativesForExternalItem(dir, integration, externalId) {
13220
13547
  );
13221
13548
  }
13222
13549
  var RESOURCE_ARTIFACT_TYPE2 = "project-resource";
13223
- function str2(v) {
13550
+ function str3(v) {
13224
13551
  return typeof v === "string" && v.trim() ? v.trim() : null;
13225
13552
  }
13226
- function strArray3(v) {
13553
+ function strArray4(v) {
13227
13554
  if (Array.isArray(v)) return v.map((x2) => String(x2).trim()).filter(Boolean);
13228
13555
  if (typeof v === "string" && v.trim()) return [v.trim()];
13229
13556
  return [];
@@ -13234,16 +13561,16 @@ function parseInterfaces2(v) {
13234
13561
  for (const raw of v) {
13235
13562
  if (!raw || typeof raw !== "object") continue;
13236
13563
  const o2 = raw;
13237
- const type = str2(o2.type);
13564
+ const type = str3(o2.type);
13238
13565
  if (!type) continue;
13239
13566
  out.push({
13240
13567
  type,
13241
- tool: str2(o2.tool),
13242
- provider: str2(o2.provider),
13243
- purpose: str2(o2.purpose),
13244
- operations: strArray3(o2.operations),
13245
- environments: strArray3(o2.environments),
13246
- constraints: str2(o2.constraints)
13568
+ tool: str3(o2.tool),
13569
+ provider: str3(o2.provider),
13570
+ purpose: str3(o2.purpose),
13571
+ operations: strArray4(o2.operations),
13572
+ environments: strArray4(o2.environments),
13573
+ constraints: str3(o2.constraints)
13247
13574
  });
13248
13575
  }
13249
13576
  return out;
@@ -13252,7 +13579,7 @@ function parseBoundaries2(v) {
13252
13579
  if (!v || typeof v !== "object" || Array.isArray(v)) return {};
13253
13580
  const out = {};
13254
13581
  for (const [env, val] of Object.entries(v)) {
13255
- const list22 = strArray3(val);
13582
+ const list22 = strArray4(val);
13256
13583
  if (list22.length > 0) out[env] = list22;
13257
13584
  }
13258
13585
  return out;
@@ -13260,17 +13587,17 @@ function parseBoundaries2(v) {
13260
13587
  function parseAuth2(v) {
13261
13588
  if (!v || typeof v !== "object" || Array.isArray(v)) return { mode: null, refs: [] };
13262
13589
  const o2 = v;
13263
- const refs = strArray3(o2.refs).filter((r2) => !r2.includes("=") && /^[A-Za-z0-9_.-]+$/.test(r2));
13264
- return { mode: str2(o2.mode), refs };
13590
+ const refs = strArray4(o2.refs).filter((r2) => !r2.includes("=") && /^[A-Za-z0-9_.-]+$/.test(r2));
13591
+ return { mode: str3(o2.mode), refs };
13265
13592
  }
13266
13593
  function parseScope2(v) {
13267
13594
  if (!v || typeof v !== "object" || Array.isArray(v)) return null;
13268
13595
  const o2 = v;
13269
- const type = str2(o2.type);
13596
+ const type = str3(o2.type);
13270
13597
  if (!type) return null;
13271
- return { type, module: str2(o2.module) };
13598
+ return { type, module: str3(o2.module) };
13272
13599
  }
13273
- function purposeFromBody2(body) {
13600
+ function purposeFromBody3(body) {
13274
13601
  const m2 = body.match(/^#{1,2}\s+Purpose\s*\n+([\s\S]*?)(?=\n#{1,2}\s|\s*$)/m);
13275
13602
  const text22 = m2 ? m2[1].trim() : "";
13276
13603
  return text22 || null;
@@ -13279,17 +13606,17 @@ function toDetail2(filePath, relPath, raw) {
13279
13606
  const parsed = matter42(raw);
13280
13607
  const fm = parsed.data ?? {};
13281
13608
  const body = parsed.content.trim();
13282
- const id = str2(fm.id) ?? "";
13609
+ const id = str3(fm.id) ?? "";
13283
13610
  return {
13284
13611
  id,
13285
- title: str2(fm.title) ?? id,
13286
- resourceType: str2(fm.resource_type),
13287
- provider: str2(fm.provider),
13288
- environments: strArray3(fm.environments),
13612
+ title: str3(fm.title) ?? id,
13613
+ resourceType: str3(fm.resource_type),
13614
+ provider: str3(fm.provider),
13615
+ environments: strArray4(fm.environments),
13289
13616
  scope: parseScope2(fm.scope),
13290
- modules: strArray3(fm.modules),
13617
+ modules: strArray4(fm.modules),
13291
13618
  path: relPath,
13292
- purpose: str2(fm.purpose) ?? purposeFromBody2(body),
13619
+ purpose: str3(fm.purpose) ?? purposeFromBody3(body),
13293
13620
  interfaces: parseInterfaces2(fm.access_interfaces),
13294
13621
  boundaries: parseBoundaries2(fm.access_boundaries),
13295
13622
  ...(() => {
@@ -13303,13 +13630,13 @@ function getResources2(dir) {
13303
13630
  return discoverKnowledge2(dir).filter((a2) => a2.type === RESOURCE_ARTIFACT_TYPE2).map((a2) => {
13304
13631
  const fm = a2.rawFrontmatter;
13305
13632
  return {
13306
- id: a2.id || str2(fm.id) || a2.title,
13307
- title: a2.title || str2(fm.title) || a2.id,
13308
- resourceType: str2(fm.resource_type),
13309
- provider: str2(fm.provider),
13310
- environments: strArray3(fm.environments),
13633
+ id: a2.id || str3(fm.id) || a2.title,
13634
+ title: a2.title || str3(fm.title) || a2.id,
13635
+ resourceType: str3(fm.resource_type),
13636
+ provider: str3(fm.provider),
13637
+ environments: strArray4(fm.environments),
13311
13638
  scope: parseScope2(fm.scope),
13312
- modules: strArray3(fm.modules),
13639
+ modules: strArray4(fm.modules),
13313
13640
  path: a2.relPath
13314
13641
  };
13315
13642
  }).sort((x2, y) => x2.id.localeCompare(y.id));
@@ -13329,12 +13656,184 @@ function parseWorkItemResources2(fm) {
13329
13656
  for (const raw of v) {
13330
13657
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
13331
13658
  const o2 = raw;
13332
- const id = str2(o2.id);
13659
+ const id = str3(o2.id);
13333
13660
  if (!id) continue;
13334
- out.push({ id, role: str2(o2.role) ?? "affected" });
13661
+ out.push({ id, role: str3(o2.role) ?? "affected" });
13335
13662
  }
13336
13663
  return out;
13337
13664
  }
13665
+ var DEPRECATION_ARTIFACT_TYPE2 = "deprecation-plan";
13666
+ var ENVIRONMENTS2 = ["development", "test", "production"];
13667
+ var EVIDENCE_SIGNALS2 = [
13668
+ "static",
13669
+ "runtime",
13670
+ "database",
13671
+ "external_consumers",
13672
+ "human_review"
13673
+ ];
13674
+ function str22(v) {
13675
+ return typeof v === "string" && v.trim() ? v.trim() : null;
13676
+ }
13677
+ function parseWorkItemDeprecationTargets2(fm) {
13678
+ const v = fm.deprecation_targets;
13679
+ if (!Array.isArray(v)) return [];
13680
+ const out = [];
13681
+ for (const raw of v) {
13682
+ if (typeof raw === "string") {
13683
+ const s = raw.trim();
13684
+ if (!s) continue;
13685
+ const slash = s.indexOf("/");
13686
+ if (slash > 0) out.push({ plan: s.slice(0, slash).trim(), target: s.slice(slash + 1).trim() });
13687
+ else out.push({ plan: null, target: s });
13688
+ continue;
13689
+ }
13690
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
13691
+ const o2 = raw;
13692
+ const target = str22(o2.target) ?? str22(o2.id);
13693
+ if (!target) continue;
13694
+ out.push({ plan: str22(o2.plan), target });
13695
+ }
13696
+ }
13697
+ return out;
13698
+ }
13699
+ function strArray22(v) {
13700
+ if (Array.isArray(v)) return v.map((x2) => String(x2).trim()).filter(Boolean);
13701
+ if (typeof v === "string" && v.trim()) return [v.trim()];
13702
+ return [];
13703
+ }
13704
+ function parseAssessment2(v) {
13705
+ if (!v || typeof v !== "object" || Array.isArray(v)) {
13706
+ return { unusedConfidence: null, blastRadius: null, reversibility: null };
13707
+ }
13708
+ const o2 = v;
13709
+ return {
13710
+ unusedConfidence: str22(o2.unused_confidence),
13711
+ blastRadius: str22(o2.blast_radius),
13712
+ reversibility: str22(o2.reversibility)
13713
+ };
13714
+ }
13715
+ function parseUsageEvidence2(v) {
13716
+ if (!v || typeof v !== "object" || Array.isArray(v)) return {};
13717
+ const out = {};
13718
+ for (const signal of EVIDENCE_SIGNALS2) {
13719
+ const raw = v[signal];
13720
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
13721
+ const o2 = raw;
13722
+ out[signal] = {
13723
+ status: str22(o2.status) ?? "unknown",
13724
+ quality: str22(o2.quality) ?? "unknown",
13725
+ detail: str22(o2.detail)
13726
+ };
13727
+ }
13728
+ return out;
13729
+ }
13730
+ function parseSoftDeprecation2(v) {
13731
+ if (!v || typeof v !== "object" || Array.isArray(v)) return null;
13732
+ const o2 = v;
13733
+ const windowRaw = o2.observation_window_days;
13734
+ const window = typeof windowRaw === "number" && Number.isFinite(windowRaw) && windowRaw >= 0 ? Math.floor(windowRaw) : null;
13735
+ const exitCriteria = Array.isArray(o2.exit_criteria) ? o2.exit_criteria.map((c) => {
13736
+ if (typeof c === "string") return c.trim() ? { description: c.trim(), met: false } : null;
13737
+ if (c && typeof c === "object" && !Array.isArray(c)) {
13738
+ const co = c;
13739
+ const description = str22(co.description) ?? str22(co.criterion);
13740
+ if (!description) return null;
13741
+ return { description, met: co.met === true };
13742
+ }
13743
+ return null;
13744
+ }).filter((c) => Boolean(c)) : [];
13745
+ return {
13746
+ strategy: str22(o2.strategy),
13747
+ mechanism: str22(o2.mechanism),
13748
+ startedAt: str22(o2.started_at),
13749
+ observationWindowDays: window,
13750
+ exitCriteria
13751
+ };
13752
+ }
13753
+ function parseEnvironments2(v) {
13754
+ if (!v || typeof v !== "object" || Array.isArray(v)) return {};
13755
+ const out = {};
13756
+ for (const env of ENVIRONMENTS2) {
13757
+ const s = str22(v[env]);
13758
+ if (s) out[env] = s;
13759
+ }
13760
+ return out;
13761
+ }
13762
+ function parseTarget2(raw) {
13763
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
13764
+ const o2 = raw;
13765
+ const id = str22(o2.id);
13766
+ if (!id) return null;
13767
+ return {
13768
+ id,
13769
+ type: str22(o2.type) ?? "other",
13770
+ locator: str22(o2.locator),
13771
+ title: str22(o2.title),
13772
+ lifecycle: str22(o2.lifecycle) ?? "candidate",
13773
+ environments: parseEnvironments2(o2.environments),
13774
+ assessment: parseAssessment2(o2.assessment),
13775
+ usageEvidence: parseUsageEvidence2(o2.usage_evidence),
13776
+ dependsOnRemoval: strArray22(o2.depends_on_removal),
13777
+ resource: str22(o2.resource),
13778
+ module: str22(o2.module),
13779
+ softDeprecation: parseSoftDeprecation2(o2.soft_deprecation)
13780
+ };
13781
+ }
13782
+ function parseTargets2(v) {
13783
+ if (!Array.isArray(v)) return [];
13784
+ const out = [];
13785
+ for (const raw of v) {
13786
+ const t = parseTarget2(raw);
13787
+ if (t) out.push(t);
13788
+ }
13789
+ return out;
13790
+ }
13791
+ function lifecycleCountsOf2(targets) {
13792
+ const counts = {};
13793
+ for (const t of targets) counts[t.lifecycle] = (counts[t.lifecycle] ?? 0) + 1;
13794
+ return counts;
13795
+ }
13796
+ function purposeFromBody22(body) {
13797
+ const m2 = body.match(/^#{1,2}\s+Purpose\s*\n+([\s\S]*?)(?=\n#{1,2}\s|\s*$)/m);
13798
+ const text22 = m2 ? m2[1].trim() : "";
13799
+ return text22 || null;
13800
+ }
13801
+ function getDeprecationPlans2(dir) {
13802
+ return discoverKnowledge2(dir).filter((a2) => a2.type === DEPRECATION_ARTIFACT_TYPE2).map((a2) => {
13803
+ const fm = a2.rawFrontmatter;
13804
+ const targets = parseTargets2(fm.targets);
13805
+ return {
13806
+ id: a2.id || str22(fm.id) || a2.title,
13807
+ title: a2.title || str22(fm.title) || a2.id,
13808
+ path: a2.relPath,
13809
+ status: str22(fm.status),
13810
+ targetCount: targets.length,
13811
+ lifecycleCounts: lifecycleCountsOf2(targets)
13812
+ };
13813
+ }).sort((x2, y) => x2.id.localeCompare(y.id));
13814
+ }
13815
+ function getDeprecationPlan2(dir, id) {
13816
+ const match = discoverKnowledge2(dir).find(
13817
+ (a2) => a2.type === DEPRECATION_ARTIFACT_TYPE2 && (a2.id === id || a2.title === id)
13818
+ );
13819
+ if (!match) return null;
13820
+ const raw = readFile2(match.filePath);
13821
+ const parsed = matter52(raw);
13822
+ const fm = parsed.data ?? {};
13823
+ const body = parsed.content.trim();
13824
+ const targets = parseTargets2(fm.targets);
13825
+ return {
13826
+ id: str22(fm.id) ?? match.id,
13827
+ title: str22(fm.title) ?? match.title,
13828
+ path: match.relPath,
13829
+ status: str22(fm.status),
13830
+ targetCount: targets.length,
13831
+ lifecycleCounts: lifecycleCountsOf2(targets),
13832
+ purpose: str22(fm.purpose) ?? purposeFromBody22(body),
13833
+ targets,
13834
+ markdownBody: body
13835
+ };
13836
+ }
13338
13837
  var KNOWLEDGE22 = "knowledge";
13339
13838
  var RESOURCE_ROLE_EDGE2 = {
13340
13839
  affected: "affects",
@@ -13378,9 +13877,9 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
13378
13877
  ];
13379
13878
  const presentLayers = [];
13380
13879
  for (const layer2 of layerDocs) {
13381
- const path52 = layer2.files.map((f3) => `${KNOWLEDGE22}/${f3}`).find((rel) => exists2(join3(dir, rel)));
13382
- if (path52) {
13383
- addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path: path52 });
13880
+ const path62 = layer2.files.map((f3) => `${KNOWLEDGE22}/${f3}`).find((rel) => exists2(join3(dir, rel)));
13881
+ if (path62) {
13882
+ addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path: path62 });
13384
13883
  presentLayers.push(layer2.id);
13385
13884
  }
13386
13885
  }
@@ -13438,6 +13937,33 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
13438
13937
  addEdge(modId, resId, "depends_on");
13439
13938
  }
13440
13939
  }
13940
+ for (const summary of getDeprecationPlans2(dir)) {
13941
+ const plan = getDeprecationPlan2(dir, summary.id);
13942
+ if (!plan) continue;
13943
+ const planId = `deprecation-plan:${plan.id}`;
13944
+ addNode({ id: planId, type: "deprecation-plan", label: `${plan.id} ${plan.title}`.trim(), path: plan.path, status: plan.status ?? void 0 });
13945
+ for (const target of plan.targets) {
13946
+ const targetId = `deprecation-target:${target.id}`;
13947
+ addNode({ id: targetId, type: "deprecation-target", label: target.title ? `${target.id} ${target.title}` : target.id, path: plan.path, status: target.lifecycle });
13948
+ addEdge(planId, targetId, "targets");
13949
+ if (target.module) {
13950
+ const moduleId = `module:${target.module}`;
13951
+ addNode({ id: moduleId, type: "module", label: target.module });
13952
+ addEdge(targetId, moduleId, "targets");
13953
+ }
13954
+ if (target.resource) {
13955
+ const resourceId = `resource:${target.resource}`;
13956
+ const title = resourceTitleById.get(target.resource);
13957
+ addNode({ id: resourceId, type: "project-resource", label: title ? `${target.resource} ${title}` : target.resource });
13958
+ addEdge(targetId, resourceId, "targets");
13959
+ }
13960
+ for (const dependency of target.dependsOnRemoval) {
13961
+ const dependencyId = `deprecation-target:${dependency}`;
13962
+ addNode({ id: dependencyId, type: "deprecation-target", label: dependency });
13963
+ addEdge(targetId, dependencyId, "depends_on_removal");
13964
+ }
13965
+ }
13966
+ }
13441
13967
  for (const wi of selectedWIs) {
13442
13968
  const id = wi.id || wi.title;
13443
13969
  if (!id || !id.trim()) continue;
@@ -13468,6 +13994,11 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
13468
13994
  addNode({ id: resId, type: "project-resource", label: title ? `${rel.id} ${title}`.trim() : rel.id });
13469
13995
  addEdge(wiNodeId, resId, RESOURCE_ROLE_EDGE2[rel.role] ?? "uses");
13470
13996
  }
13997
+ for (const ref of parseWorkItemDeprecationTargets2(wi.rawFrontmatter)) {
13998
+ const targetId = `deprecation-target:${ref.target}`;
13999
+ addNode({ id: targetId, type: "deprecation-target", label: ref.target });
14000
+ addEdge(wiNodeId, targetId, "targets");
14001
+ }
13471
14002
  for (const dec of wi.decisions) {
13472
14003
  if (!dec || !dec.trim()) continue;
13473
14004
  const adrId = `adr:${dec}`;
@@ -13827,13 +14358,13 @@ function meaningful2(text22) {
13827
14358
  });
13828
14359
  }
13829
14360
  function readPocSummary2(dir) {
13830
- const path52 = join3(dir, POC_ARTIFACT_PATH2);
13831
- if (!exists2(path52)) {
14361
+ const path62 = join3(dir, POC_ARTIFACT_PATH2);
14362
+ if (!exists2(path62)) {
13832
14363
  return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
13833
14364
  }
13834
14365
  let markdown = "";
13835
14366
  try {
13836
- markdown = readFile2(path52);
14367
+ markdown = readFile2(path62);
13837
14368
  } catch {
13838
14369
  return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
13839
14370
  }
@@ -13864,11 +14395,11 @@ function redactSecrets2(value) {
13864
14395
  return line.replace(/([:=]\s*)\S+.*/, "$1[redacted]");
13865
14396
  }).join("\n");
13866
14397
  }
13867
- function source2(dir, path52, kind) {
13868
- const absolute = join3(dir, path52);
14398
+ function source2(dir, path62, kind) {
14399
+ const absolute = join3(dir, path62);
13869
14400
  if (!exists2(absolute)) return null;
13870
14401
  const raw = readFile2(absolute);
13871
- return { path: posix2(path52), kind, digest: sha2(raw), content: redactSecrets2(raw) };
14402
+ return { path: posix2(path62), kind, digest: sha2(raw), content: redactSecrets2(raw) };
13872
14403
  }
13873
14404
  function reportFiles2(dir) {
13874
14405
  const delivery = join3(dir, "knowledge", "delivery");
@@ -13876,7 +14407,7 @@ function reportFiles2(dir) {
13876
14407
  const match = name.match(reportName2);
13877
14408
  if (!match) return [];
13878
14409
  try {
13879
- const parsed = matter62(readFile2(join3(delivery, name)));
14410
+ const parsed = matter72(readFile2(join3(delivery, name)));
13880
14411
  const fingerprint = typeof parsed.data.source_fingerprint === "string" ? parsed.data.source_fingerprint : "";
13881
14412
  const manifest = parsed.data.source_manifest;
13882
14413
  const sourceManifest = manifest && typeof manifest === "object" && !Array.isArray(manifest) ? Object.fromEntries(Object.entries(manifest).filter(([, digest]) => typeof digest === "string")) : {};
@@ -13908,9 +14439,9 @@ function buildPocReportContext2(dir) {
13908
14439
  };
13909
14440
  }
13910
14441
  const sources = [];
13911
- const add = (path52, kind) => {
13912
- if (sources.some((item2) => item2.path === posix2(path52))) return;
13913
- const item = source2(dir, path52, kind);
14442
+ const add = (path62, kind) => {
14443
+ if (sources.some((item2) => item2.path === posix2(path62))) return;
14444
+ const item = source2(dir, path62, kind);
13914
14445
  if (item) sources.push(item);
13915
14446
  };
13916
14447
  add(POC_ARTIFACT_PATH2, "poc");
@@ -13938,7 +14469,7 @@ function buildPocReportContext2(dir) {
13938
14469
  const sourceFingerprint = sha2(sources.map((item) => `${item.path}\0${item.digest}`).join("\n"));
13939
14470
  const status = !latestReport ? "missing" : latestReport.sourceFingerprint === sourceFingerprint ? "current" : "stale";
13940
14471
  const currentManifest = Object.fromEntries(sources.map((item) => [item.path, item.digest]));
13941
- const changesSinceLatest = status === "stale" ? [.../* @__PURE__ */ new Set([...Object.keys(currentManifest), ...Object.keys(latestReport?.sourceManifest ?? {})])].filter((path52) => currentManifest[path52] !== latestReport?.sourceManifest[path52]).sort() : [];
14472
+ const changesSinceLatest = status === "stale" ? [.../* @__PURE__ */ new Set([...Object.keys(currentManifest), ...Object.keys(latestReport?.sourceManifest ?? {})])].filter((path62) => currentManifest[path62] !== latestReport?.sourceManifest[path62]).sort() : [];
13942
14473
  const sourceList = sources.map((item) => `- ${item.path} (${item.kind})`).join("\n") || "- No sources selected.";
13943
14474
  return {
13944
14475
  eligible: true,
@@ -13954,6 +14485,9 @@ function buildPocReportContext2(dir) {
13954
14485
  `Canonical conclusion: ${poc.conclusion}. Do not contradict it.`,
13955
14486
  "Use the 15 canonical sections. Mark missing evidence as Not evaluated, Not applicable, or No evidence available.",
13956
14487
  "Distinguish measured, calculated, estimated, and projected values. Never include secret values.",
14488
+ "Choose the representation that best communicates each grounded fact: Markdown tables for structured comparisons, Mermaid for grounded architecture, flows, or relationships, and prose for explanation and interpretation.",
14489
+ "Do not invent data, entities, relationships, priorities, or metrics to create a table or diagram. Do not use ASCII diagrams when Mermaid can represent the same grounded flow.",
14490
+ "In Traceability, use repository-relative Markdown links or paths. Never use file:/// URLs or local absolute filesystem paths.",
13957
14491
  "",
13958
14492
  "Selected sources:",
13959
14493
  sourceList
@@ -13972,7 +14506,7 @@ function canonicalReport(context, proposal) {
13972
14506
  source_manifest: Object.fromEntries(context.sources.map((item) => [item.path, item.digest]))
13973
14507
  };
13974
14508
  if (supersedes) data.supersedes = supersedes;
13975
- return matter62.stringify(redactSecrets2(proposal).trimEnd() + "\n", data);
14509
+ return matter72.stringify(redactSecrets2(proposal).trimEnd() + "\n", data);
13976
14510
  }
13977
14511
  function persistPocReport(dir, proposal, opts = {}) {
13978
14512
  const context = buildPocReportContext2(dir);
@@ -14678,6 +15212,49 @@ After a POC has a canonical conclusion and Core has prepared its deterministic r
14678
15212
  - Use \`Not evaluated in this POC.\`, \`Not applicable.\`, or \`No evidence available.\` when sources do not support a section.
14679
15213
  - Return a proposal for human review; Core persists it only after confirmation.
14680
15214
 
15215
+ ## Representation selection
15216
+
15217
+ - Use prose for context, interpretation, rationale, findings, and conclusions.
15218
+ - Use a Markdown table when supplied evidence has comparable rows, such as success criteria and
15219
+ outcomes, resources, experiments, measurements, costs, risks, recommendations, or traceability.
15220
+ - Use Mermaid only for architecture, topology, or flow relationships explicitly supported by the
15221
+ supplied sources. Prefer it to an ASCII diagram when both can represent the same grounded flow.
15222
+ - Tables and diagrams are communication tools, not completeness requirements. Do not produce one
15223
+ when the context does not support a useful representation.
15224
+ - Never invent table rows, diagram nodes or edges, statuses, priorities, metrics, or relationships
15225
+ to make a report look more complete.
15226
+ - Preserve measured, calculated, estimated, and projected semantics in every representation.
15227
+ - In Traceability, use repository-relative Markdown links or paths; never emit \`file:///\` URLs or
15228
+ local absolute filesystem paths.
15229
+
15230
+ ## Section representation guide
15231
+
15232
+ | Section | Preferred representation when supported by evidence |
15233
+ |---|---|
15234
+ | Executive Summary | Snapshot table and short prose |
15235
+ | Problem and Objective | Prose |
15236
+ | Hypothesis and Success Criteria | Evaluation matrix |
15237
+ | Scope, Constraints and Non-goals | Compact bullets or table |
15238
+ | Technical Approach and Architecture | Mermaid and prose |
15239
+ | Infrastructure and Project Resources | Table, with optional Mermaid |
15240
+ | Implementation and Relevant Technical Decisions | Table or concise prose |
15241
+ | Experiments and Validation | Validation matrix |
15242
+ | Results and Measurements | Measurement tables |
15243
+ | Cost and Efficiency Analysis | Comparison table |
15244
+ | Observability and Operational Findings | Table or prose |
15245
+ | Findings, Learnings, Limitations and Risks | Structured matrix |
15246
+ | Conclusion | Status and concise prose |
15247
+ | Recommendation and Next Steps | Recommendation matrix only when priority is evidenced |
15248
+ | Traceability and Sources | Relative-link table or list |
15249
+
15250
+ ## Quality checklist
15251
+
15252
+ - Structured evidence uses tables when clearer than prose.
15253
+ - Grounded architecture or flows use Mermaid when useful; no decorative or unsupported diagrams exist.
15254
+ - Tables and Mermaid preserve source grounding and measurement semantics.
15255
+ - Traceability contains repository-portable paths.
15256
+ - The report is scannable before reading the full narrative.
15257
+
14681
15258
  ## Required structure
14682
15259
 
14683
15260
  1. Executive Summary
@@ -17895,6 +18472,8 @@ function getWorkItem(dir, workItemId) {
17895
18472
  markdownBody: body.trim(),
17896
18473
  // External systems this WI relates to (WI-030), separate from affectedModules (code).
17897
18474
  resources: parseWorkItemResources2(fm),
18475
+ // Deprecation Targets this WI retires (WI-050), referenced not copied.
18476
+ deprecationTargets: parseWorkItemDeprecationTargets2(fm),
17898
18477
  ...parseSystemImpact(dir, fm)
17899
18478
  };
17900
18479
  return { ...detail, refinement: computeRefinementStatus(detail) };
@@ -17903,8 +18482,8 @@ function parseGraphReason(v) {
17903
18482
  if (!v || typeof v !== "object") return null;
17904
18483
  const o2 = v;
17905
18484
  const relationship = typeof o2.relationship === "string" && o2.relationship.trim() ? o2.relationship.trim() : null;
17906
- const path52 = Array.isArray(o2.path) ? o2.path.map(String).filter(Boolean) : [];
17907
- return relationship || path52.length ? { relationship, path: path52 } : null;
18485
+ const path62 = Array.isArray(o2.path) ? o2.path.map(String).filter(Boolean) : [];
18486
+ return relationship || path62.length ? { relationship, path: path62 } : null;
17908
18487
  }
17909
18488
  function parseExplain(o2) {
17910
18489
  const refs = Array.isArray(o2.evidence) ? o2.evidence : Array.isArray(o2.evidence_refs) ? o2.evidence_refs : [];
@@ -17959,7 +18538,7 @@ function parseOriginalSnapshot(fm) {
17959
18538
  function readBody(filePath) {
17960
18539
  try {
17961
18540
  const raw = readFile2(filePath);
17962
- return matter102(raw).content;
18541
+ return matter112(raw).content;
17963
18542
  } catch {
17964
18543
  return "";
17965
18544
  }
@@ -18244,7 +18823,7 @@ function ensureH1(body, title) {
18244
18823
  ${trimmed}`;
18245
18824
  }
18246
18825
  function serialize2(data, body) {
18247
- return matter112.stringify(`
18826
+ return matter122.stringify(`
18248
18827
  ${body.trim()}
18249
18828
  `, data);
18250
18829
  }
@@ -18609,6 +19188,61 @@ function buildRefinementHandoff(dir, workItemId) {
18609
19188
  text: lines.join("\n")
18610
19189
  };
18611
19190
  }
19191
+ var DAY_MS = 24 * 60 * 60 * 1e3;
19192
+ function isStrongNoUsage(sig) {
19193
+ return sig.status === "no-usage" && (sig.quality === "observed-fact" || sig.quality === "human-confirmed");
19194
+ }
19195
+ function deriveUnusedConfidence(evidence) {
19196
+ const present = EVIDENCE_SIGNALS2.map((name) => ({ name, sig: evidence[name] })).filter(
19197
+ (e) => Boolean(e.sig)
19198
+ );
19199
+ const usageFound = present.filter((e) => e.sig.status === "usage-found");
19200
+ if (usageFound.length > 0) {
19201
+ return {
19202
+ confidence: "in-use",
19203
+ requiresHumanConfirmation: false,
19204
+ rationale: [`Usage found via: ${usageFound.map((e) => e.name).join(", ")}.`]
19205
+ };
19206
+ }
19207
+ const strong = present.filter((e) => isStrongNoUsage(e.sig));
19208
+ const unknown = present.filter((e) => e.sig.status === "unknown");
19209
+ const notEvaluated = EVIDENCE_SIGNALS2.filter(
19210
+ (name) => !evidence[name] || evidence[name]?.status === "not-evaluated"
19211
+ );
19212
+ const rationale = [];
19213
+ if (strong.length > 0) rationale.push(`No usage observed via: ${strong.map((e) => e.name).join(", ")}.`);
19214
+ if (unknown.length > 0) {
19215
+ rationale.push(`Unknown (looked but could not determine): ${unknown.map((e) => e.name).join(", ")} \u2014 NOT treated as no-usage.`);
19216
+ }
19217
+ if (notEvaluated.length > 0) {
19218
+ rationale.push(`Not evaluated: ${notEvaluated.join(", ")} \u2014 absence of a reference is not evidence of no usage.`);
19219
+ }
19220
+ let confidence;
19221
+ if (strong.length === 0) {
19222
+ confidence = "insufficient-evidence";
19223
+ } else if (unknown.length > 0) {
19224
+ confidence = "medium";
19225
+ } else if (strong.length >= 2) {
19226
+ confidence = "high";
19227
+ } else {
19228
+ confidence = "medium";
19229
+ }
19230
+ return { confidence, requiresHumanConfirmation: true, rationale };
19231
+ }
19232
+ var CANDIDATE_DISCOVERY_GUIDANCE = [
19233
+ "Propose deprecation candidates from multiple sources: static references (code/routes/UI/jobs),",
19234
+ "the System Graph (dependencies and blast radius), Legacy Knowledge (risks/unknowns/modernization",
19235
+ "candidates), Project Resources (databases/external systems and their boundaries), runtime evidence",
19236
+ "when telemetry exists, and database metadata when reachable.",
19237
+ "",
19238
+ "Rules:",
19239
+ "- Discovery is a PROPOSAL, not a materialization: never create or retire anything automatically.",
19240
+ "- Always explain WHY something is a candidate, citing the signal(s) observed.",
19241
+ "- Absence of a reference is NOT evidence of no usage. If a source cannot be evaluated, record it",
19242
+ " as not-evaluated/unknown and require human investigation \u2014 never collapse it to no-usage.",
19243
+ "- A modernization candidate is not automatically a deprecation target.",
19244
+ "- Soft deprecation precedes physical removal; order retirements by dependency, dependents first."
19245
+ ].join("\n");
18612
19246
  var WorkItemNotReadyError = class extends Error {
18613
19247
  constructor(workItemId, actualState) {
18614
19248
  super(`Work Item "${workItemId}" must be in "ready" state to produce an Implementation Handoff, but is "${actualState}".`);
@@ -18639,6 +19273,16 @@ function buildImplementationHandoff(dir, workItemId) {
18639
19273
  const domains = match.domains;
18640
19274
  const scopeConfidence = match.scopeConfidence;
18641
19275
  const wiResources = parseWorkItemResources2(match.rawFrontmatter);
19276
+ const wiDeprecationRefs = parseWorkItemDeprecationTargets2(match.rawFrontmatter);
19277
+ const resolveTarget = (ref) => {
19278
+ const plans = ref.plan ? [getDeprecationPlan2(dir, ref.plan)].filter(Boolean) : getDeprecationPlans2(dir).map((p) => getDeprecationPlan2(dir, p.id));
19279
+ for (const p of plans) {
19280
+ if (!p) continue;
19281
+ const t = p.targets.find((x2) => x2.id.toUpperCase() === ref.target.toUpperCase());
19282
+ if (t) return { planId: p.id, target: t };
19283
+ }
19284
+ return null;
19285
+ };
18642
19286
  const lines = [
18643
19287
  `Implement Work Item ${wiId} \u2014 "${wiTitle}" \u2014 in project "${projectName}" using Kaddo.`,
18644
19288
  "",
@@ -18763,6 +19407,48 @@ function buildImplementationHandoff(dir, workItemId) {
18763
19407
  "installed or connected. Do not execute any interface or migration without explicit human confirmation."
18764
19408
  );
18765
19409
  }
19410
+ if (wiDeprecationRefs.length > 0) {
19411
+ lines.push(
19412
+ "",
19413
+ "--- Relevant Deprecation Targets ---",
19414
+ "",
19415
+ "This Work Item is part of a Controlled Deprecation plan. It RETIRES the targets below. Soft",
19416
+ "deprecation precedes physical removal; in Dev/Test you may remove code/endpoints/dev-test data",
19417
+ "when the resource boundary allows and there is confirmation. A production change needs a Runbook",
19418
+ "and explicit human approval \u2014 never execute a destructive production operation autonomously."
19419
+ );
19420
+ for (const ref of wiDeprecationRefs) {
19421
+ const resolved = resolveTarget(ref);
19422
+ if (!resolved) {
19423
+ lines.push("", `- ${ref.target}${ref.plan ? ` (plan ${ref.plan})` : ""} \u2014 target not found in any Deprecation Plan.`);
19424
+ continue;
19425
+ }
19426
+ const { planId, target: t } = resolved;
19427
+ lines.push("", `- ${t.id} [${t.type}] \u2014 plan ${planId}, lifecycle: ${t.lifecycle}`);
19428
+ if (t.locator) lines.push(` locator: ${t.locator}`);
19429
+ const envs = Object.entries(t.environments).map(([e, s]) => `${e}=${s}`).join(" ");
19430
+ if (envs) lines.push(` environments: ${envs}`);
19431
+ const unused = deriveUnusedConfidence(t.usageEvidence);
19432
+ lines.push(` unused confidence: ${unused.confidence}${unused.requiresHumanConfirmation ? " (human confirmation required)" : ""}`);
19433
+ if (t.assessment.blastRadius) lines.push(` blast radius: ${t.assessment.blastRadius}`);
19434
+ if (t.assessment.reversibility) lines.push(` reversibility: ${t.assessment.reversibility}`);
19435
+ if (t.dependsOnRemoval.length > 0) lines.push(` depends_on_removal: ${t.dependsOnRemoval.join(", ")} (retire these first)`);
19436
+ if (t.resource) {
19437
+ const res = getResource2(dir, t.resource);
19438
+ if (res) {
19439
+ const bounds = Object.entries(res.boundaries).map(([env, ops]) => `${env}: ${ops.join("/")}`).join(" \xB7 ");
19440
+ lines.push(` resource: ${res.id} \u2014 ${res.title}${bounds ? ` (boundaries \u2014 ${bounds})` : ""}`);
19441
+ } else {
19442
+ lines.push(` resource: ${t.resource} \u2014 definition not found under knowledge/tech/resources/.`);
19443
+ }
19444
+ }
19445
+ }
19446
+ lines.push(
19447
+ "",
19448
+ "Record what you actually retired as Evidence. Prepare a rollback for every change; for production,",
19449
+ "the rollback/abort path lives in the Runbook, not here."
19450
+ );
19451
+ }
18766
19452
  lines.push(
18767
19453
  "",
18768
19454
  "--- Implementation Plan ---",
@@ -18784,6 +19470,7 @@ function buildImplementationHandoff(dir, workItemId) {
18784
19470
  lifecycle,
18785
19471
  affectedModules,
18786
19472
  resources: wiResources,
19473
+ deprecationTargets: wiDeprecationRefs,
18787
19474
  domains,
18788
19475
  scopeConfidence,
18789
19476
  hasUnmaterializedDecisions: unmaterialized,
@@ -19035,7 +19722,7 @@ function detectFormat(raw) {
19035
19722
  if (!trimmed) throw new ImportError("EMPTY_INPUT", "Import content is empty.");
19036
19723
  if (trimmed.startsWith("---")) {
19037
19724
  try {
19038
- const { data } = matter122(trimmed);
19725
+ const { data } = matter132(trimmed);
19039
19726
  if (data && typeof data === "object" && Object.keys(data).length > 0) {
19040
19727
  const d3 = data;
19041
19728
  if (d3.type === "work-item" || d3.id && typeof d3.id === "string" && /^WI-\d+/.test(d3.id)) {
@@ -19057,7 +19744,7 @@ function parseContent(raw) {
19057
19744
  let bodyText = trimmed;
19058
19745
  if (format === "kaddo-frontmatter" || format === "markdown-frontmatter") {
19059
19746
  try {
19060
- const parsed = matter122(trimmed);
19747
+ const parsed = matter132(trimmed);
19061
19748
  frontmatterFields = parsed.data ?? {};
19062
19749
  bodyText = parsed.content.trim();
19063
19750
  } catch {
@@ -19154,7 +19841,7 @@ function findDuplicateByHash(dir, hash) {
19154
19841
  const raw = readFile2(item.filePath);
19155
19842
  if (!raw) continue;
19156
19843
  try {
19157
- const { data } = matter122(raw);
19844
+ const { data } = matter132(raw);
19158
19845
  const source22 = data?.source;
19159
19846
  if (source22 && typeof source22 === "object" && !Array.isArray(source22)) {
19160
19847
  const s = source22;
@@ -19663,8 +20350,8 @@ async function handleJiraResponse(res) {
19663
20350
  }
19664
20351
  return await res.json();
19665
20352
  }
19666
- async function jiraFetch(baseUrl, path52, auth, timeoutMs, params) {
19667
- const url = new URL(path52, baseUrl + "/");
20353
+ async function jiraFetch(baseUrl, path62, auth, timeoutMs, params) {
20354
+ const url = new URL(path62, baseUrl + "/");
19668
20355
  if (params) for (const [k2, v] of Object.entries(params)) url.searchParams.set(k2, v);
19669
20356
  const controller = new AbortController();
19670
20357
  const timer = setTimeout(() => controller.abort(), timeoutMs);
@@ -19682,8 +20369,8 @@ async function jiraFetch(baseUrl, path52, auth, timeoutMs, params) {
19682
20369
  clearTimeout(timer);
19683
20370
  }
19684
20371
  }
19685
- async function jiraPost(baseUrl, path52, auth, timeoutMs, body) {
19686
- const url = new URL(path52, baseUrl + "/");
20372
+ async function jiraPost(baseUrl, path62, auth, timeoutMs, body) {
20373
+ const url = new URL(path62, baseUrl + "/");
19687
20374
  const controller = new AbortController();
19688
20375
  const timer = setTimeout(() => controller.abort(), timeoutMs);
19689
20376
  try {
@@ -20049,7 +20736,7 @@ function buildInitiativeContent(id, input) {
20049
20736
  if (input.priority) data.priority = input.priority;
20050
20737
  if (input.source) data.source = input.source;
20051
20738
  if (input.sourceId) data.source_id = input.sourceId;
20052
- return matter14.stringify("\n" + (input.body ?? defaultBody(input.title.trim())), data);
20739
+ return matter15.stringify("\n" + (input.body ?? defaultBody(input.title.trim())), data);
20053
20740
  }
20054
20741
  function createInitiative(dir, input) {
20055
20742
  if (!input.title?.trim()) throw new InitiativeWriteError("Initiative title is required.");
@@ -20073,10 +20760,10 @@ function transitionInitiative(dir, id, to) {
20073
20760
  throw new InitiativeWriteError(`Invalid transition ${ini.status} \u2192 ${to}.`);
20074
20761
  }
20075
20762
  const raw = readFile2(ini.filePath);
20076
- const { data, content } = matter14(raw, {});
20763
+ const { data, content } = matter15(raw, {});
20077
20764
  data.status = to;
20078
20765
  if (to === "completed") data.completed_at = today2();
20079
- const newContent = matter14.stringify(content, data);
20766
+ const newContent = matter15.stringify(content, data);
20080
20767
  writeFile2(ini.filePath, newContent);
20081
20768
  const updated = parseInitiative2(dir, ini.filePath, newContent);
20082
20769
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after transition.");
@@ -20086,7 +20773,7 @@ function addCandidate(dir, id, candidate) {
20086
20773
  const ini = getInitiative(dir, id);
20087
20774
  if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
20088
20775
  const raw = readFile2(ini.filePath);
20089
- const { data, content } = matter14(raw, {});
20776
+ const { data, content } = matter15(raw, {});
20090
20777
  const existing = Array.isArray(data.candidates) ? data.candidates : [];
20091
20778
  const fallbackId = `WI-CANDIDATE-${String(existing.length + 1).padStart(3, "0")}`;
20092
20779
  const normalized = normalizeCandidate(candidate, fallbackId);
@@ -20094,7 +20781,7 @@ function addCandidate(dir, id, candidate) {
20094
20781
  throw new InitiativeWriteError(`Candidate ${normalized.id} already exists in ${id}.`);
20095
20782
  }
20096
20783
  data.candidates = [...existing, candidateToData(normalized)];
20097
- const newContent = matter14.stringify(content, data);
20784
+ const newContent = matter15.stringify(content, data);
20098
20785
  writeFile2(ini.filePath, newContent);
20099
20786
  const updated = parseInitiative2(dir, ini.filePath, newContent);
20100
20787
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding a candidate.");
@@ -20104,7 +20791,7 @@ function updateInitiative(dir, id, patch) {
20104
20791
  const ini = getInitiative(dir, id);
20105
20792
  if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
20106
20793
  const raw = readFile2(ini.filePath);
20107
- const { data, content } = matter14(raw, {});
20794
+ const { data, content } = matter15(raw, {});
20108
20795
  if (patch.title !== void 0) {
20109
20796
  if (!patch.title.trim()) throw new InitiativeWriteError("Initiative title cannot be empty.");
20110
20797
  data.title = patch.title.trim();
@@ -20114,7 +20801,7 @@ function updateInitiative(dir, id, patch) {
20114
20801
  if (patch.knowledgeLevel !== void 0) data.knowledge_level = patch.knowledgeLevel;
20115
20802
  if (patch.domains !== void 0) data.domains = patch.domains;
20116
20803
  if (patch.relatedCapabilities !== void 0) data.related_capabilities = patch.relatedCapabilities;
20117
- const newContent = matter14.stringify(content, data);
20804
+ const newContent = matter15.stringify(content, data);
20118
20805
  writeFile2(ini.filePath, newContent);
20119
20806
  const updated = parseInitiative2(dir, ini.filePath, newContent);
20120
20807
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after update.");
@@ -20132,10 +20819,10 @@ function addExternalLink(dir, id, link) {
20132
20819
  throw new InitiativeWriteError(`External link ${link.integration}:${link.externalId} already exists on ${id}.`);
20133
20820
  }
20134
20821
  const raw = readFile2(ini.filePath);
20135
- const { data, content } = matter14(raw, {});
20822
+ const { data, content } = matter15(raw, {});
20136
20823
  const existing = Array.isArray(data.external_links) ? data.external_links : [];
20137
20824
  data.external_links = [...existing, externalLinkToData(link)];
20138
- const newContent = matter14.stringify(content, data);
20825
+ const newContent = matter15.stringify(content, data);
20139
20826
  writeFile2(ini.filePath, newContent);
20140
20827
  const updated = parseInitiative2(dir, ini.filePath, newContent);
20141
20828
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding an external link.");
@@ -20204,7 +20891,7 @@ function buildWorkItemContent(wiId, iniId, candidate) {
20204
20891
  "_What did we learn? Update after completion._",
20205
20892
  ""
20206
20893
  ].join("\n");
20207
- return matter14.stringify("\n" + body, data);
20894
+ return matter15.stringify("\n" + body, data);
20208
20895
  }
20209
20896
  function materializeCandidate(dir, id, candidateId) {
20210
20897
  const ini = getInitiative(dir, id);
@@ -20221,13 +20908,13 @@ function materializeCandidate(dir, id, candidateId) {
20221
20908
  ensureDir2(join3(dir, DRAFT_DIR));
20222
20909
  writeFile2(join3(dir, DRAFT_DIR, fileName), buildWorkItemContent(wiId, ini.id, candidate));
20223
20910
  const raw = readFile2(ini.filePath);
20224
- const { data, content } = matter14(raw, {});
20911
+ const { data, content } = matter15(raw, {});
20225
20912
  const cands = Array.isArray(data.candidates) ? data.candidates : [];
20226
20913
  for (const c of cands) {
20227
20914
  if (c && typeof c === "object" && String(c.id) === candidateId) c.materialized_as = wiId;
20228
20915
  }
20229
20916
  data.candidates = cands;
20230
- const newContent = matter14.stringify(content, data);
20917
+ const newContent = matter15.stringify(content, data);
20231
20918
  writeFile2(ini.filePath, newContent);
20232
20919
  const updated = parseInitiative2(dir, ini.filePath, newContent);
20233
20920
  if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after materialization.");
@@ -20341,7 +21028,7 @@ function evaluateInitiativeCompletion(dir, ini) {
20341
21028
  // ../cli/src/core/resource-write.ts
20342
21029
  import fs6 from "fs";
20343
21030
  import path5 from "path";
20344
- import matter15 from "gray-matter";
21031
+ import matter18 from "gray-matter";
20345
21032
  var RESOURCES_DIR = "knowledge/tech/resources";
20346
21033
  var ResourceWriteError = class extends Error {
20347
21034
  constructor(code, message) {
@@ -20421,7 +21108,7 @@ function buildFrontmatter(id, input, base = {}) {
20421
21108
  return data;
20422
21109
  }
20423
21110
  function serialize(data, body) {
20424
- return matter15.stringify(`
21111
+ return matter18.stringify(`
20425
21112
  ${body.trim()}
20426
21113
  `, data);
20427
21114
  }
@@ -20461,7 +21148,7 @@ function updateResource(dir, id, input) {
20461
21148
  throw new ResourceWriteError("VALIDATION_FAILED", findings.filter((f3) => f3.level === "error").map((f3) => f3.message).join("; "));
20462
21149
  }
20463
21150
  const raw = readFile(match.filePath);
20464
- const parsed = matter15(raw);
21151
+ const parsed = matter18(raw);
20465
21152
  const base = parsed.data ?? {};
20466
21153
  const data = buildFrontmatter(match.id || id, input, base);
20467
21154
  let body = parsed.content.trim();
@@ -20496,6 +21183,355 @@ function deleteResource(dir, id, opts = {}) {
20496
21183
  return { deleted: true, path: match.relPath };
20497
21184
  }
20498
21185
 
21186
+ // ../cli/src/core/deprecation-observation.ts
21187
+ var DAY_MS2 = 24 * 60 * 60 * 1e3;
21188
+ function addDaysIso(startIso, days) {
21189
+ const start = Date.parse(startIso);
21190
+ if (Number.isNaN(start)) return null;
21191
+ return new Date(start + days * DAY_MS2).toISOString().slice(0, 10);
21192
+ }
21193
+ function observationStatus(target, now = /* @__PURE__ */ new Date()) {
21194
+ const sd = target.softDeprecation;
21195
+ const reasons = [];
21196
+ if (!sd) {
21197
+ return {
21198
+ declared: false,
21199
+ windowElapsed: false,
21200
+ windowEndsAt: null,
21201
+ criteriaTotal: 0,
21202
+ criteriaMet: 0,
21203
+ allCriteriaMet: false,
21204
+ eligibleForRemovalReview: false,
21205
+ reasons: ["No soft-deprecation/observation window declared \u2014 not eligible for removal review."]
21206
+ };
21207
+ }
21208
+ const criteriaTotal = sd.exitCriteria.length;
21209
+ const criteriaMet = sd.exitCriteria.filter((c) => c.met).length;
21210
+ const allCriteriaMet = criteriaTotal > 0 && criteriaMet === criteriaTotal;
21211
+ let windowElapsed = false;
21212
+ let windowEndsAt = null;
21213
+ if (sd.startedAt && sd.observationWindowDays != null) {
21214
+ windowEndsAt = addDaysIso(sd.startedAt, sd.observationWindowDays);
21215
+ if (windowEndsAt) {
21216
+ windowElapsed = now.getTime() >= Date.parse(windowEndsAt);
21217
+ }
21218
+ }
21219
+ if (!sd.startedAt) reasons.push("Observation has not started (no started_at).");
21220
+ else if (sd.observationWindowDays == null) reasons.push("No observation_window_days declared.");
21221
+ else if (!windowElapsed) reasons.push(`Observation window not elapsed (ends ${windowEndsAt ?? "unknown"}).`);
21222
+ if (criteriaTotal === 0) reasons.push("No exit criteria declared.");
21223
+ else if (!allCriteriaMet) reasons.push(`Exit criteria not all met (${criteriaMet}/${criteriaTotal}).`);
21224
+ const eligibleForRemovalReview = windowElapsed && allCriteriaMet;
21225
+ if (eligibleForRemovalReview) {
21226
+ reasons.length = 0;
21227
+ reasons.push("Observation window elapsed and all exit criteria met \u2014 eligible for removal REVIEW (human decision).");
21228
+ }
21229
+ return {
21230
+ declared: true,
21231
+ windowElapsed,
21232
+ windowEndsAt,
21233
+ criteriaTotal,
21234
+ criteriaMet,
21235
+ allCriteriaMet,
21236
+ eligibleForRemovalReview,
21237
+ reasons
21238
+ };
21239
+ }
21240
+
21241
+ // ../cli/src/core/deprecation-intelligence.ts
21242
+ function isStrongNoUsage2(sig) {
21243
+ return sig.status === "no-usage" && (sig.quality === "observed-fact" || sig.quality === "human-confirmed");
21244
+ }
21245
+ function deriveUnusedConfidence2(evidence) {
21246
+ const present = EVIDENCE_SIGNALS.map((name) => ({ name, sig: evidence[name] })).filter(
21247
+ (e) => Boolean(e.sig)
21248
+ );
21249
+ const usageFound = present.filter((e) => e.sig.status === "usage-found");
21250
+ if (usageFound.length > 0) {
21251
+ return {
21252
+ confidence: "in-use",
21253
+ requiresHumanConfirmation: false,
21254
+ rationale: [`Usage found via: ${usageFound.map((e) => e.name).join(", ")}.`]
21255
+ };
21256
+ }
21257
+ const strong = present.filter((e) => isStrongNoUsage2(e.sig));
21258
+ const unknown = present.filter((e) => e.sig.status === "unknown");
21259
+ const notEvaluated = EVIDENCE_SIGNALS.filter(
21260
+ (name) => !evidence[name] || evidence[name]?.status === "not-evaluated"
21261
+ );
21262
+ const rationale = [];
21263
+ if (strong.length > 0) rationale.push(`No usage observed via: ${strong.map((e) => e.name).join(", ")}.`);
21264
+ if (unknown.length > 0) {
21265
+ rationale.push(`Unknown (looked but could not determine): ${unknown.map((e) => e.name).join(", ")} \u2014 NOT treated as no-usage.`);
21266
+ }
21267
+ if (notEvaluated.length > 0) {
21268
+ rationale.push(`Not evaluated: ${notEvaluated.join(", ")} \u2014 absence of a reference is not evidence of no usage.`);
21269
+ }
21270
+ let confidence;
21271
+ if (strong.length === 0) {
21272
+ confidence = "insufficient-evidence";
21273
+ } else if (unknown.length > 0) {
21274
+ confidence = "medium";
21275
+ } else if (strong.length >= 2) {
21276
+ confidence = "high";
21277
+ } else {
21278
+ confidence = "medium";
21279
+ }
21280
+ return { confidence, requiresHumanConfirmation: true, rationale };
21281
+ }
21282
+ function deriveRemovalWaves(plan) {
21283
+ const ids = new Set(plan.targets.map((t) => t.id));
21284
+ const deps = /* @__PURE__ */ new Map();
21285
+ const blocked = [];
21286
+ for (const t of plan.targets) {
21287
+ const resolved = [];
21288
+ for (const d3 of t.dependsOnRemoval) {
21289
+ if (d3 === t.id) {
21290
+ blocked.push({ target: t.id, reason: "Target depends on its own removal." });
21291
+ continue;
21292
+ }
21293
+ if (!ids.has(d3)) {
21294
+ blocked.push({ target: t.id, reason: `depends_on_removal references unknown target: ${d3}.` });
21295
+ continue;
21296
+ }
21297
+ resolved.push(d3);
21298
+ }
21299
+ deps.set(t.id, resolved);
21300
+ }
21301
+ const placed = /* @__PURE__ */ new Set();
21302
+ const waves = [];
21303
+ let remaining = plan.targets.map((t) => t.id);
21304
+ while (remaining.length > 0) {
21305
+ const wave = remaining.filter((id) => (deps.get(id) ?? []).every((d3) => placed.has(d3)));
21306
+ if (wave.length === 0) break;
21307
+ for (const id of wave) placed.add(id);
21308
+ waves.push(wave);
21309
+ remaining = remaining.filter((id) => !placed.has(id));
21310
+ }
21311
+ for (const id of remaining) {
21312
+ blocked.push({ target: id, reason: "Involved in a removal dependency cycle \u2014 cannot be ordered." });
21313
+ }
21314
+ return { waves, blocked };
21315
+ }
21316
+ var RETIRED_STATES = /* @__PURE__ */ new Set([
21317
+ "removed-devtest",
21318
+ "production-changed",
21319
+ "observing",
21320
+ "completed",
21321
+ "deprecated-retained"
21322
+ ]);
21323
+ function isRetiredLifecycle(state) {
21324
+ return RETIRED_STATES.has(state);
21325
+ }
21326
+ function targetsEligibleForRemoval(plan) {
21327
+ const byId = new Map(plan.targets.map((t) => [t.id, t]));
21328
+ return plan.targets.filter((t) => !isRetiredLifecycle(String(t.lifecycle))).filter(
21329
+ (t) => t.dependsOnRemoval.every((d3) => {
21330
+ const dep = byId.get(d3);
21331
+ return dep ? isRetiredLifecycle(String(dep.lifecycle)) : false;
21332
+ })
21333
+ ).map((t) => t.id);
21334
+ }
21335
+ function planIntelligence(plan) {
21336
+ const waves = deriveRemovalWaves(plan);
21337
+ const eligible = new Set(targetsEligibleForRemoval(plan));
21338
+ const byId = new Map(plan.targets.map((t) => [t.id, t]));
21339
+ const targets = plan.targets.map((t) => ({
21340
+ id: t.id,
21341
+ unused: deriveUnusedConfidence2(t.usageEvidence),
21342
+ eligibleForRemovalReview: eligible.has(t.id),
21343
+ blockedBy: t.dependsOnRemoval.filter((d3) => {
21344
+ const dep = byId.get(d3);
21345
+ return !dep || !isRetiredLifecycle(String(dep.lifecycle));
21346
+ }),
21347
+ observation: observationStatus(t)
21348
+ }));
21349
+ return { waves, eligible: [...eligible], targets };
21350
+ }
21351
+ var CANDIDATE_DISCOVERY_GUIDANCE2 = [
21352
+ "Propose deprecation candidates from multiple sources: static references (code/routes/UI/jobs),",
21353
+ "the System Graph (dependencies and blast radius), Legacy Knowledge (risks/unknowns/modernization",
21354
+ "candidates), Project Resources (databases/external systems and their boundaries), runtime evidence",
21355
+ "when telemetry exists, and database metadata when reachable.",
21356
+ "",
21357
+ "Rules:",
21358
+ "- Discovery is a PROPOSAL, not a materialization: never create or retire anything automatically.",
21359
+ "- Always explain WHY something is a candidate, citing the signal(s) observed.",
21360
+ "- Absence of a reference is NOT evidence of no usage. If a source cannot be evaluated, record it",
21361
+ " as not-evaluated/unknown and require human investigation \u2014 never collapse it to no-usage.",
21362
+ "- A modernization candidate is not automatically a deprecation target.",
21363
+ "- Soft deprecation precedes physical removal; order retirements by dependency, dependents first."
21364
+ ].join("\n");
21365
+
21366
+ // ../cli/src/core/deprecation-write.ts
21367
+ import fs7 from "fs";
21368
+ import path6 from "path";
21369
+ import matter19 from "gray-matter";
21370
+ var DeprecationWriteError = class extends Error {
21371
+ constructor(code, message) {
21372
+ super(message);
21373
+ this.code = code;
21374
+ this.name = "DeprecationWriteError";
21375
+ }
21376
+ code;
21377
+ };
21378
+ function slug5(s) {
21379
+ return s.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60);
21380
+ }
21381
+ function atomicWrite2(filePath, content) {
21382
+ const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
21383
+ fs7.writeFileSync(tmp, content, "utf-8");
21384
+ fs7.renameSync(tmp, filePath);
21385
+ }
21386
+ function serialize3(data, body) {
21387
+ return matter19.stringify(`
21388
+ ${body.trim()}
21389
+ `, data);
21390
+ }
21391
+ function allPlanArtifacts(dir) {
21392
+ return discoverKnowledge(dir).filter((a2) => a2.type === DEPRECATION_ARTIFACT_TYPE);
21393
+ }
21394
+ function nextPlanId(dir, explicit) {
21395
+ if (explicit && explicit.trim()) return explicit.trim();
21396
+ let max = 0;
21397
+ for (const a2 of allPlanArtifacts(dir)) {
21398
+ const m2 = (a2.id || "").match(/^DEP-(\d+)$/i);
21399
+ if (m2) max = Math.max(max, parseInt(m2[1], 10));
21400
+ }
21401
+ return `DEP-${String(max + 1).padStart(3, "0")}`;
21402
+ }
21403
+ function nextTargetId(targetIds) {
21404
+ let max = 0;
21405
+ for (const id of targetIds) {
21406
+ const m2 = id.match(/^DEP-TGT-(\d+)$/i);
21407
+ if (m2) max = Math.max(max, parseInt(m2[1], 10));
21408
+ }
21409
+ return `DEP-TGT-${String(max + 1).padStart(3, "0")}`;
21410
+ }
21411
+ function buildTargetRaw(input, base = {}) {
21412
+ const out = { ...base };
21413
+ if (input.id != null) out.id = input.id;
21414
+ if (input.type != null) out.type = input.type;
21415
+ if (input.locator != null) out.locator = input.locator;
21416
+ if (input.title != null) out.title = input.title;
21417
+ if (input.lifecycle != null) out.lifecycle = input.lifecycle;
21418
+ if (input.environments != null) out.environments = input.environments;
21419
+ if (input.assessment != null) {
21420
+ const a2 = { ...typeof base.assessment === "object" && base.assessment ? base.assessment : {} };
21421
+ if (input.assessment.unusedConfidence != null) a2.unused_confidence = input.assessment.unusedConfidence;
21422
+ if (input.assessment.blastRadius != null) a2.blast_radius = input.assessment.blastRadius;
21423
+ if (input.assessment.reversibility != null) a2.reversibility = input.assessment.reversibility;
21424
+ out.assessment = a2;
21425
+ }
21426
+ if (input.dependsOnRemoval != null) out.depends_on_removal = input.dependsOnRemoval;
21427
+ if (input.resource != null) out.resource = input.resource;
21428
+ if (input.module != null) out.module = input.module;
21429
+ return out;
21430
+ }
21431
+ function validateTargetInput(dir, raw) {
21432
+ const findings = [];
21433
+ const parsed = parseTarget(raw);
21434
+ if (!parsed) {
21435
+ findings.push({ level: "error", message: "A target id is required." });
21436
+ return findings;
21437
+ }
21438
+ for (const f3 of validateTarget(parsed)) {
21439
+ findings.push({ level: f3.severity === "error" ? "error" : "warning", message: f3.message });
21440
+ }
21441
+ const mapped = loadMappedModules(dir).map((m2) => m2.id);
21442
+ if (parsed.module && mapped.length > 0 && !mapped.includes(parsed.module)) {
21443
+ findings.push({ level: "warning", message: `Module "${parsed.module}" is not in .kaddo/modules.yml.` });
21444
+ }
21445
+ if (parsed.resource && !getResource(dir, parsed.resource)) {
21446
+ findings.push({ level: "warning", message: `Project Resource "${parsed.resource}" not found.` });
21447
+ }
21448
+ return findings;
21449
+ }
21450
+ function loadPlanRaw(dir, planId) {
21451
+ const match = allPlanArtifacts(dir).find((a2) => a2.id === planId || a2.title === planId);
21452
+ if (!match) throw new DeprecationWriteError("NOT_FOUND", `Deprecation Plan "${planId}" not found.`);
21453
+ const parsed = matter19(readFile(match.filePath));
21454
+ const data = { ...parsed.data ?? {} };
21455
+ return { filePath: match.filePath, data, body: parsed.content.trim() };
21456
+ }
21457
+ function rawTargets(data) {
21458
+ return Array.isArray(data.targets) ? data.targets : [];
21459
+ }
21460
+ function createPlan(dir, input) {
21461
+ const title = (input.title ?? "").trim();
21462
+ if (!title) throw new DeprecationWriteError("INVALID_INPUT", "A plan title is required.");
21463
+ const existing = allPlanArtifacts(dir);
21464
+ if (input.id && existing.some((a2) => (a2.id || "").toUpperCase() === input.id.toUpperCase())) {
21465
+ throw new DeprecationWriteError("DUPLICATE", `A plan with id "${input.id}" already exists.`);
21466
+ }
21467
+ if (existing.some((a2) => (a2.title || "").toLowerCase() === title.toLowerCase())) {
21468
+ throw new DeprecationWriteError("DUPLICATE", `A plan titled "${title}" already exists.`);
21469
+ }
21470
+ const id = nextPlanId(dir, input.id);
21471
+ const data = {
21472
+ type: DEPRECATION_ARTIFACT_TYPE,
21473
+ id,
21474
+ title,
21475
+ status: (input.status ?? "draft").trim() || "draft",
21476
+ targets: []
21477
+ };
21478
+ const body = `# Purpose
21479
+
21480
+ ${(input.purpose ?? "").trim() || "TODO: describe what this plan retires and why."}`;
21481
+ const relPath = `${DEPRECATION_PLANS_DIR}/${id}-${slug5(title) || "plan"}.md`;
21482
+ const filePath = join(dir, relPath);
21483
+ if (exists(filePath)) throw new DeprecationWriteError("DUPLICATE", `Plan file already exists: ${relPath}`);
21484
+ ensureDir(path6.dirname(filePath));
21485
+ atomicWrite2(filePath, serialize3(data, body));
21486
+ return { id, path: relPath, findings: [] };
21487
+ }
21488
+ function addTarget(dir, planId, input) {
21489
+ const { filePath, data, body } = loadPlanRaw(dir, planId);
21490
+ const targets = rawTargets(data);
21491
+ const existingIds = targets.map((t) => String(t.id ?? ""));
21492
+ const targetId = (input.id ?? "").trim() || nextTargetId(existingIds);
21493
+ if (existingIds.some((id) => id.toUpperCase() === targetId.toUpperCase())) {
21494
+ throw new DeprecationWriteError("DUPLICATE", `Target "${targetId}" already exists in ${planId}.`);
21495
+ }
21496
+ const raw = buildTargetRaw({ ...input, id: targetId, lifecycle: input.lifecycle ?? "candidate" });
21497
+ const findings = validateTargetInput(dir, raw);
21498
+ if (findings.some((f3) => f3.level === "error")) {
21499
+ throw new DeprecationWriteError("VALIDATION_FAILED", findings.filter((f3) => f3.level === "error").map((f3) => f3.message).join("; "));
21500
+ }
21501
+ data.targets = [...targets, raw];
21502
+ atomicWrite2(filePath, serialize3(data, body));
21503
+ return { planId, targetId, path: `${DEPRECATION_PLANS_DIR}/${path6.basename(filePath)}`, findings };
21504
+ }
21505
+ function updateTarget(dir, planId, targetId, patch) {
21506
+ const { filePath, data, body } = loadPlanRaw(dir, planId);
21507
+ const targets = rawTargets(data);
21508
+ const idx = targets.findIndex((t) => String(t.id ?? "").toUpperCase() === targetId.toUpperCase());
21509
+ if (idx === -1) throw new DeprecationWriteError("NOT_FOUND", `Target "${targetId}" not found in ${planId}.`);
21510
+ const merged = buildTargetRaw({ ...patch, id: String(targets[idx].id) }, targets[idx]);
21511
+ const findings = validateTargetInput(dir, merged);
21512
+ if (findings.some((f3) => f3.level === "error")) {
21513
+ throw new DeprecationWriteError("VALIDATION_FAILED", findings.filter((f3) => f3.level === "error").map((f3) => f3.message).join("; "));
21514
+ }
21515
+ const next = [...targets];
21516
+ next[idx] = merged;
21517
+ data.targets = next;
21518
+ atomicWrite2(filePath, serialize3(data, body));
21519
+ return { planId, targetId: String(targets[idx].id), path: `${DEPRECATION_PLANS_DIR}/${path6.basename(filePath)}`, findings };
21520
+ }
21521
+ function removeTarget(dir, planId, targetId, opts = {}) {
21522
+ const { filePath, data, body } = loadPlanRaw(dir, planId);
21523
+ const targets = rawTargets(data);
21524
+ const exists3 = targets.some((t) => String(t.id ?? "").toUpperCase() === targetId.toUpperCase());
21525
+ if (!exists3) throw new DeprecationWriteError("NOT_FOUND", `Target "${targetId}" not found in ${planId}.`);
21526
+ const dependents = targets.filter((t) => (Array.isArray(t.depends_on_removal) ? t.depends_on_removal.map(String) : []).some((d3) => d3.toUpperCase() === targetId.toUpperCase())).map((t) => String(t.id));
21527
+ if (!opts.confirm) {
21528
+ return { removed: false, preview: { planId, targetId, dependents } };
21529
+ }
21530
+ data.targets = targets.filter((t) => String(t.id ?? "").toUpperCase() !== targetId.toUpperCase());
21531
+ atomicWrite2(filePath, serialize3(data, body));
21532
+ return { removed: true, path: `${DEPRECATION_PLANS_DIR}/${path6.basename(filePath)}` };
21533
+ }
21534
+
20499
21535
  // src/tools.ts
20500
21536
  var ok = (data) => ({ ok: true, data });
20501
21537
  var fail = (message) => ({ ok: false, message });
@@ -20602,12 +21638,12 @@ function markWorkItemReady(root, id, confirm2) {
20602
21638
  }
20603
21639
  const raw = readText(root, item.path);
20604
21640
  if (!raw) return fail(`Could not read Work Item file: ${item.path}`);
20605
- const parsed = matter16(raw);
21641
+ const parsed = matter20(raw);
20606
21642
  const data = { ...parsed.data };
20607
21643
  const today3 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
20608
21644
  data.status = "ready";
20609
21645
  data.ready_at = today3;
20610
- const newContent = matter16.stringify(parsed.content, data);
21646
+ const newContent = matter20.stringify(parsed.content, data);
20611
21647
  const posixPath = item.path.replace(/\\/g, "/");
20612
21648
  let newRelPath = item.path;
20613
21649
  if (posixPath.includes("/work-items/draft/")) {
@@ -20831,11 +21867,78 @@ function deleteResourceTool(root, id, confirm2) {
20831
21867
  return fail(e instanceof ResourceWriteError ? e.message : String(e));
20832
21868
  }
20833
21869
  }
21870
+ function listDeprecationsTool(root) {
21871
+ return ok(getDeprecationPlans(root));
21872
+ }
21873
+ function getDeprecationTool(root, id) {
21874
+ const plan = getDeprecationPlan(root, id);
21875
+ if (!plan) return fail(`Deprecation Plan "${id}" not found.`);
21876
+ return ok(plan);
21877
+ }
21878
+ function planDeprecationTool(root, id) {
21879
+ const plan = getDeprecationPlan(root, id);
21880
+ if (!plan) return fail(`Deprecation Plan "${id}" not found.`);
21881
+ return ok(deriveRemovalWaves(plan));
21882
+ }
21883
+ function analyzeDeprecationTool(root, id) {
21884
+ const plan = getDeprecationPlan(root, id);
21885
+ if (!plan) return fail(`Deprecation Plan "${id}" not found.`);
21886
+ return ok(planIntelligence(plan));
21887
+ }
21888
+ function createDeprecationTool(root, args, confirm2) {
21889
+ if (!confirm2) {
21890
+ return ok({ status: "needs_confirmation", action: "create", proposal: args, message: "Re-call with confirm=true to create this Deprecation Plan." });
21891
+ }
21892
+ try {
21893
+ return ok({ status: "created", ...createPlan(root, args) });
21894
+ } catch (e) {
21895
+ return fail(e instanceof DeprecationWriteError ? e.message : String(e));
21896
+ }
21897
+ }
21898
+ function addDeprecationTargetTool(root, planId, args, confirm2) {
21899
+ if (!getDeprecationPlan(root, planId)) return fail(`Deprecation Plan "${planId}" not found.`);
21900
+ if (!confirm2) {
21901
+ const findings = validateTargetInput(root, { ...args, id: args.id ?? "DEP-TGT-NEW", lifecycle: args.lifecycle ?? "candidate" });
21902
+ return ok({ status: "needs_confirmation", action: "add_target", planId, proposal: args, findings, message: "Re-call with confirm=true to add this target." });
21903
+ }
21904
+ try {
21905
+ return ok({ status: "added", ...addTarget(root, planId, args) });
21906
+ } catch (e) {
21907
+ return fail(e instanceof DeprecationWriteError ? e.message : String(e));
21908
+ }
21909
+ }
21910
+ function updateDeprecationTargetTool(root, planId, targetId, args, confirm2) {
21911
+ if (!getDeprecationPlan(root, planId)) return fail(`Deprecation Plan "${planId}" not found.`);
21912
+ if (!confirm2) {
21913
+ return ok({ status: "needs_confirmation", action: "update_target", planId, targetId, changes: args, message: "Re-call with confirm=true to apply this update." });
21914
+ }
21915
+ try {
21916
+ return ok({ status: "updated", ...updateTarget(root, planId, targetId, args) });
21917
+ } catch (e) {
21918
+ return fail(e instanceof DeprecationWriteError ? e.message : String(e));
21919
+ }
21920
+ }
21921
+ function removeDeprecationTargetTool(root, planId, targetId, confirm2) {
21922
+ if (!getDeprecationPlan(root, planId)) return fail(`Deprecation Plan "${planId}" not found.`);
21923
+ if (!confirm2) {
21924
+ try {
21925
+ const preview = removeTarget(root, planId, targetId);
21926
+ return ok({ status: "needs_confirmation", action: "remove_target", ...preview.preview, message: "Re-call with confirm=true to remove. Dependents\u2019 depends_on_removal are NOT rewritten." });
21927
+ } catch (e) {
21928
+ return fail(e instanceof DeprecationWriteError ? e.message : String(e));
21929
+ }
21930
+ }
21931
+ try {
21932
+ return ok({ status: "removed", ...removeTarget(root, planId, targetId, { confirm: true }) });
21933
+ } catch (e) {
21934
+ return fail(e instanceof DeprecationWriteError ? e.message : String(e));
21935
+ }
21936
+ }
20834
21937
 
20835
21938
  // src/multirepo.ts
20836
- import fs7 from "fs";
20837
- import path6 from "path";
20838
- import matter17 from "gray-matter";
21939
+ import fs8 from "fs";
21940
+ import path7 from "path";
21941
+ import matter21 from "gray-matter";
20839
21942
  import { parse as parseYaml11, stringify as stringifyYaml8 } from "yaml";
20840
21943
  var ok2 = (data) => ({ ok: true, data });
20841
21944
  var fail2 = (message) => ({ ok: false, message });
@@ -20855,24 +21958,24 @@ function isCore2(config) {
20855
21958
  return projectRole2(config) === "core";
20856
21959
  }
20857
21960
  function readModuleFile(root, repoPath, relFile) {
20858
- const moduleDir = path6.resolve(root, repoPath);
20859
- const abs = path6.resolve(moduleDir, relFile);
21961
+ const moduleDir = path7.resolve(root, repoPath);
21962
+ const abs = path7.resolve(moduleDir, relFile);
20860
21963
  if (!abs.startsWith(moduleDir)) return null;
20861
- if (!fs7.existsSync(abs) || !fs7.statSync(abs).isFile()) return null;
20862
- return fs7.readFileSync(abs, "utf-8");
21964
+ if (!fs8.existsSync(abs) || !fs8.statSync(abs).isFile()) return null;
21965
+ return fs8.readFileSync(abs, "utf-8");
20863
21966
  }
20864
21967
  function detectModuleStatus2(root, repoPath) {
20865
- const moduleDir = path6.resolve(root, repoPath);
20866
- if (!fs7.existsSync(moduleDir)) {
21968
+ const moduleDir = path7.resolve(root, repoPath);
21969
+ if (!fs8.existsSync(moduleDir)) {
20867
21970
  return { status: "missing", configured: false, moduleContext: false, currentState: false, codebase: false, warning: `Directory \`${repoPath}\` does not exist.` };
20868
21971
  }
20869
- const configPath = path6.join(moduleDir, ".kaddo", "config.yml");
20870
- if (!fs7.existsSync(configPath)) {
21972
+ const configPath = path7.join(moduleDir, ".kaddo", "config.yml");
21973
+ if (!fs8.existsSync(configPath)) {
20871
21974
  return { status: "not_configured", configured: false, moduleContext: false, currentState: false, codebase: false, warning: `Run \`kaddo init\` inside \`${repoPath}\` and select multirepo \u2192 module.` };
20872
21975
  }
20873
21976
  let parsed = null;
20874
21977
  try {
20875
- parsed = parseYaml11(fs7.readFileSync(configPath, "utf-8"));
21978
+ parsed = parseYaml11(fs8.readFileSync(configPath, "utf-8"));
20876
21979
  } catch {
20877
21980
  }
20878
21981
  const role = parsed?.project?.role ?? parsed?.multirepo?.role;
@@ -20883,14 +21986,14 @@ function detectModuleStatus2(root, repoPath) {
20883
21986
  status: "configured",
20884
21987
  configured: true,
20885
21988
  role: "module",
20886
- moduleContext: fs7.existsSync(path6.join(moduleDir, "knowledge", "tech", "module", "module-context.md")) || fs7.existsSync(path6.join(moduleDir, "knowledge", "module", "module-context.md")),
20887
- currentState: fs7.existsSync(path6.join(moduleDir, "knowledge", "tech", "current-state.md")),
20888
- codebase: fs7.existsSync(path6.join(moduleDir, "knowledge", "tech", "codebase.md"))
21989
+ moduleContext: fs8.existsSync(path7.join(moduleDir, "knowledge", "tech", "module", "module-context.md")) || fs8.existsSync(path7.join(moduleDir, "knowledge", "module", "module-context.md")),
21990
+ currentState: fs8.existsSync(path7.join(moduleDir, "knowledge", "tech", "current-state.md")),
21991
+ codebase: fs8.existsSync(path7.join(moduleDir, "knowledge", "tech", "codebase.md"))
20889
21992
  };
20890
21993
  }
20891
21994
  function resolveModuleContextPath(root, repoPath) {
20892
- const moduleDir = path6.resolve(root, repoPath);
20893
- if (fs7.existsSync(path6.join(moduleDir, "knowledge", "tech", "module", "module-context.md"))) {
21995
+ const moduleDir = path7.resolve(root, repoPath);
21996
+ if (fs8.existsSync(path7.join(moduleDir, "knowledge", "tech", "module", "module-context.md"))) {
20894
21997
  return "knowledge/tech/module/module-context.md";
20895
21998
  }
20896
21999
  return "knowledge/module/module-context.md";
@@ -20987,7 +22090,7 @@ function validateWorkItemModulesTool(root, args) {
20987
22090
  if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
20988
22091
  const raw = readText(root, wi.path);
20989
22092
  if (!raw) return fail2(`Could not read Work Item file.`);
20990
- const parsed = matter17(raw);
22093
+ const parsed = matter21(raw);
20991
22094
  const data = parsed.data;
20992
22095
  const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
20993
22096
  const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
@@ -21040,7 +22143,7 @@ function getWorkItemContextTool(root, args) {
21040
22143
  if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
21041
22144
  const raw = readText(root, wi.path);
21042
22145
  if (!raw) return fail2(`Could not read Work Item file.`);
21043
- const parsed = matter17(raw);
22146
+ const parsed = matter21(raw);
21044
22147
  const data = parsed.data;
21045
22148
  const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
21046
22149
  const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
@@ -21094,15 +22197,15 @@ function extractSection(content, heading) {
21094
22197
  return out;
21095
22198
  }
21096
22199
  function buildBranchStrategy(wi, affectedModules, codeEntries, descriptor) {
21097
- const slug6 = slugify4(wi.title);
22200
+ const slug7 = slugify4(wi.title);
21098
22201
  const wiLower = wi.id.toLowerCase();
21099
- const coreBranch = `feature/${wiLower}-${slug6}`;
22202
+ const coreBranch = `feature/${wiLower}-${slug7}`;
21100
22203
  const moduleBranches = {};
21101
22204
  const commitMessages = {};
21102
22205
  for (const modId of affectedModules) {
21103
22206
  const mod = (descriptor.modules ?? []).find((m2) => m2.id === modId);
21104
22207
  const repo = mod ? moduleRepoPath(mod) || modId : modId;
21105
- moduleBranches[repo] = `feature/${wiLower}-${modId}-${slug6.slice(0, 20)}`;
22208
+ moduleBranches[repo] = `feature/${wiLower}-${modId}-${slug7.slice(0, 20)}`;
21106
22209
  commitMessages[repo] = `fix(${wiLower}): update ${modId}`;
21107
22210
  }
21108
22211
  commitMessages["core"] = `docs(${wiLower}): capture cross-repo implementation summary`;
@@ -21126,7 +22229,7 @@ function suggestBranchStrategyTool(root, args) {
21126
22229
  if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
21127
22230
  const raw = readText(root, wi.path);
21128
22231
  if (!raw) return fail2(`Could not read Work Item file.`);
21129
- const parsed = matter17(raw);
22232
+ const parsed = matter21(raw);
21130
22233
  const data = parsed.data;
21131
22234
  const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
21132
22235
  const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
@@ -21275,31 +22378,31 @@ function modulesDiscoverTool(root, args) {
21275
22378
  const sysName = config.system?.name ?? config.project?.name ?? "";
21276
22379
  const workspaceRoots2 = config.multirepo?.workspace_roots ?? [".."];
21277
22380
  const IGNORED = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", "coverage", "vendor", ".next", "out", "tmp", "temp", ".kaddo"]);
21278
- const coreAbs = path6.resolve(root);
22381
+ const coreAbs = path7.resolve(root);
21279
22382
  const discovered = [];
21280
22383
  for (const wr of workspaceRoots2) {
21281
- const rootAbs = path6.resolve(root, wr);
21282
- if (!fs7.existsSync(rootAbs) || !fs7.statSync(rootAbs).isDirectory()) continue;
22384
+ const rootAbs = path7.resolve(root, wr);
22385
+ if (!fs8.existsSync(rootAbs) || !fs8.statSync(rootAbs).isDirectory()) continue;
21283
22386
  let entries;
21284
22387
  try {
21285
- entries = fs7.readdirSync(rootAbs);
22388
+ entries = fs8.readdirSync(rootAbs);
21286
22389
  } catch {
21287
22390
  continue;
21288
22391
  }
21289
22392
  for (const entry of entries) {
21290
22393
  if (IGNORED.has(entry)) continue;
21291
- const candidateAbs = path6.resolve(rootAbs, entry);
21292
- if (!fs7.statSync(candidateAbs).isDirectory()) continue;
22394
+ const candidateAbs = path7.resolve(rootAbs, entry);
22395
+ if (!fs8.statSync(candidateAbs).isDirectory()) continue;
21293
22396
  if (candidateAbs === coreAbs) continue;
21294
- const relPath = path6.relative(root, candidateAbs).replace(/\\/g, "/");
21295
- const cfgPath = path6.join(candidateAbs, ".kaddo", "config.yml");
21296
- if (!fs7.existsSync(cfgPath)) {
22397
+ const relPath = path7.relative(root, candidateAbs).replace(/\\/g, "/");
22398
+ const cfgPath = path7.join(candidateAbs, ".kaddo", "config.yml");
22399
+ if (!fs8.existsSync(cfgPath)) {
21297
22400
  discovered.push({ id: entry, path: relPath, status: "not_configured", eligibleForMapping: false });
21298
22401
  continue;
21299
22402
  }
21300
22403
  let parsed = null;
21301
22404
  try {
21302
- parsed = parseYaml11(fs7.readFileSync(cfgPath, "utf-8"));
22405
+ parsed = parseYaml11(fs8.readFileSync(cfgPath, "utf-8"));
21303
22406
  } catch {
21304
22407
  }
21305
22408
  const role = parsed?.project?.role ?? parsed?.multirepo?.role;
@@ -21373,8 +22476,8 @@ ${list3(data.owners)}
21373
22476
 
21374
22477
  // ../cli/src/core/system-topology.ts
21375
22478
  import { parse as parseYaml12, stringify as stringifyYaml9 } from "yaml";
21376
- import fs8 from "fs";
21377
- import path7 from "path";
22479
+ import fs9 from "fs";
22480
+ import path8 from "path";
21378
22481
  import crypto from "crypto";
21379
22482
  var TOPOLOGY_FILE2 = "knowledge/tech/system-topology.yml";
21380
22483
  var SYSTEM_ENTITY_KINDS2 = /* @__PURE__ */ new Set([
@@ -21746,16 +22849,16 @@ function findSystemPaths(dir, source3, target, opts = {}) {
21746
22849
  out.get(r2.source).push(r2.target);
21747
22850
  }
21748
22851
  const paths = [];
21749
- const walk = (node, path9) => {
22852
+ const walk = (node, path10) => {
21750
22853
  if (paths.length >= 20) return;
21751
- if (node === target && path9.length > 1) {
21752
- paths.push([...path9]);
22854
+ if (node === target && path10.length > 1) {
22855
+ paths.push([...path10]);
21753
22856
  return;
21754
22857
  }
21755
- if (path9.length > maxDepth) return;
22858
+ if (path10.length > maxDepth) return;
21756
22859
  for (const nxt of out.get(node) ?? []) {
21757
- if (path9.includes(nxt)) continue;
21758
- walk(nxt, [...path9, nxt]);
22860
+ if (path10.includes(nxt)) continue;
22861
+ walk(nxt, [...path10, nxt]);
21759
22862
  }
21760
22863
  };
21761
22864
  walk(source3, [source3]);
@@ -21780,14 +22883,14 @@ function getImpactCandidates(dir, seedIds, opts = {}) {
21780
22883
  existing.reason.push(reason);
21781
22884
  continue;
21782
22885
  }
21783
- const path9 = findSystemPaths(dir, seed, n.id, { maxDepth: opts.maxDepth ?? DEFAULT_MAX_DEPTH })[0];
22886
+ const path10 = findSystemPaths(dir, seed, n.id, { maxDepth: opts.maxDepth ?? DEFAULT_MAX_DEPTH })[0];
21784
22887
  candidates.set(n.id, {
21785
22888
  nodeId: n.id,
21786
22889
  label: n.label,
21787
22890
  kind: n.type,
21788
22891
  ...n.moduleId ? { moduleId: n.moduleId } : {},
21789
22892
  reason: [reason],
21790
- ...path9 ? { graphPath: path9.map((id) => byId.get(id)?.label ?? id) } : {},
22893
+ ...path10 ? { graphPath: path10.map((id) => byId.get(id)?.label ?? id) } : {},
21791
22894
  ...n.implementationRefs?.length ? { implementationRefs: n.implementationRefs } : {},
21792
22895
  ...n.knowledgeRefs?.length ? { knowledgeRefs: n.knowledgeRefs } : {},
21793
22896
  status: "candidate",
@@ -21886,14 +22989,14 @@ function systemImpactCandidatesTool(root, args) {
21886
22989
  }
21887
22990
 
21888
22991
  // ../cli/src/services/integrations.ts
21889
- import matter19 from "gray-matter";
22992
+ import matter24 from "gray-matter";
21890
22993
  import { parse as parseYaml13, stringify as stringifyYaml10 } from "yaml";
21891
22994
 
21892
22995
  // ../cli/src/core/work-item-write.ts
21893
- import fs9 from "fs";
21894
- import path8 from "path";
22996
+ import fs10 from "fs";
22997
+ import path9 from "path";
21895
22998
  import crypto4 from "crypto";
21896
- import matter18 from "gray-matter";
22999
+ import matter23 from "gray-matter";
21897
23000
  var SECTION_ORDER2 = [
21898
23001
  "Actor",
21899
23002
  "Outcome",
@@ -22561,8 +23664,8 @@ async function handleJiraResponse2(res) {
22561
23664
  }
22562
23665
  return await res.json();
22563
23666
  }
22564
- async function jiraFetch2(baseUrl, path9, auth, timeoutMs, params) {
22565
- const url = new URL(path9, baseUrl + "/");
23667
+ async function jiraFetch2(baseUrl, path10, auth, timeoutMs, params) {
23668
+ const url = new URL(path10, baseUrl + "/");
22566
23669
  if (params) for (const [k2, v] of Object.entries(params)) url.searchParams.set(k2, v);
22567
23670
  const controller = new AbortController();
22568
23671
  const timer = setTimeout(() => controller.abort(), timeoutMs);
@@ -22580,8 +23683,8 @@ async function jiraFetch2(baseUrl, path9, auth, timeoutMs, params) {
22580
23683
  clearTimeout(timer);
22581
23684
  }
22582
23685
  }
22583
- async function jiraPost2(baseUrl, path9, auth, timeoutMs, body) {
22584
- const url = new URL(path9, baseUrl + "/");
23686
+ async function jiraPost2(baseUrl, path10, auth, timeoutMs, body) {
23687
+ const url = new URL(path10, baseUrl + "/");
22585
23688
  const controller = new AbortController();
22586
23689
  const timer = setTimeout(() => controller.abort(), timeoutMs);
22587
23690
  try {
@@ -22941,7 +24044,7 @@ async function integrationsWorkItemTool(root, args) {
22941
24044
  }
22942
24045
 
22943
24046
  // ../cli/src/core/context-pack.ts
22944
- import matter20 from "gray-matter";
24047
+ import matter25 from "gray-matter";
22945
24048
  var CONTEXT_PACK_VERSION = "1";
22946
24049
  var ARCH_DIR4 = "knowledge";
22947
24050
  function readScanJson(dir) {
@@ -22954,7 +24057,7 @@ function readScanJson(dir) {
22954
24057
  }
22955
24058
  }
22956
24059
  function firstParagraph4(markdown) {
22957
- const body = matter20(markdown).content.trim();
24060
+ const body = matter25(markdown).content.trim();
22958
24061
  const para = body.split("\n\n").map((p) => p.trim()).find((p) => p && !p.startsWith("#"));
22959
24062
  return para ?? "";
22960
24063
  }
@@ -23946,7 +25049,7 @@ function requireKnowledge(root) {
23946
25049
  throw new KaddoMcpError("Knowledge repository not found. Run `kaddo bootstrap` first.");
23947
25050
  }
23948
25051
  }
23949
- function slug5(s) {
25052
+ function slug6(s) {
23950
25053
  return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
23951
25054
  }
23952
25055
  function generateContext(root) {
@@ -24079,7 +25182,7 @@ function generateCapsuleDraft(root) {
24079
25182
  const config = requireConfig(root);
24080
25183
  requireKnowledge(root);
24081
25184
  const capsule = buildCapsule(root, config);
24082
- const name = slug5(config.project.name);
25185
+ const name = slug6(config.project.name);
24083
25186
  const md = `.kaddo/exports/${name}.capsule.md`;
24084
25187
  const json = `.kaddo/exports/${name}.capsule.json`;
24085
25188
  writeDerived(root, md, renderCapsuleMarkdown(capsule));
@@ -24442,6 +25545,74 @@ function createServer(root) {
24442
25545
  },
24443
25546
  async (args) => toolText(guarded(root, () => deleteResourceTool(root, args.id, args.confirm)))
24444
25547
  );
25548
+ server.registerTool(
25549
+ "kaddo_list_deprecations",
25550
+ { title: "List Deprecation Plans", description: "List Controlled Deprecation Plans under knowledge/legacy/deprecations/ (read-only).", inputSchema: {} },
25551
+ async () => toolText(guarded(root, () => listDeprecationsTool(root)))
25552
+ );
25553
+ server.registerTool(
25554
+ "kaddo_get_deprecation",
25555
+ { title: "Get Deprecation Plan", description: "Get one Deprecation Plan by ID: purpose, typed targets, lifecycle and per-environment state.", inputSchema: { id: z4.string() } },
25556
+ async (args) => toolText(guarded(root, () => getDeprecationTool(root, args.id)))
25557
+ );
25558
+ server.registerTool(
25559
+ "kaddo_plan_deprecation",
25560
+ { title: "Deprecation Removal Waves", description: "Derive the dependency-ordered removal waves (dependents first) and blocked targets for a plan. Explainable, not a manual priority.", inputSchema: { id: z4.string() } },
25561
+ async (args) => toolText(guarded(root, () => planDeprecationTool(root, args.id)))
25562
+ );
25563
+ server.registerTool(
25564
+ "kaddo_analyze_deprecation",
25565
+ { title: "Analyze Deprecation Plan", description: "Per-target unused-confidence from usage evidence + removal eligibility. Absence of a reference is never treated as no-usage; removal always requires human confirmation.", inputSchema: { id: z4.string() } },
25566
+ async (args) => toolText(guarded(root, () => analyzeDeprecationTool(root, args.id)))
25567
+ );
25568
+ const targetFields = {
25569
+ id: z4.string().optional(),
25570
+ type: z4.string().optional(),
25571
+ locator: z4.string().optional(),
25572
+ title: z4.string().optional(),
25573
+ lifecycle: z4.string().optional(),
25574
+ environments: z4.record(z4.string()).optional(),
25575
+ assessment: z4.object({ unusedConfidence: z4.string().optional(), blastRadius: z4.string().optional(), reversibility: z4.string().optional() }).optional(),
25576
+ dependsOnRemoval: z4.array(z4.string()).optional(),
25577
+ resource: z4.string().optional(),
25578
+ module: z4.string().optional()
25579
+ };
25580
+ server.registerTool(
25581
+ "kaddo_create_deprecation",
25582
+ {
25583
+ title: "Create Deprecation Plan",
25584
+ description: "Propose creating a Deprecation Plan. Without confirm, returns a preview (writes nothing); with confirm=true, Core persists it as Markdown.",
25585
+ inputSchema: { title: z4.string().optional(), purpose: z4.string().optional(), status: z4.string().optional(), confirm: z4.boolean().optional() }
25586
+ },
25587
+ async (args) => toolText(guarded(root, () => createDeprecationTool(root, args, args.confirm)))
25588
+ );
25589
+ server.registerTool(
25590
+ "kaddo_add_deprecation_target",
25591
+ {
25592
+ title: "Add Deprecation Target",
25593
+ description: "Propose adding a typed target to a plan. Without confirm, returns a preview + validation findings; with confirm=true, Core appends it (no-lossy).",
25594
+ inputSchema: { planId: z4.string(), ...targetFields, confirm: z4.boolean().optional() }
25595
+ },
25596
+ async (args) => toolText(guarded(root, () => addDeprecationTargetTool(root, args.planId, args, args.confirm)))
25597
+ );
25598
+ server.registerTool(
25599
+ "kaddo_update_deprecation_target",
25600
+ {
25601
+ title: "Update Deprecation Target",
25602
+ description: "Propose updating a target in place (no-lossy). Without confirm, returns a preview; with confirm=true, Core applies it.",
25603
+ inputSchema: { planId: z4.string(), targetId: z4.string(), ...targetFields, confirm: z4.boolean().optional() }
25604
+ },
25605
+ async (args) => toolText(guarded(root, () => updateDeprecationTargetTool(root, args.planId, args.targetId, args, args.confirm)))
25606
+ );
25607
+ server.registerTool(
25608
+ "kaddo_remove_deprecation_target",
25609
+ {
25610
+ title: "Remove Deprecation Target",
25611
+ description: "Remove a target. Without confirm, returns what depends on it (writes nothing); with confirm=true, Core removes it \u2014 dependents\u2019 depends_on_removal are left intact.",
25612
+ inputSchema: { planId: z4.string(), targetId: z4.string(), confirm: z4.boolean().optional() }
25613
+ },
25614
+ async (args) => toolText(guarded(root, () => removeDeprecationTargetTool(root, args.planId, args.targetId, args.confirm)))
25615
+ );
24445
25616
  server.registerTool(
24446
25617
  "kaddo_list_capsules",
24447
25618
  { title: "List Knowledge Capsules", description: "List registered external Knowledge Capsules.", inputSchema: {} },