397777089e
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
39 lines
2.1 KiB
JavaScript
39 lines
2.1 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* router-task-id (✅O17) — persisted first-plan-anchor. task-id присваивается при ПЕРВОМ
|
|
* плане задачи, ПЕРСИСТИТСЯ, НЕ меняется через re-issue (новый plan-hash не сбрасывает).
|
|
* Чистое ядро; персист (loadTaskId/saveTaskId) — отдельно, инъектируемый fs,
|
|
* assertSafeSessionId guard (N3-shared, path-injection).
|
|
*/
|
|
import { assertSafeSessionId } from './action-journal.mjs';
|
|
|
|
/** Стабильный task-id: существующий → возвращается как есть (персист); иначе якорь из
|
|
* firstPlanHash (первый план задачи). Нет ни того ни другого → null. */
|
|
export function deriveTaskId({ existingTaskId = null, firstPlanHash = '' } = {}) {
|
|
if (typeof existingTaskId === 'string' && existingTaskId.trim()) return existingTaskId;
|
|
const h = String(firstPlanHash || '').trim();
|
|
return h ? `task:${h}` : null;
|
|
}
|
|
|
|
function taskIdPath(runtimeDir, sessionId) {
|
|
assertSafeSessionId(sessionId);
|
|
const sep = runtimeDir.endsWith('/') ? '' : '/';
|
|
return `${runtimeDir}${sep}router-task-id-${sessionId}.txt`;
|
|
}
|
|
|
|
/** Загрузить персистнутый task-id (или null). fsImpl инъектируем. */
|
|
export function loadTaskId({ sessionId, runtimeDir, fsImpl }) {
|
|
try { return String(fsImpl.readFileSync(taskIdPath(runtimeDir, sessionId), 'utf8')).trim() || null; }
|
|
catch (e) { if (e && e.code === 'ENOENT') return null; throw e; }
|
|
}
|
|
|
|
/** Персистнуть task-id (идемпотентно — A0 пишет один раз при первом плане).
|
|
* F-C1 (sharp-edges): атомарно temp→rename (зеркало plan-lock writeAtomic) — частичная
|
|
* запись не оставляет битый якорь на финальном пути. */
|
|
export function saveTaskId({ taskId, sessionId, runtimeDir, fsImpl }) {
|
|
const path = taskIdPath(runtimeDir, sessionId);
|
|
const tmp = `${path}.tmp`;
|
|
fsImpl.writeFileSync(tmp, String(taskId || ''));
|
|
fsImpl.renameSync(tmp, path);
|
|
}
|