@wrongstack/core 0.303.0 → 0.305.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 (76) hide show
  1. package/dist/chronicle/project-server.js +18 -48
  2. package/dist/coordination/agents/index.js +530 -130
  3. package/dist/coordination/agents/project-agent-consolidation.d.ts +5 -0
  4. package/dist/coordination/agents/project-agent-directive-outcome.d.ts +57 -0
  5. package/dist/coordination/agents/project-agent-identity.d.ts +26 -12
  6. package/dist/coordination/agents/project-agent-learning-policy.d.ts +22 -1
  7. package/dist/coordination/agents/project-agent-learning-structured.d.ts +46 -1
  8. package/dist/coordination/agents/project-agent-quarantine.d.ts +63 -0
  9. package/dist/coordination/agents/project-agent-skill-layer.d.ts +55 -10
  10. package/dist/coordination/agents/types.d.ts +10 -2
  11. package/dist/coordination/director-prompts.d.ts +19 -6
  12. package/dist/coordination/director-tools.d.ts +2 -2
  13. package/dist/coordination/fleet.d.ts +0 -6
  14. package/dist/coordination/index.d.ts +2 -1
  15. package/dist/coordination/index.js +1581 -955
  16. package/dist/coordination/mailbox-project-server.js +28 -57
  17. package/dist/core/agent-types.d.ts +4 -2
  18. package/dist/core/agent.d.ts +1 -0
  19. package/dist/core/context.d.ts +15 -0
  20. package/dist/core/conversation-state.d.ts +14 -0
  21. package/dist/core/fallback-profile-manager.d.ts +70 -2
  22. package/dist/core/index.js +308 -108
  23. package/dist/core/system-prompt-blocks.d.ts +1 -1
  24. package/dist/core/system-prompt-builder.d.ts +13 -1
  25. package/dist/core/system-prompt-glossary.d.ts +73 -0
  26. package/dist/core/system-prompt-memory-skills.d.ts +2 -2
  27. package/dist/defaults/index.js +910 -693
  28. package/dist/execution/council-orchestrator.d.ts +3 -13
  29. package/dist/execution/index.js +211 -75
  30. package/dist/execution/one-shot-llm.d.ts +5 -0
  31. package/dist/hq/index.js +17 -7
  32. package/dist/hq/protocol/kanban.d.ts +21 -0
  33. package/dist/hq/protocol.js +5 -1
  34. package/dist/hq/redaction.d.ts +14 -0
  35. package/dist/index.d.ts +1 -0
  36. package/dist/index.js +3505 -2539
  37. package/dist/infrastructure/index.js +247 -122
  38. package/dist/plugin/index.js +101 -3
  39. package/dist/registry/index.js +11 -0
  40. package/dist/registry/tool-registry.d.ts +8 -0
  41. package/dist/replay/hash.d.ts +9 -0
  42. package/dist/replay/index.js +14 -4
  43. package/dist/replay/replay-provider-runner.d.ts +31 -1
  44. package/dist/security/index.js +25 -20
  45. package/dist/security/secret-vault.d.ts +2 -0
  46. package/dist/session-catalog/index.js +62 -8
  47. package/dist/session-catalog/project-server.js +109 -78
  48. package/dist/session-catalog/protocol.d.ts +11 -4
  49. package/dist/session-catalog/store.d.ts +2 -2
  50. package/dist/storage/index.js +224 -67
  51. package/dist/storage/memory-consolidator.d.ts +4 -2
  52. package/dist/storage/session-resume-validation.d.ts +24 -0
  53. package/dist/storage/session-store/directory-scan.d.ts +5 -1
  54. package/dist/storage/session-store/fork-session.d.ts +13 -1
  55. package/dist/storage/session-store/load-cache.d.ts +11 -0
  56. package/dist/storage/session-store/prune-helpers.d.ts +5 -0
  57. package/dist/storage/session-store.d.ts +18 -0
  58. package/dist/tools/index.js +174 -74
  59. package/dist/types/config/mcp-features.d.ts +31 -1
  60. package/dist/types/config/root.d.ts +12 -0
  61. package/dist/types/config/tools.d.ts +22 -0
  62. package/dist/types/config/ui.d.ts +7 -4
  63. package/dist/types/default-config.d.ts +1 -0
  64. package/dist/types/index.js +24 -1
  65. package/dist/types/session.d.ts +9 -1
  66. package/dist/utils/index.d.ts +1 -0
  67. package/dist/utils/index.js +214 -76
  68. package/dist/utils/project-state-guard.d.ts +21 -0
  69. package/dist/utils/session-scoped-path.d.ts +17 -0
  70. package/dist/utils/todos-format.d.ts +20 -0
  71. package/instructions/leader-after-task.md +3 -4
  72. package/instructions/system-lite.md +10 -13
  73. package/instructions/system-pro.md +18 -25
  74. package/instructions/system.md +18 -23
  75. package/package.json +3 -3
  76. package/skills/wrongstack-kanban/SKILL.md +95 -124
@@ -1,6 +1,6 @@
1
1
  // src/coordination/agents/agent-prompts.ts
2
- import { readFileSync as readFileSync9, statSync as statSync2 } from "node:fs";
3
- import * as path10 from "node:path";
2
+ import { readFileSync as readFileSync10, statSync as statSync2 } from "node:fs";
3
+ import * as path12 from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
 
6
6
  // src/utils/wstack-paths.ts
@@ -143,8 +143,8 @@ function resolveWstackPaths(opts) {
143
143
  }
144
144
 
145
145
  // src/coordination/agents/project-agent-identity.ts
146
- import { existsSync as existsSync4, readFileSync as readFileSync8 } from "node:fs";
147
- import * as path9 from "node:path";
146
+ import { existsSync as existsSync4, readFileSync as readFileSync9 } from "node:fs";
147
+ import * as path11 from "node:path";
148
148
 
149
149
  // src/security/capabilities.ts
150
150
  var ToolCapabilities = {
@@ -777,8 +777,8 @@ function loadProjectAgentConfig(role, projectRoot) {
777
777
  }
778
778
 
779
779
  // src/coordination/agents/project-agent-consolidation.ts
780
- import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync as rmSync2 } from "node:fs";
781
- import * as path5 from "node:path";
780
+ import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync4, rmSync as rmSync2 } from "node:fs";
781
+ import * as path6 from "node:path";
782
782
 
783
783
  // src/coordination/agents/project-agent-learning-entries.ts
