mister-wolf 2.18.4 → 2.19.0

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 (72) hide show
  1. package/README.md +1 -1
  2. package/README.ru.md +16 -14
  3. package/dist/adapters/cli/cli-entry.d.ts.map +1 -1
  4. package/dist/adapters/cli/cli-entry.js +10 -1
  5. package/dist/adapters/cli/cli-entry.js.map +1 -1
  6. package/dist/adapters/cli/commands/claim.d.ts.map +1 -1
  7. package/dist/adapters/cli/commands/claim.js +2 -1
  8. package/dist/adapters/cli/commands/claim.js.map +1 -1
  9. package/dist/adapters/cli/commands/memory-sync.d.ts.map +1 -1
  10. package/dist/adapters/cli/commands/memory-sync.js +3 -0
  11. package/dist/adapters/cli/commands/memory-sync.js.map +1 -1
  12. package/dist/adapters/cli/commands/memory-upgrade.d.ts +133 -0
  13. package/dist/adapters/cli/commands/memory-upgrade.d.ts.map +1 -1
  14. package/dist/adapters/cli/commands/memory-upgrade.js +507 -5
  15. package/dist/adapters/cli/commands/memory-upgrade.js.map +1 -1
  16. package/dist/adapters/cli/commands/upgrade-projects.d.ts +17 -0
  17. package/dist/adapters/cli/commands/upgrade-projects.d.ts.map +1 -0
  18. package/dist/adapters/cli/commands/upgrade-projects.js +75 -0
  19. package/dist/adapters/cli/commands/upgrade-projects.js.map +1 -0
  20. package/dist/adapters/cli/report-renderer.d.ts +43 -0
  21. package/dist/adapters/cli/report-renderer.d.ts.map +1 -0
  22. package/dist/adapters/cli/report-renderer.js +184 -0
  23. package/dist/adapters/cli/report-renderer.js.map +1 -0
  24. package/dist/adapters/fs/claim-registry.d.ts.map +1 -1
  25. package/dist/adapters/fs/claim-registry.js +15 -2
  26. package/dist/adapters/fs/claim-registry.js.map +1 -1
  27. package/dist/adapters/fs/projects-registry.d.ts +21 -0
  28. package/dist/adapters/fs/projects-registry.d.ts.map +1 -1
  29. package/dist/adapters/fs/projects-registry.js +54 -1
  30. package/dist/adapters/fs/projects-registry.js.map +1 -1
  31. package/dist/adapters/fs/update-map.d.ts +102 -0
  32. package/dist/adapters/fs/update-map.d.ts.map +1 -0
  33. package/dist/adapters/fs/update-map.js +95 -0
  34. package/dist/adapters/fs/update-map.js.map +1 -0
  35. package/dist/adapters/fs/upgrade-lock.d.ts +23 -0
  36. package/dist/adapters/fs/upgrade-lock.d.ts.map +1 -0
  37. package/dist/adapters/fs/upgrade-lock.js +59 -0
  38. package/dist/adapters/fs/upgrade-lock.js.map +1 -0
  39. package/dist/adapters/render/opencode/opencode-renderer.d.ts +13 -1
  40. package/dist/adapters/render/opencode/opencode-renderer.d.ts.map +1 -1
  41. package/dist/adapters/render/opencode/opencode-renderer.js +76 -8
  42. package/dist/adapters/render/opencode/opencode-renderer.js.map +1 -1
  43. package/dist/app/use-cases/claim-artifact.d.ts +4 -1
  44. package/dist/app/use-cases/claim-artifact.d.ts.map +1 -1
  45. package/dist/app/use-cases/claim-artifact.js +9 -2
  46. package/dist/app/use-cases/claim-artifact.js.map +1 -1
  47. package/dist/app/use-cases/project-step.d.ts +52 -0
  48. package/dist/app/use-cases/project-step.d.ts.map +1 -0
  49. package/dist/app/use-cases/project-step.js +77 -0
  50. package/dist/app/use-cases/project-step.js.map +1 -0
  51. package/dist/app/use-cases/scan-unregistered.d.ts +25 -0
  52. package/dist/app/use-cases/scan-unregistered.d.ts.map +1 -0
  53. package/dist/app/use-cases/scan-unregistered.js +74 -0
  54. package/dist/app/use-cases/scan-unregistered.js.map +1 -0
  55. package/dist/app/use-cases/seed-base-playbooks.d.ts +10 -0
  56. package/dist/app/use-cases/seed-base-playbooks.d.ts.map +1 -1
  57. package/dist/app/use-cases/seed-base-playbooks.js +54 -13
  58. package/dist/app/use-cases/seed-base-playbooks.js.map +1 -1
  59. package/dist/app/use-cases/upgrade-projects.d.ts +61 -0
  60. package/dist/app/use-cases/upgrade-projects.d.ts.map +1 -0
  61. package/dist/app/use-cases/upgrade-projects.js +379 -0
  62. package/dist/app/use-cases/upgrade-projects.js.map +1 -0
  63. package/dist/app/use-cases/verify-against-map.d.ts +48 -0
  64. package/dist/app/use-cases/verify-against-map.d.ts.map +1 -0
  65. package/dist/app/use-cases/verify-against-map.js +161 -0
  66. package/dist/app/use-cases/verify-against-map.js.map +1 -0
  67. package/dist/ports/base-set-renderer.port.d.ts +13 -0
  68. package/dist/ports/base-set-renderer.port.d.ts.map +1 -1
  69. package/package.json +3 -2
  70. package/templates/.wolf/router.log +1 -1
  71. package/templates/opencode/plugins/wolf-router.ts +158 -48
  72. package/update-map.json +204 -0
