mister-wolf 2.17.0 → 2.18.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.
- package/README.md +31 -18
- package/README.ru.md +25 -14
- package/dist/adapters/cli/cli-entry.d.ts.map +1 -1
- package/dist/adapters/cli/cli-entry.js +8 -0
- package/dist/adapters/cli/cli-entry.js.map +1 -1
- package/dist/adapters/cli/commands/claim.d.ts +6 -0
- package/dist/adapters/cli/commands/claim.d.ts.map +1 -0
- package/dist/adapters/cli/commands/claim.js +74 -0
- package/dist/adapters/cli/commands/claim.js.map +1 -0
- package/dist/adapters/cli/commands/face.d.ts.map +1 -1
- package/dist/adapters/cli/commands/face.js +36 -3
- package/dist/adapters/cli/commands/face.js.map +1 -1
- package/dist/adapters/cli/commands/memory-doctor.d.ts.map +1 -1
- package/dist/adapters/cli/commands/memory-doctor.js +37 -5
- package/dist/adapters/cli/commands/memory-doctor.js.map +1 -1
- package/dist/adapters/cli/commands/memory-edit.d.ts.map +1 -1
- package/dist/adapters/cli/commands/memory-edit.js +6 -52
- package/dist/adapters/cli/commands/memory-edit.js.map +1 -1
- package/dist/adapters/cli/commands/memory-init.d.ts.map +1 -1
- package/dist/adapters/cli/commands/memory-init.js +26 -1
- package/dist/adapters/cli/commands/memory-init.js.map +1 -1
- package/dist/adapters/cli/commands/memory-scaffold.d.ts.map +1 -1
- package/dist/adapters/cli/commands/memory-scaffold.js +2 -1
- package/dist/adapters/cli/commands/memory-scaffold.js.map +1 -1
- package/dist/adapters/fs/claim-registry.d.ts +114 -0
- package/dist/adapters/fs/claim-registry.d.ts.map +1 -0
- package/dist/adapters/fs/claim-registry.js +343 -0
- package/dist/adapters/fs/claim-registry.js.map +1 -0
- package/dist/adapters/fs/memory-root.d.ts +26 -1
- package/dist/adapters/fs/memory-root.d.ts.map +1 -1
- package/dist/adapters/fs/memory-root.js +88 -2
- package/dist/adapters/fs/memory-root.js.map +1 -1
- package/dist/app/use-cases/build-analytics.d.ts +22 -0
- package/dist/app/use-cases/build-analytics.d.ts.map +1 -1
- package/dist/app/use-cases/build-analytics.js +34 -0
- package/dist/app/use-cases/build-analytics.js.map +1 -1
- package/dist/app/use-cases/claim-artifact.d.ts +34 -0
- package/dist/app/use-cases/claim-artifact.d.ts.map +1 -0
- package/dist/app/use-cases/claim-artifact.js +43 -0
- package/dist/app/use-cases/claim-artifact.js.map +1 -0
- package/dist/app/use-cases/create-face-version.d.ts +26 -4
- package/dist/app/use-cases/create-face-version.d.ts.map +1 -1
- package/dist/app/use-cases/create-face-version.js +9 -133
- package/dist/app/use-cases/create-face-version.js.map +1 -1
- package/dist/app/use-cases/edit-memory-object.d.ts +26 -0
- package/dist/app/use-cases/edit-memory-object.d.ts.map +1 -0
- package/dist/app/use-cases/edit-memory-object.js +57 -0
- package/dist/app/use-cases/edit-memory-object.js.map +1 -0
- package/dist/app/use-cases/face-operation.d.ts +81 -0
- package/dist/app/use-cases/face-operation.d.ts.map +1 -0
- package/dist/app/use-cases/face-operation.js +503 -0
- package/dist/app/use-cases/face-operation.js.map +1 -0
- package/dist/app/use-cases/get-call-injections.d.ts +12 -1
- package/dist/app/use-cases/get-call-injections.d.ts.map +1 -1
- package/dist/app/use-cases/get-call-injections.js +73 -12
- package/dist/app/use-cases/get-call-injections.js.map +1 -1
- package/dist/app/use-cases/init-project.d.ts +6 -0
- package/dist/app/use-cases/init-project.d.ts.map +1 -1
- package/dist/app/use-cases/init-project.js +34 -1
- package/dist/app/use-cases/init-project.js.map +1 -1
- package/dist/app/use-cases/migrate-faces.d.ts +45 -11
- package/dist/app/use-cases/migrate-faces.d.ts.map +1 -1
- package/dist/app/use-cases/migrate-faces.js +234 -32
- package/dist/app/use-cases/migrate-faces.js.map +1 -1
- package/dist/app/use-cases/promote-memory.d.ts +14 -1
- package/dist/app/use-cases/promote-memory.d.ts.map +1 -1
- package/dist/app/use-cases/promote-memory.js +36 -5
- package/dist/app/use-cases/promote-memory.js.map +1 -1
- package/dist/app/use-cases/scaffold-artifact.d.ts +5 -0
- package/dist/app/use-cases/scaffold-artifact.d.ts.map +1 -1
- package/dist/app/use-cases/scaffold-artifact.js +2 -0
- package/dist/app/use-cases/scaffold-artifact.js.map +1 -1
- package/dist/app/use-cases/seed-base-playbooks.d.ts +3 -3
- package/dist/app/use-cases/seed-base-playbooks.d.ts.map +1 -1
- package/dist/app/use-cases/seed-base-playbooks.js +75 -29
- package/dist/app/use-cases/seed-base-playbooks.js.map +1 -1
- package/dist/app/use-cases/supersede-memory-object.d.ts +3 -1
- package/dist/app/use-cases/supersede-memory-object.d.ts.map +1 -1
- package/dist/app/use-cases/supersede-memory-object.js +8 -1
- package/dist/app/use-cases/supersede-memory-object.js.map +1 -1
- package/dist/app/use-cases/transition-memory-object.d.ts.map +1 -1
- package/dist/app/use-cases/transition-memory-object.js +6 -0
- package/dist/app/use-cases/transition-memory-object.js.map +1 -1
- package/dist/domain/memory-types.d.ts +28 -2
- package/dist/domain/memory-types.d.ts.map +1 -1
- package/dist/domain/memory-types.js +50 -2
- package/dist/domain/memory-types.js.map +1 -1
- package/dist/domain/schemas/memory-event-schema.d.ts +4 -0
- package/dist/domain/schemas/memory-event-schema.d.ts.map +1 -1
- package/dist/domain/schemas/memory-event-schema.js +12 -0
- package/dist/domain/schemas/memory-event-schema.js.map +1 -1
- package/dist/domain/type-schema-builder.d.ts +1 -1
- package/dist/domain/type-schema-builder.d.ts.map +1 -1
- package/dist/domain/type-schema-builder.js +20 -0
- package/dist/domain/type-schema-builder.js.map +1 -1
- package/package.json +1 -1
- package/templates/.wolf/router.log +1 -1
- package/templates/base/agents/steward.md +9 -6
- package/templates/base/playbooks/complaint-protocol.md +1 -1
- package/templates/base/playbooks/steward-nastavnik.md +36 -17
- package/templates/base/skills/wolf-brainstorm/SKILL.md +25 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { closeSync, existsSync, linkSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync, } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { UserFacingError } from '../../domain/errors.js';
|
|
4
|
+
/**
|
|
5
|
+
* 2.17.1 REQ-05 (R06), design §3.5 / ADR-5: файловый claim-реестр владения
|
|
6
|
+
* общими артефактами (TASK-бриф, план, отчёт, worktree). JSON-файлы в общем
|
|
7
|
+
* `.wolf/shared/claims/` (cross-copy видимость — root из resolveMemoryRoot);
|
|
8
|
+
* атомарный захват — `openSync(…, 'wx')` (механика FsMemoryLock tryAcquire);
|
|
9
|
+
* stale: PID мёртв (ESRCH) ИЛИ возраст > CLAIM_TTL ИЛИ heartbeat старше окна
|
|
10
|
+
* (2.18.0 REQ-03); перехват stale сохраняет
|
|
11
|
+
* прежний файл жёсткой ссылкой в след `<slug>.<epochMs>.json` (excl, NFR-02).
|
|
12
|
+
*/
|
|
13
|
+
/** TTL claim'а: локальный-first (heartbeat — opt-in продление, REQ-03). */
|
|
14
|
+
export const CLAIM_TTL_MS = 24 * 60 * 60 * 1000;
|
|
15
|
+
/** Окно свежести heartbeat (2.18.0 REQ-03, design §3.3 / ADR-7): heartbeat
|
|
16
|
+
* старше окна — claim stale; независимая величина от CLAIM_TTL_MS (DAP-2). */
|
|
17
|
+
export const CLAIM_HEARTBEAT_WINDOW_MS = 10 * 60 * 1000;
|
|
18
|
+
export class ClaimHeldError extends Error {
|
|
19
|
+
claimPath;
|
|
20
|
+
owner;
|
|
21
|
+
reason;
|
|
22
|
+
constructor(claimPath, owner, reason) {
|
|
23
|
+
super(`Claim held by ${owner}: ${claimPath} (${reason})`);
|
|
24
|
+
this.claimPath = claimPath;
|
|
25
|
+
this.owner = owner;
|
|
26
|
+
this.reason = reason;
|
|
27
|
+
this.name = 'ClaimHeldError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function claimsDir(root) {
|
|
31
|
+
return join(root, '.wolf', 'shared', 'claims');
|
|
32
|
+
}
|
|
33
|
+
/** Имя файла claim'а: слэг артефакта, безопасный для ФС.
|
|
34
|
+
* shortcut: различные артефакты могут слэгнуться одинаково (отказ fail-closed, не порча). */
|
|
35
|
+
export function claimSlug(artifact) {
|
|
36
|
+
return artifact.replace(/[^A-Za-z0-9._-]+/g, '_');
|
|
37
|
+
}
|
|
38
|
+
export function claimFilePath(root, artifact) {
|
|
39
|
+
return join(claimsDir(root), `${claimSlug(artifact)}.json`);
|
|
40
|
+
}
|
|
41
|
+
/** След перехвата: `<slug>.<epochMs>.json` (исходные байты сохраняются, NFR-02). */
|
|
42
|
+
export function claimTrailPath(root, artifact, epochMs) {
|
|
43
|
+
return join(claimsDir(root), `${claimSlug(artifact)}.${epochMs}.json`);
|
|
44
|
+
}
|
|
45
|
+
/** Путь heartbeat-файла артефакта (2.18.0 REQ-03): `<slug>.heartbeat` рядом с claim-файлом. */
|
|
46
|
+
export function heartbeatFilePath(root, artifact) {
|
|
47
|
+
return join(claimsDir(root), `${claimSlug(artifact)}.heartbeat`);
|
|
48
|
+
}
|
|
49
|
+
/** Чтение heartbeat: null при отсутствии/повреждении — нет heartbeat = правила
|
|
50
|
+
* 2.17.1 (NFR-03), повреждённый файл = stale (как у claim). */
|
|
51
|
+
export function readHeartbeat(path) {
|
|
52
|
+
let raw;
|
|
53
|
+
try {
|
|
54
|
+
raw = readFileSync(path, 'utf-8');
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const d = JSON.parse(raw);
|
|
61
|
+
if (typeof d.written_at !== 'string' || typeof d.writer !== 'string')
|
|
62
|
+
return null;
|
|
63
|
+
return { written_at: d.written_at, writer: d.writer };
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** Запись heartbeat (REQ-03, ADR-7): атомарная замена — уникальный tmp (wx) +
|
|
70
|
+
* rename поверх, поэтому параллельные писатели не ломают JSON, последний
|
|
71
|
+
* выигрывает, следа (в отличие от claim-перехвата) не остаётся.
|
|
72
|
+
* shortcut: уникальность tmp через hrtime+Math.random — pid делят воркеры
|
|
73
|
+
* на macOS, threadId есть только в worker, атомарность через wx+rename. */
|
|
74
|
+
export function writeHeartbeat(deps, input) {
|
|
75
|
+
const dir = claimsDir(deps.root);
|
|
76
|
+
mkdirSync(dir, { recursive: true });
|
|
77
|
+
const now = (deps.now ?? (() => new Date()))();
|
|
78
|
+
const record = { written_at: now.toISOString(), writer: input.writer };
|
|
79
|
+
const tag = `${process.hrtime.bigint().toString(36)}.${Math.random().toString(36).slice(2, 10)}`;
|
|
80
|
+
const tmp = join(dir, `${claimSlug(input.artifact)}.heartbeat.${tag}.tmp`);
|
|
81
|
+
const fd = openSync(tmp, 'wx');
|
|
82
|
+
writeFileSync(fd, JSON.stringify(record), 'utf-8');
|
|
83
|
+
closeSync(fd);
|
|
84
|
+
try {
|
|
85
|
+
renameSync(tmp, heartbeatFilePath(deps.root, input.artifact));
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
try {
|
|
89
|
+
unlinkSync(tmp);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
/* конкурент уже убрал — не маскируем исходную ошибку */
|
|
93
|
+
}
|
|
94
|
+
throw e;
|
|
95
|
+
}
|
|
96
|
+
return record;
|
|
97
|
+
}
|
|
98
|
+
/** Прод-liveness: signal 0; ESRCH — мёртв, EPERM — жив (чужой user). pid<=0 —
|
|
99
|
+
* неизвестен (переданный claim) — считаем живым, пока не истечёт TTL. */
|
|
100
|
+
export function pidAlive(pid) {
|
|
101
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
102
|
+
return true;
|
|
103
|
+
try {
|
|
104
|
+
process.kill(pid, 0);
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
return e.code === 'EPERM';
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/** Синхронное ожидание in-flight окна (MINOR-2): Atomics.wait — без busy-loop. */
|
|
112
|
+
function sleepMs(ms) {
|
|
113
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
114
|
+
}
|
|
115
|
+
function parseClaim(raw) {
|
|
116
|
+
try {
|
|
117
|
+
const d = JSON.parse(raw);
|
|
118
|
+
if (typeof d.artifact !== 'string' || typeof d.owner !== 'string')
|
|
119
|
+
return null;
|
|
120
|
+
return {
|
|
121
|
+
artifact: d.artifact,
|
|
122
|
+
owner: d.owner,
|
|
123
|
+
session_id: typeof d.session_id === 'string' ? d.session_id : null,
|
|
124
|
+
pid: typeof d.pid === 'number' ? d.pid : 0,
|
|
125
|
+
cwd: typeof d.cwd === 'string' ? d.cwd : '',
|
|
126
|
+
acquired_at: typeof d.acquired_at === 'string' ? d.acquired_at : '',
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/** Stale (design §3.3): мёртвый PID ИЛИ возраст > TTL ИЛИ (heartbeat есть и
|
|
134
|
+
* старше окна/повреждён); нет heartbeat → правила 2.17.1 (NFR-03). */
|
|
135
|
+
export function isStale(rec, hb, deps) {
|
|
136
|
+
if (rec === null)
|
|
137
|
+
return true;
|
|
138
|
+
const alive = deps.isAlive ?? pidAlive;
|
|
139
|
+
if (!alive(rec.pid))
|
|
140
|
+
return true;
|
|
141
|
+
const acquired = Date.parse(rec.acquired_at);
|
|
142
|
+
if (Number.isNaN(acquired))
|
|
143
|
+
return true;
|
|
144
|
+
const now = (deps.now ?? (() => new Date()))().getTime();
|
|
145
|
+
if (now - acquired > CLAIM_TTL_MS)
|
|
146
|
+
return true;
|
|
147
|
+
if (hb !== null) {
|
|
148
|
+
const written = Date.parse(hb.written_at);
|
|
149
|
+
if (Number.isNaN(written))
|
|
150
|
+
return true; // повреждён = stale
|
|
151
|
+
if (now - written > CLAIM_HEARTBEAT_WINDOW_MS)
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
function readClaimFile(path) {
|
|
157
|
+
try {
|
|
158
|
+
const raw = readFileSync(path, 'utf-8');
|
|
159
|
+
return { raw, rec: parseClaim(raw) };
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return { raw: null, rec: null };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function describeHolder(rec) {
|
|
166
|
+
return `session ${rec.session_id ?? '-'}, pid ${rec.pid}, acquired ${rec.acquired_at}`;
|
|
167
|
+
}
|
|
168
|
+
/** Атомарный захват (REQ-05 AC-1): wx-create; занято и fresh — ClaimHeldError
|
|
169
|
+
* с идентификатором владельца; stale/порченый — перехват со следом. */
|
|
170
|
+
export function acquireClaim(deps, input) {
|
|
171
|
+
const dir = claimsDir(deps.root);
|
|
172
|
+
mkdirSync(dir, { recursive: true });
|
|
173
|
+
const path = claimFilePath(deps.root, input.artifact);
|
|
174
|
+
const now = deps.now ?? (() => new Date());
|
|
175
|
+
const record = {
|
|
176
|
+
artifact: input.artifact,
|
|
177
|
+
owner: input.identity.owner,
|
|
178
|
+
session_id: input.identity.sessionId,
|
|
179
|
+
pid: input.identity.pid,
|
|
180
|
+
cwd: input.cwd,
|
|
181
|
+
acquired_at: now().toISOString(),
|
|
182
|
+
};
|
|
183
|
+
for (let attempt = 0;; attempt++) {
|
|
184
|
+
try {
|
|
185
|
+
const fd = openSync(path, 'wx');
|
|
186
|
+
writeFileSync(fd, JSON.stringify(record), 'utf-8');
|
|
187
|
+
closeSync(fd);
|
|
188
|
+
return record;
|
|
189
|
+
}
|
|
190
|
+
catch (e) {
|
|
191
|
+
const code = e.code;
|
|
192
|
+
if (code !== 'EEXIST')
|
|
193
|
+
throw e;
|
|
194
|
+
}
|
|
195
|
+
const { raw, rec } = readClaimFile(path);
|
|
196
|
+
// MINOR-2 (ревью T5): пустой файл — in-flight окно создателя (между wx и
|
|
197
|
+
// write), не порча: takeover запрещён, короткое ожидание и повтор
|
|
198
|
+
if (raw !== null && raw.trim() === '') {
|
|
199
|
+
if (attempt >= 60) {
|
|
200
|
+
throw new ClaimHeldError(path, 'unknown', 'claim file exists but stays empty (in-flight write?) — if it persists, remove it manually');
|
|
201
|
+
}
|
|
202
|
+
(deps.wait ?? sleepMs)(10);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (rec !== null && !isStale(rec, readHeartbeat(heartbeatFilePath(deps.root, input.artifact)), deps)) {
|
|
206
|
+
throw new ClaimHeldError(path, rec.owner, describeHolder(rec));
|
|
207
|
+
}
|
|
208
|
+
// stale/порченый (rec === null, сырые байты могли быть) → перехват со следом;
|
|
209
|
+
// проигранная гонка перехвата (ENOENT на link/unlink) — повтор захвата
|
|
210
|
+
try {
|
|
211
|
+
const epoch = now().getTime();
|
|
212
|
+
// FIX-4 (MINOR-4, ревью T5): след — через linkSync (excl-семантика):
|
|
213
|
+
// exists+rename TOCTOU позволял двум перехватчикам одной мс перезаписать
|
|
214
|
+
// чужой след (потеря байтов, NFR-02); при EEXIST — bump эпохи до свободного
|
|
215
|
+
let preserved = null;
|
|
216
|
+
for (let bump = 0; bump < 50 && preserved === null; bump++) {
|
|
217
|
+
const trail = claimTrailPath(deps.root, input.artifact, epoch + bump);
|
|
218
|
+
try {
|
|
219
|
+
linkSync(path, trail);
|
|
220
|
+
preserved = trail;
|
|
221
|
+
}
|
|
222
|
+
catch (e) {
|
|
223
|
+
if (e.code !== 'EEXIST')
|
|
224
|
+
throw e;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (preserved !== null) {
|
|
228
|
+
// решение принималось по raw, а слинковаться мог уже переписанный файл
|
|
229
|
+
// (чужой takeover между чтением и link): чужую свежую запись в след не
|
|
230
|
+
// берём и claim-имя под ней не освобождаем — перечитаем и откажемся
|
|
231
|
+
if (raw !== null && readFileSync(preserved, 'utf-8') !== raw) {
|
|
232
|
+
unlinkSync(preserved);
|
|
233
|
+
}
|
|
234
|
+
else if (statSync(path).ino === statSync(preserved).ino) {
|
|
235
|
+
// unlink только сохранённый inode: конкурент мог уже пересоздать claim
|
|
236
|
+
unlinkSync(path);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
/* конкурент уже сохранил/убрал файл — идём на повторный wx */
|
|
242
|
+
}
|
|
243
|
+
if (attempt > 20) {
|
|
244
|
+
throw new ClaimHeldError(path, rec?.owner ?? 'unknown', 'too many contention attempts');
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function ownerSessionMatch(rec, identity) {
|
|
249
|
+
return rec.owner === identity.owner && rec.session_id === identity.sessionId;
|
|
250
|
+
}
|
|
251
|
+
/** Освобождение (design §3.5): владелец — совпадение owner+session+pid.
|
|
252
|
+
* Освобождение чужим (иной owner/session, или pid записи жив и не совпал) —
|
|
253
|
+
* отказ с именем владельца. Мёртвый/stale держатель: тот же owner+session
|
|
254
|
+
* может убрать запись (равносильно перехвату acquire — новой власти не даёт). */
|
|
255
|
+
export function releaseClaim(deps, input) {
|
|
256
|
+
const path = claimFilePath(deps.root, input.artifact);
|
|
257
|
+
const { rec } = readClaimFile(path);
|
|
258
|
+
if (!existsSync(path)) {
|
|
259
|
+
throw new UserFacingError(`No claim held for "${input.artifact}" (claim file: ${path})`);
|
|
260
|
+
}
|
|
261
|
+
if (rec === null) {
|
|
262
|
+
// FIX-5 (MINOR-5, ревью T5): файл есть, но не читается как claim — «No claim
|
|
263
|
+
// held» лгало об отсутствии; честный отказ без расширения поведения.
|
|
264
|
+
throw new UserFacingError(`Claim file for "${input.artifact}" is corrupt — ownership cannot be verified; take over via "wolf claim acquire" (preserves bytes) or remove it manually (claim file: ${path})`);
|
|
265
|
+
}
|
|
266
|
+
const fullMatch = ownerSessionMatch(rec, input.identity) && rec.pid === input.identity.pid;
|
|
267
|
+
const pidReleasable = rec.pid <= 0 || isStale(rec, readHeartbeat(heartbeatFilePath(deps.root, input.artifact)), deps);
|
|
268
|
+
// MINOR-1b (интегральное ревью 2.17.1): зеркально requireClaim — адресат
|
|
269
|
+
// transfer (session null + owner) освобождает своей сессией, иначе передача
|
|
270
|
+
// паркует release до TTL
|
|
271
|
+
const transferredToWho = rec.session_id === null && rec.owner === input.identity.owner;
|
|
272
|
+
if (!fullMatch && !transferredToWho && !(ownerSessionMatch(rec, input.identity) && pidReleasable)) {
|
|
273
|
+
throw new UserFacingError(`Claim for "${input.artifact}" is owned by ${rec.owner} (${describeHolder(rec)}) — release is owner-only (claim file: ${path})`);
|
|
274
|
+
}
|
|
275
|
+
if (input.transferTo !== undefined) {
|
|
276
|
+
const now = (deps.now ?? (() => new Date()))();
|
|
277
|
+
const transferred = {
|
|
278
|
+
artifact: rec.artifact,
|
|
279
|
+
owner: input.transferTo,
|
|
280
|
+
// новый владелец ещё не предъявил процесс — liveness неизвестна (TTL решает)
|
|
281
|
+
session_id: null,
|
|
282
|
+
pid: 0,
|
|
283
|
+
cwd: rec.cwd,
|
|
284
|
+
acquired_at: now.toISOString(),
|
|
285
|
+
};
|
|
286
|
+
writeFileSync(path, JSON.stringify(transferred), 'utf-8');
|
|
287
|
+
return { action: 'transferred', owner: input.transferTo };
|
|
288
|
+
}
|
|
289
|
+
unlinkSync(path);
|
|
290
|
+
return { action: 'released', owner: rec.owner };
|
|
291
|
+
}
|
|
292
|
+
/** Реестр для наблюдаемости (REQ-05 AC-2): кто/когда/что из файлов; следы
|
|
293
|
+
* `<slug>.<epochMs>.json` исключаются — по содержимому: след хранит запись
|
|
294
|
+
* базового артефакта, его slug ≠ имя файла следа (MINOR-4). */
|
|
295
|
+
export function listClaims(deps) {
|
|
296
|
+
const dir = claimsDir(deps.root);
|
|
297
|
+
let names;
|
|
298
|
+
try {
|
|
299
|
+
names = readdirSync(dir);
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
return [];
|
|
303
|
+
}
|
|
304
|
+
const out = [];
|
|
305
|
+
for (const name of names) {
|
|
306
|
+
if (!name.endsWith('.json'))
|
|
307
|
+
continue;
|
|
308
|
+
const { rec } = readClaimFile(join(dir, name));
|
|
309
|
+
if (rec === null)
|
|
310
|
+
continue;
|
|
311
|
+
// MINOR-4: имя с 13 цифрами на конце может быть легитимным claim'ом —
|
|
312
|
+
// след распознаём по несоответствию имени и slug(rec.artifact), не по маске
|
|
313
|
+
if (claimFilePath(deps.root, rec.artifact) !== join(dir, name))
|
|
314
|
+
continue;
|
|
315
|
+
out.push(rec);
|
|
316
|
+
}
|
|
317
|
+
return out;
|
|
318
|
+
}
|
|
319
|
+
/** Guard записи (REQ-05 AC-3): активный claim артефакта должен существовать и
|
|
320
|
+
* принадлежать пишущему (owner+session; pid писателя ≠ pid захватчика —
|
|
321
|
+
* захват и запись обычно разные процессы). Отказ — UserFacingError с именем
|
|
322
|
+
* владельца и путём claim-файла. */
|
|
323
|
+
export function requireClaim(deps, artifact, who) {
|
|
324
|
+
const path = claimFilePath(deps.root, artifact);
|
|
325
|
+
const { rec } = readClaimFile(path);
|
|
326
|
+
if (!existsSync(path)) {
|
|
327
|
+
throw new UserFacingError(`No claim held for "${artifact}" — acquire it first: wolf claim acquire "${artifact}" (claim file: ${path})`);
|
|
328
|
+
}
|
|
329
|
+
if (rec === null) {
|
|
330
|
+
throw new UserFacingError(`Claim file for "${artifact}" is corrupt — treated as stale; re-acquire: wolf claim acquire "${artifact}" (claim file: ${path})`);
|
|
331
|
+
}
|
|
332
|
+
if (isStale(rec, readHeartbeat(heartbeatFilePath(deps.root, artifact)), deps)) {
|
|
333
|
+
throw new UserFacingError(`Claim for "${artifact}" is stale (holder ${rec.owner}) — re-acquire: wolf claim acquire "${artifact}" (claim file: ${path})`);
|
|
334
|
+
}
|
|
335
|
+
// MINOR-1 (ревью T5): переданный claim (transfer: session null, pid 0)
|
|
336
|
+
// доступен адресату с любой его сессией — иначе передача парковала бы
|
|
337
|
+
// запись до TTL. pid-проверка для pid:0 не применяется (pidAlive).
|
|
338
|
+
const transferredToWho = rec.session_id === null && rec.owner === who.owner;
|
|
339
|
+
if (!transferredToWho && (rec.owner !== who.owner || rec.session_id !== who.sessionId)) {
|
|
340
|
+
throw new UserFacingError(`Artifact "${artifact}" is claimed by ${rec.owner} (${describeHolder(rec)}) — claim file: ${path}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
//# sourceMappingURL=claim-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-registry.js","sourceRoot":"","sources":["../../../src/adapters/fs/claim-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,38 @@
|
|
|
1
1
|
export interface MemoryRootInfo {
|
|
2
2
|
/** Корень чекаута, владеющего общим .wolf/ (main-чекоут; вне worktree — сам baseDir). */
|
|
3
3
|
root: string;
|
|
4
|
-
/**
|
|
4
|
+
/** Токен WT-ID linked worktree (`w-<12 hex>`) или null (main-чекоут / вне git / аномалия).
|
|
5
|
+
* 2.17.1 REQ-02 (R02): токен вместо basename — одинаковые basename двух копий дают
|
|
6
|
+
* разные идентификаторы; consumer подставляет его в scope как `worktree:${worktree}`. */
|
|
5
7
|
worktree: string | null;
|
|
6
8
|
}
|
|
9
|
+
/** 2.17.1 REQ-02 (R02), design §3.1 (ADR-1): устойчивая идентичность linked worktree. */
|
|
10
|
+
export interface WorktreeIdentity {
|
|
11
|
+
/** Полный scope-токен копии: `worktree:w-<12 hex sha1(realpath(baseDir))>`. */
|
|
12
|
+
id: string;
|
|
13
|
+
/** basename(baseDir) — маппинг legacy-токенов `worktree:<name>` (записанных до 2.17.1). */
|
|
14
|
+
legacyName: string;
|
|
15
|
+
/** true — ровно одна живая копия с этим basename: legacy-имя однозначно маппится на неё. */
|
|
16
|
+
legacyUnique: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** Токен WT-ID без префикса scope: `w-` + первые 12 hex sha1(realpath(dir)).
|
|
19
|
+
* Идентичность по realpath: одинаковые basename двух копий дают разные токены;
|
|
20
|
+
* переименование каталога = новая копия с новым WT-ID (ограничение ADR-1). */
|
|
21
|
+
export declare function worktreeScopeToken(dir: string): string;
|
|
7
22
|
/** WP3 §6.1 (спека 2.16): linked worktree резолвит общий .wolf/ в main-чекоуте.
|
|
8
23
|
* Механика спеки — git common-dir (`git rev-parse --git-common-dir`); здесь тот же
|
|
9
24
|
* информационный fs-парс без подпроцессов (прецедент чтения .git/HEAD:
|
|
10
25
|
* heuristic-project-scanner.ts:221-239) — юнит-тестируемо без git, нет подпроцесса
|
|
11
26
|
* в hot-path. Любой сбой — безопасный fallback на локальный .wolf/ (риск Р5 спеки). */
|
|
12
27
|
export declare function resolveMemoryRoot(baseDir: string): MemoryRootInfo;
|
|
28
|
+
/** 2.17.1 REQ-02 (R02), design §3.1 (ADR-1): идентичность linked worktree.
|
|
29
|
+
* null — main-чекоут, вне git, bare-аномалия, сбой git-метаданных (fail-closed:
|
|
30
|
+
* legacy-токены не маппятся, чужой scope недоступен — §5).
|
|
31
|
+
* Вызывать лениво — по встрече legacy-токена (бюджет NFR-01: ≤1 git-подпроцесса
|
|
32
|
+
* на процесс, только linked worktree; main-чекоут git не запускает). */
|
|
33
|
+
export declare function resolveWorktreeIdentity(baseDir: string): WorktreeIdentity | null;
|
|
34
|
+
/** Каталог выглядит linked worktree (.git — файл-указатель), но метаданные не
|
|
35
|
+
* резолвятся в общий root: fail-closed предупреждение доставки (REQ-02 AC-5,
|
|
36
|
+
* design §5 — предупреждение в wolf call). null — не broken (main/outside git). */
|
|
37
|
+
export declare function brokenWorktreeHint(baseDir: string): string | null;
|
|
13
38
|
//# sourceMappingURL=memory-root.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-root.d.ts","sourceRoot":"","sources":["../../../src/adapters/fs/memory-root.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"memory-root.d.ts","sourceRoot":"","sources":["../../../src/adapters/fs/memory-root.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,cAAc;IAC7B,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;IACb;;6FAEyF;IACzF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,yFAAyF;AACzF,MAAM,WAAW,gBAAgB;IAC/B,+EAA+E;IAC/E,EAAE,EAAE,MAAM,CAAC;IACX,2FAA2F;IAC3F,UAAU,EAAE,MAAM,CAAC;IACnB,4FAA4F;IAC5F,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;8EAE8E;AAC9E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;;uFAIuF;AACvF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAmBjE;AAsCD;;;;wEAIwE;AACxE,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAWhF;AAED;;mFAEmF;AACnF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWjE"}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFileSync } from 'child_process';
|
|
2
|
+
import { createHash } from 'crypto';
|
|
3
|
+
import { existsSync, readFileSync, realpathSync, statSync } from 'fs';
|
|
2
4
|
import { basename, dirname, isAbsolute, join, resolve } from 'path';
|
|
5
|
+
/** Токен WT-ID без префикса scope: `w-` + первые 12 hex sha1(realpath(dir)).
|
|
6
|
+
* Идентичность по realpath: одинаковые basename двух копий дают разные токены;
|
|
7
|
+
* переименование каталога = новая копия с новым WT-ID (ограничение ADR-1). */
|
|
8
|
+
export function worktreeScopeToken(dir) {
|
|
9
|
+
return 'w-' + createHash('sha1').update(realpathSync(dir)).digest('hex').slice(0, 12);
|
|
10
|
+
}
|
|
3
11
|
/** WP3 §6.1 (спека 2.16): linked worktree резолвит общий .wolf/ в main-чекоуте.
|
|
4
12
|
* Механика спеки — git common-dir (`git rev-parse --git-common-dir`); здесь тот же
|
|
5
13
|
* информационный fs-парс без подпроцессов (прецедент чтения .git/HEAD:
|
|
@@ -25,10 +33,88 @@ export function resolveMemoryRoot(baseDir) {
|
|
|
25
33
|
// bare-репо + linked worktree: commondir указывает мимо чекаута — fallback локальный (Р5)
|
|
26
34
|
if (!existsSync(join(root, '.git')))
|
|
27
35
|
return { root: baseDir, worktree: null };
|
|
28
|
-
return { root, worktree:
|
|
36
|
+
return { root, worktree: worktreeScopeToken(baseDir) };
|
|
29
37
|
}
|
|
30
38
|
catch {
|
|
31
39
|
return { root: baseDir, worktree: null };
|
|
32
40
|
}
|
|
33
41
|
}
|
|
42
|
+
/** basename каждой живой записи `git worktree list --porcelain` (включая main-чекоут).
|
|
43
|
+
* Дубликаты сохраняются: однозначность legacy-имени — счёт, не множество. Кэш на
|
|
44
|
+
* процесс (NFR-01 (R02): ≤1 git-подпроцесса); null — сбой git-метаданных. */
|
|
45
|
+
const liveBasenamesCache = new Map();
|
|
46
|
+
function liveWorktreeBasenames(baseDir) {
|
|
47
|
+
let key;
|
|
48
|
+
try {
|
|
49
|
+
key = realpathSync(baseDir);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
if (liveBasenamesCache.has(key))
|
|
55
|
+
return liveBasenamesCache.get(key);
|
|
56
|
+
let result = null;
|
|
57
|
+
try {
|
|
58
|
+
const out = execFileSync('git', ['worktree', 'list', '--porcelain'], {
|
|
59
|
+
cwd: baseDir,
|
|
60
|
+
encoding: 'utf-8',
|
|
61
|
+
timeout: 10_000,
|
|
62
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
63
|
+
});
|
|
64
|
+
result = [];
|
|
65
|
+
for (const line of out.split('\n')) {
|
|
66
|
+
if (!line.startsWith('worktree '))
|
|
67
|
+
continue;
|
|
68
|
+
try {
|
|
69
|
+
result.push(basename(realpathSync(line.slice('worktree '.length).trim())));
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// каталог записи исчез — git пометит prunable; в счёт живых он не входит
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
result = null; // битые git-метаданные — fail-closed (design §5)
|
|
78
|
+
}
|
|
79
|
+
liveBasenamesCache.set(key, result);
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
/** 2.17.1 REQ-02 (R02), design §3.1 (ADR-1): идентичность linked worktree.
|
|
83
|
+
* null — main-чекоут, вне git, bare-аномалия, сбой git-метаданных (fail-closed:
|
|
84
|
+
* legacy-токены не маппятся, чужой scope недоступен — §5).
|
|
85
|
+
* Вызывать лениво — по встрече legacy-токена (бюджет NFR-01: ≤1 git-подпроцесса
|
|
86
|
+
* на процесс, только linked worktree; main-чекоут git не запускает). */
|
|
87
|
+
export function resolveWorktreeIdentity(baseDir) {
|
|
88
|
+
const info = resolveMemoryRoot(baseDir);
|
|
89
|
+
if (info.worktree === null)
|
|
90
|
+
return null;
|
|
91
|
+
const live = liveWorktreeBasenames(baseDir);
|
|
92
|
+
if (live === null)
|
|
93
|
+
return null;
|
|
94
|
+
const legacyName = basename(baseDir);
|
|
95
|
+
return {
|
|
96
|
+
id: `worktree:${info.worktree}`,
|
|
97
|
+
legacyName,
|
|
98
|
+
legacyUnique: live.filter((n) => n === legacyName).length === 1,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/** Каталог выглядит linked worktree (.git — файл-указатель), но метаданные не
|
|
102
|
+
* резолвятся в общий root: fail-closed предупреждение доставки (REQ-02 AC-5,
|
|
103
|
+
* design §5 — предупреждение в wolf call). null — не broken (main/outside git). */
|
|
104
|
+
export function brokenWorktreeHint(baseDir) {
|
|
105
|
+
try {
|
|
106
|
+
const dotGit = join(baseDir, '.git');
|
|
107
|
+
if (!existsSync(dotGit) || statSync(dotGit).isDirectory())
|
|
108
|
+
return null;
|
|
109
|
+
const raw = readFileSync(dotGit, 'utf-8');
|
|
110
|
+
if (!raw.startsWith('gitdir:'))
|
|
111
|
+
return null;
|
|
112
|
+
if (resolveMemoryRoot(baseDir).worktree !== null)
|
|
113
|
+
return null;
|
|
114
|
+
return `worktree git metadata is broken in ${baseDir} — delivery is fail-closed, worktree-scoped memory stays hidden`;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
34
120
|
//# sourceMappingURL=memory-root.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-root.js","sourceRoot":"","sources":["../../../src/adapters/fs/memory-root.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"memory-root.js","sourceRoot":"","sources":["../../../src/adapters/fs/memory-root.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,7 @@ import type { MemoryStore } from '../../ports/memory-store.port.js';
|
|
|
7
7
|
import type { EventLog } from '../../ports/event-log.port.js';
|
|
8
8
|
import type { RelationLog } from '../../ports/relation-log.port.js';
|
|
9
9
|
import type { Clock } from '../../ports/clock.port.js';
|
|
10
|
+
import type { MemoryObject } from '../../domain/schemas/memory-object-schema.js';
|
|
10
11
|
import { type SignalEvent } from '../../adapters/fs/session-metrics-log.js';
|
|
11
12
|
import { type EconomyResult } from '../../domain/tool-economy.js';
|
|
12
13
|
import type { RouterLogRow } from '../../domain/router-log.js';
|
|
@@ -395,6 +396,10 @@ export interface CouncilsView {
|
|
|
395
396
|
sharePct: number | null;
|
|
396
397
|
medianHours: number | null;
|
|
397
398
|
};
|
|
399
|
+
/** A9 (2.18.0 REQ-01): сколько мнений несут claims (аудируемые), и суммарный
|
|
400
|
+
* неразрешённый dissent по синтезам — расширение, не замена прежних полей. */
|
|
401
|
+
audited_count: number;
|
|
402
|
+
unresolved_dissent_total: number;
|
|
398
403
|
weeks: CouncilWeekActivity[];
|
|
399
404
|
openQuestions: CouncilOpenQuestion[];
|
|
400
405
|
}
|
|
@@ -467,6 +472,23 @@ export declare function parseSkillInvocations(text: string): {
|
|
|
467
472
|
rows: SkillInvocationRow[];
|
|
468
473
|
malformedLines: number;
|
|
469
474
|
};
|
|
475
|
+
/** A9 (2.18.0 REQ-01, NFR-01): claims мнения; legacy-запись без поля → [] —
|
|
476
|
+
* чтение исторических советов не падает и не даёт мусора. */
|
|
477
|
+
export declare function extractClaims(op: MemoryObject): string[];
|
|
478
|
+
/** A9: метаданные синтеза (5 полей v2-контракта, design §3.1). */
|
|
479
|
+
export interface SynthesisMeta {
|
|
480
|
+
accepted_claims: string[];
|
|
481
|
+
rejected_alternatives: {
|
|
482
|
+
claim: string;
|
|
483
|
+
reason: string;
|
|
484
|
+
}[];
|
|
485
|
+
unresolved_dissent: string[];
|
|
486
|
+
confidence: MemoryObject['confidence'];
|
|
487
|
+
required_validation: string[];
|
|
488
|
+
}
|
|
489
|
+
/** A9: v2-метаданные синтеза; legacy-синтез без новых полей → пустые списки и
|
|
490
|
+
* confidence 'medium' (ADR-9) — чтение не падает (NFR-01). */
|
|
491
|
+
export declare function extractSynthesisMeta(s: MemoryObject): SynthesisMeta;
|
|
470
492
|
/**
|
|
471
493
|
* Полный аналитический отчёт M5 из трёх портов: чистая агрегация, детерминированная.
|
|
472
494
|
* Не падает на пустой памяти — все блоки возвращают нули/null/пустые массивы.
|