784
784
  function tokenOverlap(a, b) {
@@ -916,6 +916,21 @@ function normalizeForComparison(text) {
916
916
  }
917
917
 
918
918
  // src/coordination/agents/project-agent-learning-structured.ts
919
+ function directiveTrials(entry) {
920
+ const count = (n) => typeof n === "number" && Number.isFinite(n) && n >= 0 ? Math.floor(n) : 0;
921
+ const applied = count(entry.applied);
922
+ const wins = Math.min(applied, count(entry.wins));
923
+ return { applied, wins, losses: applied - wins };
924
+ }
925
+ function directiveUtility(entry) {
926
+ const { applied, wins } = directiveTrials(entry);
927
+ return (wins + 1) / (applied + 2);
928
+ }
929
+ var DIRECTIVE_PROVEN_MIN_APPLIED = 5;
930
+ var DIRECTIVE_PROVEN_MIN_UTILITY = 0.7;
931
+ function isProvenDirective(entry) {
932
+ return directiveTrials(entry).applied >= DIRECTIVE_PROVEN_MIN_APPLIED && directiveUtility(entry) >= DIRECTIVE_PROVEN_MIN_UTILITY;
933
+ }
919
934
  function parseLearnedEntryStamp(entry) {
920
935
  const structuredMatch = entry.match(
921
936
  /<!--\s*learned-stamp:\s*category=([\w-]+);\s*capturedAt=([^;]+?)\s*-->/
@@ -1034,6 +1049,10 @@ function parseStructuredLearnedEntriesFromContent(raw, legacyEntries = splitLear
1034
1049
  const category = parseLearnedCategory(attributes["category"]) ?? "fact";
1035
1050
  const capturedAt = attributes["capturedAt"] ?? "";
1036
1051
  const skill = attributes["skill"];
1052
+ const trials = directiveTrials({
1053
+ applied: Number(attributes["applied"]),
1054
+ wins: Number(attributes["wins"])
1055
+ });
1037
1056
  const start = (stamp.index ?? 0) + stamp[0].length;
1038
1057
  const end = stamps[index + 1]?.index ?? raw.length;
1039
1058
  const parsed = parseEntryBody(raw.slice(start, end));
@@ -1045,7 +1064,8 @@ function parseStructuredLearnedEntriesFromContent(raw, legacyEntries = splitLear
1045
1064
  why: parsed.why || WHY_BY_CATEGORY[category],
1046
1065
  how: parsed.how,
1047
1066
  capturedAt,
1048
- ...skill ? { skill } : {}
1067
+ ...skill ? { skill } : {},
1068
+ ...trials.applied > 0 ? { applied: trials.applied, wins: trials.wins } : {}
1049
1069
  });
1050
1070
  }
1051
1071
  if (structured.length === 0) {
@@ -1069,17 +1089,31 @@ function parseStructuredLearnedEntriesFromContent(raw, legacyEntries = splitLear
1069
1089
  }
1070
1090
  function mergeStructuredEntries(existing, fresh) {
1071
1091
  const { what, why, how } = decomposeLearnedEntry(fresh.text, fresh.category);
1092
+ const key = directiveKey(fresh.text);
1093
+ const overlapping = existing.filter((entry) => tokenOverlap(entry.key, key) >= 0.55);
1094
+ const proven = overlapping.find(
1095
+ (entry) => entry.category === fresh.category && isProvenDirective(entry)
1096
+ );
1097
+ if (proven) return sortStructuredEntries([...existing]);
1098
+ const ancestor = overlapping.filter((entry) => entry.category === fresh.category).sort((a, b) => directiveTrials(b).applied - directiveTrials(a).applied)[0];
1099
+ const inherited = ancestor ? directiveTrials(ancestor) : { applied: 0, wins: 0 };
1072
1100
  const freshEntry = {
1073
- key: directiveKey(fresh.text),
1101
+ key,
1074
1102
  category: fresh.category,
1075
1103
  what,
1076
1104
  why,
1077
1105
  how,
1078
1106
  capturedAt: fresh.capturedAt,
1079
- ...fresh.skill ? { skill: fresh.skill } : {}
1107
+ ...fresh.skill ? { skill: fresh.skill } : {},
1108
+ ...inherited.applied > 0 ? { applied: inherited.applied, wins: inherited.wins } : {}
1080
1109
  };
1081
- const merged = existing.filter((entry) => tokenOverlap(entry.key, freshEntry.key) < 0.55);
1110
+ const merged = existing.filter(
1111
+ (entry) => entry.category !== fresh.category || tokenOverlap(entry.key, key) < 0.55
1112
+ );
1082
1113
  merged.push(freshEntry);
1114
+ return sortStructuredEntries(merged);
1115
+ }
1116
+ function sortStructuredEntries(merged) {
1083
1117
  const order = {
1084
1118
  warning: 0,
1085
1119
  convention: 1,
@@ -1129,10 +1163,15 @@ function renderLearnedInstructions(role, entries, capturedAt) {
1129
1163
  sections.push(SECTION_TITLE[category]);
1130
1164
  sections.push("");
1131
1165
  for (const entry of list) {
1166
+ const trials = directiveTrials(entry);
1132
1167
  const attributes = [
1133
1168
  `category=${entry.category}`,
1134
1169
  `capturedAt=${entry.capturedAt}`,
1135
- ...entry.skill ? [`skill=${entry.skill}`] : []
1170
+ ...entry.skill ? [`skill=${entry.skill}`] : [],
1171
+ // Zero counters are omitted so an entry with no track record renders
1172
+ // exactly as it did before this field existed — the parse→render
1173
+ // fixed point holds for every pre-existing buffer on disk.
1174
+ ...trials.applied > 0 ? [`applied=${trials.applied}`, `wins=${trials.wins}`] : []
1136
1175
  ].join("; ");
1137
1176
  sections.push(`<!-- learned-stamp: ${attributes} -->`);
1138
1177
  sections.push(`- **${entry.what}**`);
@@ -1156,26 +1195,155 @@ function enforceLearnedBudget(entries, capturedAt, maxBytes, role = "agent") {
1156
1195
  const kept = [...entries];
1157
1196
  const dropped = [];
1158
1197
  const size = (list) => Buffer.byteLength(renderLearnedInstructions(role, list, capturedAt), "utf8");
1198
+ const UTILITY_EPSILON = 1e-9;
1159
1199
  while (kept.length > 1 && size(kept) > maxBytes) {
1160
1200
  let victimIndex = 0;
1161
1201
  for (let i = 1; i < kept.length; i++) {
1162
1202
  const a = kept[i];
1163
1203
  const b = kept[victimIndex];
1164
1204
  const byPriority = DROP_PRIORITY[a.category] - DROP_PRIORITY[b.category];
1165
- if (byPriority < 0 || byPriority === 0 && a.capturedAt < b.capturedAt) victimIndex = i;
1205
+ if (byPriority !== 0) {
1206
+ if (byPriority < 0) victimIndex = i;
1207
+ continue;
1208
+ }
1209
+ const byUtility = directiveUtility(a) - directiveUtility(b);
1210
+ if (Math.abs(byUtility) > UTILITY_EPSILON) {
1211
+ if (byUtility < 0) victimIndex = i;
1212
+ continue;
1213
+ }
1214
+ if (a.capturedAt < b.capturedAt) victimIndex = i;
1166
1215
  }
1167
1216
  dropped.push(...kept.splice(victimIndex, 1));
1168
1217
  }
1169
1218
  return { kept, dropped };
1170
1219
  }
1171
1220
 
1221
+ // src/coordination/agents/project-agent-quarantine.ts
1222
+ import { readFileSync as readFileSync3 } from "node:fs";
1223
+ import * as path5 from "node:path";
1224
+ var QUARANTINE_MAX_BYTES = 64 * 1024;
1225
+ var RETIRED_MATCH_THRESHOLD = 0.55;
1226
+ function quarantinePath(role, projectRoot) {
1227
+ return path5.join(roleDir(role, projectRoot), "quarantine.md");
1228
+ }
1229
+ function readTextOrEmpty(filePath) {
1230
+ try {
1231
+ return readFileSync3(filePath, "utf8");
1232
+ } catch {
1233
+ return "";
1234
+ }
1235
+ }
1236
+ function appendQuarantine(role, retired, at, projectRoot) {
1237
+ if (retired.length === 0) return;
1238
+ const filePath = quarantinePath(role, projectRoot);
1239
+ const existing = readTextOrEmpty(filePath);
1240
+ const header = existing ? "" : `# Retired directives for \`${role}\`
1241
+
1242
+ > Directives that were exercised repeatedly and kept correlating with
1243
+ > failure. They are no longer injected anywhere. Kept for audit \u2014 a directive
1244
+ > can be right about a project that has since changed.
1245
+
1246
+ `;
1247
+ const block = retired.map((entry) => {
1248
+ const { applied, wins, losses } = directiveTrials(entry);
1249
+ const attributes = [
1250
+ `at=${at}`,
1251
+ `category=${entry.category}`,
1252
+ ...entry.skill ? [`skill=${entry.skill}`] : [],
1253
+ `applied=${applied}`,
1254
+ `wins=${wins}`
1255
+ ].join("; ");
1256
+ return [
1257
+ `<!-- quarantined: ${attributes} -->`,
1258
+ `- **${entry.what}**`,
1259
+ ` - *Record:* ${wins} succeeded / ${losses} failed of ${applied} applied`,
1260
+ ""
1261
+ ].join("\n");
1262
+ }).join("\n");
1263
+ let next = `${existing}${header}${block}`;
1264
+ if (Buffer.byteLength(next, "utf8") > QUARANTINE_MAX_BYTES) {
1265
+ const marker = "<!-- quarantined:";
1266
+ const overflow = Buffer.byteLength(next, "utf8") - QUARANTINE_MAX_BYTES;
1267
+ const cut = next.indexOf(marker, overflow);
1268
+ next = cut === -1 ? block : next.slice(cut);
1269
+ }
1270
+ writeTextAtomically(filePath, next);
1271
+ }
1272
+ function readQuarantinedDirectives(role, projectRoot) {
1273
+ const raw = readTextOrEmpty(quarantinePath(role, projectRoot));
1274
+ if (!raw) return [];
1275
+ const out = [];
1276
+ const stamps = [...raw.matchAll(/<!--\s*quarantined:\s*([^>]*?)\s*-->/g)];
1277
+ for (const [index, stamp] of stamps.entries()) {
1278
+ const attributes = {};
1279
+ for (const part of (stamp[1] ?? "").split(";")) {
1280
+ const eq = part.indexOf("=");
1281
+ if (eq === -1) continue;
1282
+ attributes[part.slice(0, eq).trim()] = part.slice(eq + 1).trim();
1283
+ }
1284
+ const start = (stamp.index ?? 0) + stamp[0].length;
1285
+ const end = stamps[index + 1]?.index ?? raw.length;
1286
+ const what = /^-\s+\*\*([\s\S]+?)\*\*\s*$/m.exec(raw.slice(start, end))?.[1]?.replace(/\s+/g, " ").trim();
1287
+ if (!what) continue;
1288
+ const skill = attributes["skill"];
1289
+ out.push({ what, ...skill ? { skill } : {} });
1290
+ }
1291
+ return out;
1292
+ }
1293
+ function bareClaim(line) {
1294
+ return line.replace(/^\s*[-*+]\s+/, "").replace(/^\s*\d+[.)]\s+/, "").replace(/\*\*/g, "").replace(/`/g, "").trim();
1295
+ }
1296
+ function indentOf(line) {
1297
+ return line.length - line.trimStart().length;
1298
+ }
1299
+ function scrubRetiredLines(text, retired) {
1300
+ if (!text.trim() || retired.length === 0) return text;
1301
+ const keys = retired.map((what) => normalizeForComparison(what));
1302
+ const lines = text.split("\n");
1303
+ const kept = [];
1304
+ for (let i = 0; i < lines.length; i++) {
1305
+ const line = lines[i];
1306
+ const claim = bareClaim(line);
1307
+ if (claim.length >= MIN_INSTRUCTIVE_LENGTH && keys.some(
1308
+ (key) => tokenOverlap(key, normalizeForComparison(claim)) >= RETIRED_MATCH_THRESHOLD
1309
+ )) {
1310
+ const base = indentOf(line);
1311
+ while (i + 1 < lines.length) {
1312
+ const next = lines[i + 1];
1313
+ if (!next.trim() || indentOf(next) <= base) break;
1314
+ i++;
1315
+ }
1316
+ continue;
1317
+ }
1318
+ kept.push(line);
1319
+ }
1320
+ return kept.join("\n").replace(/\n{3,}/g, "\n\n").trim();
1321
+ }
1322
+ function hasDirectiveContent(text) {
1323
+ return text.split("\n").some((line) => {
1324
+ const trimmed = line.trimStart();
1325
+ if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith(">")) return false;
1326
+ if (/^-{3,}\s*$/.test(trimmed)) return false;
1327
+ if (/^\*[^*].*\*$/.test(trimmed)) return false;
1328
+ return bareClaim(line).length >= MIN_INSTRUCTIVE_LENGTH;
1329
+ });
1330
+ }
1331
+ function retiredDirectivesToWarnAbout(role, currentDirectives, projectRoot, skill) {
1332
+ const currentKeys = currentDirectives.map((text) => normalizeForComparison(text));
1333
+ return readQuarantinedDirectives(role, projectRoot).filter((entry) => skill ? entry.skill === skill : true).map((entry) => entry.what).filter(
1334
+ (what) => !currentKeys.some(
1335
+ (key) => tokenOverlap(key, normalizeForComparison(what)) >= RETIRED_MATCH_THRESHOLD
1336
+ )
1337
+ );
1338
+ }
1339
+
1172
1340
  // src/coordination/agents/project-agent-consolidation.ts
1173
1341
  function learnedPath(role, projectRoot) {
1174
- return path5.join(roleDir(role, projectRoot), "learned.md");
1342
+ return path6.join(roleDir(role, projectRoot), "learned.md");
1175
1343
  }
1176
1344
  function loadProjectAgentLearnedText(role, projectRoot) {
1177
1345
  try {
1178
- return readFileSync3(learnedPath(role, projectRoot), "utf8").trim();
1346
+ return readFileSync4(learnedPath(role, projectRoot), "utf8").trim();
1179
1347
  } catch {
1180
1348
  return "";
1181
1349
  }
@@ -1185,28 +1353,27 @@ function readRawLearnedEntries(role, projectRoot) {
1185
1353
  return parseStructuredLearnedEntriesFromContent(raw, splitLearnedEntries(raw));
1186
1354
  }
1187
1355
  function consolidationPath(role, projectRoot) {
1188
- return path5.join(roleDir(role, projectRoot), "consolidated.md");
1356
+ return path6.join(roleDir(role, projectRoot), "consolidated.md");
1357
+ }
1358
+ function consolidatedDocumentPath(role, projectRoot) {
1359
+ return consolidationPath(assertProjectAgentRole(role), projectRoot);
1189
1360
  }
1190
1361
  function consolidationMetaPath(role, projectRoot) {
1191
- return path5.join(roleDir(role, projectRoot), "consolidation.json");
1362
+ return path6.join(roleDir(role, projectRoot), "consolidation.json");
1192
1363
  }
1193
1364
  function archivePath(role, at, projectRoot) {
1194
- return path5.join(
1195
- roleDir(role, projectRoot),
1196
- "archive",
1197
- `learned-${at.replace(/[:.]/g, "-")}.md`
1198
- );
1365
+ return path6.join(roleDir(role, projectRoot), "archive", `learned-${at.replace(/[:.]/g, "-")}.md`);
1199
1366
  }
1200
1367
  function loadProjectAgentConsolidated(role, projectRoot) {
1201
1368
  try {
1202
- return readFileSync3(consolidationPath(role, projectRoot), "utf8").trim();
1369
+ return readFileSync4(consolidationPath(role, projectRoot), "utf8").trim();
1203
1370
  } catch {
1204
1371
  return "";
1205
1372
  }
1206
1373
  }
1207
1374
  function loadConsolidationMetadata(role, projectRoot) {
1208
1375
  try {
1209
- const raw = readFileSync3(consolidationMetaPath(role, projectRoot), "utf8");
1376
+ const raw = readFileSync4(consolidationMetaPath(role, projectRoot), "utf8");
1210
1377
  const parsed = JSON.parse(raw);
1211
1378
  if (typeof parsed === "object" && parsed !== null && typeof parsed.consolidatedAt === "string" && typeof parsed.sourceEntryCount === "number") {
1212
1379
  return parsed;
@@ -1277,10 +1444,13 @@ function clearProjectAgentConsolidated(role, projectRoot) {
1277
1444
  function buildConsolidationInstruction(role, projectRoot) {
1278
1445
  const normalizedRole = assertProjectAgentRole(role);
1279
1446
  const entries = readRawLearnedEntries(normalizedRole, projectRoot);
1280
- const rawEntries = entries.map(
1281
- (entry) => entry.how ? `${entry.what}
1282
- Anchors: ${entry.how.split("\n").join(", ")}` : entry.what
1283
- );
1447
+ const rawEntries = entries.map((entry) => {
1448
+ const lines = [entry.what];
1449
+ if (entry.how) lines.push(` Anchors: ${entry.how.split("\n").join(", ")}`);
1450
+ const { applied, wins } = directiveTrials(entry);
1451
+ if (applied > 0) lines.push(` Track record: applied ${applied}\xD7, ${wins} succeeded`);
1452
+ return lines.join("\n");
1453
+ });
1284
1454
  const existingConsolidation = loadProjectAgentConsolidated(normalizedRole, projectRoot);
1285
1455
  const rawBody = rawEntries.map((entry, i) => `### Entry ${i + 1}
1286
1456
 
@@ -1300,6 +1470,7 @@ ${entry}`).join("\n\n");
1300
1470
  "6. **Structured format.** Organize the content under clear markdown headings (##) by topic. Use bullet points for individual facts. Group by category (Conventions, Patterns, Warnings, Project Facts) when it helps scannability.",
1301
1471
  "7. **Preserve actionable anchors.** Keep exact file paths, command names, package names, and configuration values that make a directive concrete and runnable.",
1302
1472
  "8. **No filler.** Do not include meta-commentary about the consolidation process. The document should read as if it were always a single authoritative reference.",
1473
+ "9. **Trust the track record over your own judgement of plausibility.** An entry may carry `Track record: applied N\xD7, M succeeded` \u2014 completed tasks that exercised it, and how many of those succeeded. Keep the ones that keep working, even when they read as obvious. An entry applied several times with few successes has been tested and has failed the test: drop it, or narrow it to the condition under which it actually holds. Entries with no track record are unproven rather than bad \u2014 judge them on merit, but do not let one displace a proven entry.",
1303
1474
  "",
1304
1475
  rawEntries.length > 0 ? `## Raw learned entries (${rawEntries.length} total)
1305
1476
 
@@ -1313,6 +1484,21 @@ ${rawBody}` : "## Raw learned entries\n\n(No raw entries yet \u2014 return an em
1313
1484
  ${existingConsolidation}`
1314
1485
  );
1315
1486
  }
1487
+ const retired = retiredDirectivesToWarnAbout(
1488
+ normalizedRole,
1489
+ entries.map((entry) => entry.what),
1490
+ projectRoot
1491
+ );
1492
+ if (retired.length > 0) {
1493
+ sections.push(
1494
+ "",
1495
+ `## Retired \u2014 do not reinstate (${retired.length})
1496
+
1497
+ These were exercised repeatedly and kept correlating with failed tasks. If the existing document above still states any of them, drop it.
1498
+
1499
+ ${retired.map((what) => `- ${what}`).join("\n")}`
1500
+ );
1501
+ }
1316
1502
  sections.push(
1317
1503
  "",
1318
1504
  "## Output",
@@ -1392,7 +1578,7 @@ var BUILT_IN_KNOWLEDGE_MANIFESTS = {
1392
1578
  };
1393
1579
 
1394
1580
  // src/coordination/agents/project-agent-learning-policy.ts
1395
- import { readFileSync as readFileSync4 } from "node:fs";
1581
+ import { readFileSync as readFileSync5 } from "node:fs";
1396
1582
  var DEFAULT_LEARNING_POLICY = {
1397
1583
  enabled: true,
1398
1584
  lifetimeCaptureCount: 0
@@ -1400,23 +1586,26 @@ var DEFAULT_LEARNING_POLICY = {
1400
1586
  function loadProjectAgentLearningPolicy(role, projectRoot) {
1401
1587
  try {
1402
1588
  const parsed = JSON.parse(
1403
- readFileSync4(learningPolicyPath(role, projectRoot), "utf8")
1589
+ readFileSync5(learningPolicyPath(role, projectRoot), "utf8")
1404
1590
  );
1405
1591
  return {
1406
1592
  enabled: parsed.enabled !== false,
1407
1593
  lifetimeCaptureCount: typeof parsed.lifetimeCaptureCount === "number" && Number.isInteger(parsed.lifetimeCaptureCount) && parsed.lifetimeCaptureCount >= 0 ? parsed.lifetimeCaptureCount : 0,
1408
1594
  ...typeof parsed.lastCaptureAt === "string" ? { lastCaptureAt: parsed.lastCaptureAt } : {},
1409
- ...parsed.lastCaptureSource === "automatic" || parsed.lastCaptureSource === "manual" || parsed.lastCaptureSource === "taught" ? { lastCaptureSource: parsed.lastCaptureSource } : {}
1595
+ ...parsed.lastCaptureSource === "automatic" || parsed.lastCaptureSource === "manual" || parsed.lastCaptureSource === "taught" ? { lastCaptureSource: parsed.lastCaptureSource } : {},
1596
+ ...typeof parsed.lastOptimizeAt === "string" ? { lastOptimizeAt: parsed.lastOptimizeAt } : {}
1410
1597
  };
1411
1598
  } catch {
1412
1599
  return { ...DEFAULT_LEARNING_POLICY };
1413
1600
  }
1414
1601
  }
1415
1602
  function updateProjectAgentLearningPolicy(role, patch, projectRoot) {
1416
- const current = loadProjectAgentLearningPolicy(role, projectRoot);
1603
+ const defined = Object.fromEntries(
1604
+ Object.entries(patch).filter(([, value]) => value !== void 0)
1605
+ );
1417
1606
  const updated = {
1418
- ...current,
1419
- ...patch.enabled !== void 0 ? { enabled: patch.enabled } : {}
1607
+ ...loadProjectAgentLearningPolicy(role, projectRoot),
1608
+ ...defined
1420
1609
  };
1421
1610
  writeTextAtomically(
1422
1611
  learningPolicyPath(role, projectRoot),
@@ -1425,14 +1614,20 @@ function updateProjectAgentLearningPolicy(role, patch, projectRoot) {
1425
1614
  );
1426
1615
  return updated;
1427
1616
  }
1617
+ function recordProjectAgentOptimizePass(role, projectRoot, at = (/* @__PURE__ */ new Date()).toISOString()) {
1618
+ try {
1619
+ updateProjectAgentLearningPolicy(role, { lastOptimizeAt: at }, projectRoot);
1620
+ } catch {
1621
+ }
1622
+ }
1428
1623
 
1429
1624
  // src/coordination/agents/project-agent-skill-layer.ts
1430
- import { readdirSync as readdirSync2, readFileSync as readFileSync6, rmSync as rmSync3 } from "node:fs";
1431
- import * as path7 from "node:path";
1625
+ import { readdirSync as readdirSync2, readFileSync as readFileSync7, rmSync as rmSync3 } from "node:fs";
1626
+ import * as path8 from "node:path";
1432
1627
 
1433
1628
  // src/coordination/agents/project-agent-profile.ts
1434
- import { existsSync as existsSync2, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync5 } from "node:fs";
1435
- import * as path6 from "node:path";
1629
+ import { existsSync as existsSync2, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync6 } from "node:fs";
1630
+ import * as path7 from "node:path";
1436
1631
  function slugifyProjectAgentRole(name) {
1437
1632
  const slug = name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[._-]+|[._-]+$/g, "").slice(0, 96);
1438
1633
  return assertProjectAgentRole(slug);
@@ -1440,7 +1635,7 @@ function slugifyProjectAgentRole(name) {
1440
1635
  function loadProjectAgentProfile(role, projectRoot) {
1441
1636
  try {
1442
1637
  const parsed = JSON.parse(
1443
- readFileSync5(projectAgentProfilePath(role, projectRoot), "utf8")
1638
+ readFileSync6(projectAgentProfilePath(role, projectRoot), "utf8")
1444
1639
  );
1445
1640
  const normalizedRole = assertProjectAgentRole(parsed.role ?? role);
1446
1641
  const baseRole = assertProjectAgentRole(parsed.baseRole ?? "generic");
@@ -1511,7 +1706,7 @@ function createProjectAgent(input, projectRoot) {
1511
1706
  ""
1512
1707
  ].join("\n");
1513
1708
  mkdirSync3(dir, { recursive: true });
1514
- writeTextAtomically(path6.join(dir, "identity.md"), identity);
1709
+ writeTextAtomically(path7.join(dir, "identity.md"), identity);
1515
1710
  writeTextAtomically(
1516
1711
  projectAgentProfilePath(role, projectRoot),
1517
1712
  `${JSON.stringify(profile, null, 2)}
@@ -1826,17 +2021,17 @@ function assertProjectSkillName(name) {
1826
2021
  return normalized;
1827
2022
  }
1828
2023
  function projectSkillsDir(role, projectRoot) {
1829
- return path7.join(roleDir(role, projectRoot), "skills");
2024
+ return path8.join(roleDir(role, projectRoot), "skills");
1830
2025
  }
1831
2026
  function projectSkillAugmentationPath(role, skill, projectRoot) {
1832
- return path7.join(projectSkillsDir(role, projectRoot), `${assertProjectSkillName(skill)}.md`);
2027
+ return path8.join(projectSkillsDir(role, projectRoot), `${assertProjectSkillName(skill)}.md`);
1833
2028
  }
1834
2029
  function projectSkillAffinityPath(role, projectRoot) {
1835
- return path7.join(projectSkillsDir(role, projectRoot), "affinity.json");
2030
+ return path8.join(projectSkillsDir(role, projectRoot), "affinity.json");
1836
2031
  }
1837
2032
  function loadProjectSkillAugmentation(role, skill, projectRoot) {
1838
2033
  try {
1839
- return readFileSync6(projectSkillAugmentationPath(role, skill, projectRoot), "utf8").trim();
2034
+ return readFileSync7(projectSkillAugmentationPath(role, skill, projectRoot), "utf8").trim();
1840
2035
  } catch {
1841
2036
  return "";
1842
2037
  }
@@ -1845,13 +2040,32 @@ function saveProjectSkillAugmentation(role, skill, content, projectRoot) {
1845
2040
  const filePath = projectSkillAugmentationPath(role, skill, projectRoot);
1846
2041
  const trimmed = content.trim();
1847
2042
  if (!trimmed) return filePath;
1848
- const bounded = Buffer.byteLength(trimmed, "utf8") > SKILL_AUGMENTATION_MAX_BYTES ? `${trimmed.slice(0, SKILL_AUGMENTATION_MAX_BYTES)}
1849
-
1850
- _(truncated at ${SKILL_AUGMENTATION_MAX_BYTES} bytes)_` : trimmed;
1851
- writeTextAtomically(filePath, `${bounded}
2043
+ writeTextAtomically(filePath, `${boundAugmentation(trimmed)}
1852
2044
  `);
1853
2045
  return filePath;
1854
2046
  }
2047
+ function boundAugmentation(text) {
2048
+ if (Buffer.byteLength(text, "utf8") <= SKILL_AUGMENTATION_MAX_BYTES) return text;
2049
+ const notice = `
2050
+
2051
+ _(truncated at ${SKILL_AUGMENTATION_MAX_BYTES} bytes)_`;
2052
+ const lines = text.split("\n");
2053
+ const title = lines[0] ?? "";
2054
+ let budget = SKILL_AUGMENTATION_MAX_BYTES - Buffer.byteLength(`${title}
2055
+ ${notice}`, "utf8") - Buffer.byteLength("\u2026\n", "utf8");
2056
+ const tail = [];
2057
+ for (let i = lines.length - 1; i > 0; i--) {
2058
+ const cost = Buffer.byteLength(`${lines[i]}
2059
+ `, "utf8");
2060
+ if (cost > budget) break;
2061
+ tail.unshift(lines[i]);
2062
+ budget -= cost;
2063
+ }
2064
+ if (tail.length === 0) return `${title.slice(0, SKILL_AUGMENTATION_MAX_BYTES)}${notice}`;
2065
+ return `${title}
2066
+ \u2026
2067
+ ${tail.join("\n").trimEnd()}${notice}`;
2068
+ }
1855
2069
  function clearProjectSkillAugmentation(role, skill, projectRoot) {
1856
2070
  const target = skill ? projectSkillAugmentationPath(role, skill, projectRoot) : projectSkillsDir(role, projectRoot);
1857
2071
  try {
@@ -1866,7 +2080,25 @@ function listProjectSkillAugmentations(role, projectRoot) {
1866
2080
  return [];
1867
2081
  }
1868
2082
  }
1869
- function renderSkillAugmentation(role, skill, directives, updatedAt) {
2083
+ function renderSkillAugmentation(role, skill, directives, updatedAt, existing) {
2084
+ const priorBody = stripAugmentationFooter(existing ?? "");
2085
+ const priorLines = priorBody.split("\n").map((line) => normalizeForComparison(line.replace(/^[-*]\s+/, ""))).filter(Boolean);
2086
+ const fresh = directives.filter(
2087
+ (directive) => !priorLines.some((line) => tokenOverlap(line, normalizeForComparison(directive)) >= 0.55)
2088
+ );
2089
+ if (priorBody) {
2090
+ if (fresh.length === 0) return `${(existing ?? "").trim()}
2091
+ `;
2092
+ return [
2093
+ priorBody,
2094
+ "",
2095
+ ...fresh.map((directive) => `- ${directive}`),
2096
+ "",
2097
+ "---",
2098
+ `*Distilled ${updatedAt} \xB7 ${fresh.length} new directive${fresh.length === 1 ? "" : "s"}*`,
2099
+ ""
2100
+ ].join("\n");
2101
+ }
1870
2102
  return [
1871
2103
  `# Project practice: \`${skill}\` (role: \`${role}\`)`,
1872
2104
  "",
@@ -1880,7 +2112,10 @@ function renderSkillAugmentation(role, skill, directives, updatedAt) {
1880
2112
  ""
1881
2113
  ].join("\n");
1882
2114
  }
1883
- function buildSkillDistillInstruction(role, skill, directives, existing) {
2115
+ function stripAugmentationFooter(text) {
2116
+ return text.replace(/\n*---\s*\n\*(?:Distilled|Truncated)[^\n]*\*\s*$/i, "").replace(/\n*_\(truncated at \d+ bytes\)_\s*$/i, "").trimEnd();
2117
+ }
2118
+ function buildSkillDistillInstruction(role, skill, directives, existing, retired = []) {
1884
2119
  const sections = [
1885
2120
  `You are refining the \`${skill}\` skill for the "${role}" agent **in this project**.`,
1886
2121
  "",
@@ -1894,6 +2129,7 @@ function buildSkillDistillInstruction(role, skill, directives, existing) {
1894
2129
  "4. **Merge, do not append.** Combine overlapping directives into one sharper rule. The output must be shorter than the input.",
1895
2130
  "5. **Structure.** Short `##` sections when it helps (e.g. Commands, Conventions, Pitfalls); bullets otherwise.",
1896
2131
  "6. **Budget.** Stay under 400 words. Drop the weakest directives rather than compressing everything into noise.",
2132
+ "7. **Weigh the track record.** Some directives carry one, shown as `[applied N\xD7, M ok]` \u2014 the number of completed tasks that exercised the directive and how many of those succeeded. Lead with the ones that keep working. A directive applied several times with few successes has been tested and found wanting: drop it, or state the narrower condition under which it actually holds. A directive with no record is unproven, not bad \u2014 keep it if it is sound, but do not let it displace a proven one.",
1897
2133
  "",
1898
2134
  `## Directives captured for \`${skill}\` (${directives.length})`,
1899
2135
  "",
@@ -1902,6 +2138,16 @@ function buildSkillDistillInstruction(role, skill, directives, existing) {
1902
2138
  if (existing?.trim()) {
1903
2139
  sections.push("", "## Existing addendum (improve upon it)", "", existing.trim());
1904
2140
  }
2141
+ if (retired.length > 0) {
2142
+ sections.push(
2143
+ "",
2144
+ `## Retired \u2014 do not reinstate (${retired.length})`,
2145
+ "",
2146
+ "These were exercised repeatedly and kept correlating with failed tasks. If the existing addendum still states any of them, drop it.",
2147
+ "",
2148
+ ...retired.map((what) => `- ${what}`)
2149
+ );
2150
+ }
1905
2151
  sections.push(
1906
2152
  "",
1907
2153
  "## Output",
@@ -1927,7 +2173,7 @@ function loadSkillAffinity(role, projectRoot) {
1927
2173
  const normalizedRole = assertProjectAgentRole(role);
1928
2174
  try {
1929
2175
  const parsed = JSON.parse(
1930
- readFileSync6(projectSkillAffinityPath(normalizedRole, projectRoot), "utf8")
2176
+ readFileSync7(projectSkillAffinityPath(normalizedRole, projectRoot), "utf8")
1931
2177
  );
1932
2178
  const entries = {};
1933
2179
  for (const [skill, entry] of Object.entries(parsed.entries ?? {})) {
@@ -1976,9 +2222,11 @@ function recordSkillLoad(role, skills, projectRoot) {
1976
2222
  });
1977
2223
  }
1978
2224
  function recordSkillOutcome(role, skills, ok, projectRoot) {
2225
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1979
2226
  mutateAffinity(role, skills, projectRoot, (entry) => {
1980
2227
  if (ok) entry.succeeded += 1;
1981
2228
  else entry.failed += 1;
2229
+ entry.lastUsedAt = now;
1982
2230
  });
1983
2231
  }
1984
2232
  function recordSkillLearned(role, skill, projectRoot) {
@@ -1992,24 +2240,30 @@ function setSkillPinned(role, skill, pinned, projectRoot) {
1992
2240
  else delete entry.pinned;
1993
2241
  });
1994
2242
  }
1995
- function scoreSkillAffinity(entry) {
1996
- if (!entry) return 0;
1997
- if (entry.pinned) return Number.POSITIVE_INFINITY;
1998
- const outcomes = entry.succeeded + entry.failed;
1999
- const successRate = outcomes === 0 ? 0 : (entry.succeeded + 1) / (outcomes + 2);
2000
- return entry.learned * 2 + successRate * 3 + Math.min(entry.loaded, 10) * 0.1;
2243
+ var SKILL_EVIDENCE_HALF_LIFE_DAYS = 30;
2244
+ function scoreSkillAffinity(entry, now = Date.now()) {
2245
+ const record = entry ?? EMPTY_ENTRY;
2246
+ if (record.pinned) return Number.POSITIVE_INFINITY;
2247
+ const outcomes = record.succeeded + record.failed;
2248
+ const successRate = (record.succeeded + 1) / (outcomes + 2);
2249
+ const ageDays = record.lastUsedAt ? (now - Date.parse(record.lastUsedAt)) / 864e5 : Number.NaN;
2250
+ const recency = Number.isFinite(ageDays) ? 0.5 ** (Math.max(0, ageDays) / SKILL_EVIDENCE_HALF_LIFE_DAYS) : 1;
2251
+ const evidence = (successRate - 0.5) * 4 * recency;
2252
+ const exploration = 1 / Math.sqrt(1 + record.loaded);
2253
+ return Math.log1p(record.learned) * 2 + evidence + exploration;
2001
2254
  }
2002
2255
  function rankRoleSkills(role, candidates, projectRoot, limit = DEFAULT_EAGER_SKILL_LIMIT) {
2003
2256
  const unique = [...new Set(candidates.filter(isProjectSkillName))];
2004
2257
  if (unique.length <= limit) return unique;
2005
2258
  const affinity = loadSkillAffinity(role, projectRoot);
2006
2259
  const augmented = new Set(listProjectSkillAugmentations(role, projectRoot));
2260
+ const now = Date.now();
2007
2261
  return unique.map((skill, index) => ({
2008
2262
  skill,
2009
2263
  index,
2010
2264
  // An existing addendum is itself evidence the project developed this
2011
2265
  // skill, even when the affinity file was reset or hand-edited.
2012
- score: scoreSkillAffinity(affinity.entries[skill]) + (augmented.has(skill) ? 1 : 0)
2266
+ score: scoreSkillAffinity(affinity.entries[skill], now) + (augmented.has(skill) ? 1 : 0)
2013
2267
  })).sort((a, b) => b.score === a.score ? a.index - b.index : b.score - a.score).slice(0, limit).map((item) => item.skill);
2014
2268
  }
2015
2269
  function resolveRoleSkillCandidates(role, projectRoot) {
@@ -2079,6 +2333,13 @@ function isTextBlock(b) {
2079
2333
  // src/coordination/agents/project-agent-optimizer.ts
2080
2334
  var DEFAULT_MAX_TOKENS = 8e3;
2081
2335
  var DEFAULT_TIMEOUT_MS = 12e4;
2336
+ function describeDirective(entry) {
2337
+ const parts = [entry.what];
2338
+ if (entry.how) parts.push(`(anchors: ${entry.how.split("\n").join(", ")})`);
2339
+ const { applied, wins } = directiveTrials(entry);
2340
+ if (applied > 0) parts.push(`[applied ${applied}\xD7, ${wins} ok]`);
2341
+ return parts.join(" ");
2342
+ }
2082
2343
  function unwrapWholeDocumentFence(text) {
2083
2344
  const wholeDocFence = /^```(?:markdown|md)?[^\n]*\n([\s\S]*?)\n?```\s*$/i;
2084
2345
  const inner = wholeDocFence.exec(text.trim())?.[1];
@@ -2125,13 +2386,14 @@ async function optimizeProjectAgentLearning(role, projectRoot, options = {}) {
2125
2386
  for (const entry of entries) {
2126
2387
  if (!entry.skill) continue;
2127
2388
  const bucket = bySkill.get(entry.skill) ?? [];
2128
- bucket.push(entry.how ? `${entry.what} (anchors: ${entry.how.split("\n").join(", ")})` : entry.what);
2389
+ bucket.push(describeDirective(entry));
2129
2390
  bySkill.set(entry.skill, bucket);
2130
2391
  }
2131
2392
  const at = (/* @__PURE__ */ new Date()).toISOString();
2132
2393
  const refreshed = [];
2133
2394
  for (const [skill, directives] of bySkill) {
2134
- let body = renderSkillAugmentation(normalizedRole, skill, directives, at);
2395
+ const existing = loadProjectSkillAugmentation(normalizedRole, skill, projectRoot);
2396
+ let body = renderSkillAugmentation(normalizedRole, skill, directives, at, existing);
2135
2397
  if (options.llm) {
2136
2398
  try {
2137
2399
  const synthesized = await complete(
@@ -2141,7 +2403,8 @@ async function optimizeProjectAgentLearning(role, projectRoot, options = {}) {
2141
2403
  normalizedRole,
2142
2404
  skill,
2143
2405
  directives,
2144
- loadProjectSkillAugmentation(normalizedRole, skill, projectRoot)
2406
+ existing,
2407
+ retiredDirectivesToWarnAbout(normalizedRole, directives, projectRoot, skill)
2145
2408
  ),
2146
2409
  options
2147
2410
  );
@@ -2154,6 +2417,7 @@ async function optimizeProjectAgentLearning(role, projectRoot, options = {}) {
2154
2417
  }
2155
2418
  const { instruction } = buildConsolidationInstruction(normalizedRole, projectRoot);
2156
2419
  if (!options.llm) {
2420
+ recordProjectAgentOptimizePass(normalizedRole, projectRoot, at);
2157
2421
  return { ...base, status: "no-llm", skills: refreshed, instruction };
2158
2422
  }
2159
2423
  let content;
@@ -2168,6 +2432,7 @@ async function optimizeProjectAgentLearning(role, projectRoot, options = {}) {
2168
2432
  };
2169
2433
  }
2170
2434
  if (!content) {
2435
+ recordProjectAgentOptimizePass(normalizedRole, projectRoot);
2171
2436
  return { ...base, status: "empty-synthesis", skills: refreshed, model: options.llm.model };
2172
2437
  }
2173
2438
  const prune = options.prune ?? true;
@@ -2186,6 +2451,7 @@ async function optimizeProjectAgentLearning(role, projectRoot, options = {}) {
2186
2451
  error: error instanceof Error ? error.message : "failed to persist consolidation"
2187
2452
  };
2188
2453
  }
2454
+ recordProjectAgentOptimizePass(normalizedRole, projectRoot);
2189
2455
  return {
2190
2456
  ...base,
2191
2457
  status: "optimized",
@@ -2223,17 +2489,16 @@ function resolveAutoOptimizePolicy(overrides) {
2223
2489
  function evaluateAutoOptimize(role, projectRoot, policy, now = Date.now()) {
2224
2490
  if (!policy.enabled) return { eligible: false, reason: "disabled" };
2225
2491
  const normalizedRole = assertProjectAgentRole(role);
2226
- if (!loadProjectAgentLearningPolicy(normalizedRole, projectRoot).enabled) {
2492
+ const learningPolicy = loadProjectAgentLearningPolicy(normalizedRole, projectRoot);
2493
+ if (!learningPolicy.enabled) {
2227
2494
  return { eligible: false, reason: "learning-paused" };
2228
2495
  }
2229
2496
  const entries = readRawLearnedEntries(normalizedRole, projectRoot);
2230
2497
  if (entries.length < policy.minEntries) return { eligible: false, reason: "too-few-entries" };
2231
2498
  const meta = loadConsolidationMetadata(normalizedRole, projectRoot);
2232
- if (meta) {
2233
- const since = now - Date.parse(meta.consolidatedAt);
2234
- if (Number.isFinite(since) && since < policy.minIntervalMs) {
2235
- return { eligible: false, reason: "cooling-down" };
2236
- }
2499
+ const lastPassAt = [meta?.consolidatedAt, learningPolicy.lastOptimizeAt].map((stamp) => stamp ? Date.parse(stamp) : Number.NaN).filter((value) => Number.isFinite(value));
2500
+ if (lastPassAt.length > 0 && now - Math.max(...lastPassAt) < policy.minIntervalMs) {
2501
+ return { eligible: false, reason: "cooling-down" };
2237
2502
  }
2238
2503
  const bytes = entries.reduce(
2239
2504
  (sum, entry) => sum + Buffer.byteLength(`${entry.what}${entry.why}${entry.how}`, "utf8"),
@@ -2374,26 +2639,130 @@ var LearningOptimizationScheduler = class {
2374
2639
  }
2375
2640
  };
2376
2641
 
2642
+ // src/coordination/agents/project-agent-directive-outcome.ts
2643
+ import * as path9 from "node:path";
2644
+ var DIRECTIVE_QUARANTINE_MIN_APPLIED = 8;
2645
+ var DIRECTIVE_QUARANTINE_MAX_UTILITY = 0.3;
2646
+ var MIN_ANCHOR_LENGTH = 6;
2647
+ var DISTINCTIVE_TOKEN_LENGTH = 6;
2648
+ var MIN_DISTINCTIVE_TOKENS = 5;
2649
+ var DISTINCTIVE_COVERAGE = 0.6;
2650
+ function learnedPath2(role, projectRoot) {
2651
+ return path9.join(roleDir(role, projectRoot), "learned.md");
2652
+ }
2653
+ function directiveAnchors(entry) {
2654
+ return entry.how.split("\n").map((line) => line.replace(/`/g, "").trim().toLowerCase()).filter((anchor) => anchor.length >= MIN_ANCHOR_LENGTH);
2655
+ }
2656
+ function distinctiveTokens(text) {
2657
+ return [
2658
+ ...new Set(
2659
+ text.toLowerCase().replace(/[^\w\s./@-]/g, " ").split(/\s+/).map((token) => token.replace(/^[./@-]+/, "").replace(/[./@-]+$/, "")).filter((token) => token.length >= DISTINCTIVE_TOKEN_LENGTH)
2660
+ )
2661
+ ];
2662
+ }
2663
+ function directiveWasApplied(entry, report) {
2664
+ const haystack = report.toLowerCase();
2665
+ if (!haystack) return false;
2666
+ const anchors = directiveAnchors(entry);
2667
+ if (anchors.length > 0) {
2668
+ return anchors.some((anchor) => haystack.includes(anchor));
2669
+ }
2670
+ const tokens = distinctiveTokens(entry.what);
2671
+ if (tokens.length < MIN_DISTINCTIVE_TOKENS) return false;
2672
+ const hits = tokens.filter((token) => haystack.includes(token)).length;
2673
+ return hits >= MIN_DISTINCTIVE_TOKENS && hits / tokens.length >= DISTINCTIVE_COVERAGE;
2674
+ }
2675
+ function recordDirectiveOutcomes(role, report, succeeded, projectRoot) {
2676
+ const normalizedRole = assertProjectAgentRole(role);
2677
+ const empty = { role: normalizedRole, attributed: 0, quarantined: [] };
2678
+ if (!report.trim()) return empty;
2679
+ const entries = readRawLearnedEntries(normalizedRole, projectRoot);
2680
+ if (entries.length === 0) return empty;
2681
+ const updated = [];
2682
+ const retired = [];
2683
+ let attributed = 0;
2684
+ for (const entry of entries) {
2685
+ if (!directiveWasApplied(entry, report)) {
2686
+ updated.push(entry);
2687
+ continue;
2688
+ }
2689
+ attributed++;
2690
+ const trials = directiveTrials(entry);
2691
+ const next = {
2692
+ ...entry,
2693
+ applied: trials.applied + 1,
2694
+ wins: trials.wins + (succeeded ? 1 : 0)
2695
+ };
2696
+ if (directiveTrials(next).applied >= DIRECTIVE_QUARANTINE_MIN_APPLIED && directiveUtility(next) < DIRECTIVE_QUARANTINE_MAX_UTILITY) {
2697
+ retired.push(next);
2698
+ continue;
2699
+ }
2700
+ updated.push(next);
2701
+ }
2702
+ if (attributed === 0) return empty;
2703
+ const at = (/* @__PURE__ */ new Date()).toISOString();
2704
+ const newest = updated.map((entry) => entry.capturedAt).filter(Boolean).sort().pop() ?? at;
2705
+ writeTextAtomically(
2706
+ learnedPath2(normalizedRole, projectRoot),
2707
+ renderLearnedInstructions(normalizedRole, updated, newest)
2708
+ );
2709
+ if (retired.length > 0) {
2710
+ appendQuarantine(normalizedRole, retired, at, projectRoot);
2711
+ scrubRetiredFromInjectedDocuments(normalizedRole, retired, projectRoot);
2712
+ }
2713
+ return {
2714
+ role: normalizedRole,
2715
+ attributed,
2716
+ quarantined: retired.map((entry) => entry.what)
2717
+ };
2718
+ }
2719
+ function scrubRetiredFromInjectedDocuments(role, retired, projectRoot) {
2720
+ const texts = retired.map((entry) => entry.what);
2721
+ for (const skill of new Set(retired.map((entry) => entry.skill).filter(Boolean))) {
2722
+ try {
2723
+ const current = loadProjectSkillAugmentation(role, skill, projectRoot);
2724
+ if (!current) continue;
2725
+ const scrubbed = scrubRetiredLines(current, texts);
2726
+ if (scrubbed === current.trim()) continue;
2727
+ if (!hasDirectiveContent(scrubbed)) {
2728
+ clearProjectSkillAugmentation(role, skill, projectRoot);
2729
+ continue;
2730
+ }
2731
+ writeTextAtomically(projectSkillAugmentationPath(role, skill, projectRoot), `${scrubbed}
2732
+ `);
2733
+ } catch {
2734
+ }
2735
+ }
2736
+ try {
2737
+ const consolidated = loadProjectAgentConsolidated(role, projectRoot);
2738
+ if (!consolidated) return;
2739
+ const scrubbed = scrubRetiredLines(consolidated, texts);
2740
+ if (scrubbed === consolidated.trim() || !hasDirectiveContent(scrubbed)) return;
2741
+ writeTextAtomically(consolidatedDocumentPath(role, projectRoot), scrubbed);
2742
+ } catch {
2743
+ }
2744
+ }
2745
+
2377
2746
  // src/coordination/agents/project-agent-files.ts
2378
2747
  import {
2379
2748
  existsSync as existsSync3,
2380
2749
  mkdirSync as mkdirSync4,
2381
2750
  readdirSync as readdirSync3,
2382
- readFileSync as readFileSync7,
2751
+ readFileSync as readFileSync8,
2383
2752
  rmSync as rmSync4
2384
2753
  } from "node:fs";
2385
- import * as path8 from "node:path";
2754
+ import * as path10 from "node:path";
2386
2755
  function updateProjectAgentLearned(role, content, projectRoot, mode = "append") {
2387
2756
  const dir = roleDir(role, projectRoot);
2388
2757
  mkdirSync4(dir, { recursive: true });
2389
- const filePath = path8.join(dir, "learned.md");
2758
+ const filePath = path10.join(dir, "learned.md");
2390
2759
  if (mode === "replace") {
2391
2760
  writeTextAtomically(filePath, content);
2392
2761
  return filePath;
2393
2762
  }
2394
2763
  const existing = (() => {
2395
2764
  try {
2396
- return readFileSync7(filePath, "utf8");
2765
+ return readFileSync8(filePath, "utf8");
2397
2766
  } catch {
2398
2767
  return "";
2399
2768
  }
@@ -2421,7 +2790,7 @@ function updateProjectAgentLearned(role, content, projectRoot, mode = "append")
2421
2790
  function updateProjectAgentIdentity(role, content, projectRoot) {
2422
2791
  const dir = roleDir(role, projectRoot);
2423
2792
  mkdirSync4(dir, { recursive: true });
2424
- const filePath = path8.join(dir, "identity.md");
2793
+ const filePath = path10.join(dir, "identity.md");
2425
2794
  writeTextAtomically(filePath, content);
2426
2795
  return filePath;
2427
2796
  }
@@ -2429,7 +2798,7 @@ function updateProjectAgentConfig(role, config, projectRoot) {
2429
2798
  const validated = validateProjectAgentConfig(config);
2430
2799
  const dir = roleDir(role, projectRoot);
2431
2800
  mkdirSync4(dir, { recursive: true });
2432
- const filePath = path8.join(dir, "config.json");
2801
+ const filePath = path10.join(dir, "config.json");
2433
2802
  writeTextAtomically(filePath, `${JSON.stringify(validated, null, 2)}
2434
2803
  `);
2435
2804
  return filePath;
@@ -2437,7 +2806,7 @@ function updateProjectAgentConfig(role, config, projectRoot) {
2437
2806
  function updateProjectAgentKnowledge(role, manifest, projectRoot) {
2438
2807
  const dir = roleDir(role, projectRoot);
2439
2808
  mkdirSync4(dir, { recursive: true });
2440
- const filePath = path8.join(dir, "knowledge.json");
2809
+ const filePath = path10.join(dir, "knowledge.json");
2441
2810
  writeTextAtomically(filePath, `${JSON.stringify(manifest, null, 2)}
2442
2811
  `);
2443
2812
  return filePath;
@@ -2463,18 +2832,18 @@ function refreshProjectAgentIdentity(role, projectRoot) {
2463
2832
  const dir = roleDir(role, projectRoot);
2464
2833
  mkdirSync4(dir, { recursive: true });
2465
2834
  for (const file of ["learned.md", "identity.md", "consolidated.md", "consolidation.json"]) {
2466
- const fp = path8.join(dir, file);
2835
+ const fp = path10.join(dir, file);
2467
2836
  try {
2468
2837
  rmSync4(fp, { force: true });
2469
2838
  } catch {
2470
2839
  }
2471
2840
  }
2472
2841
  writeTextAtomically(
2473
- path8.join(dir, "learned.md"),
2842
+ path10.join(dir, "learned.md"),
2474
2843
  renderLearnedInstructions(role, [], (/* @__PURE__ */ new Date()).toISOString())
2475
2844
  );
2476
2845
  writeTextAtomically(
2477
- path8.join(dir, "identity.md"),
2846
+ path10.join(dir, "identity.md"),
2478
2847
  `# Project identity for ${role}
2479
2848
 
2480
2849
  <!-- Describe how this agent should behave in the context of this project. -->
@@ -2487,7 +2856,7 @@ function listProjectAgentRoles(projectRoot) {
2487
2856
  try {
2488
2857
  return readdirSync3(dir).filter((name) => {
2489
2858
  if (!isProjectAgentRoleName(name)) return false;
2490
- const sub = path8.join(dir, name);
2859
+ const sub = path10.join(dir, name);
2491
2860
  try {
2492
2861
  return [
2493
2862
  "learned.md",
@@ -2498,7 +2867,7 @@ function listProjectAgentRoles(projectRoot) {
2498
2867
  "profile.json",
2499
2868
  "consolidated.md",
2500
2869
  "consolidation.json"
2501
- ].some((file) => existsSync3(path8.join(sub, file)));
2870
+ ].some((file) => existsSync3(path10.join(sub, file)));
2502
2871
  } catch {
2503
2872
  return false;
2504
2873
  }
@@ -2565,25 +2934,25 @@ function createProjectAgentRoster(baseRoster, projectRoot) {
2565
2934
  });
2566
2935
  }
2567
2936
  function loadProjectAgentIdentity(role, projectRoot) {
2568
- const identityPath = path9.join(roleDir(role, projectRoot), "identity.md");
2937
+ const identityPath = path11.join(roleDir(role, projectRoot), "identity.md");
2569
2938
  try {
2570
- return readFileSync8(identityPath, "utf8").trim();
2939
+ return readFileSync9(identityPath, "utf8").trim();
2571
2940
  } catch {
2572
2941
  return "";
2573
2942
  }
2574
2943
  }
2575
2944
  function loadProjectAgentLearned(role, projectRoot) {
2576
- const learnedPath2 = path9.join(roleDir(role, projectRoot), "learned.md");
2945
+ const learnedPath3 = path11.join(roleDir(role, projectRoot), "learned.md");
2577
2946
  try {
2578
- return readFileSync8(learnedPath2, "utf8").trim();
2947
+ return readFileSync9(learnedPath3, "utf8").trim();
2579
2948
  } catch {
2580
2949
  return "";
2581
2950
  }
2582
2951
  }
2583
2952
  function loadRoleKnowledgeManifest(role, projectRoot) {
2584
- const projectPath = path9.join(roleDir(role, projectRoot), "knowledge.json");
2953
+ const projectPath = path11.join(roleDir(role, projectRoot), "knowledge.json");
2585
2954
  try {
2586
- const raw = readFileSync8(projectPath, "utf8");
2955
+ const raw = readFileSync9(projectPath, "utf8");
2587
2956
  return JSON.parse(raw);
2588
2957
  } catch {
2589
2958
  return BUILT_IN_KNOWLEDGE_MANIFESTS[role];
@@ -2724,7 +3093,7 @@ ${learnedContent}`);
2724
3093
  }
2725
3094
  }
2726
3095
  const effectiveProjectRoot = projectRoot || process.cwd();
2727
- const learnedFilePath = path9.join(
3096
+ const learnedFilePath = path11.join(
2728
3097
  effectiveProjectRoot,
2729
3098
  ".wrongstack",
2730
3099
  "agents",
@@ -2751,6 +3120,8 @@ The file below stores **learning data for this project's "${role}" agent** \u201
2751
3120
  - **Self-contained** \u2014 understandable without the surrounding session context.
2752
3121
  - **Front-load concrete anchors** \u2014 commands in backticks, package names like \`@wrongstack/core\`, file paths like \`packages/core/src/.../foo.ts\`. The structured-list renderer extracts these as the "how" for the entry.
2753
3122
 
3123
+ **Your directives are scored against real outcomes.** After every task the runtime checks which stored directives were actually exercised \u2014 it matches their anchors against the report \u2014 and folds that task's success or failure into each one's record. A directive that keeps correlating with success outlives newer arrivals and survives rewording; one that has been exercised repeatedly and kept correlating with failure is retired and stops being injected. Two consequences for how you write them: anchors are what make a directive *measurable*, not just runnable, so an anchorless directive can never earn a record; and a directive you are unsure about costs nothing to write, because the loop will find out.
3124
+
2754
3125
  **Tag the skill you are developing.** When a directive refines one of your skills, mark it: \`## LEARNED [skill: testing]\`. Tagged directives are distilled into that skill's project addendum, so the lesson arrives as part of the skill itself on every future run instead of as a loose fact. Untagged directives are routed automatically when the wording makes the target obvious, and stay role-level otherwise.
2755
3126
 
2756
3127
  **Bad** (session log \u2014 rejected at capture time):
@@ -2816,7 +3187,7 @@ ${additions}
2816
3187
  ${contextEnd}`;
2817
3188
  }
2818
3189
  function resolveCaptureKey(role, projectRoot) {
2819
- return `${path9.resolve(projectRoot ?? process.cwd())}\0${assertProjectAgentRole(role)}`;
3190
+ return `${path11.resolve(projectRoot ?? process.cwd())}\0${assertProjectAgentRole(role)}`;
2820
3191
  }
2821
3192
  function canCaptureNewLearned(role, _existingSize, isManual, projectRoot) {
2822
3193
  if (isManual) return void 0;
@@ -2834,13 +3205,13 @@ function canCaptureNewLearned(role, _existingSize, isManual, projectRoot) {
2834
3205
  }
2835
3206
  function getProjectAgentLearnStats(role, projectRoot) {
2836
3207
  const dir = roleDir(role, projectRoot);
2837
- const learnedPath_ = path9.join(dir, "learned.md");
2838
- const identityPath_ = path9.join(dir, "identity.md");
2839
- const configPath_ = path9.join(dir, "config.json");
2840
- const knowledgePath_ = path9.join(dir, "knowledge.json");
3208
+ const learnedPath_ = path11.join(dir, "learned.md");
3209
+ const identityPath_ = path11.join(dir, "identity.md");
3210
+ const configPath_ = path11.join(dir, "config.json");
3211
+ const knowledgePath_ = path11.join(dir, "knowledge.json");
2841
3212
  let learnedText = "";
2842
3213
  try {
2843
- learnedText = readFileSync8(learnedPath_, "utf8");
3214
+ learnedText = readFileSync9(learnedPath_, "utf8");
2844
3215
  } catch {
2845
3216
  }
2846
3217
  const entries = parseStructuredLearnedEntries(role, projectRoot);
@@ -2854,12 +3225,18 @@ function getProjectAgentLearnStats(role, projectRoot) {
2854
3225
  const lastTs = runtimeLastTs ?? (Number.isFinite(persistedLastTs) ? persistedLastTs : null);
2855
3226
  const cooldownRemaining = lastTs ? Math.max(0, CAPTURE_COOLDOWN_MS - (Date.now() - lastTs)) : 0;
2856
3227
  const freq = window.count;
3228
+ const trials = entries.map((entry) => directiveTrials(entry));
3229
+ const totalApplied = trials.reduce((sum, trial) => sum + trial.applied, 0);
3230
+ const totalWins = trials.reduce((sum, trial) => sum + trial.wins, 0);
2857
3231
  return {
2858
3232
  role,
2859
3233
  exists,
2860
3234
  entryCount: entries.length,
2861
3235
  skills: skillAugmentations,
2862
3236
  skilledEntryCount: entries.filter((entry) => entry.skill).length,
3237
+ appliedEntryCount: trials.filter((trial) => trial.applied > 0).length,
3238
+ deadEntryCount: trials.filter((trial) => trial.applied === 0).length,
3239
+ directiveHitRate: totalApplied > 0 ? totalWins / totalApplied : null,
2863
3240
  totalBytes: Buffer.byteLength(learnedText, "utf8"),
2864
3241
  lastCapture: lastTs ? new Date(lastTs).toISOString() : null,
2865
3242
  lastCaptureTimestamp: lastTs,
@@ -3032,7 +3409,7 @@ function captureLearnedFromAgentOutputDetailed(output, role, projectRoot, isManu
3032
3409
  normalizedRole
3033
3410
  );
3034
3411
  const newContent = renderLearnedInstructions(normalizedRole, budget.kept, nowIso);
3035
- writeTextAtomically(path9.join(roleDir(normalizedRole, projectRoot), "learned.md"), newContent);
3412
+ writeTextAtomically(path11.join(roleDir(normalizedRole, projectRoot), "learned.md"), newContent);
3036
3413
  recordCaptureAttempt(key, now.getTime());
3037
3414
  for (const skill of new Set(routedSkills)) {
3038
3415
  try {
@@ -3040,16 +3417,14 @@ function captureLearnedFromAgentOutputDetailed(output, role, projectRoot, isManu
3040
3417
  } catch {
3041
3418
  }
3042
3419
  }
3043
- const nextPolicy = {
3044
- ...policy,
3045
- lifetimeCaptureCount: policy.lifetimeCaptureCount + captured,
3046
- lastCaptureAt: now.toISOString(),
3047
- lastCaptureSource: isManual ? "manual" : "automatic"
3048
- };
3049
- writeTextAtomically(
3050
- learningPolicyPath(normalizedRole, projectRoot),
3051
- `${JSON.stringify(nextPolicy, null, 2)}
3052
- `
3420
+ updateProjectAgentLearningPolicy(
3421
+ normalizedRole,
3422
+ {
3423
+ lifetimeCaptureCount: policy.lifetimeCaptureCount + captured,
3424
+ lastCaptureAt: now.toISOString(),
3425
+ lastCaptureSource: isManual ? "manual" : "automatic"
3426
+ },
3427
+ projectRoot
3053
3428
  );
3054
3429
  return {
3055
3430
  role: normalizedRole,
@@ -3145,38 +3520,38 @@ function loadTechVersionPolicy() {
3145
3520
  return techVersionPolicyBody;
3146
3521
  }
3147
3522
  function policyCandidateDirs() {
3148
- const here = path10.dirname(fileURLToPath(import.meta.url));
3523
+ const here = path12.dirname(fileURLToPath(import.meta.url));
3149
3524
  const profileInstructions = resolveWstackPaths({ projectRoot: process.cwd() }).globalInstructions;
3150
3525
  return [
3151
- path10.resolve(here, "../../../../instructions/agents"),
3152
- path10.resolve(here, "../../../instructions/agents"),
3153
- path10.resolve(here, "../../instructions/agents"),
3154
- path10.resolve(here, "../instructions/agents"),
3155
- path10.resolve(here, "instructions/agents"),
3156
- path10.join(profileInstructions, "agents"),
3526
+ path12.resolve(here, "../../../../instructions/agents"),
3527
+ path12.resolve(here, "../../../instructions/agents"),
3528
+ path12.resolve(here, "../../instructions/agents"),
3529
+ path12.resolve(here, "../instructions/agents"),
3530
+ path12.resolve(here, "instructions/agents"),
3531
+ path12.join(profileInstructions, "agents"),
3157
3532
  profileInstructions
3158
3533
  ].filter((dir, index, all) => all.indexOf(dir) === index);
3159
3534
  }
3160
3535
  function policyBody() {
3161
- const here = path10.dirname(fileURLToPath(import.meta.url));
3536
+ const here = path12.dirname(fileURLToPath(import.meta.url));
3162
3537
  const roots = [
3163
- path10.resolve(here, "_policy/tech-version.md"),
3164
- path10.resolve(here, "../_policy/tech-version.md"),
3165
- path10.resolve(here, "../../../instructions/agents/_policy/tech-version.md"),
3166
- path10.resolve(here, "../../instructions/agents/_policy/tech-version.md"),
3167
- path10.resolve(here, "../instructions/agents/_policy/tech-version.md"),
3168
- path10.resolve(here, "../../../../instructions/agents/_policy/tech-version.md"),
3169
- path10.resolve(here, "instructions/agents/_policy/tech-version.md")
3538
+ path12.resolve(here, "_policy/tech-version.md"),
3539
+ path12.resolve(here, "../_policy/tech-version.md"),
3540
+ path12.resolve(here, "../../../instructions/agents/_policy/tech-version.md"),
3541
+ path12.resolve(here, "../../instructions/agents/_policy/tech-version.md"),
3542
+ path12.resolve(here, "../instructions/agents/_policy/tech-version.md"),
3543
+ path12.resolve(here, "../../../../instructions/agents/_policy/tech-version.md"),
3544
+ path12.resolve(here, "instructions/agents/_policy/tech-version.md")
3170
3545
  ];
3171
3546
  for (const file of roots) {
3172
3547
  try {
3173
- return readFileSync9(file, "utf8").trim();
3548
+ return readFileSync10(file, "utf8").trim();
3174
3549
  } catch {
3175
3550
  }
3176
3551
  }
3177
3552
  for (const dir of policyCandidateDirs()) {
3178
3553
  try {
3179
- return readFileSync9(path10.join(dir, "_policy", "tech-version.md"), "utf8").trim();
3554
+ return readFileSync10(path12.join(dir, "_policy", "tech-version.md"), "utf8").trim();
3180
3555
  } catch {
3181
3556
  }
3182
3557
  }
@@ -3195,7 +3570,7 @@ function agentPrompt(id) {
3195
3570
  let resolved = "";
3196
3571
  for (const dir of agentPromptDirCandidates(envDir)) {
3197
3572
  try {
3198
- resolved = readFileSync9(path10.join(dir, fileName), "utf8").trim();
3573
+ resolved = readFileSync10(path12.join(dir, fileName), "utf8").trim();
3199
3574
  break;
3200
3575
  } catch {
3201
3576
  }
@@ -3221,16 +3596,16 @@ function agentPromptDirCandidates(envDir) {
3221
3596
  const candKey = `${envDir}\0${profileInstructions}`;
3222
3597
  const cached = candidateCache.get(candKey);
3223
3598
  if (cached !== void 0) return cached;
3224
- const here = path10.dirname(fileURLToPath(import.meta.url));
3599
+ const here = path12.dirname(fileURLToPath(import.meta.url));
3225
3600
  const explicitDir = envDir || void 0;
3226
3601
  const candidates = [
3227
- ...explicitDir ? [path10.resolve(explicitDir)] : [],
3228
- path10.join(profileInstructions, "agents"),
3229
- path10.resolve(here, "../../../../instructions/agents"),
3230
- path10.resolve(here, "../../../instructions/agents"),
3231
- path10.resolve(here, "../../instructions/agents"),
3232
- path10.resolve(here, "../instructions/agents"),
3233
- path10.resolve(here, "instructions/agents")
3602
+ ...explicitDir ? [path12.resolve(explicitDir)] : [],
3603
+ path12.join(profileInstructions, "agents"),
3604
+ path12.resolve(here, "../../../../instructions/agents"),
3605
+ path12.resolve(here, "../../../instructions/agents"),
3606
+ path12.resolve(here, "../../instructions/agents"),
3607
+ path12.resolve(here, "../instructions/agents"),
3608
+ path12.resolve(here, "instructions/agents")
3234
3609
  ];
3235
3610
  const ordered = candidates.sort((a, b) => Number(!isDirectory(a)) - Number(!isDirectory(b)));
3236
3611
  candidateCache.set(candKey, ordered);
@@ -3287,8 +3662,16 @@ var TOOLS = {
3287
3662
  "test",
3288
3663
  "mailbox"
3289
3664
  ],
3290
- /** Version control. */
3291
- vcs: ["read", "grep", "glob", "git", "diff"],
3665
+ /**
3666
+ * Version control.
3667
+ *
3668
+ * `mailbox` is in every preset on purpose: a subagent that hits a wall must
3669
+ * be able to say so. This was the one preset without it, which left the `git`
3670
+ * and `release` roles able to fail but not to ask — the two roles whose work
3671
+ * most often needs a decision from the leader (force-push, tag collision,
3672
+ * dirty tree) and least often has a safe default.
3673
+ */
3674
+ vcs: ["read", "grep", "glob", "git", "diff", "mailbox"],
3292
3675
  /** Dependency management + CVE audit. */
3293
3676
  deps: ["read", "grep", "glob", "install", "outdated", "audit", "json", "mailbox"],
3294
3677
  /** Documentation authoring. */
@@ -5783,6 +6166,10 @@ export {
5783
6166
  DEFAULT_AUTO_OPTIMIZE_POLICY,
5784
6167
  DEFAULT_EAGER_SKILL_LIMIT,
5785
6168
  DELIVERY_AGENTS,
6169
+ DIRECTIVE_PROVEN_MIN_APPLIED,
6170
+ DIRECTIVE_PROVEN_MIN_UTILITY,
6171
+ DIRECTIVE_QUARANTINE_MAX_UTILITY,
6172
+ DIRECTIVE_QUARANTINE_MIN_APPLIED,
5786
6173
  DISCOVERY_AGENTS,
5787
6174
  DOMAIN_AGENTS,
5788
6175
  HEAVY_BUDGET,
@@ -5801,6 +6188,7 @@ export {
5801
6188
  RUNTIME_CAPABILITY_MANIFEST,
5802
6189
  SHADOW_AGENT_SKILLS,
5803
6190
  SKILL_AUGMENTATION_MAX_BYTES,
6191
+ SKILL_EVIDENCE_HALF_LIFE_DAYS,
5804
6192
  SPECIALIST_TOOLS,
5805
6193
  TOOLS,
5806
6194
  VERIFY_AGENTS,
@@ -5828,10 +6216,14 @@ export {
5828
6216
  classifyLearnedEntry,
5829
6217
  clearProjectAgentConsolidated,
5830
6218
  clearProjectSkillAugmentation,
6219
+ consolidatedDocumentPath,
5831
6220
  createProjectAgent,
5832
6221
  createProjectAgentRoster,
5833
6222
  decomposeLearnedEntry,
5834
6223
  detectLearnedConflicts,
6224
+ directiveTrials,
6225
+ directiveUtility,
6226
+ directiveWasApplied,
5835
6227
  enforceLearnedBudget,
5836
6228
  evaluateAutoOptimize,
5837
6229
  existsSync4 as existsSync,
@@ -5842,6 +6234,7 @@ export {
5842
6234
  isConsolidated,
5843
6235
  isDeprecatedRuntimeToolAlias,
5844
6236
  isKnownRuntimeCapability,
6237
+ isProvenDirective,
5845
6238
  listProjectAgentLearnedEntries,
5846
6239
  listProjectAgentRoles,
5847
6240
  listProjectSkillAugmentations,
@@ -5864,8 +6257,12 @@ export {
5864
6257
  parseLearnedEntryStamp,
5865
6258
  parseStructuredLearnedEntries,
5866
6259
  parseStructuredLearnedEntriesFromContent,
6260
+ quarantinePath,
5867
6261
  rankRoleSkills,
6262
+ readQuarantinedDirectives,
5868
6263
  readRawLearnedEntries,
6264
+ recordDirectiveOutcomes,
6265
+ recordProjectAgentOptimizePass,
5869
6266
  recordSkillLoad,
5870
6267
  recordSkillOutcome,
5871
6268
  refreshProjectAgentIdentity,
@@ -5876,11 +6273,14 @@ export {
5876
6273
  resetProjectAgentIdentity,
5877
6274
  resolveAutoOptimizePolicy,
5878
6275
  resolveRoleSkillCandidates,
6276
+ retiredDirectivesToWarnAbout,
5879
6277
  routeDirectiveToSkill,
5880
6278
  runtimeCapabilityForTool,
5881
6279
  runtimeToolReferencesFromText,
5882
6280
  saveProjectAgentConsolidated,
5883
6281
  saveProjectSkillAugmentation,
6282
+ scoreSkillAffinity,
6283
+ scrubRetiredLines,
5884
6284
  setSkillPinned,
5885
6285
  slugifyProjectAgentRole,
5886
6286
  splitLearnedEntries,