@@ -3,12 +3,25 @@ export interface RenderOutcome {
3
3
  file: string;
4
4
  action: RenderAction;
5
5
  reason?: string;
6
+ /** 2.19.0 (design §«Сверка»): структурный флаг mutated-skip — сверка
7
+ * verifyAgainstMap различает skipped-identical / skipped-mutated полем,
8
+ * не парсингом reason-строки. */
9
+ mutated?: boolean;
6
10
  }
7
11
  /** Контекст подстановки моделей (onboarding v2, §4.5): {{model.primary}} / {{model.worker}}. */
8
12
  export interface ModelContext {
9
13
  primary: string;
10
14
  worker: string;
11
15
  }
16
+ /** Действие read-only инспекции цели (T10, REQ-08, design §«--check»): цель
17
+ * сверяется с текущим рендером без единой записи на диск. foreign — unstamped
18
+ * файл на месте цели (wx-политика: чужое, вне покрытия). */
19
+ export type InspectAction = 'identical' | 'differs' | 'missing' | 'mutated' | 'foreign' | 'unknown';
20
+ export interface InspectOutcome {
21
+ /** rel-путь от baseDir — тот же ключ, что RenderOutcome.file. */
22
+ file: string;
23
+ action: InspectAction;
24
+ }
12
25
  export interface BakeResolver {
13
26
  /** Вернуть тело playbook'а для вживления (bake-in) или null. Источник — память целевого проекта (§4). */
14
27
  (baseName: string): string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"base-set-renderer.port.d.ts","sourceRoot":"","sources":["../../src/ports/base-set-renderer.port.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AAEvF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,gGAAgG;AAChG,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,yGAAyG;IACzG,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,0GAA0G;IAC1G,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;IAC/B;+CAC2C;IAC3C,KAAK,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,uGAAuG;IACvG,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IACtF;;;;OAIG;IACH,WAAW,CACT,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,GAC7B,OAAO,CAAC;QAAE,QAAQ,EAAE,aAAa,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CAC/D"}
1
+ {"version":3,"file":"base-set-renderer.port.d.ts","sourceRoot":"","sources":["../../src/ports/base-set-renderer.port.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AAEvF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;sCAEkC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,gGAAgG;AAChG,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;6DAE6D;AAC7D,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAEpG,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,yGAAyG;IACzG,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,0GAA0G;IAC1G,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;IAC/B;+CAC2C;IAC3C,KAAK,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,uGAAuG;IACvG,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IACtF;;;;OAIG;IACH,WAAW,CACT,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,GAC7B,OAAO,CAAC;QAAE,QAAQ,EAAE,aAAa,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CAC/D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mister-wolf",
3
- "version": "2.18.4",
3
+ "version": "2.19.0",
4
4
  "description": "Local-first project memory for AI coding agents: decisions, lessons, tools and processes that outlive the session",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -24,7 +24,8 @@
24
24
  },
25
25
  "files": [
26
26
  "dist",
27
- "templates"
27
+ "templates",
28
+ "update-map.json"
28
29
  ],
29
30
  "scripts": {
30
31
  "build": "tsc",
@@ -1 +1 @@
1
- 2026-10-15T21:56:04.402Z agent-id=v2-router-probe-xyz playbook=hit name=fallback variant=fallback injected=yes ms=0 bytes=1274
1
+ 2026-10-24T17:36:23.551Z agent-id=v2-router-probe-xyz playbook=hit name=fallback variant=fallback source=default resolution=error face_id=- version=- hash=- session=opc-277ab5c4-acf5-40ed-bfb9-6037555e3f73 injected=yes ms=1 bytes=1274
@@ -6,21 +6,23 @@
6
6
  * `agent-id: <id>` В ТЕЛЕ рамки агента (frontmatter в system-промпт не
7
7
  * попадает — известная грабля) → 2.18.4: канонический face-resolver
8
8
  * (`wolf face resolve`: type=face, status=active, owner_skill === agentId;
9
- * ровно одно активное лицо, иначе отказ) → при miss/conflict legacy-путь:
10
- * wolf search `<agentId> playbook` (после
11
- * таксономии 2.13 канон — note+facet howto, type-фильтр не применим) → get +
12
- * гвард owner_skill === agentId | `skill:${agentId}` (legacy) → первый
13
- * прошедший гвард кандидат (ранний стоп, A2-б) → инжект в system-промпт.
9
+ * ровно одно активное лицо, иначе отказ) → 2.18.5 (AC1) поведение разделено:
10
+ * unique → доставка лица; none → legacy-поиск note/howto/playbook с гвардами
11
+ * владельца, типа (лица — только резолвер) и статуса (active); multiple →
12
+ * конфликт зафиксирован, методика НЕ выбирается и НЕ внедряется; resolver
13
+ * error → диагностируемый сбой — оба случая без запасного поиска (ревью
14
+ * 2.18.4: текстовый fallback обходил конфликт через proposed-лицо).
14
15
  * Вариант Б (CR-2026-10-13-02): инъекция мемоизируется по opencode-сессии
15
16
  * (пересборка system в пределах сессии получает запечатанный текст той же
16
17
  * версии — без переключения посередине задания), кэш версионируется ключом
17
18
  * head-записи — новая сессия всегда получает актуальную версию.
18
19
  *
19
- * Fallback (T012, волна 1.1): канон приоритетен; при miss для ЛЮБОГО
20
- * agent-id инъецируется встроенный универсальный FALLBACK_PLAYBOOK —
21
- * доставка не зависит от того, звала ли рамка wolf search (слой №2).
22
- * В router.log: variant=canonical | variant=fallback. Реестр доставок =
23
- * сами playbook-объекты через owner_skill, отдельного конфига нет.
20
+ * Fallback (T012, волна 1.1): методика недоступна (miss/conflict/error) →
21
+ * инъекция встроенного универсального FALLBACK_PLAYBOOK для любого agent-id.
22
+ * В router.log (2.18.5, AC3): source=face|legacy|default,
23
+ * resolution=unique|none|conflict|error, face_id/version/hash, session.
24
+ * Реестр доставок = сами playbook-объекты через owner_skill, отдельного
25
+ * конфига нет.
24
26
  *
25
27
  * Ноль зависимостей (Node stdlib; ai-sdk НЕ используется). Fail-safe: всё в
26
28
  * try/catch — плагин не имеет права уронить сессию (лог: .wolf/router.log —
@@ -32,7 +34,6 @@ import path from 'path';
32
34
  import fs from 'fs';
33
35
  import { execFile } from 'child_process';
34
36
  import { randomUUID } from 'crypto';
35
- import { promisify } from 'util';
36
37
  import { fileURLToPath } from 'url';
37
38
 
38
39
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -71,17 +72,29 @@ const CACHE_TTL_MS = 300_000;
71
72
  // agent-id последней рамки, пропущенной через system.transform (для skill-метрики B3)
72
73
  let lastAgentId: string | null = null;
73
74
 
74
- const run = promisify(execFile);
75
75
  // execFile: args array, no shell. Целевой проект — глобальный `wolf`;
76
76
  // догфуд в репо Wolf — локальный dist. Ошибка обоих → исключение наверх (fail-safe).
77
77
  // WOLF_SESSION (4.C): ключ ставится фабрикой ОДИН раз на процесс opencode —
78
78
  // спавны и bash-вызовы волка наследуют его из process.env (execFile без
79
79
  // env-поля наследует сам). Известный потолок: несколько чатов одного процесса
80
80
  // opencode шарят ключ — консервативная недо-дедупликация, не ошибка.
81
- const runWolf = (args: string[]): Promise<{ stdout: string }> => {
82
- const opts = { cwd: PROJECT_ROOT, timeout: 5000 };
83
- return run('wolf', args, opts).catch(() => run('node', [LOCAL_CLI, ...args], opts));
84
- };
81
+ // 2.18.5: runWolf резолвит stdout и при ненулевом exit — контракт CLI печатает
82
+ // разборчивый JSON ({ok:false,error:{kind}}) на stdout с exit 1; promisify
83
+ // терял его (reject без stdout), превращая реальный miss в необъяснимый error.
84
+ const runWolf = (args: string[]): Promise<{ stdout: string }> =>
85
+ new Promise((resolve, reject) => {
86
+ const attempt = (file: string, a: string[]): void => {
87
+ execFile(file, a, { cwd: PROJECT_ROOT, timeout: 5000 }, (err, stdout) => {
88
+ const out = typeof stdout === 'string' ? stdout : '';
89
+ // resolve при exit 0 (пустой stdout — валидный miss search) и при
90
+ // exit≠0 с разборчивым выводом (контракт CLI miss/conflict с exit 1)
91
+ if (!err || out) return resolve({ stdout: out });
92
+ if (file === 'wolf') return attempt('node', [LOCAL_CLI, ...args]);
93
+ reject(err ?? new Error('wolf CLI produced no output'));
94
+ });
95
+ };
96
+ attempt('wolf', args);
97
+ });
85
98
 
86
99
  function logRoute(line: string): void {
87
100
  try {
@@ -97,44 +110,96 @@ function logRoute(line: string): void {
97
110
  // версионируется ключом head — кэш экономит только get-спавн и отдаётся лишь
98
111
  // при совпадении id с актуальной записью живого search; устаревшая версия
99
112
  // лица не отдаётся никогда (новая сессия в пределах TTL получает head).
100
- const cache = new Map<string, { value: { id: string; body: string } | null; at: number }>();
113
+ const cache = new Map<string, { value: RouteResult; at: number }>();
101
114
 
102
115
  // Вариант Б (а): мемоизация инъекции по opencode-сессии. Ключ сессии —
103
116
  // WOLF_SESSION (ставится на процесс opencode; несколько чатов одного
104
117
  // процесса — задокументированный потолок, см. ensureSession). Сессия
105
118
  // пригвождена к версии первого задания: пересборка system не запускает
106
119
  // ре-резолв — переключение версии посередине задания исключено (REQ-05).
107
- const sessionInject = new Map<string, { text: string; name: string; variant: string; bytes: number }>();
120
+ interface InjectedPlaybook {
121
+ text: string;
122
+ name: string;
123
+ variant: string;
124
+ bytes: number;
125
+ source: string;
126
+ resolution: string;
127
+ face_id: string;
128
+ version: string;
129
+ hash: string;
130
+ }
131
+ const sessionInject = new Map<string, InjectedPlaybook>();
132
+
133
+ /** Исход доставки (2.18.5, AC1/AC3): unique — каноническое лицо; none — лица
134
+ * нет, legacy-поиск разрешён; conflict/error — методика НЕ выбирается и НЕ
135
+ * внедряется (только универсальный fallback + диагностика в router.log). */
136
+ type RouteResolution = 'unique' | 'none' | 'conflict' | 'error';
137
+
138
+ interface RouteResult {
139
+ id: string | null;
140
+ body: string | null;
141
+ source: 'face' | 'legacy' | 'default';
142
+ resolution: RouteResolution;
143
+ version: string | null;
144
+ hash: string | null;
145
+ }
146
+
147
+ const routeFailure = (resolution: 'conflict' | 'error'): RouteResult => ({
148
+ id: null,
149
+ body: null,
150
+ source: 'default',
151
+ resolution,
152
+ version: null,
153
+ hash: null,
154
+ });
108
155
 
109
156
  // 2.18.4: канонический резолвер лица — строгий запрос type=face/status=active/
110
157
  // owner_skill (`wolf face resolve`) вместо вероятностного текстового поиска.
111
- // miss/conflict/сбой CLI → null → legacy-путь (note+howto + гвард) ниже:
112
- // поведение 2.13 для howto-плейбуков сохранено, конфликт лиц ловит doctor.
113
- async function resolveFaceCanonical(agentId: string): Promise<{ id: string; body: string } | null> {
158
+ // 2.18.5 (AC1): результат различается — unique доставляется; none → legacy-путь
159
+ // ниже; conflict/error → НЕ fallback к текстовому поиску (ревью 2.18.4:
160
+ // запасной поиск обходил конфликт, выбирая proposed-лицо по owner_skill).
161
+ async function resolveFaceCanonical(agentId: string): Promise<RouteResult | null> {
162
+ let stdout: string;
163
+ try {
164
+ ({ stdout } = await runWolf(['face', 'resolve', agentId]));
165
+ } catch {
166
+ return routeFailure('error'); // обе формы CLI упали без вывода — диагностируемый сбой
167
+ }
114
168
  try {
115
- const { stdout } = await runWolf(['face', 'resolve', agentId]);
116
- const parsed = JSON.parse(stdout) as { ok?: boolean; face?: { id?: unknown; body?: unknown } };
169
+ const parsed = JSON.parse(stdout) as {
170
+ ok?: boolean;
171
+ face?: { id?: unknown; body?: unknown; version?: unknown; hash?: unknown };
172
+ error?: { kind?: unknown };
173
+ };
117
174
  if (parsed?.ok && typeof parsed.face?.body === 'string' && parsed.face.body.length > 0) {
118
- return { id: String(parsed.face.id ?? ''), body: parsed.face.body };
175
+ return {
176
+ id: String(parsed.face.id ?? ''),
177
+ body: parsed.face.body,
178
+ source: 'face',
179
+ resolution: 'unique',
180
+ version: typeof parsed.face.version === 'string' ? parsed.face.version : null,
181
+ hash: typeof parsed.face.hash === 'string' ? parsed.face.hash : null,
182
+ };
119
183
  }
184
+ if (parsed?.error?.kind === 'multiple') return routeFailure('conflict');
185
+ if (parsed?.error?.kind === 'none') return null; // miss → legacy-путь
186
+ return routeFailure('error'); // ok:false без kind / неразборчивый ответ
120
187
  } catch {
121
- /* fail-safe: miss/conflict — legacy-путь */
188
+ return routeFailure('error');
122
189
  }
123
- return null;
124
190
  }
125
191
 
126
- async function resolvePlaybook(agentId: string): Promise<{ id: string; body: string } | null> {
192
+ async function resolvePlaybook(agentId: string): Promise<RouteResult> {
127
193
  const face = await resolveFaceCanonical(agentId);
128
194
  if (face) {
129
- cache.set(agentId, { value: face, at: Date.now() });
130
- return face;
195
+ if (face.resolution === 'unique') cache.set(agentId, { value: face, at: Date.now() });
196
+ return face; // unique | conflict | error — стоп, запасной поиск не гасит исход
131
197
  }
132
- let found: { id: string; body: string } | null = null;
198
+ let found: RouteResult | null = null;
133
199
  try {
134
200
  // 2.13: плейбуки живут как note+facet howto (type 'playbook' — legacy),
135
201
  // поэтому без --type; слово «playbook» (title/tags канона) сужает выборку.
136
- // Формат строки search: `<id> [<type>] [<facet>] <title>`; тип в строке не
137
- // фильтруем — каноничность решает гвард owner_skill ниже.
202
+ // Формат строки search: `<id> [<type>] [<facet>] <title>`.
138
203
  const { stdout } = await runWolf(['search', agentId, 'playbook', '--hide-superseded']);
139
204
  const ids = [...stdout.matchAll(/^([\w-]+) \[\w+\]/gm)].map((m) => m[1]);
140
205
  // Вариант Б (б): search живой на каждой новой сессии (мемоизация в
@@ -147,20 +212,46 @@ async function resolvePlaybook(agentId: string): Promise<{ id: string; body: str
147
212
  }
148
213
  // Ранний стоп (спека A2-б): релевантность поиска уже отсортировала,
149
214
  // --hide-superseded отсекает старые версии — первый кандидат, прошедший
150
- // гвард владельца, и есть результат (один get при miss вместо K подряд).
215
+ // гварды, и есть результат (один get при miss вместо K подряд).
216
+ // 2.18.5 (AC1): гварды — owner_skill И тип (note|howto|playbook; лица
217
+ // только через канонический резолвер) И статус (active или не задан;
218
+ // proposed/superseded не доставляются).
151
219
  for (const id of ids) {
152
220
  const { stdout: json } = await runWolf(['get', id]);
153
- const obj = JSON.parse(json);
221
+ const obj = JSON.parse(json) as {
222
+ id?: unknown;
223
+ body?: unknown;
224
+ owner_skill?: unknown;
225
+ extra?: { owner_skill?: unknown };
226
+ type?: unknown;
227
+ status?: unknown;
228
+ version?: unknown;
229
+ hash?: unknown;
230
+ };
154
231
  const owner = obj.owner_skill ?? obj.extra?.owner_skill;
155
232
  if (owner !== agentId && owner !== `skill:${agentId}`) continue; // гвард владельца
156
- if (obj.body) found = { id: String(obj.id ?? id), body: obj.body };
233
+ if (obj.type !== 'note' && obj.type !== 'howto' && obj.type !== 'playbook') continue; // лица — только резолвер
234
+ if (obj.status !== undefined && obj.status !== 'active') continue; // гвард статуса
235
+ if (obj.body) {
236
+ found = {
237
+ id: String(obj.id ?? id),
238
+ body: obj.body,
239
+ source: 'legacy',
240
+ resolution: 'none',
241
+ version: typeof obj.version === 'string' ? obj.version : null,
242
+ hash: typeof obj.hash === 'string' ? obj.hash : null,
243
+ };
244
+ }
157
245
  break;
158
246
  }
159
247
  } catch {
160
- /* fail-safe: без playbook — рамка работает через wolf search */
248
+ // 2.18.5: сбой legacy-поиска — диагностируемый error, не молчаливый miss
249
+ cache.set(agentId, { value: routeFailure('error'), at: Date.now() });
250
+ return routeFailure('error');
161
251
  }
162
- cache.set(agentId, { value: found, at: Date.now() });
163
- return found;
252
+ const result = found ?? { ...routeFailure('none'), source: 'default' as const };
253
+ cache.set(agentId, { value: result, at: Date.now() });
254
+ return result;
164
255
  }
165
256
 
166
257
  // B3: метрика вызовов скиллов. Форма входа v1: input.tool — имя тула,
@@ -187,7 +278,9 @@ function recordSkillMetric(tool: unknown, args: unknown): void {
187
278
  // Общая доставка playbook (v1 system.transform / v2 context): joined system-текст
188
279
  // → маркер agent-id → гвард идемпотентности INJECT_HEADER → мемоизация сессии
189
280
  // (вариант Б: запечатанный текст без ре-резолва) → resolvePlaybook → push(текст).
190
- // Fail-safe: не роняем сессию ни на одном шаге.
281
+ // Fail-safe: не роняем сессию ни на одном шаге. 2.18.5 (AC3): каждая строка
282
+ // журнала несёт source/resolution/face_id/version/hash/session — видно, как
283
+ // именно доставлена методика (canonical-метка legacy-результатом не ставится).
191
284
  async function deliverPlaybook(joined: string, push: (text: string) => void): Promise<void> {
192
285
  try {
193
286
  const m = joined.match(AGENT_ID_RE);
@@ -195,6 +288,7 @@ async function deliverPlaybook(joined: string, push: (text: string) => void): Pr
195
288
  if (joined.includes(INJECT_HEADER)) return; // идемпотентность: не вставляем дважды
196
289
  const agentId = m[1];
197
290
  lastAgentId = agentId; // агент известен хуку skill-метрики (B3)
291
+ const sessionId = process.env.WOLF_SESSION ?? '-';
198
292
  // Вариант Б (а): сессия уже получила инъекцию → отдаём запечатанный текст
199
293
  // (то же содержание, тот же вариант), без CLI-спавнов и ре-резолва —
200
294
  // независимо от того, персистит ли рантайм system-текст между сообщениями.
@@ -203,7 +297,9 @@ async function deliverPlaybook(joined: string, push: (text: string) => void): Pr
203
297
  if (memoized) {
204
298
  push(memoized.text);
205
299
  logRoute(
206
- `agent-id=${agentId} playbook=hit name=${memoized.name} variant=${memoized.variant} injected=yes` +
300
+ `agent-id=${agentId} playbook=hit name=${memoized.name} variant=${memoized.variant}` +
301
+ ` source=${memoized.source} resolution=${memoized.resolution} face_id=${memoized.face_id}` +
302
+ ` version=${memoized.version} hash=${memoized.hash} session=${sessionId} injected=yes` +
207
303
  ` ms=0 bytes=${memoized.bytes}`
208
304
  );
209
305
  return;
@@ -211,32 +307,46 @@ async function deliverPlaybook(joined: string, push: (text: string) => void): Pr
211
307
  const startedAt = Date.now();
212
308
  const resolved = await resolvePlaybook(agentId);
213
309
  const ms = Date.now() - startedAt;
214
- if (resolved) {
310
+ const fields =
311
+ ` source=${resolved.source} resolution=${resolved.resolution} face_id=${resolved.source === 'face' ? (resolved.id ?? '-') : '-'}` +
312
+ ` version=${resolved.version ?? '-'} hash=${resolved.hash ?? '-'}`;
313
+ if (resolved.body) {
215
314
  const text = `\n\n${INJECT_HEADER}\n\n${resolved.body}`;
216
315
  sessionInject.set(memoKey, {
217
316
  text,
218
- name: resolved.id,
219
- variant: 'canonical',
317
+ name: resolved.id ?? '',
318
+ variant: resolved.source === 'face' ? 'canonical' : 'legacy',
220
319
  bytes: Buffer.byteLength(resolved.body),
320
+ source: resolved.source,
321
+ resolution: resolved.resolution,
322
+ face_id: resolved.source === 'face' ? (resolved.id ?? '-') : '-',
323
+ version: resolved.version ?? '-',
324
+ hash: resolved.hash ?? '-',
221
325
  });
222
326
  push(text);
223
327
  logRoute(
224
- `agent-id=${agentId} playbook=hit name=${resolved.id} variant=canonical injected=yes` +
225
- ` ms=${ms} bytes=${Buffer.byteLength(resolved.body)}`
328
+ `agent-id=${agentId} playbook=hit name=${resolved.id} variant=${resolved.source === 'face' ? 'canonical' : 'legacy'}` +
329
+ `${fields} session=${sessionId} injected=yes ms=${ms} bytes=${Buffer.byteLength(resolved.body)}`
226
330
  );
227
331
  } else {
228
- // T012: miss канона → универсальный fallback; инъекция для любого agent-id
332
+ // T012: методика недоступна (miss/conflict/error) → универсальный fallback;
333
+ // инъекция для любого agent-id; конфликт/сбой зафиксированы в resolution.
229
334
  const text = `\n\n${INJECT_HEADER}\n\n${FALLBACK_PLAYBOOK}`;
230
335
  sessionInject.set(memoKey, {
231
336
  text,
232
337
  name: 'fallback',
233
338
  variant: 'fallback',
234
339
  bytes: Buffer.byteLength(FALLBACK_PLAYBOOK),
340
+ source: 'default',
341
+ resolution: resolved.resolution,
342
+ face_id: '-',
343
+ version: '-',
344
+ hash: '-',
235
345
  });
236
346
  push(text);
237
347
  logRoute(
238
- `agent-id=${agentId} playbook=hit name=fallback variant=fallback injected=yes` +
239
- ` ms=${ms} bytes=${Buffer.byteLength(FALLBACK_PLAYBOOK)}`
348
+ `agent-id=${agentId} playbook=hit name=fallback variant=fallback` +
349
+ `${fields} session=${sessionId} injected=yes ms=${ms} bytes=${Buffer.byteLength(FALLBACK_PLAYBOOK)}`
240
350
  );
241
351
  }
242
352
  } catch {
@@ -0,0 +1,204 @@
1
+ {
2
+ "version": "2.19.0",
3
+ "prev": "2.18.5",
4
+ "files": [
5
+ {
6
+ "path": "base/agents/executor-lead.md",
7
+ "kind": "agent",
8
+ "category": "unchanged"
9
+ },
10
+ {
11
+ "path": "base/agents/mr-wolf.md",
12
+ "kind": "agent",
13
+ "category": "unchanged"
14
+ },
15
+ {
16
+ "path": "base/agents/steward.md",
17
+ "kind": "agent",
18
+ "category": "unchanged"
19
+ },
20
+ {
21
+ "path": "base/agents/worker-implementer.md",
22
+ "kind": "agent",
23
+ "category": "unchanged"
24
+ },
25
+ {
26
+ "path": "base/agents/worker-researcher.md",
27
+ "kind": "agent",
28
+ "category": "unchanged"
29
+ },
30
+ {
31
+ "path": "base/agents/worker-reviewer.md",
32
+ "kind": "agent",
33
+ "category": "unchanged"
34
+ },
35
+ {
36
+ "path": "base/commands/analyze-doc.md",
37
+ "kind": "command",
38
+ "category": "unchanged"
39
+ },
40
+ {
41
+ "path": "base/commands/complain.md",
42
+ "kind": "command",
43
+ "category": "unchanged"
44
+ },
45
+ {
46
+ "path": "base/commands/doc-review.md",
47
+ "kind": "command",
48
+ "category": "unchanged"
49
+ },
50
+ {
51
+ "path": "base/skills/finishing-a-development-branch/SKILL.md",
52
+ "kind": "skill",
53
+ "category": "unchanged"
54
+ },
55
+ {
56
+ "path": "base/skills/receiving-code-review/SKILL.md",
57
+ "kind": "skill",
58
+ "category": "unchanged"
59
+ },
60
+ {
61
+ "path": "base/skills/requesting-code-review/SKILL.md",
62
+ "kind": "skill",
63
+ "category": "unchanged"
64
+ },
65
+ {
66
+ "path": "base/skills/test-driven-development/SKILL.md",
67
+ "kind": "skill",
68
+ "category": "unchanged"
69
+ },
70
+ {
71
+ "path": "base/skills/using-git-worktrees/SKILL.md",
72
+ "kind": "skill",
73
+ "category": "unchanged"
74
+ },
75
+ {
76
+ "path": "base/skills/using-skills/SKILL.md",
77
+ "kind": "skill",
78
+ "category": "unchanged"
79
+ },
80
+ {
81
+ "path": "base/skills/verification-before-completion/SKILL.md",
82
+ "kind": "skill",
83
+ "category": "unchanged"
84
+ },
85
+ {
86
+ "path": "base/skills/wolf-brainstorm/SKILL.md",
87
+ "kind": "skill",
88
+ "category": "unchanged"
89
+ },
90
+ {
91
+ "path": "base/skills/wolf-debug/SKILL.md",
92
+ "kind": "skill",
93
+ "category": "unchanged"
94
+ },
95
+ {
96
+ "path": "base/skills/wolf-design/SKILL.md",
97
+ "kind": "skill",
98
+ "category": "unchanged"
99
+ },
100
+ {
101
+ "path": "base/skills/wolf-execute/SKILL.md",
102
+ "kind": "skill",
103
+ "category": "unchanged"
104
+ },
105
+ {
106
+ "path": "base/skills/wolf-explain/SKILL.md",
107
+ "kind": "skill",
108
+ "category": "unchanged"
109
+ },
110
+ {
111
+ "path": "base/skills/wolf-handoff/SKILL.md",
112
+ "kind": "skill",
113
+ "category": "unchanged"
114
+ },
115
+ {
116
+ "path": "base/skills/wolf-plan/SKILL.md",
117
+ "kind": "skill",
118
+ "category": "unchanged"
119
+ },
120
+ {
121
+ "path": "base/skills/wolf-review/SKILL.md",
122
+ "kind": "skill",
123
+ "category": "unchanged"
124
+ },
125
+ {
126
+ "path": "base/skills/wolf-sdd/SKILL.md",
127
+ "kind": "skill",
128
+ "category": "unchanged"
129
+ },
130
+ {
131
+ "path": "base/skills/wolf-skill-intake/SKILL.md",
132
+ "kind": "skill",
133
+ "category": "unchanged"
134
+ },
135
+ {
136
+ "path": "base/skills/wolf-testplan/SKILL.md",
137
+ "kind": "skill",
138
+ "category": "unchanged"
139
+ },
140
+ {
141
+ "path": "base/skills/writing-skills/SKILL.md",
142
+ "kind": "skill",
143
+ "category": "unchanged"
144
+ },
145
+ {
146
+ "path": "opencode/plugins/wolf-router.ts",
147
+ "kind": "plugin",
148
+ "category": "unchanged"
149
+ },
150
+ {
151
+ "path": "opencode/plugins/wolf-session-start.js",
152
+ "kind": "plugin",
153
+ "category": "unchanged"
154
+ }
155
+ ],
156
+ "faces": [
157
+ {
158
+ "template": "base/playbooks/complaint-protocol.md",
159
+ "owner_skill": "mr-wolf",
160
+ "category": "unchanged",
161
+ "seed": true
162
+ },
163
+ {
164
+ "template": "base/playbooks/executor-lead-playbook.md",
165
+ "owner_skill": "executor-lead",
166
+ "category": "unchanged",
167
+ "seed": true
168
+ },
169
+ {
170
+ "template": "base/playbooks/steward-nastavnik.md",
171
+ "owner_skill": "steward",
172
+ "category": "unchanged",
173
+ "seed": true
174
+ },
175
+ {
176
+ "template": "base/playbooks/worker-implementer-playbook.md",
177
+ "owner_skill": "worker-implementer",
178
+ "category": "unchanged",
179
+ "seed": true
180
+ },
181
+ {
182
+ "template": "base/playbooks/worker-researcher-playbook.md",
183
+ "owner_skill": "worker-researcher",
184
+ "category": "unchanged",
185
+ "seed": true
186
+ },
187
+ {
188
+ "template": "base/playbooks/worker-reviewer-playbook.md",
189
+ "owner_skill": "worker-reviewer",
190
+ "category": "unchanged",
191
+ "seed": true
192
+ }
193
+ ],
194
+ "agents_md": {
195
+ "category": "unchanged",
196
+ "mode": "append-block"
197
+ },
198
+ "schema_migrations": [],
199
+ "outside_v1": [
200
+ "taxonomy (config.yaml memory_types — manual wolf taxonomy)",
201
+ "tool-skills (generated by wolf tool — not a base-set path)",
202
+ "local/foreign artifacts (untracked .opencode/* and .wolf/* files)"
203
+ ]
204
+ }