@basou/core 0.4.0 → 0.5.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/dist/index.d.ts +2724 -2328
- package/dist/index.js +843 -59
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/claude-code/claude-code-adapter.ts","../src/approval/approval-store.ts","../src/lib/error-codes.ts","../src/schemas/approval.schema.ts","../src/schemas/shared.schema.ts","../src/ids/ulid.ts","../src/storage/yaml-store.ts","../src/storage/atomic.ts","../src/decisions/decisions-renderer.ts","../src/events/event-replay.ts","../src/schemas/event.schema.ts","../src/storage/sessions.ts","../src/schemas/session.schema.ts","../src/events/event-writer.ts","../src/git/snapshot.ts","../src/storage/status.ts","../src/schemas/status.schema.ts","../src/git/diff.ts","../src/handoff/handoff-renderer.ts","../src/storage/tasks.ts","../src/schemas/task.schema.ts","../src/storage/ad-hoc-session.ts","../src/lib/path-sanitizer.ts","../src/storage/lockfile.ts","../src/storage/task-index.ts","../src/schemas/task-index.schema.ts","../src/lib/duration.ts","../src/lib/id-resolver.ts","../src/runtime/child-process-runner.ts","../src/schemas/manifest.schema.ts","../src/schemas/session-import.schema.ts","../src/storage/basou-dir.ts","../src/storage/gitignore.ts","../src/storage/manifest.ts","../src/storage/markdown-store.ts","../src/storage/session-import.ts","../src/index.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\n\n/**\n * Static metadata identifying the claude-code adapter as the session source.\n * Consumed by the CLI orchestration when populating `session.yaml.source`\n * and event `source` fields. The literal `kind` is part of the wire format\n * defined by the session schema; do not change without coordinated schema\n * migration.\n */\nexport const claudeCodeAdapterMetadata = {\n kind: \"claude-code-adapter\",\n version: \"0.1.0\",\n} as const;\n\n/**\n * Lookup predicate used by {@link resolveClaudeCodeCommand} to decide\n * whether a candidate executable is reachable on PATH. Exposed as a\n * parameter so tests can substitute a deterministic mock; production\n * callers should omit it and rely on the default `which`-based lookup.\n */\nexport type CommandLookup = (command: string) => Promise<boolean>;\n\n/**\n * Resolve the Claude Code CLI executable name. Tries `claude-code` first\n * and falls back to `claude`; the first candidate found on PATH wins.\n *\n * Throws a fixed-message Error when neither candidate is reachable, so\n * callers can present a single user-facing prompt to install the CLI.\n *\n * @throws Error(\"Claude Code CLI not found in PATH. Install claude-code (or claude) first.\")\n */\nexport async function resolveClaudeCodeCommand(\n lookup: CommandLookup = isOnPath,\n): Promise<{ command: string }> {\n for (const candidate of [\"claude-code\", \"claude\"]) {\n if (await lookup(candidate)) return { command: candidate };\n }\n throw new Error(\"Claude Code CLI not found in PATH. Install claude-code (or claude) first.\");\n}\n\n/**\n * Default {@link CommandLookup} backed by `which` (POSIX) — the spawn\n * succeeds with exit code 0 iff the candidate is on PATH. Windows fallback\n * is intentionally not implemented in v0.1; call sites that target Windows\n * supply their own lookup.\n */\nasync function isOnPath(command: string): Promise<boolean> {\n return new Promise((resolve) => {\n const child = spawn(\"which\", [command], { stdio: \"ignore\" });\n child.on(\"error\", () => resolve(false));\n child.on(\"exit\", (code) => resolve(code === 0));\n });\n}\n\n/**\n * Stub for the future `adapter_output` summary generator.\n *\n * The current release keeps `capture: \"none\"` and intentionally does\n * not emit `adapter_output` events, so this hook has no production\n * callers yet. The signature is committed so a later release can\n * implement raw_ref generation without retrofitting the adapter\n * scaffold.\n *\n * @throws Error - always; not implemented in this release.\n */\nexport function summarizeAdapterOutput(_stream: \"stdout\" | \"stderr\", _raw: string): string {\n throw new Error(\"adapter_output summary is not implemented in this release\");\n}\n","import { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { findErrorCode } from \"../lib/error-codes.js\";\nimport { type Approval, ApprovalSchema } from \"../schemas/approval.schema.js\";\nimport type { BasouPaths } from \"../storage/basou-dir.js\";\nimport { readYamlFile } from \"../storage/yaml-store.js\";\n\n/** Which side of `.basou/approvals/` an approval YAML lives on. */\nexport type ApprovalLocation = \"pending\" | \"resolved\";\n\n/** Result returned by {@link loadApproval}: the parsed approval and where it was found. */\nexport type LoadedApproval = {\n approval: Approval;\n location: ApprovalLocation;\n};\n\n/**\n * Locate and load the approval YAML for `approvalId`. Searches resolved\n * first so that a duplicated YAML (the crash-window scenario where both\n * pending and resolved exist for the same id) returns the resolved-side\n * record — matching the dedupe rule used by `approval list` and\n * `resolveApprovalId`. Returns null if neither directory contains the\n * YAML. Throws with a pathless message on read or schema-validation\n * failure.\n */\nexport async function loadApproval(\n paths: BasouPaths,\n approvalId: string,\n): Promise<LoadedApproval | null> {\n for (const location of [\"resolved\", \"pending\"] as const) {\n const filePath = join(paths.approvals[location], `${approvalId}.yaml`);\n let raw: unknown;\n try {\n raw = await readYamlFile(filePath);\n } catch (error: unknown) {\n // ENOENT (i.e. \"YAML file not found\") → continue to the other directory.\n if (error instanceof Error && error.message === \"YAML file not found\") continue;\n throw new Error(\"Failed to read approval\", { cause: error });\n }\n const result = ApprovalSchema.safeParse(raw);\n if (!result.success) {\n throw new Error(\"Failed to read approval\", { cause: result.error });\n }\n // Defensive id check: a hand-edited YAML whose `id` field disagrees\n // with the filename-derived id would otherwise let the CLI render or\n // mutate one approval while citing another. Treat the mismatch as a\n // read failure rather than silently picking one side.\n if (result.data.id !== approvalId) {\n throw new Error(\"Failed to read approval\", {\n cause: new Error(\n `Approval id mismatch: filename id ${approvalId} vs YAML body id ${result.data.id}`,\n ),\n });\n }\n return { approval: result.data, location };\n }\n return null;\n}\n\n/**\n * Enumerate approval IDs by inspecting `<id>.yaml` filenames in pending\n * and resolved. ENOENT on either directory is treated as empty (e.g. a\n * workspace that has no resolved approvals yet). YAML parse and schema\n * validation are NOT performed; callers that need the parsed approval\n * should use {@link loadApproval} per ID.\n */\nexport async function enumerateApprovals(paths: BasouPaths): Promise<{\n pending: string[];\n resolved: string[];\n}> {\n const [pending, resolved] = await Promise.all([\n enumerateIds(paths.approvals.pending),\n enumerateIds(paths.approvals.resolved),\n ]);\n return { pending, resolved };\n}\n\nasync function enumerateIds(dir: string): Promise<string[]> {\n let entries: string[];\n try {\n const dirents = await readdir(dir, { withFileTypes: true });\n entries = dirents\n .filter((e) => e.isFile() && e.name.endsWith(\".yaml\"))\n .map((e) => e.name.slice(0, -\".yaml\".length));\n } catch (error: unknown) {\n if (findErrorCode(error, \"ENOENT\")) return [];\n throw new Error(\"Failed to enumerate approvals\", { cause: error });\n }\n return entries;\n}\n\n/**\n * Return true when an approval is in `pending` state and its `expires_at`\n * timestamp has elapsed. Used by `basou approval list` / `show` to surface\n * a `(expired)` label without mutating the YAML file. Approval expiry uses\n * lazy-evaluation semantics; actual `approval_expired` event firing is\n * deferred to a later step.\n *\n * `now` is taken as a parameter so a single CLI invocation can share one\n * \"now\" across every record it inspects (avoids boundary races where two\n * reads of `Date.now()` straddle an expiry instant).\n */\nexport function isLazyExpired(approval: Approval, now: Date): boolean {\n if (approval.status !== \"pending\") return false;\n if (approval.expires_at === null) return false;\n const expiresMs = Date.parse(approval.expires_at);\n if (!Number.isFinite(expiresMs)) return false;\n return expiresMs < now.getTime();\n}\n","/**\n * Walk the cause chain (up to `depth` levels) looking for an Error whose\n * errno-style `code` matches `code`. Returns true on the first match.\n * Resilient to wrapper depth changes so that ENOENT detection survives\n * future error-wrapping refactors.\n */\nexport function findErrorCode(error: unknown, code: string, depth = 4): boolean {\n let cur: unknown = error;\n for (let i = 0; i < depth && cur instanceof Error; i++) {\n const c = (cur as { code?: unknown }).code;\n if (typeof c === \"string\" && c === code) return true;\n cur = (cur as Error).cause;\n }\n return false;\n}\n","import { z } from \"zod\";\nimport {\n ApprovalIdSchema,\n IsoTimestampSchema,\n RiskLevelSchema,\n SchemaVersionSchema,\n SessionIdSchema,\n} from \"./shared.schema.js\";\n\n/**\n * Lifecycle states of a Basou approval. The status is stored directly on\n * the approval YAML (flat shape) so that pending → resolved transitions\n * are atomic-move + in-place rewrites rather than schema-variant swaps.\n */\nexport const ApprovalStatusSchema = z.enum([\"pending\", \"approved\", \"rejected\", \"expired\"]);\n/** Inferred runtime type for {@link ApprovalStatusSchema}. */\nexport type ApprovalStatus = z.infer<typeof ApprovalStatusSchema>;\n\n/**\n * Schema for `.basou/approvals/{pending,resolved}/<approval_id>.yaml`.\n *\n * The schema is intentionally flat (one shape regardless of `status`) so\n * that pending and resolved YAMLs share the same parser. Required vs.\n * optional semantics by status (e.g. `rejection_reason` MUST be set when\n * `status === \"rejected\"`) are enforced at the CLI orchestration layer\n * rather than here, mirroring the approval event variants in\n * `event.schema.ts`.\n *\n * The `action` field is `{ kind: string }` with `passthrough()` so that\n * adapter-defined keys (e.g. `command`, `path`, `target_url`) survive the\n * round-trip without being stripped — matching the approval_requested\n * event variant.\n */\nexport const ApprovalSchema = z.object({\n schema_version: SchemaVersionSchema,\n id: ApprovalIdSchema,\n session_id: SessionIdSchema,\n created_at: IsoTimestampSchema,\n status: ApprovalStatusSchema,\n risk_level: RiskLevelSchema,\n action: z.object({ kind: z.string() }).passthrough(),\n reason: z.string(),\n expires_at: IsoTimestampSchema.nullable().default(null),\n // The four fields below are null while `status === \"pending\"` and set\n // once a resolver records a decision. Defaulting to null keeps the\n // pending YAML free of explicit nulls if a producer omits them.\n resolver: z.string().nullable().default(null),\n resolved_at: IsoTimestampSchema.nullable().default(null),\n note: z.string().nullable().default(null),\n rejection_reason: z.string().nullable().default(null),\n});\n\n/** Inferred runtime type for {@link ApprovalSchema}. */\nexport type Approval = z.infer<typeof ApprovalSchema>;\n","import { z } from \"zod\";\nimport { type IdPrefix, isValidPrefixedId, type PrefixedId } from \"../ids/ulid.js\";\n\n/**\n * Schema version literal pinned to \"0.1.0\" for Basou v0.1.\n * Reused across every entity schema so inferred types narrow to the literal.\n */\nexport const SchemaVersionSchema = z.literal(\"0.1.0\");\n\n/**\n * ISO 8601 timestamp with explicit timezone offset (e.g. `+09:00`).\n *\n * The spec samples include offsets, so the default zod `.datetime()` (which\n * rejects offsets) is insufficient; `{ offset: true }` is required.\n */\nexport const IsoTimestampSchema = z.string().datetime({ offset: true });\n\n// Internal factory shared by every prefixed-ID schema. Not exported because\n// the public API surface should only expose the six fully-typed ID schemas.\nconst createPrefixedIdSchema = <P extends IdPrefix>(prefix: P) => {\n const refiner = (value: string): value is PrefixedId<P> =>\n isValidPrefixedId(value) && value.startsWith(`${prefix}_`);\n return z.string().refine(refiner, { message: `Expected ${prefix}_<ULID>` });\n};\n\n/** Workspace ID schema: validates `ws_<26-char ULID>`. */\nexport const WorkspaceIdSchema = createPrefixedIdSchema(\"ws\");\n/** Task ID schema: validates `task_<26-char ULID>`. */\nexport const TaskIdSchema = createPrefixedIdSchema(\"task\");\n/** Session ID schema: validates `ses_<26-char ULID>`. */\nexport const SessionIdSchema = createPrefixedIdSchema(\"ses\");\n/** Event ID schema: validates `evt_<26-char ULID>`. */\nexport const EventIdSchema = createPrefixedIdSchema(\"evt\");\n/** Approval ID schema: validates `appr_<26-char ULID>`. */\nexport const ApprovalIdSchema = createPrefixedIdSchema(\"appr\");\n/** Decision ID schema: validates `decision_<26-char ULID>`. */\nexport const DecisionIdSchema = createPrefixedIdSchema(\"decision\");\n\n/**\n * Risk level vocabulary fixed by the spec. Adapters MUST emit one of these\n * four values; arbitrary strings are rejected at schema parse time.\n */\nexport const RiskLevelSchema = z.enum([\"low\", \"medium\", \"high\", \"critical\"]);\n/** Inferred runtime type for {@link RiskLevelSchema}. */\nexport type RiskLevel = z.infer<typeof RiskLevelSchema>;\n\n/**\n * Source attribution for events (e.g. \"claude-code-adapter\",\n * \"git-capability\", \"terminal-recording\", \"local-cli\", \"human\"). Free-form\n * non-empty string in v0.1; a stricter enum may be introduced post-v0.1.\n */\nexport const EventSourceSchema = z.string().min(1);\n","import { isValid as isValidUlid, monotonicFactory } from \"ulid\";\n\n/**\n * Allowed ID type prefixes for Basou entities.\n *\n * Frozen at runtime so that mutating the exported array cannot diverge from\n * the validation set used internally. The single source of truth for both\n * the `IdPrefix` type and runtime prefix checks.\n */\nexport const ID_PREFIXES = Object.freeze([\"ws\", \"task\", \"ses\", \"evt\", \"appr\", \"decision\"] as const);\n\n/**\n * Type prefix used for Basou entity IDs.\n * Format: `<prefix>_<26-char ULID>`, e.g. `ws_01HXABCDEF1234567890ABCDEF`.\n */\nexport type IdPrefix = (typeof ID_PREFIXES)[number];\n\n/**\n * A Basou entity ID as a template literal type.\n *\n * `PrefixedId<\"ses\">` narrows to ``ses_${string}`` so a session schema can\n * preserve the prefix in its inferred type beyond runtime validation.\n */\nexport type PrefixedId<P extends IdPrefix = IdPrefix> = `${P}_${string}`;\n\nconst PREFIX_SET = new Set<string>(ID_PREFIXES);\n\n// ULID body shape: 26 chars, first char 0-7 (48-bit timestamp / 5-bit Crockford\n// symbols), remaining 25 chars use Crockford alphabet excluding I, L, O, U.\n// Enforced locally because npm `ulid`'s `isValid` does not reject leading 8 or 9.\nconst ULID_BODY_REGEX = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/;\n\n// Module-scope monotonic factory. Created once at module load. Pure function\n// so it does not violate `sideEffects: false` of the surrounding package.\nconst monotonic = monotonicFactory();\n\n/**\n * Generate a Crockford Base32 ULID.\n *\n * The result is a 26-character, lexicographically time-sortable identifier.\n * Multiple calls within the same millisecond are strictly increasing for the\n * lifetime of the current process.\n *\n * NOTE: `seedTime` is forwarded to the underlying monotonic factory and is\n * NOT a deterministic seed: repeated calls with the same `seedTime` still\n * return strictly increasing values, because the factory increments its\n * internal counter on each call.\n *\n * @param seedTime Optional millisecond timestamp passed to the monotonic\n * factory. Useful for ordered generation in tests; not deterministic.\n */\nexport function ulid(seedTime?: number): string {\n return monotonic(seedTime);\n}\n\n/**\n * Generate a prefixed Basou ID, e.g. `ses_01HXABCDEF1234567890ABCDEF`.\n *\n * The return type preserves the prefix as a template literal type so that\n * downstream zod schemas can narrow an `IdPrefix` parameter through the API.\n *\n * Throws if `prefix` is not one of {@link ID_PREFIXES}. The runtime guard\n * defends against JavaScript callers and casted TypeScript that bypass the\n * compile-time `IdPrefix` constraint.\n */\nexport function prefixedUlid<P extends IdPrefix>(prefix: P): PrefixedId<P> {\n if (!PREFIX_SET.has(prefix)) {\n throw new Error(`Unknown ID prefix: ${prefix}`);\n }\n return `${prefix}_${ulid()}` as PrefixedId<P>;\n}\n\n/**\n * Check whether the given string is a valid prefixed Basou ID.\n *\n * Returns true only if the string has shape `<prefix>_<ULID>` where prefix is\n * one of {@link ID_PREFIXES} and the trailing 26 characters form a valid\n * Crockford Base32 ULID. Validation combines a strict shape regex (to enforce\n * the 0-7 leading char and the I/L/O/U exclusion) with the npm `ulid`\n * library's `isValid` for forward compatibility.\n *\n * NOTE: This validates the prefix is known. Schemas that require a specific\n * prefix (e.g. only `ses_*` for a session ID) must add their own narrowing.\n */\nexport function isValidPrefixedId(value: string): boolean {\n const idx = value.indexOf(\"_\");\n if (idx <= 0) return false;\n const prefix = value.slice(0, idx);\n const ulidPart = value.slice(idx + 1);\n if (!PREFIX_SET.has(prefix)) return false;\n if (!ULID_BODY_REGEX.test(ulidPart)) return false;\n return isValidUlid(ulidPart);\n}\n","import { readFile } from \"node:fs/promises\";\nimport { parse, stringify } from \"yaml\";\nimport { atomicCreate, atomicReplace } from \"./atomic.js\";\n\n/**\n * Read a YAML file as `unknown`. Caller MUST validate via a zod schema.\n *\n * Throws Error with pathless message and the original native error attached\n * as `cause` for I/O failures and YAML parse errors. All fs and parse exits\n * go through fixed messages so absolute paths cannot leak via `error.message`.\n */\nexport async function readYamlFile(filePath: string): Promise<unknown> {\n let body: string;\n try {\n body = await readFile(filePath, \"utf8\");\n } catch (error: unknown) {\n if (hasErrorCode(error) && error.code === \"ENOENT\") {\n throw new Error(\"YAML file not found\", { cause: error });\n }\n throw new Error(\"Failed to read YAML file\", { cause: error });\n }\n try {\n return parse(body);\n } catch (error: unknown) {\n throw new Error(\"Failed to parse YAML content\", { cause: error });\n }\n}\n\n/**\n * Write a value as YAML using {@link atomicReplace} for crash-resistant\n * atomicity. The shared helper handles the tmp-file + rename sequence,\n * `wx` collision guard, and best-effort tmp cleanup on failure. This\n * wrapper adds the YAML serialisation and the pathless error vocabulary.\n */\nexport async function writeYamlFile(filePath: string, value: unknown): Promise<void> {\n const body = stringify(value);\n try {\n await atomicReplace(filePath, body);\n } catch (error: unknown) {\n throw new Error(\"Failed to write YAML file\", { cause: error });\n }\n}\n\n/**\n * Atomically create a new YAML file. Like {@link writeYamlFile} but\n * delegates to {@link atomicCreate} so a pre-existing target fails with\n * EEXIST instead of being silently overwritten.\n *\n * Used by `basou approval approve` / `reject` to write the resolved-side\n * YAML, so a concurrent resolver cannot overwrite an already-resolved\n * approval.\n *\n * Throws `Error(\"Failed to write YAML file\", { cause })` on failure; if\n * `cause.code === \"EEXIST\"` the caller can detect a target-exists race.\n */\nexport async function linkYamlFile(filePath: string, value: unknown): Promise<void> {\n const body = stringify(value);\n try {\n await atomicCreate(filePath, body);\n } catch (error: unknown) {\n throw new Error(\"Failed to write YAML file\", { cause: error });\n }\n}\n\n/**\n * Overwrite an existing YAML file atomically. Like {@link writeYamlFile}\n * but with a distinct pathless message label, used for files that\n * legitimately need in-place mutation (e.g. session.yaml's status /\n * ended_at lifecycle updates).\n */\nexport async function overwriteYamlFile(filePath: string, value: unknown): Promise<void> {\n const body = stringify(value);\n try {\n await atomicReplace(filePath, body);\n } catch (error: unknown) {\n throw new Error(\"Failed to overwrite YAML file\", { cause: error });\n }\n}\n\nfunction hasErrorCode(error: unknown): error is Error & { code: string } {\n if (!(error instanceof Error)) return false;\n return typeof (error as unknown as Record<string, unknown>).code === \"string\";\n}\n","import { randomUUID } from \"node:crypto\";\nimport { link, rename, unlink, writeFile } from \"node:fs/promises\";\n\n/**\n * Atomically create a new file at `targetPath` via tmp + link.\n *\n * Strategy: write the body to a sibling tmp file (`${targetPath}.tmp.<uuid>`)\n * with the `wx` flag, then `link()` the tmp inode into place at `targetPath`.\n * If the target already exists, `link` fails with EEXIST — callers detect this\n * via `findErrorCode(error, \"EEXIST\")` to surface a domain-specific\n * \"already exists\" message.\n *\n * The tmp file lives in the SAME directory as the target so `link` cannot\n * fail with EXDEV. On every code path (success and failure) the tmp inode\n * is best-effort unlinked, so after a successful call the tmp side of the\n * hard-link pair is removed and only `targetPath` remains.\n *\n * The native fs error is re-thrown WITHOUT wrapping so callers can attach\n * their own pathless message via `new Error(\"<fixed msg>\", { cause })`. The\n * caller is responsible for the final error vocabulary (pathless contract).\n */\nexport async function atomicCreate(targetPath: string, content: string | Buffer): Promise<void> {\n const tmpPath = `${targetPath}.tmp.${randomUUID()}`;\n try {\n await writeFile(tmpPath, content, { encoding: \"utf8\", flag: \"wx\" });\n await link(tmpPath, targetPath);\n } catch (error: unknown) {\n await unlink(tmpPath).catch(() => undefined);\n throw error;\n }\n // tmp inode is now linked twice (tmp + target); unlink the tmp side so\n // disk does not carry a spurious sibling after a successful create.\n await unlink(tmpPath).catch(() => undefined);\n}\n\n/**\n * Atomically replace the file at `targetPath` via tmp + rename.\n *\n * Strategy: write the body to a sibling tmp file (`${targetPath}.tmp.<uuid>`)\n * with the `wx` flag, then `rename()` the tmp over `targetPath`. Silently\n * overwrites any existing file at `targetPath`. The tmp file lives in the\n * SAME directory as the target so `rename` cannot fail with EXDEV. `rename`\n * consumes the tmp file, so no post-success cleanup is needed.\n *\n * On failure the tmp file is best-effort unlinked so disk never carries a\n * half-written rename source. The native fs error is re-thrown WITHOUT\n * wrapping so callers can attach their own pathless message.\n */\nexport async function atomicReplace(targetPath: string, content: string | Buffer): Promise<void> {\n const tmpPath = `${targetPath}.tmp.${randomUUID()}`;\n try {\n await writeFile(tmpPath, content, { encoding: \"utf8\", flag: \"wx\" });\n await rename(tmpPath, targetPath);\n } catch (error: unknown) {\n await unlink(tmpPath).catch(() => undefined);\n throw error;\n }\n}\n","import { lstat } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { type ReplayWarning, replayEvents } from \"../events/event-replay.js\";\nimport type { BasouPaths } from \"../storage/basou-dir.js\";\nimport { loadSessionEntries, type SessionSkipReason } from \"../storage/sessions.js\";\n\nexport type DecisionsRendererInput = {\n paths: BasouPaths;\n nowIso: string;\n onWarning?: (warning: ReplayWarning, sessionId: string) => void;\n onSessionSkip?: (sessionId: string, reason: SessionSkipReason) => void;\n};\n\nexport type DecisionsRendererResult = {\n /** Generated body WITHOUT BASOU:GENERATED markers. */\n body: string;\n decisionCount: number;\n};\n\ntype DecisionRecord = {\n decisionId: string;\n title: string;\n occurredAt: string;\n sessionId: string;\n // Rich fields. All optional; populated only when the decision_recorded\n // event carried the field.\n rationale: string | null | undefined;\n alternatives: readonly string[] | undefined;\n rejectedReason: string | null | undefined;\n linkedEvents: readonly string[] | undefined;\n linkedFiles: readonly string[] | undefined;\n};\n\n/**\n * Render the body of `decisions.md` from `decision_recorded` events across\n * every healthy session in the workspace.\n *\n * Session enumeration goes through {@link loadSessionEntries} (the same path\n * the handoff renderer uses) so that `session.yaml`-broken sessions are\n * skipped in BOTH outputs and the handoff's `decisionCount` summary stays\n * consistent with the number of sections rendered here.\n *\n * Order: `occurred_at` ascending with `decisionId` (= ULID) as tie-breaker.\n * Both fields are monotonic, so the result is a stable cross-session\n * timeline.\n *\n * The decision rich fields (rationale / alternatives / rejected_reason /\n * linked_events / linked_files) are rendered when the event carries them.\n * `linked_events` and `linked_files` are OPAQUE references: the schema only\n * validates the SHAPE, not existence — references that cannot be resolved\n * to a known event id or an existing file on disk are surfaced inline as\n * `(missing)` so cross-workspace round-trips never reject parse-time.\n */\nexport async function renderDecisions(\n input: DecisionsRendererInput,\n): Promise<DecisionsRendererResult> {\n const now = new Date(input.nowIso);\n // Same rationale as handoff-renderer. Track which\n // sessions already had `events_jsonl_unreadable` surfaced so non-running\n // sessions whose events.jsonl is unreadable still produce a stderr\n // warning instead of silently dropping their decisions.\n const unreadableEmitted = new Set<string>();\n const wrappedSkip: (sid: string, reason: SessionSkipReason) => void = (sid, reason) => {\n if (reason === \"events_jsonl_unreadable\") unreadableEmitted.add(sid);\n input.onSessionSkip?.(sid, reason);\n };\n const loadOpts: Parameters<typeof loadSessionEntries>[1] = { now, onSkip: wrappedSkip };\n if (input.onWarning !== undefined) loadOpts.onWarning = input.onWarning;\n const entries = await loadSessionEntries(input.paths, loadOpts);\n\n const decisions: DecisionRecord[] = [];\n // Workspace-wide event id index, populated during the same scan that\n // collects decisions, so `linked_events` membership can be resolved\n // without a second pass over events.jsonl.\n const knownEventIds = new Set<string>();\n for (const entry of entries) {\n const sessionDir = join(input.paths.sessions, entry.sessionId);\n try {\n for await (const ev of replayEvents(sessionDir, {\n onWarning: (w) => input.onWarning?.(w, entry.sessionId),\n })) {\n knownEventIds.add(ev.id);\n if (ev.type === \"decision_recorded\") {\n decisions.push({\n decisionId: ev.decision_id,\n title: ev.title,\n occurredAt: ev.occurred_at,\n sessionId: entry.sessionId,\n rationale: ev.rationale,\n alternatives: ev.alternatives,\n rejectedReason: ev.rejected_reason,\n linkedEvents: ev.linked_events,\n linkedFiles: ev.linked_files,\n });\n }\n }\n } catch {\n if (!unreadableEmitted.has(entry.sessionId)) {\n wrappedSkip(entry.sessionId, \"events_jsonl_unreadable\");\n }\n }\n }\n decisions.sort((a, b) => {\n const c = Date.parse(a.occurredAt) - Date.parse(b.occurredAt);\n return c !== 0 ? c : a.decisionId.localeCompare(b.decisionId);\n });\n\n // Resolve linked_files relative to the repository root (= parent of\n // `.basou/`). Existence is checked with `lstat` so symlinks are treated\n // honestly — a dangling symlink is reported as `(missing)`. The check\n // runs once per unique path so repeated references share their lookup.\n const repoRoot = dirname(input.paths.root);\n const fileExistenceCache = new Map<string, boolean>();\n async function fileExists(relPath: string): Promise<boolean> {\n const cached = fileExistenceCache.get(relPath);\n if (cached !== undefined) return cached;\n const abs = resolve(repoRoot, relPath);\n let exists: boolean;\n try {\n await lstat(abs);\n exists = true;\n } catch {\n exists = false;\n }\n fileExistenceCache.set(relPath, exists);\n return exists;\n }\n\n const body = await formatDecisionsBody({\n nowIso: input.nowIso,\n decisions,\n knownEventIds,\n fileExists,\n });\n return { body, decisionCount: decisions.length };\n}\n\nasync function formatDecisionsBody(args: {\n nowIso: string;\n decisions: ReadonlyArray<DecisionRecord>;\n knownEventIds: ReadonlySet<string>;\n fileExists: (relPath: string) => Promise<boolean>;\n}): Promise<string> {\n const lines: string[] = [];\n lines.push(\"# Decisions\");\n lines.push(\"\");\n lines.push(`> Generated at ${args.nowIso}`);\n lines.push(\"\");\n if (args.decisions.length === 0) {\n lines.push(\"(no decisions recorded yet)\");\n return lines.join(\"\\n\");\n }\n for (const d of args.decisions) {\n lines.push(`## ${d.decisionId}: ${d.title}`);\n lines.push(\"\");\n const occurredDate = d.occurredAt.slice(0, 10); // YYYY-MM-DD\n lines.push(`- 決定日: ${occurredDate}`);\n lines.push(`- session: ${shortDecisionSessionId(d.sessionId)}`);\n lines.push(`- 判断: ${d.title}`);\n if (typeof d.rationale === \"string\" && d.rationale.length > 0) {\n lines.push(`- rationale: ${d.rationale}`);\n }\n if (d.alternatives !== undefined && d.alternatives.length > 0) {\n lines.push(`- alternatives: ${d.alternatives.join(\", \")}`);\n }\n if (typeof d.rejectedReason === \"string\" && d.rejectedReason.length > 0) {\n lines.push(`- rejected_reason: ${d.rejectedReason}`);\n }\n if (d.linkedEvents !== undefined && d.linkedEvents.length > 0) {\n const parts = d.linkedEvents.map((eid) =>\n args.knownEventIds.has(eid) ? eid : `${eid} (missing)`,\n );\n lines.push(`- linked_events: ${parts.join(\", \")}`);\n }\n if (d.linkedFiles !== undefined && d.linkedFiles.length > 0) {\n const parts = await Promise.all(\n d.linkedFiles.map(async (path) =>\n (await args.fileExists(path)) ? path : `${path} (missing)`,\n ),\n );\n lines.push(`- linked_files: ${parts.join(\", \")}`);\n }\n lines.push(\"\");\n }\n return lines.join(\"\\n\");\n}\n\nfunction shortDecisionSessionId(sessionId: string): string {\n const SES = \"ses_\";\n if (sessionId.startsWith(SES)) return sessionId.slice(SES.length, SES.length + 10);\n return sessionId.slice(0, 10);\n}\n","import { createReadStream } from \"node:fs\";\nimport { stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { findErrorCode } from \"../lib/error-codes.js\";\nimport { type Event, EventSchema } from \"../schemas/event.schema.js\";\n\n/**\n * Recoverable warning surfaced via {@link ReplayOptions.onWarning}. The replay\n * generator never throws on these — it skips the offending line and continues.\n *\n * `partial_trailing_line` indicates the events.jsonl did not end with `\\n` and\n * the unterminated tail parsed as a complete event. The line is dropped\n * instead of yielded so consumers cannot accidentally observe a\n * partially-written record.\n */\nexport type ReplayWarning =\n | { kind: \"partial_trailing_line\"; line: number }\n | { kind: \"malformed_json\"; line: number; cause: unknown }\n | { kind: \"schema_violation\"; line: number; cause: unknown };\n\nexport type ReplayOptions = {\n /**\n * Hook to receive recoverable warnings (partial line / malformed JSON /\n * schema violation). When omitted, warnings are silently dropped — callers\n * that want to surface them (e.g. CLI orchestration) MUST provide this hook.\n */\n onWarning?: (warning: ReplayWarning) => void;\n};\n\n/**\n * Stream events from `<sessionDir>/events.jsonl` line by line.\n *\n * Behavior:\n * - ENOENT or empty file: yields nothing without warning.\n * - I/O error: throws `Error(\"Failed to read events.jsonl\")` with the native\n * error attached as `cause`. The thrown message never embeds an absolute\n * path (pathless contract).\n * - Trailing partial line that parses as a valid event: dropped silently when\n * {@link ReplayOptions.onWarning} is omitted; otherwise reported as\n * `partial_trailing_line`. A trailing partial line that fails JSON parsing\n * is reported as `malformed_json` instead.\n * - Malformed JSON / schema violation: skipped, with the corresponding\n * warning when a hook is provided.\n *\n * Single-writer-per-session is assumed (see `event-writer.ts` JSDoc on\n * {@link appendEvent}). Concurrent writers may interleave lines beyond\n * `PIPE_BUF` and are not recovered here in v0.1.\n */\n// NOTE: switched from plan A (Transform stream + readline) to plan B (manual\n// chunk-level split) because plan A's source-stream errors do not propagate\n// through `pipe()` to the readline iterator, so an EACCES on the events.jsonl\n// hangs the for-await loop instead of throwing. Plan B observes errors\n// directly via the for-await over `createReadStream` and reaches end-of-stream\n// deterministically with the trailing buffer in hand.\nexport async function* replayEvents(\n sessionDir: string,\n options: ReplayOptions = {},\n): AsyncGenerator<Event, void, void> {\n const filePath = join(sessionDir, \"events.jsonl\");\n\n // Probe existence first so ENOENT (= empty session) is silent while every\n // other I/O failure surfaces as a single fixed-message error.\n try {\n await stat(filePath);\n } catch (error: unknown) {\n if (findErrorCode(error, \"ENOENT\")) return;\n throw new Error(\"Failed to read events.jsonl\", { cause: error });\n }\n\n let stream: ReturnType<typeof createReadStream>;\n try {\n stream = createReadStream(filePath, { encoding: \"utf8\" });\n } catch (error: unknown) {\n throw new Error(\"Failed to read events.jsonl\", { cause: error });\n }\n\n let buffer = \"\";\n let lineNo = 0;\n\n try {\n for await (const chunk of stream as unknown as AsyncIterable<string>) {\n buffer += chunk;\n let newlineIdx = buffer.indexOf(\"\\n\");\n while (newlineIdx !== -1) {\n lineNo += 1;\n const rawLine = buffer.slice(0, newlineIdx);\n buffer = buffer.slice(newlineIdx + 1);\n const ev = processLine(rawLine, lineNo, options);\n if (ev !== null) yield ev;\n newlineIdx = buffer.indexOf(\"\\n\");\n }\n }\n } catch (error: unknown) {\n throw new Error(\"Failed to read events.jsonl\", { cause: error });\n }\n\n // Stream ended mid-line: anything left in `buffer` is the trailing partial\n // line. Empty / whitespace-only trailing content is treated as a normal end\n // of file (e.g. a final '\\n' was stripped by the loop above).\n const trimmed = buffer.replace(/[\\r\\n\\t ]+$/u, \"\");\n if (trimmed.length === 0) return;\n lineNo += 1;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(trimmed);\n } catch (cause) {\n // The trailing buffer was non-empty AND JSON-invalid. Either\n // partial_trailing_line or malformed_json captures the same observable\n // outcome; we surface malformed_json because the JSON layer rejected it\n // first and the line number is meaningful for the consumer.\n options.onWarning?.({ kind: \"malformed_json\", line: lineNo, cause });\n return;\n }\n\n const result = EventSchema.safeParse(parsed);\n if (!result.success) {\n options.onWarning?.({ kind: \"schema_violation\", line: lineNo, cause: result.error });\n return;\n }\n\n // Valid JSON + valid event schema BUT no terminating newline. Drop instead\n // of yielding so a half-flushed write cannot be consumed as a real event.\n options.onWarning?.({ kind: \"partial_trailing_line\", line: lineNo });\n}\n\nfunction processLine(rawLine: string, lineNo: number, options: ReplayOptions): Event | null {\n const trimmed = rawLine.trim();\n if (trimmed.length === 0) return null;\n let parsed: unknown;\n try {\n parsed = JSON.parse(trimmed);\n } catch (cause) {\n options.onWarning?.({ kind: \"malformed_json\", line: lineNo, cause });\n return null;\n }\n const result = EventSchema.safeParse(parsed);\n if (!result.success) {\n options.onWarning?.({ kind: \"schema_violation\", line: lineNo, cause: result.error });\n return null;\n }\n return result.data;\n}\n\n/**\n * Eager array helper: collect every event from {@link replayEvents} into\n * memory. Convenience for callers that need the full list in one structure\n * (e.g. `basou session show` rendering).\n */\nexport async function readAllEvents(\n sessionDir: string,\n options: ReplayOptions = {},\n): Promise<Event[]> {\n const out: Event[] = [];\n for await (const ev of replayEvents(sessionDir, options)) {\n out.push(ev);\n }\n return out;\n}\n","import { z } from \"zod\";\nimport {\n ApprovalIdSchema,\n DecisionIdSchema,\n EventIdSchema,\n EventSourceSchema,\n IsoTimestampSchema,\n RiskLevelSchema,\n SchemaVersionSchema,\n SessionIdSchema,\n TaskIdSchema,\n} from \"./shared.schema.js\";\n\n// Common base every event variant extends. Each variant declares its own\n// `type: z.literal(...)` and adds variant-specific fields.\nconst BaseEventSchema = z.object({\n schema_version: SchemaVersionSchema,\n id: EventIdSchema,\n session_id: SessionIdSchema,\n occurred_at: IsoTimestampSchema,\n source: EventSourceSchema,\n});\n\n// --- Session lifecycle events ---\n\nconst SessionStartedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"session_started\"),\n});\n\nconst SessionEndedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"session_ended\"),\n exit_code: z.number().int().optional(),\n});\n\n// `from`/`to` use `string` to keep this module independent of session.schema\n// and avoid a circular import. A later event-replay layer may narrow these to\n// SessionStatusSchema by relocating the enum into shared.schema.\nconst SessionStatusChangedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"session_status_changed\"),\n from: z.string(),\n to: z.string(),\n});\n\n// --- Approval events ---\n\nconst ApprovalRequestedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"approval_requested\"),\n approval_id: ApprovalIdSchema,\n expires_at: IsoTimestampSchema.nullable().default(null),\n risk_level: RiskLevelSchema,\n // `action.kind` is required; additional fields are allowed to support\n // future action shapes (shell_command, external_send, ...).\n action: z.object({ kind: z.string() }).passthrough(),\n reason: z.string(),\n status: z.literal(\"pending\"),\n});\n\nconst ApprovalApprovedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"approval_approved\"),\n approval_id: ApprovalIdSchema,\n resolver: z.string().optional(),\n note: z.string().nullable().optional(),\n});\n\nconst ApprovalRejectedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"approval_rejected\"),\n approval_id: ApprovalIdSchema,\n resolver: z.string().optional(),\n reason: z.string(),\n});\n\nconst ApprovalExpiredEventSchema = BaseEventSchema.extend({\n type: z.literal(\"approval_expired\"),\n approval_id: ApprovalIdSchema,\n});\n\n// --- Command / Git / File events ---\n\n// `command` is the spawned executable name only (e.g. \"npm\"); arguments are\n// kept in `args` to preserve quoting and avoid shell-injection round-trips.\n// `exit_code` is null when the child terminated by signal. `signal` records\n// the child's terminating signal; `received_signal` records what the parent\n// process received (SIGINT/SIGTERM) and forwarded as cancellation, so a\n// timeout (signal set, received_signal absent) can be distinguished from a\n// user interrupt (both set).\nconst CommandExecutedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"command_executed\"),\n command: z.string(),\n args: z.array(z.string()),\n cwd: z.string(),\n exit_code: z.number().int().nullable(),\n signal: z.string().nullable().optional(),\n received_signal: z.string().nullable().optional(),\n duration_ms: z.number().int().nonnegative(),\n});\n\nconst GitSnapshotEventSchema = BaseEventSchema.extend({\n type: z.literal(\"git_snapshot\"),\n head: z.string(),\n branch: z.string(),\n dirty: z.boolean(),\n staged: z.array(z.string()),\n unstaged: z.array(z.string()),\n untracked: z.array(z.string()),\n ahead: z.number().int().nonnegative().optional(),\n behind: z.number().int().nonnegative().optional(),\n});\n\nconst FileChangedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"file_changed\"),\n path: z.string(),\n change_type: z.enum([\"added\", \"modified\", \"deleted\", \"renamed\"]),\n // Renamed entries record the previous path here. Optional + nullable to\n // keep the wire format stable for added / modified / deleted events.\n old_path: z.string().nullable().optional(),\n});\n\n// --- Decision / Task / Note events ---\n\n// Decision rich fields are all optional so v0.1 payloads\n// (= core 4 fields only) round-trip unchanged. References are opaque — the\n// schema only validates the SHAPE (EventId format, non-empty / length-capped\n// strings); existence of the referenced event or file is the renderer's\n// concern and surfaces as `(missing)` rather than a parse failure, so\n// import/export round-trips across workspaces never reject on a stale id.\nconst DecisionRecordedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"decision_recorded\"),\n decision_id: DecisionIdSchema,\n title: z.string(),\n rationale: z.string().nullable().optional(),\n alternatives: z.array(z.string().min(1)).optional(),\n rejected_reason: z.string().nullable().optional(),\n linked_events: z.array(EventIdSchema).optional(),\n linked_files: z.array(z.string().min(1).max(4096)).optional(),\n});\n\nconst TaskCreatedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"task_created\"),\n task_id: TaskIdSchema,\n title: z.string(),\n});\n\nconst TaskStatusChangedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"task_status_changed\"),\n task_id: TaskIdSchema,\n from: z.string(),\n to: z.string(),\n});\n\n// emitted by `basou task reconcile --write` after broken session\n// references in a task.md are cleaned up. `.strict()` so that any extra field\n// (likely a core-side miscoding of an audit value) is rejected at parse time\n// rather than silently stripped — the event is the audit trail.\nconst TaskReconciledEventSchema = BaseEventSchema.extend({\n type: z.literal(\"task_reconciled\"),\n task_id: TaskIdSchema,\n removed_created_in_session: SessionIdSchema.nullable().default(null),\n created_in_session_replacement: SessionIdSchema.nullable().default(null),\n removed_linked_sessions: z.array(SessionIdSchema).default([]),\n}).strict();\n\n// v0.2: emitted by `basou task refresh-linkage` after the task.md\n// `linked_sessions[]` snapshot is re-derived from `session.yaml.task_id`\n// matches across the workspace. Distinct from `task_reconciled` (= broken\n// ref cleanup) so each event carries a single, focused audit story.\n// `.strict()` for the same reason as TaskReconciledEvent — the event is the\n// authoritative record. The three count/array fields are all optional with\n// sensible defaults so the schema is backward-compatible (= a future caller\n// that omits them parses to an empty refresh, which is also the no-op\n// dry-run record shape).\nconst TaskLinkageRefreshedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"task_linkage_refreshed\"),\n task_id: TaskIdSchema,\n added_linked_sessions: z.array(SessionIdSchema).default([]),\n removed_linked_sessions: z.array(SessionIdSchema).default([]),\n final_count: z.number().int().nonnegative().optional(),\n}).strict();\n\n// v0.2 lifecycle events for `basou task delete` / `basou task archive`.\n// Both are `.strict()` so the audit record is exactly what the orchestrator\n// emits, and both carry the task's last-known title so events.jsonl can\n// describe what was deleted / archived without requiring the (now gone or\n// relocated) task.md.\nconst TaskDeletedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"task_deleted\"),\n task_id: TaskIdSchema,\n title: z.string().min(1),\n}).strict();\n\nconst TaskArchivedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"task_archived\"),\n task_id: TaskIdSchema,\n title: z.string().min(1),\n}).strict();\n\nconst NoteAddedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"note_added\"),\n body: z.string(),\n});\n\n// --- Adapter output (`.strict()` rejects raw bodies) ---\n//\n// The spec forbids embedding raw adapter output (`content`, `body`, `raw`,\n// ...) directly in events.jsonl (see\n// `docs/spec/schemas.md#74-adapter_output-constraint-important`). The\n// strict variant rejects any schema-unknown key so that contract is\n// enforced at parse time.\nconst AdapterOutputEventSchema = BaseEventSchema.extend({\n type: z.literal(\"adapter_output\"),\n stream: z.enum([\"stdout\", \"stderr\"]),\n summary: z.string(),\n raw_ref: z.string(),\n redacted: z.boolean().optional(),\n}).strict();\n\n/**\n * Discriminated union of every Basou v0.1 event type. The `type` literal\n * narrows TypeScript to the appropriate variant. The `adapter_output`\n * variant is uniquely strict to bar raw adapter bodies.\n */\nexport const EventSchema = z.discriminatedUnion(\"type\", [\n SessionStartedEventSchema,\n SessionEndedEventSchema,\n SessionStatusChangedEventSchema,\n ApprovalRequestedEventSchema,\n ApprovalApprovedEventSchema,\n ApprovalRejectedEventSchema,\n ApprovalExpiredEventSchema,\n CommandExecutedEventSchema,\n GitSnapshotEventSchema,\n FileChangedEventSchema,\n DecisionRecordedEventSchema,\n TaskCreatedEventSchema,\n TaskStatusChangedEventSchema,\n TaskReconciledEventSchema,\n TaskLinkageRefreshedEventSchema,\n TaskDeletedEventSchema,\n TaskArchivedEventSchema,\n NoteAddedEventSchema,\n AdapterOutputEventSchema,\n]);\n\n/** Inferred runtime type for any Basou event. */\nexport type Event = z.infer<typeof EventSchema>;\n\n/** Narrowed runtime type for the `session_started` event variant. */\nexport type SessionStartedEvent = z.infer<typeof SessionStartedEventSchema>;\n/** Narrowed runtime type for the `session_ended` event variant. */\nexport type SessionEndedEvent = z.infer<typeof SessionEndedEventSchema>;\n/** Narrowed runtime type for the `session_status_changed` event variant. */\nexport type SessionStatusChangedEvent = z.infer<typeof SessionStatusChangedEventSchema>;\n/** Narrowed runtime type for the `approval_requested` event variant. */\nexport type ApprovalRequestedEvent = z.infer<typeof ApprovalRequestedEventSchema>;\n/** Narrowed runtime type for the `approval_approved` event variant. */\nexport type ApprovalApprovedEvent = z.infer<typeof ApprovalApprovedEventSchema>;\n/** Narrowed runtime type for the `approval_rejected` event variant. */\nexport type ApprovalRejectedEvent = z.infer<typeof ApprovalRejectedEventSchema>;\n/** Narrowed runtime type for the `approval_expired` event variant. */\nexport type ApprovalExpiredEvent = z.infer<typeof ApprovalExpiredEventSchema>;\n/** Narrowed runtime type for the `command_executed` event variant. */\nexport type CommandExecutedEvent = z.infer<typeof CommandExecutedEventSchema>;\n/** Narrowed runtime type for the `git_snapshot` event variant. */\nexport type GitSnapshotEvent = z.infer<typeof GitSnapshotEventSchema>;\n/** Narrowed runtime type for the `file_changed` event variant. */\nexport type FileChangedEvent = z.infer<typeof FileChangedEventSchema>;\n/** Narrowed runtime type for the `decision_recorded` event variant. */\nexport type DecisionRecordedEvent = z.infer<typeof DecisionRecordedEventSchema>;\n/** Narrowed runtime type for the `task_created` event variant. */\nexport type TaskCreatedEvent = z.infer<typeof TaskCreatedEventSchema>;\n/** Narrowed runtime type for the `task_status_changed` event variant. */\nexport type TaskStatusChangedEvent = z.infer<typeof TaskStatusChangedEventSchema>;\n/** Narrowed runtime type for the `task_reconciled` event variant (.strict()). */\nexport type TaskReconciledEvent = z.infer<typeof TaskReconciledEventSchema>;\n/** Narrowed runtime type for the `task_linkage_refreshed` event variant (.strict()). */\nexport type TaskLinkageRefreshedEvent = z.infer<typeof TaskLinkageRefreshedEventSchema>;\n/** Narrowed runtime type for the `task_deleted` event variant (.strict()). */\nexport type TaskDeletedEvent = z.infer<typeof TaskDeletedEventSchema>;\n/** Narrowed runtime type for the `task_archived` event variant (.strict()). */\nexport type TaskArchivedEvent = z.infer<typeof TaskArchivedEventSchema>;\n/** Narrowed runtime type for the `note_added` event variant. */\nexport type NoteAddedEvent = z.infer<typeof NoteAddedEventSchema>;\n/** Narrowed runtime type for the `adapter_output` event variant (.strict()). */\nexport type AdapterOutputEvent = z.infer<typeof AdapterOutputEventSchema>;\n","import { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { type ReplayWarning, replayEvents } from \"../events/event-replay.js\";\nimport { findErrorCode } from \"../lib/error-codes.js\";\nimport { type Session, SessionSchema } from \"../schemas/session.schema.js\";\nimport type { BasouPaths } from \"./basou-dir.js\";\nimport { readYamlFile } from \"./yaml-store.js\";\n\n/**\n * Threshold above which a still-`running` session with no `session_ended`\n * event is flagged suspect.\n *\n * 24h: long enough that an active long-running session will not be flagged,\n * short enough that an abandoned process is surfaced within a working day.\n * Tunable via CLI option in a later step (continuation backlog #23).\n */\nexport const STUCK_THRESHOLD_MS = 24 * 60 * 60 * 1000;\n\nexport type SuspectReason = \"events_say_ended_but_yaml_running\" | \"running_no_end_event\";\n\nexport type SessionEntry = {\n sessionId: string;\n session: Session;\n suspect: boolean;\n suspectReason: SuspectReason | null;\n};\n\n/**\n * Per-session degradation reason emitted by {@link loadSessionEntries.onSkip}.\n *\n * - `session_yaml_missing` (ENOENT) and `session_yaml_invalid` (parse or schema\n * failure) both omit the entry from the result.\n * - `events_jsonl_unreadable` still pushes the entry with `suspect=false` so\n * the session row remains visible to the caller; only the suspect check is\n * degraded. Matches the existing CLI behaviour at\n * `packages/cli/src/commands/session.ts` (suspect-check stderr warning).\n */\nexport type SessionSkipReason =\n | \"session_yaml_missing\"\n | \"session_yaml_invalid\"\n | \"events_jsonl_unreadable\";\n\nexport type LoadSessionEntriesOptions = {\n /**\n * Single `now` shared across every {@link classifySuspect} call so that\n * sessions classified back-to-back observe the same instant. Avoids\n * boundary races where a session at age ≈ 24h would flip between calls.\n */\n now: Date;\n onWarning?: (warning: ReplayWarning, sessionId: string) => void;\n onSkip?: (sessionId: string, reason: SessionSkipReason) => void;\n};\n\n/**\n * List session directory names under `paths.sessions`, ULID ascending.\n *\n * - Returns `[]` when the sessions directory does not exist (empty workspace\n * or pre-init state).\n * - Throws `Error(\"Failed to enumerate sessions\", { cause })` on other I/O.\n * - Only directories are returned (`.gitkeep` and other files are filtered).\n *\n * Sort order is `Array.prototype.sort()` default (Unicode code-point\n * compare). ULIDs are Crockford base32 in uppercase, so the natural sort\n * is also chronological session-start order.\n */\nexport async function enumerateSessionDirs(paths: BasouPaths): Promise<string[]> {\n try {\n const dirents = await readdir(paths.sessions, { withFileTypes: true });\n return dirents\n .filter((d) => d.isDirectory())\n .map((d) => d.name)\n .sort();\n } catch (error: unknown) {\n if (findErrorCode(error, \"ENOENT\")) return [];\n throw new Error(\"Failed to enumerate sessions\", { cause: error });\n }\n}\n\n/**\n * Read and validate `<paths.sessions>/<sessionId>/session.yaml`.\n *\n * - Re-throws the yaml-store fixed-message `\"YAML file not found\"` for\n * ENOENT so the caller can branch on it.\n * - Throws `Error(\"Failed to read session.yaml\", { cause })` for parse\n * failures and schema violations (cause is either the YAML parser error\n * or the zod error).\n */\nexport async function readSessionYaml(paths: BasouPaths, sessionId: string): Promise<Session> {\n const filePath = join(paths.sessions, sessionId, \"session.yaml\");\n let raw: unknown;\n try {\n raw = await readYamlFile(filePath);\n } catch (error: unknown) {\n if (error instanceof Error && error.message === \"YAML file not found\") throw error;\n throw new Error(\"Failed to read session.yaml\", { cause: error });\n }\n const result = SessionSchema.safeParse(raw);\n if (!result.success) {\n throw new Error(\"Failed to read session.yaml\", { cause: result.error });\n }\n return result.data;\n}\n\n/**\n * Classify a `running` session as suspect using one of two rules:\n *\n * - Rule A (`events_say_ended_but_yaml_running`): events.jsonl contains a\n * `session_ended` event but the session.yaml is still `running`. The\n * session ended cleanly in the event log but the YAML write was lost or\n * never reached.\n * - Rule B (`running_no_end_event`): no `session_ended` event and the last\n * event is older than {@link STUCK_THRESHOLD_MS}. The process likely\n * crashed or was killed.\n *\n * Sessions that are not `running` are never suspect.\n *\n * I/O failure on events.jsonl is re-thrown unwrapped so the caller can\n * degrade with a warning instead of treating the session as healthy. The\n * caller is also responsible for surfacing replay warnings via `onWarning`.\n */\nexport async function classifySuspect(\n paths: BasouPaths,\n sessionId: string,\n session: Session,\n now: Date,\n onWarning?: (warning: ReplayWarning) => void,\n): Promise<{ suspect: boolean; suspectReason: SuspectReason | null }> {\n if (session.session.status !== \"running\") {\n return { suspect: false, suspectReason: null };\n }\n const sessionDir = join(paths.sessions, sessionId);\n let endedFound = false;\n let lastEventOccurredAt: string | null = null;\n // Forward onWarning only when supplied — `exactOptionalPropertyTypes`\n // rejects passing a literal `undefined` for an optional property.\n const replayOpts = onWarning !== undefined ? { onWarning } : {};\n for await (const ev of replayEvents(sessionDir, replayOpts)) {\n lastEventOccurredAt = ev.occurred_at;\n if (ev.type === \"session_ended\") endedFound = true;\n }\n if (endedFound) {\n return { suspect: true, suspectReason: \"events_say_ended_but_yaml_running\" };\n }\n if (lastEventOccurredAt !== null) {\n const ageMs = now.getTime() - Date.parse(lastEventOccurredAt);\n if (Number.isFinite(ageMs) && ageMs > STUCK_THRESHOLD_MS) {\n return { suspect: true, suspectReason: \"running_no_end_event\" };\n }\n }\n return { suspect: false, suspectReason: null };\n}\n\n/**\n * High-level helper that enumerates session dirs, reads each `session.yaml`,\n * and classifies suspect for `running` sessions in one pass.\n *\n * Per-session degradations are surfaced via `options.onSkip`:\n * - `session_yaml_missing` (ENOENT) and `session_yaml_invalid` (parse or\n * schema violation): the entry is omitted from the result.\n * - `events_jsonl_unreadable`: the entry is still pushed with `suspect=false`\n * so callers can render the session row plus a CLI-side warning.\n *\n * `options.now` is taken once and threaded into every {@link classifySuspect}\n * call so age comparisons are consistent across sessions.\n */\nexport async function loadSessionEntries(\n paths: BasouPaths,\n options: LoadSessionEntriesOptions,\n): Promise<SessionEntry[]> {\n const sessionIds = await enumerateSessionDirs(paths);\n const entries: SessionEntry[] = [];\n for (const sid of sessionIds) {\n let session: Session;\n try {\n session = await readSessionYaml(paths, sid);\n } catch (error: unknown) {\n if (error instanceof Error && error.message === \"YAML file not found\") {\n options.onSkip?.(sid, \"session_yaml_missing\");\n } else {\n options.onSkip?.(sid, \"session_yaml_invalid\");\n }\n continue;\n }\n let suspect = false;\n let suspectReason: SuspectReason | null = null;\n try {\n const r = await classifySuspect(paths, sid, session, options.now, (w) =>\n options.onWarning?.(w, sid),\n );\n suspect = r.suspect;\n suspectReason = r.suspectReason;\n } catch {\n // events.jsonl I/O failure (EACCES etc.) on the suspect check is\n // unrecoverable for the classification but should not drop the session\n // entry. Surface a dedicated reason so the caller can distinguish a\n // broken events.jsonl from a broken session.yaml.\n options.onSkip?.(sid, \"events_jsonl_unreadable\");\n }\n entries.push({ sessionId: sid, session, suspect, suspectReason });\n }\n return entries;\n}\n","import { z } from \"zod\";\nimport {\n IsoTimestampSchema,\n SchemaVersionSchema,\n SessionIdSchema,\n TaskIdSchema,\n WorkspaceIdSchema,\n} from \"./shared.schema.js\";\n\n/** Session lifecycle states. */\nexport const SessionStatusSchema = z.enum([\n \"initialized\",\n \"running\",\n \"waiting_approval\",\n \"completed\",\n \"failed\",\n \"interrupted\",\n \"imported\",\n \"archived\",\n]);\n/** Inferred runtime type for {@link SessionStatusSchema}. */\nexport type SessionStatus = z.infer<typeof SessionStatusSchema>;\n\n/** Source kind that produced the session. */\nexport const SessionSourceKindSchema = z.enum([\n \"claude-code-adapter\",\n \"human\",\n \"import\",\n \"terminal\",\n]);\n/** Inferred runtime type for {@link SessionSourceKindSchema}. */\nexport type SessionSourceKind = z.infer<typeof SessionSourceKindSchema>;\n\nconst SessionSourceSchema = z.object({\n kind: SessionSourceKindSchema,\n version: z.literal(\"0.1.0\"),\n});\n\nconst InvocationSchema = z.object({\n command: z.string().min(1),\n args: z.array(z.string()).default([]),\n // Nullable to record signal-terminated runs where the child has no exit\n // code; the same nullability is mirrored in CommandExecutedEventSchema.\n exit_code: z.number().int().nullable(),\n});\n\nconst SessionInnerSchema = z.object({\n id: SessionIdSchema,\n label: z.string().optional(),\n task_id: TaskIdSchema.nullable().optional(),\n workspace_id: WorkspaceIdSchema,\n source: SessionSourceSchema,\n started_at: IsoTimestampSchema,\n // ended_at is optional because initialized / running sessions have no end time yet.\n ended_at: IsoTimestampSchema.optional(),\n status: SessionStatusSchema,\n working_directory: z.string().min(1),\n invocation: InvocationSchema,\n related_files: z.array(z.string()).default([]),\n events_log: z.string().default(\"events.jsonl\"),\n summary: z.string().nullable().optional(),\n});\n\n/**\n * Schema for `.basou/sessions/<session_id>/session.yaml`. The minimal\n * session document carries the actual fields nested under the outer\n * `session:` key.\n */\nexport const SessionSchema = z.object({\n schema_version: SchemaVersionSchema,\n session: SessionInnerSchema,\n});\n\n/** Inferred runtime type for {@link SessionSchema}. */\nexport type Session = z.infer<typeof SessionSchema>;\n","import { appendFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { type Event, EventSchema } from \"../schemas/event.schema.js\";\nimport { atomicReplace } from \"../storage/atomic.js\";\n\n/**\n * Append a single Basou event to `<sessionDir>/events.jsonl`.\n *\n * The event is validated against the discriminated union {@link EventSchema}\n * before being serialized as a single JSONL line (UTF-8, terminated by `\\n`).\n * Validation enforces the per-variant contract (required fields, source\n * vocabulary, strict variants such as `adapter_output`).\n *\n * Atomicity: writes go through `appendFile` which uses `O_APPEND`. Lines up\n * to `PIPE_BUF` bytes (Linux 4096 / macOS 512) are written atomically by the\n * kernel; longer lines may interleave with concurrent writers and are not\n * recovered here. v0.1 assumes a single writer per session, so partial-line\n * recovery is delegated to the read side (event replay) when introduced.\n *\n * Throws if validation fails or the underlying append errors. The thrown\n * Error message is pathless; the original error is attached as `cause`.\n *\n * @param sessionDir absolute path to `.basou/sessions/<session_id>/`\n * @param event unknown payload to validate and append\n */\nexport async function appendEvent(sessionDir: string, event: unknown): Promise<void> {\n let validated: ReturnType<typeof EventSchema.parse>;\n try {\n validated = EventSchema.parse(event);\n } catch (error: unknown) {\n throw new Error(\"Invalid Basou event payload\", { cause: error });\n }\n const line = `${JSON.stringify(validated)}\\n`;\n try {\n await appendFile(join(sessionDir, \"events.jsonl\"), line, \"utf8\");\n } catch (error: unknown) {\n throw new Error(\"Failed to append event to events.jsonl\", { cause: error });\n }\n}\n\n/**\n * Write `events.jsonl` in one atomic tmp+rename pass via {@link atomicReplace},\n * validating every event against {@link EventSchema} before any disk I/O so\n * a payload that fails validation never leaves a partial file behind.\n *\n * The helper is used by the round-trip importer (`session-import.ts`) and the\n * ad-hoc session orchestrator (`ad-hoc-session.ts`) where a small, fixed batch\n * of events must land together or not at all. Zero events produces a\n * zero-byte file so the session_yaml `events_log` pointer remains valid.\n *\n * Throws `\"Invalid Basou event payload\"` (same fixed message as\n * {@link appendEvent}) on validation failure, or `\"Failed to write\n * events.jsonl\"` on a disk I/O failure. The original native error is attached\n * as `cause`.\n */\nexport async function writeEventsBulk(sessionDir: string, events: Event[]): Promise<void> {\n const validated: Event[] = [];\n try {\n for (const event of events) {\n validated.push(EventSchema.parse(event));\n }\n } catch (error: unknown) {\n throw new Error(\"Invalid Basou event payload\", { cause: error });\n }\n const filePath = join(sessionDir, \"events.jsonl\");\n const body =\n validated.length > 0 ? `${validated.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n` : \"\";\n try {\n await atomicReplace(filePath, body);\n } catch (error: unknown) {\n throw new Error(\"Failed to write events.jsonl\", { cause: error });\n }\n}\n","import { type SimpleGit, simpleGit } from \"simple-git\";\nimport type { GitSnapshotEvent } from \"../schemas/event.schema.js\";\nimport { findErrorCode } from \"../storage/status.js\";\n\n/**\n * Build a {@link SimpleGit} instance bound to `repoRoot`. Production callers\n * use this single helper so any future tightening (additional safety opts,\n * environment scrubbing, ...) lands in one place. Test fixtures that need\n * `unsafe.allowUnsafeConfigPaths` for isolated `GIT_CONFIG_*` paths build\n * their own SimpleGit locally and intentionally bypass this helper.\n */\nexport function safeSimpleGit(repoRoot: string): SimpleGit {\n return simpleGit({ baseDir: repoRoot });\n}\n\n/**\n * Detect \"git executable not found\" across error wrappers used by simple-git.\n * simple-git surfaces spawn errors as `GitError` instances which discard the\n * original errno `code` property — only the underlying `\"spawn git ENOENT\"`\n * text survives in the message string. We therefore check both the errno\n * `code` (via {@link findErrorCode}) and the message chain.\n */\nexport function isGitNotFound(error: unknown): boolean {\n if (findErrorCode(error, \"ENOENT\")) return true;\n let cur: unknown = error;\n for (let i = 0; i < 4 && cur instanceof Error; i++) {\n if (/\\bENOENT\\b/.test(cur.message)) return true;\n cur = (cur as Error).cause;\n }\n return false;\n}\n\n/**\n * Payload subset of `git_snapshot` event, mechanically derived from the\n * zod-inferred event type. The wrapping event-shape fields\n * (schema_version, id, session_id, occurred_at, source, type) are added by\n * the caller (session lifecycle in later steps) when constructing the\n * event, so the schema remains the single source of truth.\n *\n * `ahead` / `behind` are omitted when there is no remote or no upstream\n * tracking; the schema declares both as optional non-negative integers.\n */\nexport type GitSnapshot = Omit<\n GitSnapshotEvent,\n \"schema_version\" | \"id\" | \"session_id\" | \"occurred_at\" | \"source\" | \"type\"\n>;\n\n/**\n * Resolve the absolute path of the Git repository root that contains `cwd`.\n * Equivalent to `git rev-parse --show-toplevel`.\n *\n * Throws `Error(\"Git executable not found in PATH. Install git first.\")`\n * with the spawn error attached as `cause` when git itself is missing.\n * Throws `Error(\"Not a git repository\")` (without command-specific suffix)\n * when `cwd` is not inside a repository — callers MAY wrap with their own\n * \"Run 'git init' first, then re-run 'basou XXX'.\" suffix.\n *\n * Pathless contract: the thrown message never embeds `cwd` or any absolute\n * path; native errors are kept on `error.cause` for verbose surfacing.\n */\nexport async function resolveRepositoryRoot(cwd: string): Promise<string> {\n const git = safeSimpleGit(cwd);\n try {\n const root = (await git.revparse([\"--show-toplevel\"])).trimEnd();\n if (root.length === 0) {\n throw new Error(\"Not a git repository\");\n }\n return root;\n } catch (error: unknown) {\n if (isGitNotFound(error)) {\n throw new Error(\"Git executable not found in PATH. Install git first.\", { cause: error });\n }\n if (error instanceof Error && error.message === \"Not a git repository\") {\n throw error;\n }\n throw new Error(\"Not a git repository\", { cause: error });\n }\n}\n\n/**\n * Read `remote.origin.url` from the local repository config. Returns\n * `undefined` if the remote is unset, the value is empty, or the lookup\n * fails for any reason (best-effort).\n *\n * The `--local` scope is critical: callers MUST NOT pick up the developer's\n * global remote.origin.url, which could leak the wrong repository URL into\n * `manifest.yaml`.\n */\nexport async function tryRemoteUrl(repositoryRoot: string): Promise<string | undefined> {\n const git = safeSimpleGit(repositoryRoot);\n try {\n const result = await git.getConfig(\"remote.origin.url\", \"local\");\n const url = (result.value ?? \"\").trimEnd();\n return url.length > 0 ? url : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Build a {@link GitSnapshot} for the repository at `repositoryRoot`. The\n * caller is responsible for ensuring `repositoryRoot` is the canonical root\n * (typically obtained via {@link resolveRepositoryRoot}); this function\n * verifies repo membership via `git rev-parse --is-inside-work-tree` to\n * distinguish a non-git directory from an empty repository.\n *\n * Edge cases:\n * - **non-git directory**: throws `Error(\"Not a git repository\")`\n * - **empty repo (no commits)**: throws `Error(\"No commits in repository\")`\n * - **detached HEAD**: `branch = \"HEAD\"`, `head = commit hash`,\n * `ahead`/`behind` omitted\n * - **no remote / no upstream tracking**: `ahead`/`behind` omitted\n *\n * Pathless contract preserved on every throw path.\n */\nexport async function getSnapshot(repositoryRoot: string): Promise<GitSnapshot> {\n const git = safeSimpleGit(repositoryRoot);\n\n let inside: boolean;\n try {\n inside = await git.checkIsRepo();\n } catch (error: unknown) {\n if (isGitNotFound(error)) {\n throw new Error(\"Git executable not found in PATH. Install git first.\", { cause: error });\n }\n throw new Error(\"Failed to read git state\", { cause: error });\n }\n if (!inside) {\n throw new Error(\"Not a git repository\");\n }\n\n let head: string;\n try {\n head = (await git.revparse([\"HEAD\"])).trimEnd();\n } catch (error: unknown) {\n if (isGitNotFound(error)) {\n throw new Error(\"Git executable not found in PATH. Install git first.\", { cause: error });\n }\n throw new Error(\"No commits in repository\", { cause: error });\n }\n if (head.length === 0) {\n throw new Error(\"No commits in repository\");\n }\n\n let branch: string;\n try {\n const raw = (await git.raw([\"branch\", \"--show-current\"])).trimEnd();\n branch = raw.length > 0 ? raw : \"HEAD\";\n } catch (error: unknown) {\n throw new Error(\"Failed to read git state\", { cause: error });\n }\n\n let dirty: boolean;\n const staged: string[] = [];\n const unstaged: string[] = [];\n const untracked: string[] = [];\n try {\n const status = await git.status();\n dirty = !status.isClean();\n // Walk status.files so deleted / renamed / conflicted entries are\n // classified correctly (StatusResult's top-level `staged` / `modified`\n // / `not_added` arrays exclude D / R / U entries).\n for (const f of status.files) {\n if (f.index === \"?\" && f.working_dir === \"?\") {\n untracked.push(f.path);\n continue;\n }\n if (f.index !== \" \" && f.index !== \"?\") staged.push(f.path);\n if (f.working_dir !== \" \" && f.working_dir !== \"?\") unstaged.push(f.path);\n }\n } catch (error: unknown) {\n throw new Error(\"Failed to read git state\", { cause: error });\n }\n\n let ahead: number | undefined;\n let behind: number | undefined;\n if (branch !== \"HEAD\") {\n try {\n const upstream = `${branch}@{upstream}`;\n const counts = (\n await git.raw([\"rev-list\", \"--left-right\", \"--count\", `${upstream}...HEAD`])\n ).trim();\n const [behindStr, aheadStr] = counts.split(/\\s+/);\n const parsedBehind = Number.parseInt(behindStr ?? \"\", 10);\n const parsedAhead = Number.parseInt(aheadStr ?? \"\", 10);\n if (Number.isFinite(parsedBehind) && parsedBehind >= 0) behind = parsedBehind;\n if (Number.isFinite(parsedAhead) && parsedAhead >= 0) ahead = parsedAhead;\n } catch {\n // No upstream tracking: leave both undefined; the schema allows omission.\n }\n }\n\n const snapshot: GitSnapshot = {\n head,\n branch,\n dirty,\n staged,\n unstaged,\n untracked,\n ...(ahead !== undefined ? { ahead } : {}),\n ...(behind !== undefined ? { behind } : {}),\n };\n return snapshot;\n}\n","// Namespace import keeps lstat / readFile behind a single binding for the\n// read-side guards. The EACCES test exercises this module via real fs +\n// chmod on the parent directory rather than vi.spyOn, because vi.spyOn\n// cannot redefine ESM module exports under vitest 2.x.\nimport * as fsp from \"node:fs/promises\";\nimport type { Manifest } from \"../schemas/manifest.schema.js\";\nimport { StatusSchema, type StatusSnapshot } from \"../schemas/status.schema.js\";\nimport { atomicReplace } from \"./atomic.js\";\nimport type { BasouPaths } from \"./basou-dir.js\";\n\n/**\n * @internal Compile-time exhaustiveness via Record: every key of\n * `StatusSnapshot[\"directories_present\"]` MUST have a paths accessor here,\n * otherwise the file fails to typecheck. Run-time exhaustiveness is\n * verified by status.test.ts (key-set equality with\n * `StatusSchema.shape.directories_present.shape`). Exported only so the\n * test can perform that equality check; not part of the public API.\n */\nexport const DIRECTORY_CHECKS: Record<\n keyof StatusSnapshot[\"directories_present\"],\n (p: BasouPaths) => string\n> = {\n sessions: (p) => p.sessions,\n tasks: (p) => p.tasks,\n approvals_pending: (p) => p.approvals.pending,\n approvals_resolved: (p) => p.approvals.resolved,\n logs: (p) => p.logs,\n raw: (p) => p.raw,\n tmp: (p) => p.tmp,\n};\n\n/**\n * Refuse to operate on `.basou` if it is a symlink or not a directory. This\n * prevents `writeStatus` from being tricked into writing `status.json`\n * outside the repository root via a swapped `.basou` symlink. Mirrors\n * `ensureBasouDirectory`'s lstat-based guard.\n *\n * If `.basou` is absent the underlying ENOENT is propagated (wrapped) so\n * callers can map it to \"workspace not initialized\" via `findErrorCode`.\n *\n * Note: this is a baseline safety net, not a TOCTOU fix — the directory\n * could still be replaced between this check and the subsequent write. The\n * goal is to detect already-swapped symlinks, not to race-proof the\n * filesystem.\n */\nexport async function assertBasouRootSafe(rootPath: string): Promise<void> {\n let stat: Awaited<ReturnType<typeof fsp.lstat>>;\n try {\n stat = await fsp.lstat(rootPath);\n } catch (error: unknown) {\n if (hasErrorCode(error) && error.code === \"ENOENT\") {\n throw new Error(\"Basou workspace not found\", { cause: error });\n }\n throw new Error(\"Failed to inspect .basou root\", { cause: error });\n }\n if (stat.isSymbolicLink()) {\n throw new Error(\".basou root is a symlink; refusing to operate\");\n }\n if (!stat.isDirectory()) {\n throw new Error(\".basou root exists but is not a directory\");\n }\n}\n\n/**\n * Probe whether `path` is a directory using `lstat` (without following\n * symlinks, so a symlink-to-directory is reported as `false`).\n *\n * Only ENOENT and ENOTDIR are mapped to `false`; permission-style errors\n * (EACCES, EPERM, ...) are re-thrown so a misleading \"not present\" answer\n * is never written into status.json. This keeps the snapshot honest about\n * what was actually observed.\n */\nasync function dirPresent(path: string): Promise<boolean> {\n try {\n return (await fsp.lstat(path)).isDirectory();\n } catch (error: unknown) {\n if (hasErrorCode(error) && (error.code === \"ENOENT\" || error.code === \"ENOTDIR\")) {\n return false;\n }\n throw new Error(\"Failed to inspect .basou subdirectory\", { cause: error });\n }\n}\n\n/**\n * Build a StatusSnapshot from a manifest plus the path layout, observing\n * each subdirectory's presence via `lstat`. Read-only with respect to the\n * workspace state; writes nothing. The result is re-validated by\n * `StatusSchema.parse` before being returned.\n *\n * @param input.now Override for testing; defaults to `new Date()`.\n */\nexport async function buildStatusSnapshot(input: {\n manifest: Manifest;\n paths: BasouPaths;\n now?: Date;\n}): Promise<StatusSnapshot> {\n const { manifest, paths } = input;\n const generatedAt = (input.now ?? new Date()).toISOString();\n\n const entries = Object.entries(DIRECTORY_CHECKS) as Array<\n [keyof StatusSnapshot[\"directories_present\"], (p: BasouPaths) => string]\n >;\n const presence = await Promise.all(\n entries.map(async ([key, get]) => [key, await dirPresent(get(paths))] as const),\n );\n const directoriesEntries = Object.fromEntries(presence) as StatusSnapshot[\"directories_present\"];\n\n const snapshot: StatusSnapshot = {\n schema_version: \"0.1.0\",\n generated_at: generatedAt,\n workspace: {\n id: manifest.workspace.id,\n name: manifest.workspace.name,\n basou_version: manifest.basou_version,\n },\n directories_present: directoriesEntries,\n };\n return StatusSchema.parse(snapshot);\n}\n\n/**\n * Atomically write a StatusSnapshot to `paths.files.status`.\n *\n * Re-validates via `StatusSchema.parse` before any file I/O, so an invalid\n * snapshot throws synchronously and never overwrites the existing\n * `status.json`. Delegates the tmp-file + rename pass to {@link atomicReplace}.\n *\n * **Precondition**: callers MUST invoke {@link assertBasouRootSafe} on\n * `paths.root` first to ensure `.basou` is a real directory and not a\n * swapped symlink. `writeStatus` does not redo this guard — it trusts the\n * caller — so a direct invocation without the guard could write\n * `status.json` outside the repository root.\n */\nexport async function writeStatus(paths: BasouPaths, snapshot: StatusSnapshot): Promise<void> {\n const validated = StatusSchema.parse(snapshot);\n const body = `${JSON.stringify(validated, null, 2)}\\n`;\n try {\n await atomicReplace(paths.files.status, body);\n } catch (error: unknown) {\n throw new Error(\"Failed to write status file\", { cause: error });\n }\n}\n\n/**\n * Read `.basou/status.json` for the current schema_version (0.1.0). This\n * is a cache reader only; cross-version migration is not supported here.\n * Older or newer status.json shapes will fail `StatusSchema.parse` —\n * callers regenerate by calling `buildStatusSnapshot` + `writeStatus`.\n */\nexport async function readStatus(paths: BasouPaths): Promise<StatusSnapshot> {\n let body: string;\n try {\n body = await fsp.readFile(paths.files.status, \"utf8\");\n } catch (error: unknown) {\n if (hasErrorCode(error) && error.code === \"ENOENT\") {\n throw new Error(\"Status file not found\", { cause: error });\n }\n throw new Error(\"Failed to read status file\", { cause: error });\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch (error: unknown) {\n throw new Error(\"Failed to parse status JSON\", { cause: error });\n }\n return StatusSchema.parse(parsed);\n}\n\n// Re-exported from lib so existing import paths (`./storage/status.js`,\n// `@basou/core/storage/index.js`, `@basou/core`) continue to resolve while\n// the canonical definition lives in `core/src/lib/error-codes.ts`.\nexport { findErrorCode } from \"../lib/error-codes.js\";\n\nfunction hasErrorCode(error: unknown): error is Error & { code: string } {\n if (!(error instanceof Error)) return false;\n return typeof (error as unknown as Record<string, unknown>).code === \"string\";\n}\n","import { z } from \"zod\";\nimport { IsoTimestampSchema, SchemaVersionSchema, WorkspaceIdSchema } from \"./shared.schema.js\";\n\n/**\n * Schema for `.basou/status.json` — a forward-incompat cache of the current\n * workspace state.\n *\n * Each level uses `.strict()` so unknown keys are rejected rather than\n * silently stripped. A v0.1 reader that encounters a future-shape\n * `status.json` therefore fails parsing instead of returning a partially\n * empty snapshot; callers regenerate by calling `buildStatusSnapshot` +\n * `writeStatus` rather than trying to migrate.\n */\nexport const StatusSchema = z\n .object({\n schema_version: SchemaVersionSchema,\n generated_at: IsoTimestampSchema,\n workspace: z\n .object({\n id: WorkspaceIdSchema,\n name: z.string().min(1),\n basou_version: z.literal(\"0.1.0\"),\n })\n .strict(),\n directories_present: z\n .object({\n sessions: z.boolean(),\n tasks: z.boolean(),\n approvals_pending: z.boolean(),\n approvals_resolved: z.boolean(),\n logs: z.boolean(),\n raw: z.boolean(),\n tmp: z.boolean(),\n })\n .strict(),\n })\n .strict();\n\n/** Inferred runtime type for {@link StatusSchema}. */\nexport type StatusSnapshot = z.infer<typeof StatusSchema>;\n","import type { SimpleGit } from \"simple-git\";\nimport { isGitNotFound, safeSimpleGit } from \"./snapshot.js\";\n\n/**\n * Status classification used by the `file_changed` event schema. Limited to\n * the four classes that simple-git's `git diff --name-status` reliably\n * surfaces; copy / unmerged / typechange entries are intentionally dropped\n * to keep the event payload shape narrow.\n */\nexport type FileChangeStatus = \"added\" | \"modified\" | \"deleted\" | \"renamed\";\n\n/**\n * Single file-level change observed between two refs. `old_path` is set\n * only for `renamed` entries (the previous path of the file).\n */\nexport type FileChange = {\n path: string;\n old_path?: string;\n status: FileChangeStatus;\n};\n\n/**\n * Result of {@link getDiff}. The `changed_files` array is in git's natural\n * `--name-status` order; callers requiring deterministic ordering should\n * sort by `path` themselves.\n */\nexport type DiffResult = {\n changed_files: FileChange[];\n};\n\n/**\n * Compute the file-level diff between two git refs.\n *\n * Returns a list of changed file paths classified by status (added /\n * modified / deleted / renamed). Diff content is intentionally NOT\n * returned — `file_changed` events record paths only, and raw diff bodies\n * are excluded so the trace cannot inadvertently leak source code that may\n * be sensitive. Use `git show <ref>` to obtain the underlying diff.\n *\n * Pathless contract: every thrown message is a fixed string from the set\n * {`Not a git repository`, `Git executable not found in PATH. Install git\n * first.`, `Invalid ref`, `Failed to compute git diff`}; native errors are\n * preserved on `Error.cause`.\n *\n * Special cases:\n * - `baseRef === headRef` short-circuits to an empty result\n * - copy / unmerged / typechange / unknown status codes are skipped\n *\n * @param repoRoot absolute path to the git repository root\n * @param baseRef base ref (e.g. session-start HEAD sha)\n * @param headRef head ref (e.g. session-end HEAD sha)\n */\nexport async function getDiff(\n repoRoot: string,\n baseRef: string,\n headRef: string,\n): Promise<DiffResult> {\n let git: SimpleGit;\n try {\n git = safeSimpleGit(repoRoot);\n } catch (error: unknown) {\n if (isGitNotFound(error)) {\n throw new Error(\"Git executable not found in PATH. Install git first.\", { cause: error });\n }\n throw new Error(\"Not a git repository\", { cause: error });\n }\n\n if (baseRef === headRef) return { changed_files: [] };\n\n let raw: string;\n try {\n raw = await git.raw([\"diff\", \"--name-status\", `${baseRef}..${headRef}`]);\n } catch (error: unknown) {\n if (isGitNotFound(error)) {\n throw new Error(\"Git executable not found in PATH. Install git first.\", { cause: error });\n }\n const message = error instanceof Error ? error.message : \"\";\n if (/not a git repository/i.test(message)) {\n throw new Error(\"Not a git repository\", { cause: error });\n }\n if (\n message.includes(\"bad revision\") ||\n message.includes(\"unknown revision\") ||\n message.includes(\"ambiguous argument\")\n ) {\n throw new Error(\"Invalid ref\", { cause: error });\n }\n throw new Error(\"Failed to compute git diff\", { cause: error });\n }\n\n return { changed_files: parseDiffNameStatus(raw) };\n}\n\nfunction parseDiffNameStatus(raw: string): FileChange[] {\n const lines = raw.split(\"\\n\").filter((l) => l.trim() !== \"\");\n const changes: FileChange[] = [];\n for (const line of lines) {\n const parts = line.split(\"\\t\");\n const code = parts[0];\n if (code === undefined || code.length === 0) continue;\n if (code.startsWith(\"R\") && parts.length >= 3) {\n const newPath = parts[2];\n const oldPath = parts[1];\n if (newPath === undefined) continue;\n changes.push({\n path: newPath,\n status: \"renamed\",\n ...(oldPath !== undefined ? { old_path: oldPath } : {}),\n });\n } else if (code === \"A\" && parts[1]) {\n changes.push({ path: parts[1], status: \"added\" });\n } else if (code === \"M\" && parts[1]) {\n changes.push({ path: parts[1], status: \"modified\" });\n } else if (code === \"D\" && parts[1]) {\n changes.push({ path: parts[1], status: \"deleted\" });\n }\n // C / U / T / X (copy / unmerged / typechange / unknown) are skipped:\n // the file_changed status enum does not cover them in v0.1.\n }\n return changes;\n}\n","import { join } from \"node:path\";\nimport { enumerateApprovals } from \"../approval/approval-store.js\";\nimport { type ReplayWarning, replayEvents } from \"../events/event-replay.js\";\nimport type { BasouPaths } from \"../storage/basou-dir.js\";\nimport {\n loadSessionEntries,\n type SessionEntry,\n type SessionSkipReason,\n type SuspectReason,\n} from \"../storage/sessions.js\";\nimport { loadTaskEntries, type TaskDocument, type TaskSkipReason } from \"../storage/tasks.js\";\n\n/** Input contract for {@link renderHandoff}. */\nexport type HandoffRendererInput = {\n paths: BasouPaths;\n /** ISO timestamp embedded in the generated body header. Caller-provided for testability. */\n nowIso: string;\n /** Forwarded to {@link replayEvents} / {@link loadSessionEntries} per session. */\n onWarning?: (warning: ReplayWarning, sessionId: string) => void;\n /**\n * Per-session degradation reasons (missing/invalid session.yaml or\n * unreadable events.jsonl). The CLI maps `events_jsonl_unreadable` to the\n * existing suspect-check stderr wording to keep the user-facing surface\n * consistent with `basou session list`.\n */\n onSessionSkip?: (sessionId: string, reason: SessionSkipReason) => void;\n /**\n * Per-task degradation reasons (invalid front matter / unreadable file).\n * Surfaced so the CLI can warn the operator about a malformed task.md\n * without aborting the handoff render.\n */\n onTaskSkip?: (taskId: string, reason: TaskSkipReason) => void;\n /** Maximum related_files entries to display before `... +N more`. Default 20. */\n relatedFilesLimit?: number;\n};\n\nexport type HandoffRendererResult = {\n /** Generated body WITHOUT BASOU:GENERATED markers (markdown-store wraps them). */\n body: string;\n sessionCount: number;\n decisionCount: number;\n pendingApprovalsCount: number;\n suspectCount: number;\n /** Total number of task.md files successfully loaded. */\n taskCount: number;\n /** Tasks whose status is `planned` or `in_progress` (= shown in 次に実行すべき作業). */\n pendingTaskCount: number;\n};\n\ntype DecisionRecord = {\n decisionId: string;\n title: string;\n occurredAt: string;\n sessionId: string;\n};\n\ntype TaskCreatedRecord = {\n taskId: string;\n title: string;\n occurredAt: string;\n sessionId: string;\n};\n\ntype TaskStatusChangedRecord = {\n taskId: string;\n occurredAt: string;\n sessionId: string;\n};\n\n/**\n * Render the body of `handoff.md` from the current workspace state.\n *\n * The renderer is a pure function (no I/O beyond {@link replayEvents} /\n * {@link loadSessionEntries} / {@link enumerateApprovals}). It assembles the\n * the spec's `handoff.md` sections in order:\n *\n * 1. `現在の状態`: latest live session (status not archived, source not import).\n * 2. `直近の変更ファイル`: union of `related_files` across sessions, dedup +\n * sorted asc + truncated to `relatedFilesLimit` (default 20).\n * 3. `直近の判断`: latest `decision_recorded` event (chronological).\n * 4. `未決事項`: pending-approval count + suspect-session count.\n * 5. `次に読むべきファイル`: `.basou/decisions.md` + top-3 related files\n * (the same `displayedFiles` source is intentionally reused in two\n * sections — overview vs. resume context).\n * 6. `次に実行すべき作業`: placeholder until task events land.\n * 7. `セッション一覧`: all sessions newest first with inline suspect labels.\n *\n * Session enumeration goes through {@link loadSessionEntries} so the set of\n * sessions whose `decision_recorded` events we replay matches the\n * decisions renderer.\n */\nexport async function renderHandoff(input: HandoffRendererInput): Promise<HandoffRendererResult> {\n const limit = input.relatedFilesLimit ?? 20;\n const now = new Date(input.nowIso);\n // Wrap the caller's onSkip so we can detect whether loadSessionEntries'\n // suspect pass already emitted `events_jsonl_unreadable` for a session\n // For non-running sessions the suspect pass does not\n // touch events.jsonl, so the second replay below may be the first to\n // hit the unreadable file — without this bookkeeping that error would\n // be silently swallowed.\n const unreadableEmitted = new Set<string>();\n const wrappedSkip: (sid: string, reason: SessionSkipReason) => void = (sid, reason) => {\n if (reason === \"events_jsonl_unreadable\") unreadableEmitted.add(sid);\n input.onSessionSkip?.(sid, reason);\n };\n // `exactOptionalPropertyTypes` forbids passing literal `undefined` for an\n // optional property, so build the options object conditionally.\n const loadOpts: Parameters<typeof loadSessionEntries>[1] = { now, onSkip: wrappedSkip };\n if (input.onWarning !== undefined) loadOpts.onWarning = input.onWarning;\n const entries = await loadSessionEntries(input.paths, loadOpts);\n\n const decisions: DecisionRecord[] = [];\n const tasksCreated: TaskCreatedRecord[] = [];\n const tasksStatusChanged: TaskStatusChangedRecord[] = [];\n for (const entry of entries) {\n const sessionDir = join(input.paths.sessions, entry.sessionId);\n try {\n for await (const ev of replayEvents(sessionDir, {\n onWarning: (w) => input.onWarning?.(w, entry.sessionId),\n })) {\n if (ev.type === \"decision_recorded\") {\n decisions.push({\n decisionId: ev.decision_id,\n title: ev.title,\n occurredAt: ev.occurred_at,\n sessionId: entry.sessionId,\n });\n } else if (ev.type === \"task_created\") {\n tasksCreated.push({\n taskId: ev.task_id,\n title: ev.title,\n occurredAt: ev.occurred_at,\n sessionId: entry.sessionId,\n });\n } else if (ev.type === \"task_status_changed\") {\n tasksStatusChanged.push({\n taskId: ev.task_id,\n occurredAt: ev.occurred_at,\n sessionId: entry.sessionId,\n });\n }\n }\n } catch {\n // events.jsonl unreadable on the decision-aggregation pass. If the\n // suspect pass has not already surfaced a warning for this session\n // (e.g. completed session, where classifySuspect short-circuits\n // before reading events.jsonl), emit the skip now so the operator\n // is not left wondering why a decision is missing.\n if (!unreadableEmitted.has(entry.sessionId)) {\n wrappedSkip(entry.sessionId, \"events_jsonl_unreadable\");\n }\n }\n }\n decisions.sort((a, b) => {\n const c = Date.parse(a.occurredAt) - Date.parse(b.occurredAt);\n return c !== 0 ? c : a.decisionId.localeCompare(b.decisionId);\n });\n tasksCreated.sort((a, b) => {\n const c = Date.parse(a.occurredAt) - Date.parse(b.occurredAt);\n return c !== 0 ? c : a.taskId.localeCompare(b.taskId);\n });\n tasksStatusChanged.sort((a, b) => {\n const c = Date.parse(a.occurredAt) - Date.parse(b.occurredAt);\n return c !== 0 ? c : a.taskId.localeCompare(b.taskId);\n });\n\n const taskLoadOpts: Parameters<typeof loadTaskEntries>[1] = {};\n if (input.onTaskSkip !== undefined) taskLoadOpts.onSkip = input.onTaskSkip;\n const taskEntries = await loadTaskEntries(input.paths, taskLoadOpts);\n const taskById = new Map<string, TaskDocument>();\n for (const t of taskEntries) taskById.set(t.task.task.id, t);\n\n // Latest activity = most recent task_status_changed, falling back to the\n // most recent task_created when no status change has been recorded yet.\n // This surfaces \"the task whose status most recently changed (including\n // done)\" instead of \"the most recently created task\", so a task that just\n // transitioned to done is no longer hidden from the handoff.\n const latestStatusChange = tasksStatusChanged[tasksStatusChanged.length - 1];\n const latestCreatedRecord = tasksCreated[tasksCreated.length - 1];\n const latestActivityTaskId = latestStatusChange?.taskId ?? latestCreatedRecord?.taskId;\n const latestActivityTitle =\n latestActivityTaskId !== undefined\n ? (tasksCreated.find((t) => t.taskId === latestActivityTaskId)?.title ?? \"(title unknown)\")\n : undefined;\n const latestActivityRecord =\n latestActivityTaskId !== undefined && latestActivityTitle !== undefined\n ? { taskId: latestActivityTaskId, title: latestActivityTitle }\n : undefined;\n const latestTaskDoc =\n latestActivityRecord !== undefined ? taskById.get(latestActivityRecord.taskId) : undefined;\n const pendingTasks = taskEntries.filter(\n (t) => t.task.task.status === \"planned\" || t.task.task.status === \"in_progress\",\n );\n\n const approvals = await enumerateApprovals(input.paths);\n const pendingApprovalsCount = approvals.pending.length;\n\n const liveEntries = entries.filter(\n (e) => e.session.session.status !== \"archived\" && e.session.session.source.kind !== \"import\",\n );\n const latestSession = [...liveEntries].sort(\n (a, b) => Date.parse(b.session.session.started_at) - Date.parse(a.session.session.started_at),\n )[0];\n\n // 「直近の変更ファイル」 collects related_files from live sessions only.\n // Imported sessions are historical / cross-workspace and would otherwise\n // pollute the \"what changed recently\" view with paths that don't belong\n // to the current workspace's live work.\n const allFiles = new Set<string>();\n for (const e of entries) {\n if (e.session.session.source.kind === \"import\") continue;\n for (const f of e.session.session.related_files) allFiles.add(f);\n }\n const sortedFiles = [...allFiles].sort();\n const displayedFiles = sortedFiles.slice(0, limit);\n const overflow = Math.max(0, sortedFiles.length - limit);\n\n const suspectCount = entries.filter((e) => e.suspect).length;\n\n const firstEntry = entries[0];\n const lastEntry = entries[entries.length - 1];\n const sessionRange =\n firstEntry !== undefined && lastEntry !== undefined\n ? `${firstEntry.sessionId}..${lastEntry.sessionId}`\n : \"\";\n\n const body = formatHandoffBody({\n nowIso: input.nowIso,\n sessionRange,\n sessionCount: entries.length,\n latestSession,\n decisions,\n pendingApprovalsCount,\n suspectCount,\n displayedFiles,\n overflow,\n entries,\n latestActivityRecord,\n latestTaskDoc,\n pendingTasks,\n totalTaskCount: taskEntries.length,\n });\n\n return {\n body,\n sessionCount: entries.length,\n decisionCount: decisions.length,\n pendingApprovalsCount,\n suspectCount,\n taskCount: taskEntries.length,\n pendingTaskCount: pendingTasks.length,\n };\n}\n\nfunction formatHandoffBody(args: {\n nowIso: string;\n sessionRange: string;\n sessionCount: number;\n latestSession: SessionEntry | undefined;\n decisions: ReadonlyArray<DecisionRecord>;\n pendingApprovalsCount: number;\n suspectCount: number;\n displayedFiles: ReadonlyArray<string>;\n overflow: number;\n entries: ReadonlyArray<SessionEntry>;\n latestActivityRecord: { taskId: string; title: string } | undefined;\n latestTaskDoc: TaskDocument | undefined;\n pendingTasks: ReadonlyArray<TaskDocument>;\n totalTaskCount: number;\n}): string {\n const lines: string[] = [];\n lines.push(\"# Handoff\");\n lines.push(\"\");\n if (args.sessionRange !== \"\") {\n lines.push(`> Generated at ${args.nowIso} from ${args.sessionRange}`);\n } else {\n lines.push(`> Generated at ${args.nowIso}`);\n }\n lines.push(\"\");\n\n // 現在の状態\n lines.push(\"## 現在の状態\");\n lines.push(\"\");\n if (args.latestSession !== undefined) {\n const sid = args.latestSession.sessionId;\n const status = args.latestSession.session.session.status;\n lines.push(`- 最終 session: ${sid} (${status})`);\n } else {\n lines.push(\"- 最終 session: (no live sessions)\");\n }\n if (args.latestActivityRecord !== undefined) {\n // Status comes from task.md when available. If the task_created event\n // exists but task.md is missing / invalid we MUST NOT fabricate\n // \"planned\" — events alone cannot restore the initial status and\n // operators would miss an unsafe-state reconcile.\n const statusLabel =\n args.latestTaskDoc !== undefined\n ? args.latestTaskDoc.task.task.status\n : \"status unknown — task.md missing or invalid\";\n // Surface linked_sessions cardinality as a trailing parenthetical when\n // the latest task spans more than one session. Suppressed when the\n // task is single-session (the common case) or when task.md is\n // unavailable, keeping single-session output visually quiet.\n const linkedCount = args.latestTaskDoc?.task.task.linked_sessions?.length;\n const linkedSuffix =\n linkedCount !== undefined && linkedCount > 1 ? ` (linked_sessions: ${linkedCount})` : \"\";\n lines.push(\n `- 最終 task: ${args.latestActivityRecord.taskId} (${statusLabel}): ${args.latestActivityRecord.title}${linkedSuffix}`,\n );\n } else {\n lines.push(\"- 最終 task: (no tasks recorded yet)\");\n }\n lines.push(\"\");\n\n // 直近の変更ファイル\n lines.push(\"## 直近の変更ファイル\");\n lines.push(\"\");\n if (args.displayedFiles.length === 0) {\n lines.push(\"(no related files recorded)\");\n } else {\n for (const f of args.displayedFiles) lines.push(`- ${f}`);\n if (args.overflow > 0) lines.push(`- ... +${args.overflow} more`);\n }\n lines.push(\"\");\n\n // 直近の判断\n lines.push(\"## 直近の判断\");\n lines.push(\"\");\n if (args.decisions.length === 0) {\n lines.push(\"(no decisions recorded yet)\");\n } else {\n const last = args.decisions[args.decisions.length - 1] as DecisionRecord;\n lines.push(`- ${last.decisionId}: ${last.title}`);\n lines.push(\"\");\n lines.push(`(${args.decisions.length} decisions total — see decisions.md)`);\n }\n lines.push(\"\");\n\n // 未決事項\n lines.push(\"## 未決事項\");\n lines.push(\"\");\n if (args.pendingApprovalsCount > 0) {\n lines.push(`- ${args.pendingApprovalsCount} pending approvals`);\n }\n if (args.suspectCount > 0) {\n lines.push(`- ${args.suspectCount} suspect sessions detected`);\n }\n if (args.pendingApprovalsCount === 0 && args.suspectCount === 0) {\n lines.push(\"(none)\");\n }\n lines.push(\"\");\n\n // 次に読むべきファイル\n // Drop self-reference to handoff.md, include `.basou/decisions.md` + the\n // top-3 of `displayedFiles` so the section points to concrete files. The\n // same `displayedFiles` source is reused intentionally (overview vs.\n // resume context).\n lines.push(\"## 次に読むべきファイル\");\n lines.push(\"\");\n lines.push(\"- .basou/decisions.md\");\n for (const f of args.displayedFiles.slice(0, 3)) lines.push(`- ${f}`);\n lines.push(\"\");\n\n // 次に実行すべき作業\n lines.push(\"## 次に実行すべき作業\");\n lines.push(\"\");\n if (args.pendingTasks.length === 0) {\n lines.push(\"(no pending tasks)\");\n } else {\n for (const t of args.pendingTasks) {\n lines.push(`- ${t.task.task.id} (${t.task.task.status}): ${t.task.task.title}`);\n }\n }\n lines.push(\"\");\n\n // セッション一覧 — live sessions first, then a separate\n // 「Imported sessions」 sub-section so the operator can tell at a glance\n // which sessions belong to the current workspace's live work vs. which\n // were brought in via `basou session import`. The \"(no sessions yet)\"\n // placeholder fires only when the workspace is completely empty.\n const liveTableEntries = args.entries.filter((e) => e.session.session.source.kind !== \"import\");\n const importedTableEntries = args.entries.filter(\n (e) => e.session.session.source.kind === \"import\",\n );\n lines.push(\"## セッション一覧\");\n lines.push(\"\");\n if (args.entries.length === 0) {\n lines.push(\"(no sessions yet)\");\n } else if (liveTableEntries.length === 0) {\n lines.push(\"(no live sessions; see Imported sessions below)\");\n } else {\n lines.push(\"| short_id | status | started_at | label |\");\n lines.push(\"|---|---|---|---|\");\n for (const e of [...liveTableEntries].reverse()) {\n const sid = shortHandoffId(e.sessionId);\n const status = e.session.session.status + suspectLabel(e.suspectReason);\n const startedAt = e.session.session.started_at;\n const label = e.session.session.label ?? \"\";\n lines.push(`| ${sid} | ${status} | ${startedAt} | ${label} |`);\n }\n }\n if (importedTableEntries.length > 0) {\n lines.push(\"\");\n lines.push(\"### Imported sessions\");\n lines.push(\"\");\n lines.push(\"| short_id | status | started_at | label |\");\n lines.push(\"|---|---|---|---|\");\n for (const e of [...importedTableEntries].reverse()) {\n const sid = shortHandoffId(e.sessionId);\n const status = e.session.session.status + suspectLabel(e.suspectReason);\n const startedAt = e.session.session.started_at;\n const label = e.session.session.label ?? \"\";\n lines.push(`| ${sid} | ${status} | ${startedAt} | ${label} |`);\n }\n }\n lines.push(\"\");\n // Session-status breakdown: surface completed / failed / running counts\n // alongside the total so an at-a-glance read distinguishes \"ten sessions,\n // all done\" from \"ten sessions, three still failing\". Order is fixed\n // (completed first since handoff is read after the work) and zero-count\n // statuses are omitted. When the workspace is empty the breakdown\n // parenthetical is suppressed entirely so the existing terse line stays.\n const statusCounts = new Map<string, number>();\n for (const e of args.entries) {\n const s = e.session.session.status;\n statusCounts.set(s, (statusCounts.get(s) ?? 0) + 1);\n }\n const orderedStatuses = [\n \"completed\",\n \"failed\",\n \"running\",\n \"interrupted\",\n \"waiting_approval\",\n \"initialized\",\n \"imported\",\n ] as const;\n const breakdown = orderedStatuses\n .filter((s) => (statusCounts.get(s) ?? 0) > 0)\n .map((s) => `${s} ${statusCounts.get(s)}`)\n .join(\", \");\n const sessionsLine =\n breakdown !== \"\"\n ? `Sessions: ${args.sessionCount} (${breakdown}). Tasks: ${args.totalTaskCount}.`\n : `Sessions: ${args.sessionCount}. Tasks: ${args.totalTaskCount}.`;\n lines.push(sessionsLine);\n\n return lines.join(\"\\n\");\n}\n\nfunction suspectLabel(reason: SuspectReason | null): string {\n if (reason === \"events_say_ended_but_yaml_running\") return \" ⚠ ended (yaml stale)\";\n if (reason === \"running_no_end_event\") return \" ⚠ no end event\";\n return \"\";\n}\n\n// First 10 chars after the `ses_` prefix. Matches the truncation that\n// `basou session list` uses for its shortest display column.\nfunction shortHandoffId(sessionId: string): string {\n const SES = \"ses_\";\n if (sessionId.startsWith(SES)) return sessionId.slice(SES.length, SES.length + 10);\n return sessionId.slice(0, 10);\n}\n","import { createHash } from \"node:crypto\";\nimport { mkdir, readdir, readFile, rename, stat, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { parse as parseYaml, stringify as stringifyYaml } from \"yaml\";\nimport { z } from \"zod\";\nimport type { PrefixedId } from \"../ids/ulid.js\";\nimport { findErrorCode } from \"../lib/error-codes.js\";\nimport type { Event } from \"../schemas/event.schema.js\";\nimport type { Manifest } from \"../schemas/manifest.schema.js\";\nimport type { SessionStatus } from \"../schemas/session.schema.js\";\nimport { IsoTimestampSchema, SessionIdSchema, TaskIdSchema } from \"../schemas/shared.schema.js\";\nimport {\n type Task,\n TaskSchema,\n type TaskStatus,\n TaskStatusSchema,\n} from \"../schemas/task.schema.js\";\nimport type { TaskIndexEntry } from \"../schemas/task-index.schema.js\";\nimport {\n type AttachableStatus,\n appendEventToExistingSession,\n createAdHocSessionWithEvent,\n FailedToFinalizeError,\n} from \"./ad-hoc-session.js\";\nimport { atomicCreate, atomicReplace } from \"./atomic.js\";\nimport type { BasouPaths } from \"./basou-dir.js\";\nimport { acquireLock } from \"./lockfile.js\";\nimport { enumerateSessionDirs, readSessionYaml } from \"./sessions.js\";\nimport { readTaskIndex, rebuildTaskIndex, updateTaskIndex } from \"./task-index.js\";\nimport { overwriteYamlFile } from \"./yaml-store.js\";\n\n// ============================================================================\n// File format constants\n// ============================================================================\n\nconst FRONT_MATTER_DELIM = \"---\";\n// Raised from the original 40-char cap to 80 chars so long task /\n// reconcile titles retain their core information. The same cap applies\n// to `Ad-hoc task:`, `Ad-hoc task status:`, and `Ad-hoc task reconcile:`\n// labels so the three ad-hoc label generators stay consistent with the\n// decision-side cap (cli/src/commands/decision.ts).\nconst LABEL_TITLE_MAX = 80;\nconst LABEL_TRUNCATE_HEAD = LABEL_TITLE_MAX - 3;\n\nconst DEFAULT_ATTACHABLE_STATUSES: ReadonlySet<AttachableStatus> = new Set<AttachableStatus>([\n \"initialized\",\n \"running\",\n \"waiting_approval\",\n]);\n\n// Boundary parses for direct callers so a malformed task cannot smuggle\n// past the CLI-side parsers and commit a `task_created` event. The set\n// originally rejected `done` / `cancelled` as initial values, but the\n// orchestrator now emits a follow-up `task_status_changed` for terminal\n// initial statuses so retroactively-recorded completed tasks can be\n// entered in one CLI call; widening the schema lets that path through.\nconst InitialTaskStatusSchema = TaskStatusSchema;\nconst TaskTitleSchema = z.string().min(1);\nconst TaskLabelSchema = z.string().min(1);\n// `completedAt` is an optional ISO-8601 string. Validate it at the boundary\n// so a direct (non-CLI) caller cannot smuggle a garbage timestamp past the\n// orchestrator and leave durable `task_created` / `task_status_changed`\n// events with no valid task.md to back them up.\nconst CompletedAtSchema = IsoTimestampSchema;\n\nconst TERMINAL_TASK_STATUSES: ReadonlySet<TaskStatus> = new Set<TaskStatus>([\"done\", \"cancelled\"]);\n\nfunction isTerminalTaskStatus(status: TaskStatus): boolean {\n return TERMINAL_TASK_STATUSES.has(status);\n}\n\n// ============================================================================\n// File read / parse\n// ============================================================================\n\nexport type TaskDocument = {\n /** Parsed + zod-validated front matter. */\n task: Task;\n /** Raw markdown body after the closing front matter delimiter. */\n body: string;\n};\n\n/**\n * Split a task.md file body into the YAML front matter and the trailing\n * markdown body. The expected format is:\n *\n * ---\\n\n * <yaml>\\n\n * ---\\n\n * <body>\n *\n * Strict rules:\n * - A UTF-8 BOM at the head is rejected.\n * - CRLF inside the file is normalised to LF before delimiter scanning so\n * editors that auto-convert line endings stay compatible.\n * - The closing delimiter is the FIRST `---` line after the opening one,\n * so `---` lines inside the markdown body do not confuse the parser.\n */\nfunction splitFrontMatter(raw: string): { yamlText: string; body: string } {\n if (raw.length > 0 && raw.charCodeAt(0) === 0xfeff) {\n throw new Error(\"Invalid task file format\");\n }\n const normalised = raw.replace(/\\r\\n/g, \"\\n\");\n if (!normalised.startsWith(`${FRONT_MATTER_DELIM}\\n`)) {\n throw new Error(\"Invalid task file format\");\n }\n const remainder = normalised.slice(FRONT_MATTER_DELIM.length + 1);\n // Find the first line that is exactly `---`. Scan line-by-line so a `---`\n // appearing mid-line inside YAML text is not matched.\n const lines = remainder.split(\"\\n\");\n let closingIdx = -1;\n for (let i = 0; i < lines.length; i++) {\n if (lines[i] === FRONT_MATTER_DELIM) {\n closingIdx = i;\n break;\n }\n }\n if (closingIdx < 0) {\n throw new Error(\"Invalid task file format\");\n }\n const yamlText = lines.slice(0, closingIdx).join(\"\\n\");\n // The body is everything after the closing delimiter line, with one\n // separating newline consumed (if present) so the body does not start\n // with a stray blank line.\n const afterClosing = lines.slice(closingIdx + 1);\n let body = afterClosing.join(\"\\n\");\n if (body.startsWith(\"\\n\")) body = body.slice(1);\n return { yamlText, body };\n}\n\n/**\n * Read and validate `<paths.tasks>/<taskId>.md`. Returns the parsed front\n * matter (Task) plus the markdown body string. Error contract:\n *\n * - ENOENT → throw `\"Task file not found\"`.\n * - format violation → throw `\"Invalid task file format\"`.\n * - YAML parse / schema violation → throw `\"Failed to read task file\"`.\n * - any other I/O failure → throw `\"Failed to read task file\"` with cause.\n */\nexport async function readTaskFile(paths: BasouPaths, taskId: string): Promise<TaskDocument> {\n const filePath = join(paths.tasks, `${taskId}.md`);\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf8\");\n } catch (error: unknown) {\n if (findErrorCode(error, \"ENOENT\")) {\n throw new Error(\"Task file not found\", { cause: error });\n }\n throw new Error(\"Failed to read task file\", { cause: error });\n }\n let split: { yamlText: string; body: string };\n try {\n split = splitFrontMatter(raw);\n } catch (error: unknown) {\n if (error instanceof Error && error.message === \"Invalid task file format\") {\n throw error;\n }\n throw new Error(\"Failed to read task file\", { cause: error });\n }\n let parsed: unknown;\n try {\n parsed = parseYaml(split.yamlText);\n } catch (error: unknown) {\n throw new Error(\"Failed to read task file\", { cause: error });\n }\n const result = TaskSchema.safeParse(parsed);\n if (!result.success) {\n throw new Error(\"Failed to read task file\", { cause: result.error });\n }\n return { task: result.data, body: split.body };\n}\n\n// ============================================================================\n// File write (atomic, mode-aware)\n// ============================================================================\n\nexport type WriteTaskFileMode = \"create\" | \"overwrite\";\n\n/**\n * Atomically write `<paths.tasks>/<taskId>.md`.\n *\n * `mode: \"create\"` delegates to {@link atomicCreate} so a pre-existing file\n * fails fast with EEXIST → `\"Task file already exists\"`.\n * `mode: \"overwrite\"` delegates to {@link atomicReplace} and silently\n * replaces any prior file.\n *\n * The serialised body is structured as:\n *\n * ---\\n\n * <yaml>\\n\n * ---\\n\n * \\n\n * <body>\\n (only when body is non-empty)\n */\nexport async function writeTaskFile(\n paths: BasouPaths,\n taskId: string,\n doc: TaskDocument,\n options: { mode: WriteTaskFileMode },\n): Promise<void> {\n // Runtime self-defense: even if a caller bypassed the TypeScript boundary,\n // a malformed task object cannot reach disk.\n const validated = TaskSchema.parse(doc.task);\n\n const filePath = join(paths.tasks, `${taskId}.md`);\n const yamlText = stringifyYaml(validated);\n const trimmedBody =\n doc.body.length === 0 ? \"\" : `\\n${doc.body.endsWith(\"\\n\") ? doc.body : `${doc.body}\\n`}`;\n const fileBody = `${FRONT_MATTER_DELIM}\\n${yamlText}${FRONT_MATTER_DELIM}\\n${trimmedBody}`;\n\n if (options.mode === \"create\") {\n try {\n await atomicCreate(filePath, fileBody);\n } catch (error: unknown) {\n if (findErrorCode(error, \"EEXIST\")) {\n throw new Error(\"Task file already exists\", { cause: error });\n }\n throw new Error(\"Failed to write task file\", { cause: error });\n }\n return;\n }\n\n // overwrite mode\n try {\n await atomicReplace(filePath, fileBody);\n } catch (error: unknown) {\n throw new Error(\"Failed to write task file\", { cause: error });\n }\n}\n\n// ============================================================================\n// Directory enumeration / loading\n// ============================================================================\n\nconst TASK_FILENAME_RE = /^(.+)\\.md$/;\n\n/**\n * Enumerate task ids by listing `<paths.tasks>/`. Filenames that do not\n * match the `<task_id>.md` shape, or that decode to a non-conforming task\n * id (per `TaskIdSchema`), are silently skipped — they are surfaced via\n * the caller's `options.onSkip` hook in {@link loadTaskEntries} so list\n * commands can show a warning row.\n *\n * Returns ids in ULID-ascending order (filename sort matches ULID order).\n * Empty directory or ENOENT → `[]`. Other I/O failures throw\n * `\"Failed to enumerate tasks\"`.\n */\nexport async function enumerateTaskIds(paths: BasouPaths): Promise<string[]> {\n // Fast path: read `tasks/index.json` if it exists and is valid. The index\n // is maintained write-through by every task mutation API so the cache\n // matches disk except across crashes / hand-edits / version bumps.\n try {\n const index = await readTaskIndex(paths);\n return index.tasks.map((t) => t.id);\n } catch {\n // Index missing / parse fail / schema mismatch — fall through to the\n // disk-scan rebuild path below. The discrete error classes from\n // readTaskIndex (Task index not found / Invalid task index / Failed\n // to read task index) are all equivalent here.\n }\n\n const ids = await enumerateTaskIdsFromDisk(paths);\n\n // Skip the lazy rebuild entirely when there is nothing to record: a\n // pre-init / empty workspace has no tasks/ dir, so a rebuild attempt\n // would fail with ENOENT and emit a misleading warning. The next write\n // will recreate the index from scratch via updateTaskIndex anyway.\n if (ids.length === 0) {\n return ids;\n }\n\n // Lazy rebuild: scan each task.md and write the resulting index. Best-\n // effort — a per-task read failure (= a malformed file we'd surface as\n // a skip elsewhere) is excluded from the rebuilt index but still\n // returned to the caller in `ids` so loadTaskEntries can surface its\n // own skip reason. Rebuild write failure (disk full, EACCES) is logged\n // and swallowed; the next enumerateTaskIds call will retry the rebuild.\n const entries: TaskIndexEntry[] = [];\n for (const id of ids) {\n try {\n const doc = await readTaskFile(paths, id);\n entries.push(buildTaskIndexEntry(doc.task.task));\n } catch {\n // Skip unreadable entry from the rebuild.\n }\n }\n await rebuildTaskIndex(paths, entries).catch(() => {\n console.warn(\"Failed to rebuild tasks/index.json; subsequent reads will retry\");\n });\n return ids;\n}\n\nasync function enumerateTaskIdsFromDisk(paths: BasouPaths): Promise<string[]> {\n let entries: string[];\n try {\n entries = (await readdir(paths.tasks, { withFileTypes: true }))\n .filter((d) => d.isFile())\n .map((d) => d.name);\n } catch (error: unknown) {\n if (findErrorCode(error, \"ENOENT\")) return [];\n throw new Error(\"Failed to enumerate tasks\", { cause: error });\n }\n const taskIds: string[] = [];\n for (const name of entries) {\n const match = TASK_FILENAME_RE.exec(name);\n if (match === null) continue;\n const candidate = match[1] as string;\n if (!TaskIdSchema.safeParse(candidate).success) continue;\n taskIds.push(candidate);\n }\n taskIds.sort();\n return taskIds;\n}\n\n/**\n * Convert the inner `task` portion of a task.md document into the\n * compact entry shape stored inside `tasks/index.json`. Omits `label`\n * when the task has no label set so the JSON does not store an\n * `undefined` literal.\n */\nfunction buildTaskIndexEntry(task: Task[\"task\"]): TaskIndexEntry {\n return {\n id: task.id,\n status: task.status,\n ...(task.label !== undefined ? { label: task.label } : {}),\n updated_at: task.updated_at,\n };\n}\n\n/**\n * Apply a single write-through update to `tasks/index.json` and swallow\n * any failure with a console.warn so the calling task-write API stays\n * successful (= task.md is the source of truth, index is a soft cache).\n */\nasync function safeUpdateTaskIndex(\n paths: BasouPaths,\n op: Parameters<typeof updateTaskIndex>[1],\n): Promise<void> {\n try {\n await updateTaskIndex(paths, op);\n } catch {\n console.warn(\"Index update failed; rebuild on next read\");\n }\n}\n\nconst ARCHIVE_DIR_NAME = \"archive\";\n\nfunction archiveTasksDir(paths: BasouPaths): string {\n return join(paths.tasks, ARCHIVE_DIR_NAME);\n}\n\n/**\n * Enumerate task ids inside `<paths.tasks>/archive/`. Returns `[]` when the\n * archive directory does not exist (= no task has ever been archived).\n * Filtering / ordering rules mirror {@link enumerateTaskIds}.\n */\nexport async function enumerateArchivedTaskIds(paths: BasouPaths): Promise<string[]> {\n let entries: string[];\n try {\n entries = (await readdir(archiveTasksDir(paths), { withFileTypes: true }))\n .filter((d) => d.isFile())\n .map((d) => d.name);\n } catch (error: unknown) {\n if (findErrorCode(error, \"ENOENT\")) return [];\n throw new Error(\"Failed to enumerate archived tasks\", { cause: error });\n }\n const taskIds: string[] = [];\n for (const name of entries) {\n const match = TASK_FILENAME_RE.exec(name);\n if (match === null) continue;\n const candidate = match[1] as string;\n if (!TaskIdSchema.safeParse(candidate).success) continue;\n taskIds.push(candidate);\n }\n taskIds.sort();\n return taskIds;\n}\n\n/**\n * Read a task.md file looking in the main tasks directory first and falling\n * back to `<paths.tasks>/archive/` if the file is missing there. Returns the\n * parsed document plus a flag indicating whether the hit came from the\n * archive dir. Useful for `basou task show` which surfaces archived tasks\n * read-only without requiring the operator to opt in.\n *\n * Error contract matches {@link readTaskFile} — only the lookup location\n * differs.\n */\nexport async function readTaskFileWithArchiveFallback(\n paths: BasouPaths,\n taskId: string,\n): Promise<{ doc: TaskDocument; archived: boolean }> {\n try {\n const doc = await readTaskFile(paths, taskId);\n return { doc, archived: false };\n } catch (error: unknown) {\n if (!(error instanceof Error && error.message === \"Task file not found\")) {\n throw error;\n }\n }\n const archiveFilePath = join(archiveTasksDir(paths), `${taskId}.md`);\n let raw: string;\n try {\n raw = await readFile(archiveFilePath, \"utf8\");\n } catch (error: unknown) {\n if (findErrorCode(error, \"ENOENT\")) {\n throw new Error(\"Task file not found\", { cause: error });\n }\n throw new Error(\"Failed to read task file\", { cause: error });\n }\n // Parsing mirrors readTaskFile; archived files share the schema.\n let split: { yamlText: string; body: string };\n try {\n split = splitFrontMatter(raw);\n } catch (error: unknown) {\n if (error instanceof Error && error.message === \"Invalid task file format\") {\n throw error;\n }\n throw new Error(\"Failed to read task file\", { cause: error });\n }\n let parsed: unknown;\n try {\n parsed = parseYaml(split.yamlText);\n } catch (error: unknown) {\n throw new Error(\"Failed to read task file\", { cause: error });\n }\n const result = TaskSchema.safeParse(parsed);\n if (!result.success) {\n throw new Error(\"Failed to read task file\", { cause: result.error });\n }\n return { doc: { task: result.data, body: split.body }, archived: true };\n}\n\nexport type TaskSkipReason = \"task_file_invalid\" | \"task_file_unreadable\";\n\nexport type LoadTaskEntriesOptions = {\n onSkip?: (taskId: string, reason: TaskSkipReason) => void;\n};\n\n/**\n * Read every task.md under `<paths.tasks>/` and return the valid documents,\n * skipping malformed / unreadable files with an `onSkip` callback for each.\n *\n * Returned entries are sorted ascending by `task.created_at` (internal asc;\n * the CLI layer reverses for newest-first display).\n */\nexport async function loadTaskEntries(\n paths: BasouPaths,\n options: LoadTaskEntriesOptions = {},\n): Promise<TaskDocument[]> {\n const ids = await enumerateTaskIds(paths);\n const entries: TaskDocument[] = [];\n for (const id of ids) {\n let doc: TaskDocument;\n try {\n doc = await readTaskFile(paths, id);\n } catch (error: unknown) {\n if (error instanceof Error && error.message === \"Invalid task file format\") {\n options.onSkip?.(id, \"task_file_invalid\");\n } else if (error instanceof Error && error.message === \"Failed to read task file\") {\n options.onSkip?.(id, \"task_file_invalid\");\n } else if (error instanceof Error && error.message === \"Task file not found\") {\n // Race: file was enumerated then deleted before read. Treat as unreadable.\n options.onSkip?.(id, \"task_file_unreadable\");\n } else {\n options.onSkip?.(id, \"task_file_unreadable\");\n }\n continue;\n }\n entries.push(doc);\n }\n entries.sort((a, b) => {\n const c = Date.parse(a.task.task.created_at) - Date.parse(b.task.task.created_at);\n return c !== 0 ? c : a.task.task.id.localeCompare(b.task.task.id);\n });\n return entries;\n}\n\n// ============================================================================\n// Status transition rules\n// ============================================================================\n\n// `planned -> done` and `planned -> cancelled` are direct shortcuts so a\n// task that was queued but completed (or abandoned) outside of an explicit\n// `in_progress` phase can be closed with a single CLI call. The 1\n// transition = 1 event invariant is preserved: each shortcut emits exactly\n// one `task_status_changed` event capturing the new from / to pair.\nconst ALLOWED_TRANSITIONS: Readonly<Record<TaskStatus, ReadonlySet<TaskStatus>>> = {\n planned: new Set<TaskStatus>([\"in_progress\", \"done\", \"cancelled\"]),\n in_progress: new Set<TaskStatus>([\"done\", \"cancelled\"]),\n done: new Set<TaskStatus>(),\n cancelled: new Set<TaskStatus>(),\n};\n\nfunction assertTransitionAllowed(from: TaskStatus, to: TaskStatus): void {\n const allowed = ALLOWED_TRANSITIONS[from];\n if (!allowed.has(to)) {\n throw new Error(`Invalid task status transition: ${from} -> ${to}`);\n }\n}\n\n// ============================================================================\n// Specialised error for task.md write failure after the event was persisted\n// ============================================================================\n\n/**\n * Thrown when the task event (`task_created` / `task_status_changed`) was\n * fully persisted to events.jsonl but the accompanying `task.md` write\n * failed. The caller is responsible for surfacing a \"do not rerun\"\n * warning — re-running the same CLI invocation would duplicate the event\n * in events.jsonl.\n *\n * Reconciliation (= regenerating the missing task.md from events) is a\n * v0.2 follow-up (= `task reconcile` family).\n */\n/**\n * `phase` identifies which staged write failed after the event commit:\n * - `create`: task.md create write (ad-hoc or attach path)\n * - `overwrite`: task.md overwrite during a status change\n * - `link-session`: session.yaml `task_id` update during the attach path\n * (split out so CLI warnings describe the actual unsafe artefact\n * instead of always saying \"task.md creation failed\")\n * - `reconcile`: task.md overwrite during `basou task reconcile --write`\n * after the `task_reconciled` event was persisted\n * - `reconcile-finalize`: ad-hoc reconcile session finalize failed (=\n * `FailedToFinalizeError` caught and re-classified)\n * - `reconcile-concurrent`: task.md was modified between the pre-write\n * snapshot and the post-event re-read; the operator is told to re-run\n * reconcile rather than overwrite a stale snapshot\n */\nexport type TaskWriteAfterEventPhase =\n | \"create\"\n | \"overwrite\"\n | \"link-session\"\n | \"reconcile\"\n | \"reconcile-finalize\"\n | \"reconcile-concurrent\"\n // Mirror the reconcile-failure phases for the `refreshTaskLinkedSessions`\n // path. Failure semantics are identical (= ad-hoc session committed, then\n // task.md write / concurrency check failed), but the operator-facing\n // recovery hint must point at `basou task refresh-linkage`, not reconcile.\n | \"linkage-refresh\"\n | \"linkage-refresh-finalize\"\n | \"linkage-refresh-concurrent\"\n // `task_deleted` / `task_archived` event was persisted in events.jsonl but\n // the subsequent file mutation (unlink / move to archive) failed. The\n // event remains the authoritative audit record; the operator must reconcile\n // the residual file state by hand.\n | \"delete\"\n | \"archive\";\n\nexport class TaskWriteAfterEventError extends Error {\n readonly taskId: PrefixedId<\"task\">;\n readonly eventId: PrefixedId<\"evt\">;\n readonly sessionId: PrefixedId<\"ses\">;\n readonly phase: TaskWriteAfterEventPhase;\n\n constructor(args: {\n taskId: PrefixedId<\"task\">;\n eventId: PrefixedId<\"evt\">;\n sessionId: PrefixedId<\"ses\">;\n phase: TaskWriteAfterEventPhase;\n cause: unknown;\n }) {\n super(\"Failed to write task file after event was persisted\", { cause: args.cause });\n this.name = \"TaskWriteAfterEventError\";\n this.taskId = args.taskId;\n this.eventId = args.eventId;\n this.sessionId = args.sessionId;\n this.phase = args.phase;\n }\n}\n\n// ============================================================================\n// Orchestrator: createTaskWithEvent\n// ============================================================================\n\nexport type CreateAdHocTaskInput = {\n mode: \"ad-hoc\";\n paths: BasouPaths;\n manifest: Manifest;\n occurredAt: string;\n taskId: PrefixedId<\"task\">;\n title: string;\n label?: string;\n initialStatus: TaskStatus;\n description: string;\n workingDirectory: string;\n /**\n * Optional override for `task.md.updated_at` when `initialStatus` is a\n * terminal value (done / cancelled). Lets the operator backdate a\n * retroactively-recorded completed task so `task.md` reflects the actual\n * completion moment while `events.jsonl` keeps recording time. Ignored\n * for non-terminal statuses.\n */\n completedAt?: string;\n};\n\nexport type AttachTaskInput = {\n mode: \"attach\";\n paths: BasouPaths;\n occurredAt: string;\n sessionId: PrefixedId<\"ses\">;\n taskId: PrefixedId<\"task\">;\n title: string;\n label?: string;\n initialStatus: TaskStatus;\n description: string;\n attachableStatuses?: ReadonlySet<AttachableStatus>;\n /** See {@link CreateAdHocTaskInput.completedAt}. */\n completedAt?: string;\n};\n\nexport type CreateTaskInput = CreateAdHocTaskInput | AttachTaskInput;\n\nexport type CreateTaskResult = {\n taskId: PrefixedId<\"task\">;\n eventId: PrefixedId<\"evt\">;\n sessionId: PrefixedId<\"ses\">;\n sessionStatus: SessionStatus;\n};\n\nfunction buildTaskCreatedEvent(input: {\n eventId: PrefixedId<\"evt\">;\n sessionId: PrefixedId<\"ses\">;\n taskId: PrefixedId<\"task\">;\n title: string;\n occurredAt: string;\n}): Event {\n return {\n schema_version: \"0.1.0\",\n id: input.eventId,\n session_id: input.sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"task_created\",\n task_id: input.taskId,\n title: input.title,\n };\n}\n\nfunction buildTaskStatusChangedEvent(input: {\n eventId: PrefixedId<\"evt\">;\n sessionId: PrefixedId<\"ses\">;\n taskId: PrefixedId<\"task\">;\n from: TaskStatus;\n to: TaskStatus;\n occurredAt: string;\n}): Event {\n return {\n schema_version: \"0.1.0\",\n id: input.eventId,\n session_id: input.sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"task_status_changed\",\n task_id: input.taskId,\n from: input.from,\n to: input.to,\n };\n}\n\nfunction buildAdHocTaskLabel(title: string, mode: \"new\" | \"status\"): string {\n const truncated =\n title.length > LABEL_TITLE_MAX ? `${title.slice(0, LABEL_TRUNCATE_HEAD)}...` : title;\n return mode === \"new\" ? `Ad-hoc task: ${truncated}` : `Ad-hoc task status: ${truncated}`;\n}\n\n// Kept distinct from buildAdHocTaskLabel rather than threading a third mode\n// through that helper — `basou task reconcile` is a management operation, not\n// a creation/status flow, and the label prefix should read that way.\nfunction buildAdHocReconcileLabel(title: string): string {\n const truncated =\n title.length > LABEL_TITLE_MAX ? `${title.slice(0, LABEL_TRUNCATE_HEAD)}...` : title;\n return `Ad-hoc task reconcile: ${truncated}`;\n}\n\n// Separate label generator for `basou task refresh-linkage` so the operator\n// can distinguish refresh runs from reconcile runs at a glance in session\n// listings — both flow through `createAdHocSessionWithEvent` but answer\n// different questions (broken-ref repair vs. snapshot-vs-events sync).\nfunction buildAdHocRefreshLinkageLabel(title: string): string {\n const truncated =\n title.length > LABEL_TITLE_MAX ? `${title.slice(0, LABEL_TRUNCATE_HEAD)}...` : title;\n return `Ad-hoc task refresh-linkage: ${truncated}`;\n}\n\nfunction buildAdHocDeleteLabel(title: string): string {\n const truncated =\n title.length > LABEL_TITLE_MAX ? `${title.slice(0, LABEL_TRUNCATE_HEAD)}...` : title;\n return `Ad-hoc task delete: ${truncated}`;\n}\n\nfunction buildAdHocArchiveLabel(title: string): string {\n const truncated =\n title.length > LABEL_TITLE_MAX ? `${title.slice(0, LABEL_TRUNCATE_HEAD)}...` : title;\n return `Ad-hoc task archive: ${truncated}`;\n}\n\nfunction buildTaskReconciledEvent(input: {\n eventId: PrefixedId<\"evt\">;\n sessionId: PrefixedId<\"ses\">;\n taskId: PrefixedId<\"task\">;\n removedCreatedInSession: PrefixedId<\"ses\"> | null;\n createdInSessionReplacement: PrefixedId<\"ses\"> | null;\n removedLinkedSessions: PrefixedId<\"ses\">[];\n occurredAt: string;\n}): Event {\n return {\n schema_version: \"0.1.0\",\n id: input.eventId,\n session_id: input.sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"task_reconciled\",\n task_id: input.taskId,\n removed_created_in_session: input.removedCreatedInSession,\n created_in_session_replacement: input.createdInSessionReplacement,\n removed_linked_sessions: input.removedLinkedSessions,\n };\n}\n\nfunction buildTaskDeletedEvent(input: {\n eventId: PrefixedId<\"evt\">;\n sessionId: PrefixedId<\"ses\">;\n taskId: PrefixedId<\"task\">;\n title: string;\n occurredAt: string;\n}): Event {\n return {\n schema_version: \"0.1.0\",\n id: input.eventId,\n session_id: input.sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"task_deleted\",\n task_id: input.taskId,\n title: input.title,\n };\n}\n\nfunction buildTaskArchivedEvent(input: {\n eventId: PrefixedId<\"evt\">;\n sessionId: PrefixedId<\"ses\">;\n taskId: PrefixedId<\"task\">;\n title: string;\n occurredAt: string;\n}): Event {\n return {\n schema_version: \"0.1.0\",\n id: input.eventId,\n session_id: input.sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"task_archived\",\n task_id: input.taskId,\n title: input.title,\n };\n}\n\nfunction buildTaskLinkageRefreshedEvent(input: {\n eventId: PrefixedId<\"evt\">;\n sessionId: PrefixedId<\"ses\">;\n taskId: PrefixedId<\"task\">;\n addedLinkedSessions: PrefixedId<\"ses\">[];\n removedLinkedSessions: PrefixedId<\"ses\">[];\n finalCount: number;\n occurredAt: string;\n}): Event {\n return {\n schema_version: \"0.1.0\",\n id: input.eventId,\n session_id: input.sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"task_linkage_refreshed\",\n task_id: input.taskId,\n added_linked_sessions: input.addedLinkedSessions,\n removed_linked_sessions: input.removedLinkedSessions,\n final_count: input.finalCount,\n };\n}\n\n/**\n * Create a new task: fires a single `task_created` event and writes\n * `.basou/tasks/<taskId>.md` with status = `initialStatus`.\n *\n * Ad-hoc path: a fresh ad-hoc session is minted (5-event bulk write,\n * `task_created` as the target event, session.yaml.task_id pinned to the\n * new task).\n *\n * Attach path: the target session's `task_id` is validated against the\n * session ⇆ task anchor invariant (see\n * `docs/spec/workspace.md#21-confirmed-invariants`; null → updated to the\n * new task; existing X → rejected since X is already owned). If validation\n * passes, the event is appended to events.jsonl and session.yaml's\n * `task_id` is updated to the new task.\n *\n * Race window (v0.1 accepts): stage 2 writes the event, stage 3 writes\n * task.md. A failure on stage 3 leaves events.jsonl ahead of task.md;\n * {@link TaskWriteAfterEventError} surfaces this with a \"do not rerun\"\n * warning so the operator can reconcile manually until the v0.2 reconcile\n * flow arrives.\n */\nexport async function createTaskWithEvent(input: CreateTaskInput): Promise<CreateTaskResult> {\n // Boundary parses so direct (non-CLI) callers can't smuggle in malformed\n // ids / statuses / titles past the CLI-side guards. All checks here run\n // BEFORE any persistent write, so a rejection leaves events.jsonl and\n // task.md untouched.\n TaskIdSchema.parse(input.taskId);\n InitialTaskStatusSchema.parse(input.initialStatus);\n TaskTitleSchema.parse(input.title);\n if (input.label !== undefined) {\n TaskLabelSchema.parse(input.label);\n }\n if (input.completedAt !== undefined) {\n CompletedAtSchema.parse(input.completedAt);\n }\n\n if (input.mode === \"ad-hoc\") {\n return createTaskAdHoc(input);\n }\n return createTaskAttach(input);\n}\n\nasync function createTaskAdHoc(input: CreateAdHocTaskInput): Promise<CreateTaskResult> {\n const adHoc = await createAdHocSessionWithEvent({\n paths: input.paths,\n manifest: input.manifest,\n label: buildAdHocTaskLabel(input.title, \"new\"),\n occurredAt: input.occurredAt,\n sessionSource: \"human\",\n workingDirectory: input.workingDirectory,\n invocation: {\n command: \"basou task new\",\n args: buildTaskNewInvocationArgs(input.title, input.initialStatus, input.completedAt),\n },\n taskId: input.taskId,\n targetEventBuilders: buildTaskNewTargetEventBuilders({\n taskId: input.taskId,\n title: input.title,\n initialStatus: input.initialStatus,\n occurredAt: input.occurredAt,\n }),\n });\n\n const task: Task = buildInitialTask({\n taskId: input.taskId,\n title: input.title,\n ...(input.label !== undefined ? { label: input.label } : {}),\n status: input.initialStatus,\n occurredAt: input.occurredAt,\n ...(input.completedAt !== undefined ? { completedAt: input.completedAt } : {}),\n workspaceId: input.manifest.workspace.id,\n createdInSession: adHoc.sessionId,\n });\n // `targetEventIds[0]` is the `task_created` anchor (= what the caller cares\n // about); a second `task_status_changed` event may also live in this\n // ad-hoc session when initialStatus is terminal, but it is not the\n // primary task-lifecycle anchor.\n const anchorEventId = adHoc.targetEventIds[0] as PrefixedId<\"evt\">;\n try {\n await writeTaskFile(\n input.paths,\n input.taskId,\n { task, body: input.description },\n { mode: \"create\" },\n );\n } catch (error: unknown) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: anchorEventId,\n sessionId: adHoc.sessionId,\n phase: \"create\",\n cause: error,\n });\n }\n await safeUpdateTaskIndex(input.paths, { kind: \"add\", entry: buildTaskIndexEntry(task.task) });\n return {\n taskId: input.taskId,\n eventId: anchorEventId,\n sessionId: adHoc.sessionId,\n sessionStatus: \"completed\",\n };\n}\n\nasync function createTaskAttach(input: AttachTaskInput): Promise<CreateTaskResult> {\n SessionIdSchema.parse(input.sessionId);\n\n // Per-session lock spans the entire attach window (session.yaml read →\n // collision-matrix check → task_created append → session.yaml task_id\n // update → optional task_status_changed append). Without it, two\n // concurrent attaches on the same session.yaml could both observe a\n // null task_id, both append their own `task_created`, and race on the\n // final task_id overwrite.\n const sessionLock = await acquireLock(input.paths, \"session\", input.sessionId);\n try {\n return await createTaskAttachLocked(input);\n } finally {\n await sessionLock.release();\n }\n}\n\nasync function createTaskAttachLocked(input: AttachTaskInput): Promise<CreateTaskResult> {\n // 1. Read session.yaml + validate the §F.7.2 collision matrix BEFORE writing\n // anything. status / task_id checks share the same read.\n const sessionDoc = await readSessionYaml(input.paths, input.sessionId);\n const status = sessionDoc.session.status;\n if (status === \"imported\") {\n throw new Error(\"Cannot attach to imported session\");\n }\n const attachable = input.attachableStatuses ?? DEFAULT_ATTACHABLE_STATUSES;\n if (!attachable.has(status as AttachableStatus)) {\n throw new Error(`Session is not active: ${status}`);\n }\n const existingTaskId = sessionDoc.session.task_id ?? null;\n if (existingTaskId !== null && existingTaskId !== input.taskId) {\n throw new Error(`Session already linked to a different task: ${existingTaskId}`);\n }\n if (existingTaskId === input.taskId) {\n // Re-creating the same task on the same session would duplicate\n // `task_created` in events.jsonl. Reject up front.\n throw new Error(`Task already exists: ${input.taskId}`);\n }\n\n // 2. Append `task_created` to events.jsonl. We use appendEventToExistingSession\n // so the same status/imported-rejection logic is shared across attach-flavoured callers.\n const appendResult = await appendEventToExistingSession({\n paths: input.paths,\n sessionId: input.sessionId,\n ...(input.attachableStatuses !== undefined\n ? { attachableStatuses: input.attachableStatuses }\n : {}),\n eventBuilder: (eventId) =>\n buildTaskCreatedEvent({\n eventId,\n sessionId: input.sessionId,\n taskId: input.taskId,\n title: input.title,\n occurredAt: input.occurredAt,\n }),\n });\n\n // 3. Update session.yaml task_id (null → new) so the single-session ↔\n // single-task invariant holds. Failure here puts us into the same\n // \"event persisted, side-effect missing\" band as task.md. Use\n // phase: \"link-session\" so the operator warning identifies the failed\n // artefact correctly.\n try {\n const updated = {\n ...sessionDoc,\n session: { ...sessionDoc.session, task_id: input.taskId },\n };\n await overwriteYamlFile(join(input.paths.sessions, input.sessionId, \"session.yaml\"), updated);\n } catch (error: unknown) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: appendResult.eventId,\n sessionId: input.sessionId,\n phase: \"link-session\",\n cause: error,\n });\n }\n\n // 4. For terminal initialStatus (done / cancelled) append a second target\n // event `task_status_changed (planned → terminal)` so the events.jsonl\n // audit trail records the implicit transition. The ALLOWED_TRANSITIONS\n // shortcut from `planned` to `done|cancelled` makes this a single\n // permitted edge. The session.yaml `task_id` link from step 3 covers\n // both events; no further session.yaml write is needed.\n if (isTerminalTaskStatus(input.initialStatus)) {\n await appendEventToExistingSession({\n paths: input.paths,\n sessionId: input.sessionId,\n ...(input.attachableStatuses !== undefined\n ? { attachableStatuses: input.attachableStatuses }\n : {}),\n eventBuilder: (eventId) =>\n buildTaskStatusChangedEvent({\n eventId,\n sessionId: input.sessionId,\n taskId: input.taskId,\n from: \"planned\",\n to: input.initialStatus,\n occurredAt: input.occurredAt,\n }),\n });\n }\n\n // 5. Write task.md (create mode, collision = rerun guard).\n const task: Task = buildInitialTask({\n taskId: input.taskId,\n title: input.title,\n ...(input.label !== undefined ? { label: input.label } : {}),\n status: input.initialStatus,\n occurredAt: input.occurredAt,\n ...(input.completedAt !== undefined ? { completedAt: input.completedAt } : {}),\n workspaceId: sessionDoc.session.workspace_id,\n createdInSession: input.sessionId,\n });\n try {\n await writeTaskFile(\n input.paths,\n input.taskId,\n { task, body: input.description },\n { mode: \"create\" },\n );\n } catch (error: unknown) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: appendResult.eventId,\n sessionId: input.sessionId,\n phase: \"create\",\n cause: error,\n });\n }\n\n await safeUpdateTaskIndex(input.paths, { kind: \"add\", entry: buildTaskIndexEntry(task.task) });\n\n return {\n taskId: input.taskId,\n eventId: appendResult.eventId,\n sessionId: input.sessionId,\n sessionStatus: status,\n };\n}\n\nfunction buildInitialTask(input: {\n taskId: PrefixedId<\"task\">;\n title: string;\n label?: string;\n status: TaskStatus;\n occurredAt: string;\n /**\n * Override for `updated_at` when `status` is terminal. Ignored for\n * non-terminal statuses so backdating a non-completed task is not\n * possible by accident.\n */\n completedAt?: string;\n workspaceId: PrefixedId<\"ws\">;\n createdInSession: PrefixedId<\"ses\">;\n}): Task {\n const updatedAt =\n input.completedAt !== undefined && isTerminalTaskStatus(input.status)\n ? input.completedAt\n : input.occurredAt;\n return {\n schema_version: \"0.1.0\",\n task: {\n id: input.taskId,\n title: input.title,\n ...(input.label !== undefined ? { label: input.label } : {}),\n status: input.status,\n created_at: input.occurredAt,\n updated_at: updatedAt,\n workspace_id: input.workspaceId,\n created_in_session: input.createdInSession,\n linked_sessions: [input.createdInSession],\n },\n };\n}\n\n// Helpers for the ad-hoc `task new` path. The invocation args list mirrors\n// the operator's CLI input so the recorded `session.yaml.invocation.args`\n// stays accurate even when `--status` / `--completed-at` were supplied.\nfunction buildTaskNewInvocationArgs(\n title: string,\n initialStatus: TaskStatus,\n completedAt: string | undefined,\n): string[] {\n const args = [\"--title\", title];\n if (initialStatus !== \"planned\") {\n args.push(\"--status\", initialStatus);\n }\n if (completedAt !== undefined && isTerminalTaskStatus(initialStatus)) {\n args.push(\"--completed-at\", completedAt);\n }\n return args;\n}\n\nfunction buildTaskNewTargetEventBuilders(input: {\n taskId: PrefixedId<\"task\">;\n title: string;\n initialStatus: TaskStatus;\n occurredAt: string;\n}): Array<(sessionId: PrefixedId<\"ses\">, eventId: PrefixedId<\"evt\">) => Event> {\n const createdBuilder = (sessionId: PrefixedId<\"ses\">, eventId: PrefixedId<\"evt\">): Event =>\n buildTaskCreatedEvent({\n eventId,\n sessionId,\n taskId: input.taskId,\n title: input.title,\n occurredAt: input.occurredAt,\n });\n if (!isTerminalTaskStatus(input.initialStatus)) {\n return [createdBuilder];\n }\n // For terminal initialStatus, emit `task_status_changed (planned → terminal)`\n // right after `task_created` so replay reconstructs the implicit\n // transition. The shortcut edges `planned → done|cancelled` are already\n // allowed by ALLOWED_TRANSITIONS.\n const statusChangedBuilder = (sessionId: PrefixedId<\"ses\">, eventId: PrefixedId<\"evt\">): Event =>\n buildTaskStatusChangedEvent({\n eventId,\n sessionId,\n taskId: input.taskId,\n from: \"planned\",\n to: input.initialStatus,\n occurredAt: input.occurredAt,\n });\n return [createdBuilder, statusChangedBuilder];\n}\n\n// ============================================================================\n// Orchestrator: updateTaskStatusWithEvent\n// ============================================================================\n\nexport type UpdateAdHocTaskStatusInput = {\n mode: \"ad-hoc\";\n paths: BasouPaths;\n manifest: Manifest;\n occurredAt: string;\n taskId: PrefixedId<\"task\">;\n newStatus: TaskStatus;\n workingDirectory: string;\n};\n\nexport type AttachUpdateTaskStatusInput = {\n mode: \"attach\";\n paths: BasouPaths;\n occurredAt: string;\n sessionId: PrefixedId<\"ses\">;\n taskId: PrefixedId<\"task\">;\n newStatus: TaskStatus;\n attachableStatuses?: ReadonlySet<AttachableStatus>;\n};\n\nexport type UpdateTaskStatusInput = UpdateAdHocTaskStatusInput | AttachUpdateTaskStatusInput;\n\nexport type UpdateTaskStatusResult = {\n taskId: PrefixedId<\"task\">;\n eventId: PrefixedId<\"evt\">;\n sessionId: PrefixedId<\"ses\">;\n sessionStatus: SessionStatus;\n previousStatus: TaskStatus;\n newStatus: TaskStatus;\n};\n\n/**\n * Fire a `task_status_changed` event and overwrite the task.md front matter\n * with the new status / `updated_at` / appended-but-deduped `linked_sessions`.\n *\n * Validates the transition BEFORE any event write so a rejected transition\n * leaves events.jsonl untouched. The canonical edge set lives in\n * {@link ALLOWED_TRANSITIONS}; the current shape is:\n * planned → {in_progress, done, cancelled}\n * in_progress → {done, cancelled}\n * done / cancelled are terminal (= idempotent same-state is rejected too).\n */\nexport async function updateTaskStatusWithEvent(\n input: UpdateTaskStatusInput,\n): Promise<UpdateTaskStatusResult> {\n TaskIdSchema.parse(input.taskId);\n\n // Per-task lock guards the read-modify-write of task.md against concurrent\n // writers on the same task id (= another `task status` / `task edit` /\n // `task reconcile` on the same task). Released in a finally block so the\n // lock never lingers on either the success or the failure path.\n const handle = await acquireLock(input.paths, \"task\", input.taskId);\n try {\n // 1. Load current task.md (= source of truth for current status).\n const currentDoc = await readTaskFile(input.paths, input.taskId);\n const previousStatus = currentDoc.task.task.status;\n\n // 2. Validate transition before touching any persistent state.\n assertTransitionAllowed(previousStatus, input.newStatus);\n\n if (input.mode === \"ad-hoc\") {\n return await updateTaskStatusAdHoc(input, currentDoc, previousStatus);\n }\n return await updateTaskStatusAttach(input, currentDoc, previousStatus);\n } finally {\n await handle.release();\n }\n}\n\nasync function updateTaskStatusAdHoc(\n input: UpdateAdHocTaskStatusInput,\n currentDoc: TaskDocument,\n previousStatus: TaskStatus,\n): Promise<UpdateTaskStatusResult> {\n const title = currentDoc.task.task.title;\n const adHoc = await createAdHocSessionWithEvent({\n paths: input.paths,\n manifest: input.manifest,\n label: buildAdHocTaskLabel(title, \"status\"),\n occurredAt: input.occurredAt,\n sessionSource: \"human\",\n workingDirectory: input.workingDirectory,\n invocation: { command: \"basou task status\", args: [input.taskId, input.newStatus] },\n taskId: input.taskId,\n targetEventBuilders: [\n (sessionId, eventId) =>\n buildTaskStatusChangedEvent({\n eventId,\n sessionId,\n taskId: input.taskId,\n from: previousStatus,\n to: input.newStatus,\n occurredAt: input.occurredAt,\n }),\n ],\n });\n\n const anchorEventId = adHoc.targetEventIds[0] as PrefixedId<\"evt\">;\n // 3. Overwrite task.md (status + updated_at + linked_sessions append-dedup).\n const updatedDoc = buildUpdatedDoc({\n currentDoc,\n newStatus: input.newStatus,\n occurredAt: input.occurredAt,\n appendSessionId: adHoc.sessionId,\n });\n try {\n await writeTaskFile(input.paths, input.taskId, updatedDoc, { mode: \"overwrite\" });\n } catch (error: unknown) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: anchorEventId,\n sessionId: adHoc.sessionId,\n phase: \"overwrite\",\n cause: error,\n });\n }\n await safeUpdateTaskIndex(input.paths, {\n kind: \"update\",\n entry: buildTaskIndexEntry(updatedDoc.task.task),\n });\n return {\n taskId: input.taskId,\n eventId: anchorEventId,\n sessionId: adHoc.sessionId,\n sessionStatus: \"completed\",\n previousStatus,\n newStatus: input.newStatus,\n };\n}\n\nasync function updateTaskStatusAttach(\n input: AttachUpdateTaskStatusInput,\n currentDoc: TaskDocument,\n previousStatus: TaskStatus,\n): Promise<UpdateTaskStatusResult> {\n SessionIdSchema.parse(input.sessionId);\n\n // Per-session lock guards the session.yaml read → events.jsonl append\n // window so a concurrent writer on the same session cannot append a\n // conflicting `task_status_changed` or flip session.yaml to a state\n // that invalidates the status check below. We are already inside the\n // per-task lock (updateTaskStatusWithEvent acquires it); the per-task\n // → per-session order is fixed across the codebase to keep cross-API\n // deadlocks impossible.\n const sessionLock = await acquireLock(input.paths, \"session\", input.sessionId);\n try {\n return await updateTaskStatusAttachLocked(input, currentDoc, previousStatus);\n } finally {\n await sessionLock.release();\n }\n}\n\nasync function updateTaskStatusAttachLocked(\n input: AttachUpdateTaskStatusInput,\n currentDoc: TaskDocument,\n previousStatus: TaskStatus,\n): Promise<UpdateTaskStatusResult> {\n const sessionDoc = await readSessionYaml(input.paths, input.sessionId);\n const status = sessionDoc.session.status;\n if (status === \"imported\") {\n throw new Error(\"Cannot attach to imported session\");\n }\n const attachable = input.attachableStatuses ?? DEFAULT_ATTACHABLE_STATUSES;\n if (!attachable.has(status as AttachableStatus)) {\n throw new Error(`Session is not active: ${status}`);\n }\n // task_id collision: the session MUST already be linked to the same task,\n // otherwise a status change on a task that the session does not own would\n // violate the session ⇆ task anchor invariant.\n const existingTaskId = sessionDoc.session.task_id ?? null;\n if (existingTaskId === null) {\n throw new Error(`Session is not linked to task: ${input.taskId}`);\n }\n if (existingTaskId !== input.taskId) {\n throw new Error(`Session already linked to a different task: ${existingTaskId}`);\n }\n\n const appendResult = await appendEventToExistingSession({\n paths: input.paths,\n sessionId: input.sessionId,\n ...(input.attachableStatuses !== undefined\n ? { attachableStatuses: input.attachableStatuses }\n : {}),\n eventBuilder: (eventId) =>\n buildTaskStatusChangedEvent({\n eventId,\n sessionId: input.sessionId,\n taskId: input.taskId,\n from: previousStatus,\n to: input.newStatus,\n occurredAt: input.occurredAt,\n }),\n });\n\n const updatedDoc = buildUpdatedDoc({\n currentDoc,\n newStatus: input.newStatus,\n occurredAt: input.occurredAt,\n appendSessionId: input.sessionId,\n });\n try {\n await writeTaskFile(input.paths, input.taskId, updatedDoc, { mode: \"overwrite\" });\n } catch (error: unknown) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: appendResult.eventId,\n sessionId: input.sessionId,\n phase: \"overwrite\",\n cause: error,\n });\n }\n await safeUpdateTaskIndex(input.paths, {\n kind: \"update\",\n entry: buildTaskIndexEntry(updatedDoc.task.task),\n });\n return {\n taskId: input.taskId,\n eventId: appendResult.eventId,\n sessionId: input.sessionId,\n sessionStatus: status,\n previousStatus,\n newStatus: input.newStatus,\n };\n}\n\nfunction buildUpdatedDoc(input: {\n currentDoc: TaskDocument;\n newStatus: TaskStatus;\n occurredAt: string;\n appendSessionId: PrefixedId<\"ses\">;\n}): TaskDocument {\n const linked = input.currentDoc.task.task.linked_sessions;\n const merged = linked.includes(input.appendSessionId)\n ? linked\n : [...linked, input.appendSessionId];\n const next: Task = {\n ...input.currentDoc.task,\n task: {\n ...input.currentDoc.task.task,\n status: input.newStatus,\n updated_at: input.occurredAt,\n linked_sessions: merged,\n },\n };\n return { task: next, body: input.currentDoc.body };\n}\n\n// ============================================================================\n// Reconcile (basou task reconcile)\n// ============================================================================\n\n/**\n * Single-task audit result. Always returned by {@link reconcileTask} regardless\n * of mode: in dry-run the `clean` / `broken*` fields describe what would change\n * and `reconcileSession` is `null`; in write mode the same fields describe\n * what did change and `reconcileSession` carries the minted ad-hoc session +\n * `task_reconciled` event ids.\n *\n * Broken `linked_sessions[]` entries are deduplicated against the same session\n * id appearing more than once in the source task.md (hand-edit defence).\n */\nexport type ReconcileResult = {\n taskId: PrefixedId<\"task\">;\n clean: boolean;\n brokenCreatedInSession: PrefixedId<\"ses\"> | null;\n brokenLinkedSessions: PrefixedId<\"ses\">[];\n reconcileSession: {\n sessionId: PrefixedId<\"ses\">;\n eventId: PrefixedId<\"evt\">;\n } | null;\n};\n\n/**\n * Per-task failure record collected by {@link reconcileAllTasks}. The scan\n * keeps running on isolated failures so one bad task does not freeze the\n * batch; the CLI layer renders this list and exits 1 if any entry is present.\n *\n * `phase` is populated only for {@link TaskWriteAfterEventError}; for any\n * other error class it is `null` and the operator must use `--verbose` to\n * surface the cause chain.\n */\nexport type ReconcileFailure = {\n taskId: PrefixedId<\"task\">;\n errorClass: string;\n phase: TaskWriteAfterEventPhase | null;\n};\n\n/**\n * Batch audit result. Order follows `enumerateTaskIds(paths)` (ULID-ascending).\n * `scanned` is the number of readable task.md files processed (= excludes\n * malformed task.md from the count so an integrity-broken file does not\n * pad the total).\n */\nexport type ReconcileAllResult = {\n results: ReconcileResult[];\n failed: ReconcileFailure[];\n scanned: number;\n};\n\nexport type ReconcileTaskInput = {\n taskId: PrefixedId<\"task\">;\n occurredAt: string;\n workingDirectory: string;\n write: boolean;\n /**\n * Whether the caller invoked reconcile against a single task (`--task <id>`)\n * or as part of a full scan. The ad-hoc reconcile session records the form\n * on its `invocation.args` so audit trails distinguish targeted repairs\n * from sweeps:\n * - `\"single\"` -> `[\"--task\", <taskId>, \"--write\"]`\n * - `\"all\"` -> `[\"--write\"]` (= the operator typed no task id, so the\n * scan-wide intent is preserved instead of synthesising one per task)\n * Defaults to `\"single\"` so direct callers (tests, programmatic uses) keep\n * the targeted form without an explicit argument.\n */\n scope?: \"single\" | \"all\";\n /**\n * Test-only hook: the test runner uses this to mutate the task file\n * from outside the reconcile flow between the pre-write snapshot and\n * the post-event re-read, simulating a concurrent edit so the\n * `reconcile-concurrent` branch can be exercised deterministically.\n * Production callers leave it undefined.\n */\n _onPhaseCompleted?: (phase: \"phase-4-snapshot\" | \"phase-5-bulk-write\") => Promise<void>;\n};\n\nexport type ReconcileAllTasksInput = {\n /**\n * Per-task timestamp factory. Each reconciled task gets a fresh ISO string\n * so concurrent ad-hoc sessions do not collide on `occurred_at`. The CLI\n * layer wires this to `ctx.nowProvider().toISOString()`.\n */\n occurredAt: () => string;\n workingDirectory: string;\n write: boolean;\n};\n\nexport type ReconcileAllTasksOptions = {\n /**\n * When true the result includes clean tasks (= no broken refs). The CLI\n * layer leaves this false so the human output only mentions tasks that\n * actually changed.\n */\n includeClean?: boolean;\n};\n\ntype TaskMdSnapshot = {\n mtimeMs: number;\n hash: string;\n};\n\nasync function computeTaskMdSnapshot(paths: BasouPaths, taskId: string): Promise<TaskMdSnapshot> {\n const filePath = join(paths.tasks, `${taskId}.md`);\n const [stats, raw] = await Promise.all([stat(filePath), readFile(filePath)]);\n const hash = createHash(\"sha256\").update(raw).digest(\"hex\");\n return { mtimeMs: stats.mtimeMs, hash };\n}\n\n// Read task.md and derive its mtime/sha256 snapshot from the SAME raw bytes\n// the TaskDocument was parsed from. An earlier internal review flagged that\n// the previous \"readTaskFile, then computeTaskMdSnapshot\" sequence left a window\n// where a concurrent edit between those two reads could leave the caller\n// acting on stale content while the snapshot already reflected the new\n// content — and stage 7 would then clobber the new bytes with the stale\n// TaskDocument. Sharing the raw bytes here means stage 6's re-read is\n// compared against the EXACT bytes that produced this document, so any\n// drift since this read is caught.\nasync function readTaskFileWithSnapshot(\n paths: BasouPaths,\n taskId: string,\n): Promise<{ doc: TaskDocument; snapshot: TaskMdSnapshot }> {\n const filePath = join(paths.tasks, `${taskId}.md`);\n let rawBuffer: Buffer;\n let stats: Awaited<ReturnType<typeof stat>>;\n try {\n [rawBuffer, stats] = await Promise.all([readFile(filePath), stat(filePath)]);\n } catch (error: unknown) {\n if (findErrorCode(error, \"ENOENT\")) {\n throw new Error(\"Task file not found\", { cause: error });\n }\n throw new Error(\"Failed to read task file\", { cause: error });\n }\n const raw = rawBuffer.toString(\"utf8\");\n const hash = createHash(\"sha256\").update(rawBuffer).digest(\"hex\");\n // Parse logic mirrors readTaskFile so the error contract stays identical\n // (Invalid task file format / Failed to read task file). Duplicated here to\n // avoid a second readFile from the public helper.\n let split: { yamlText: string; body: string };\n try {\n split = splitFrontMatter(raw);\n } catch (error: unknown) {\n if (error instanceof Error && error.message === \"Invalid task file format\") {\n throw error;\n }\n throw new Error(\"Failed to read task file\", { cause: error });\n }\n let parsed: unknown;\n try {\n parsed = parseYaml(split.yamlText);\n } catch (error: unknown) {\n throw new Error(\"Failed to read task file\", { cause: error });\n }\n const result = TaskSchema.safeParse(parsed);\n if (!result.success) {\n throw new Error(\"Failed to read task file\", { cause: result.error });\n }\n return {\n doc: { task: result.data, body: split.body },\n snapshot: { mtimeMs: stats.mtimeMs, hash },\n };\n}\n\ntype DetectedBrokenRefs = {\n brokenCreatedInSession: PrefixedId<\"ses\"> | null;\n brokenLinkedSessions: PrefixedId<\"ses\">[];\n};\n\n// `enumerateSessionDirs` returns directory names only — it does NOT validate\n// the contents of each `session.yaml`. By treating directory existence alone\n// as \"reachable\", reconcile targets the dogfood failure mode where a session\n// directory is removed entirely and a dangling id remains in task.md, while\n// keeping the \"broken\" predicate cheap. A directory that exists but whose\n// session.yaml is missing or schema-invalid is intentionally classified as\n// reachable here; that flavour of corruption is the responsibility of session\n// integrity tooling and is out of scope for v0.2 reconcile.\nasync function detectBrokenRefs(\n paths: BasouPaths,\n task: Task[\"task\"],\n): Promise<DetectedBrokenRefs> {\n const sessionDirs = new Set(await enumerateSessionDirs(paths));\n const brokenCreatedInSession = sessionDirs.has(task.created_in_session)\n ? null\n : (task.created_in_session as PrefixedId<\"ses\">);\n // Deduplicate broken entries so duplicate broken ids in a hand-edited task.md\n // surface as a single entry on the event payload.\n const seen = new Set<string>();\n const brokenLinkedSessions: PrefixedId<\"ses\">[] = [];\n for (const sid of task.linked_sessions) {\n if (sessionDirs.has(sid)) continue;\n if (seen.has(sid)) continue;\n seen.add(sid);\n brokenLinkedSessions.push(sid as PrefixedId<\"ses\">);\n }\n return { brokenCreatedInSession, brokenLinkedSessions };\n}\n\nfunction buildReconciledDoc(input: {\n currentDoc: TaskDocument;\n brokenCreatedInSession: PrefixedId<\"ses\"> | null;\n brokenLinkedSessions: ReadonlyArray<PrefixedId<\"ses\">>;\n reconcileSessionId: PrefixedId<\"ses\">;\n occurredAt: string;\n}): TaskDocument {\n const brokenSet = new Set<string>(input.brokenLinkedSessions);\n const filtered = input.currentDoc.task.task.linked_sessions.filter((sid) => !brokenSet.has(sid));\n const merged: PrefixedId<\"ses\">[] = [...filtered] as PrefixedId<\"ses\">[];\n if (!merged.includes(input.reconcileSessionId)) {\n merged.push(input.reconcileSessionId);\n }\n const nextCreatedInSession =\n input.brokenCreatedInSession !== null\n ? input.reconcileSessionId\n : input.currentDoc.task.task.created_in_session;\n const next: Task = {\n ...input.currentDoc.task,\n task: {\n ...input.currentDoc.task.task,\n created_in_session: nextCreatedInSession,\n updated_at: input.occurredAt,\n linked_sessions: merged,\n },\n };\n return { task: next, body: input.currentDoc.body };\n}\n\n/**\n * Audit a single task's session references. In `write: false` mode this is a\n * pure read-only report (no events, no task.md change). In `write: true` mode,\n * if any broken reference is found, mint an ad-hoc reconcile session, fire\n * `task_reconciled`, and overwrite task.md with the repaired refs.\n *\n * The broken `created_in_session` field is REPLACED with the new reconcile\n * session id rather than nulled out — `TaskSchema.created_in_session` is\n * non-nullable, so dropping it would leave the file schema-invalid.\n * The old broken id is preserved on the event payload via\n * `removed_created_in_session` for audit.\n *\n * Stages — failures after stage 5 surface a phase-specific\n * {@link TaskWriteAfterEventError} so the CLI can render a tailored \"do not\n * rerun\" hint:\n * 1. Boundary parse\n * 2. Read task.md AND snapshot its mtime/hash from the same raw bytes,\n * then detect broken refs (sharing the raw bytes here closes the\n * readTaskFile-then-snapshot race window).\n * 3. Early return when clean (no event fired, no overwrite)\n * 4. (no separate stage anymore — snapshot is taken at stage 2)\n * 5. Mint ad-hoc session + `task_reconciled` event (catch\n * `FailedToFinalizeError` → `phase: \"reconcile-finalize\"`)\n * 6. Re-snapshot task.md; if changed since stage 2 →\n * `phase: \"reconcile-concurrent\"`\n * 7. Overwrite task.md; failure → `phase: \"reconcile\"`\n */\nexport async function reconcileTask(\n paths: BasouPaths,\n manifest: Manifest,\n input: ReconcileTaskInput,\n): Promise<ReconcileResult> {\n TaskIdSchema.parse(input.taskId);\n\n // Per-task lock spans the entire reconcile window (= snapshot read,\n // ad-hoc session + event write, post-write snapshot probe, task.md\n // overwrite) so the mtime/hash invariant cannot be defeated by a\n // concurrent writer the helper would otherwise not notice.\n const handle = await acquireLock(paths, \"task\", input.taskId);\n try {\n return await reconcileTaskLocked(paths, manifest, input);\n } finally {\n await handle.release();\n }\n}\n\nasync function reconcileTaskLocked(\n paths: BasouPaths,\n manifest: Manifest,\n input: ReconcileTaskInput,\n): Promise<ReconcileResult> {\n const { doc: currentDoc, snapshot: preSnapshot } = await readTaskFileWithSnapshot(\n paths,\n input.taskId,\n );\n const { brokenCreatedInSession, brokenLinkedSessions } = await detectBrokenRefs(\n paths,\n currentDoc.task.task,\n );\n\n if (brokenCreatedInSession === null && brokenLinkedSessions.length === 0) {\n return {\n taskId: input.taskId,\n clean: true,\n brokenCreatedInSession: null,\n brokenLinkedSessions: [],\n reconcileSession: null,\n };\n }\n\n if (!input.write) {\n return {\n taskId: input.taskId,\n clean: false,\n brokenCreatedInSession,\n brokenLinkedSessions,\n reconcileSession: null,\n };\n }\n\n if (input._onPhaseCompleted !== undefined) {\n await input._onPhaseCompleted(\"phase-4-snapshot\");\n }\n\n let adHoc: Awaited<ReturnType<typeof createAdHocSessionWithEvent>>;\n try {\n adHoc = await createAdHocSessionWithEvent({\n paths,\n manifest,\n label: buildAdHocReconcileLabel(currentDoc.task.task.title),\n occurredAt: input.occurredAt,\n sessionSource: \"human\",\n workingDirectory: input.workingDirectory,\n invocation: {\n command: \"basou task reconcile\",\n args:\n (input.scope ?? \"single\") === \"single\"\n ? [\"--task\", input.taskId, \"--write\"]\n : [\"--write\"],\n },\n taskId: input.taskId,\n targetEventBuilders: [\n (sessionId, eventId) =>\n buildTaskReconciledEvent({\n eventId,\n sessionId,\n taskId: input.taskId,\n removedCreatedInSession: brokenCreatedInSession,\n createdInSessionReplacement: brokenCreatedInSession !== null ? sessionId : null,\n removedLinkedSessions: brokenLinkedSessions,\n occurredAt: input.occurredAt,\n }),\n ],\n });\n } catch (error: unknown) {\n if (error instanceof FailedToFinalizeError) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: error.targetEventIds[0] as PrefixedId<\"evt\">,\n sessionId: error.sessionId,\n phase: \"reconcile-finalize\",\n cause: error,\n });\n }\n throw error;\n }\n\n if (input._onPhaseCompleted !== undefined) {\n await input._onPhaseCompleted(\"phase-5-bulk-write\");\n }\n\n const anchorEventId = adHoc.targetEventIds[0] as PrefixedId<\"evt\">;\n\n const postSnapshot = await computeTaskMdSnapshot(paths, input.taskId);\n if (postSnapshot.mtimeMs !== preSnapshot.mtimeMs || postSnapshot.hash !== preSnapshot.hash) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: anchorEventId,\n sessionId: adHoc.sessionId,\n phase: \"reconcile-concurrent\",\n cause: new Error(\"task.md changed during reconcile\"),\n });\n }\n\n const repaired = buildReconciledDoc({\n currentDoc,\n brokenCreatedInSession,\n brokenLinkedSessions,\n reconcileSessionId: adHoc.sessionId,\n occurredAt: input.occurredAt,\n });\n try {\n await writeTaskFile(paths, input.taskId, repaired, { mode: \"overwrite\" });\n } catch (error: unknown) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: anchorEventId,\n sessionId: adHoc.sessionId,\n phase: \"reconcile\",\n cause: error,\n });\n }\n\n await safeUpdateTaskIndex(paths, {\n kind: \"update\",\n entry: buildTaskIndexEntry(repaired.task.task),\n });\n\n return {\n taskId: input.taskId,\n clean: false,\n brokenCreatedInSession,\n brokenLinkedSessions,\n reconcileSession: {\n sessionId: adHoc.sessionId,\n eventId: anchorEventId,\n },\n };\n}\n\n/**\n * Reconcile every task in `.basou/tasks/`. Continues on per-task failures so\n * an isolated {@link TaskWriteAfterEventError} does not stop the batch.\n * Malformed task.md files are skipped silently and excluded from `scanned`.\n */\nexport async function reconcileAllTasks(\n paths: BasouPaths,\n manifest: Manifest,\n input: ReconcileAllTasksInput,\n options: ReconcileAllTasksOptions = {},\n): Promise<ReconcileAllResult> {\n const taskIds = await enumerateTaskIds(paths);\n const results: ReconcileResult[] = [];\n const failed: ReconcileFailure[] = [];\n let scanned = 0;\n\n for (const id of taskIds) {\n // Probe readability first so malformed task.md does NOT inflate `scanned`\n // and never reaches the reconcile flow. The readTaskFile call is replayed\n // inside reconcileTask itself — re-reading is cheap and keeps reconcileTask's\n // contract single-purpose.\n try {\n await readTaskFile(paths, id);\n } catch {\n continue;\n }\n scanned += 1;\n\n try {\n const r = await reconcileTask(paths, manifest, {\n taskId: id as PrefixedId<\"task\">,\n occurredAt: input.occurredAt(),\n workingDirectory: input.workingDirectory,\n write: input.write,\n scope: \"all\",\n });\n if (options.includeClean === true || !r.clean) {\n results.push(r);\n }\n } catch (error: unknown) {\n const errorClass = error instanceof Error ? error.constructor.name : \"Error\";\n const phase = error instanceof TaskWriteAfterEventError ? error.phase : null;\n failed.push({\n taskId: id as PrefixedId<\"task\">,\n errorClass,\n phase,\n });\n }\n }\n\n return { results, failed, scanned };\n}\n\n// ============================================================================\n// Linkage refresh: events.jsonl → task.md `linked_sessions[]` forward sync\n// ============================================================================\n\n/**\n * Single-task linkage refresh result. In `write: false` mode this is a pure\n * dry-run report (no event, no task.md change); `addedLinkedSessions` and\n * `removedLinkedSessions` describe what would change. In `write: true` mode\n * the same fields describe what did change and `refreshSession` carries the\n * ad-hoc session + `task_linkage_refreshed` event ids that were minted.\n *\n * `clean === true` means the existing `task.md.linked_sessions[]` already\n * matches the union of `session.yaml.task_id` matches plus the anchor\n * (`created_in_session`) — no event fired, no overwrite.\n */\nexport type RefreshLinkageResult = {\n taskId: PrefixedId<\"task\">;\n clean: boolean;\n addedLinkedSessions: PrefixedId<\"ses\">[];\n removedLinkedSessions: PrefixedId<\"ses\">[];\n /** Number of entries in `linked_sessions[]` after the refresh would run. */\n finalCount: number;\n refreshSession: {\n sessionId: PrefixedId<\"ses\">;\n eventId: PrefixedId<\"evt\">;\n } | null;\n};\n\nexport type RefreshLinkageInput = {\n taskId: PrefixedId<\"task\">;\n occurredAt: string;\n workingDirectory: string;\n write: boolean;\n};\n\ntype DetectedLinkageDelta = {\n addedLinkedSessions: PrefixedId<\"ses\">[];\n removedLinkedSessions: PrefixedId<\"ses\">[];\n finalLinkedSessions: PrefixedId<\"ses\">[];\n};\n\n// Re-derive `linked_sessions[]` from the source of truth: every\n// `session.yaml` whose `task_id` points at this task, plus the\n// `created_in_session` anchor (which is preserved even if its session.yaml\n// no longer carries the task_id — that flavour of drift is the\n// `task reconcile` path's concern, not this one).\n//\n// `enumerateSessionDirs` already filters to dir-named-`ses_<ulid>` entries.\n// Sessions whose `session.yaml` is missing or schema-invalid are silently\n// skipped so a single broken session does not abort the workspace-wide\n// refresh; surfacing those is the responsibility of the session-integrity\n// tooling.\nasync function detectLinkageDelta(\n paths: BasouPaths,\n task: Task[\"task\"],\n): Promise<DetectedLinkageDelta> {\n const sessionIds = await enumerateSessionDirs(paths);\n const reachable = new Set<string>();\n for (const sid of sessionIds) {\n try {\n const doc = await readSessionYaml(paths, sid);\n if (doc.session.task_id === task.id) {\n reachable.add(sid);\n }\n } catch {\n // Missing / malformed session.yaml — skip. Surfacing those is the\n // responsibility of session-integrity tooling, not the linkage-refresh\n // path; a single corrupt session.yaml must not abort the workspace\n // scan.\n }\n }\n // The session ⇆ task anchor invariant\n // (`docs/spec/workspace.md#21-confirmed-invariants`) requires\n // `linked_sessions[]` to always contain `created_in_session`. Preserve it\n // here even if the session.yaml was hand-edited to clear task_id\n // (rare; handled by reconcile).\n const finalSet = new Set<string>(reachable);\n finalSet.add(task.created_in_session);\n\n const currentSet = new Set<string>(task.linked_sessions);\n const addedLinkedSessions: PrefixedId<\"ses\">[] = [];\n const removedLinkedSessions: PrefixedId<\"ses\">[] = [];\n for (const sid of finalSet) {\n if (!currentSet.has(sid)) addedLinkedSessions.push(sid as PrefixedId<\"ses\">);\n }\n for (const sid of currentSet) {\n if (!finalSet.has(sid)) removedLinkedSessions.push(sid as PrefixedId<\"ses\">);\n }\n // Stable ordering: ULID-ascending so two runs against the same workspace\n // produce identical event payloads (matters for replay determinism).\n addedLinkedSessions.sort();\n removedLinkedSessions.sort();\n const finalLinkedSessions = [...finalSet].sort() as PrefixedId<\"ses\">[];\n return { addedLinkedSessions, removedLinkedSessions, finalLinkedSessions };\n}\n\nfunction buildRefreshedDoc(input: {\n currentDoc: TaskDocument;\n finalLinkedSessions: ReadonlyArray<PrefixedId<\"ses\">>;\n refreshSessionId: PrefixedId<\"ses\">;\n occurredAt: string;\n}): TaskDocument {\n // Include the refresh session itself in `linked_sessions` (it is the\n // session that wrote the `task_linkage_refreshed` event, so it is by\n // definition linked). Deduplicate via a Set in case the ad-hoc session id\n // somehow already shows up in finalLinkedSessions (defensive).\n const merged = new Set<string>(input.finalLinkedSessions);\n merged.add(input.refreshSessionId);\n const linked = [...merged].sort() as PrefixedId<\"ses\">[];\n const next: Task = {\n ...input.currentDoc.task,\n task: {\n ...input.currentDoc.task.task,\n updated_at: input.occurredAt,\n linked_sessions: linked,\n },\n };\n return { task: next, body: input.currentDoc.body };\n}\n\n/**\n * Refresh `task.md.linked_sessions[]` so it matches the union of\n * `session.yaml.task_id` references in the workspace plus the\n * `created_in_session` anchor. In `write: false` this is a pure read-only\n * report; in `write: true` the diff is recorded as a\n * `task_linkage_refreshed` event inside a fresh ad-hoc session and the\n * task.md is overwritten with the new snapshot.\n *\n * Stages mirror `reconcileTask` so the operator gets the same\n * \"do-not-rerun\" hint shape on partial failure:\n * 1. Boundary parse\n * 2. Read task.md AND snapshot its mtime/hash from the same raw bytes\n * 3. Detect linkage delta (= scan workspace session.yaml)\n * 4. Early return when clean\n * 5. Mint ad-hoc session + `task_linkage_refreshed` event (catch\n * `FailedToFinalizeError` → `phase: \"linkage-refresh-finalize\"`)\n * 6. Re-snapshot task.md; if changed since stage 2 →\n * `phase: \"linkage-refresh-concurrent\"`\n * 7. Overwrite task.md; failure → `phase: \"linkage-refresh\"`\n *\n * The refresh event is distinct from `task_reconciled` (= broken-ref\n * cleanup, `.strict()` with broken-ref-specific fields) so each event\n * carries a single, focused audit story. Reusing `task_reconciled` here\n * would either redefine its semantics or require widening its strict\n * schema, both of which break replay determinism for older events.\n */\nexport async function refreshTaskLinkedSessions(\n paths: BasouPaths,\n manifest: Manifest,\n input: RefreshLinkageInput,\n): Promise<RefreshLinkageResult> {\n TaskIdSchema.parse(input.taskId);\n\n // Per-task lock spans the entire refresh window so the snapshot taken in\n // stage 2 cannot be invalidated by another writer the helper does not see;\n // the stage 6 mtime/hash probe still acts as a belt-and-braces guard\n // against drift from non-locked code paths (e.g. an external `vi` edit).\n const handle = await acquireLock(paths, \"task\", input.taskId);\n try {\n return await refreshTaskLinkedSessionsLocked(paths, manifest, input);\n } finally {\n await handle.release();\n }\n}\n\nasync function refreshTaskLinkedSessionsLocked(\n paths: BasouPaths,\n manifest: Manifest,\n input: RefreshLinkageInput,\n): Promise<RefreshLinkageResult> {\n const { doc: currentDoc, snapshot: preSnapshot } = await readTaskFileWithSnapshot(\n paths,\n input.taskId,\n );\n const { addedLinkedSessions, removedLinkedSessions, finalLinkedSessions } =\n await detectLinkageDelta(paths, currentDoc.task.task);\n\n if (addedLinkedSessions.length === 0 && removedLinkedSessions.length === 0) {\n return {\n taskId: input.taskId,\n clean: true,\n addedLinkedSessions: [],\n removedLinkedSessions: [],\n finalCount: finalLinkedSessions.length,\n refreshSession: null,\n };\n }\n\n if (!input.write) {\n return {\n taskId: input.taskId,\n clean: false,\n addedLinkedSessions,\n removedLinkedSessions,\n finalCount: finalLinkedSessions.length,\n refreshSession: null,\n };\n }\n\n // The refresh session is itself a new linked entry; account for it on\n // the event payload's `final_count` so the audit number matches the\n // post-write task.md. This is a +1 over the workspace-scan count.\n const finalCountWithRefreshSession = finalLinkedSessions.length + 1;\n\n let adHoc: Awaited<ReturnType<typeof createAdHocSessionWithEvent>>;\n try {\n adHoc = await createAdHocSessionWithEvent({\n paths,\n manifest,\n label: buildAdHocRefreshLinkageLabel(currentDoc.task.task.title),\n occurredAt: input.occurredAt,\n sessionSource: \"human\",\n workingDirectory: input.workingDirectory,\n invocation: {\n command: \"basou task refresh-linkage\",\n args: [input.taskId, \"--write\"],\n },\n taskId: input.taskId,\n targetEventBuilders: [\n (sessionId, eventId) =>\n buildTaskLinkageRefreshedEvent({\n eventId,\n sessionId,\n taskId: input.taskId,\n addedLinkedSessions,\n removedLinkedSessions,\n finalCount: finalCountWithRefreshSession,\n occurredAt: input.occurredAt,\n }),\n ],\n });\n } catch (error: unknown) {\n if (error instanceof FailedToFinalizeError) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: error.targetEventIds[0] as PrefixedId<\"evt\">,\n sessionId: error.sessionId,\n phase: \"linkage-refresh-finalize\",\n cause: error,\n });\n }\n throw error;\n }\n\n const anchorEventId = adHoc.targetEventIds[0] as PrefixedId<\"evt\">;\n\n const postSnapshot = await computeTaskMdSnapshot(paths, input.taskId);\n if (postSnapshot.mtimeMs !== preSnapshot.mtimeMs || postSnapshot.hash !== preSnapshot.hash) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: anchorEventId,\n sessionId: adHoc.sessionId,\n phase: \"linkage-refresh-concurrent\",\n cause: new Error(\"task.md changed during linkage refresh\"),\n });\n }\n\n const refreshed = buildRefreshedDoc({\n currentDoc,\n finalLinkedSessions,\n refreshSessionId: adHoc.sessionId,\n occurredAt: input.occurredAt,\n });\n try {\n await writeTaskFile(paths, input.taskId, refreshed, { mode: \"overwrite\" });\n } catch (error: unknown) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: anchorEventId,\n sessionId: adHoc.sessionId,\n phase: \"linkage-refresh\",\n cause: error,\n });\n }\n\n await safeUpdateTaskIndex(paths, {\n kind: \"update\",\n entry: buildTaskIndexEntry(refreshed.task.task),\n });\n\n return {\n taskId: input.taskId,\n clean: false,\n addedLinkedSessions,\n removedLinkedSessions,\n finalCount: finalCountWithRefreshSession,\n refreshSession: {\n sessionId: adHoc.sessionId,\n eventId: anchorEventId,\n },\n };\n}\n\n// ============================================================================\n// editTask — field-level update (no event for pure title edits)\n// ============================================================================\n\nexport type EditTaskInput = {\n paths: BasouPaths;\n taskId: PrefixedId<\"task\">;\n /** New title; rejected when empty. Undefined leaves the field unchanged. */\n title?: string;\n /**\n * New status; routed through transition rules so the call rejects\n * invalid edges (e.g. `done -> planned`). Undefined leaves the field\n * unchanged.\n */\n newStatus?: TaskStatus;\n occurredAt: string;\n /**\n * Required when {@link newStatus} is provided — the status change fires\n * a `task_status_changed` event in a fresh ad-hoc session, which needs\n * a Manifest to seed the new session record. Title-only edits ignore\n * this field.\n */\n manifest?: Manifest;\n /** Working directory for the ad-hoc status-change session. */\n workingDirectory?: string;\n};\n\nexport type EditTaskResult = {\n taskId: PrefixedId<\"task\">;\n titleUpdated: boolean;\n statusUpdated: boolean;\n /** When {@link statusUpdated} is true, the previous status before the edit. */\n previousStatus: TaskStatus | null;\n /** When {@link statusUpdated} is true, the new status. */\n newStatus: TaskStatus | null;\n /** ad-hoc session minted when status was changed; null for title-only edits. */\n statusChangeSession: {\n sessionId: PrefixedId<\"ses\">;\n eventId: PrefixedId<\"evt\">;\n } | null;\n};\n\n/**\n * Update one or both of the user-editable fields on a task.md.\n *\n * - `title`: in-place overwrite of `task.md` only. v0.1 does not emit a\n * `task_title_changed` event — title changes are storage-level metadata\n * maintenance, not part of the audit trail.\n * - `newStatus`: routed through {@link updateTaskStatusWithEvent} so the\n * ALLOWED_TRANSITIONS gate is honored and a `task_status_changed` event is\n * appended to the audit trail.\n *\n * When both are supplied the status change runs first (= event committed)\n * and then the title overwrite runs against the freshly updated task.md\n * (= same `updated_at` from the status change). A failure of the\n * subsequent title overwrite leaves the status change committed; the\n * status-change side of an edit is the only side with an event, so the\n * audit trail is consistent regardless.\n */\nexport async function editTask(input: EditTaskInput): Promise<EditTaskResult> {\n TaskIdSchema.parse(input.taskId);\n if (input.title === undefined && input.newStatus === undefined) {\n throw new Error(\"Nothing to edit: provide --title or --status\");\n }\n if (input.title !== undefined) {\n TaskTitleSchema.parse(input.title);\n }\n\n let statusUpdated = false;\n let previousStatus: TaskStatus | null = null;\n let newStatus: TaskStatus | null = null;\n let statusChangeSession: EditTaskResult[\"statusChangeSession\"] = null;\n\n // Stage 1: status change (if any). Failure here exits with the existing\n // transition / not-found errors and leaves task.md untouched.\n if (input.newStatus !== undefined) {\n if (input.manifest === undefined || input.workingDirectory === undefined) {\n throw new Error(\"editTask requires manifest + workingDirectory when newStatus is supplied\");\n }\n const result = await updateTaskStatusWithEvent({\n mode: \"ad-hoc\",\n paths: input.paths,\n manifest: input.manifest,\n occurredAt: input.occurredAt,\n taskId: input.taskId,\n newStatus: input.newStatus,\n workingDirectory: input.workingDirectory,\n });\n statusUpdated = true;\n previousStatus = result.previousStatus;\n newStatus = result.newStatus;\n statusChangeSession = { sessionId: result.sessionId, eventId: result.eventId };\n }\n\n // Stage 2: title overwrite (if any). Re-read so the status change above\n // (which updated linked_sessions / updated_at) is preserved. The lock is\n // taken HERE (not around the whole function) because stage 1 calls\n // `updateTaskStatusWithEvent` which acquires the same per-task lock\n // internally — wrapping both stages in one outer lock would deadlock on\n // its own helper. Each stage being independently atomic is the operator-\n // visible invariant we care about, not stage-1-and-2 atomicity.\n let titleUpdated = false;\n if (input.title !== undefined) {\n const handle = await acquireLock(input.paths, \"task\", input.taskId);\n try {\n const doc = await readTaskFile(input.paths, input.taskId);\n if (doc.task.task.title !== input.title) {\n const next: Task = {\n ...doc.task,\n task: {\n ...doc.task.task,\n title: input.title,\n updated_at: input.occurredAt,\n },\n };\n await writeTaskFile(\n input.paths,\n input.taskId,\n { task: next, body: doc.body },\n { mode: \"overwrite\" },\n );\n await safeUpdateTaskIndex(input.paths, {\n kind: \"update\",\n entry: buildTaskIndexEntry(next.task),\n });\n titleUpdated = true;\n }\n } finally {\n await handle.release();\n }\n }\n\n return {\n taskId: input.taskId,\n titleUpdated,\n statusUpdated,\n previousStatus,\n newStatus,\n statusChangeSession,\n };\n}\n\n// ============================================================================\n// deleteTask — destructive removal with audit event\n// ============================================================================\n\nexport type DeleteTaskInput = {\n paths: BasouPaths;\n manifest: Manifest;\n taskId: PrefixedId<\"task\">;\n occurredAt: string;\n workingDirectory: string;\n};\n\nexport type DeleteTaskResult = {\n taskId: PrefixedId<\"task\">;\n title: string;\n sessionId: PrefixedId<\"ses\">;\n eventId: PrefixedId<\"evt\">;\n};\n\n/**\n * Hard-delete a task.md file with a `task_deleted` audit event.\n *\n * Sequence:\n * 1. Read task.md to capture the current title (which goes onto the\n * event payload so the audit record is self-describing even after\n * the file is gone).\n * 2. Mint an ad-hoc session, fire `task_deleted` as the target event.\n * The session's `task_id` is intentionally NOT pinned to the\n * to-be-deleted task — otherwise the audit session would carry a\n * broken reference the moment we unlink the file.\n * 3. Unlink `<paths.tasks>/<task_id>.md`.\n *\n * Failure of step 3 after the event is committed surfaces as a\n * {@link TaskWriteAfterEventError} with `phase: \"delete\"`; the operator\n * is told the event is durable but task.md still exists, and that a\n * manual `rm` (or a rerun) is required.\n *\n * v0.1 contract: no tombstone, no recovery. Restoring a deleted task is\n * not supported; the event payload (`task_id` + `title`) is the only\n * persistent record after the unlink succeeds.\n */\nexport async function deleteTask(input: DeleteTaskInput): Promise<DeleteTaskResult> {\n TaskIdSchema.parse(input.taskId);\n\n // Per-task lock keeps the read → audit event → unlink chain free of a\n // concurrent writer that could otherwise observe task.md after we read it\n // and before we delete it (e.g. another CLI running `task status`).\n const handle = await acquireLock(input.paths, \"task\", input.taskId);\n try {\n return await deleteTaskLocked(input);\n } finally {\n await handle.release();\n }\n}\n\nasync function deleteTaskLocked(input: DeleteTaskInput): Promise<DeleteTaskResult> {\n // Stage 1: capture the current title before mint.\n const doc = await readTaskFile(input.paths, input.taskId);\n const title = doc.task.task.title;\n\n // Stage 2: fire the audit event. NOTE we do NOT pass `taskId` to the\n // ad-hoc session — pinning the session to a task that is about to vanish\n // would create a guaranteed broken reference on session.yaml.task_id.\n const adHoc = await createAdHocSessionWithEvent({\n paths: input.paths,\n manifest: input.manifest,\n label: buildAdHocDeleteLabel(title),\n occurredAt: input.occurredAt,\n sessionSource: \"human\",\n workingDirectory: input.workingDirectory,\n invocation: {\n command: \"basou task delete\",\n args: [input.taskId, \"--yes\"],\n },\n targetEventBuilders: [\n (sessionId, eventId) =>\n buildTaskDeletedEvent({\n eventId,\n sessionId,\n taskId: input.taskId,\n title,\n occurredAt: input.occurredAt,\n }),\n ],\n });\n const eventId = adHoc.targetEventIds[0] as PrefixedId<\"evt\">;\n\n // Stage 3: unlink the file.\n try {\n await unlink(join(input.paths.tasks, `${input.taskId}.md`));\n } catch (error: unknown) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId,\n sessionId: adHoc.sessionId,\n phase: \"delete\",\n cause: error,\n });\n }\n\n await safeUpdateTaskIndex(input.paths, { kind: \"remove\", id: input.taskId });\n\n return {\n taskId: input.taskId,\n title,\n sessionId: adHoc.sessionId,\n eventId,\n };\n}\n\n// ============================================================================\n// archiveTask — move main/<id>.md to archive/<id>.md with audit event\n// ============================================================================\n\nexport type ArchiveTaskInput = {\n paths: BasouPaths;\n manifest: Manifest;\n taskId: PrefixedId<\"task\">;\n occurredAt: string;\n workingDirectory: string;\n};\n\nexport type ArchiveTaskResult = {\n taskId: PrefixedId<\"task\">;\n title: string;\n sessionId: PrefixedId<\"ses\">;\n eventId: PrefixedId<\"evt\">;\n};\n\n/**\n * Move a task.md file from `<paths.tasks>/<id>.md` to\n * `<paths.tasks>/archive/<id>.md` with a `task_archived` audit event.\n *\n * Sequence:\n * 1. Read task.md to capture the current title and existing content.\n * 2. Mint an ad-hoc session, fire `task_archived` as the target event.\n * The session's `task_id` IS pinned to the archived task — unlike\n * `task_deleted`, the task continues to exist (just at a new path),\n * so the session-task linkage stays a valid forward reference.\n * 3. Append the audit session to the task's `linked_sessions[]` and\n * overwrite the source task.md so the snapshot reflects the archive\n * session before the move.\n * 4. Ensure the archive directory exists.\n * 5. Rename main/<id>.md to archive/<id>.md (= atomic on the same fs).\n *\n * Failure modes after step 2 surface as\n * {@link TaskWriteAfterEventError} with `phase: \"archive\"`; the operator\n * is told the event is durable but the on-disk move is incomplete and\n * must be resolved manually (typically by rerunning `task archive`).\n */\nexport async function archiveTask(input: ArchiveTaskInput): Promise<ArchiveTaskResult> {\n TaskIdSchema.parse(input.taskId);\n\n // Per-task lock spans read → audit event → task.md overwrite → rename so\n // a concurrent writer cannot interleave between the linked_sessions\n // append and the move into archive/.\n const handle = await acquireLock(input.paths, \"task\", input.taskId);\n try {\n return await archiveTaskLocked(input);\n } finally {\n await handle.release();\n }\n}\n\nasync function archiveTaskLocked(input: ArchiveTaskInput): Promise<ArchiveTaskResult> {\n const doc = await readTaskFile(input.paths, input.taskId);\n const title = doc.task.task.title;\n\n const adHoc = await createAdHocSessionWithEvent({\n paths: input.paths,\n manifest: input.manifest,\n label: buildAdHocArchiveLabel(title),\n occurredAt: input.occurredAt,\n sessionSource: \"human\",\n workingDirectory: input.workingDirectory,\n invocation: {\n command: \"basou task archive\",\n args: [input.taskId, \"--yes\"],\n },\n taskId: input.taskId,\n targetEventBuilders: [\n (sessionId, eventId) =>\n buildTaskArchivedEvent({\n eventId,\n sessionId,\n taskId: input.taskId,\n title,\n occurredAt: input.occurredAt,\n }),\n ],\n });\n const eventId = adHoc.targetEventIds[0] as PrefixedId<\"evt\">;\n\n // Stage 3-5 share the same recovery contract: any failure surfaces as\n // phase \"archive\" so the operator gets a uniform \"rerun task archive\"\n // hint. Specific failure cases:\n // - 3: writeTaskFile (overwrite) — fs/yaml-serialize error\n // - 4: mkdir of archive dir — usually EACCES\n // - 5: rename across the same fs — EEXIST when archive/<id>.md is\n // already there, EACCES, or rare ENOSPC\n try {\n const linked = doc.task.task.linked_sessions;\n const merged = linked.includes(adHoc.sessionId) ? linked : [...linked, adHoc.sessionId];\n const next: Task = {\n ...doc.task,\n task: {\n ...doc.task.task,\n updated_at: input.occurredAt,\n linked_sessions: merged,\n },\n };\n await writeTaskFile(\n input.paths,\n input.taskId,\n { task: next, body: doc.body },\n { mode: \"overwrite\" },\n );\n\n await mkdir(archiveTasksDir(input.paths), { recursive: true });\n await rename(\n join(input.paths.tasks, `${input.taskId}.md`),\n join(archiveTasksDir(input.paths), `${input.taskId}.md`),\n );\n } catch (error: unknown) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId,\n sessionId: adHoc.sessionId,\n phase: \"archive\",\n cause: error,\n });\n }\n\n // Archived tasks live under tasks/archive/<id>.md, which enumerateTaskIds\n // ignores. Remove the entry from the active index so `task list` matches\n // disk reality.\n await safeUpdateTaskIndex(input.paths, { kind: \"remove\", id: input.taskId });\n\n return {\n taskId: input.taskId,\n title,\n sessionId: adHoc.sessionId,\n eventId,\n };\n}\n","import { z } from \"zod\";\nimport {\n IsoTimestampSchema,\n SchemaVersionSchema,\n SessionIdSchema,\n TaskIdSchema,\n WorkspaceIdSchema,\n} from \"./shared.schema.js\";\n\n/**\n * Task lifecycle states.\n *\n * The storage layer's `ALLOWED_TRANSITIONS` map (= source of truth in\n * `tasks.ts`) is the authoritative graph; the comment below is a snapshot.\n * `planned` reaches `done` / `cancelled` directly so tasks completed (or\n * abandoned) outside an explicit in-progress phase can close in a single\n * CLI call:\n *\n * planned → {in_progress | done | cancelled}\n * in_progress → {done | cancelled}\n * done / cancelled = terminal\n *\n * Self-edges are rejected so the audit trail stays monotonic.\n */\nexport const TaskStatusSchema = z.enum([\"planned\", \"in_progress\", \"done\", \"cancelled\"]);\n/** Inferred runtime type for {@link TaskStatusSchema}. */\nexport type TaskStatus = z.infer<typeof TaskStatusSchema>;\n\nconst TaskInnerSchema = z.object({\n id: TaskIdSchema,\n title: z.string().min(1),\n label: z.string().min(1).optional(),\n status: TaskStatusSchema,\n created_at: IsoTimestampSchema,\n updated_at: IsoTimestampSchema,\n workspace_id: WorkspaceIdSchema,\n /**\n * Session id that anchors this task. For freshly created tasks it is the\n * session that wrote the `task_created` event (= ad-hoc reconcile target\n * for ad-hoc paths, or the target session id for attach paths). After\n * `basou task reconcile --write` repairs a broken anchor the\n * value is replaced with the ad-hoc reconcile session id; the old broken\n * session_id is preserved on the `task_reconciled` event payload via\n * `removed_created_in_session` for audit. So this field always names a\n * reachable session, even after the original anchor is gone.\n */\n created_in_session: SessionIdSchema,\n /**\n * Snapshot of sessions linked to this task. The events.jsonl history is\n * the source of truth (see\n * `docs/spec/generated-markdown.md#105-decisionsmd-generation-principle`);\n * this field is maintained as a UX-only cache so editors can read the\n * task.md and immediately see related sessions. Defaults to `[]` for\n * backward compatibility.\n */\n linked_sessions: z.array(SessionIdSchema).default([]),\n});\n\n/**\n * Schema for the YAML front matter of `.basou/tasks/<task_id>.md`.\n *\n * The markdown body after the front matter is intentionally NOT modelled\n * here — it is free-form user-edited content. The storage layer splits\n * the file into `task` (this schema) and `body` (the trailing string).\n */\nexport const TaskSchema = z.object({\n schema_version: SchemaVersionSchema,\n task: TaskInnerSchema,\n});\n/** Inferred runtime type for {@link TaskSchema}. */\nexport type Task = z.infer<typeof TaskSchema>;\n","import { mkdir, rm } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { appendEvent, writeEventsBulk } from \"../events/event-writer.js\";\nimport { type PrefixedId, prefixedUlid } from \"../ids/ulid.js\";\nimport { findErrorCode } from \"../lib/error-codes.js\";\nimport { sanitizeWorkingDirectory } from \"../lib/path-sanitizer.js\";\nimport type { Event } from \"../schemas/event.schema.js\";\nimport type { Manifest } from \"../schemas/manifest.schema.js\";\nimport {\n type Session,\n SessionSchema,\n type SessionSourceKind,\n SessionSourceKindSchema,\n type SessionStatus,\n} from \"../schemas/session.schema.js\";\nimport { SessionIdSchema } from \"../schemas/shared.schema.js\";\nimport type { BasouPaths } from \"./basou-dir.js\";\nimport { readSessionYaml } from \"./sessions.js\";\nimport { linkYamlFile, overwriteYamlFile } from \"./yaml-store.js\";\n\n// ============================================================================\n// Finalization-failure error\n// ============================================================================\n\n/**\n * Thrown when the ad-hoc session was fully written to disk (4 lifecycle\n * events + N target events plus the initial `session.yaml`) but the final\n * `session.yaml` update to status `completed` failed. The caller can read\n * `sessionId` / `targetEventIds` to emit a retry-duplicate-prevention\n * warning, since the target events themselves are already persisted in\n * `events.jsonl`.\n *\n * `targetEventIds` is an array because a single ad-hoc session may carry\n * multiple target events (e.g. `task new --status done` fires both\n * `task_created` and `task_status_changed`). Callers that need a single\n * anchor id should use `targetEventIds[0]`, which by convention is the\n * primary event for the operation.\n */\nexport class FailedToFinalizeError extends Error {\n readonly sessionId: PrefixedId<\"ses\">;\n readonly targetEventIds: ReadonlyArray<PrefixedId<\"evt\">>;\n\n constructor(\n sessionId: PrefixedId<\"ses\">,\n targetEventIds: ReadonlyArray<PrefixedId<\"evt\">>,\n cause: unknown,\n ) {\n super(\"Failed to finalize ad-hoc session\", { cause });\n this.name = \"FailedToFinalizeError\";\n if (targetEventIds.length === 0) {\n // Defensive guard for direct (non-orchestrator) constructors. The\n // orchestrator already rejects an empty `targetEventBuilders` array\n // before any ID minting, but `FailedToFinalizeError` is a public\n // exported class and `error-render.ts` reads `targetEventIds[0]` as\n // the operator-facing anchor — an empty array there would surface as\n // `\"Recorded undefined ...\"`.\n throw new Error(\"FailedToFinalizeError requires at least one target event id\");\n }\n this.sessionId = sessionId;\n this.targetEventIds = targetEventIds;\n }\n}\n\n// ============================================================================\n// Ad-hoc session path\n// ============================================================================\n\nexport type CreateAdHocSessionInput = {\n paths: BasouPaths;\n manifest: Manifest;\n /** Pre-built session label (caller is responsible for truncation). */\n label: string;\n /** ISO timestamp shared across the 5 lifecycle/target events. */\n occurredAt: string;\n sessionSource: SessionSourceKind;\n workingDirectory: string;\n invocation: { command: string; args: string[] };\n /**\n * Optional task id to link this ad-hoc session to. When provided, both the\n * initial and the final `session.yaml` writes embed `task_id` so the\n * single-session-to-single-task invariant (see\n * `docs/spec/workspace.md#21-confirmed-invariants`) holds for task-flavoured\n * ad-hoc paths (`basou task new` / `task status` without `--session`).\n * Defaults to `null` so existing callers (decision / note) are unchanged.\n */\n taskId?: PrefixedId<\"task\">;\n /**\n * Builds the variant-specific target events. Each builder receives the\n * freshly minted session id and a freshly minted event id (one per\n * builder) so callers can fill in cross-reference fields (`decision_id`,\n * `body`, ...) without owning ID generation.\n *\n * The most common case is a single-element array (`[builder]`) for the\n * one-target-event flows (`basou decision record`, `basou session note`,\n * `basou task new --status planned`, `basou task status`,\n * `basou task reconcile`). Two-element arrays are used by\n * `basou task new --status done|cancelled` to emit `task_created` plus\n * an immediate `task_status_changed` in the same atomic bulk write.\n *\n * Must be non-empty; an empty array is rejected at the start of\n * {@link createAdHocSessionWithEvent}.\n */\n targetEventBuilders: ReadonlyArray<\n (sessionId: PrefixedId<\"ses\">, eventId: PrefixedId<\"evt\">) => Event\n >;\n};\n\nexport type CreateAdHocSessionResult = {\n sessionId: PrefixedId<\"ses\">;\n /**\n * Target event IDs in the order their builders were supplied. Length\n * equals `input.targetEventBuilders.length`. Callers that conceptually\n * have a single anchor event should use `targetEventIds[0]`.\n */\n targetEventIds: PrefixedId<\"evt\">[];\n /**\n * Lifecycle event IDs in chronological order:\n * `[started, status→running, status→completed, ended]`.\n * Target event IDs are reported separately in {@link targetEventIds}.\n */\n lifecycleEventIds: PrefixedId<\"evt\">[];\n};\n\n/**\n * Atomically create a fresh ad-hoc session that produces one or more target\n * events then immediately closes itself. The session lifecycle\n * (`initialized → running → completed`, see\n * `docs/spec/terminal-and-import.md#62-transition-diagram`) is honored:\n * `4 + N` events are\n * written in one bulk atomic pass (where N = number of target builders) and\n * `session.yaml` is written twice (`initialized` → `completed`).\n *\n * The single-target case (N = 1) covers `basou decision record`,\n * `basou session note`, `basou task new --status planned|in_progress`,\n * `basou task status`, and `basou task reconcile`. The two-target case\n * (N = 2) covers `basou task new --status done|cancelled` which fires\n * `task_created` followed immediately by `task_status_changed (planned → terminal)`\n * so the audit trail captures the implicit transition.\n *\n * Failures during `mkdir`, the initial `session.yaml` write, or the bulk\n * `events.jsonl` write trigger a best-effort `rm -rf` of the session\n * directory so partial ad-hoc sessions do not pollute the workspace.\n *\n * A failure on the final `session.yaml` status update is fatal but the\n * session directory is NOT cleaned up — `events.jsonl` is consistent and\n * carries the full lifecycle trail, so callers can reconcile manually. The\n * thrown {@link FailedToFinalizeError} carries the `sessionId` and\n * `targetEventIds` so the CLI layer can warn the user not to re-run the\n * command and duplicate the target events.\n *\n * Direct (non-CLI) callers are self-defended by zod boundary parses on\n * `sessionSource` and the initial session record.\n */\nexport async function createAdHocSessionWithEvent(\n input: CreateAdHocSessionInput,\n): Promise<CreateAdHocSessionResult> {\n // 1. core boundary parse — direct callers may pass arbitrary strings.\n SessionSourceKindSchema.parse(input.sessionSource);\n if (input.targetEventBuilders.length === 0) {\n throw new Error(\"Ad-hoc session requires at least one target event builder\");\n }\n\n // 2. ID minting. One target event id per builder; lifecycle ids are fixed.\n const sessionId = prefixedUlid(\"ses\");\n const startedEventId = prefixedUlid(\"evt\");\n const statusToRunningEventId = prefixedUlid(\"evt\");\n const targetEventIds = input.targetEventBuilders.map(() => prefixedUlid(\"evt\"));\n const statusToCompletedEventId = prefixedUlid(\"evt\");\n const endedEventId = prefixedUlid(\"evt\");\n\n // 3. Build the initial session record (status=initialized) and validate it\n // so a malformed input shape fails fast before any disk write.\n const initialSession: Session = SessionSchema.parse(\n buildInitialSession({\n sessionId,\n workspaceId: input.manifest.workspace.id,\n sourceKind: input.sessionSource,\n startedAt: input.occurredAt,\n label: input.label,\n workingDirectory: input.workingDirectory,\n invocation: input.invocation,\n taskId: input.taskId ?? null,\n }),\n );\n\n // 4. Create the session directory (recursive=true so a stripped-down\n // workspace with `.basou/sessions` missing still recovers).\n const sessionDir = join(input.paths.sessions, sessionId);\n try {\n await mkdir(sessionDir, { recursive: true });\n } catch (error: unknown) {\n throw new Error(\"Failed to create session directory\", { cause: error });\n }\n\n // 5. Initial session.yaml write (status=initialized).\n const sessionYamlPath = join(sessionDir, \"session.yaml\");\n try {\n await linkYamlFile(sessionYamlPath, initialSession);\n } catch (error: unknown) {\n await rm(sessionDir, { recursive: true, force: true }).catch(() => undefined);\n if (findErrorCode(error, \"EEXIST\")) {\n throw new Error(\"Session directory collision (retry the command)\", {\n cause: error,\n });\n }\n throw error;\n }\n\n // 6. events.jsonl bulk write — five events written atomically in a single\n // tmp+rename pass. A failure here removes the session directory so no\n // partial state survives (status=initialized + no events is not visible\n // in `basou session list`).\n try {\n const targetEvents: Event[] = input.targetEventBuilders.map((build, index) => {\n const targetEventId = targetEventIds[index] as PrefixedId<\"evt\">;\n return assertTargetEventIdentity(build(sessionId, targetEventId), sessionId, targetEventId);\n });\n const events: Event[] = [\n {\n schema_version: \"0.1.0\",\n id: startedEventId,\n session_id: sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"session_started\",\n },\n {\n schema_version: \"0.1.0\",\n id: statusToRunningEventId,\n session_id: sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"session_status_changed\",\n from: \"initialized\",\n to: \"running\",\n },\n ...targetEvents,\n {\n schema_version: \"0.1.0\",\n id: statusToCompletedEventId,\n session_id: sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"session_status_changed\",\n from: \"running\",\n to: \"completed\",\n },\n {\n schema_version: \"0.1.0\",\n id: endedEventId,\n session_id: sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"session_ended\",\n exit_code: 0,\n },\n ];\n await writeEventsBulk(sessionDir, events);\n } catch (error: unknown) {\n await rm(sessionDir, { recursive: true, force: true }).catch(() => undefined);\n throw error;\n }\n\n // 7. Finalize: overwrite session.yaml with status=completed + ended_at +\n // invocation.exit_code=0. Failure is fatal but events.jsonl is already\n // complete, so the directory is intentionally NOT removed — the caller\n // surfaces the partial state via FailedToFinalizeError.\n try {\n const finalSession: Session = SessionSchema.parse({\n ...initialSession,\n session: {\n ...initialSession.session,\n status: \"completed\" satisfies SessionStatus,\n ended_at: input.occurredAt,\n invocation: { ...initialSession.session.invocation, exit_code: 0 },\n },\n });\n await overwriteYamlFile(sessionYamlPath, finalSession);\n } catch (error: unknown) {\n throw new FailedToFinalizeError(sessionId, targetEventIds, error);\n }\n\n return {\n sessionId,\n targetEventIds,\n lifecycleEventIds: [\n startedEventId,\n statusToRunningEventId,\n statusToCompletedEventId,\n endedEventId,\n ],\n };\n}\n\nfunction buildInitialSession(input: {\n sessionId: PrefixedId<\"ses\">;\n workspaceId: PrefixedId<\"ws\">;\n sourceKind: SessionSourceKind;\n startedAt: string;\n label: string;\n workingDirectory: string;\n invocation: { command: string; args: string[] };\n taskId: PrefixedId<\"task\"> | null;\n}): Session {\n return {\n schema_version: \"0.1.0\",\n session: {\n id: input.sessionId,\n label: input.label,\n task_id: input.taskId,\n workspace_id: input.workspaceId,\n source: { kind: input.sourceKind, version: \"0.1.0\" },\n started_at: input.startedAt,\n status: \"initialized\",\n working_directory: sanitizeWorkingDirectory(input.workingDirectory, { homedir: homedir() }),\n invocation: { ...input.invocation, exit_code: null },\n related_files: [],\n events_log: \"events.jsonl\",\n },\n };\n}\n\n// ============================================================================\n// Attach path\n// ============================================================================\n\nexport type AttachableStatus = \"initialized\" | \"running\" | \"waiting_approval\";\n\nconst DEFAULT_ATTACHABLE_STATUSES: ReadonlySet<AttachableStatus> = new Set<AttachableStatus>([\n \"initialized\",\n \"running\",\n \"waiting_approval\",\n]);\n\nexport type AppendEventToExistingInput = {\n paths: BasouPaths;\n /** Already resolved via `resolveSessionId`; parsed at boundary again. */\n sessionId: PrefixedId<\"ses\">;\n attachableStatuses?: ReadonlySet<AttachableStatus>;\n eventBuilder: (eventId: PrefixedId<\"evt\">) => Event;\n};\n\nexport type AppendEventToExistingResult = {\n eventId: PrefixedId<\"evt\">;\n sessionStatus: SessionStatus;\n};\n\n/**\n * Read `session.yaml`, verify the session is in an attachable state, and\n * append a single event to its `events.jsonl`. `session.yaml` is NOT modified\n * so the caller can safely append `decision_recorded` / `note_added` without\n * mutating `related_files`, `summary`, or the session status.\n *\n * Race note: the status check and the event append are not atomic.\n * Between them another writer (e.g. `basou run claude-code` ending its\n * session) can flip the YAML to `completed` and append `session_ended`.\n * v0.1 accepts this race; the `events_say_ended_but_yaml_running`-style\n * suspect rule surfaces the inconsistency. Per-session locking is\n * deferred to a v0.3+ follow-up.\n */\nexport async function appendEventToExistingSession(\n input: AppendEventToExistingInput,\n): Promise<AppendEventToExistingResult> {\n // 1. Boundary parse (direct caller self-defense).\n SessionIdSchema.parse(input.sessionId);\n\n // 2. Read session.yaml.\n const sessionDoc = await readSessionYaml(input.paths, input.sessionId);\n const status = sessionDoc.session.status;\n\n // 3. Status check.\n if (status === \"imported\") {\n throw new Error(\"Cannot attach to imported session\");\n }\n const attachable = input.attachableStatuses ?? DEFAULT_ATTACHABLE_STATUSES;\n if (!attachable.has(status as AttachableStatus)) {\n throw new Error(`Session is not active: ${status}`);\n }\n\n // 4. Mint event ID and build payload.\n const eventId = prefixedUlid(\"evt\");\n const event = assertTargetEventIdentity(input.eventBuilder(eventId), input.sessionId, eventId);\n\n // 5. Append (appendEvent validates with EventSchema; bad payloads are\n // rejected with the fixed `\"Invalid Basou event payload\"` message).\n const sessionDir = join(input.paths.sessions, input.sessionId);\n await appendEvent(sessionDir, event);\n\n return { eventId, sessionStatus: status };\n}\n\n/**\n * Defensive check: a builder closure could in principle hand back\n * an event whose `id` or `session_id` differs from the orchestrator's\n * minted values. EventSchema only validates the shape, so this slip would\n * silently corrupt events.jsonl. Reject with a fixed pathless message so\n * direct-caller misuse never reaches disk.\n */\nfunction assertTargetEventIdentity(\n event: Event,\n expectedSessionId: PrefixedId<\"ses\">,\n expectedEventId: PrefixedId<\"evt\">,\n): Event {\n if (event.session_id !== expectedSessionId) {\n throw new Error(\"Target event session_id mismatch\");\n }\n if (event.id !== expectedEventId) {\n throw new Error(\"Target event id mismatch\");\n }\n return event;\n}\n","import { posix as path } from \"node:path\";\n\n/**\n * Options for {@link sanitizePath}. Both `workingDirectory` and `homedir`\n * are absolute POSIX paths the caller has already resolved (typically via\n * `process.cwd()` and `os.homedir()`). Callers are responsible for passing\n * fully normalised values; the sanitizer normalises them again internally\n * so a trailing slash or `.`-segment does not corrupt the prefix match.\n */\nexport type SanitizePathOptions = {\n /**\n * The session's working directory (= the `working_directory` field the\n * caller is about to write). Paths under this directory are rewritten\n * relative to it so the operator-private absolute prefix never leaks\n * into the workspace's persistent state.\n */\n workingDirectory: string;\n /**\n * The operator's home directory. Paths under this directory (but NOT\n * under `workingDirectory`) are rewritten with a `~/` prefix.\n */\n homedir: string;\n};\n\n/**\n * Rewrite an absolute path into a workspace-friendly form so the persisted\n * state of `.basou/` does not leak the operator's machine layout:\n *\n * 1. Paths under `opts.workingDirectory` become repository-relative\n * (e.g. `<wd>/src/x.ts` → `src/x.ts`, `<wd>` itself → `.`).\n * 2. Paths under `opts.homedir` (but not workingDirectory) become\n * tilde-prefixed (`/Users/u/notes/x.md` → `~/notes/x.md`,\n * `/Users/u` → `~`).\n * 3. Anything else — relative paths, system paths under `/etc/*`,\n * `..`-escapes from either base, paths that simply do not share a\n * prefix with either option — is returned verbatim (after `..`\n * normalisation). The sanitizer is intentionally non-redacting on\n * system paths so an operator who deliberately recorded a system\n * file (e.g. `/etc/hosts`) is not silently stripped of context.\n *\n * Hardening:\n * - A null byte in the input is rejected with `Invalid path: contains\n * null byte` (= POSIX path APIs treat \\0 as terminator and any path\n * containing one is malformed; we never accept it on the write side).\n * - `..` segments are resolved purely (no fs access) so the prefix\n * match cannot be defeated by `<wd>/../escape/x.ts` masquerading as\n * workingDirectory-internal.\n * - Backslashes are folded to forward slashes so a Windows-style input\n * can still be matched against POSIX bases. v0.3 targets macOS /\n * Linux only; full Windows support is a v0.4+ task.\n */\nexport function sanitizePath(rawPath: string, opts: SanitizePathOptions): string {\n if (rawPath.includes(\"\\0\")) {\n throw new Error(\"Invalid path: contains null byte\");\n }\n const normalized = path.normalize(rawPath.replace(/\\\\/g, \"/\"));\n const wd = path.normalize(opts.workingDirectory.replace(/\\\\/g, \"/\"));\n const home = path.normalize(opts.homedir.replace(/\\\\/g, \"/\"));\n\n // Only attempt prefix matching for absolute inputs; an already-relative\n // path stays as-is so write paths that pre-relativised do not get\n // mangled.\n if (!path.isAbsolute(normalized)) {\n return normalized;\n }\n\n // (1) workingDirectory-internal -> repo-relative.\n if (normalized === wd) return \".\";\n const wdRel = path.relative(wd, normalized);\n if (wdRel !== \"\" && !wdRel.startsWith(\"..\")) {\n return wdRel;\n }\n\n // (2) homedir-internal -> ~/...\n if (normalized === home) return \"~\";\n const homeRel = path.relative(home, normalized);\n if (homeRel !== \"\" && !homeRel.startsWith(\"..\")) {\n return `~/${homeRel}`;\n }\n\n // (3) preserve as-is.\n return normalized;\n}\n\n/**\n * Sanitize the `working_directory` field itself. This is a distinct entry\n * point because the field's own value is the workingDirectory of every\n * `related_files[]` entry written alongside it — running it through\n * {@link sanitizePath} with `opts.workingDirectory = rawPath` would\n * collapse the result to `\".\"` and lose the homedir-relative form the\n * spec requires.\n *\n * Strategy: bypass the workingDirectory rule entirely by passing a\n * sentinel that no real path can match. The homedir rule (rule 2) and\n * the preserve-as-is rule (rule 3) still apply, so:\n * - `/Users/u/projects/foo` → `~/projects/foo`\n * - `/Users/u` → `~`\n * - `/srv/work` → `/srv/work` (preserved, off-tree)\n *\n * Callers should still pass the live `homedir` so the rewrite uses the\n * real operator-private prefix.\n */\nexport function sanitizeWorkingDirectory(\n rawPath: string,\n opts: Pick<SanitizePathOptions, \"homedir\">,\n): string {\n // A sentinel that no real absolute path on disk can equal or be under.\n // `path.posix.normalize` collapses leading `/` so any sentinel must\n // remain non-prefixing post-normalisation; the sentinel below survives\n // normalisation as itself and never matches a real path.\n return sanitizePath(rawPath, {\n workingDirectory: \"/__basou_sentinel_never_match__\",\n homedir: opts.homedir,\n });\n}\n\n/** Result of {@link sanitizeRelatedFiles}. */\nexport type SanitizeRelatedFilesResult = {\n /** Sanitized path list (same length as the input). */\n sanitized: string[];\n /** Number of entries whose sanitized form differs from the input. */\n mutationCount: number;\n};\n\n/**\n * Apply {@link sanitizePath} to every entry of a `related_files[]` array\n * and report how many entries actually changed shape so callers (e.g. the\n * session-import CLI) can surface a single-line warning. The helper does\n * not deduplicate — callers already collect related_files into a Set\n * before serialising.\n */\nexport function sanitizeRelatedFiles(\n paths: ReadonlyArray<string>,\n opts: SanitizePathOptions,\n): SanitizeRelatedFilesResult {\n const sanitized: string[] = [];\n let mutationCount = 0;\n for (const p of paths) {\n const next = sanitizePath(p, opts);\n sanitized.push(next);\n if (next !== p) mutationCount += 1;\n }\n return { sanitized, mutationCount };\n}\n","import { readFile, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { findErrorCode } from \"../lib/error-codes.js\";\nimport { atomicCreate } from \"./atomic.js\";\nimport type { BasouPaths } from \"./basou-dir.js\";\n\n/**\n * The two lock scopes basou uses. `task` guards the read-modify-write window\n * around a single `task.md`; `session` guards the events.jsonl append plus\n * surrounding `session.yaml` mutation for a single session. Two scopes use\n * different lockfile names so they never collide on disk.\n */\nexport type LockScope = \"task\" | \"session\";\n\n/**\n * Any lock older than this is treated as stale and force-released even if the\n * holding pid is still alive. basou CLI invocations hold a lock for ms to a\n * few seconds at most, so an hour is a 10000x safety margin; the upper bound\n * is also our defence against pid reuse (a different process happening to\n * receive a long-dead pid).\n */\nconst STALE_LOCK_MAX_AGE_MS = 60 * 60 * 1000;\n\ntype LockFileBody = {\n pid: number;\n acquired_at: string;\n};\n\nexport type LockHandle = {\n /**\n * Release the lock by unlinking the lockfile. Best-effort: any unlink error\n * is swallowed so a doubled release does not raise, and disk state never\n * holds a stranded lockfile after the caller's `finally` block.\n */\n release: () => Promise<void>;\n};\n\n/**\n * Acquire an advisory lock at `<paths.locks>/<scope>_<id>.lock` for the\n * lifetime of the returned handle. Lockfile body records the holder's pid\n * and acquire timestamp so a competitor can detect stale locks left by a\n * SIGINT'd CLI run and recover automatically.\n *\n * Acquisition strategy:\n * 1. {@link atomicCreate} the lockfile (POSIX link(2) + EEXIST).\n * 2. On EEXIST, probe the existing lockfile via {@link isStaleLock}.\n * - If stale (= holder pid is dead or lock is older than\n * {@link STALE_LOCK_MAX_AGE_MS}), `unlink` the stale file and retry\n * the atomic create once.\n * - If still EEXIST after the retry (= another competitor won the race),\n * throw `\"Lock is held by another process\"`.\n * - If the holder is alive, throw `\"Lock is held by another process\"`\n * without retrying.\n *\n * The caller MUST call `release()` (typically from a `finally` block); the\n * `process.exit()` path or a fatal crash relies on stale-lock detection on\n * the next acquire to recover.\n */\nexport async function acquireLock(\n paths: BasouPaths,\n scope: LockScope,\n resourceId: string,\n): Promise<LockHandle> {\n const lockPath = lockfilePath(paths, scope, resourceId);\n const body: LockFileBody = {\n pid: process.pid,\n acquired_at: new Date().toISOString(),\n };\n const serialised = JSON.stringify(body);\n\n try {\n await atomicCreate(lockPath, serialised);\n } catch (error: unknown) {\n if (!findErrorCode(error, \"EEXIST\")) {\n throw error;\n }\n const stale = await isStaleLock(lockPath);\n if (!stale) {\n throw new Error(\"Lock is held by another process\", { cause: error });\n }\n // Best-effort cleanup of the stale lockfile, then a single retry. A\n // second EEXIST means another competitor beat us to the cleared lock;\n // surface that as a normal \"held\" failure rather than looping.\n await unlink(lockPath).catch(() => undefined);\n try {\n await atomicCreate(lockPath, serialised);\n } catch (retryError: unknown) {\n throw new Error(\"Lock is held by another process\", { cause: retryError });\n }\n }\n\n return {\n release: async () => {\n await unlink(lockPath).catch(() => undefined);\n },\n };\n}\n\n/**\n * Read the lockfile at `lockPath` and decide whether the holder is dead or\n * the lock is too old to trust. Used by {@link acquireLock} on EEXIST to\n * recover from SIGINT'd CLI runs that left the lockfile behind.\n *\n * Stale predicates (any of these = stale):\n * - lockfile body unreadable or malformed\n * - `acquired_at` is older than {@link STALE_LOCK_MAX_AGE_MS}\n * - `process.kill(pid, 0)` throws ESRCH (holder pid is dead)\n *\n * EPERM from `process.kill` means the pid is alive but owned by a different\n * uid; we treat that as alive so cross-user lockfile takeover does not happen\n * by accident.\n */\nasync function isStaleLock(lockPath: string): Promise<boolean> {\n let body: LockFileBody;\n try {\n const raw = await readFile(lockPath, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n if (typeof parsed !== \"object\" || parsed === null) return true;\n const candidate = parsed as Partial<LockFileBody>;\n if (typeof candidate.pid !== \"number\" || typeof candidate.acquired_at !== \"string\") {\n return true;\n }\n body = { pid: candidate.pid, acquired_at: candidate.acquired_at };\n } catch {\n // Unreadable lockfile (e.g. truncated mid-write) counts as stale so we\n // can recover instead of looping forever on EEXIST.\n return true;\n }\n const ageMs = Date.now() - Date.parse(body.acquired_at);\n if (!Number.isFinite(ageMs) || ageMs > STALE_LOCK_MAX_AGE_MS) {\n return true;\n }\n try {\n process.kill(body.pid, 0);\n return false;\n } catch (error: unknown) {\n if (findErrorCode(error, \"ESRCH\")) return true;\n // EPERM or any other surface — pid is alive (or unknown), keep the lock.\n return false;\n }\n}\n\nfunction lockfilePath(paths: BasouPaths, scope: LockScope, resourceId: string): string {\n // Strip the type prefix to keep the lockfile name compact (`task_01HX...` →\n // `01HX...`, `ses_01HX...` → `01HX...`). The scope literal at the start of\n // the filename keeps task/session lockfiles disjoint even when the ULID\n // tails happen to coincide.\n const sep = resourceId.indexOf(\"_\");\n const ulid = sep >= 0 ? resourceId.slice(sep + 1) : resourceId;\n return join(paths.locks, `${scope}_${ulid}.lock`);\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { findErrorCode } from \"../lib/error-codes.js\";\nimport {\n TASK_INDEX_SCHEMA_VERSION,\n type TaskIndex,\n type TaskIndexEntry,\n TaskIndexSchema,\n} from \"../schemas/task-index.schema.js\";\nimport { atomicReplace } from \"./atomic.js\";\nimport type { BasouPaths } from \"./basou-dir.js\";\n\n/**\n * Absolute path of the workspace's `tasks/index.json`. The index lives\n * INSIDE `<paths.tasks>` (not under `<paths.root>`) so a future\n * monorepo-style layout with multiple task families could carry its own\n * index without colliding at the basou root.\n */\nexport function taskIndexPath(paths: BasouPaths): string {\n return join(paths.tasks, \"index.json\");\n}\n\n/**\n * Read and validate `tasks/index.json`. Returns the parsed payload only\n * when the schema_version matches the current literal — a mismatch is\n * surfaced as a schema parse failure so the caller falls through to the\n * lazy-rebuild path.\n *\n * Error contract:\n * - ENOENT → throw `Error(\"Task index not found\", { cause })`\n * - JSON parse / schema fail / version mismatch → throw\n * `Error(\"Invalid task index\", { cause })`\n * - any other I/O failure → throw `Error(\"Failed to read task index\", { cause })`\n *\n * Callers should treat all three as \"rebuild from disk\"; the distinct\n * messages exist so debug output / dogfood notes can tell them apart.\n */\nexport async function readTaskIndex(paths: BasouPaths): Promise<TaskIndex> {\n const filePath = taskIndexPath(paths);\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf8\");\n } catch (error: unknown) {\n if (findErrorCode(error, \"ENOENT\")) {\n throw new Error(\"Task index not found\", { cause: error });\n }\n throw new Error(\"Failed to read task index\", { cause: error });\n }\n let parsedJson: unknown;\n try {\n parsedJson = JSON.parse(raw);\n } catch (error: unknown) {\n throw new Error(\"Invalid task index\", { cause: error });\n }\n const result = TaskIndexSchema.safeParse(parsedJson);\n if (!result.success) {\n throw new Error(\"Invalid task index\", { cause: result.error });\n }\n if (result.data.schema_version !== TASK_INDEX_SCHEMA_VERSION) {\n // Reject older / newer schema versions so a future bump triggers a\n // forced rebuild rather than silent migration.\n throw new Error(\"Invalid task index\", {\n cause: new Error(`Unsupported task index schema_version: ${result.data.schema_version}`),\n });\n }\n return result.data;\n}\n\n/**\n * Atomically write `tasks/index.json` with the given entries. Entries\n * are sorted by id (= ULID-ascending) so two rebuilds on the same disk\n * state produce byte-identical output and `git diff` stays clean.\n *\n * Caller-controlled `now` lets tests assert on `last_rebuilt_at`\n * without faking `Date`. When omitted the current wall clock is used.\n */\nexport async function rebuildTaskIndex(\n paths: BasouPaths,\n entries: ReadonlyArray<TaskIndexEntry>,\n now?: () => Date,\n): Promise<TaskIndex> {\n const sorted = [...entries].sort((a, b) => a.id.localeCompare(b.id));\n const payload: TaskIndex = {\n schema_version: TASK_INDEX_SCHEMA_VERSION,\n tasks: sorted,\n last_rebuilt_at: (now ?? (() => new Date()))().toISOString(),\n };\n // Self-defense — boundary-parse so a buggy caller cannot smuggle in\n // an invalid entry shape past the read-side schema check.\n TaskIndexSchema.parse(payload);\n await atomicReplace(taskIndexPath(paths), `${JSON.stringify(payload, null, 2)}\\n`);\n return payload;\n}\n\n/**\n * Mutation kind for {@link updateTaskIndex}. `add` and `update` carry a\n * full entry payload; `remove` carries only the id (the entry is gone\n * from disk by the time we write the index).\n *\n * archiveTask uses `remove` too: the archived task no longer participates\n * in the active task index because `enumerateTaskIds` (= the index's\n * read consumer) scans only `tasks/<id>.md`, not `tasks/archive/<id>.md`.\n */\nexport type TaskIndexOp =\n | { kind: \"add\"; entry: TaskIndexEntry }\n | { kind: \"update\"; entry: TaskIndexEntry }\n | { kind: \"remove\"; id: string };\n\n/**\n * Apply a single mutation to `tasks/index.json` and atomically rewrite\n * it. Falls through to {@link rebuildTaskIndex} when the current index is\n * missing / invalid so the first write after a workspace migration\n * still produces a valid file.\n *\n * Write failure (atomic-rename ENOSPC / EACCES etc.) is re-thrown\n * unwrapped so the caller (= each task write API) can decide whether to\n * surface it as a warning or escalate. The recommended policy in\n * `tasks.ts` is `console.warn(...)` plus keep the task.md write\n * successful (= index is a soft cache, not source of truth).\n */\nexport async function updateTaskIndex(\n paths: BasouPaths,\n op: TaskIndexOp,\n options?: { now?: () => Date },\n): Promise<TaskIndex> {\n const nowFn = options?.now ?? (() => new Date());\n let current: TaskIndex;\n try {\n current = await readTaskIndex(paths);\n } catch {\n // Index missing or invalid — rebuild empty before applying op.\n current = {\n schema_version: TASK_INDEX_SCHEMA_VERSION,\n tasks: [],\n last_rebuilt_at: nowFn().toISOString(),\n };\n }\n\n let nextTasks: TaskIndexEntry[];\n switch (op.kind) {\n case \"add\":\n nextTasks = current.tasks.some((t) => t.id === op.entry.id)\n ? current.tasks.map((t) => (t.id === op.entry.id ? op.entry : t))\n : [...current.tasks, op.entry];\n break;\n case \"update\":\n nextTasks = current.tasks.some((t) => t.id === op.entry.id)\n ? current.tasks.map((t) => (t.id === op.entry.id ? op.entry : t))\n : [...current.tasks, op.entry];\n break;\n case \"remove\":\n nextTasks = current.tasks.filter((t) => t.id !== op.id);\n break;\n }\n\n return await rebuildTaskIndex(paths, nextTasks, nowFn);\n}\n","import { z } from \"zod\";\nimport { IsoTimestampSchema, SchemaVersionSchema, TaskIdSchema } from \"./shared.schema.js\";\nimport { TaskStatusSchema } from \"./task.schema.js\";\n\n/**\n * Single entry inside `.basou/tasks/index.json`.\n *\n * Source of truth remains `task.md`; this is a derived cache populated\n * write-through on every task mutation (`createTask`,\n * `updateTaskStatusWithEvent`, `editTask`, `deleteTask`, `archiveTask`,\n * `reconcileTask`, `refreshTaskLinkedSessions`). The minimum field set\n * lets `basou task list` filter / sort without re-parsing every front\n * matter, while keeping the index small enough that rebuilds stay cheap.\n *\n * `label` is omitted when the task has no explicit label so the JSON\n * round-trips without storing `undefined` literals.\n */\nexport const TaskIndexEntrySchema = z\n .object({\n id: TaskIdSchema,\n status: TaskStatusSchema,\n label: z.string().min(1).optional(),\n updated_at: IsoTimestampSchema,\n })\n .strict();\nexport type TaskIndexEntry = z.infer<typeof TaskIndexEntrySchema>;\n\n/**\n * Top-level schema for `.basou/tasks/index.json`. `tasks[]` is the\n * compact projection used for fast enumeration; `last_rebuilt_at`\n * records the wall-clock moment of the latest full readdir rebuild so\n * a future migration / debugging tool can spot stale caches without\n * comparing every entry against disk.\n *\n * `schema_version` lets a future bump trigger a forced rebuild instead\n * of attempting silent schema migration — readTaskIndex returns the\n * parsed payload only when the version matches the current literal, so\n * a mismatch falls through to the lazy-rebuild path.\n */\nexport const TaskIndexSchema = z\n .object({\n schema_version: SchemaVersionSchema,\n tasks: z.array(TaskIndexEntrySchema),\n last_rebuilt_at: IsoTimestampSchema,\n })\n .strict();\nexport type TaskIndex = z.infer<typeof TaskIndexSchema>;\n\n/** Current schema version. Bump triggers a forced rebuild on next read. */\nexport const TASK_INDEX_SCHEMA_VERSION = \"0.1.0\" as const;\n","// `[1-9]\\d*` rejects \"0\" and leading zeros so that callers cannot smuggle in\n// a non-positive duration (which the underlying spawn validators would later\n// reject anyway). The unit is fixed to `ms`/`s`/`m`/`h`; days and weeks are\n// out of scope for v0.1.\nconst DURATION_RE = /^([1-9]\\d*)(ms|s|m|h)$/;\n\n/**\n * Parse a unit-suffixed duration string (e.g. `30s`, `5m`, `1h`, `100ms`)\n * into milliseconds.\n *\n * Rejects formats that cannot represent a positive, finite millisecond\n * value: malformed inputs, zero, leading-zero values, and computations that\n * overflow to `Infinity`. The returned number is always a positive integer.\n *\n * Supported units: `ms` (milliseconds), `s` (seconds), `m` (minutes),\n * `h` (hours).\n *\n * @param input duration string with required unit suffix\n * @returns duration in milliseconds (positive, finite)\n * @throws Error with message\n * `Invalid duration: <input>. Expected format: <positive-integer><unit> where unit is ms/s/m/h`\n * for format errors, or `Duration overflow: <input>` for non-finite results.\n */\nexport function parseDuration(input: string): number {\n const trimmed = input.trim();\n const match = DURATION_RE.exec(trimmed);\n if (!match) {\n throw new Error(\n `Invalid duration: ${trimmed}. Expected format: <positive-integer><unit> where unit is ms/s/m/h`,\n );\n }\n const value = Number(match[1]);\n const unit = match[2];\n let ms: number;\n switch (unit) {\n case \"ms\":\n ms = value;\n break;\n case \"s\":\n ms = value * 1000;\n break;\n case \"m\":\n ms = value * 60_000;\n break;\n case \"h\":\n ms = value * 3_600_000;\n break;\n default:\n // Unreachable per the regex; satisfy exhaustiveness analysis.\n throw new Error(`Invalid duration unit: ${unit}`);\n }\n if (!Number.isFinite(ms)) {\n throw new Error(`Duration overflow: ${trimmed}`);\n }\n return ms;\n}\n","import type { BasouPaths } from \"../storage/basou-dir.js\";\nimport { enumerateSessionDirs } from \"../storage/sessions.js\";\nimport { enumerateArchivedTaskIds, enumerateTaskIds } from \"../storage/tasks.js\";\n\n/**\n * Resolve a possibly-truncated session id prefix to a full session id by\n * scanning `<paths.sessions>/`. Existing message contract (carried over\n * from `packages/cli/src/commands/session.ts`) is\n * preserved exactly so callers that grep stderr keep working:\n *\n * - `\"Session id is empty\"`\n * - `\"Session not found: <input>\"`\n * - `\"Ambiguous session id '<input>': matched <N> sessions. Disambiguate\n * with a longer prefix.\"`\n */\nexport async function resolveSessionId(paths: BasouPaths, input: string): Promise<string> {\n return resolveIdInternal(paths, input, \"session\");\n}\n\n/**\n * Resolve a possibly-truncated task id prefix to a full task id by scanning\n * `<paths.tasks>/`. Mirrors {@link resolveSessionId} with the noun changed\n * to `task` in every error message.\n *\n * `options.includeArchived` extends the scan to `<paths.tasks>/archive/` so\n * read-only commands (e.g. `basou task show`) can address tasks that were\n * archived by `basou task archive`. Defaults to `false` so destructive flows\n * (status change, edit, delete, archive itself) cannot operate on archived\n * tasks accidentally.\n */\nexport async function resolveTaskId(\n paths: BasouPaths,\n input: string,\n options: { includeArchived?: boolean } = {},\n): Promise<string> {\n return resolveIdInternal(paths, input, \"task\", options);\n}\n\ntype IdKind = \"session\" | \"task\";\n\ntype KindConfig = {\n prefix: string;\n noun: string;\n nounPlural: string;\n capNoun: string;\n enumerate: (paths: BasouPaths) => Promise<string[]>;\n};\n\nconst KIND_CONFIG: Record<IdKind, KindConfig> = {\n session: {\n prefix: \"ses_\",\n noun: \"session\",\n nounPlural: \"sessions\",\n capNoun: \"Session\",\n enumerate: enumerateSessionDirs,\n },\n task: {\n prefix: \"task_\",\n noun: \"task\",\n nounPlural: \"tasks\",\n capNoun: \"Task\",\n enumerate: enumerateTaskIds,\n },\n};\n\nasync function resolveIdInternal(\n paths: BasouPaths,\n input: string,\n kind: IdKind,\n options: { includeArchived?: boolean } = {},\n): Promise<string> {\n const cfg = KIND_CONFIG[kind];\n const trimmed = input.trim();\n if (trimmed.length === 0) {\n throw new Error(`${cfg.capNoun} id is empty`);\n }\n const normalized = trimmed.startsWith(cfg.prefix) ? trimmed : `${cfg.prefix}${trimmed}`;\n if (normalized.length <= cfg.prefix.length) {\n throw new Error(`${cfg.capNoun} not found: ${input}`);\n }\n const primary = await cfg.enumerate(paths);\n // Merge in archived task ids when the caller opts in. Dedupe via a Set so\n // a single id appearing in both surfaces (shouldn't happen but defend\n // anyway) does not falsely register as ambiguous.\n const merged = new Set<string>(primary);\n if (kind === \"task\" && options.includeArchived === true) {\n for (const id of await enumerateArchivedTaskIds(paths)) {\n merged.add(id);\n }\n }\n if (merged.size === 0) {\n throw new Error(`${cfg.capNoun} not found: ${input}`);\n }\n const matches = [...merged].filter((e) => e.startsWith(normalized));\n if (matches.length === 0) {\n throw new Error(`${cfg.capNoun} not found: ${input}`);\n }\n if (matches.length > 1) {\n throw new Error(\n `Ambiguous ${cfg.noun} id '${input}': matched ${matches.length} ${cfg.nounPlural}. Disambiguate with a longer prefix.`,\n );\n }\n return matches[0] as string;\n}\n","import { type ChildProcess, spawn } from \"node:child_process\";\n\nimport { findErrorCode } from \"../storage/status.js\";\n\nimport type { ProcessRunner, RunOptions, RunResult } from \"./process-runner.js\";\n\nconst DEFAULT_KILL_GRACE_MS = 5_000;\n\n/**\n * Spawn-based ProcessRunner implementation.\n *\n * Behavior:\n * - `shell: false` and `detached: false`. The process group is not\n * detached, but the OS does not guarantee the child is reaped when\n * the parent terminates abruptly; callers handle SIGINT/SIGTERM/exit\n * hooks themselves.\n * - `capture: \"buffer\"` (default): `stdio: ['pipe', 'pipe', 'pipe']`,\n * stdout / stderr are decoded as UTF-8 and accumulated as full\n * strings (no streaming callbacks).\n * - `capture: \"none\"`: `stdio: ['inherit', 'inherit', 'inherit']`, the\n * child writes directly to the parent terminal in real time and\n * `RunResult.stdout` / `stderr` are empty strings. `stdin` is\n * incompatible with this mode (the child has no writable stdin pipe)\n * and the combination is rejected before spawn.\n * - `timeout_ms` and `AbortSignal` both trigger a two-stage kill:\n * `SIGTERM`, then `SIGKILL` after `DEFAULT_KILL_GRACE_MS` (5_000 ms).\n * - A non-zero `exit_code` does not throw; it is returned via\n * `RunResult`. Spawn-time errors throw with a pathless message and\n * the original error attached as `cause`.\n *\n * Error message contract: messages never include `cwd` or absolute\n * command paths. The original errno (and any nested wrapping) is\n * preserved on `Error.cause`, allowing callers to classify with\n * `findErrorCode` when needed.\n */\nexport class ChildProcessRunner implements ProcessRunner {\n async run(command: string, args: readonly string[], options: RunOptions): Promise<RunResult> {\n validateOptions(options);\n\n if (options.signal?.aborted) {\n throw new Error(\"Process aborted before spawn\", {\n cause: options.signal.reason,\n });\n }\n\n // Freeze the invocation snapshot at spawn time so the eventual RunResult\n // reflects the call as it was issued, even if the caller mutates `args`\n // or `options` afterward.\n const snapshotCommand = command;\n const snapshotArgs: readonly string[] = [...args];\n const snapshotCwd = options.cwd;\n const captureMode = options.capture ?? \"buffer\";\n\n const started_at = new Date();\n\n let child: ChildProcess;\n try {\n child = spawn(snapshotCommand, [...snapshotArgs], {\n cwd: snapshotCwd,\n env: options.env ?? process.env,\n stdio:\n captureMode === \"none\" ? [\"inherit\", \"inherit\", \"inherit\"] : [\"pipe\", \"pipe\", \"pipe\"],\n shell: false,\n detached: false,\n });\n } catch (error: unknown) {\n throw classifySpawnError(error);\n }\n\n // Notify caller that the child exists so they can wire parent-side\n // cleanup (e.g. an `exit` hook). The runner ignores any throw from\n // the callback; the caller is responsible for keeping it side-effect\n // safe.\n if (options.onSpawn) {\n try {\n options.onSpawn(child);\n } catch {\n // intentional: do not let onSpawn failures abort the run.\n }\n }\n\n let timeoutTimer: NodeJS.Timeout | null = null;\n let killTimer: NodeJS.Timeout | null = null;\n let killed = false;\n let settled = false;\n\n const triggerKill = (): void => {\n if (killed || child.exitCode !== null) return;\n killed = true;\n child.kill(\"SIGTERM\");\n killTimer = setTimeout(() => {\n if (child.exitCode === null) {\n child.kill(\"SIGKILL\");\n }\n }, DEFAULT_KILL_GRACE_MS);\n };\n\n // Attach the abort listener immediately, then re-check `aborted` to\n // close the window between spawn() returning and addEventListener.\n const onAbort = (): void => {\n triggerKill();\n };\n options.signal?.addEventListener(\"abort\", onAbort);\n if (options.signal?.aborted) {\n triggerKill();\n }\n\n let stdout = \"\";\n let stderr = \"\";\n if (captureMode === \"buffer\") {\n // stdio is ['pipe', 'pipe', 'pipe'] so stdout/stderr/stdin are non-null.\n child.stdout?.setEncoding(\"utf8\");\n child.stderr?.setEncoding(\"utf8\");\n child.stdout?.on(\"data\", (chunk: string) => {\n stdout += chunk;\n });\n child.stderr?.on(\"data\", (chunk: string) => {\n stderr += chunk;\n });\n\n if (options.stdin !== undefined) {\n child.stdin?.end(options.stdin);\n } else {\n child.stdin?.end();\n }\n }\n // capture: \"none\" leaves stdio inherited; stdout/stderr remain \"\".\n\n if (options.timeout_ms !== undefined) {\n timeoutTimer = setTimeout(triggerKill, options.timeout_ms);\n }\n\n const cleanup = (): void => {\n if (timeoutTimer !== null) clearTimeout(timeoutTimer);\n if (killTimer !== null) clearTimeout(killTimer);\n options.signal?.removeEventListener(\"abort\", onAbort);\n };\n\n return new Promise<RunResult>((resolve, reject) => {\n child.once(\"error\", (error: Error) => {\n if (settled) return;\n settled = true;\n cleanup();\n reject(classifySpawnError(error));\n });\n child.once(\"close\", (code: number | null, signal: NodeJS.Signals | null) => {\n if (settled) return;\n settled = true;\n cleanup();\n const ended_at = new Date();\n resolve({\n command: snapshotCommand,\n args: snapshotArgs,\n cwd: snapshotCwd,\n exit_code: code,\n signal,\n stdout,\n stderr,\n started_at: started_at.toISOString(),\n ended_at: ended_at.toISOString(),\n duration_ms: ended_at.getTime() - started_at.getTime(),\n pid: child.pid ?? null,\n });\n });\n });\n }\n}\n\nfunction validateOptions(options: RunOptions): void {\n if (\n options.timeout_ms !== undefined &&\n (!Number.isFinite(options.timeout_ms) || options.timeout_ms <= 0)\n ) {\n throw new Error(\"Invalid timeout_ms\");\n }\n if (options.capture === \"none\" && options.stdin !== undefined) {\n throw new Error('Combination of capture: \"none\" and stdin is not supported');\n }\n}\n\nfunction classifySpawnError(error: unknown): Error {\n if (findErrorCode(error, \"ENOENT\")) {\n return new Error(\"Command not found\", { cause: error });\n }\n return new Error(\"Failed to spawn child process\", { cause: error });\n}\n","import { z } from \"zod\";\nimport { IsoTimestampSchema, SchemaVersionSchema, WorkspaceIdSchema } from \"./shared.schema.js\";\n\nconst ProjectSchema = z.object({\n name: z.string().optional(),\n description: z.string().optional(),\n repository_url: z.string().nullable().optional(),\n});\n\nconst CapabilitiesSchema = z.object({\n enabled: z.array(z.string()),\n});\n\nconst ApprovalConfigSchema = z.object({\n required_for: z.array(z.string()).optional(),\n default_risk_level: z.enum([\"low\", \"medium\", \"high\", \"critical\"]),\n});\n\nconst ClaudeCodeAdapterConfigSchema = z.object({\n enabled: z.boolean(),\n config_path: z.string().optional(),\n});\n\nconst AdaptersSchema = z.object({\n \"claude-code\": ClaudeCodeAdapterConfigSchema,\n});\n\nconst GitConfigSchema = z.object({\n events_log: z.enum([\"ignore\", \"commit\"]).default(\"ignore\"),\n});\n\nconst WorkspaceMetaSchema = z.object({\n id: WorkspaceIdSchema,\n name: z.string().min(1),\n created_at: IsoTimestampSchema,\n updated_at: IsoTimestampSchema,\n});\n\n/**\n * Schema for `.basou/manifest.yaml`. The minimal manifest carries\n * schema_version, basou_version, workspace metadata, project info, enabled\n * capabilities, approval policy, adapter config, and git policy. The\n * `adapters.\"claude-code\"` key uses a hyphen; downstream code accesses it\n * via bracket notation.\n */\nexport const ManifestSchema = z.object({\n schema_version: SchemaVersionSchema,\n basou_version: z.literal(\"0.1.0\"),\n workspace: WorkspaceMetaSchema,\n project: ProjectSchema,\n capabilities: CapabilitiesSchema,\n approval: ApprovalConfigSchema,\n adapters: AdaptersSchema,\n git: GitConfigSchema,\n});\n\n/** Inferred runtime type for {@link ManifestSchema}. */\nexport type Manifest = z.infer<typeof ManifestSchema>;\n","import { z } from \"zod\";\nimport { EventSchema } from \"./event.schema.js\";\nimport { SessionSourceKindSchema, SessionStatusSchema } from \"./session.schema.js\";\nimport {\n IsoTimestampSchema,\n SessionIdSchema,\n TaskIdSchema,\n WorkspaceIdSchema,\n} from \"./shared.schema.js\";\n\n// Independent copy of SessionInnerSchema for import payloads. The differences\n// from session.schema.ts are deliberate:\n// - `id` is `SessionIdSchema.optional()` so format is validated when present\n// but the orchestrator discards it and assigns a fresh ULID.\n// - `status` / `source.kind` are validated against the canonical enums but\n// overwritten by the orchestrator (status -> \"imported\", source.kind\n// retained from input).\n// - `events_log` is plain `z.string().optional()`; the orchestrator forces\n// \"events.jsonl\" to block path traversal.\n// - `.strict()` rejects unknown session-level keys at parse time.\n//\n// Events strictness follows EventSchema as authored: `adapter_output` is\n// `.strict()`, the other 14 variants are permissive, and\n// `approval_requested.action` is `.passthrough()`. This keeps the spec's\n// additive-event-fields rule and round-trip imports of post-v0.1 events\n// compatible. A blanket strict wrap for every variant is deferred.\n//\n// `schema_version` at the top level is `z.string()` rather than the\n// `SchemaVersionSchema = z.literal(\"0.1.0\")` literal. The strict reject for\n// unsupported versions emits a dedicated `Unsupported import schema_version`\n// message from the orchestrator; a literal here would short-circuit the\n// branch and turn every mismatched version into the generic\n// `Invalid import payload`.\nexport const SessionInnerImportSchema = z\n .object({\n id: SessionIdSchema.optional(),\n label: z.string().optional(),\n task_id: TaskIdSchema.nullable().optional(),\n workspace_id: WorkspaceIdSchema,\n source: z.object({\n kind: SessionSourceKindSchema,\n version: z.literal(\"0.1.0\"),\n }),\n started_at: IsoTimestampSchema,\n ended_at: IsoTimestampSchema.optional(),\n status: SessionStatusSchema,\n working_directory: z.string().min(1),\n invocation: z.object({\n command: z.string().min(1),\n args: z.array(z.string()),\n exit_code: z.number().int().nullable(),\n }),\n related_files: z.array(z.string()).default([]),\n events_log: z.string().optional(),\n summary: z.string().nullable().optional(),\n })\n .strict();\n\n/**\n * Schema for the round-trip JSON payload accepted by `basou session import\n * --format json`. The top level is `.strict()`; unknown keys at the outer\n * envelope are rejected.\n */\nexport const SessionImportPayloadSchema = z\n .object({\n schema_version: z.string(),\n session: SessionInnerImportSchema,\n events: z.array(EventSchema),\n })\n .strict();\n\n/** Inferred runtime type for {@link SessionImportPayloadSchema}. */\nexport type SessionImportPayload = z.infer<typeof SessionImportPayloadSchema>;\n/** Inferred runtime type for {@link SessionInnerImportSchema}. */\nexport type SessionInnerImportInput = z.infer<typeof SessionInnerImportSchema>;\n","import { lstat, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\n/**\n * Absolute paths to the standard `.basou/` directory layout, derived from a\n * given repository root. The shape mirrors the canonical `.basou/` tree\n * (see `docs/spec/workspace.md`). `root` is the `.basou/` directory itself\n * (i.e. `repositoryRoot/.basou`).\n *\n * `files` exposes the well-known top-level files inside `.basou/`. Each path\n * is computed but not created — they are written by their respective\n * subsystems (e.g. `writeManifest` for `manifest.yaml`).\n *\n * All fields are deeply readonly; consumers must not mutate the returned\n * object.\n */\nexport type BasouPaths = {\n readonly root: string;\n readonly sessions: string;\n readonly tasks: string;\n readonly approvals: {\n readonly pending: string;\n readonly resolved: string;\n };\n readonly locks: string;\n readonly logs: string;\n readonly raw: string;\n readonly tmp: string;\n readonly files: {\n readonly manifest: string;\n readonly status: string;\n readonly handoff: string;\n readonly decisions: string;\n };\n};\n\n/**\n * Compute absolute paths to the standard `.basou/` directory layout under\n * `repositoryRoot`. Pure: performs no I/O and is safe to call before the\n * directory exists.\n *\n * @param repositoryRoot Absolute path to the git repository root (the\n * parent directory of `.basou/`). Caller is responsible for resolving\n * `process.cwd()` or running `git rev-parse --show-toplevel` upstream;\n * this function does not validate that the path exists or is a git\n * repository.\n */\nexport function basouPaths(repositoryRoot: string): BasouPaths {\n const root = join(repositoryRoot, \".basou\");\n const approvalsBase = join(root, \"approvals\");\n return {\n root,\n sessions: join(root, \"sessions\"),\n tasks: join(root, \"tasks\"),\n approvals: {\n pending: join(approvalsBase, \"pending\"),\n resolved: join(approvalsBase, \"resolved\"),\n },\n locks: join(root, \"locks\"),\n logs: join(root, \"logs\"),\n raw: join(root, \"raw\"),\n tmp: join(root, \"tmp\"),\n files: {\n manifest: join(root, \"manifest.yaml\"),\n status: join(root, \"status.json\"),\n handoff: join(root, \"handoff.md\"),\n decisions: join(root, \"decisions.md\"),\n },\n };\n}\n\n// Labels for sub-paths inside `.basou/`. Used in pathless error messages so\n// the surface area for absolute-path leakage is bounded by this map.\nconst PATH_LABELS = {\n sessions: \".basou/sessions\",\n tasks: \".basou/tasks\",\n approvalsPending: \".basou/approvals/pending\",\n approvalsResolved: \".basou/approvals/resolved\",\n locks: \".basou/locks\",\n logs: \".basou/logs\",\n raw: \".basou/raw\",\n tmp: \".basou/tmp\",\n} as const;\n\n/**\n * Create the standard `.basou/` directory layout under `repositoryRoot`.\n *\n * Idempotent: a no-op on an already-initialized layout. Returns the resolved\n * {@link BasouPaths} so callers can immediately use them.\n *\n * Throws if `repositoryRoot/.basou` (or any required subdirectory) exists\n * but is not a directory, or if filesystem permissions prevent creation.\n * All thrown error messages are pathless; the original native error is\n * attached as `cause` for diagnostics.\n *\n * @param repositoryRoot Absolute path to the git repository root. See\n * {@link basouPaths} for the contract on this parameter.\n */\nexport async function ensureBasouDirectory(repositoryRoot: string): Promise<BasouPaths> {\n const paths = basouPaths(repositoryRoot);\n\n // lstat (not stat) so that a symlink at `.basou` is detected as a symlink\n // and rejected; following the link could place Basou state outside the\n // git repository root, violating the workspace-root invariant.\n let existing: Awaited<ReturnType<typeof lstat>> | undefined;\n try {\n existing = await lstat(paths.root);\n } catch (error: unknown) {\n if (!hasErrorCode(error) || error.code !== \"ENOENT\") {\n throw new Error(\"Failed to inspect .basou directory\", { cause: error });\n }\n }\n if (existing !== undefined && !existing.isDirectory()) {\n throw new Error(\"Basou root .basou exists but is not a directory\");\n }\n\n await Promise.all([\n mkdirLabeled(paths.sessions, PATH_LABELS.sessions),\n mkdirLabeled(paths.tasks, PATH_LABELS.tasks),\n mkdirLabeled(paths.approvals.pending, PATH_LABELS.approvalsPending),\n mkdirLabeled(paths.approvals.resolved, PATH_LABELS.approvalsResolved),\n mkdirLabeled(paths.locks, PATH_LABELS.locks),\n mkdirLabeled(paths.logs, PATH_LABELS.logs),\n mkdirLabeled(paths.raw, PATH_LABELS.raw),\n mkdirLabeled(paths.tmp, PATH_LABELS.tmp),\n ]);\n\n return paths;\n}\n\nasync function mkdirLabeled(target: string, label: string): Promise<void> {\n try {\n await mkdir(target, { recursive: true });\n } catch (error: unknown) {\n if (hasErrorCode(error) && (error.code === \"ENOTDIR\" || error.code === \"EEXIST\")) {\n throw new Error(`${label} exists but is not a directory`, { cause: error });\n }\n throw new Error(`Failed to create ${label}`, { cause: error });\n }\n}\n\nfunction hasErrorCode(error: unknown): error is Error & { code: string } {\n if (!(error instanceof Error)) return false;\n const codeProp = (error as unknown as Record<string, unknown>).code;\n return typeof codeProp === \"string\";\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nconst MARKER = \"# Basou - default ignore\";\n\n// Recommended .gitignore block (ignore + commit). The test asserts an\n// exact match against this spec string literal to detect spec drift.\nconst BASOU_GITIGNORE_BLOCK =\n \"# Basou - default ignore\\n\" +\n \".basou/logs/\\n\" +\n \".basou/raw/\\n\" +\n \".basou/tmp/\\n\" +\n \".basou/locks/\\n\" +\n \".basou/status.json\\n\" +\n \".basou/sessions/*/events.jsonl\\n\" +\n \".basou/sessions/*/artifacts/\\n\" +\n \".basou/approvals/pending/\\n\" +\n \".basou/approvals/resolved/\\n\" +\n \"\\n\" +\n \"# Basou - default commit\\n\" +\n \"# .basou/manifest.yaml\\n\" +\n \"# .basou/handoff.md\\n\" +\n \"# .basou/decisions.md\\n\" +\n \"# .basou/tasks/\\n\" +\n \"# .basou/sessions/*/session.yaml\\n\" +\n \"# .basou/sessions/*/transcript.md\\n\" +\n \"# .basou/sessions/*/changed-files.json\\n\";\n\nexport type AppendBasouGitignoreResult = {\n /** True if the block was appended (or the file was newly created). */\n readonly appended: boolean;\n};\n\n/**\n * Append Basou's default `.gitignore` block to `repositoryRoot/.gitignore`.\n *\n * The block contents are derived from the Basou v0.1 specification (the\n * standard ignore + commit recommendations). Callers must pass an absolute\n * path to a Git repository root.\n *\n * Behavior:\n * - If `.gitignore` does not exist, it is created with the Basou block.\n * - If a line starting with `# Basou - default ignore` is already present,\n * the file is left untouched and `appended: false` is returned\n * (idempotent).\n * - If `.gitignore` is a symlink, the link is followed and the target file\n * is updated. Symlinks are not rejected.\n *\n * On I/O failure throws Error with a pathless message\n * (`Failed to read .gitignore` / `Failed to write .gitignore`) and the\n * original native error attached as `cause`.\n */\nexport async function appendBasouGitignore(\n repositoryRoot: string,\n): Promise<AppendBasouGitignoreResult> {\n const gitignorePath = join(repositoryRoot, \".gitignore\");\n\n let body: string;\n let existed: boolean;\n try {\n body = await readFile(gitignorePath, \"utf8\");\n existed = true;\n } catch (error: unknown) {\n if (hasErrorCode(error) && error.code === \"ENOENT\") {\n body = \"\";\n existed = false;\n } else {\n throw new Error(\"Failed to read .gitignore\", { cause: error });\n }\n }\n\n if (existed && hasBasouMarker(body)) {\n return { appended: false };\n }\n\n const next = composeNextBody(body);\n try {\n await writeFile(gitignorePath, next, { encoding: \"utf8\" });\n } catch (error: unknown) {\n throw new Error(\"Failed to write .gitignore\", { cause: error });\n }\n return { appended: true };\n}\n\nfunction hasBasouMarker(body: string): boolean {\n for (const rawLine of body.split(\"\\n\")) {\n if (rawLine.trimEnd().startsWith(MARKER)) return true;\n }\n return false;\n}\n\nfunction composeNextBody(existing: string): string {\n if (existing.length === 0) return BASOU_GITIGNORE_BLOCK;\n const normalized = existing.endsWith(\"\\n\") ? existing : `${existing}\\n`;\n return `${normalized}\\n${BASOU_GITIGNORE_BLOCK}`;\n}\n\nfunction hasErrorCode(error: unknown): error is Error & { code: string } {\n if (!(error instanceof Error)) return false;\n return typeof (error as unknown as Record<string, unknown>).code === \"string\";\n}\n","import { lstat } from \"node:fs/promises\";\nimport { type PrefixedId, prefixedUlid } from \"../ids/ulid.js\";\nimport { type Manifest, ManifestSchema } from \"../schemas/manifest.schema.js\";\nimport type { BasouPaths } from \"./basou-dir.js\";\nimport { readYamlFile, writeYamlFile } from \"./yaml-store.js\";\n\n/**\n * Inputs for {@link createManifest}. Optional fields drop out of the\n * resulting Manifest entirely (they are not emitted as `null`/`undefined`\n * in YAML); pass `null` for `repositoryUrl` to keep an explicit `null`.\n */\nexport type CreateManifestInput = {\n workspaceName: string;\n projectName?: string;\n projectDescription?: string;\n repositoryUrl?: string | null;\n /** Override for tests; defaults to `new Date()`. */\n now?: Date;\n /** Override for tests; defaults to a freshly generated `ws_<ULID>`. */\n workspaceId?: PrefixedId<\"ws\">;\n};\n\n/**\n * Build a fresh Manifest object that satisfies the manifest schema's\n * minimum shape. Performs no I/O. Returned object is parse-validated by\n * `ManifestSchema`.\n */\nexport function createManifest(input: CreateManifestInput): Manifest {\n if (input.workspaceName.length === 0) {\n throw new Error(\"Workspace name is empty. Pass --name explicitly.\");\n }\n const now = (input.now ?? new Date()).toISOString();\n const workspaceId = input.workspaceId ?? prefixedUlid(\"ws\");\n\n const project: Manifest[\"project\"] = {\n ...(input.projectName !== undefined ? { name: input.projectName } : {}),\n ...(input.projectDescription !== undefined ? { description: input.projectDescription } : {}),\n ...(input.repositoryUrl !== undefined ? { repository_url: input.repositoryUrl } : {}),\n };\n\n const manifest: Manifest = {\n schema_version: \"0.1.0\",\n basou_version: \"0.1.0\",\n workspace: {\n id: workspaceId,\n name: input.workspaceName,\n created_at: now,\n updated_at: now,\n },\n project,\n capabilities: {\n enabled: [\"core\", \"claude-code-adapter\", \"terminal-recording\", \"git-capability\", \"approval\"],\n },\n approval: {\n required_for: [\"destructive_command\", \"external_send\"],\n default_risk_level: \"medium\",\n },\n adapters: {\n \"claude-code\": { enabled: true },\n },\n git: { events_log: \"ignore\" },\n };\n return ManifestSchema.parse(manifest);\n}\n\n/**\n * Write a Manifest to `paths.files.manifest`. Re-validates via\n * `ManifestSchema` before serialization.\n *\n * Refuses to overwrite an existing manifest unless `force: true`.\n */\nexport async function writeManifest(\n paths: BasouPaths,\n manifest: Manifest,\n options?: { force?: boolean },\n): Promise<void> {\n const force = options?.force === true;\n const validated = ManifestSchema.parse(manifest);\n\n if (!force) {\n let existed = false;\n try {\n await lstat(paths.files.manifest);\n existed = true;\n } catch (error: unknown) {\n if (!hasErrorCode(error) || error.code !== \"ENOENT\") {\n throw new Error(\"Failed to inspect existing manifest\", { cause: error });\n }\n }\n if (existed) {\n throw new Error(\"Already initialized. Use --force to overwrite.\");\n }\n }\n\n await writeYamlFile(paths.files.manifest, validated);\n}\n\n/**\n * Read and parse a Manifest from `paths.files.manifest`. Throws if the file\n * is missing or contents fail `ManifestSchema` validation.\n */\nexport async function readManifest(paths: BasouPaths): Promise<Manifest> {\n const raw = await readYamlFile(paths.files.manifest);\n return ManifestSchema.parse(raw);\n}\n\nfunction hasErrorCode(error: unknown): error is Error & { code: string } {\n if (!(error instanceof Error)) return false;\n return typeof (error as unknown as Record<string, unknown>).code === \"string\";\n}\n","import { readFile } from \"node:fs/promises\";\nimport { atomicReplace } from \"./atomic.js\";\n\n/** Marker line that begins the auto-generated region. */\nexport const GENERATED_START = \"<!-- BASOU:GENERATED:START -->\";\n/** Marker line that ends the auto-generated region. */\nexport const GENERATED_END = \"<!-- BASOU:GENERATED:END -->\";\n\n/**\n * Result of parsing a markdown body for the BASOU:GENERATED marker region.\n *\n * The spec mandates strict line-level matching (see\n * `docs/spec/generated-markdown.md#102-marker-convention`): a marker is\n * only recognized when an entire line is exactly the marker string.\n * Leading/trailing whitespace, comment compression, and BOM are treated as\n * legacy formats (`no_markers`) so that re-generation refuses to silently\n * overwrite a mismatched manual edit.\n */\nexport type MarkerSection =\n | { kind: \"ok\"; before: string; generated: string; after: string }\n | { kind: \"no_markers\" }\n | { kind: \"missing_start\" }\n | { kind: \"missing_end\" }\n | { kind: \"multiple_pairs\" }\n | { kind: \"wrong_order\" };\n\n/**\n * Read a markdown file as UTF-8 text. Returns `null` when the file does not\n * exist; throws `Error(\"Failed to read markdown file\", { cause })` for other\n * I/O failures (pathless contract — never embed the absolute path in the\n * thrown `message`).\n */\nexport async function readMarkdownFile(filePath: string): Promise<string | null> {\n try {\n return await readFile(filePath, \"utf8\");\n } catch (error: unknown) {\n if (hasErrorCode(error) && error.code === \"ENOENT\") return null;\n throw new Error(\"Failed to read markdown file\", { cause: error });\n }\n}\n\n/**\n * Atomically write a markdown body via {@link atomicReplace}. The shared\n * helper handles the tmp-file + rename sequence, `wx` collision guard, and\n * best-effort tmp cleanup on failure.\n *\n * On any failure the original error is re-thrown as\n * `Error(\"Failed to write markdown file\", { cause })` (pathless contract).\n */\nexport async function writeMarkdownFile(filePath: string, body: string): Promise<void> {\n try {\n await atomicReplace(filePath, body);\n } catch (error: unknown) {\n throw new Error(\"Failed to write markdown file\", { cause: error });\n }\n}\n\n/**\n * Parse a markdown body and identify the BASOU:GENERATED marker region.\n *\n * Returns one of six `kind` discriminants:\n * - `ok`: exactly one START line followed by exactly one END line in the\n * correct order. `before` / `generated` / `after` slice the original\n * text by character offsets so CRLF / LF are preserved verbatim outside\n * the marker region.\n * - `no_markers`: both START and END absent (legacy file / fresh write).\n * - `missing_start` / `missing_end`: exactly one of the pair is present.\n * - `multiple_pairs`: more than one START or END line.\n * - `wrong_order`: END appears before START.\n *\n * Matching is strict: leading/trailing whitespace, BOM, and comment\n * compression (`<!--BASOU:...-->`) all bypass the marker and are treated\n * as legacy content.\n */\nexport function parseMarkers(content: string): MarkerSection {\n // Split on either CRLF or LF so the line count is consistent regardless of\n // the file's line ending. The reconstruction step below slices the original\n // string by character offsets to preserve the actual line endings outside\n // the generated region.\n const lines = content.split(/\\r?\\n/);\n const startLines: number[] = [];\n const endLines: number[] = [];\n for (let i = 0; i < lines.length; i++) {\n if (lines[i] === GENERATED_START) startLines.push(i);\n else if (lines[i] === GENERATED_END) endLines.push(i);\n }\n if (startLines.length === 0 && endLines.length === 0) return { kind: \"no_markers\" };\n if (startLines.length === 0) return { kind: \"missing_start\" };\n if (endLines.length === 0) return { kind: \"missing_end\" };\n if (startLines.length >= 2 || endLines.length >= 2) return { kind: \"multiple_pairs\" };\n const startLineIdx = startLines[0] as number;\n const endLineIdx = endLines[0] as number;\n if (endLineIdx < startLineIdx) return { kind: \"wrong_order\" };\n\n // Walk the original string to find byte offsets of the marker lines. This\n // preserves CRLF vs LF in the surrounding text — splitting and re-joining\n // would normalize the line endings.\n const startOffset = lineStartOffset(content, startLineIdx);\n const endLineStart = lineStartOffset(content, endLineIdx);\n const startLineEnd = startOffset + GENERATED_START.length;\n const endLineEnd = endLineStart + GENERATED_END.length;\n\n const before = content.slice(0, startOffset);\n // The generated region is everything between the two marker lines,\n // exclusive of the marker line themselves but including the newline after\n // START and excluding the newline before END (so re-render can plug in\n // its own body without doubling separators).\n const afterStartNewline = skipOneNewline(content, startLineEnd);\n const beforeEndNewline = trimOneNewline(content, endLineStart);\n const generated = content.slice(afterStartNewline, beforeEndNewline);\n const after = content.slice(endLineEnd);\n return { kind: \"ok\", before, generated, after };\n}\n\n/**\n * Build the final markdown body by replacing the BASOU:GENERATED region.\n *\n * - `existing === null` (no file yet): return `<START>\\n<generated>\\n<END>\\n`.\n * - existing parses to `ok`: replace the marked region and keep everything\n * before START and after END untouched (preserving manual additions).\n * - any other parse result: throw a pathless error referencing `fileLabel`.\n *\n * The caller passes `fileLabel` (e.g. `\"handoff.md\"` or `\"decisions.md\"`)\n * so the error message is informative without leaking an absolute path.\n */\nexport function renderWithMarkers(\n existing: string | null,\n generated: string,\n fileLabel: string,\n): string {\n const normalized = generated.endsWith(\"\\n\") ? generated : `${generated}\\n`;\n if (existing === null) {\n return `${GENERATED_START}\\n${normalized}${GENERATED_END}\\n`;\n }\n const section = parseMarkers(existing);\n switch (section.kind) {\n case \"ok\":\n return `${section.before}${GENERATED_START}\\n${normalized}${GENERATED_END}${section.after}`;\n case \"no_markers\":\n throw new Error(`Markers missing in ${fileLabel}`);\n case \"missing_start\":\n case \"missing_end\":\n case \"multiple_pairs\":\n case \"wrong_order\":\n throw new Error(`Markers mismatched in ${fileLabel}`);\n }\n}\n\n/** Character offset of the first character of `lineIdx` (0-based). */\nfunction lineStartOffset(content: string, lineIdx: number): number {\n if (lineIdx === 0) return 0;\n let offset = 0;\n let line = 0;\n while (offset < content.length && line < lineIdx) {\n const ch = content[offset];\n if (ch === \"\\n\") {\n line += 1;\n offset += 1;\n } else if (ch === \"\\r\") {\n // CR or CRLF both count as a line terminator.\n offset += 1;\n if (content[offset] === \"\\n\") offset += 1;\n line += 1;\n } else {\n offset += 1;\n }\n }\n return offset;\n}\n\n/** Advance past one trailing `\\n` or `\\r\\n` if present. */\nfunction skipOneNewline(content: string, offset: number): number {\n if (content[offset] === \"\\r\" && content[offset + 1] === \"\\n\") return offset + 2;\n if (content[offset] === \"\\n\") return offset + 1;\n return offset;\n}\n\n/** Walk back past one leading `\\n` or `\\r\\n` if present. */\nfunction trimOneNewline(content: string, offset: number): number {\n if (offset >= 2 && content[offset - 2] === \"\\r\" && content[offset - 1] === \"\\n\")\n return offset - 2;\n if (offset >= 1 && content[offset - 1] === \"\\n\") return offset - 1;\n return offset;\n}\n\nfunction hasErrorCode(error: unknown): error is Error & { code: string } {\n if (!(error instanceof Error)) return false;\n const codeProp = (error as unknown as Record<string, unknown>).code;\n return typeof codeProp === \"string\";\n}\n","import { mkdir, rm } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { writeEventsBulk } from \"../events/event-writer.js\";\nimport { type PrefixedId, prefixedUlid } from \"../ids/ulid.js\";\nimport { findErrorCode } from \"../lib/error-codes.js\";\nimport { sanitizeRelatedFiles, sanitizeWorkingDirectory } from \"../lib/path-sanitizer.js\";\nimport type { Event } from \"../schemas/event.schema.js\";\nimport type { Manifest } from \"../schemas/manifest.schema.js\";\nimport type { Session, SessionSourceKind, SessionStatus } from \"../schemas/session.schema.js\";\nimport type {\n SessionImportPayload,\n SessionInnerImportInput,\n} from \"../schemas/session-import.schema.js\";\nimport { TaskIdSchema } from \"../schemas/shared.schema.js\";\nimport type { BasouPaths } from \"./basou-dir.js\";\nimport { enumerateTaskIds } from \"./tasks.js\";\nimport { linkYamlFile } from \"./yaml-store.js\";\n\n/**\n * Options for {@link importSessionFromJson}. All fields are optional.\n *\n * - `labelOverride` / `taskIdOverride` come from the CLI `--label` / `--task`\n * flags and win over the corresponding fields on the input payload.\n * - `dryRun` skips disk writes entirely and returns a preview result.\n */\nexport type ImportSessionOptions = {\n labelOverride?: string;\n taskIdOverride?: string;\n dryRun?: boolean;\n};\n\n/**\n * Result of a successful import. `finalStatus` is always the literal\n * `\"imported\"` (per the import-session lifecycle policy); `finalSourceKind`\n * mirrors the input's `session.source.kind` so round-trip imports preserve\n * provenance.\n *\n * `pathSanitizeReport` summarises how many path-shaped fields the importer\n * rewrote on the way in: `related_files[]` entries plus a single boolean\n * for `working_directory`. The CLI wrapper surfaces this as a one-line\n * stderr warning when the total is non-zero so the operator sees that\n * machine-private prefixes were stripped.\n */\nexport type ImportSessionResult = {\n sessionId: PrefixedId<\"ses\">;\n eventCount: number;\n finalStatus: SessionStatus;\n finalSourceKind: SessionSourceKind;\n pathSanitizeReport: {\n relatedFiles: number;\n workingDirectoryRewritten: boolean;\n };\n};\n\n/**\n * Import a round-trip JSON payload into `.basou/sessions/<new>/`. The caller\n * MUST validate the payload against {@link SessionImportPayloadSchema} first\n * and gate the `schema_version === \"0.1.0\"` literal check externally; this\n * function trusts both invariants.\n *\n * On success a fresh session ID is minted and a complete\n * `session.yaml` + `events.jsonl` pair is written atomically. On any post-\n * mkdir failure the session directory is removed best-effort so partial\n * imports do not leave `session_yaml_missing` half-states behind.\n *\n * Throws `Error` with one of the fixed messages enumerated by the import contract\n * §\"Error messages\" table; the original native error is attached as `cause`\n * for `--verbose` rendering.\n */\nexport async function importSessionFromJson(\n paths: BasouPaths,\n manifest: Manifest,\n payload: SessionImportPayload,\n options: ImportSessionOptions,\n): Promise<ImportSessionResult> {\n // Defense in depth: the CLI converter (parseTaskIdOverride) already gates\n // this, but a direct core API caller could still pass an arbitrary string.\n if (\n options.taskIdOverride !== undefined &&\n !TaskIdSchema.safeParse(options.taskIdOverride).success\n ) {\n throw new Error(`Invalid task_id: ${options.taskIdOverride}`);\n }\n\n // Reachability guard: rewriteEvents\n // preserves variant-specific task_id fields, so importing a session that\n // references a task absent from the local workspace would silently\n // install a dangling reference. Validate every task_id carrier:\n // task_created / task_status_changed / task_reconciled events plus the\n // effective session task_id (override-wins, matches buildSessionRecord\n // below).\n const effectiveSessionTaskId = options.taskIdOverride ?? payload.session.task_id ?? null;\n await assertImportedTaskReferencesAreReachable(paths, payload.events, effectiveSessionTaskId);\n\n const newSessionId = prefixedUlid(\"ses\");\n\n const rewrittenEvents = rewriteEvents(payload.events, newSessionId);\n assertChronologicalOrder(rewrittenEvents);\n\n const { record: sessionRecord, pathSanitizeReport } = buildSessionRecord(\n payload.session,\n manifest,\n newSessionId,\n options,\n );\n\n if (options.dryRun === true) {\n return {\n sessionId: newSessionId,\n eventCount: rewrittenEvents.length,\n finalStatus: \"imported\",\n finalSourceKind: sessionRecord.session.source.kind,\n pathSanitizeReport,\n };\n }\n\n // recursive: true lets a stripped-down workspace (manifest present but\n // `.basou/sessions` missing) recover instead of failing with ENOENT; ULID\n // collision on the new session dir itself is statistically impossible, so\n // the silent EEXIST on an existing directory is acceptable here. Concurrent\n // attempts to write the same session.yaml are caught by linkYamlFile below.\n const sessionDir = join(paths.sessions, newSessionId);\n try {\n await mkdir(sessionDir, { recursive: true });\n } catch (error: unknown) {\n throw new Error(\"Failed to create session directory\", { cause: error });\n }\n\n try {\n await writeEventsBulk(sessionDir, rewrittenEvents);\n } catch (error: unknown) {\n await rm(sessionDir, { recursive: true, force: true }).catch(() => undefined);\n throw error;\n }\n\n try {\n const sessionYamlPath = join(sessionDir, \"session.yaml\");\n await linkYamlFile(sessionYamlPath, sessionRecord);\n } catch (error: unknown) {\n await rm(sessionDir, { recursive: true, force: true }).catch(() => undefined);\n if (findErrorCode(error, \"EEXIST\")) {\n throw new Error(\"Session directory collision (retry the command)\", {\n cause: error,\n });\n }\n throw error;\n }\n\n return {\n sessionId: newSessionId,\n eventCount: rewrittenEvents.length,\n finalStatus: \"imported\",\n finalSourceKind: sessionRecord.session.source.kind,\n pathSanitizeReport,\n };\n}\n\n// Reachability guard: refuse any payload that\n// references task ids absent from the local workspace, across every carrier:\n// task_created / task_status_changed / task_reconciled events plus the\n// effective session task_id (= the override if supplied, otherwise the\n// imported session.yaml.task_id; matches buildSessionRecord's override-wins\n// semantics so we never reject on an id the final record will discard). The\n// fixed message is pathless-contract compliant; broken ids are not echoed\n// back so an adversarial payload cannot probe the local task namespace.\nasync function assertImportedTaskReferencesAreReachable(\n paths: BasouPaths,\n events: ReadonlyArray<Event>,\n effectiveSessionTaskId: string | null,\n): Promise<void> {\n const taskIdsToCheck = new Set<string>();\n for (const ev of events) {\n if (\n ev.type === \"task_created\" ||\n ev.type === \"task_status_changed\" ||\n ev.type === \"task_reconciled\" ||\n ev.type === \"task_linkage_refreshed\" ||\n ev.type === \"task_deleted\" ||\n ev.type === \"task_archived\"\n ) {\n taskIdsToCheck.add(ev.task_id);\n }\n }\n if (effectiveSessionTaskId !== null) {\n taskIdsToCheck.add(effectiveSessionTaskId);\n }\n if (taskIdsToCheck.size === 0) {\n // skip the tasks-dir scan when nothing references a task,\n // so imports that carry no task_id at all keep the original perf.\n return;\n }\n const knownTaskIds = new Set(await enumerateTaskIds(paths));\n for (const id of taskIdsToCheck) {\n if (!knownTaskIds.has(id)) {\n throw new Error(\"Imported session references unknown task_id\");\n }\n }\n}\n\n// Rewrite each event's `id` and `session_id` to brand-new values while\n// retaining every other field — including variant-specific cross-reference\n// IDs (approval_id, decision_id, task_id, file paths, raw_ref) — so that\n// chains like `approval_requested` -> `approval_approved` remain joinable on\n// the imported side. The events were already validated against EventSchema,\n// and prefixedUlid output satisfies EventIdSchema by construction, so the\n// rewritten events do not need to be re-parsed.\nfunction rewriteEvents(events: Event[], newSessionId: PrefixedId<\"ses\">): Event[] {\n return events.map((event) => ({\n ...event,\n id: prefixedUlid(\"evt\"),\n session_id: newSessionId,\n }));\n}\n\n// Enforce strict chronological order with same-ms duplicates allowed (`>=`).\n// Out-of-order events indicate an exporter bug or\n// hand-edited payload — refuse to silently sort.\nfunction assertChronologicalOrder(events: Event[]): void {\n for (let i = 1; i < events.length; i++) {\n const prevEvent = events[i - 1];\n const currEvent = events[i];\n if (prevEvent === undefined || currEvent === undefined) continue;\n const prev = Date.parse(prevEvent.occurred_at);\n const curr = Date.parse(currEvent.occurred_at);\n if (!Number.isFinite(prev) || !Number.isFinite(curr) || curr < prev) {\n throw new Error(\"Events are not in chronological order\");\n }\n }\n}\n\nfunction buildSessionRecord(\n input: SessionInnerImportInput,\n manifest: Manifest,\n newSessionId: PrefixedId<\"ses\">,\n options: ImportSessionOptions,\n): {\n record: Session;\n pathSanitizeReport: ImportSessionResult[\"pathSanitizeReport\"];\n} {\n // Sanitize before constructing the record so the operator-private\n // absolute prefix never reaches disk (= same write-time policy as\n // run.ts / exec.ts / ad-hoc-session.ts). We use the imported\n // working_directory itself as the base for related_files so paths\n // recorded relative to the original repo continue to read as\n // repo-internal; the working_directory field itself is sanitized via\n // the sentinel-based helper so the same value yields \"~/projects/foo\"\n // instead of collapsing to \".\".\n const home = homedir();\n const workingDirectoryRaw = input.working_directory;\n const workingDirectorySanitized = sanitizeWorkingDirectory(workingDirectoryRaw, {\n homedir: home,\n });\n const relatedSanitized = sanitizeRelatedFiles(input.related_files, {\n workingDirectory: workingDirectoryRaw,\n homedir: home,\n });\n\n const inner: Session[\"session\"] = {\n id: newSessionId,\n ...(options.labelOverride !== undefined || input.label !== undefined\n ? { label: options.labelOverride ?? input.label }\n : {}),\n task_id:\n options.taskIdOverride !== undefined\n ? (options.taskIdOverride as Session[\"session\"][\"task_id\"])\n : (input.task_id ?? null),\n workspace_id: manifest.workspace.id,\n source: input.source,\n started_at: input.started_at,\n ...(input.ended_at !== undefined ? { ended_at: input.ended_at } : {}),\n status: \"imported\",\n working_directory: workingDirectorySanitized,\n invocation: input.invocation,\n related_files: relatedSanitized.sanitized,\n events_log: \"events.jsonl\",\n summary: input.summary ?? null,\n };\n return {\n record: { schema_version: \"0.1.0\", session: inner },\n pathSanitizeReport: {\n relatedFiles: relatedSanitized.mutationCount,\n workingDirectoryRewritten: workingDirectorySanitized !== workingDirectoryRaw,\n },\n };\n}\n","/**\n * Version of the `@basou/core` package, aligned with `manifest.yaml`'s\n * `basou_version` field as defined in the Basou v0.1 specification.\n */\nexport const BASOU_CORE_VERSION = \"0.1.0\";\n\nexport type { CommandLookup } from \"./adapters/claude-code/index.js\";\nexport {\n claudeCodeAdapterMetadata,\n resolveClaudeCodeCommand,\n summarizeAdapterOutput,\n} from \"./adapters/claude-code/index.js\";\nexport type { ApprovalLocation, LoadedApproval } from \"./approval/index.js\";\nexport { enumerateApprovals, isLazyExpired, loadApproval } from \"./approval/index.js\";\nexport type { DecisionsRendererInput, DecisionsRendererResult } from \"./decisions/index.js\";\nexport { renderDecisions } from \"./decisions/index.js\";\nexport type { ReplayOptions, ReplayWarning } from \"./events/index.js\";\nexport { appendEvent, readAllEvents, replayEvents, writeEventsBulk } from \"./events/index.js\";\nexport type { DiffResult, FileChange, FileChangeStatus } from \"./git/diff.js\";\nexport { getDiff } from \"./git/diff.js\";\nexport type { GitSnapshot } from \"./git/snapshot.js\";\nexport { getSnapshot, resolveRepositoryRoot, tryRemoteUrl } from \"./git/snapshot.js\";\nexport type { HandoffRendererInput, HandoffRendererResult } from \"./handoff/index.js\";\nexport { renderHandoff } from \"./handoff/index.js\";\nexport type { IdPrefix, PrefixedId } from \"./ids/ulid.js\";\nexport { ID_PREFIXES, isValidPrefixedId, prefixedUlid, ulid } from \"./ids/ulid.js\";\nexport { parseDuration } from \"./lib/duration.js\";\nexport { resolveSessionId, resolveTaskId } from \"./lib/id-resolver.js\";\nexport type { SanitizePathOptions, SanitizeRelatedFilesResult } from \"./lib/path-sanitizer.js\";\nexport {\n sanitizePath,\n sanitizeRelatedFiles,\n sanitizeWorkingDirectory,\n} from \"./lib/path-sanitizer.js\";\nexport { ChildProcessRunner } from \"./runtime/child-process-runner.js\";\nexport type {\n CaptureMode,\n ProcessRunner,\n RunOptions,\n RunResult,\n} from \"./runtime/process-runner.js\";\nexport type {\n AdapterOutputEvent,\n Approval,\n ApprovalApprovedEvent,\n ApprovalExpiredEvent,\n ApprovalRejectedEvent,\n ApprovalRequestedEvent,\n ApprovalStatus,\n CommandExecutedEvent,\n DecisionRecordedEvent,\n Event,\n FileChangedEvent,\n GitSnapshotEvent,\n Manifest,\n NoteAddedEvent,\n RiskLevel,\n Session,\n SessionEndedEvent,\n SessionImportPayload,\n SessionInnerImportInput,\n SessionSourceKind,\n SessionStartedEvent,\n SessionStatus,\n SessionStatusChangedEvent,\n StatusSnapshot,\n Task,\n TaskArchivedEvent,\n TaskCreatedEvent,\n TaskDeletedEvent,\n TaskLinkageRefreshedEvent,\n TaskReconciledEvent,\n TaskStatus,\n TaskStatusChangedEvent,\n} from \"./schemas/index.js\";\nexport {\n ApprovalIdSchema,\n ApprovalSchema,\n ApprovalStatusSchema,\n DecisionIdSchema,\n EventIdSchema,\n EventSchema,\n EventSourceSchema,\n IsoTimestampSchema,\n ManifestSchema,\n RiskLevelSchema,\n SchemaVersionSchema,\n SessionIdSchema,\n SessionImportPayloadSchema,\n SessionInnerImportSchema,\n SessionSchema,\n SessionSourceKindSchema,\n SessionStatusSchema,\n StatusSchema,\n TaskIdSchema,\n TaskSchema,\n TaskStatusSchema,\n WorkspaceIdSchema,\n} from \"./schemas/index.js\";\nexport type {\n AppendBasouGitignoreResult,\n AppendEventToExistingInput,\n AppendEventToExistingResult,\n ArchiveTaskInput,\n ArchiveTaskResult,\n AttachableStatus,\n AttachTaskInput,\n AttachUpdateTaskStatusInput,\n BasouPaths,\n CreateAdHocSessionInput,\n CreateAdHocSessionResult,\n CreateAdHocTaskInput,\n CreateManifestInput,\n CreateTaskInput,\n CreateTaskResult,\n DeleteTaskInput,\n DeleteTaskResult,\n EditTaskInput,\n EditTaskResult,\n ImportSessionOptions,\n ImportSessionResult,\n LoadSessionEntriesOptions,\n LoadTaskEntriesOptions,\n LockHandle,\n LockScope,\n MarkerSection,\n ReconcileAllResult,\n ReconcileAllTasksInput,\n ReconcileAllTasksOptions,\n ReconcileFailure,\n ReconcileResult,\n ReconcileTaskInput,\n RefreshLinkageInput,\n RefreshLinkageResult,\n SessionEntry,\n SessionSkipReason,\n SuspectReason,\n TaskDocument,\n TaskSkipReason,\n TaskWriteAfterEventPhase,\n UpdateAdHocTaskStatusInput,\n UpdateTaskStatusInput,\n UpdateTaskStatusResult,\n WriteTaskFileMode,\n} from \"./storage/index.js\";\nexport {\n acquireLock,\n appendBasouGitignore,\n appendEventToExistingSession,\n archiveTask,\n assertBasouRootSafe,\n basouPaths,\n buildStatusSnapshot,\n classifySuspect,\n createAdHocSessionWithEvent,\n createManifest,\n createTaskWithEvent,\n deleteTask,\n editTask,\n ensureBasouDirectory,\n enumerateArchivedTaskIds,\n enumerateSessionDirs,\n enumerateTaskIds,\n FailedToFinalizeError,\n findErrorCode,\n GENERATED_END,\n GENERATED_START,\n importSessionFromJson,\n linkYamlFile,\n loadSessionEntries,\n loadTaskEntries,\n overwriteYamlFile,\n parseMarkers,\n readManifest,\n readMarkdownFile,\n readSessionYaml,\n readStatus,\n readTaskFile,\n readTaskFileWithArchiveFallback,\n readYamlFile,\n reconcileAllTasks,\n reconcileTask,\n refreshTaskLinkedSessions,\n renderWithMarkers,\n STUCK_THRESHOLD_MS,\n TaskWriteAfterEventError,\n updateTaskStatusWithEvent,\n writeManifest,\n writeMarkdownFile,\n writeStatus,\n writeTaskFile,\n writeYamlFile,\n} from \"./storage/index.js\";\n"],"mappings":"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","names":["resolve","z","z","join","join","z","z","join","readdir","join","z","z","readdir","join","join","path","join","join","z","z","stat","hasErrorCode","path","join","mkdir","readdir","readFile","rename","stat","unlink","join","z","z","z","join","join","readFile","unlink","join","unlink","readFile","ulid","join","readFile","join","z","z","join","readFile","DEFAULT_ATTACHABLE_STATUSES","z","join","readFile","readdir","stat","eventId","unlink","mkdir","rename","join","spawn","spawn","resolve","z","z","z","z","lstat","mkdir","join","hasErrorCode","readFile","writeFile","join","hasErrorCode","lstat","lstat","hasErrorCode","readFile","readFile","hasErrorCode","mkdir","rm","homedir","join","join","mkdir","rm","homedir"]}
|
|
1
|
+
{"version":3,"sources":["../src/adapters/claude-code/claude-code-adapter.ts","../src/ids/ulid.ts","../src/stats/active-time.ts","../src/adapters/claude-code/transcript-importer.ts","../src/adapters/codex/rollout-importer.ts","../src/approval/approval-store.ts","../src/lib/error-codes.ts","../src/schemas/approval.schema.ts","../src/schemas/shared.schema.ts","../src/storage/yaml-store.ts","../src/storage/atomic.ts","../src/decisions/decisions-renderer.ts","../src/events/event-replay.ts","../src/schemas/event.schema.ts","../src/storage/sessions.ts","../src/schemas/session.schema.ts","../src/events/event-writer.ts","../src/git/snapshot.ts","../src/storage/status.ts","../src/schemas/status.schema.ts","../src/git/diff.ts","../src/handoff/handoff-renderer.ts","../src/storage/tasks.ts","../src/schemas/task.schema.ts","../src/storage/ad-hoc-session.ts","../src/lib/path-sanitizer.ts","../src/storage/lockfile.ts","../src/storage/task-index.ts","../src/schemas/task-index.schema.ts","../src/lib/duration.ts","../src/lib/id-resolver.ts","../src/runtime/child-process-runner.ts","../src/schemas/manifest.schema.ts","../src/schemas/session-import.schema.ts","../src/stats/work-stats.ts","../src/storage/basou-dir.ts","../src/storage/gitignore.ts","../src/storage/manifest.ts","../src/storage/markdown-store.ts","../src/storage/session-import.ts","../src/index.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\n\n/**\n * Static metadata identifying the claude-code adapter as the session source.\n * Consumed by the CLI orchestration when populating `session.yaml.source`\n * and event `source` fields. The literal `kind` is part of the wire format\n * defined by the session schema; do not change without coordinated schema\n * migration.\n */\nexport const claudeCodeAdapterMetadata = {\n kind: \"claude-code-adapter\",\n version: \"0.1.0\",\n} as const;\n\n/**\n * Lookup predicate used by {@link resolveClaudeCodeCommand} to decide\n * whether a candidate executable is reachable on PATH. Exposed as a\n * parameter so tests can substitute a deterministic mock; production\n * callers should omit it and rely on the default `which`-based lookup.\n */\nexport type CommandLookup = (command: string) => Promise<boolean>;\n\n/**\n * Resolve the Claude Code CLI executable name. Tries `claude-code` first\n * and falls back to `claude`; the first candidate found on PATH wins.\n *\n * Throws a fixed-message Error when neither candidate is reachable, so\n * callers can present a single user-facing prompt to install the CLI.\n *\n * @throws Error(\"Claude Code CLI not found in PATH. Install claude-code (or claude) first.\")\n */\nexport async function resolveClaudeCodeCommand(\n lookup: CommandLookup = isOnPath,\n): Promise<{ command: string }> {\n for (const candidate of [\"claude-code\", \"claude\"]) {\n if (await lookup(candidate)) return { command: candidate };\n }\n throw new Error(\"Claude Code CLI not found in PATH. Install claude-code (or claude) first.\");\n}\n\n/**\n * Default {@link CommandLookup} backed by `which` (POSIX) — the spawn\n * succeeds with exit code 0 iff the candidate is on PATH. Windows fallback\n * is intentionally not implemented in v0.1; call sites that target Windows\n * supply their own lookup.\n */\nasync function isOnPath(command: string): Promise<boolean> {\n return new Promise((resolve) => {\n const child = spawn(\"which\", [command], { stdio: \"ignore\" });\n child.on(\"error\", () => resolve(false));\n child.on(\"exit\", (code) => resolve(code === 0));\n });\n}\n\n/**\n * Stub for the future `adapter_output` summary generator.\n *\n * The current release keeps `capture: \"none\"` and intentionally does\n * not emit `adapter_output` events, so this hook has no production\n * callers yet. The signature is committed so a later release can\n * implement raw_ref generation without retrofitting the adapter\n * scaffold.\n *\n * @throws Error - always; not implemented in this release.\n */\nexport function summarizeAdapterOutput(_stream: \"stdout\" | \"stderr\", _raw: string): string {\n throw new Error(\"adapter_output summary is not implemented in this release\");\n}\n","import { isValid as isValidUlid, monotonicFactory } from \"ulid\";\n\n/**\n * Allowed ID type prefixes for Basou entities.\n *\n * Frozen at runtime so that mutating the exported array cannot diverge from\n * the validation set used internally. The single source of truth for both\n * the `IdPrefix` type and runtime prefix checks.\n */\nexport const ID_PREFIXES = Object.freeze([\"ws\", \"task\", \"ses\", \"evt\", \"appr\", \"decision\"] as const);\n\n/**\n * Type prefix used for Basou entity IDs.\n * Format: `<prefix>_<26-char ULID>`, e.g. `ws_01HXABCDEF1234567890ABCDEF`.\n */\nexport type IdPrefix = (typeof ID_PREFIXES)[number];\n\n/**\n * A Basou entity ID as a template literal type.\n *\n * `PrefixedId<\"ses\">` narrows to ``ses_${string}`` so a session schema can\n * preserve the prefix in its inferred type beyond runtime validation.\n */\nexport type PrefixedId<P extends IdPrefix = IdPrefix> = `${P}_${string}`;\n\nconst PREFIX_SET = new Set<string>(ID_PREFIXES);\n\n// ULID body shape: 26 chars, first char 0-7 (48-bit timestamp / 5-bit Crockford\n// symbols), remaining 25 chars use Crockford alphabet excluding I, L, O, U.\n// Enforced locally because npm `ulid`'s `isValid` does not reject leading 8 or 9.\nconst ULID_BODY_REGEX = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/;\n\n// Module-scope monotonic factory. Created once at module load. Pure function\n// so it does not violate `sideEffects: false` of the surrounding package.\nconst monotonic = monotonicFactory();\n\n/**\n * Generate a Crockford Base32 ULID.\n *\n * The result is a 26-character, lexicographically time-sortable identifier.\n * Multiple calls within the same millisecond are strictly increasing for the\n * lifetime of the current process.\n *\n * NOTE: `seedTime` is forwarded to the underlying monotonic factory and is\n * NOT a deterministic seed: repeated calls with the same `seedTime` still\n * return strictly increasing values, because the factory increments its\n * internal counter on each call.\n *\n * @param seedTime Optional millisecond timestamp passed to the monotonic\n * factory. Useful for ordered generation in tests; not deterministic.\n */\nexport function ulid(seedTime?: number): string {\n return monotonic(seedTime);\n}\n\n/**\n * Generate a prefixed Basou ID, e.g. `ses_01HXABCDEF1234567890ABCDEF`.\n *\n * The return type preserves the prefix as a template literal type so that\n * downstream zod schemas can narrow an `IdPrefix` parameter through the API.\n *\n * Throws if `prefix` is not one of {@link ID_PREFIXES}. The runtime guard\n * defends against JavaScript callers and casted TypeScript that bypass the\n * compile-time `IdPrefix` constraint.\n */\nexport function prefixedUlid<P extends IdPrefix>(prefix: P): PrefixedId<P> {\n if (!PREFIX_SET.has(prefix)) {\n throw new Error(`Unknown ID prefix: ${prefix}`);\n }\n return `${prefix}_${ulid()}` as PrefixedId<P>;\n}\n\n/**\n * Check whether the given string is a valid prefixed Basou ID.\n *\n * Returns true only if the string has shape `<prefix>_<ULID>` where prefix is\n * one of {@link ID_PREFIXES} and the trailing 26 characters form a valid\n * Crockford Base32 ULID. Validation combines a strict shape regex (to enforce\n * the 0-7 leading char and the I/L/O/U exclusion) with the npm `ulid`\n * library's `isValid` for forward compatibility.\n *\n * NOTE: This validates the prefix is known. Schemas that require a specific\n * prefix (e.g. only `ses_*` for a session ID) must add their own narrowing.\n */\nexport function isValidPrefixedId(value: string): boolean {\n const idx = value.indexOf(\"_\");\n if (idx <= 0) return false;\n const prefix = value.slice(0, idx);\n const ulidPart = value.slice(idx + 1);\n if (!PREFIX_SET.has(prefix)) return false;\n if (!ULID_BODY_REGEX.test(ulidPart)) return false;\n return isValidUlid(ulidPart);\n}\n","/**\n * Gap longer than this between two consecutive engagement timestamps is treated\n * as idle and not credited as active time. A deliberately coarse heuristic: a\n * focus / billable-attention proxy, NOT a measure of model compute. 5 minutes.\n */\nexport const ACTIVE_GAP_CAP_MS = 5 * 60 * 1000;\n\n/** A wall-clock range, in epoch milliseconds, expressed as `[start, end]`. */\nexport type IntervalMs = [start: number, end: number];\n\n/** A wall-clock range expressed as ISO-8601 strings (for persistence). */\nexport type IsoInterval = { start: string; end: string };\n\n/**\n * Identifier stored in `metrics.active_time_method` for active time derived\n * from genuine engagement timestamps (conversation turns plus action events).\n * Bump this string if the derivation method changes, so stored numbers remain\n * interpretable.\n */\nexport const ENGAGED_TURNS_METHOD = \"engaged-turns\";\n\n/**\n * Build active intervals from a list of engagement timestamps (epoch ms).\n *\n * Each consecutive pair credits the range `[t_prev, t_prev + min(gap, capMs)]`:\n * activity at a timestamp is assumed to continue until the next timestamp, or\n * for at most `capMs` if the next is further away (the remainder is idle).\n * Adjacent or overlapping ranges are merged into runs. The summed duration of\n * the merged intervals equals `sum(min(gap, capMs))`, so this both reproduces a\n * gap-capped active-time scalar and yields the real ranges needed for\n * cross-session union.\n *\n * Non-finite timestamps are skipped (the single invalid-timestamp policy) and\n * the input is sorted internally, so callers may pass timestamps in any order.\n */\nexport function activeTimeFromTimestamps(\n timestampsMs: ReadonlyArray<number>,\n capMs: number,\n): { ms: number; intervals: IntervalMs[] } {\n const sorted = timestampsMs.filter((t) => Number.isFinite(t)).sort((a, b) => a - b);\n const raw: IntervalMs[] = [];\n for (let i = 1; i < sorted.length; i++) {\n const prev = sorted[i - 1];\n const curr = sorted[i];\n if (prev === undefined || curr === undefined) continue;\n const gap = curr - prev;\n if (gap <= 0) continue;\n raw.push([prev, prev + Math.min(gap, capMs)]);\n }\n const intervals = mergeIntervals(raw);\n return { ms: sumDurations(intervals), intervals };\n}\n\n/** Merge a set of (possibly unsorted / overlapping) intervals into disjoint runs. */\nexport function mergeIntervals(intervals: ReadonlyArray<IntervalMs>): IntervalMs[] {\n const sorted = [...intervals].sort((a, b) => a[0] - b[0]);\n const merged: IntervalMs[] = [];\n for (const [start, end] of sorted) {\n const last = merged[merged.length - 1];\n // `start <= last end` joins adjacent runs (an interval ending exactly where\n // the next begins) as well as genuine overlaps.\n if (last !== undefined && start <= last[1]) {\n if (end > last[1]) last[1] = end;\n } else {\n merged.push([start, end]);\n }\n }\n return merged;\n}\n\n/**\n * De-duplicate active time across many sessions: merge all their intervals and\n * return the union duration (so concurrent sessions do not double-count human\n * wall-clock) together with the merged ranges.\n */\nexport function unionDurationMs(intervals: ReadonlyArray<IntervalMs>): {\n ms: number;\n merged: IntervalMs[];\n} {\n const merged = mergeIntervals(intervals);\n return { ms: sumDurations(merged), merged };\n}\n\n/** Convert epoch-ms intervals to ISO ranges for persistence. */\nexport function intervalsMsToIso(intervals: ReadonlyArray<IntervalMs>): IsoInterval[] {\n return intervals.map(([start, end]) => ({\n start: new Date(start).toISOString(),\n end: new Date(end).toISOString(),\n }));\n}\n\n/** Parse stored ISO ranges back to epoch-ms intervals, skipping unparseable ones. */\nexport function intervalsIsoToMs(intervals: ReadonlyArray<IsoInterval>): IntervalMs[] {\n const out: IntervalMs[] = [];\n for (const { start, end } of intervals) {\n const s = Date.parse(start);\n const e = Date.parse(end);\n if (Number.isFinite(s) && Number.isFinite(e) && e >= s) out.push([s, e]);\n }\n return out;\n}\n\nfunction sumDurations(intervals: ReadonlyArray<IntervalMs>): number {\n let total = 0;\n for (const [start, end] of intervals) total += end - start;\n return total;\n}\n","import { type PrefixedId, prefixedUlid } from \"../../ids/ulid.js\";\nimport type { Event } from \"../../schemas/event.schema.js\";\nimport type { Manifest } from \"../../schemas/manifest.schema.js\";\nimport type { SessionImportPayload } from \"../../schemas/session-import.schema.js\";\nimport {\n ACTIVE_GAP_CAP_MS,\n activeTimeFromTimestamps,\n ENGAGED_TURNS_METHOD,\n intervalsMsToIso,\n} from \"../../stats/active-time.js\";\n\n/**\n * The `source` string stamped on every event derived from a Claude Code\n * native transcript, and the matching session `source.kind`.\n */\nexport const CLAUDE_IMPORT_SOURCE = \"claude-code-import\";\n\n/**\n * One parsed line of a Claude Code native transcript\n * (`~/.claude/projects/<encoded-cwd>/<uuid>.jsonl`). The shape is the\n * vendor's internal message log, not Basou's event schema, so every field\n * is read defensively — unknown record types and missing fields are skipped\n * rather than rejected (transcripts are an undocumented format that may gain\n * fields between Claude Code releases).\n */\nexport type ClaudeTranscriptRecord = Record<string, unknown>;\n\n/** Options for {@link claudeTranscriptToImportPayload}. */\nexport type ClaudeTranscriptToPayloadOptions = {\n /** Workspace id of the target Basou workspace (from its manifest). */\n workspaceId: Manifest[\"workspace\"][\"id\"];\n /**\n * Claude Code session id (transcript filename / `sessionId`). Stored as\n * `session.source.external_id` so re-imports can be deduplicated. Falls\n * back to the `sessionId` read from the records when omitted.\n */\n externalId?: string;\n};\n\n/**\n * Transform a Claude Code native transcript into a Basou\n * {@link SessionImportPayload}, ready to hand to `importSessionFromJson`.\n *\n * This is a pure function: no disk or environment access. It DERIVES Basou's\n * provenance-level events from the transcript's message-level records, rather\n * than mapping one-to-one:\n *\n * - `session_started` / `session_ended` from the first / last timestamped record.\n * - `command_executed` from each `Bash` tool use, recorded as `bash -c \"<cmd>\"`\n * (the transcript carries the shell line, not a parsed argv).\n * - `file_changed` from each `Edit` / `Write` / `NotebookEdit` tool use.\n * - `decision_recorded` from each `AskUserQuestion` tool use: one decision per\n * question, titled `<question> -> <chosen answer>`. The chosen answer is read\n * from the paired result record's structured `toolUseResult.answers` map; a\n * question with no recorded string answer is skipped.\n *\n * Exit codes and per-command durations are not present in the transcript, so\n * `command_executed.exit_code` is `null` and `duration_ms` is `0`.\n *\n * Returns `null` when the transcript has no timestamped records, or no\n * observable command / file / decision action — such sessions carry no\n * provenance worth importing and are skipped by the caller.\n *\n * Event `id` / `session_id` are placeholders; `importSessionFromJson` mints\n * fresh ids on the way in. They are valid-by-construction so the payload\n * still passes `SessionImportPayloadSchema` validation upstream.\n */\nexport function claudeTranscriptToImportPayload(\n records: ReadonlyArray<ClaudeTranscriptRecord>,\n options: ClaudeTranscriptToPayloadOptions,\n): SessionImportPayload | null {\n const placeholderSessionId = prefixedUlid(\"ses\");\n // AskUserQuestion answers live on the *result* record, which arrives after\n // the originating tool_use; pre-index them so decisions can be derived at the\n // tool_use site in the single forward pass below.\n const askAnswers = indexAskAnswers(records);\n const derived: Event[] = [];\n const relatedFiles = new Set<string>();\n // Real transcripts are NOT strictly ordered by timestamp on disk\n // (sidechains and async writes interleave), so file order cannot be\n // trusted. Track the earliest / latest timestamp explicitly, and order the\n // derived events by occurred_at below.\n let minTs: string | undefined;\n let maxTs: string | undefined;\n let workingDir: string | undefined;\n let claudeSessionId: string | undefined;\n // Model-usage rollup: the transcript carries per-assistant-message token\n // usage; sum it into a session total (see metrics on the payload below).\n let outputTokens = 0;\n let inputTokens = 0;\n let cachedInputTokens = 0;\n // A single assistant message is split across multiple records (thinking /\n // text / tool_use), each carrying the SAME message.id and the SAME usage.\n // Count usage once per message.id to avoid multiplying the token totals.\n const seenMessageIds = new Set<string>();\n // Genuine engagement timestamps for the billing-oriented active-time metric:\n // real human prompts and assistant messages (action tool uses live inside\n // assistant messages, so they are subsumed). Sub-agent sidechains and\n // tool-result / meta records are excluded so autonomous loops and noise do\n // not inflate billable time. Assistant messages are counted once per\n // message.id, matching the token dedup above.\n const engagementTsMs: number[] = [];\n const seenEngagementMessageIds = new Set<string>();\n\n for (const record of records) {\n const ts = readString(record.timestamp);\n if (ts === undefined) continue;\n if (minTs === undefined || Date.parse(ts) < Date.parse(minTs)) minTs = ts;\n if (maxTs === undefined || Date.parse(ts) > Date.parse(maxTs)) maxTs = ts;\n if (workingDir === undefined) workingDir = readString(record.cwd);\n if (claudeSessionId === undefined) claudeSessionId = readString(record.sessionId);\n\n if (record.isSidechain !== true) {\n const tsMs = Date.parse(ts);\n if (Number.isFinite(tsMs)) {\n const recType = readString(record.type);\n if (recType === \"user\") {\n if (isHumanUserMessage(record)) engagementTsMs.push(tsMs);\n } else if (recType === \"assistant\") {\n const msg = isObject(record.message) ? record.message : undefined;\n const mid = msg !== undefined ? readString(msg.id) : undefined;\n if (mid === undefined || !seenEngagementMessageIds.has(mid)) {\n if (mid !== undefined) seenEngagementMessageIds.add(mid);\n engagementTsMs.push(tsMs);\n }\n }\n }\n }\n\n if (readString(record.type) !== \"assistant\") continue;\n\n const message = isObject(record.message) ? record.message : undefined;\n const usage = message !== undefined && isObject(message.usage) ? message.usage : undefined;\n if (usage !== undefined) {\n // Dedup by message.id; records without an id are counted individually.\n const messageId = message !== undefined ? readString(message.id) : undefined;\n const alreadyCounted = messageId !== undefined && seenMessageIds.has(messageId);\n if (!alreadyCounted) {\n if (messageId !== undefined) seenMessageIds.add(messageId);\n outputTokens += readNonNegInt(usage.output_tokens);\n inputTokens += readNonNegInt(usage.input_tokens);\n cachedInputTokens += readNonNegInt(usage.cache_read_input_tokens);\n }\n }\n\n const cwd = readString(record.cwd) ?? workingDir ?? \".\";\n for (const item of toolUses(record)) {\n const name = readString(item.name);\n const input = isObject(item.input) ? item.input : undefined;\n if (input === undefined) continue;\n\n if (name === \"Bash\") {\n const command = readString(input.command);\n if (command !== undefined) {\n derived.push(commandExecutedEvent(ts, placeholderSessionId, command, cwd));\n }\n continue;\n }\n\n if (name === \"AskUserQuestion\") {\n const useId = readString(item.id);\n const answers = useId !== undefined ? askAnswers.get(useId) : undefined;\n if (answers !== undefined) {\n // One decision per question; preserve the order the questions were\n // asked (Object.entries keeps insertion order for string keys, and\n // the stable sort below keeps it among the same-timestamp events).\n for (const [question, answer] of Object.entries(answers)) {\n if (question.length === 0) continue;\n const answerStr = typeof answer === \"string\" && answer.length > 0 ? answer : undefined;\n const title = answerStr !== undefined ? `${question} -> ${answerStr}` : question;\n derived.push(decisionRecordedEvent(ts, placeholderSessionId, title));\n }\n }\n continue;\n }\n\n if (name === \"Edit\" || name === \"Write\" || name === \"NotebookEdit\") {\n const path = readString(input.file_path) ?? readString(input.notebook_path);\n if (path !== undefined) {\n // Edit / NotebookEdit mutate an existing file; Write is treated as a\n // creation. The transcript does not reliably distinguish a Write\n // that overwrites, so \"added\" is the conservative default.\n const changeType = name === \"Write\" ? \"added\" : \"modified\";\n relatedFiles.add(path);\n derived.push(fileChangedEvent(ts, placeholderSessionId, path, changeType));\n }\n }\n }\n }\n\n if (minTs === undefined || maxTs === undefined) return null;\n if (derived.length === 0) return null;\n\n // Order derived events by occurred_at so the assembled stream is\n // non-decreasing — importSessionFromJson rejects out-of-order events.\n // Array.prototype.sort is stable, so same-timestamp events keep their\n // transcript order.\n derived.sort((a, b) => Date.parse(a.occurred_at) - Date.parse(b.occurred_at));\n\n const events: Event[] = [\n sessionStartedEvent(minTs, placeholderSessionId),\n ...derived,\n sessionEndedEvent(maxTs, placeholderSessionId),\n ];\n\n const externalId = options.externalId ?? claudeSessionId;\n // Human-readable label: when + how much, so the session reads as content in\n // `basou session list` / handoff rather than an opaque id. The source id is\n // kept structurally in `source.external_id` (not the label), and paths are\n // deliberately excluded — the label is NOT path-sanitized downstream, so a\n // raw file path here would leak an operator-private prefix.\n const commandCount = derived.reduce((n, e) => (e.type === \"command_executed\" ? n + 1 : n), 0);\n const fileCount = relatedFiles.size;\n const date = minTs.slice(0, 10);\n const label = `claude-code ${date}: ${commandCount} ${commandCount === 1 ? \"command\" : \"commands\"}, ${fileCount} ${fileCount === 1 ? \"file\" : \"files\"}`;\n\n // Engaged-active time from the genuine engagement series (needs >= 2 points\n // to bound any gap); omitted when too sparse so stats falls back to the\n // event-derived measure.\n const active =\n engagementTsMs.length >= 2\n ? activeTimeFromTimestamps(engagementTsMs, ACTIVE_GAP_CAP_MS)\n : undefined;\n\n // Only include fields actually present; omit metrics entirely for a\n // transcript that carried neither token usage nor an engaged-time signal.\n const metricsFields = {\n ...(outputTokens > 0 ? { output_tokens: outputTokens } : {}),\n ...(inputTokens > 0 ? { input_tokens: inputTokens } : {}),\n ...(cachedInputTokens > 0 ? { cached_input_tokens: cachedInputTokens } : {}),\n ...(active !== undefined && active.ms > 0\n ? {\n active_time_ms: active.ms,\n active_intervals: intervalsMsToIso(active.intervals),\n active_gap_cap_ms: ACTIVE_GAP_CAP_MS,\n active_time_method: ENGAGED_TURNS_METHOD,\n }\n : {}),\n };\n const metrics = Object.keys(metricsFields).length > 0 ? metricsFields : undefined;\n\n const payload: SessionImportPayload = {\n schema_version: \"0.1.0\",\n session: {\n label,\n workspace_id: options.workspaceId,\n source: {\n kind: CLAUDE_IMPORT_SOURCE,\n version: \"0.1.0\",\n ...(externalId !== undefined ? { external_id: externalId } : {}),\n },\n started_at: minTs,\n ended_at: maxTs,\n // Validated against the canonical enum here; importSessionFromJson\n // overwrites it with the literal \"imported\" regardless.\n status: \"imported\",\n working_directory: workingDir ?? \".\",\n invocation: { command: \"claude\", args: [], exit_code: null },\n related_files: [...relatedFiles].sort(),\n summary: null,\n ...(metrics !== undefined ? { metrics } : {}),\n },\n events,\n };\n return payload;\n}\n\n// --- event builders -------------------------------------------------------\n\nfunction baseEvent(\n occurredAt: string,\n sessionId: PrefixedId<\"ses\">,\n): {\n schema_version: \"0.1.0\";\n id: PrefixedId<\"evt\">;\n session_id: PrefixedId<\"ses\">;\n occurred_at: string;\n source: string;\n} {\n return {\n schema_version: \"0.1.0\",\n id: prefixedUlid(\"evt\"),\n session_id: sessionId,\n occurred_at: occurredAt,\n source: CLAUDE_IMPORT_SOURCE,\n };\n}\n\nfunction sessionStartedEvent(occurredAt: string, sessionId: PrefixedId<\"ses\">): Event {\n return { ...baseEvent(occurredAt, sessionId), type: \"session_started\" };\n}\n\nfunction sessionEndedEvent(occurredAt: string, sessionId: PrefixedId<\"ses\">): Event {\n return { ...baseEvent(occurredAt, sessionId), type: \"session_ended\" };\n}\n\nfunction commandExecutedEvent(\n occurredAt: string,\n sessionId: PrefixedId<\"ses\">,\n command: string,\n cwd: string,\n): Event {\n return {\n ...baseEvent(occurredAt, sessionId),\n type: \"command_executed\",\n command: \"bash\",\n args: [\"-c\", command],\n cwd,\n exit_code: null,\n duration_ms: 0,\n };\n}\n\nfunction fileChangedEvent(\n occurredAt: string,\n sessionId: PrefixedId<\"ses\">,\n path: string,\n changeType: \"added\" | \"modified\",\n): Event {\n return {\n ...baseEvent(occurredAt, sessionId),\n type: \"file_changed\",\n path,\n change_type: changeType,\n };\n}\n\nfunction decisionRecordedEvent(\n occurredAt: string,\n sessionId: PrefixedId<\"ses\">,\n title: string,\n): Event {\n return {\n ...baseEvent(occurredAt, sessionId),\n type: \"decision_recorded\",\n decision_id: prefixedUlid(\"decision\"),\n title,\n };\n}\n\n// --- defensive readers ----------------------------------------------------\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\n/** Read a non-negative integer token count, treating anything else as 0. */\nfunction readNonNegInt(value: unknown): number {\n return typeof value === \"number\" && Number.isInteger(value) && value >= 0 ? value : 0;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * A `user` record is a genuine human prompt when its message content is a\n * non-empty string, or an array containing at least one non-`tool_result`\n * block (e.g. text / image). A record whose content is only `tool_result`\n * blocks is the assistant's tool-feedback loop, not human input, and is\n * excluded from the engagement series.\n */\nfunction isHumanUserMessage(record: ClaudeTranscriptRecord): boolean {\n const message = isObject(record.message) ? record.message : undefined;\n if (message === undefined) return false;\n const content = message.content;\n if (typeof content === \"string\") return content.length > 0;\n if (Array.isArray(content)) {\n return content.some((block) => {\n if (!isObject(block)) return false;\n const type = readString(block.type);\n return type !== undefined && type !== \"tool_result\";\n });\n }\n return false;\n}\n\n/**\n * Extract the `tool_use` items from an assistant record's\n * `message.content[]`. Returns an empty array for any record that does not\n * match the expected nesting, so callers can iterate unconditionally.\n */\nfunction toolUses(record: ClaudeTranscriptRecord): Array<Record<string, unknown>> {\n const message = isObject(record.message) ? record.message : undefined;\n const content = message !== undefined && Array.isArray(message.content) ? message.content : [];\n const result: Array<Record<string, unknown>> = [];\n for (const item of content) {\n if (isObject(item) && readString(item.type) === \"tool_use\") {\n result.push(item);\n }\n }\n return result;\n}\n\n/**\n * Index the structured answers of every `AskUserQuestion` tool use by its\n * tool_use id. The chosen answers live on the *result* record's\n * `toolUseResult.answers` — a `{ \"<question>\": \"<chosen answer>\" }` map — which\n * is only present on AskUserQuestion results, so its presence is the\n * discriminator. The result record carries the originating tool_use id inside\n * its `message.content[].tool_use_id`.\n */\nfunction indexAskAnswers(\n records: ReadonlyArray<ClaudeTranscriptRecord>,\n): Map<string, Record<string, unknown>> {\n const byId = new Map<string, Record<string, unknown>>();\n for (const record of records) {\n const result = record.toolUseResult;\n if (!isObject(result)) continue;\n const answers = result.answers;\n if (!isObject(answers)) continue;\n const message = isObject(record.message) ? record.message : undefined;\n const content = message !== undefined && Array.isArray(message.content) ? message.content : [];\n for (const item of content) {\n if (isObject(item) && readString(item.type) === \"tool_result\") {\n const id = readString(item.tool_use_id);\n if (id !== undefined) byId.set(id, answers);\n }\n }\n }\n return byId;\n}\n","import { type PrefixedId, prefixedUlid } from \"../../ids/ulid.js\";\nimport type { Event } from \"../../schemas/event.schema.js\";\nimport type { Manifest } from \"../../schemas/manifest.schema.js\";\nimport type { SessionImportPayload } from \"../../schemas/session-import.schema.js\";\nimport {\n ACTIVE_GAP_CAP_MS,\n activeTimeFromTimestamps,\n ENGAGED_TURNS_METHOD,\n intervalsMsToIso,\n} from \"../../stats/active-time.js\";\n\n/**\n * The `source` string stamped on every event derived from an OpenAI Codex\n * native rollout log, and the matching session `source.kind`.\n */\nexport const CODEX_IMPORT_SOURCE = \"codex-import\";\n\n/**\n * One parsed line of a Codex rollout log\n * (`~/.codex/sessions/<YYYY>/<MM>/<DD>/rollout-*.jsonl`). Each line is an\n * envelope `{ type, timestamp, payload }` where `payload` shape depends on\n * `type`. As with the Claude importer the format is the vendor's internal\n * log, not Basou's schema, so every field is read defensively — unknown\n * record / payload types and missing fields are skipped rather than rejected.\n */\nexport type CodexRolloutRecord = Record<string, unknown>;\n\n/** Options for {@link codexRolloutToImportPayload}. */\nexport type CodexRolloutToPayloadOptions = {\n /** Workspace id of the target Basou workspace (from its manifest). */\n workspaceId: Manifest[\"workspace\"][\"id\"];\n /**\n * Codex session id (`session_meta.payload.id`). Stored as\n * `session.source.external_id` so re-imports can be deduplicated. Falls back\n * to the id read from the rollout's `session_meta` record when omitted.\n */\n externalId?: string;\n};\n\n/**\n * Transform a Codex native rollout log into a Basou {@link SessionImportPayload},\n * ready to hand to `importSessionFromJson`.\n *\n * This is a pure function: no disk or environment access. It DERIVES Basou's\n * provenance-level events from the rollout's message-level records:\n *\n * - `session_started` / `session_ended` from the first / last timestamped record.\n * - `command_executed` from each `exec_command` function call, recorded as\n * `bash -c \"<cmd>\"`. The shell line and working directory come from the\n * call's JSON `arguments` (`{ cmd, workdir }`); the exit code and duration\n * are parsed from the paired `function_call_output` (matched by `call_id`),\n * whose text carries `Process exited with code N` and `Wall time: X seconds`.\n *\n * Unlike the Claude importer this derives no `file_changed`: Codex has no\n * dedicated edit tool and applies edits inside `exec_command` (e.g.\n * `apply_patch`), so there is no clean file-change signal to map. Decisions\n * and approvals are likewise not derivable — Codex records an `approval_policy`\n * (a policy, not a per-action approval) and has no structured question/answer\n * record. Both are deferred.\n *\n * Returns `null` when the rollout has no timestamped records or no observable\n * `exec_command` — such sessions carry no provenance worth importing and are\n * skipped by the caller.\n *\n * Event `id` / `session_id` are placeholders; `importSessionFromJson` mints\n * fresh ids on the way in. They are valid-by-construction so the payload still\n * passes `SessionImportPayloadSchema` validation upstream.\n */\nexport function codexRolloutToImportPayload(\n records: ReadonlyArray<CodexRolloutRecord>,\n options: CodexRolloutToPayloadOptions,\n): SessionImportPayload | null {\n const placeholderSessionId = prefixedUlid(\"ses\");\n // A command's exit code and duration live on its `function_call_output`,\n // which arrives after the originating `function_call`; pre-index outputs by\n // call_id so commands can be completed in the single forward pass below.\n const outputsByCallId = indexOutputs(records);\n const derived: Event[] = [];\n // Real rollouts are written in arrival order, but track the earliest /\n // latest timestamp explicitly (rather than trusting first / last line) and\n // order the derived events by occurred_at below, mirroring the Claude path.\n let minTs: string | undefined;\n let maxTs: string | undefined;\n let workingDir: string | undefined;\n let codexSessionId: string | undefined;\n // Codex emits cumulative token_count events; the last one's\n // total_token_usage is the session total (see metrics on the payload below).\n let lastTokenTotals: Record<string, unknown> | undefined;\n // Genuine engagement timestamps for the billing-oriented active-time metric:\n // conversation turns (user / agent messages and task boundaries) plus the\n // exec_command actions. Token-count heartbeats, reasoning, web-search and\n // tool-output records are excluded so they cannot inflate billable time.\n const engagementTsMs: number[] = [];\n\n for (const record of records) {\n const ts = readString(record.timestamp);\n if (ts === undefined) continue;\n if (minTs === undefined || Date.parse(ts) < Date.parse(minTs)) minTs = ts;\n if (maxTs === undefined || Date.parse(ts) > Date.parse(maxTs)) maxTs = ts;\n\n const payload = isObject(record.payload) ? record.payload : undefined;\n if (payload === undefined) continue;\n\n if (readString(record.type) === \"session_meta\") {\n // The session-level cwd and id are the most reliable working directory\n // and dedup key; take the first occurrence and keep it.\n if (workingDir === undefined) workingDir = readString(payload.cwd);\n if (codexSessionId === undefined) codexSessionId = readString(payload.id);\n continue;\n }\n\n if (readString(record.type) === \"event_msg\" && readString(payload.type) === \"token_count\") {\n const info = isObject(payload.info) ? payload.info : undefined;\n const totals =\n info !== undefined && isObject(info.total_token_usage) ? info.total_token_usage : undefined;\n // Cumulative; keep the latest so the final value is the session total.\n if (totals !== undefined) lastTokenTotals = totals;\n continue;\n }\n\n if (readString(record.type) === \"event_msg\") {\n const pt = readString(payload.type);\n if (\n pt === \"user_message\" ||\n pt === \"agent_message\" ||\n pt === \"task_started\" ||\n pt === \"task_complete\"\n ) {\n const tsMs = Date.parse(ts);\n if (Number.isFinite(tsMs)) engagementTsMs.push(tsMs);\n }\n // event_msg records are never response_items; skip the rest.\n continue;\n }\n\n if (readString(record.type) !== \"response_item\") continue;\n if (readString(payload.type) !== \"function_call\") continue;\n if (readString(payload.name) !== \"exec_command\") continue;\n\n const command = readExecCommand(payload.arguments);\n if (command === undefined) continue;\n const cwd = command.workdir ?? workingDir ?? \".\";\n const output = readCallId(payload.call_id, outputsByCallId);\n const execTsMs = Date.parse(ts);\n if (Number.isFinite(execTsMs)) engagementTsMs.push(execTsMs);\n derived.push(\n commandExecutedEvent(ts, placeholderSessionId, command.cmd, cwd, {\n exitCode: parseExitCode(output),\n durationMs: parseWallTimeMs(output),\n }),\n );\n }\n\n if (minTs === undefined || maxTs === undefined) return null;\n if (derived.length === 0) return null;\n\n // Order derived events by occurred_at so the assembled stream is\n // non-decreasing — importSessionFromJson rejects out-of-order events.\n // Array.prototype.sort is stable, so same-timestamp events keep their\n // rollout order.\n derived.sort((a, b) => Date.parse(a.occurred_at) - Date.parse(b.occurred_at));\n\n const events: Event[] = [\n sessionStartedEvent(minTs, placeholderSessionId),\n ...derived,\n sessionEndedEvent(maxTs, placeholderSessionId),\n ];\n\n const externalId = options.externalId ?? codexSessionId;\n // Human-readable label: when + how much, so the session reads as content in\n // `basou session list` / handoff rather than an opaque id. The source id is\n // kept structurally in `source.external_id` (not the label), and paths are\n // deliberately excluded — the label is NOT path-sanitized downstream, so a\n // raw file path here would leak an operator-private prefix.\n const commandCount = derived.length;\n const date = minTs.slice(0, 10);\n const label = `codex ${date}: ${commandCount} ${commandCount === 1 ? \"command\" : \"commands\"}`;\n\n // Engaged-active time from the genuine engagement series (needs >= 2 points\n // to bound any gap); omitted when too sparse so stats falls back to the\n // event-derived measure.\n const active =\n engagementTsMs.length >= 2\n ? activeTimeFromTimestamps(engagementTsMs, ACTIVE_GAP_CAP_MS)\n : undefined;\n\n // Token totals from the last cumulative token_count event; include only the\n // fields actually present (> 0). Metrics is emitted if either token usage or\n // an engaged-time signal is present.\n const tokenFields =\n lastTokenTotals === undefined\n ? {}\n : {\n ...(readNonNegInt(lastTokenTotals.output_tokens) > 0\n ? { output_tokens: readNonNegInt(lastTokenTotals.output_tokens) }\n : {}),\n ...(readNonNegInt(lastTokenTotals.input_tokens) > 0\n ? { input_tokens: readNonNegInt(lastTokenTotals.input_tokens) }\n : {}),\n ...(readNonNegInt(lastTokenTotals.cached_input_tokens) > 0\n ? { cached_input_tokens: readNonNegInt(lastTokenTotals.cached_input_tokens) }\n : {}),\n ...(readNonNegInt(lastTokenTotals.reasoning_output_tokens) > 0\n ? { reasoning_output_tokens: readNonNegInt(lastTokenTotals.reasoning_output_tokens) }\n : {}),\n };\n const metricsFields = {\n ...tokenFields,\n ...(active !== undefined && active.ms > 0\n ? {\n active_time_ms: active.ms,\n active_intervals: intervalsMsToIso(active.intervals),\n active_gap_cap_ms: ACTIVE_GAP_CAP_MS,\n active_time_method: ENGAGED_TURNS_METHOD,\n }\n : {}),\n };\n const metrics = Object.keys(metricsFields).length > 0 ? metricsFields : undefined;\n\n const payload: SessionImportPayload = {\n schema_version: \"0.1.0\",\n session: {\n label,\n workspace_id: options.workspaceId,\n source: {\n kind: CODEX_IMPORT_SOURCE,\n version: \"0.1.0\",\n ...(externalId !== undefined ? { external_id: externalId } : {}),\n },\n started_at: minTs,\n ended_at: maxTs,\n // Validated against the canonical enum here; importSessionFromJson\n // overwrites it with the literal \"imported\" regardless.\n status: \"imported\",\n working_directory: workingDir ?? \".\",\n invocation: { command: \"codex\", args: [], exit_code: null },\n related_files: [],\n summary: null,\n ...(metrics !== undefined ? { metrics } : {}),\n },\n events,\n };\n return payload;\n}\n\n// --- event builders -------------------------------------------------------\n\nfunction baseEvent(\n occurredAt: string,\n sessionId: PrefixedId<\"ses\">,\n): {\n schema_version: \"0.1.0\";\n id: PrefixedId<\"evt\">;\n session_id: PrefixedId<\"ses\">;\n occurred_at: string;\n source: string;\n} {\n return {\n schema_version: \"0.1.0\",\n id: prefixedUlid(\"evt\"),\n session_id: sessionId,\n occurred_at: occurredAt,\n source: CODEX_IMPORT_SOURCE,\n };\n}\n\nfunction sessionStartedEvent(occurredAt: string, sessionId: PrefixedId<\"ses\">): Event {\n return { ...baseEvent(occurredAt, sessionId), type: \"session_started\" };\n}\n\nfunction sessionEndedEvent(occurredAt: string, sessionId: PrefixedId<\"ses\">): Event {\n return { ...baseEvent(occurredAt, sessionId), type: \"session_ended\" };\n}\n\nfunction commandExecutedEvent(\n occurredAt: string,\n sessionId: PrefixedId<\"ses\">,\n command: string,\n cwd: string,\n outcome: { exitCode: number | null; durationMs: number },\n): Event {\n return {\n ...baseEvent(occurredAt, sessionId),\n type: \"command_executed\",\n command: \"bash\",\n args: [\"-c\", command],\n cwd,\n exit_code: outcome.exitCode,\n duration_ms: outcome.durationMs,\n };\n}\n\n// --- defensive readers ----------------------------------------------------\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\n/** Read a non-negative integer token count, treating anything else as 0. */\nfunction readNonNegInt(value: unknown): number {\n return typeof value === \"number\" && Number.isInteger(value) && value >= 0 ? value : 0;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Parse an `exec_command` call's JSON `arguments` string into its shell line\n * and optional working directory. Returns `undefined` when the arguments are\n * not parseable or carry no `cmd`, so the caller can skip the call.\n */\nfunction readExecCommand(value: unknown): { cmd: string; workdir: string | undefined } | undefined {\n const raw = readString(value);\n if (raw === undefined) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return undefined;\n }\n if (!isObject(parsed)) return undefined;\n const cmd = readString(parsed.cmd);\n if (cmd === undefined) return undefined;\n return { cmd, workdir: readString(parsed.workdir) };\n}\n\nfunction readCallId(value: unknown, outputs: ReadonlyMap<string, string>): string | undefined {\n const callId = readString(value);\n return callId !== undefined ? outputs.get(callId) : undefined;\n}\n\n/**\n * Codex's `exec_command` output text reports the child's exit code as\n * `Process exited with code N` (N may be negative for signal termination).\n * Returns `null` when the line is absent — the command may have yielded before\n * completing or the session was cut off mid-command.\n */\nfunction parseExitCode(output: string | undefined): number | null {\n if (output === undefined) return null;\n const match = output.match(/Process exited with code (-?\\d+)/);\n return match?.[1] !== undefined ? Number.parseInt(match[1], 10) : null;\n}\n\n/**\n * Codex's `exec_command` output text reports wall-clock duration as\n * `Wall time: X seconds`. Returns `0` (the schema floor) when absent or\n * non-finite, matching the Claude importer's missing-duration default.\n */\nfunction parseWallTimeMs(output: string | undefined): number {\n if (output === undefined) return 0;\n const match = output.match(/Wall time:\\s*([\\d.]+)\\s*seconds/);\n if (match?.[1] === undefined) return 0;\n const seconds = Number.parseFloat(match[1]);\n return Number.isFinite(seconds) ? Math.round(seconds * 1000) : 0;\n}\n\n/**\n * Index every `function_call_output`'s text by its `call_id`, so a command's\n * exit code and duration can be looked up at the originating `function_call`.\n * Only string outputs are kept — image / structured tool results are arrays\n * and carry no command outcome.\n */\nfunction indexOutputs(records: ReadonlyArray<CodexRolloutRecord>): Map<string, string> {\n const byId = new Map<string, string>();\n for (const record of records) {\n if (readString(record.type) !== \"response_item\") continue;\n const payload = isObject(record.payload) ? record.payload : undefined;\n if (payload === undefined) continue;\n if (readString(payload.type) !== \"function_call_output\") continue;\n const callId = readString(payload.call_id);\n const output = readString(payload.output);\n if (callId !== undefined && output !== undefined) byId.set(callId, output);\n }\n return byId;\n}\n","import { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { findErrorCode } from \"../lib/error-codes.js\";\nimport { type Approval, ApprovalSchema } from \"../schemas/approval.schema.js\";\nimport type { BasouPaths } from \"../storage/basou-dir.js\";\nimport { readYamlFile } from \"../storage/yaml-store.js\";\n\n/** Which side of `.basou/approvals/` an approval YAML lives on. */\nexport type ApprovalLocation = \"pending\" | \"resolved\";\n\n/** Result returned by {@link loadApproval}: the parsed approval and where it was found. */\nexport type LoadedApproval = {\n approval: Approval;\n location: ApprovalLocation;\n};\n\n/**\n * Locate and load the approval YAML for `approvalId`. Searches resolved\n * first so that a duplicated YAML (the crash-window scenario where both\n * pending and resolved exist for the same id) returns the resolved-side\n * record — matching the dedupe rule used by `approval list` and\n * `resolveApprovalId`. Returns null if neither directory contains the\n * YAML. Throws with a pathless message on read or schema-validation\n * failure.\n */\nexport async function loadApproval(\n paths: BasouPaths,\n approvalId: string,\n): Promise<LoadedApproval | null> {\n for (const location of [\"resolved\", \"pending\"] as const) {\n const filePath = join(paths.approvals[location], `${approvalId}.yaml`);\n let raw: unknown;\n try {\n raw = await readYamlFile(filePath);\n } catch (error: unknown) {\n // ENOENT (i.e. \"YAML file not found\") → continue to the other directory.\n if (error instanceof Error && error.message === \"YAML file not found\") continue;\n throw new Error(\"Failed to read approval\", { cause: error });\n }\n const result = ApprovalSchema.safeParse(raw);\n if (!result.success) {\n throw new Error(\"Failed to read approval\", { cause: result.error });\n }\n // Defensive id check: a hand-edited YAML whose `id` field disagrees\n // with the filename-derived id would otherwise let the CLI render or\n // mutate one approval while citing another. Treat the mismatch as a\n // read failure rather than silently picking one side.\n if (result.data.id !== approvalId) {\n throw new Error(\"Failed to read approval\", {\n cause: new Error(\n `Approval id mismatch: filename id ${approvalId} vs YAML body id ${result.data.id}`,\n ),\n });\n }\n return { approval: result.data, location };\n }\n return null;\n}\n\n/**\n * Enumerate approval IDs by inspecting `<id>.yaml` filenames in pending\n * and resolved. ENOENT on either directory is treated as empty (e.g. a\n * workspace that has no resolved approvals yet). YAML parse and schema\n * validation are NOT performed; callers that need the parsed approval\n * should use {@link loadApproval} per ID.\n */\nexport async function enumerateApprovals(paths: BasouPaths): Promise<{\n pending: string[];\n resolved: string[];\n}> {\n const [pending, resolved] = await Promise.all([\n enumerateIds(paths.approvals.pending),\n enumerateIds(paths.approvals.resolved),\n ]);\n return { pending, resolved };\n}\n\nasync function enumerateIds(dir: string): Promise<string[]> {\n let entries: string[];\n try {\n const dirents = await readdir(dir, { withFileTypes: true });\n entries = dirents\n .filter((e) => e.isFile() && e.name.endsWith(\".yaml\"))\n .map((e) => e.name.slice(0, -\".yaml\".length));\n } catch (error: unknown) {\n if (findErrorCode(error, \"ENOENT\")) return [];\n throw new Error(\"Failed to enumerate approvals\", { cause: error });\n }\n return entries;\n}\n\n/**\n * Return true when an approval is in `pending` state and its `expires_at`\n * timestamp has elapsed. Used by `basou approval list` / `show` to surface\n * a `(expired)` label without mutating the YAML file. Approval expiry uses\n * lazy-evaluation semantics; actual `approval_expired` event firing is\n * deferred to a later step.\n *\n * `now` is taken as a parameter so a single CLI invocation can share one\n * \"now\" across every record it inspects (avoids boundary races where two\n * reads of `Date.now()` straddle an expiry instant).\n */\nexport function isLazyExpired(approval: Approval, now: Date): boolean {\n if (approval.status !== \"pending\") return false;\n if (approval.expires_at === null) return false;\n const expiresMs = Date.parse(approval.expires_at);\n if (!Number.isFinite(expiresMs)) return false;\n return expiresMs < now.getTime();\n}\n","/**\n * Walk the cause chain (up to `depth` levels) looking for an Error whose\n * errno-style `code` matches `code`. Returns true on the first match.\n * Resilient to wrapper depth changes so that ENOENT detection survives\n * future error-wrapping refactors.\n */\nexport function findErrorCode(error: unknown, code: string, depth = 4): boolean {\n let cur: unknown = error;\n for (let i = 0; i < depth && cur instanceof Error; i++) {\n const c = (cur as { code?: unknown }).code;\n if (typeof c === \"string\" && c === code) return true;\n cur = (cur as Error).cause;\n }\n return false;\n}\n","import { z } from \"zod\";\nimport {\n ApprovalIdSchema,\n IsoTimestampSchema,\n RiskLevelSchema,\n SchemaVersionSchema,\n SessionIdSchema,\n} from \"./shared.schema.js\";\n\n/**\n * Lifecycle states of a Basou approval. The status is stored directly on\n * the approval YAML (flat shape) so that pending → resolved transitions\n * are atomic-move + in-place rewrites rather than schema-variant swaps.\n */\nexport const ApprovalStatusSchema = z.enum([\"pending\", \"approved\", \"rejected\", \"expired\"]);\n/** Inferred runtime type for {@link ApprovalStatusSchema}. */\nexport type ApprovalStatus = z.infer<typeof ApprovalStatusSchema>;\n\n/**\n * Schema for `.basou/approvals/{pending,resolved}/<approval_id>.yaml`.\n *\n * The schema is intentionally flat (one shape regardless of `status`) so\n * that pending and resolved YAMLs share the same parser. Required vs.\n * optional semantics by status (e.g. `rejection_reason` MUST be set when\n * `status === \"rejected\"`) are enforced at the CLI orchestration layer\n * rather than here, mirroring the approval event variants in\n * `event.schema.ts`.\n *\n * The `action` field is `{ kind: string }` with `passthrough()` so that\n * adapter-defined keys (e.g. `command`, `path`, `target_url`) survive the\n * round-trip without being stripped — matching the approval_requested\n * event variant.\n */\nexport const ApprovalSchema = z.object({\n schema_version: SchemaVersionSchema,\n id: ApprovalIdSchema,\n session_id: SessionIdSchema,\n created_at: IsoTimestampSchema,\n status: ApprovalStatusSchema,\n risk_level: RiskLevelSchema,\n action: z.object({ kind: z.string() }).passthrough(),\n reason: z.string(),\n expires_at: IsoTimestampSchema.nullable().default(null),\n // The four fields below are null while `status === \"pending\"` and set\n // once a resolver records a decision. Defaulting to null keeps the\n // pending YAML free of explicit nulls if a producer omits them.\n resolver: z.string().nullable().default(null),\n resolved_at: IsoTimestampSchema.nullable().default(null),\n note: z.string().nullable().default(null),\n rejection_reason: z.string().nullable().default(null),\n});\n\n/** Inferred runtime type for {@link ApprovalSchema}. */\nexport type Approval = z.infer<typeof ApprovalSchema>;\n","import { z } from \"zod\";\nimport { type IdPrefix, isValidPrefixedId, type PrefixedId } from \"../ids/ulid.js\";\n\n/**\n * Schema version literal pinned to \"0.1.0\" for Basou v0.1.\n * Reused across every entity schema so inferred types narrow to the literal.\n */\nexport const SchemaVersionSchema = z.literal(\"0.1.0\");\n\n/**\n * ISO 8601 timestamp with explicit timezone offset (e.g. `+09:00`).\n *\n * The spec samples include offsets, so the default zod `.datetime()` (which\n * rejects offsets) is insufficient; `{ offset: true }` is required.\n */\nexport const IsoTimestampSchema = z.string().datetime({ offset: true });\n\n// Internal factory shared by every prefixed-ID schema. Not exported because\n// the public API surface should only expose the six fully-typed ID schemas.\nconst createPrefixedIdSchema = <P extends IdPrefix>(prefix: P) => {\n const refiner = (value: string): value is PrefixedId<P> =>\n isValidPrefixedId(value) && value.startsWith(`${prefix}_`);\n return z.string().refine(refiner, { message: `Expected ${prefix}_<ULID>` });\n};\n\n/** Workspace ID schema: validates `ws_<26-char ULID>`. */\nexport const WorkspaceIdSchema = createPrefixedIdSchema(\"ws\");\n/** Task ID schema: validates `task_<26-char ULID>`. */\nexport const TaskIdSchema = createPrefixedIdSchema(\"task\");\n/** Session ID schema: validates `ses_<26-char ULID>`. */\nexport const SessionIdSchema = createPrefixedIdSchema(\"ses\");\n/** Event ID schema: validates `evt_<26-char ULID>`. */\nexport const EventIdSchema = createPrefixedIdSchema(\"evt\");\n/** Approval ID schema: validates `appr_<26-char ULID>`. */\nexport const ApprovalIdSchema = createPrefixedIdSchema(\"appr\");\n/** Decision ID schema: validates `decision_<26-char ULID>`. */\nexport const DecisionIdSchema = createPrefixedIdSchema(\"decision\");\n\n/**\n * Risk level vocabulary fixed by the spec. Adapters MUST emit one of these\n * four values; arbitrary strings are rejected at schema parse time.\n */\nexport const RiskLevelSchema = z.enum([\"low\", \"medium\", \"high\", \"critical\"]);\n/** Inferred runtime type for {@link RiskLevelSchema}. */\nexport type RiskLevel = z.infer<typeof RiskLevelSchema>;\n\n/**\n * Source attribution for events (e.g. \"claude-code-adapter\",\n * \"git-capability\", \"terminal-recording\", \"local-cli\", \"human\"). Free-form\n * non-empty string in v0.1; a stricter enum may be introduced post-v0.1.\n */\nexport const EventSourceSchema = z.string().min(1);\n","import { readFile } from \"node:fs/promises\";\nimport { parse, stringify } from \"yaml\";\nimport { atomicCreate, atomicReplace } from \"./atomic.js\";\n\n/**\n * Read a YAML file as `unknown`. Caller MUST validate via a zod schema.\n *\n * Throws Error with pathless message and the original native error attached\n * as `cause` for I/O failures and YAML parse errors. All fs and parse exits\n * go through fixed messages so absolute paths cannot leak via `error.message`.\n */\nexport async function readYamlFile(filePath: string): Promise<unknown> {\n let body: string;\n try {\n body = await readFile(filePath, \"utf8\");\n } catch (error: unknown) {\n if (hasErrorCode(error) && error.code === \"ENOENT\") {\n throw new Error(\"YAML file not found\", { cause: error });\n }\n throw new Error(\"Failed to read YAML file\", { cause: error });\n }\n try {\n return parse(body);\n } catch (error: unknown) {\n throw new Error(\"Failed to parse YAML content\", { cause: error });\n }\n}\n\n/**\n * Write a value as YAML using {@link atomicReplace} for crash-resistant\n * atomicity. The shared helper handles the tmp-file + rename sequence,\n * `wx` collision guard, and best-effort tmp cleanup on failure. This\n * wrapper adds the YAML serialisation and the pathless error vocabulary.\n */\nexport async function writeYamlFile(filePath: string, value: unknown): Promise<void> {\n const body = stringify(value);\n try {\n await atomicReplace(filePath, body);\n } catch (error: unknown) {\n throw new Error(\"Failed to write YAML file\", { cause: error });\n }\n}\n\n/**\n * Atomically create a new YAML file. Like {@link writeYamlFile} but\n * delegates to {@link atomicCreate} so a pre-existing target fails with\n * EEXIST instead of being silently overwritten.\n *\n * Used by `basou approval approve` / `reject` to write the resolved-side\n * YAML, so a concurrent resolver cannot overwrite an already-resolved\n * approval.\n *\n * Throws `Error(\"Failed to write YAML file\", { cause })` on failure; if\n * `cause.code === \"EEXIST\"` the caller can detect a target-exists race.\n */\nexport async function linkYamlFile(filePath: string, value: unknown): Promise<void> {\n const body = stringify(value);\n try {\n await atomicCreate(filePath, body);\n } catch (error: unknown) {\n throw new Error(\"Failed to write YAML file\", { cause: error });\n }\n}\n\n/**\n * Overwrite an existing YAML file atomically. Like {@link writeYamlFile}\n * but with a distinct pathless message label, used for files that\n * legitimately need in-place mutation (e.g. session.yaml's status /\n * ended_at lifecycle updates).\n */\nexport async function overwriteYamlFile(filePath: string, value: unknown): Promise<void> {\n const body = stringify(value);\n try {\n await atomicReplace(filePath, body);\n } catch (error: unknown) {\n throw new Error(\"Failed to overwrite YAML file\", { cause: error });\n }\n}\n\nfunction hasErrorCode(error: unknown): error is Error & { code: string } {\n if (!(error instanceof Error)) return false;\n return typeof (error as unknown as Record<string, unknown>).code === \"string\";\n}\n","import { randomUUID } from \"node:crypto\";\nimport { link, rename, unlink, writeFile } from \"node:fs/promises\";\n\n/**\n * Atomically create a new file at `targetPath` via tmp + link.\n *\n * Strategy: write the body to a sibling tmp file (`${targetPath}.tmp.<uuid>`)\n * with the `wx` flag, then `link()` the tmp inode into place at `targetPath`.\n * If the target already exists, `link` fails with EEXIST — callers detect this\n * via `findErrorCode(error, \"EEXIST\")` to surface a domain-specific\n * \"already exists\" message.\n *\n * The tmp file lives in the SAME directory as the target so `link` cannot\n * fail with EXDEV. On every code path (success and failure) the tmp inode\n * is best-effort unlinked, so after a successful call the tmp side of the\n * hard-link pair is removed and only `targetPath` remains.\n *\n * The native fs error is re-thrown WITHOUT wrapping so callers can attach\n * their own pathless message via `new Error(\"<fixed msg>\", { cause })`. The\n * caller is responsible for the final error vocabulary (pathless contract).\n */\nexport async function atomicCreate(targetPath: string, content: string | Buffer): Promise<void> {\n const tmpPath = `${targetPath}.tmp.${randomUUID()}`;\n try {\n await writeFile(tmpPath, content, { encoding: \"utf8\", flag: \"wx\" });\n await link(tmpPath, targetPath);\n } catch (error: unknown) {\n await unlink(tmpPath).catch(() => undefined);\n throw error;\n }\n // tmp inode is now linked twice (tmp + target); unlink the tmp side so\n // disk does not carry a spurious sibling after a successful create.\n await unlink(tmpPath).catch(() => undefined);\n}\n\n/**\n * Atomically replace the file at `targetPath` via tmp + rename.\n *\n * Strategy: write the body to a sibling tmp file (`${targetPath}.tmp.<uuid>`)\n * with the `wx` flag, then `rename()` the tmp over `targetPath`. Silently\n * overwrites any existing file at `targetPath`. The tmp file lives in the\n * SAME directory as the target so `rename` cannot fail with EXDEV. `rename`\n * consumes the tmp file, so no post-success cleanup is needed.\n *\n * On failure the tmp file is best-effort unlinked so disk never carries a\n * half-written rename source. The native fs error is re-thrown WITHOUT\n * wrapping so callers can attach their own pathless message.\n */\nexport async function atomicReplace(targetPath: string, content: string | Buffer): Promise<void> {\n const tmpPath = `${targetPath}.tmp.${randomUUID()}`;\n try {\n await writeFile(tmpPath, content, { encoding: \"utf8\", flag: \"wx\" });\n await rename(tmpPath, targetPath);\n } catch (error: unknown) {\n await unlink(tmpPath).catch(() => undefined);\n throw error;\n }\n}\n","import { lstat } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { type ReplayWarning, replayEvents } from \"../events/event-replay.js\";\nimport type { BasouPaths } from \"../storage/basou-dir.js\";\nimport { loadSessionEntries, type SessionSkipReason } from \"../storage/sessions.js\";\n\nexport type DecisionsRendererInput = {\n paths: BasouPaths;\n nowIso: string;\n onWarning?: (warning: ReplayWarning, sessionId: string) => void;\n onSessionSkip?: (sessionId: string, reason: SessionSkipReason) => void;\n};\n\nexport type DecisionsRendererResult = {\n /** Generated body WITHOUT BASOU:GENERATED markers. */\n body: string;\n decisionCount: number;\n};\n\ntype DecisionRecord = {\n decisionId: string;\n title: string;\n occurredAt: string;\n sessionId: string;\n // Rich fields. All optional; populated only when the decision_recorded\n // event carried the field.\n rationale: string | null | undefined;\n alternatives: readonly string[] | undefined;\n rejectedReason: string | null | undefined;\n linkedEvents: readonly string[] | undefined;\n linkedFiles: readonly string[] | undefined;\n};\n\n/**\n * Render the body of `decisions.md` from `decision_recorded` events across\n * every healthy session in the workspace.\n *\n * Session enumeration goes through {@link loadSessionEntries} (the same path\n * the handoff renderer uses) so that `session.yaml`-broken sessions are\n * skipped in BOTH outputs and the handoff's `decisionCount` summary stays\n * consistent with the number of sections rendered here.\n *\n * Order: `occurred_at` ascending with `decisionId` (= ULID) as tie-breaker.\n * Both fields are monotonic, so the result is a stable cross-session\n * timeline.\n *\n * The decision rich fields (rationale / alternatives / rejected_reason /\n * linked_events / linked_files) are rendered when the event carries them.\n * `linked_events` and `linked_files` are OPAQUE references: the schema only\n * validates the SHAPE, not existence — references that cannot be resolved\n * to a known event id or an existing file on disk are surfaced inline as\n * `(missing)` so cross-workspace round-trips never reject parse-time.\n */\nexport async function renderDecisions(\n input: DecisionsRendererInput,\n): Promise<DecisionsRendererResult> {\n const now = new Date(input.nowIso);\n // Same rationale as handoff-renderer. Track which\n // sessions already had `events_jsonl_unreadable` surfaced so non-running\n // sessions whose events.jsonl is unreadable still produce a stderr\n // warning instead of silently dropping their decisions.\n const unreadableEmitted = new Set<string>();\n const wrappedSkip: (sid: string, reason: SessionSkipReason) => void = (sid, reason) => {\n if (reason === \"events_jsonl_unreadable\") unreadableEmitted.add(sid);\n input.onSessionSkip?.(sid, reason);\n };\n const loadOpts: Parameters<typeof loadSessionEntries>[1] = { now, onSkip: wrappedSkip };\n if (input.onWarning !== undefined) loadOpts.onWarning = input.onWarning;\n const entries = await loadSessionEntries(input.paths, loadOpts);\n\n const decisions: DecisionRecord[] = [];\n // Workspace-wide event id index, populated during the same scan that\n // collects decisions, so `linked_events` membership can be resolved\n // without a second pass over events.jsonl.\n const knownEventIds = new Set<string>();\n for (const entry of entries) {\n const sessionDir = join(input.paths.sessions, entry.sessionId);\n try {\n for await (const ev of replayEvents(sessionDir, {\n onWarning: (w) => input.onWarning?.(w, entry.sessionId),\n })) {\n knownEventIds.add(ev.id);\n if (ev.type === \"decision_recorded\") {\n decisions.push({\n decisionId: ev.decision_id,\n title: ev.title,\n occurredAt: ev.occurred_at,\n sessionId: entry.sessionId,\n rationale: ev.rationale,\n alternatives: ev.alternatives,\n rejectedReason: ev.rejected_reason,\n linkedEvents: ev.linked_events,\n linkedFiles: ev.linked_files,\n });\n }\n }\n } catch {\n if (!unreadableEmitted.has(entry.sessionId)) {\n wrappedSkip(entry.sessionId, \"events_jsonl_unreadable\");\n }\n }\n }\n decisions.sort((a, b) => {\n const c = Date.parse(a.occurredAt) - Date.parse(b.occurredAt);\n return c !== 0 ? c : a.decisionId.localeCompare(b.decisionId);\n });\n\n // Resolve linked_files relative to the repository root (= parent of\n // `.basou/`). Existence is checked with `lstat` so symlinks are treated\n // honestly — a dangling symlink is reported as `(missing)`. The check\n // runs once per unique path so repeated references share their lookup.\n const repoRoot = dirname(input.paths.root);\n const fileExistenceCache = new Map<string, boolean>();\n async function fileExists(relPath: string): Promise<boolean> {\n const cached = fileExistenceCache.get(relPath);\n if (cached !== undefined) return cached;\n const abs = resolve(repoRoot, relPath);\n let exists: boolean;\n try {\n await lstat(abs);\n exists = true;\n } catch {\n exists = false;\n }\n fileExistenceCache.set(relPath, exists);\n return exists;\n }\n\n const body = await formatDecisionsBody({\n nowIso: input.nowIso,\n decisions,\n knownEventIds,\n fileExists,\n });\n return { body, decisionCount: decisions.length };\n}\n\nasync function formatDecisionsBody(args: {\n nowIso: string;\n decisions: ReadonlyArray<DecisionRecord>;\n knownEventIds: ReadonlySet<string>;\n fileExists: (relPath: string) => Promise<boolean>;\n}): Promise<string> {\n const lines: string[] = [];\n lines.push(\"# Decisions\");\n lines.push(\"\");\n lines.push(`> Generated at ${args.nowIso}`);\n lines.push(\"\");\n if (args.decisions.length === 0) {\n lines.push(\"(no decisions recorded yet)\");\n return lines.join(\"\\n\");\n }\n for (const d of args.decisions) {\n lines.push(`## ${d.decisionId}: ${d.title}`);\n lines.push(\"\");\n const occurredDate = d.occurredAt.slice(0, 10); // YYYY-MM-DD\n lines.push(`- 決定日: ${occurredDate}`);\n lines.push(`- session: ${shortDecisionSessionId(d.sessionId)}`);\n lines.push(`- 判断: ${d.title}`);\n if (typeof d.rationale === \"string\" && d.rationale.length > 0) {\n lines.push(`- rationale: ${d.rationale}`);\n }\n if (d.alternatives !== undefined && d.alternatives.length > 0) {\n lines.push(`- alternatives: ${d.alternatives.join(\", \")}`);\n }\n if (typeof d.rejectedReason === \"string\" && d.rejectedReason.length > 0) {\n lines.push(`- rejected_reason: ${d.rejectedReason}`);\n }\n if (d.linkedEvents !== undefined && d.linkedEvents.length > 0) {\n const parts = d.linkedEvents.map((eid) =>\n args.knownEventIds.has(eid) ? eid : `${eid} (missing)`,\n );\n lines.push(`- linked_events: ${parts.join(\", \")}`);\n }\n if (d.linkedFiles !== undefined && d.linkedFiles.length > 0) {\n const parts = await Promise.all(\n d.linkedFiles.map(async (path) =>\n (await args.fileExists(path)) ? path : `${path} (missing)`,\n ),\n );\n lines.push(`- linked_files: ${parts.join(\", \")}`);\n }\n lines.push(\"\");\n }\n return lines.join(\"\\n\");\n}\n\nfunction shortDecisionSessionId(sessionId: string): string {\n const SES = \"ses_\";\n if (sessionId.startsWith(SES)) return sessionId.slice(SES.length, SES.length + 10);\n return sessionId.slice(0, 10);\n}\n","import { createReadStream } from \"node:fs\";\nimport { stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { findErrorCode } from \"../lib/error-codes.js\";\nimport { type Event, EventSchema } from \"../schemas/event.schema.js\";\n\n/**\n * Recoverable warning surfaced via {@link ReplayOptions.onWarning}. The replay\n * generator never throws on these — it skips the offending line and continues.\n *\n * `partial_trailing_line` indicates the events.jsonl did not end with `\\n` and\n * the unterminated tail parsed as a complete event. The line is dropped\n * instead of yielded so consumers cannot accidentally observe a\n * partially-written record.\n */\nexport type ReplayWarning =\n | { kind: \"partial_trailing_line\"; line: number }\n | { kind: \"malformed_json\"; line: number; cause: unknown }\n | { kind: \"schema_violation\"; line: number; cause: unknown };\n\nexport type ReplayOptions = {\n /**\n * Hook to receive recoverable warnings (partial line / malformed JSON /\n * schema violation). When omitted, warnings are silently dropped — callers\n * that want to surface them (e.g. CLI orchestration) MUST provide this hook.\n */\n onWarning?: (warning: ReplayWarning) => void;\n};\n\n/**\n * Stream events from `<sessionDir>/events.jsonl` line by line.\n *\n * Behavior:\n * - ENOENT or empty file: yields nothing without warning.\n * - I/O error: throws `Error(\"Failed to read events.jsonl\")` with the native\n * error attached as `cause`. The thrown message never embeds an absolute\n * path (pathless contract).\n * - Trailing partial line that parses as a valid event: dropped silently when\n * {@link ReplayOptions.onWarning} is omitted; otherwise reported as\n * `partial_trailing_line`. A trailing partial line that fails JSON parsing\n * is reported as `malformed_json` instead.\n * - Malformed JSON / schema violation: skipped, with the corresponding\n * warning when a hook is provided.\n *\n * Single-writer-per-session is assumed (see `event-writer.ts` JSDoc on\n * {@link appendEvent}). Concurrent writers may interleave lines beyond\n * `PIPE_BUF` and are not recovered here in v0.1.\n */\n// NOTE: switched from plan A (Transform stream + readline) to plan B (manual\n// chunk-level split) because plan A's source-stream errors do not propagate\n// through `pipe()` to the readline iterator, so an EACCES on the events.jsonl\n// hangs the for-await loop instead of throwing. Plan B observes errors\n// directly via the for-await over `createReadStream` and reaches end-of-stream\n// deterministically with the trailing buffer in hand.\nexport async function* replayEvents(\n sessionDir: string,\n options: ReplayOptions = {},\n): AsyncGenerator<Event, void, void> {\n const filePath = join(sessionDir, \"events.jsonl\");\n\n // Probe existence first so ENOENT (= empty session) is silent while every\n // other I/O failure surfaces as a single fixed-message error.\n try {\n await stat(filePath);\n } catch (error: unknown) {\n if (findErrorCode(error, \"ENOENT\")) return;\n throw new Error(\"Failed to read events.jsonl\", { cause: error });\n }\n\n let stream: ReturnType<typeof createReadStream>;\n try {\n stream = createReadStream(filePath, { encoding: \"utf8\" });\n } catch (error: unknown) {\n throw new Error(\"Failed to read events.jsonl\", { cause: error });\n }\n\n let buffer = \"\";\n let lineNo = 0;\n\n try {\n for await (const chunk of stream as unknown as AsyncIterable<string>) {\n buffer += chunk;\n let newlineIdx = buffer.indexOf(\"\\n\");\n while (newlineIdx !== -1) {\n lineNo += 1;\n const rawLine = buffer.slice(0, newlineIdx);\n buffer = buffer.slice(newlineIdx + 1);\n const ev = processLine(rawLine, lineNo, options);\n if (ev !== null) yield ev;\n newlineIdx = buffer.indexOf(\"\\n\");\n }\n }\n } catch (error: unknown) {\n throw new Error(\"Failed to read events.jsonl\", { cause: error });\n }\n\n // Stream ended mid-line: anything left in `buffer` is the trailing partial\n // line. Empty / whitespace-only trailing content is treated as a normal end\n // of file (e.g. a final '\\n' was stripped by the loop above).\n const trimmed = buffer.replace(/[\\r\\n\\t ]+$/u, \"\");\n if (trimmed.length === 0) return;\n lineNo += 1;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(trimmed);\n } catch (cause) {\n // The trailing buffer was non-empty AND JSON-invalid. Either\n // partial_trailing_line or malformed_json captures the same observable\n // outcome; we surface malformed_json because the JSON layer rejected it\n // first and the line number is meaningful for the consumer.\n options.onWarning?.({ kind: \"malformed_json\", line: lineNo, cause });\n return;\n }\n\n const result = EventSchema.safeParse(parsed);\n if (!result.success) {\n options.onWarning?.({ kind: \"schema_violation\", line: lineNo, cause: result.error });\n return;\n }\n\n // Valid JSON + valid event schema BUT no terminating newline. Drop instead\n // of yielding so a half-flushed write cannot be consumed as a real event.\n options.onWarning?.({ kind: \"partial_trailing_line\", line: lineNo });\n}\n\nfunction processLine(rawLine: string, lineNo: number, options: ReplayOptions): Event | null {\n const trimmed = rawLine.trim();\n if (trimmed.length === 0) return null;\n let parsed: unknown;\n try {\n parsed = JSON.parse(trimmed);\n } catch (cause) {\n options.onWarning?.({ kind: \"malformed_json\", line: lineNo, cause });\n return null;\n }\n const result = EventSchema.safeParse(parsed);\n if (!result.success) {\n options.onWarning?.({ kind: \"schema_violation\", line: lineNo, cause: result.error });\n return null;\n }\n return result.data;\n}\n\n/**\n * Eager array helper: collect every event from {@link replayEvents} into\n * memory. Convenience for callers that need the full list in one structure\n * (e.g. `basou session show` rendering).\n */\nexport async function readAllEvents(\n sessionDir: string,\n options: ReplayOptions = {},\n): Promise<Event[]> {\n const out: Event[] = [];\n for await (const ev of replayEvents(sessionDir, options)) {\n out.push(ev);\n }\n return out;\n}\n","import { z } from \"zod\";\nimport {\n ApprovalIdSchema,\n DecisionIdSchema,\n EventIdSchema,\n EventSourceSchema,\n IsoTimestampSchema,\n RiskLevelSchema,\n SchemaVersionSchema,\n SessionIdSchema,\n TaskIdSchema,\n} from \"./shared.schema.js\";\n\n// Common base every event variant extends. Each variant declares its own\n// `type: z.literal(...)` and adds variant-specific fields.\nconst BaseEventSchema = z.object({\n schema_version: SchemaVersionSchema,\n id: EventIdSchema,\n session_id: SessionIdSchema,\n occurred_at: IsoTimestampSchema,\n source: EventSourceSchema,\n});\n\n// --- Session lifecycle events ---\n\nconst SessionStartedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"session_started\"),\n});\n\nconst SessionEndedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"session_ended\"),\n exit_code: z.number().int().optional(),\n});\n\n// `from`/`to` use `string` to keep this module independent of session.schema\n// and avoid a circular import. A later event-replay layer may narrow these to\n// SessionStatusSchema by relocating the enum into shared.schema.\nconst SessionStatusChangedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"session_status_changed\"),\n from: z.string(),\n to: z.string(),\n});\n\n// --- Approval events ---\n\nconst ApprovalRequestedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"approval_requested\"),\n approval_id: ApprovalIdSchema,\n expires_at: IsoTimestampSchema.nullable().default(null),\n risk_level: RiskLevelSchema,\n // `action.kind` is required; additional fields are allowed to support\n // future action shapes (shell_command, external_send, ...).\n action: z.object({ kind: z.string() }).passthrough(),\n reason: z.string(),\n status: z.literal(\"pending\"),\n});\n\nconst ApprovalApprovedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"approval_approved\"),\n approval_id: ApprovalIdSchema,\n resolver: z.string().optional(),\n note: z.string().nullable().optional(),\n});\n\nconst ApprovalRejectedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"approval_rejected\"),\n approval_id: ApprovalIdSchema,\n resolver: z.string().optional(),\n reason: z.string(),\n});\n\nconst ApprovalExpiredEventSchema = BaseEventSchema.extend({\n type: z.literal(\"approval_expired\"),\n approval_id: ApprovalIdSchema,\n});\n\n// --- Command / Git / File events ---\n\n// `command` is the spawned executable name only (e.g. \"npm\"); arguments are\n// kept in `args` to preserve quoting and avoid shell-injection round-trips.\n// `exit_code` is null when the child terminated by signal. `signal` records\n// the child's terminating signal; `received_signal` records what the parent\n// process received (SIGINT/SIGTERM) and forwarded as cancellation, so a\n// timeout (signal set, received_signal absent) can be distinguished from a\n// user interrupt (both set).\nconst CommandExecutedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"command_executed\"),\n command: z.string(),\n args: z.array(z.string()),\n cwd: z.string(),\n exit_code: z.number().int().nullable(),\n signal: z.string().nullable().optional(),\n received_signal: z.string().nullable().optional(),\n duration_ms: z.number().int().nonnegative(),\n});\n\nconst GitSnapshotEventSchema = BaseEventSchema.extend({\n type: z.literal(\"git_snapshot\"),\n head: z.string(),\n branch: z.string(),\n dirty: z.boolean(),\n staged: z.array(z.string()),\n unstaged: z.array(z.string()),\n untracked: z.array(z.string()),\n ahead: z.number().int().nonnegative().optional(),\n behind: z.number().int().nonnegative().optional(),\n});\n\nconst FileChangedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"file_changed\"),\n path: z.string(),\n change_type: z.enum([\"added\", \"modified\", \"deleted\", \"renamed\"]),\n // Renamed entries record the previous path here. Optional + nullable to\n // keep the wire format stable for added / modified / deleted events.\n old_path: z.string().nullable().optional(),\n});\n\n// --- Decision / Task / Note events ---\n\n// Decision rich fields are all optional so v0.1 payloads\n// (= core 4 fields only) round-trip unchanged. References are opaque — the\n// schema only validates the SHAPE (EventId format, non-empty / length-capped\n// strings); existence of the referenced event or file is the renderer's\n// concern and surfaces as `(missing)` rather than a parse failure, so\n// import/export round-trips across workspaces never reject on a stale id.\nconst DecisionRecordedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"decision_recorded\"),\n decision_id: DecisionIdSchema,\n title: z.string(),\n rationale: z.string().nullable().optional(),\n alternatives: z.array(z.string().min(1)).optional(),\n rejected_reason: z.string().nullable().optional(),\n linked_events: z.array(EventIdSchema).optional(),\n linked_files: z.array(z.string().min(1).max(4096)).optional(),\n});\n\nconst TaskCreatedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"task_created\"),\n task_id: TaskIdSchema,\n title: z.string(),\n});\n\nconst TaskStatusChangedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"task_status_changed\"),\n task_id: TaskIdSchema,\n from: z.string(),\n to: z.string(),\n});\n\n// emitted by `basou task reconcile --write` after broken session\n// references in a task.md are cleaned up. `.strict()` so that any extra field\n// (likely a core-side miscoding of an audit value) is rejected at parse time\n// rather than silently stripped — the event is the audit trail.\nconst TaskReconciledEventSchema = BaseEventSchema.extend({\n type: z.literal(\"task_reconciled\"),\n task_id: TaskIdSchema,\n removed_created_in_session: SessionIdSchema.nullable().default(null),\n created_in_session_replacement: SessionIdSchema.nullable().default(null),\n removed_linked_sessions: z.array(SessionIdSchema).default([]),\n}).strict();\n\n// v0.2: emitted by `basou task refresh-linkage` after the task.md\n// `linked_sessions[]` snapshot is re-derived from `session.yaml.task_id`\n// matches across the workspace. Distinct from `task_reconciled` (= broken\n// ref cleanup) so each event carries a single, focused audit story.\n// `.strict()` for the same reason as TaskReconciledEvent — the event is the\n// authoritative record. The three count/array fields are all optional with\n// sensible defaults so the schema is backward-compatible (= a future caller\n// that omits them parses to an empty refresh, which is also the no-op\n// dry-run record shape).\nconst TaskLinkageRefreshedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"task_linkage_refreshed\"),\n task_id: TaskIdSchema,\n added_linked_sessions: z.array(SessionIdSchema).default([]),\n removed_linked_sessions: z.array(SessionIdSchema).default([]),\n final_count: z.number().int().nonnegative().optional(),\n}).strict();\n\n// v0.2 lifecycle events for `basou task delete` / `basou task archive`.\n// Both are `.strict()` so the audit record is exactly what the orchestrator\n// emits, and both carry the task's last-known title so events.jsonl can\n// describe what was deleted / archived without requiring the (now gone or\n// relocated) task.md.\nconst TaskDeletedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"task_deleted\"),\n task_id: TaskIdSchema,\n title: z.string().min(1),\n}).strict();\n\nconst TaskArchivedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"task_archived\"),\n task_id: TaskIdSchema,\n title: z.string().min(1),\n}).strict();\n\nconst NoteAddedEventSchema = BaseEventSchema.extend({\n type: z.literal(\"note_added\"),\n body: z.string(),\n});\n\n// --- Adapter output (`.strict()` rejects raw bodies) ---\n//\n// The spec forbids embedding raw adapter output (`content`, `body`, `raw`,\n// ...) directly in events.jsonl (see\n// `docs/spec/schemas.md#74-adapter_output-constraint-important`). The\n// strict variant rejects any schema-unknown key so that contract is\n// enforced at parse time.\nconst AdapterOutputEventSchema = BaseEventSchema.extend({\n type: z.literal(\"adapter_output\"),\n stream: z.enum([\"stdout\", \"stderr\"]),\n summary: z.string(),\n raw_ref: z.string(),\n redacted: z.boolean().optional(),\n}).strict();\n\n/**\n * Discriminated union of every Basou v0.1 event type. The `type` literal\n * narrows TypeScript to the appropriate variant. The `adapter_output`\n * variant is uniquely strict to bar raw adapter bodies.\n */\nexport const EventSchema = z.discriminatedUnion(\"type\", [\n SessionStartedEventSchema,\n SessionEndedEventSchema,\n SessionStatusChangedEventSchema,\n ApprovalRequestedEventSchema,\n ApprovalApprovedEventSchema,\n ApprovalRejectedEventSchema,\n ApprovalExpiredEventSchema,\n CommandExecutedEventSchema,\n GitSnapshotEventSchema,\n FileChangedEventSchema,\n DecisionRecordedEventSchema,\n TaskCreatedEventSchema,\n TaskStatusChangedEventSchema,\n TaskReconciledEventSchema,\n TaskLinkageRefreshedEventSchema,\n TaskDeletedEventSchema,\n TaskArchivedEventSchema,\n NoteAddedEventSchema,\n AdapterOutputEventSchema,\n]);\n\n/** Inferred runtime type for any Basou event. */\nexport type Event = z.infer<typeof EventSchema>;\n\n/** Narrowed runtime type for the `session_started` event variant. */\nexport type SessionStartedEvent = z.infer<typeof SessionStartedEventSchema>;\n/** Narrowed runtime type for the `session_ended` event variant. */\nexport type SessionEndedEvent = z.infer<typeof SessionEndedEventSchema>;\n/** Narrowed runtime type for the `session_status_changed` event variant. */\nexport type SessionStatusChangedEvent = z.infer<typeof SessionStatusChangedEventSchema>;\n/** Narrowed runtime type for the `approval_requested` event variant. */\nexport type ApprovalRequestedEvent = z.infer<typeof ApprovalRequestedEventSchema>;\n/** Narrowed runtime type for the `approval_approved` event variant. */\nexport type ApprovalApprovedEvent = z.infer<typeof ApprovalApprovedEventSchema>;\n/** Narrowed runtime type for the `approval_rejected` event variant. */\nexport type ApprovalRejectedEvent = z.infer<typeof ApprovalRejectedEventSchema>;\n/** Narrowed runtime type for the `approval_expired` event variant. */\nexport type ApprovalExpiredEvent = z.infer<typeof ApprovalExpiredEventSchema>;\n/** Narrowed runtime type for the `command_executed` event variant. */\nexport type CommandExecutedEvent = z.infer<typeof CommandExecutedEventSchema>;\n/** Narrowed runtime type for the `git_snapshot` event variant. */\nexport type GitSnapshotEvent = z.infer<typeof GitSnapshotEventSchema>;\n/** Narrowed runtime type for the `file_changed` event variant. */\nexport type FileChangedEvent = z.infer<typeof FileChangedEventSchema>;\n/** Narrowed runtime type for the `decision_recorded` event variant. */\nexport type DecisionRecordedEvent = z.infer<typeof DecisionRecordedEventSchema>;\n/** Narrowed runtime type for the `task_created` event variant. */\nexport type TaskCreatedEvent = z.infer<typeof TaskCreatedEventSchema>;\n/** Narrowed runtime type for the `task_status_changed` event variant. */\nexport type TaskStatusChangedEvent = z.infer<typeof TaskStatusChangedEventSchema>;\n/** Narrowed runtime type for the `task_reconciled` event variant (.strict()). */\nexport type TaskReconciledEvent = z.infer<typeof TaskReconciledEventSchema>;\n/** Narrowed runtime type for the `task_linkage_refreshed` event variant (.strict()). */\nexport type TaskLinkageRefreshedEvent = z.infer<typeof TaskLinkageRefreshedEventSchema>;\n/** Narrowed runtime type for the `task_deleted` event variant (.strict()). */\nexport type TaskDeletedEvent = z.infer<typeof TaskDeletedEventSchema>;\n/** Narrowed runtime type for the `task_archived` event variant (.strict()). */\nexport type TaskArchivedEvent = z.infer<typeof TaskArchivedEventSchema>;\n/** Narrowed runtime type for the `note_added` event variant. */\nexport type NoteAddedEvent = z.infer<typeof NoteAddedEventSchema>;\n/** Narrowed runtime type for the `adapter_output` event variant (.strict()). */\nexport type AdapterOutputEvent = z.infer<typeof AdapterOutputEventSchema>;\n","import { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { type ReplayWarning, replayEvents } from \"../events/event-replay.js\";\nimport { findErrorCode } from \"../lib/error-codes.js\";\nimport { type Session, SessionSchema } from \"../schemas/session.schema.js\";\nimport type { BasouPaths } from \"./basou-dir.js\";\nimport { readYamlFile } from \"./yaml-store.js\";\n\n/**\n * Threshold above which a still-`running` session with no `session_ended`\n * event is flagged suspect.\n *\n * 24h: long enough that an active long-running session will not be flagged,\n * short enough that an abandoned process is surfaced within a working day.\n * Tunable via CLI option in a later step (continuation backlog #23).\n */\nexport const STUCK_THRESHOLD_MS = 24 * 60 * 60 * 1000;\n\nexport type SuspectReason = \"events_say_ended_but_yaml_running\" | \"running_no_end_event\";\n\nexport type SessionEntry = {\n sessionId: string;\n session: Session;\n suspect: boolean;\n suspectReason: SuspectReason | null;\n};\n\n/**\n * Per-session degradation reason emitted by {@link loadSessionEntries.onSkip}.\n *\n * - `session_yaml_missing` (ENOENT) and `session_yaml_invalid` (parse or schema\n * failure) both omit the entry from the result.\n * - `events_jsonl_unreadable` still pushes the entry with `suspect=false` so\n * the session row remains visible to the caller; only the suspect check is\n * degraded. Matches the existing CLI behaviour at\n * `packages/cli/src/commands/session.ts` (suspect-check stderr warning).\n */\nexport type SessionSkipReason =\n | \"session_yaml_missing\"\n | \"session_yaml_invalid\"\n | \"events_jsonl_unreadable\";\n\nexport type LoadSessionEntriesOptions = {\n /**\n * Single `now` shared across every {@link classifySuspect} call so that\n * sessions classified back-to-back observe the same instant. Avoids\n * boundary races where a session at age ≈ 24h would flip between calls.\n */\n now: Date;\n onWarning?: (warning: ReplayWarning, sessionId: string) => void;\n onSkip?: (sessionId: string, reason: SessionSkipReason) => void;\n};\n\n/**\n * List session directory names under `paths.sessions`, ULID ascending.\n *\n * - Returns `[]` when the sessions directory does not exist (empty workspace\n * or pre-init state).\n * - Throws `Error(\"Failed to enumerate sessions\", { cause })` on other I/O.\n * - Only directories are returned (`.gitkeep` and other files are filtered).\n *\n * Sort order is `Array.prototype.sort()` default (Unicode code-point\n * compare). ULIDs are Crockford base32 in uppercase, so the natural sort\n * is also chronological session-start order.\n */\nexport async function enumerateSessionDirs(paths: BasouPaths): Promise<string[]> {\n try {\n const dirents = await readdir(paths.sessions, { withFileTypes: true });\n return dirents\n .filter((d) => d.isDirectory())\n .map((d) => d.name)\n .sort();\n } catch (error: unknown) {\n if (findErrorCode(error, \"ENOENT\")) return [];\n throw new Error(\"Failed to enumerate sessions\", { cause: error });\n }\n}\n\n/**\n * Read and validate `<paths.sessions>/<sessionId>/session.yaml`.\n *\n * - Re-throws the yaml-store fixed-message `\"YAML file not found\"` for\n * ENOENT so the caller can branch on it.\n * - Throws `Error(\"Failed to read session.yaml\", { cause })` for parse\n * failures and schema violations (cause is either the YAML parser error\n * or the zod error).\n */\nexport async function readSessionYaml(paths: BasouPaths, sessionId: string): Promise<Session> {\n const filePath = join(paths.sessions, sessionId, \"session.yaml\");\n let raw: unknown;\n try {\n raw = await readYamlFile(filePath);\n } catch (error: unknown) {\n if (error instanceof Error && error.message === \"YAML file not found\") throw error;\n throw new Error(\"Failed to read session.yaml\", { cause: error });\n }\n const result = SessionSchema.safeParse(raw);\n if (!result.success) {\n throw new Error(\"Failed to read session.yaml\", { cause: result.error });\n }\n return result.data;\n}\n\n/**\n * Classify a `running` session as suspect using one of two rules:\n *\n * - Rule A (`events_say_ended_but_yaml_running`): events.jsonl contains a\n * `session_ended` event but the session.yaml is still `running`. The\n * session ended cleanly in the event log but the YAML write was lost or\n * never reached.\n * - Rule B (`running_no_end_event`): no `session_ended` event and the last\n * event is older than {@link STUCK_THRESHOLD_MS}. The process likely\n * crashed or was killed.\n *\n * Sessions that are not `running` are never suspect.\n *\n * I/O failure on events.jsonl is re-thrown unwrapped so the caller can\n * degrade with a warning instead of treating the session as healthy. The\n * caller is also responsible for surfacing replay warnings via `onWarning`.\n */\nexport async function classifySuspect(\n paths: BasouPaths,\n sessionId: string,\n session: Session,\n now: Date,\n onWarning?: (warning: ReplayWarning) => void,\n): Promise<{ suspect: boolean; suspectReason: SuspectReason | null }> {\n if (session.session.status !== \"running\") {\n return { suspect: false, suspectReason: null };\n }\n const sessionDir = join(paths.sessions, sessionId);\n let endedFound = false;\n let lastEventOccurredAt: string | null = null;\n // Forward onWarning only when supplied — `exactOptionalPropertyTypes`\n // rejects passing a literal `undefined` for an optional property.\n const replayOpts = onWarning !== undefined ? { onWarning } : {};\n for await (const ev of replayEvents(sessionDir, replayOpts)) {\n lastEventOccurredAt = ev.occurred_at;\n if (ev.type === \"session_ended\") endedFound = true;\n }\n if (endedFound) {\n return { suspect: true, suspectReason: \"events_say_ended_but_yaml_running\" };\n }\n if (lastEventOccurredAt !== null) {\n const ageMs = now.getTime() - Date.parse(lastEventOccurredAt);\n if (Number.isFinite(ageMs) && ageMs > STUCK_THRESHOLD_MS) {\n return { suspect: true, suspectReason: \"running_no_end_event\" };\n }\n }\n return { suspect: false, suspectReason: null };\n}\n\n/**\n * High-level helper that enumerates session dirs, reads each `session.yaml`,\n * and classifies suspect for `running` sessions in one pass.\n *\n * Per-session degradations are surfaced via `options.onSkip`:\n * - `session_yaml_missing` (ENOENT) and `session_yaml_invalid` (parse or\n * schema violation): the entry is omitted from the result.\n * - `events_jsonl_unreadable`: the entry is still pushed with `suspect=false`\n * so callers can render the session row plus a CLI-side warning.\n *\n * `options.now` is taken once and threaded into every {@link classifySuspect}\n * call so age comparisons are consistent across sessions.\n */\nexport async function loadSessionEntries(\n paths: BasouPaths,\n options: LoadSessionEntriesOptions,\n): Promise<SessionEntry[]> {\n const sessionIds = await enumerateSessionDirs(paths);\n const entries: SessionEntry[] = [];\n for (const sid of sessionIds) {\n let session: Session;\n try {\n session = await readSessionYaml(paths, sid);\n } catch (error: unknown) {\n if (error instanceof Error && error.message === \"YAML file not found\") {\n options.onSkip?.(sid, \"session_yaml_missing\");\n } else {\n options.onSkip?.(sid, \"session_yaml_invalid\");\n }\n continue;\n }\n let suspect = false;\n let suspectReason: SuspectReason | null = null;\n try {\n const r = await classifySuspect(paths, sid, session, options.now, (w) =>\n options.onWarning?.(w, sid),\n );\n suspect = r.suspect;\n suspectReason = r.suspectReason;\n } catch {\n // events.jsonl I/O failure (EACCES etc.) on the suspect check is\n // unrecoverable for the classification but should not drop the session\n // entry. Surface a dedicated reason so the caller can distinguish a\n // broken events.jsonl from a broken session.yaml.\n options.onSkip?.(sid, \"events_jsonl_unreadable\");\n }\n entries.push({ sessionId: sid, session, suspect, suspectReason });\n }\n return entries;\n}\n","import { z } from \"zod\";\nimport {\n IsoTimestampSchema,\n SchemaVersionSchema,\n SessionIdSchema,\n TaskIdSchema,\n WorkspaceIdSchema,\n} from \"./shared.schema.js\";\n\n/** Session lifecycle states. */\nexport const SessionStatusSchema = z.enum([\n \"initialized\",\n \"running\",\n \"waiting_approval\",\n \"completed\",\n \"failed\",\n \"interrupted\",\n \"imported\",\n \"archived\",\n]);\n/** Inferred runtime type for {@link SessionStatusSchema}. */\nexport type SessionStatus = z.infer<typeof SessionStatusSchema>;\n\n/**\n * Source kind that produced the session.\n *\n * - `claude-code-adapter` — a live `basou run claude-code` process wrap.\n * - `claude-code-import` — derived after the fact from a Claude Code native\n * transcript (`~/.claude/projects/*.jsonl`) by `basou import claude-code`.\n * - `codex-import` — derived after the fact from an OpenAI Codex native\n * rollout log (date-partitioned `~/.codex/sessions`) by `basou import codex`.\n * - `import` — a round-trip of a Basou-format export (`basou session import`).\n * - `human` / `terminal` — manually-authored / terminal-recorded sessions.\n */\nexport const SessionSourceKindSchema = z.enum([\n \"claude-code-adapter\",\n \"claude-code-import\",\n \"codex-import\",\n \"human\",\n \"import\",\n \"terminal\",\n]);\n/** Inferred runtime type for {@link SessionSourceKindSchema}. */\nexport type SessionSourceKind = z.infer<typeof SessionSourceKindSchema>;\n\nconst SessionSourceSchema = z.object({\n kind: SessionSourceKindSchema,\n version: z.literal(\"0.1.0\"),\n // Optional id of the originating session in the SOURCE tool's own\n // namespace (e.g. the Claude Code session UUID for a `claude-code-import`).\n // Lets re-imports of the same source be deduplicated; absent for live runs.\n external_id: z.string().optional(),\n});\n\nconst InvocationSchema = z.object({\n command: z.string().min(1),\n args: z.array(z.string()).default([]),\n // Nullable to record signal-terminated runs where the child has no exit\n // code; the same nullability is mirrored in CommandExecutedEventSchema.\n exit_code: z.number().int().nullable(),\n});\n\n/**\n * Optional per-session metrics, computed at import time from the source tool's\n * native log. Two groups, both optional because not every source records them:\n *\n * - Model-usage rollup (`*_tokens`): the transcript carries per-message token\n * usage; these are the session totals. `reasoning_output_tokens` is\n * Codex-only, and live `run`/`exec` sessions carry no token usage at all.\n * - Engaged-time metrics (`active_*`): the billing-oriented active time derived\n * from the session's genuine engagement timestamps (conversation turns plus\n * action events), with idle gaps capped. `active_intervals` are the merged\n * wall-clock ranges (so cross-session totals can de-duplicate overlapping\n * work by interval union); `active_time_ms` is their summed duration;\n * `active_gap_cap_ms` and `active_time_method` lock the methodology so the\n * stored numbers stay interpretable if the method changes later.\n *\n * Absent on sessions imported before a given field existed (re-import to\n * backfill). Live sessions carry no engaged-time metrics and fall back to\n * event-derived active time at stats time.\n */\nexport const SessionMetricsSchema = z.object({\n output_tokens: z.number().int().nonnegative().optional(),\n input_tokens: z.number().int().nonnegative().optional(),\n cached_input_tokens: z.number().int().nonnegative().optional(),\n reasoning_output_tokens: z.number().int().nonnegative().optional(),\n active_time_ms: z.number().int().nonnegative().optional(),\n active_intervals: z\n .array(z.object({ start: IsoTimestampSchema, end: IsoTimestampSchema }))\n .optional(),\n active_gap_cap_ms: z.number().int().nonnegative().optional(),\n active_time_method: z.string().optional(),\n});\n/** Inferred runtime type for {@link SessionMetricsSchema}. */\nexport type SessionMetrics = z.infer<typeof SessionMetricsSchema>;\n\nconst SessionInnerSchema = z.object({\n id: SessionIdSchema,\n label: z.string().optional(),\n task_id: TaskIdSchema.nullable().optional(),\n workspace_id: WorkspaceIdSchema,\n source: SessionSourceSchema,\n started_at: IsoTimestampSchema,\n // ended_at is optional because initialized / running sessions have no end time yet.\n ended_at: IsoTimestampSchema.optional(),\n status: SessionStatusSchema,\n working_directory: z.string().min(1),\n invocation: InvocationSchema,\n related_files: z.array(z.string()).default([]),\n events_log: z.string().default(\"events.jsonl\"),\n summary: z.string().nullable().optional(),\n metrics: SessionMetricsSchema.optional(),\n});\n\n/**\n * Schema for `.basou/sessions/<session_id>/session.yaml`. The minimal\n * session document carries the actual fields nested under the outer\n * `session:` key.\n */\nexport const SessionSchema = z.object({\n schema_version: SchemaVersionSchema,\n session: SessionInnerSchema,\n});\n\n/** Inferred runtime type for {@link SessionSchema}. */\nexport type Session = z.infer<typeof SessionSchema>;\n","import { appendFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { type Event, EventSchema } from \"../schemas/event.schema.js\";\nimport { atomicReplace } from \"../storage/atomic.js\";\n\n/**\n * Append a single Basou event to `<sessionDir>/events.jsonl`.\n *\n * The event is validated against the discriminated union {@link EventSchema}\n * before being serialized as a single JSONL line (UTF-8, terminated by `\\n`).\n * Validation enforces the per-variant contract (required fields, source\n * vocabulary, strict variants such as `adapter_output`).\n *\n * Atomicity: writes go through `appendFile` which uses `O_APPEND`. Lines up\n * to `PIPE_BUF` bytes (Linux 4096 / macOS 512) are written atomically by the\n * kernel; longer lines may interleave with concurrent writers and are not\n * recovered here. v0.1 assumes a single writer per session, so partial-line\n * recovery is delegated to the read side (event replay) when introduced.\n *\n * Throws if validation fails or the underlying append errors. The thrown\n * Error message is pathless; the original error is attached as `cause`.\n *\n * @param sessionDir absolute path to `.basou/sessions/<session_id>/`\n * @param event unknown payload to validate and append\n */\nexport async function appendEvent(sessionDir: string, event: unknown): Promise<void> {\n let validated: ReturnType<typeof EventSchema.parse>;\n try {\n validated = EventSchema.parse(event);\n } catch (error: unknown) {\n throw new Error(\"Invalid Basou event payload\", { cause: error });\n }\n const line = `${JSON.stringify(validated)}\\n`;\n try {\n await appendFile(join(sessionDir, \"events.jsonl\"), line, \"utf8\");\n } catch (error: unknown) {\n throw new Error(\"Failed to append event to events.jsonl\", { cause: error });\n }\n}\n\n/**\n * Write `events.jsonl` in one atomic tmp+rename pass via {@link atomicReplace},\n * validating every event against {@link EventSchema} before any disk I/O so\n * a payload that fails validation never leaves a partial file behind.\n *\n * The helper is used by the round-trip importer (`session-import.ts`) and the\n * ad-hoc session orchestrator (`ad-hoc-session.ts`) where a small, fixed batch\n * of events must land together or not at all. Zero events produces a\n * zero-byte file so the session_yaml `events_log` pointer remains valid.\n *\n * Throws `\"Invalid Basou event payload\"` (same fixed message as\n * {@link appendEvent}) on validation failure, or `\"Failed to write\n * events.jsonl\"` on a disk I/O failure. The original native error is attached\n * as `cause`.\n */\nexport async function writeEventsBulk(sessionDir: string, events: Event[]): Promise<void> {\n const validated: Event[] = [];\n try {\n for (const event of events) {\n validated.push(EventSchema.parse(event));\n }\n } catch (error: unknown) {\n throw new Error(\"Invalid Basou event payload\", { cause: error });\n }\n const filePath = join(sessionDir, \"events.jsonl\");\n const body =\n validated.length > 0 ? `${validated.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n` : \"\";\n try {\n await atomicReplace(filePath, body);\n } catch (error: unknown) {\n throw new Error(\"Failed to write events.jsonl\", { cause: error });\n }\n}\n","import { type SimpleGit, simpleGit } from \"simple-git\";\nimport type { GitSnapshotEvent } from \"../schemas/event.schema.js\";\nimport { findErrorCode } from \"../storage/status.js\";\n\n/**\n * Build a {@link SimpleGit} instance bound to `repoRoot`. Production callers\n * use this single helper so any future tightening (additional safety opts,\n * environment scrubbing, ...) lands in one place. Test fixtures that need\n * `unsafe.allowUnsafeConfigPaths` for isolated `GIT_CONFIG_*` paths build\n * their own SimpleGit locally and intentionally bypass this helper.\n */\nexport function safeSimpleGit(repoRoot: string): SimpleGit {\n return simpleGit({ baseDir: repoRoot });\n}\n\n/**\n * Detect \"git executable not found\" across error wrappers used by simple-git.\n * simple-git surfaces spawn errors as `GitError` instances which discard the\n * original errno `code` property — only the underlying `\"spawn git ENOENT\"`\n * text survives in the message string. We therefore check both the errno\n * `code` (via {@link findErrorCode}) and the message chain.\n */\nexport function isGitNotFound(error: unknown): boolean {\n if (findErrorCode(error, \"ENOENT\")) return true;\n let cur: unknown = error;\n for (let i = 0; i < 4 && cur instanceof Error; i++) {\n if (/\\bENOENT\\b/.test(cur.message)) return true;\n cur = (cur as Error).cause;\n }\n return false;\n}\n\n/**\n * Payload subset of `git_snapshot` event, mechanically derived from the\n * zod-inferred event type. The wrapping event-shape fields\n * (schema_version, id, session_id, occurred_at, source, type) are added by\n * the caller (session lifecycle in later steps) when constructing the\n * event, so the schema remains the single source of truth.\n *\n * `ahead` / `behind` are omitted when there is no remote or no upstream\n * tracking; the schema declares both as optional non-negative integers.\n */\nexport type GitSnapshot = Omit<\n GitSnapshotEvent,\n \"schema_version\" | \"id\" | \"session_id\" | \"occurred_at\" | \"source\" | \"type\"\n>;\n\n/**\n * Resolve the absolute path of the Git repository root that contains `cwd`.\n * Equivalent to `git rev-parse --show-toplevel`.\n *\n * Throws `Error(\"Git executable not found in PATH. Install git first.\")`\n * with the spawn error attached as `cause` when git itself is missing.\n * Throws `Error(\"Not a git repository\")` (without command-specific suffix)\n * when `cwd` is not inside a repository — callers MAY wrap with their own\n * \"Run 'git init' first, then re-run 'basou XXX'.\" suffix.\n *\n * Pathless contract: the thrown message never embeds `cwd` or any absolute\n * path; native errors are kept on `error.cause` for verbose surfacing.\n */\nexport async function resolveRepositoryRoot(cwd: string): Promise<string> {\n const git = safeSimpleGit(cwd);\n try {\n const root = (await git.revparse([\"--show-toplevel\"])).trimEnd();\n if (root.length === 0) {\n throw new Error(\"Not a git repository\");\n }\n return root;\n } catch (error: unknown) {\n if (isGitNotFound(error)) {\n throw new Error(\"Git executable not found in PATH. Install git first.\", { cause: error });\n }\n if (error instanceof Error && error.message === \"Not a git repository\") {\n throw error;\n }\n throw new Error(\"Not a git repository\", { cause: error });\n }\n}\n\n/**\n * Read `remote.origin.url` from the local repository config. Returns\n * `undefined` if the remote is unset, the value is empty, or the lookup\n * fails for any reason (best-effort).\n *\n * The `--local` scope is critical: callers MUST NOT pick up the developer's\n * global remote.origin.url, which could leak the wrong repository URL into\n * `manifest.yaml`.\n */\nexport async function tryRemoteUrl(repositoryRoot: string): Promise<string | undefined> {\n const git = safeSimpleGit(repositoryRoot);\n try {\n const result = await git.getConfig(\"remote.origin.url\", \"local\");\n const url = (result.value ?? \"\").trimEnd();\n return url.length > 0 ? url : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Build a {@link GitSnapshot} for the repository at `repositoryRoot`. The\n * caller is responsible for ensuring `repositoryRoot` is the canonical root\n * (typically obtained via {@link resolveRepositoryRoot}); this function\n * verifies repo membership via `git rev-parse --is-inside-work-tree` to\n * distinguish a non-git directory from an empty repository.\n *\n * Edge cases:\n * - **non-git directory**: throws `Error(\"Not a git repository\")`\n * - **empty repo (no commits)**: throws `Error(\"No commits in repository\")`\n * - **detached HEAD**: `branch = \"HEAD\"`, `head = commit hash`,\n * `ahead`/`behind` omitted\n * - **no remote / no upstream tracking**: `ahead`/`behind` omitted\n *\n * Pathless contract preserved on every throw path.\n */\nexport async function getSnapshot(repositoryRoot: string): Promise<GitSnapshot> {\n const git = safeSimpleGit(repositoryRoot);\n\n let inside: boolean;\n try {\n inside = await git.checkIsRepo();\n } catch (error: unknown) {\n if (isGitNotFound(error)) {\n throw new Error(\"Git executable not found in PATH. Install git first.\", { cause: error });\n }\n throw new Error(\"Failed to read git state\", { cause: error });\n }\n if (!inside) {\n throw new Error(\"Not a git repository\");\n }\n\n let head: string;\n try {\n head = (await git.revparse([\"HEAD\"])).trimEnd();\n } catch (error: unknown) {\n if (isGitNotFound(error)) {\n throw new Error(\"Git executable not found in PATH. Install git first.\", { cause: error });\n }\n throw new Error(\"No commits in repository\", { cause: error });\n }\n if (head.length === 0) {\n throw new Error(\"No commits in repository\");\n }\n\n let branch: string;\n try {\n const raw = (await git.raw([\"branch\", \"--show-current\"])).trimEnd();\n branch = raw.length > 0 ? raw : \"HEAD\";\n } catch (error: unknown) {\n throw new Error(\"Failed to read git state\", { cause: error });\n }\n\n let dirty: boolean;\n const staged: string[] = [];\n const unstaged: string[] = [];\n const untracked: string[] = [];\n try {\n const status = await git.status();\n dirty = !status.isClean();\n // Walk status.files so deleted / renamed / conflicted entries are\n // classified correctly (StatusResult's top-level `staged` / `modified`\n // / `not_added` arrays exclude D / R / U entries).\n for (const f of status.files) {\n if (f.index === \"?\" && f.working_dir === \"?\") {\n untracked.push(f.path);\n continue;\n }\n if (f.index !== \" \" && f.index !== \"?\") staged.push(f.path);\n if (f.working_dir !== \" \" && f.working_dir !== \"?\") unstaged.push(f.path);\n }\n } catch (error: unknown) {\n throw new Error(\"Failed to read git state\", { cause: error });\n }\n\n let ahead: number | undefined;\n let behind: number | undefined;\n if (branch !== \"HEAD\") {\n try {\n const upstream = `${branch}@{upstream}`;\n const counts = (\n await git.raw([\"rev-list\", \"--left-right\", \"--count\", `${upstream}...HEAD`])\n ).trim();\n const [behindStr, aheadStr] = counts.split(/\\s+/);\n const parsedBehind = Number.parseInt(behindStr ?? \"\", 10);\n const parsedAhead = Number.parseInt(aheadStr ?? \"\", 10);\n if (Number.isFinite(parsedBehind) && parsedBehind >= 0) behind = parsedBehind;\n if (Number.isFinite(parsedAhead) && parsedAhead >= 0) ahead = parsedAhead;\n } catch {\n // No upstream tracking: leave both undefined; the schema allows omission.\n }\n }\n\n const snapshot: GitSnapshot = {\n head,\n branch,\n dirty,\n staged,\n unstaged,\n untracked,\n ...(ahead !== undefined ? { ahead } : {}),\n ...(behind !== undefined ? { behind } : {}),\n };\n return snapshot;\n}\n","// Namespace import keeps lstat / readFile behind a single binding for the\n// read-side guards. The EACCES test exercises this module via real fs +\n// chmod on the parent directory rather than vi.spyOn, because vi.spyOn\n// cannot redefine ESM module exports under vitest 2.x.\nimport * as fsp from \"node:fs/promises\";\nimport type { Manifest } from \"../schemas/manifest.schema.js\";\nimport { StatusSchema, type StatusSnapshot } from \"../schemas/status.schema.js\";\nimport { atomicReplace } from \"./atomic.js\";\nimport type { BasouPaths } from \"./basou-dir.js\";\n\n/**\n * @internal Compile-time exhaustiveness via Record: every key of\n * `StatusSnapshot[\"directories_present\"]` MUST have a paths accessor here,\n * otherwise the file fails to typecheck. Run-time exhaustiveness is\n * verified by status.test.ts (key-set equality with\n * `StatusSchema.shape.directories_present.shape`). Exported only so the\n * test can perform that equality check; not part of the public API.\n */\nexport const DIRECTORY_CHECKS: Record<\n keyof StatusSnapshot[\"directories_present\"],\n (p: BasouPaths) => string\n> = {\n sessions: (p) => p.sessions,\n tasks: (p) => p.tasks,\n approvals_pending: (p) => p.approvals.pending,\n approvals_resolved: (p) => p.approvals.resolved,\n logs: (p) => p.logs,\n raw: (p) => p.raw,\n tmp: (p) => p.tmp,\n};\n\n/**\n * Refuse to operate on `.basou` if it is a symlink or not a directory. This\n * prevents `writeStatus` from being tricked into writing `status.json`\n * outside the repository root via a swapped `.basou` symlink. Mirrors\n * `ensureBasouDirectory`'s lstat-based guard.\n *\n * If `.basou` is absent the underlying ENOENT is propagated (wrapped) so\n * callers can map it to \"workspace not initialized\" via `findErrorCode`.\n *\n * Note: this is a baseline safety net, not a TOCTOU fix — the directory\n * could still be replaced between this check and the subsequent write. The\n * goal is to detect already-swapped symlinks, not to race-proof the\n * filesystem.\n */\nexport async function assertBasouRootSafe(rootPath: string): Promise<void> {\n let stat: Awaited<ReturnType<typeof fsp.lstat>>;\n try {\n stat = await fsp.lstat(rootPath);\n } catch (error: unknown) {\n if (hasErrorCode(error) && error.code === \"ENOENT\") {\n throw new Error(\"Basou workspace not found\", { cause: error });\n }\n throw new Error(\"Failed to inspect .basou root\", { cause: error });\n }\n if (stat.isSymbolicLink()) {\n throw new Error(\".basou root is a symlink; refusing to operate\");\n }\n if (!stat.isDirectory()) {\n throw new Error(\".basou root exists but is not a directory\");\n }\n}\n\n/**\n * Probe whether `path` is a directory using `lstat` (without following\n * symlinks, so a symlink-to-directory is reported as `false`).\n *\n * Only ENOENT and ENOTDIR are mapped to `false`; permission-style errors\n * (EACCES, EPERM, ...) are re-thrown so a misleading \"not present\" answer\n * is never written into status.json. This keeps the snapshot honest about\n * what was actually observed.\n */\nasync function dirPresent(path: string): Promise<boolean> {\n try {\n return (await fsp.lstat(path)).isDirectory();\n } catch (error: unknown) {\n if (hasErrorCode(error) && (error.code === \"ENOENT\" || error.code === \"ENOTDIR\")) {\n return false;\n }\n throw new Error(\"Failed to inspect .basou subdirectory\", { cause: error });\n }\n}\n\n/**\n * Build a StatusSnapshot from a manifest plus the path layout, observing\n * each subdirectory's presence via `lstat`. Read-only with respect to the\n * workspace state; writes nothing. The result is re-validated by\n * `StatusSchema.parse` before being returned.\n *\n * @param input.now Override for testing; defaults to `new Date()`.\n */\nexport async function buildStatusSnapshot(input: {\n manifest: Manifest;\n paths: BasouPaths;\n now?: Date;\n}): Promise<StatusSnapshot> {\n const { manifest, paths } = input;\n const generatedAt = (input.now ?? new Date()).toISOString();\n\n const entries = Object.entries(DIRECTORY_CHECKS) as Array<\n [keyof StatusSnapshot[\"directories_present\"], (p: BasouPaths) => string]\n >;\n const presence = await Promise.all(\n entries.map(async ([key, get]) => [key, await dirPresent(get(paths))] as const),\n );\n const directoriesEntries = Object.fromEntries(presence) as StatusSnapshot[\"directories_present\"];\n\n const snapshot: StatusSnapshot = {\n schema_version: \"0.1.0\",\n generated_at: generatedAt,\n workspace: {\n id: manifest.workspace.id,\n name: manifest.workspace.name,\n basou_version: manifest.basou_version,\n },\n directories_present: directoriesEntries,\n };\n return StatusSchema.parse(snapshot);\n}\n\n/**\n * Atomically write a StatusSnapshot to `paths.files.status`.\n *\n * Re-validates via `StatusSchema.parse` before any file I/O, so an invalid\n * snapshot throws synchronously and never overwrites the existing\n * `status.json`. Delegates the tmp-file + rename pass to {@link atomicReplace}.\n *\n * **Precondition**: callers MUST invoke {@link assertBasouRootSafe} on\n * `paths.root` first to ensure `.basou` is a real directory and not a\n * swapped symlink. `writeStatus` does not redo this guard — it trusts the\n * caller — so a direct invocation without the guard could write\n * `status.json` outside the repository root.\n */\nexport async function writeStatus(paths: BasouPaths, snapshot: StatusSnapshot): Promise<void> {\n const validated = StatusSchema.parse(snapshot);\n const body = `${JSON.stringify(validated, null, 2)}\\n`;\n try {\n await atomicReplace(paths.files.status, body);\n } catch (error: unknown) {\n throw new Error(\"Failed to write status file\", { cause: error });\n }\n}\n\n/**\n * Read `.basou/status.json` for the current schema_version (0.1.0). This\n * is a cache reader only; cross-version migration is not supported here.\n * Older or newer status.json shapes will fail `StatusSchema.parse` —\n * callers regenerate by calling `buildStatusSnapshot` + `writeStatus`.\n */\nexport async function readStatus(paths: BasouPaths): Promise<StatusSnapshot> {\n let body: string;\n try {\n body = await fsp.readFile(paths.files.status, \"utf8\");\n } catch (error: unknown) {\n if (hasErrorCode(error) && error.code === \"ENOENT\") {\n throw new Error(\"Status file not found\", { cause: error });\n }\n throw new Error(\"Failed to read status file\", { cause: error });\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch (error: unknown) {\n throw new Error(\"Failed to parse status JSON\", { cause: error });\n }\n return StatusSchema.parse(parsed);\n}\n\n// Re-exported from lib so existing import paths (`./storage/status.js`,\n// `@basou/core/storage/index.js`, `@basou/core`) continue to resolve while\n// the canonical definition lives in `core/src/lib/error-codes.ts`.\nexport { findErrorCode } from \"../lib/error-codes.js\";\n\nfunction hasErrorCode(error: unknown): error is Error & { code: string } {\n if (!(error instanceof Error)) return false;\n return typeof (error as unknown as Record<string, unknown>).code === \"string\";\n}\n","import { z } from \"zod\";\nimport { IsoTimestampSchema, SchemaVersionSchema, WorkspaceIdSchema } from \"./shared.schema.js\";\n\n/**\n * Schema for `.basou/status.json` — a forward-incompat cache of the current\n * workspace state.\n *\n * Each level uses `.strict()` so unknown keys are rejected rather than\n * silently stripped. A v0.1 reader that encounters a future-shape\n * `status.json` therefore fails parsing instead of returning a partially\n * empty snapshot; callers regenerate by calling `buildStatusSnapshot` +\n * `writeStatus` rather than trying to migrate.\n */\nexport const StatusSchema = z\n .object({\n schema_version: SchemaVersionSchema,\n generated_at: IsoTimestampSchema,\n workspace: z\n .object({\n id: WorkspaceIdSchema,\n name: z.string().min(1),\n basou_version: z.literal(\"0.1.0\"),\n })\n .strict(),\n directories_present: z\n .object({\n sessions: z.boolean(),\n tasks: z.boolean(),\n approvals_pending: z.boolean(),\n approvals_resolved: z.boolean(),\n logs: z.boolean(),\n raw: z.boolean(),\n tmp: z.boolean(),\n })\n .strict(),\n })\n .strict();\n\n/** Inferred runtime type for {@link StatusSchema}. */\nexport type StatusSnapshot = z.infer<typeof StatusSchema>;\n","import type { SimpleGit } from \"simple-git\";\nimport { isGitNotFound, safeSimpleGit } from \"./snapshot.js\";\n\n/**\n * Status classification used by the `file_changed` event schema. Limited to\n * the four classes that simple-git's `git diff --name-status` reliably\n * surfaces; copy / unmerged / typechange entries are intentionally dropped\n * to keep the event payload shape narrow.\n */\nexport type FileChangeStatus = \"added\" | \"modified\" | \"deleted\" | \"renamed\";\n\n/**\n * Single file-level change observed between two refs. `old_path` is set\n * only for `renamed` entries (the previous path of the file).\n */\nexport type FileChange = {\n path: string;\n old_path?: string;\n status: FileChangeStatus;\n};\n\n/**\n * Result of {@link getDiff}. The `changed_files` array is in git's natural\n * `--name-status` order; callers requiring deterministic ordering should\n * sort by `path` themselves.\n */\nexport type DiffResult = {\n changed_files: FileChange[];\n};\n\n/**\n * Compute the file-level diff between two git refs.\n *\n * Returns a list of changed file paths classified by status (added /\n * modified / deleted / renamed). Diff content is intentionally NOT\n * returned — `file_changed` events record paths only, and raw diff bodies\n * are excluded so the trace cannot inadvertently leak source code that may\n * be sensitive. Use `git show <ref>` to obtain the underlying diff.\n *\n * Pathless contract: every thrown message is a fixed string from the set\n * {`Not a git repository`, `Git executable not found in PATH. Install git\n * first.`, `Invalid ref`, `Failed to compute git diff`}; native errors are\n * preserved on `Error.cause`.\n *\n * Special cases:\n * - `baseRef === headRef` short-circuits to an empty result\n * - copy / unmerged / typechange / unknown status codes are skipped\n *\n * @param repoRoot absolute path to the git repository root\n * @param baseRef base ref (e.g. session-start HEAD sha)\n * @param headRef head ref (e.g. session-end HEAD sha)\n */\nexport async function getDiff(\n repoRoot: string,\n baseRef: string,\n headRef: string,\n): Promise<DiffResult> {\n let git: SimpleGit;\n try {\n git = safeSimpleGit(repoRoot);\n } catch (error: unknown) {\n if (isGitNotFound(error)) {\n throw new Error(\"Git executable not found in PATH. Install git first.\", { cause: error });\n }\n throw new Error(\"Not a git repository\", { cause: error });\n }\n\n if (baseRef === headRef) return { changed_files: [] };\n\n let raw: string;\n try {\n raw = await git.raw([\"diff\", \"--name-status\", `${baseRef}..${headRef}`]);\n } catch (error: unknown) {\n if (isGitNotFound(error)) {\n throw new Error(\"Git executable not found in PATH. Install git first.\", { cause: error });\n }\n const message = error instanceof Error ? error.message : \"\";\n if (/not a git repository/i.test(message)) {\n throw new Error(\"Not a git repository\", { cause: error });\n }\n if (\n message.includes(\"bad revision\") ||\n message.includes(\"unknown revision\") ||\n message.includes(\"ambiguous argument\")\n ) {\n throw new Error(\"Invalid ref\", { cause: error });\n }\n throw new Error(\"Failed to compute git diff\", { cause: error });\n }\n\n return { changed_files: parseDiffNameStatus(raw) };\n}\n\nfunction parseDiffNameStatus(raw: string): FileChange[] {\n const lines = raw.split(\"\\n\").filter((l) => l.trim() !== \"\");\n const changes: FileChange[] = [];\n for (const line of lines) {\n const parts = line.split(\"\\t\");\n const code = parts[0];\n if (code === undefined || code.length === 0) continue;\n if (code.startsWith(\"R\") && parts.length >= 3) {\n const newPath = parts[2];\n const oldPath = parts[1];\n if (newPath === undefined) continue;\n changes.push({\n path: newPath,\n status: \"renamed\",\n ...(oldPath !== undefined ? { old_path: oldPath } : {}),\n });\n } else if (code === \"A\" && parts[1]) {\n changes.push({ path: parts[1], status: \"added\" });\n } else if (code === \"M\" && parts[1]) {\n changes.push({ path: parts[1], status: \"modified\" });\n } else if (code === \"D\" && parts[1]) {\n changes.push({ path: parts[1], status: \"deleted\" });\n }\n // C / U / T / X (copy / unmerged / typechange / unknown) are skipped:\n // the file_changed status enum does not cover them in v0.1.\n }\n return changes;\n}\n","import { join } from \"node:path\";\nimport { enumerateApprovals } from \"../approval/approval-store.js\";\nimport { type ReplayWarning, replayEvents } from \"../events/event-replay.js\";\nimport type { BasouPaths } from \"../storage/basou-dir.js\";\nimport {\n loadSessionEntries,\n type SessionEntry,\n type SessionSkipReason,\n type SuspectReason,\n} from \"../storage/sessions.js\";\nimport { loadTaskEntries, type TaskDocument, type TaskSkipReason } from \"../storage/tasks.js\";\n\n/** Input contract for {@link renderHandoff}. */\nexport type HandoffRendererInput = {\n paths: BasouPaths;\n /** ISO timestamp embedded in the generated body header. Caller-provided for testability. */\n nowIso: string;\n /** Forwarded to {@link replayEvents} / {@link loadSessionEntries} per session. */\n onWarning?: (warning: ReplayWarning, sessionId: string) => void;\n /**\n * Per-session degradation reasons (missing/invalid session.yaml or\n * unreadable events.jsonl). The CLI maps `events_jsonl_unreadable` to the\n * existing suspect-check stderr wording to keep the user-facing surface\n * consistent with `basou session list`.\n */\n onSessionSkip?: (sessionId: string, reason: SessionSkipReason) => void;\n /**\n * Per-task degradation reasons (invalid front matter / unreadable file).\n * Surfaced so the CLI can warn the operator about a malformed task.md\n * without aborting the handoff render.\n */\n onTaskSkip?: (taskId: string, reason: TaskSkipReason) => void;\n /** Maximum related_files entries to display before `... +N more`. Default 20. */\n relatedFilesLimit?: number;\n};\n\nexport type HandoffRendererResult = {\n /** Generated body WITHOUT BASOU:GENERATED markers (markdown-store wraps them). */\n body: string;\n sessionCount: number;\n decisionCount: number;\n pendingApprovalsCount: number;\n suspectCount: number;\n /** Total number of task.md files successfully loaded. */\n taskCount: number;\n /** Tasks whose status is `planned` or `in_progress` (= shown in 次に実行すべき作業). */\n pendingTaskCount: number;\n};\n\ntype DecisionRecord = {\n decisionId: string;\n title: string;\n occurredAt: string;\n sessionId: string;\n};\n\ntype TaskCreatedRecord = {\n taskId: string;\n title: string;\n occurredAt: string;\n sessionId: string;\n};\n\ntype TaskStatusChangedRecord = {\n taskId: string;\n occurredAt: string;\n sessionId: string;\n};\n\n/**\n * Render the body of `handoff.md` from the current workspace state.\n *\n * The renderer is a pure function (no I/O beyond {@link replayEvents} /\n * {@link loadSessionEntries} / {@link enumerateApprovals}). It assembles the\n * the spec's `handoff.md` sections in order:\n *\n * 1. `現在の状態`: latest live session (status not archived, source not import).\n * 2. `直近の変更ファイル`: the most recent session's `related_files`, dedup +\n * sorted asc + truncated to `relatedFilesLimit` (default 20).\n * 3. `直近の判断`: latest `decision_recorded` event (chronological).\n * 4. `未決事項`: pending-approval count + suspect-session count.\n * 5. `次に読むべきファイル`: `.basou/decisions.md` + top-3 related files\n * (the same `displayedFiles` source is intentionally reused in two\n * sections — overview vs. resume context).\n * 6. `次に実行すべき作業`: placeholder until task events land.\n * 7. `セッション一覧`: all sessions newest first with inline suspect labels.\n *\n * Session enumeration goes through {@link loadSessionEntries} so the set of\n * sessions whose `decision_recorded` events we replay matches the\n * decisions renderer.\n */\nexport async function renderHandoff(input: HandoffRendererInput): Promise<HandoffRendererResult> {\n const limit = input.relatedFilesLimit ?? 20;\n const now = new Date(input.nowIso);\n // Wrap the caller's onSkip so we can detect whether loadSessionEntries'\n // suspect pass already emitted `events_jsonl_unreadable` for a session\n // For non-running sessions the suspect pass does not\n // touch events.jsonl, so the second replay below may be the first to\n // hit the unreadable file — without this bookkeeping that error would\n // be silently swallowed.\n const unreadableEmitted = new Set<string>();\n const wrappedSkip: (sid: string, reason: SessionSkipReason) => void = (sid, reason) => {\n if (reason === \"events_jsonl_unreadable\") unreadableEmitted.add(sid);\n input.onSessionSkip?.(sid, reason);\n };\n // `exactOptionalPropertyTypes` forbids passing literal `undefined` for an\n // optional property, so build the options object conditionally.\n const loadOpts: Parameters<typeof loadSessionEntries>[1] = { now, onSkip: wrappedSkip };\n if (input.onWarning !== undefined) loadOpts.onWarning = input.onWarning;\n const entries = await loadSessionEntries(input.paths, loadOpts);\n\n const decisions: DecisionRecord[] = [];\n const tasksCreated: TaskCreatedRecord[] = [];\n const tasksStatusChanged: TaskStatusChangedRecord[] = [];\n for (const entry of entries) {\n const sessionDir = join(input.paths.sessions, entry.sessionId);\n try {\n for await (const ev of replayEvents(sessionDir, {\n onWarning: (w) => input.onWarning?.(w, entry.sessionId),\n })) {\n if (ev.type === \"decision_recorded\") {\n decisions.push({\n decisionId: ev.decision_id,\n title: ev.title,\n occurredAt: ev.occurred_at,\n sessionId: entry.sessionId,\n });\n } else if (ev.type === \"task_created\") {\n tasksCreated.push({\n taskId: ev.task_id,\n title: ev.title,\n occurredAt: ev.occurred_at,\n sessionId: entry.sessionId,\n });\n } else if (ev.type === \"task_status_changed\") {\n tasksStatusChanged.push({\n taskId: ev.task_id,\n occurredAt: ev.occurred_at,\n sessionId: entry.sessionId,\n });\n }\n }\n } catch {\n // events.jsonl unreadable on the decision-aggregation pass. If the\n // suspect pass has not already surfaced a warning for this session\n // (e.g. completed session, where classifySuspect short-circuits\n // before reading events.jsonl), emit the skip now so the operator\n // is not left wondering why a decision is missing.\n if (!unreadableEmitted.has(entry.sessionId)) {\n wrappedSkip(entry.sessionId, \"events_jsonl_unreadable\");\n }\n }\n }\n decisions.sort((a, b) => {\n const c = Date.parse(a.occurredAt) - Date.parse(b.occurredAt);\n return c !== 0 ? c : a.decisionId.localeCompare(b.decisionId);\n });\n tasksCreated.sort((a, b) => {\n const c = Date.parse(a.occurredAt) - Date.parse(b.occurredAt);\n return c !== 0 ? c : a.taskId.localeCompare(b.taskId);\n });\n tasksStatusChanged.sort((a, b) => {\n const c = Date.parse(a.occurredAt) - Date.parse(b.occurredAt);\n return c !== 0 ? c : a.taskId.localeCompare(b.taskId);\n });\n\n const taskLoadOpts: Parameters<typeof loadTaskEntries>[1] = {};\n if (input.onTaskSkip !== undefined) taskLoadOpts.onSkip = input.onTaskSkip;\n const taskEntries = await loadTaskEntries(input.paths, taskLoadOpts);\n const taskById = new Map<string, TaskDocument>();\n for (const t of taskEntries) taskById.set(t.task.task.id, t);\n\n // Latest activity = most recent task_status_changed, falling back to the\n // most recent task_created when no status change has been recorded yet.\n // This surfaces \"the task whose status most recently changed (including\n // done)\" instead of \"the most recently created task\", so a task that just\n // transitioned to done is no longer hidden from the handoff.\n const latestStatusChange = tasksStatusChanged[tasksStatusChanged.length - 1];\n const latestCreatedRecord = tasksCreated[tasksCreated.length - 1];\n const latestActivityTaskId = latestStatusChange?.taskId ?? latestCreatedRecord?.taskId;\n const latestActivityTitle =\n latestActivityTaskId !== undefined\n ? (tasksCreated.find((t) => t.taskId === latestActivityTaskId)?.title ?? \"(title unknown)\")\n : undefined;\n const latestActivityRecord =\n latestActivityTaskId !== undefined && latestActivityTitle !== undefined\n ? { taskId: latestActivityTaskId, title: latestActivityTitle }\n : undefined;\n const latestTaskDoc =\n latestActivityRecord !== undefined ? taskById.get(latestActivityRecord.taskId) : undefined;\n const pendingTasks = taskEntries.filter(\n (t) => t.task.task.status === \"planned\" || t.task.task.status === \"in_progress\",\n );\n\n const approvals = await enumerateApprovals(input.paths);\n const pendingApprovalsCount = approvals.pending.length;\n\n const liveEntries = entries.filter(\n (e) => e.session.session.status !== \"archived\" && e.session.session.source.kind !== \"import\",\n );\n const latestSession = [...liveEntries].sort(\n (a, b) => Date.parse(b.session.session.started_at) - Date.parse(a.session.session.started_at),\n )[0];\n\n // 「直近の変更ファイル」 shows the files touched by the single most recent\n // session — the same session surfaced as 最終 session above — so the section\n // reflects the latest activity rather than the whole history. Unioning every\n // session's related_files turned this into a whole-history dump once\n // transcript imports became the primary source, since each import carries a\n // full day of file changes.\n const latestFiles = latestSession?.session.session.related_files ?? [];\n const sortedFiles = [...new Set(latestFiles)].sort();\n const displayedFiles = sortedFiles.slice(0, limit);\n const overflow = Math.max(0, sortedFiles.length - limit);\n\n const suspectCount = entries.filter((e) => e.suspect).length;\n\n const firstEntry = entries[0];\n const lastEntry = entries[entries.length - 1];\n const sessionRange =\n firstEntry !== undefined && lastEntry !== undefined\n ? `${shortIdWithPrefix(firstEntry.sessionId)}..${shortIdWithPrefix(lastEntry.sessionId)}`\n : \"\";\n\n const body = formatHandoffBody({\n nowIso: input.nowIso,\n sessionRange,\n sessionCount: entries.length,\n latestSession,\n decisions,\n pendingApprovalsCount,\n suspectCount,\n displayedFiles,\n overflow,\n entries,\n latestActivityRecord,\n latestTaskDoc,\n pendingTasks,\n totalTaskCount: taskEntries.length,\n });\n\n return {\n body,\n sessionCount: entries.length,\n decisionCount: decisions.length,\n pendingApprovalsCount,\n suspectCount,\n taskCount: taskEntries.length,\n pendingTaskCount: pendingTasks.length,\n };\n}\n\nfunction formatHandoffBody(args: {\n nowIso: string;\n sessionRange: string;\n sessionCount: number;\n latestSession: SessionEntry | undefined;\n decisions: ReadonlyArray<DecisionRecord>;\n pendingApprovalsCount: number;\n suspectCount: number;\n displayedFiles: ReadonlyArray<string>;\n overflow: number;\n entries: ReadonlyArray<SessionEntry>;\n latestActivityRecord: { taskId: string; title: string } | undefined;\n latestTaskDoc: TaskDocument | undefined;\n pendingTasks: ReadonlyArray<TaskDocument>;\n totalTaskCount: number;\n}): string {\n const lines: string[] = [];\n lines.push(\"# Handoff\");\n lines.push(\"\");\n if (args.sessionRange !== \"\") {\n lines.push(`> Generated at ${args.nowIso} from ${args.sessionRange}`);\n } else {\n lines.push(`> Generated at ${args.nowIso}`);\n }\n lines.push(\"\");\n\n // 現在の状態\n lines.push(\"## 現在の状態\");\n lines.push(\"\");\n if (args.latestSession !== undefined) {\n const status = args.latestSession.session.session.status;\n const label = args.latestSession.session.session.label;\n const shortId = shortIdWithPrefix(args.latestSession.sessionId);\n // Lead with the human-readable label; the raw id is demoted to a trailing\n // [short id]. When the session has no label the short id is the only handle\n // available, so it becomes the primary text and the bracket is dropped to\n // avoid repeating it.\n if (label !== undefined && label !== \"\") {\n lines.push(`- 最終 session: ${label} (${status}) [${shortId}]`);\n } else {\n lines.push(`- 最終 session: ${shortId} (${status})`);\n }\n } else {\n lines.push(\"- 最終 session: (no live sessions)\");\n }\n if (args.latestActivityRecord !== undefined) {\n // Status comes from task.md when available. If the task_created event\n // exists but task.md is missing / invalid we MUST NOT fabricate\n // \"planned\" — events alone cannot restore the initial status and\n // operators would miss an unsafe-state reconcile.\n const statusLabel =\n args.latestTaskDoc !== undefined\n ? args.latestTaskDoc.task.task.status\n : \"status unknown — task.md missing or invalid\";\n // Surface linked_sessions cardinality inside the status parenthetical when\n // the latest task spans more than one session. Suppressed when the\n // task is single-session (the common case) or when task.md is\n // unavailable, keeping single-session output visually quiet.\n const linkedCount = args.latestTaskDoc?.task.task.linked_sessions?.length;\n const linkedSuffix =\n linkedCount !== undefined && linkedCount > 1 ? `, linked_sessions: ${linkedCount}` : \"\";\n // Lead with the task title; the raw id is demoted to a trailing [short id]\n // and linked_sessions rides alongside the status.\n lines.push(\n `- 最終 task: ${args.latestActivityRecord.title} (${statusLabel}${linkedSuffix}) [${shortIdWithPrefix(args.latestActivityRecord.taskId)}]`,\n );\n } else {\n lines.push(\"- 最終 task: (no tasks recorded yet)\");\n }\n lines.push(\"\");\n\n // 直近の変更ファイル\n lines.push(\"## 直近の変更ファイル\");\n lines.push(\"\");\n if (args.displayedFiles.length === 0) {\n lines.push(\"(no related files recorded)\");\n } else {\n for (const f of args.displayedFiles) lines.push(`- ${f}`);\n if (args.overflow > 0) lines.push(`- ... +${args.overflow} more`);\n }\n lines.push(\"\");\n\n // 直近の判断\n lines.push(\"## 直近の判断\");\n lines.push(\"\");\n if (args.decisions.length === 0) {\n lines.push(\"(no decisions recorded yet)\");\n } else {\n const last = args.decisions[args.decisions.length - 1] as DecisionRecord;\n // Lead with the decision title; the raw id is demoted to a trailing\n // [short id].\n lines.push(`- ${last.title} [${shortIdWithPrefix(last.decisionId)}]`);\n lines.push(\"\");\n lines.push(`(${args.decisions.length} decisions total — see decisions.md)`);\n }\n lines.push(\"\");\n\n // 未決事項\n lines.push(\"## 未決事項\");\n lines.push(\"\");\n if (args.pendingApprovalsCount > 0) {\n lines.push(`- ${args.pendingApprovalsCount} pending approvals`);\n }\n if (args.suspectCount > 0) {\n lines.push(`- ${args.suspectCount} suspect sessions detected`);\n }\n if (args.pendingApprovalsCount === 0 && args.suspectCount === 0) {\n lines.push(\"(none)\");\n }\n lines.push(\"\");\n\n // 次に読むべきファイル\n // Drop self-reference to handoff.md, include `.basou/decisions.md` + the\n // top-3 of `displayedFiles` so the section points to concrete files. The\n // same `displayedFiles` source is reused intentionally (overview vs.\n // resume context).\n lines.push(\"## 次に読むべきファイル\");\n lines.push(\"\");\n lines.push(\"- .basou/decisions.md\");\n for (const f of args.displayedFiles.slice(0, 3)) lines.push(`- ${f}`);\n lines.push(\"\");\n\n // 次に実行すべき作業\n lines.push(\"## 次に実行すべき作業\");\n lines.push(\"\");\n if (args.pendingTasks.length === 0) {\n lines.push(\"(no pending tasks)\");\n } else {\n for (const t of args.pendingTasks) {\n // Lead with the task title; the raw id is demoted to a trailing [short id].\n lines.push(\n `- ${t.task.task.title} (${t.task.task.status}) [${shortIdWithPrefix(t.task.task.id)}]`,\n );\n }\n }\n lines.push(\"\");\n\n // セッション一覧 — the main table lists the operator's own sessions newest\n // first. This deliberately includes `claude-code-import` sessions: a\n // transcript captured after the fact via `basou import claude-code` is still\n // the operator's own work, so it belongs here rather than below. The separate\n // 「Imported sessions」 sub-section holds ONLY cross-workspace round-trips\n // brought in via `basou session import` (source.kind === \"import\"), so it is\n // absent whenever there are none. The \"(no sessions yet)\" placeholder fires\n // only when the workspace is completely empty; \"(no live sessions; …)\" fires\n // when every session is such a round-trip import.\n const liveTableEntries = args.entries.filter((e) => e.session.session.source.kind !== \"import\");\n const importedTableEntries = args.entries.filter(\n (e) => e.session.session.source.kind === \"import\",\n );\n lines.push(\"## セッション一覧\");\n lines.push(\"\");\n if (args.entries.length === 0) {\n lines.push(\"(no sessions yet)\");\n } else if (liveTableEntries.length === 0) {\n lines.push(\"(no live sessions; see Imported sessions below)\");\n } else {\n lines.push(\"| short_id | status | started_at | label |\");\n lines.push(\"|---|---|---|---|\");\n for (const e of [...liveTableEntries].reverse()) {\n const sid = shortHandoffId(e.sessionId);\n const status = e.session.session.status + suspectLabel(e.suspectReason);\n const startedAt = e.session.session.started_at;\n const label = e.session.session.label ?? \"\";\n lines.push(`| ${sid} | ${status} | ${startedAt} | ${label} |`);\n }\n }\n if (importedTableEntries.length > 0) {\n lines.push(\"\");\n lines.push(\"### Imported sessions\");\n lines.push(\"\");\n lines.push(\"| short_id | status | started_at | label |\");\n lines.push(\"|---|---|---|---|\");\n for (const e of [...importedTableEntries].reverse()) {\n const sid = shortHandoffId(e.sessionId);\n const status = e.session.session.status + suspectLabel(e.suspectReason);\n const startedAt = e.session.session.started_at;\n const label = e.session.session.label ?? \"\";\n lines.push(`| ${sid} | ${status} | ${startedAt} | ${label} |`);\n }\n }\n lines.push(\"\");\n // Session-status breakdown: surface completed / failed / running counts\n // alongside the total so an at-a-glance read distinguishes \"ten sessions,\n // all done\" from \"ten sessions, three still failing\". Order is fixed\n // (completed first since handoff is read after the work) and zero-count\n // statuses are omitted. When the workspace is empty the breakdown\n // parenthetical is suppressed entirely so the existing terse line stays.\n const statusCounts = new Map<string, number>();\n for (const e of args.entries) {\n const s = e.session.session.status;\n statusCounts.set(s, (statusCounts.get(s) ?? 0) + 1);\n }\n const orderedStatuses = [\n \"completed\",\n \"failed\",\n \"running\",\n \"interrupted\",\n \"waiting_approval\",\n \"initialized\",\n \"imported\",\n ] as const;\n const breakdown = orderedStatuses\n .filter((s) => (statusCounts.get(s) ?? 0) > 0)\n .map((s) => `${s} ${statusCounts.get(s)}`)\n .join(\", \");\n const sessionsLine =\n breakdown !== \"\"\n ? `Sessions: ${args.sessionCount} (${breakdown}). Tasks: ${args.totalTaskCount}.`\n : `Sessions: ${args.sessionCount}. Tasks: ${args.totalTaskCount}.`;\n lines.push(sessionsLine);\n\n return lines.join(\"\\n\");\n}\n\nfunction suspectLabel(reason: SuspectReason | null): string {\n if (reason === \"events_say_ended_but_yaml_running\") return \" ⚠ ended (yaml stale)\";\n if (reason === \"running_no_end_event\") return \" ⚠ no end event\";\n return \"\";\n}\n\n// First 10 chars after the `ses_` prefix. Matches the truncation that\n// `basou session list` uses for its shortest display column.\nfunction shortHandoffId(sessionId: string): string {\n const SES = \"ses_\";\n if (sessionId.startsWith(SES)) return sessionId.slice(SES.length, SES.length + 10);\n return sessionId.slice(0, 10);\n}\n\n// Prose-line short id: keeps the type prefix (`ses_` / `task_` / `decision_`)\n// and truncates the ULID body to its first 10 chars, e.g.\n// `task_01KRNHYRS91F5GBX2VTN9ADJFV` -> `task_01KRNHYRS9`. Unlike the session\n// table — whose column header already marks the column as ids — body lines mix\n// session / task / decision ids inline, so the prefix is kept to keep each id\n// self-describing while still demoting it behind the human-readable text.\nfunction shortIdWithPrefix(id: string): string {\n const sep = id.indexOf(\"_\");\n if (sep === -1) return id.slice(0, 10);\n return id.slice(0, sep + 1) + id.slice(sep + 1, sep + 1 + 10);\n}\n","import { createHash } from \"node:crypto\";\nimport { mkdir, readdir, readFile, rename, stat, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { parse as parseYaml, stringify as stringifyYaml } from \"yaml\";\nimport { z } from \"zod\";\nimport type { PrefixedId } from \"../ids/ulid.js\";\nimport { findErrorCode } from \"../lib/error-codes.js\";\nimport type { Event } from \"../schemas/event.schema.js\";\nimport type { Manifest } from \"../schemas/manifest.schema.js\";\nimport type { SessionStatus } from \"../schemas/session.schema.js\";\nimport { IsoTimestampSchema, SessionIdSchema, TaskIdSchema } from \"../schemas/shared.schema.js\";\nimport {\n type Task,\n TaskSchema,\n type TaskStatus,\n TaskStatusSchema,\n} from \"../schemas/task.schema.js\";\nimport type { TaskIndexEntry } from \"../schemas/task-index.schema.js\";\nimport {\n type AttachableStatus,\n appendEventToExistingSession,\n createAdHocSessionWithEvent,\n FailedToFinalizeError,\n} from \"./ad-hoc-session.js\";\nimport { atomicCreate, atomicReplace } from \"./atomic.js\";\nimport type { BasouPaths } from \"./basou-dir.js\";\nimport { acquireLock } from \"./lockfile.js\";\nimport { enumerateSessionDirs, readSessionYaml } from \"./sessions.js\";\nimport { readTaskIndex, rebuildTaskIndex, updateTaskIndex } from \"./task-index.js\";\nimport { overwriteYamlFile } from \"./yaml-store.js\";\n\n// ============================================================================\n// File format constants\n// ============================================================================\n\nconst FRONT_MATTER_DELIM = \"---\";\n// Raised from the original 40-char cap to 80 chars so long task /\n// reconcile titles retain their core information. The same cap applies\n// to `Ad-hoc task:`, `Ad-hoc task status:`, and `Ad-hoc task reconcile:`\n// labels so the three ad-hoc label generators stay consistent with the\n// decision-side cap (cli/src/commands/decision.ts).\nconst LABEL_TITLE_MAX = 80;\nconst LABEL_TRUNCATE_HEAD = LABEL_TITLE_MAX - 3;\n\nconst DEFAULT_ATTACHABLE_STATUSES: ReadonlySet<AttachableStatus> = new Set<AttachableStatus>([\n \"initialized\",\n \"running\",\n \"waiting_approval\",\n]);\n\n// Boundary parses for direct callers so a malformed task cannot smuggle\n// past the CLI-side parsers and commit a `task_created` event. The set\n// originally rejected `done` / `cancelled` as initial values, but the\n// orchestrator now emits a follow-up `task_status_changed` for terminal\n// initial statuses so retroactively-recorded completed tasks can be\n// entered in one CLI call; widening the schema lets that path through.\nconst InitialTaskStatusSchema = TaskStatusSchema;\nconst TaskTitleSchema = z.string().min(1);\nconst TaskLabelSchema = z.string().min(1);\n// `completedAt` is an optional ISO-8601 string. Validate it at the boundary\n// so a direct (non-CLI) caller cannot smuggle a garbage timestamp past the\n// orchestrator and leave durable `task_created` / `task_status_changed`\n// events with no valid task.md to back them up.\nconst CompletedAtSchema = IsoTimestampSchema;\n\nconst TERMINAL_TASK_STATUSES: ReadonlySet<TaskStatus> = new Set<TaskStatus>([\"done\", \"cancelled\"]);\n\nfunction isTerminalTaskStatus(status: TaskStatus): boolean {\n return TERMINAL_TASK_STATUSES.has(status);\n}\n\n// ============================================================================\n// File read / parse\n// ============================================================================\n\nexport type TaskDocument = {\n /** Parsed + zod-validated front matter. */\n task: Task;\n /** Raw markdown body after the closing front matter delimiter. */\n body: string;\n};\n\n/**\n * Split a task.md file body into the YAML front matter and the trailing\n * markdown body. The expected format is:\n *\n * ---\\n\n * <yaml>\\n\n * ---\\n\n * <body>\n *\n * Strict rules:\n * - A UTF-8 BOM at the head is rejected.\n * - CRLF inside the file is normalised to LF before delimiter scanning so\n * editors that auto-convert line endings stay compatible.\n * - The closing delimiter is the FIRST `---` line after the opening one,\n * so `---` lines inside the markdown body do not confuse the parser.\n */\nfunction splitFrontMatter(raw: string): { yamlText: string; body: string } {\n if (raw.length > 0 && raw.charCodeAt(0) === 0xfeff) {\n throw new Error(\"Invalid task file format\");\n }\n const normalised = raw.replace(/\\r\\n/g, \"\\n\");\n if (!normalised.startsWith(`${FRONT_MATTER_DELIM}\\n`)) {\n throw new Error(\"Invalid task file format\");\n }\n const remainder = normalised.slice(FRONT_MATTER_DELIM.length + 1);\n // Find the first line that is exactly `---`. Scan line-by-line so a `---`\n // appearing mid-line inside YAML text is not matched.\n const lines = remainder.split(\"\\n\");\n let closingIdx = -1;\n for (let i = 0; i < lines.length; i++) {\n if (lines[i] === FRONT_MATTER_DELIM) {\n closingIdx = i;\n break;\n }\n }\n if (closingIdx < 0) {\n throw new Error(\"Invalid task file format\");\n }\n const yamlText = lines.slice(0, closingIdx).join(\"\\n\");\n // The body is everything after the closing delimiter line, with one\n // separating newline consumed (if present) so the body does not start\n // with a stray blank line.\n const afterClosing = lines.slice(closingIdx + 1);\n let body = afterClosing.join(\"\\n\");\n if (body.startsWith(\"\\n\")) body = body.slice(1);\n return { yamlText, body };\n}\n\n/**\n * Read and validate `<paths.tasks>/<taskId>.md`. Returns the parsed front\n * matter (Task) plus the markdown body string. Error contract:\n *\n * - ENOENT → throw `\"Task file not found\"`.\n * - format violation → throw `\"Invalid task file format\"`.\n * - YAML parse / schema violation → throw `\"Failed to read task file\"`.\n * - any other I/O failure → throw `\"Failed to read task file\"` with cause.\n */\nexport async function readTaskFile(paths: BasouPaths, taskId: string): Promise<TaskDocument> {\n const filePath = join(paths.tasks, `${taskId}.md`);\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf8\");\n } catch (error: unknown) {\n if (findErrorCode(error, \"ENOENT\")) {\n throw new Error(\"Task file not found\", { cause: error });\n }\n throw new Error(\"Failed to read task file\", { cause: error });\n }\n let split: { yamlText: string; body: string };\n try {\n split = splitFrontMatter(raw);\n } catch (error: unknown) {\n if (error instanceof Error && error.message === \"Invalid task file format\") {\n throw error;\n }\n throw new Error(\"Failed to read task file\", { cause: error });\n }\n let parsed: unknown;\n try {\n parsed = parseYaml(split.yamlText);\n } catch (error: unknown) {\n throw new Error(\"Failed to read task file\", { cause: error });\n }\n const result = TaskSchema.safeParse(parsed);\n if (!result.success) {\n throw new Error(\"Failed to read task file\", { cause: result.error });\n }\n return { task: result.data, body: split.body };\n}\n\n// ============================================================================\n// File write (atomic, mode-aware)\n// ============================================================================\n\nexport type WriteTaskFileMode = \"create\" | \"overwrite\";\n\n/**\n * Atomically write `<paths.tasks>/<taskId>.md`.\n *\n * `mode: \"create\"` delegates to {@link atomicCreate} so a pre-existing file\n * fails fast with EEXIST → `\"Task file already exists\"`.\n * `mode: \"overwrite\"` delegates to {@link atomicReplace} and silently\n * replaces any prior file.\n *\n * The serialised body is structured as:\n *\n * ---\\n\n * <yaml>\\n\n * ---\\n\n * \\n\n * <body>\\n (only when body is non-empty)\n */\nexport async function writeTaskFile(\n paths: BasouPaths,\n taskId: string,\n doc: TaskDocument,\n options: { mode: WriteTaskFileMode },\n): Promise<void> {\n // Runtime self-defense: even if a caller bypassed the TypeScript boundary,\n // a malformed task object cannot reach disk.\n const validated = TaskSchema.parse(doc.task);\n\n const filePath = join(paths.tasks, `${taskId}.md`);\n const yamlText = stringifyYaml(validated);\n const trimmedBody =\n doc.body.length === 0 ? \"\" : `\\n${doc.body.endsWith(\"\\n\") ? doc.body : `${doc.body}\\n`}`;\n const fileBody = `${FRONT_MATTER_DELIM}\\n${yamlText}${FRONT_MATTER_DELIM}\\n${trimmedBody}`;\n\n if (options.mode === \"create\") {\n try {\n await atomicCreate(filePath, fileBody);\n } catch (error: unknown) {\n if (findErrorCode(error, \"EEXIST\")) {\n throw new Error(\"Task file already exists\", { cause: error });\n }\n throw new Error(\"Failed to write task file\", { cause: error });\n }\n return;\n }\n\n // overwrite mode\n try {\n await atomicReplace(filePath, fileBody);\n } catch (error: unknown) {\n throw new Error(\"Failed to write task file\", { cause: error });\n }\n}\n\n// ============================================================================\n// Directory enumeration / loading\n// ============================================================================\n\nconst TASK_FILENAME_RE = /^(.+)\\.md$/;\n\n/**\n * Enumerate task ids by listing `<paths.tasks>/`. Filenames that do not\n * match the `<task_id>.md` shape, or that decode to a non-conforming task\n * id (per `TaskIdSchema`), are silently skipped — they are surfaced via\n * the caller's `options.onSkip` hook in {@link loadTaskEntries} so list\n * commands can show a warning row.\n *\n * Returns ids in ULID-ascending order (filename sort matches ULID order).\n * Empty directory or ENOENT → `[]`. Other I/O failures throw\n * `\"Failed to enumerate tasks\"`.\n */\nexport async function enumerateTaskIds(paths: BasouPaths): Promise<string[]> {\n // Fast path: read `tasks/index.json` if it exists and is valid. The index\n // is maintained write-through by every task mutation API so the cache\n // matches disk except across crashes / hand-edits / version bumps.\n try {\n const index = await readTaskIndex(paths);\n return index.tasks.map((t) => t.id);\n } catch {\n // Index missing / parse fail / schema mismatch — fall through to the\n // disk-scan rebuild path below. The discrete error classes from\n // readTaskIndex (Task index not found / Invalid task index / Failed\n // to read task index) are all equivalent here.\n }\n\n const ids = await enumerateTaskIdsFromDisk(paths);\n\n // Skip the lazy rebuild entirely when there is nothing to record: a\n // pre-init / empty workspace has no tasks/ dir, so a rebuild attempt\n // would fail with ENOENT and emit a misleading warning. The next write\n // will recreate the index from scratch via updateTaskIndex anyway.\n if (ids.length === 0) {\n return ids;\n }\n\n // Lazy rebuild: scan each task.md and write the resulting index. Best-\n // effort — a per-task read failure (= a malformed file we'd surface as\n // a skip elsewhere) is excluded from the rebuilt index but still\n // returned to the caller in `ids` so loadTaskEntries can surface its\n // own skip reason. Rebuild write failure (disk full, EACCES) is logged\n // and swallowed; the next enumerateTaskIds call will retry the rebuild.\n const entries: TaskIndexEntry[] = [];\n for (const id of ids) {\n try {\n const doc = await readTaskFile(paths, id);\n entries.push(buildTaskIndexEntry(doc.task.task));\n } catch {\n // Skip unreadable entry from the rebuild.\n }\n }\n await rebuildTaskIndex(paths, entries).catch(() => {\n console.warn(\"Failed to rebuild tasks/index.json; subsequent reads will retry\");\n });\n return ids;\n}\n\nasync function enumerateTaskIdsFromDisk(paths: BasouPaths): Promise<string[]> {\n let entries: string[];\n try {\n entries = (await readdir(paths.tasks, { withFileTypes: true }))\n .filter((d) => d.isFile())\n .map((d) => d.name);\n } catch (error: unknown) {\n if (findErrorCode(error, \"ENOENT\")) return [];\n throw new Error(\"Failed to enumerate tasks\", { cause: error });\n }\n const taskIds: string[] = [];\n for (const name of entries) {\n const match = TASK_FILENAME_RE.exec(name);\n if (match === null) continue;\n const candidate = match[1] as string;\n if (!TaskIdSchema.safeParse(candidate).success) continue;\n taskIds.push(candidate);\n }\n taskIds.sort();\n return taskIds;\n}\n\n/**\n * Convert the inner `task` portion of a task.md document into the\n * compact entry shape stored inside `tasks/index.json`. Omits `label`\n * when the task has no label set so the JSON does not store an\n * `undefined` literal.\n */\nfunction buildTaskIndexEntry(task: Task[\"task\"]): TaskIndexEntry {\n return {\n id: task.id,\n status: task.status,\n ...(task.label !== undefined ? { label: task.label } : {}),\n updated_at: task.updated_at,\n };\n}\n\n/**\n * Apply a single write-through update to `tasks/index.json` and swallow\n * any failure with a console.warn so the calling task-write API stays\n * successful (= task.md is the source of truth, index is a soft cache).\n */\nasync function safeUpdateTaskIndex(\n paths: BasouPaths,\n op: Parameters<typeof updateTaskIndex>[1],\n): Promise<void> {\n try {\n await updateTaskIndex(paths, op);\n } catch {\n console.warn(\"Index update failed; rebuild on next read\");\n }\n}\n\nconst ARCHIVE_DIR_NAME = \"archive\";\n\nfunction archiveTasksDir(paths: BasouPaths): string {\n return join(paths.tasks, ARCHIVE_DIR_NAME);\n}\n\n/**\n * Enumerate task ids inside `<paths.tasks>/archive/`. Returns `[]` when the\n * archive directory does not exist (= no task has ever been archived).\n * Filtering / ordering rules mirror {@link enumerateTaskIds}.\n */\nexport async function enumerateArchivedTaskIds(paths: BasouPaths): Promise<string[]> {\n let entries: string[];\n try {\n entries = (await readdir(archiveTasksDir(paths), { withFileTypes: true }))\n .filter((d) => d.isFile())\n .map((d) => d.name);\n } catch (error: unknown) {\n if (findErrorCode(error, \"ENOENT\")) return [];\n throw new Error(\"Failed to enumerate archived tasks\", { cause: error });\n }\n const taskIds: string[] = [];\n for (const name of entries) {\n const match = TASK_FILENAME_RE.exec(name);\n if (match === null) continue;\n const candidate = match[1] as string;\n if (!TaskIdSchema.safeParse(candidate).success) continue;\n taskIds.push(candidate);\n }\n taskIds.sort();\n return taskIds;\n}\n\n/**\n * Read a task.md file looking in the main tasks directory first and falling\n * back to `<paths.tasks>/archive/` if the file is missing there. Returns the\n * parsed document plus a flag indicating whether the hit came from the\n * archive dir. Useful for `basou task show` which surfaces archived tasks\n * read-only without requiring the operator to opt in.\n *\n * Error contract matches {@link readTaskFile} — only the lookup location\n * differs.\n */\nexport async function readTaskFileWithArchiveFallback(\n paths: BasouPaths,\n taskId: string,\n): Promise<{ doc: TaskDocument; archived: boolean }> {\n try {\n const doc = await readTaskFile(paths, taskId);\n return { doc, archived: false };\n } catch (error: unknown) {\n if (!(error instanceof Error && error.message === \"Task file not found\")) {\n throw error;\n }\n }\n const archiveFilePath = join(archiveTasksDir(paths), `${taskId}.md`);\n let raw: string;\n try {\n raw = await readFile(archiveFilePath, \"utf8\");\n } catch (error: unknown) {\n if (findErrorCode(error, \"ENOENT\")) {\n throw new Error(\"Task file not found\", { cause: error });\n }\n throw new Error(\"Failed to read task file\", { cause: error });\n }\n // Parsing mirrors readTaskFile; archived files share the schema.\n let split: { yamlText: string; body: string };\n try {\n split = splitFrontMatter(raw);\n } catch (error: unknown) {\n if (error instanceof Error && error.message === \"Invalid task file format\") {\n throw error;\n }\n throw new Error(\"Failed to read task file\", { cause: error });\n }\n let parsed: unknown;\n try {\n parsed = parseYaml(split.yamlText);\n } catch (error: unknown) {\n throw new Error(\"Failed to read task file\", { cause: error });\n }\n const result = TaskSchema.safeParse(parsed);\n if (!result.success) {\n throw new Error(\"Failed to read task file\", { cause: result.error });\n }\n return { doc: { task: result.data, body: split.body }, archived: true };\n}\n\nexport type TaskSkipReason = \"task_file_invalid\" | \"task_file_unreadable\";\n\nexport type LoadTaskEntriesOptions = {\n onSkip?: (taskId: string, reason: TaskSkipReason) => void;\n};\n\n/**\n * Read every task.md under `<paths.tasks>/` and return the valid documents,\n * skipping malformed / unreadable files with an `onSkip` callback for each.\n *\n * Returned entries are sorted ascending by `task.created_at` (internal asc;\n * the CLI layer reverses for newest-first display).\n */\nexport async function loadTaskEntries(\n paths: BasouPaths,\n options: LoadTaskEntriesOptions = {},\n): Promise<TaskDocument[]> {\n const ids = await enumerateTaskIds(paths);\n const entries: TaskDocument[] = [];\n for (const id of ids) {\n let doc: TaskDocument;\n try {\n doc = await readTaskFile(paths, id);\n } catch (error: unknown) {\n if (error instanceof Error && error.message === \"Invalid task file format\") {\n options.onSkip?.(id, \"task_file_invalid\");\n } else if (error instanceof Error && error.message === \"Failed to read task file\") {\n options.onSkip?.(id, \"task_file_invalid\");\n } else if (error instanceof Error && error.message === \"Task file not found\") {\n // Race: file was enumerated then deleted before read. Treat as unreadable.\n options.onSkip?.(id, \"task_file_unreadable\");\n } else {\n options.onSkip?.(id, \"task_file_unreadable\");\n }\n continue;\n }\n entries.push(doc);\n }\n entries.sort((a, b) => {\n const c = Date.parse(a.task.task.created_at) - Date.parse(b.task.task.created_at);\n return c !== 0 ? c : a.task.task.id.localeCompare(b.task.task.id);\n });\n return entries;\n}\n\n// ============================================================================\n// Status transition rules\n// ============================================================================\n\n// `planned -> done` and `planned -> cancelled` are direct shortcuts so a\n// task that was queued but completed (or abandoned) outside of an explicit\n// `in_progress` phase can be closed with a single CLI call. The 1\n// transition = 1 event invariant is preserved: each shortcut emits exactly\n// one `task_status_changed` event capturing the new from / to pair.\nconst ALLOWED_TRANSITIONS: Readonly<Record<TaskStatus, ReadonlySet<TaskStatus>>> = {\n planned: new Set<TaskStatus>([\"in_progress\", \"done\", \"cancelled\"]),\n in_progress: new Set<TaskStatus>([\"done\", \"cancelled\"]),\n done: new Set<TaskStatus>(),\n cancelled: new Set<TaskStatus>(),\n};\n\nfunction assertTransitionAllowed(from: TaskStatus, to: TaskStatus): void {\n const allowed = ALLOWED_TRANSITIONS[from];\n if (!allowed.has(to)) {\n throw new Error(`Invalid task status transition: ${from} -> ${to}`);\n }\n}\n\n// ============================================================================\n// Specialised error for task.md write failure after the event was persisted\n// ============================================================================\n\n/**\n * Thrown when the task event (`task_created` / `task_status_changed`) was\n * fully persisted to events.jsonl but the accompanying `task.md` write\n * failed. The caller is responsible for surfacing a \"do not rerun\"\n * warning — re-running the same CLI invocation would duplicate the event\n * in events.jsonl.\n *\n * Reconciliation (= regenerating the missing task.md from events) is a\n * v0.2 follow-up (= `task reconcile` family).\n */\n/**\n * `phase` identifies which staged write failed after the event commit:\n * - `create`: task.md create write (ad-hoc or attach path)\n * - `overwrite`: task.md overwrite during a status change\n * - `link-session`: session.yaml `task_id` update during the attach path\n * (split out so CLI warnings describe the actual unsafe artefact\n * instead of always saying \"task.md creation failed\")\n * - `reconcile`: task.md overwrite during `basou task reconcile --write`\n * after the `task_reconciled` event was persisted\n * - `reconcile-finalize`: ad-hoc reconcile session finalize failed (=\n * `FailedToFinalizeError` caught and re-classified)\n * - `reconcile-concurrent`: task.md was modified between the pre-write\n * snapshot and the post-event re-read; the operator is told to re-run\n * reconcile rather than overwrite a stale snapshot\n */\nexport type TaskWriteAfterEventPhase =\n | \"create\"\n | \"overwrite\"\n | \"link-session\"\n | \"reconcile\"\n | \"reconcile-finalize\"\n | \"reconcile-concurrent\"\n // Mirror the reconcile-failure phases for the `refreshTaskLinkedSessions`\n // path. Failure semantics are identical (= ad-hoc session committed, then\n // task.md write / concurrency check failed), but the operator-facing\n // recovery hint must point at `basou task refresh-linkage`, not reconcile.\n | \"linkage-refresh\"\n | \"linkage-refresh-finalize\"\n | \"linkage-refresh-concurrent\"\n // `task_deleted` / `task_archived` event was persisted in events.jsonl but\n // the subsequent file mutation (unlink / move to archive) failed. The\n // event remains the authoritative audit record; the operator must reconcile\n // the residual file state by hand.\n | \"delete\"\n | \"archive\";\n\nexport class TaskWriteAfterEventError extends Error {\n readonly taskId: PrefixedId<\"task\">;\n readonly eventId: PrefixedId<\"evt\">;\n readonly sessionId: PrefixedId<\"ses\">;\n readonly phase: TaskWriteAfterEventPhase;\n\n constructor(args: {\n taskId: PrefixedId<\"task\">;\n eventId: PrefixedId<\"evt\">;\n sessionId: PrefixedId<\"ses\">;\n phase: TaskWriteAfterEventPhase;\n cause: unknown;\n }) {\n super(\"Failed to write task file after event was persisted\", { cause: args.cause });\n this.name = \"TaskWriteAfterEventError\";\n this.taskId = args.taskId;\n this.eventId = args.eventId;\n this.sessionId = args.sessionId;\n this.phase = args.phase;\n }\n}\n\n// ============================================================================\n// Orchestrator: createTaskWithEvent\n// ============================================================================\n\nexport type CreateAdHocTaskInput = {\n mode: \"ad-hoc\";\n paths: BasouPaths;\n manifest: Manifest;\n occurredAt: string;\n taskId: PrefixedId<\"task\">;\n title: string;\n label?: string;\n initialStatus: TaskStatus;\n description: string;\n workingDirectory: string;\n /**\n * Optional override for `task.md.updated_at` when `initialStatus` is a\n * terminal value (done / cancelled). Lets the operator backdate a\n * retroactively-recorded completed task so `task.md` reflects the actual\n * completion moment while `events.jsonl` keeps recording time. Ignored\n * for non-terminal statuses.\n */\n completedAt?: string;\n};\n\nexport type AttachTaskInput = {\n mode: \"attach\";\n paths: BasouPaths;\n occurredAt: string;\n sessionId: PrefixedId<\"ses\">;\n taskId: PrefixedId<\"task\">;\n title: string;\n label?: string;\n initialStatus: TaskStatus;\n description: string;\n attachableStatuses?: ReadonlySet<AttachableStatus>;\n /** See {@link CreateAdHocTaskInput.completedAt}. */\n completedAt?: string;\n};\n\nexport type CreateTaskInput = CreateAdHocTaskInput | AttachTaskInput;\n\nexport type CreateTaskResult = {\n taskId: PrefixedId<\"task\">;\n eventId: PrefixedId<\"evt\">;\n sessionId: PrefixedId<\"ses\">;\n sessionStatus: SessionStatus;\n};\n\nfunction buildTaskCreatedEvent(input: {\n eventId: PrefixedId<\"evt\">;\n sessionId: PrefixedId<\"ses\">;\n taskId: PrefixedId<\"task\">;\n title: string;\n occurredAt: string;\n}): Event {\n return {\n schema_version: \"0.1.0\",\n id: input.eventId,\n session_id: input.sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"task_created\",\n task_id: input.taskId,\n title: input.title,\n };\n}\n\nfunction buildTaskStatusChangedEvent(input: {\n eventId: PrefixedId<\"evt\">;\n sessionId: PrefixedId<\"ses\">;\n taskId: PrefixedId<\"task\">;\n from: TaskStatus;\n to: TaskStatus;\n occurredAt: string;\n}): Event {\n return {\n schema_version: \"0.1.0\",\n id: input.eventId,\n session_id: input.sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"task_status_changed\",\n task_id: input.taskId,\n from: input.from,\n to: input.to,\n };\n}\n\nfunction buildAdHocTaskLabel(title: string, mode: \"new\" | \"status\"): string {\n const truncated =\n title.length > LABEL_TITLE_MAX ? `${title.slice(0, LABEL_TRUNCATE_HEAD)}...` : title;\n return mode === \"new\" ? `Ad-hoc task: ${truncated}` : `Ad-hoc task status: ${truncated}`;\n}\n\n// Kept distinct from buildAdHocTaskLabel rather than threading a third mode\n// through that helper — `basou task reconcile` is a management operation, not\n// a creation/status flow, and the label prefix should read that way.\nfunction buildAdHocReconcileLabel(title: string): string {\n const truncated =\n title.length > LABEL_TITLE_MAX ? `${title.slice(0, LABEL_TRUNCATE_HEAD)}...` : title;\n return `Ad-hoc task reconcile: ${truncated}`;\n}\n\n// Separate label generator for `basou task refresh-linkage` so the operator\n// can distinguish refresh runs from reconcile runs at a glance in session\n// listings — both flow through `createAdHocSessionWithEvent` but answer\n// different questions (broken-ref repair vs. snapshot-vs-events sync).\nfunction buildAdHocRefreshLinkageLabel(title: string): string {\n const truncated =\n title.length > LABEL_TITLE_MAX ? `${title.slice(0, LABEL_TRUNCATE_HEAD)}...` : title;\n return `Ad-hoc task refresh-linkage: ${truncated}`;\n}\n\nfunction buildAdHocDeleteLabel(title: string): string {\n const truncated =\n title.length > LABEL_TITLE_MAX ? `${title.slice(0, LABEL_TRUNCATE_HEAD)}...` : title;\n return `Ad-hoc task delete: ${truncated}`;\n}\n\nfunction buildAdHocArchiveLabel(title: string): string {\n const truncated =\n title.length > LABEL_TITLE_MAX ? `${title.slice(0, LABEL_TRUNCATE_HEAD)}...` : title;\n return `Ad-hoc task archive: ${truncated}`;\n}\n\nfunction buildTaskReconciledEvent(input: {\n eventId: PrefixedId<\"evt\">;\n sessionId: PrefixedId<\"ses\">;\n taskId: PrefixedId<\"task\">;\n removedCreatedInSession: PrefixedId<\"ses\"> | null;\n createdInSessionReplacement: PrefixedId<\"ses\"> | null;\n removedLinkedSessions: PrefixedId<\"ses\">[];\n occurredAt: string;\n}): Event {\n return {\n schema_version: \"0.1.0\",\n id: input.eventId,\n session_id: input.sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"task_reconciled\",\n task_id: input.taskId,\n removed_created_in_session: input.removedCreatedInSession,\n created_in_session_replacement: input.createdInSessionReplacement,\n removed_linked_sessions: input.removedLinkedSessions,\n };\n}\n\nfunction buildTaskDeletedEvent(input: {\n eventId: PrefixedId<\"evt\">;\n sessionId: PrefixedId<\"ses\">;\n taskId: PrefixedId<\"task\">;\n title: string;\n occurredAt: string;\n}): Event {\n return {\n schema_version: \"0.1.0\",\n id: input.eventId,\n session_id: input.sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"task_deleted\",\n task_id: input.taskId,\n title: input.title,\n };\n}\n\nfunction buildTaskArchivedEvent(input: {\n eventId: PrefixedId<\"evt\">;\n sessionId: PrefixedId<\"ses\">;\n taskId: PrefixedId<\"task\">;\n title: string;\n occurredAt: string;\n}): Event {\n return {\n schema_version: \"0.1.0\",\n id: input.eventId,\n session_id: input.sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"task_archived\",\n task_id: input.taskId,\n title: input.title,\n };\n}\n\nfunction buildTaskLinkageRefreshedEvent(input: {\n eventId: PrefixedId<\"evt\">;\n sessionId: PrefixedId<\"ses\">;\n taskId: PrefixedId<\"task\">;\n addedLinkedSessions: PrefixedId<\"ses\">[];\n removedLinkedSessions: PrefixedId<\"ses\">[];\n finalCount: number;\n occurredAt: string;\n}): Event {\n return {\n schema_version: \"0.1.0\",\n id: input.eventId,\n session_id: input.sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"task_linkage_refreshed\",\n task_id: input.taskId,\n added_linked_sessions: input.addedLinkedSessions,\n removed_linked_sessions: input.removedLinkedSessions,\n final_count: input.finalCount,\n };\n}\n\n/**\n * Create a new task: fires a single `task_created` event and writes\n * `.basou/tasks/<taskId>.md` with status = `initialStatus`.\n *\n * Ad-hoc path: a fresh ad-hoc session is minted (5-event bulk write,\n * `task_created` as the target event, session.yaml.task_id pinned to the\n * new task).\n *\n * Attach path: the target session's `task_id` is validated against the\n * session ⇆ task anchor invariant (see\n * `docs/spec/workspace.md#21-confirmed-invariants`; null → updated to the\n * new task; existing X → rejected since X is already owned). If validation\n * passes, the event is appended to events.jsonl and session.yaml's\n * `task_id` is updated to the new task.\n *\n * Race window (v0.1 accepts): stage 2 writes the event, stage 3 writes\n * task.md. A failure on stage 3 leaves events.jsonl ahead of task.md;\n * {@link TaskWriteAfterEventError} surfaces this with a \"do not rerun\"\n * warning so the operator can reconcile manually until the v0.2 reconcile\n * flow arrives.\n */\nexport async function createTaskWithEvent(input: CreateTaskInput): Promise<CreateTaskResult> {\n // Boundary parses so direct (non-CLI) callers can't smuggle in malformed\n // ids / statuses / titles past the CLI-side guards. All checks here run\n // BEFORE any persistent write, so a rejection leaves events.jsonl and\n // task.md untouched.\n TaskIdSchema.parse(input.taskId);\n InitialTaskStatusSchema.parse(input.initialStatus);\n TaskTitleSchema.parse(input.title);\n if (input.label !== undefined) {\n TaskLabelSchema.parse(input.label);\n }\n if (input.completedAt !== undefined) {\n CompletedAtSchema.parse(input.completedAt);\n }\n\n if (input.mode === \"ad-hoc\") {\n return createTaskAdHoc(input);\n }\n return createTaskAttach(input);\n}\n\nasync function createTaskAdHoc(input: CreateAdHocTaskInput): Promise<CreateTaskResult> {\n const adHoc = await createAdHocSessionWithEvent({\n paths: input.paths,\n manifest: input.manifest,\n label: buildAdHocTaskLabel(input.title, \"new\"),\n occurredAt: input.occurredAt,\n sessionSource: \"human\",\n workingDirectory: input.workingDirectory,\n invocation: {\n command: \"basou task new\",\n args: buildTaskNewInvocationArgs(input.title, input.initialStatus, input.completedAt),\n },\n taskId: input.taskId,\n targetEventBuilders: buildTaskNewTargetEventBuilders({\n taskId: input.taskId,\n title: input.title,\n initialStatus: input.initialStatus,\n occurredAt: input.occurredAt,\n }),\n });\n\n const task: Task = buildInitialTask({\n taskId: input.taskId,\n title: input.title,\n ...(input.label !== undefined ? { label: input.label } : {}),\n status: input.initialStatus,\n occurredAt: input.occurredAt,\n ...(input.completedAt !== undefined ? { completedAt: input.completedAt } : {}),\n workspaceId: input.manifest.workspace.id,\n createdInSession: adHoc.sessionId,\n });\n // `targetEventIds[0]` is the `task_created` anchor (= what the caller cares\n // about); a second `task_status_changed` event may also live in this\n // ad-hoc session when initialStatus is terminal, but it is not the\n // primary task-lifecycle anchor.\n const anchorEventId = adHoc.targetEventIds[0] as PrefixedId<\"evt\">;\n try {\n await writeTaskFile(\n input.paths,\n input.taskId,\n { task, body: input.description },\n { mode: \"create\" },\n );\n } catch (error: unknown) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: anchorEventId,\n sessionId: adHoc.sessionId,\n phase: \"create\",\n cause: error,\n });\n }\n await safeUpdateTaskIndex(input.paths, { kind: \"add\", entry: buildTaskIndexEntry(task.task) });\n return {\n taskId: input.taskId,\n eventId: anchorEventId,\n sessionId: adHoc.sessionId,\n sessionStatus: \"completed\",\n };\n}\n\nasync function createTaskAttach(input: AttachTaskInput): Promise<CreateTaskResult> {\n SessionIdSchema.parse(input.sessionId);\n\n // Per-session lock spans the entire attach window (session.yaml read →\n // collision-matrix check → task_created append → session.yaml task_id\n // update → optional task_status_changed append). Without it, two\n // concurrent attaches on the same session.yaml could both observe a\n // null task_id, both append their own `task_created`, and race on the\n // final task_id overwrite.\n const sessionLock = await acquireLock(input.paths, \"session\", input.sessionId);\n try {\n return await createTaskAttachLocked(input);\n } finally {\n await sessionLock.release();\n }\n}\n\nasync function createTaskAttachLocked(input: AttachTaskInput): Promise<CreateTaskResult> {\n // 1. Read session.yaml + validate the §F.7.2 collision matrix BEFORE writing\n // anything. status / task_id checks share the same read.\n const sessionDoc = await readSessionYaml(input.paths, input.sessionId);\n const status = sessionDoc.session.status;\n if (status === \"imported\") {\n throw new Error(\"Cannot attach to imported session\");\n }\n const attachable = input.attachableStatuses ?? DEFAULT_ATTACHABLE_STATUSES;\n if (!attachable.has(status as AttachableStatus)) {\n throw new Error(`Session is not active: ${status}`);\n }\n const existingTaskId = sessionDoc.session.task_id ?? null;\n if (existingTaskId !== null && existingTaskId !== input.taskId) {\n throw new Error(`Session already linked to a different task: ${existingTaskId}`);\n }\n if (existingTaskId === input.taskId) {\n // Re-creating the same task on the same session would duplicate\n // `task_created` in events.jsonl. Reject up front.\n throw new Error(`Task already exists: ${input.taskId}`);\n }\n\n // 2. Append `task_created` to events.jsonl. We use appendEventToExistingSession\n // so the same status/imported-rejection logic is shared across attach-flavoured callers.\n const appendResult = await appendEventToExistingSession({\n paths: input.paths,\n sessionId: input.sessionId,\n ...(input.attachableStatuses !== undefined\n ? { attachableStatuses: input.attachableStatuses }\n : {}),\n eventBuilder: (eventId) =>\n buildTaskCreatedEvent({\n eventId,\n sessionId: input.sessionId,\n taskId: input.taskId,\n title: input.title,\n occurredAt: input.occurredAt,\n }),\n });\n\n // 3. Update session.yaml task_id (null → new) so the single-session ↔\n // single-task invariant holds. Failure here puts us into the same\n // \"event persisted, side-effect missing\" band as task.md. Use\n // phase: \"link-session\" so the operator warning identifies the failed\n // artefact correctly.\n try {\n const updated = {\n ...sessionDoc,\n session: { ...sessionDoc.session, task_id: input.taskId },\n };\n await overwriteYamlFile(join(input.paths.sessions, input.sessionId, \"session.yaml\"), updated);\n } catch (error: unknown) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: appendResult.eventId,\n sessionId: input.sessionId,\n phase: \"link-session\",\n cause: error,\n });\n }\n\n // 4. For terminal initialStatus (done / cancelled) append a second target\n // event `task_status_changed (planned → terminal)` so the events.jsonl\n // audit trail records the implicit transition. The ALLOWED_TRANSITIONS\n // shortcut from `planned` to `done|cancelled` makes this a single\n // permitted edge. The session.yaml `task_id` link from step 3 covers\n // both events; no further session.yaml write is needed.\n if (isTerminalTaskStatus(input.initialStatus)) {\n await appendEventToExistingSession({\n paths: input.paths,\n sessionId: input.sessionId,\n ...(input.attachableStatuses !== undefined\n ? { attachableStatuses: input.attachableStatuses }\n : {}),\n eventBuilder: (eventId) =>\n buildTaskStatusChangedEvent({\n eventId,\n sessionId: input.sessionId,\n taskId: input.taskId,\n from: \"planned\",\n to: input.initialStatus,\n occurredAt: input.occurredAt,\n }),\n });\n }\n\n // 5. Write task.md (create mode, collision = rerun guard).\n const task: Task = buildInitialTask({\n taskId: input.taskId,\n title: input.title,\n ...(input.label !== undefined ? { label: input.label } : {}),\n status: input.initialStatus,\n occurredAt: input.occurredAt,\n ...(input.completedAt !== undefined ? { completedAt: input.completedAt } : {}),\n workspaceId: sessionDoc.session.workspace_id,\n createdInSession: input.sessionId,\n });\n try {\n await writeTaskFile(\n input.paths,\n input.taskId,\n { task, body: input.description },\n { mode: \"create\" },\n );\n } catch (error: unknown) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: appendResult.eventId,\n sessionId: input.sessionId,\n phase: \"create\",\n cause: error,\n });\n }\n\n await safeUpdateTaskIndex(input.paths, { kind: \"add\", entry: buildTaskIndexEntry(task.task) });\n\n return {\n taskId: input.taskId,\n eventId: appendResult.eventId,\n sessionId: input.sessionId,\n sessionStatus: status,\n };\n}\n\nfunction buildInitialTask(input: {\n taskId: PrefixedId<\"task\">;\n title: string;\n label?: string;\n status: TaskStatus;\n occurredAt: string;\n /**\n * Override for `updated_at` when `status` is terminal. Ignored for\n * non-terminal statuses so backdating a non-completed task is not\n * possible by accident.\n */\n completedAt?: string;\n workspaceId: PrefixedId<\"ws\">;\n createdInSession: PrefixedId<\"ses\">;\n}): Task {\n const updatedAt =\n input.completedAt !== undefined && isTerminalTaskStatus(input.status)\n ? input.completedAt\n : input.occurredAt;\n return {\n schema_version: \"0.1.0\",\n task: {\n id: input.taskId,\n title: input.title,\n ...(input.label !== undefined ? { label: input.label } : {}),\n status: input.status,\n created_at: input.occurredAt,\n updated_at: updatedAt,\n workspace_id: input.workspaceId,\n created_in_session: input.createdInSession,\n linked_sessions: [input.createdInSession],\n },\n };\n}\n\n// Helpers for the ad-hoc `task new` path. The invocation args list mirrors\n// the operator's CLI input so the recorded `session.yaml.invocation.args`\n// stays accurate even when `--status` / `--completed-at` were supplied.\nfunction buildTaskNewInvocationArgs(\n title: string,\n initialStatus: TaskStatus,\n completedAt: string | undefined,\n): string[] {\n const args = [\"--title\", title];\n if (initialStatus !== \"planned\") {\n args.push(\"--status\", initialStatus);\n }\n if (completedAt !== undefined && isTerminalTaskStatus(initialStatus)) {\n args.push(\"--completed-at\", completedAt);\n }\n return args;\n}\n\nfunction buildTaskNewTargetEventBuilders(input: {\n taskId: PrefixedId<\"task\">;\n title: string;\n initialStatus: TaskStatus;\n occurredAt: string;\n}): Array<(sessionId: PrefixedId<\"ses\">, eventId: PrefixedId<\"evt\">) => Event> {\n const createdBuilder = (sessionId: PrefixedId<\"ses\">, eventId: PrefixedId<\"evt\">): Event =>\n buildTaskCreatedEvent({\n eventId,\n sessionId,\n taskId: input.taskId,\n title: input.title,\n occurredAt: input.occurredAt,\n });\n if (!isTerminalTaskStatus(input.initialStatus)) {\n return [createdBuilder];\n }\n // For terminal initialStatus, emit `task_status_changed (planned → terminal)`\n // right after `task_created` so replay reconstructs the implicit\n // transition. The shortcut edges `planned → done|cancelled` are already\n // allowed by ALLOWED_TRANSITIONS.\n const statusChangedBuilder = (sessionId: PrefixedId<\"ses\">, eventId: PrefixedId<\"evt\">): Event =>\n buildTaskStatusChangedEvent({\n eventId,\n sessionId,\n taskId: input.taskId,\n from: \"planned\",\n to: input.initialStatus,\n occurredAt: input.occurredAt,\n });\n return [createdBuilder, statusChangedBuilder];\n}\n\n// ============================================================================\n// Orchestrator: updateTaskStatusWithEvent\n// ============================================================================\n\nexport type UpdateAdHocTaskStatusInput = {\n mode: \"ad-hoc\";\n paths: BasouPaths;\n manifest: Manifest;\n occurredAt: string;\n taskId: PrefixedId<\"task\">;\n newStatus: TaskStatus;\n workingDirectory: string;\n};\n\nexport type AttachUpdateTaskStatusInput = {\n mode: \"attach\";\n paths: BasouPaths;\n occurredAt: string;\n sessionId: PrefixedId<\"ses\">;\n taskId: PrefixedId<\"task\">;\n newStatus: TaskStatus;\n attachableStatuses?: ReadonlySet<AttachableStatus>;\n};\n\nexport type UpdateTaskStatusInput = UpdateAdHocTaskStatusInput | AttachUpdateTaskStatusInput;\n\nexport type UpdateTaskStatusResult = {\n taskId: PrefixedId<\"task\">;\n eventId: PrefixedId<\"evt\">;\n sessionId: PrefixedId<\"ses\">;\n sessionStatus: SessionStatus;\n previousStatus: TaskStatus;\n newStatus: TaskStatus;\n};\n\n/**\n * Fire a `task_status_changed` event and overwrite the task.md front matter\n * with the new status / `updated_at` / appended-but-deduped `linked_sessions`.\n *\n * Validates the transition BEFORE any event write so a rejected transition\n * leaves events.jsonl untouched. The canonical edge set lives in\n * {@link ALLOWED_TRANSITIONS}; the current shape is:\n * planned → {in_progress, done, cancelled}\n * in_progress → {done, cancelled}\n * done / cancelled are terminal (= idempotent same-state is rejected too).\n */\nexport async function updateTaskStatusWithEvent(\n input: UpdateTaskStatusInput,\n): Promise<UpdateTaskStatusResult> {\n TaskIdSchema.parse(input.taskId);\n\n // Per-task lock guards the read-modify-write of task.md against concurrent\n // writers on the same task id (= another `task status` / `task edit` /\n // `task reconcile` on the same task). Released in a finally block so the\n // lock never lingers on either the success or the failure path.\n const handle = await acquireLock(input.paths, \"task\", input.taskId);\n try {\n // 1. Load current task.md (= source of truth for current status).\n const currentDoc = await readTaskFile(input.paths, input.taskId);\n const previousStatus = currentDoc.task.task.status;\n\n // 2. Validate transition before touching any persistent state.\n assertTransitionAllowed(previousStatus, input.newStatus);\n\n if (input.mode === \"ad-hoc\") {\n return await updateTaskStatusAdHoc(input, currentDoc, previousStatus);\n }\n return await updateTaskStatusAttach(input, currentDoc, previousStatus);\n } finally {\n await handle.release();\n }\n}\n\nasync function updateTaskStatusAdHoc(\n input: UpdateAdHocTaskStatusInput,\n currentDoc: TaskDocument,\n previousStatus: TaskStatus,\n): Promise<UpdateTaskStatusResult> {\n const title = currentDoc.task.task.title;\n const adHoc = await createAdHocSessionWithEvent({\n paths: input.paths,\n manifest: input.manifest,\n label: buildAdHocTaskLabel(title, \"status\"),\n occurredAt: input.occurredAt,\n sessionSource: \"human\",\n workingDirectory: input.workingDirectory,\n invocation: { command: \"basou task status\", args: [input.taskId, input.newStatus] },\n taskId: input.taskId,\n targetEventBuilders: [\n (sessionId, eventId) =>\n buildTaskStatusChangedEvent({\n eventId,\n sessionId,\n taskId: input.taskId,\n from: previousStatus,\n to: input.newStatus,\n occurredAt: input.occurredAt,\n }),\n ],\n });\n\n const anchorEventId = adHoc.targetEventIds[0] as PrefixedId<\"evt\">;\n // 3. Overwrite task.md (status + updated_at + linked_sessions append-dedup).\n const updatedDoc = buildUpdatedDoc({\n currentDoc,\n newStatus: input.newStatus,\n occurredAt: input.occurredAt,\n appendSessionId: adHoc.sessionId,\n });\n try {\n await writeTaskFile(input.paths, input.taskId, updatedDoc, { mode: \"overwrite\" });\n } catch (error: unknown) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: anchorEventId,\n sessionId: adHoc.sessionId,\n phase: \"overwrite\",\n cause: error,\n });\n }\n await safeUpdateTaskIndex(input.paths, {\n kind: \"update\",\n entry: buildTaskIndexEntry(updatedDoc.task.task),\n });\n return {\n taskId: input.taskId,\n eventId: anchorEventId,\n sessionId: adHoc.sessionId,\n sessionStatus: \"completed\",\n previousStatus,\n newStatus: input.newStatus,\n };\n}\n\nasync function updateTaskStatusAttach(\n input: AttachUpdateTaskStatusInput,\n currentDoc: TaskDocument,\n previousStatus: TaskStatus,\n): Promise<UpdateTaskStatusResult> {\n SessionIdSchema.parse(input.sessionId);\n\n // Per-session lock guards the session.yaml read → events.jsonl append\n // window so a concurrent writer on the same session cannot append a\n // conflicting `task_status_changed` or flip session.yaml to a state\n // that invalidates the status check below. We are already inside the\n // per-task lock (updateTaskStatusWithEvent acquires it); the per-task\n // → per-session order is fixed across the codebase to keep cross-API\n // deadlocks impossible.\n const sessionLock = await acquireLock(input.paths, \"session\", input.sessionId);\n try {\n return await updateTaskStatusAttachLocked(input, currentDoc, previousStatus);\n } finally {\n await sessionLock.release();\n }\n}\n\nasync function updateTaskStatusAttachLocked(\n input: AttachUpdateTaskStatusInput,\n currentDoc: TaskDocument,\n previousStatus: TaskStatus,\n): Promise<UpdateTaskStatusResult> {\n const sessionDoc = await readSessionYaml(input.paths, input.sessionId);\n const status = sessionDoc.session.status;\n if (status === \"imported\") {\n throw new Error(\"Cannot attach to imported session\");\n }\n const attachable = input.attachableStatuses ?? DEFAULT_ATTACHABLE_STATUSES;\n if (!attachable.has(status as AttachableStatus)) {\n throw new Error(`Session is not active: ${status}`);\n }\n // task_id collision: the session MUST already be linked to the same task,\n // otherwise a status change on a task that the session does not own would\n // violate the session ⇆ task anchor invariant.\n const existingTaskId = sessionDoc.session.task_id ?? null;\n if (existingTaskId === null) {\n throw new Error(`Session is not linked to task: ${input.taskId}`);\n }\n if (existingTaskId !== input.taskId) {\n throw new Error(`Session already linked to a different task: ${existingTaskId}`);\n }\n\n const appendResult = await appendEventToExistingSession({\n paths: input.paths,\n sessionId: input.sessionId,\n ...(input.attachableStatuses !== undefined\n ? { attachableStatuses: input.attachableStatuses }\n : {}),\n eventBuilder: (eventId) =>\n buildTaskStatusChangedEvent({\n eventId,\n sessionId: input.sessionId,\n taskId: input.taskId,\n from: previousStatus,\n to: input.newStatus,\n occurredAt: input.occurredAt,\n }),\n });\n\n const updatedDoc = buildUpdatedDoc({\n currentDoc,\n newStatus: input.newStatus,\n occurredAt: input.occurredAt,\n appendSessionId: input.sessionId,\n });\n try {\n await writeTaskFile(input.paths, input.taskId, updatedDoc, { mode: \"overwrite\" });\n } catch (error: unknown) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: appendResult.eventId,\n sessionId: input.sessionId,\n phase: \"overwrite\",\n cause: error,\n });\n }\n await safeUpdateTaskIndex(input.paths, {\n kind: \"update\",\n entry: buildTaskIndexEntry(updatedDoc.task.task),\n });\n return {\n taskId: input.taskId,\n eventId: appendResult.eventId,\n sessionId: input.sessionId,\n sessionStatus: status,\n previousStatus,\n newStatus: input.newStatus,\n };\n}\n\nfunction buildUpdatedDoc(input: {\n currentDoc: TaskDocument;\n newStatus: TaskStatus;\n occurredAt: string;\n appendSessionId: PrefixedId<\"ses\">;\n}): TaskDocument {\n const linked = input.currentDoc.task.task.linked_sessions;\n const merged = linked.includes(input.appendSessionId)\n ? linked\n : [...linked, input.appendSessionId];\n const next: Task = {\n ...input.currentDoc.task,\n task: {\n ...input.currentDoc.task.task,\n status: input.newStatus,\n updated_at: input.occurredAt,\n linked_sessions: merged,\n },\n };\n return { task: next, body: input.currentDoc.body };\n}\n\n// ============================================================================\n// Reconcile (basou task reconcile)\n// ============================================================================\n\n/**\n * Single-task audit result. Always returned by {@link reconcileTask} regardless\n * of mode: in dry-run the `clean` / `broken*` fields describe what would change\n * and `reconcileSession` is `null`; in write mode the same fields describe\n * what did change and `reconcileSession` carries the minted ad-hoc session +\n * `task_reconciled` event ids.\n *\n * Broken `linked_sessions[]` entries are deduplicated against the same session\n * id appearing more than once in the source task.md (hand-edit defence).\n */\nexport type ReconcileResult = {\n taskId: PrefixedId<\"task\">;\n clean: boolean;\n brokenCreatedInSession: PrefixedId<\"ses\"> | null;\n brokenLinkedSessions: PrefixedId<\"ses\">[];\n reconcileSession: {\n sessionId: PrefixedId<\"ses\">;\n eventId: PrefixedId<\"evt\">;\n } | null;\n};\n\n/**\n * Per-task failure record collected by {@link reconcileAllTasks}. The scan\n * keeps running on isolated failures so one bad task does not freeze the\n * batch; the CLI layer renders this list and exits 1 if any entry is present.\n *\n * `phase` is populated only for {@link TaskWriteAfterEventError}; for any\n * other error class it is `null` and the operator must use `--verbose` to\n * surface the cause chain.\n */\nexport type ReconcileFailure = {\n taskId: PrefixedId<\"task\">;\n errorClass: string;\n phase: TaskWriteAfterEventPhase | null;\n};\n\n/**\n * Batch audit result. Order follows `enumerateTaskIds(paths)` (ULID-ascending).\n * `scanned` is the number of readable task.md files processed (= excludes\n * malformed task.md from the count so an integrity-broken file does not\n * pad the total).\n */\nexport type ReconcileAllResult = {\n results: ReconcileResult[];\n failed: ReconcileFailure[];\n scanned: number;\n};\n\nexport type ReconcileTaskInput = {\n taskId: PrefixedId<\"task\">;\n occurredAt: string;\n workingDirectory: string;\n write: boolean;\n /**\n * Whether the caller invoked reconcile against a single task (`--task <id>`)\n * or as part of a full scan. The ad-hoc reconcile session records the form\n * on its `invocation.args` so audit trails distinguish targeted repairs\n * from sweeps:\n * - `\"single\"` -> `[\"--task\", <taskId>, \"--write\"]`\n * - `\"all\"` -> `[\"--write\"]` (= the operator typed no task id, so the\n * scan-wide intent is preserved instead of synthesising one per task)\n * Defaults to `\"single\"` so direct callers (tests, programmatic uses) keep\n * the targeted form without an explicit argument.\n */\n scope?: \"single\" | \"all\";\n /**\n * Test-only hook: the test runner uses this to mutate the task file\n * from outside the reconcile flow between the pre-write snapshot and\n * the post-event re-read, simulating a concurrent edit so the\n * `reconcile-concurrent` branch can be exercised deterministically.\n * Production callers leave it undefined.\n */\n _onPhaseCompleted?: (phase: \"phase-4-snapshot\" | \"phase-5-bulk-write\") => Promise<void>;\n};\n\nexport type ReconcileAllTasksInput = {\n /**\n * Per-task timestamp factory. Each reconciled task gets a fresh ISO string\n * so concurrent ad-hoc sessions do not collide on `occurred_at`. The CLI\n * layer wires this to `ctx.nowProvider().toISOString()`.\n */\n occurredAt: () => string;\n workingDirectory: string;\n write: boolean;\n};\n\nexport type ReconcileAllTasksOptions = {\n /**\n * When true the result includes clean tasks (= no broken refs). The CLI\n * layer leaves this false so the human output only mentions tasks that\n * actually changed.\n */\n includeClean?: boolean;\n};\n\ntype TaskMdSnapshot = {\n mtimeMs: number;\n hash: string;\n};\n\nasync function computeTaskMdSnapshot(paths: BasouPaths, taskId: string): Promise<TaskMdSnapshot> {\n const filePath = join(paths.tasks, `${taskId}.md`);\n const [stats, raw] = await Promise.all([stat(filePath), readFile(filePath)]);\n const hash = createHash(\"sha256\").update(raw).digest(\"hex\");\n return { mtimeMs: stats.mtimeMs, hash };\n}\n\n// Read task.md and derive its mtime/sha256 snapshot from the SAME raw bytes\n// the TaskDocument was parsed from. An earlier internal review flagged that\n// the previous \"readTaskFile, then computeTaskMdSnapshot\" sequence left a window\n// where a concurrent edit between those two reads could leave the caller\n// acting on stale content while the snapshot already reflected the new\n// content — and stage 7 would then clobber the new bytes with the stale\n// TaskDocument. Sharing the raw bytes here means stage 6's re-read is\n// compared against the EXACT bytes that produced this document, so any\n// drift since this read is caught.\nasync function readTaskFileWithSnapshot(\n paths: BasouPaths,\n taskId: string,\n): Promise<{ doc: TaskDocument; snapshot: TaskMdSnapshot }> {\n const filePath = join(paths.tasks, `${taskId}.md`);\n let rawBuffer: Buffer;\n let stats: Awaited<ReturnType<typeof stat>>;\n try {\n [rawBuffer, stats] = await Promise.all([readFile(filePath), stat(filePath)]);\n } catch (error: unknown) {\n if (findErrorCode(error, \"ENOENT\")) {\n throw new Error(\"Task file not found\", { cause: error });\n }\n throw new Error(\"Failed to read task file\", { cause: error });\n }\n const raw = rawBuffer.toString(\"utf8\");\n const hash = createHash(\"sha256\").update(rawBuffer).digest(\"hex\");\n // Parse logic mirrors readTaskFile so the error contract stays identical\n // (Invalid task file format / Failed to read task file). Duplicated here to\n // avoid a second readFile from the public helper.\n let split: { yamlText: string; body: string };\n try {\n split = splitFrontMatter(raw);\n } catch (error: unknown) {\n if (error instanceof Error && error.message === \"Invalid task file format\") {\n throw error;\n }\n throw new Error(\"Failed to read task file\", { cause: error });\n }\n let parsed: unknown;\n try {\n parsed = parseYaml(split.yamlText);\n } catch (error: unknown) {\n throw new Error(\"Failed to read task file\", { cause: error });\n }\n const result = TaskSchema.safeParse(parsed);\n if (!result.success) {\n throw new Error(\"Failed to read task file\", { cause: result.error });\n }\n return {\n doc: { task: result.data, body: split.body },\n snapshot: { mtimeMs: stats.mtimeMs, hash },\n };\n}\n\ntype DetectedBrokenRefs = {\n brokenCreatedInSession: PrefixedId<\"ses\"> | null;\n brokenLinkedSessions: PrefixedId<\"ses\">[];\n};\n\n// `enumerateSessionDirs` returns directory names only — it does NOT validate\n// the contents of each `session.yaml`. By treating directory existence alone\n// as \"reachable\", reconcile targets the dogfood failure mode where a session\n// directory is removed entirely and a dangling id remains in task.md, while\n// keeping the \"broken\" predicate cheap. A directory that exists but whose\n// session.yaml is missing or schema-invalid is intentionally classified as\n// reachable here; that flavour of corruption is the responsibility of session\n// integrity tooling and is out of scope for v0.2 reconcile.\nasync function detectBrokenRefs(\n paths: BasouPaths,\n task: Task[\"task\"],\n): Promise<DetectedBrokenRefs> {\n const sessionDirs = new Set(await enumerateSessionDirs(paths));\n const brokenCreatedInSession = sessionDirs.has(task.created_in_session)\n ? null\n : (task.created_in_session as PrefixedId<\"ses\">);\n // Deduplicate broken entries so duplicate broken ids in a hand-edited task.md\n // surface as a single entry on the event payload.\n const seen = new Set<string>();\n const brokenLinkedSessions: PrefixedId<\"ses\">[] = [];\n for (const sid of task.linked_sessions) {\n if (sessionDirs.has(sid)) continue;\n if (seen.has(sid)) continue;\n seen.add(sid);\n brokenLinkedSessions.push(sid as PrefixedId<\"ses\">);\n }\n return { brokenCreatedInSession, brokenLinkedSessions };\n}\n\nfunction buildReconciledDoc(input: {\n currentDoc: TaskDocument;\n brokenCreatedInSession: PrefixedId<\"ses\"> | null;\n brokenLinkedSessions: ReadonlyArray<PrefixedId<\"ses\">>;\n reconcileSessionId: PrefixedId<\"ses\">;\n occurredAt: string;\n}): TaskDocument {\n const brokenSet = new Set<string>(input.brokenLinkedSessions);\n const filtered = input.currentDoc.task.task.linked_sessions.filter((sid) => !brokenSet.has(sid));\n const merged: PrefixedId<\"ses\">[] = [...filtered] as PrefixedId<\"ses\">[];\n if (!merged.includes(input.reconcileSessionId)) {\n merged.push(input.reconcileSessionId);\n }\n const nextCreatedInSession =\n input.brokenCreatedInSession !== null\n ? input.reconcileSessionId\n : input.currentDoc.task.task.created_in_session;\n const next: Task = {\n ...input.currentDoc.task,\n task: {\n ...input.currentDoc.task.task,\n created_in_session: nextCreatedInSession,\n updated_at: input.occurredAt,\n linked_sessions: merged,\n },\n };\n return { task: next, body: input.currentDoc.body };\n}\n\n/**\n * Audit a single task's session references. In `write: false` mode this is a\n * pure read-only report (no events, no task.md change). In `write: true` mode,\n * if any broken reference is found, mint an ad-hoc reconcile session, fire\n * `task_reconciled`, and overwrite task.md with the repaired refs.\n *\n * The broken `created_in_session` field is REPLACED with the new reconcile\n * session id rather than nulled out — `TaskSchema.created_in_session` is\n * non-nullable, so dropping it would leave the file schema-invalid.\n * The old broken id is preserved on the event payload via\n * `removed_created_in_session` for audit.\n *\n * Stages — failures after stage 5 surface a phase-specific\n * {@link TaskWriteAfterEventError} so the CLI can render a tailored \"do not\n * rerun\" hint:\n * 1. Boundary parse\n * 2. Read task.md AND snapshot its mtime/hash from the same raw bytes,\n * then detect broken refs (sharing the raw bytes here closes the\n * readTaskFile-then-snapshot race window).\n * 3. Early return when clean (no event fired, no overwrite)\n * 4. (no separate stage anymore — snapshot is taken at stage 2)\n * 5. Mint ad-hoc session + `task_reconciled` event (catch\n * `FailedToFinalizeError` → `phase: \"reconcile-finalize\"`)\n * 6. Re-snapshot task.md; if changed since stage 2 →\n * `phase: \"reconcile-concurrent\"`\n * 7. Overwrite task.md; failure → `phase: \"reconcile\"`\n */\nexport async function reconcileTask(\n paths: BasouPaths,\n manifest: Manifest,\n input: ReconcileTaskInput,\n): Promise<ReconcileResult> {\n TaskIdSchema.parse(input.taskId);\n\n // Per-task lock spans the entire reconcile window (= snapshot read,\n // ad-hoc session + event write, post-write snapshot probe, task.md\n // overwrite) so the mtime/hash invariant cannot be defeated by a\n // concurrent writer the helper would otherwise not notice.\n const handle = await acquireLock(paths, \"task\", input.taskId);\n try {\n return await reconcileTaskLocked(paths, manifest, input);\n } finally {\n await handle.release();\n }\n}\n\nasync function reconcileTaskLocked(\n paths: BasouPaths,\n manifest: Manifest,\n input: ReconcileTaskInput,\n): Promise<ReconcileResult> {\n const { doc: currentDoc, snapshot: preSnapshot } = await readTaskFileWithSnapshot(\n paths,\n input.taskId,\n );\n const { brokenCreatedInSession, brokenLinkedSessions } = await detectBrokenRefs(\n paths,\n currentDoc.task.task,\n );\n\n if (brokenCreatedInSession === null && brokenLinkedSessions.length === 0) {\n return {\n taskId: input.taskId,\n clean: true,\n brokenCreatedInSession: null,\n brokenLinkedSessions: [],\n reconcileSession: null,\n };\n }\n\n if (!input.write) {\n return {\n taskId: input.taskId,\n clean: false,\n brokenCreatedInSession,\n brokenLinkedSessions,\n reconcileSession: null,\n };\n }\n\n if (input._onPhaseCompleted !== undefined) {\n await input._onPhaseCompleted(\"phase-4-snapshot\");\n }\n\n let adHoc: Awaited<ReturnType<typeof createAdHocSessionWithEvent>>;\n try {\n adHoc = await createAdHocSessionWithEvent({\n paths,\n manifest,\n label: buildAdHocReconcileLabel(currentDoc.task.task.title),\n occurredAt: input.occurredAt,\n sessionSource: \"human\",\n workingDirectory: input.workingDirectory,\n invocation: {\n command: \"basou task reconcile\",\n args:\n (input.scope ?? \"single\") === \"single\"\n ? [\"--task\", input.taskId, \"--write\"]\n : [\"--write\"],\n },\n taskId: input.taskId,\n targetEventBuilders: [\n (sessionId, eventId) =>\n buildTaskReconciledEvent({\n eventId,\n sessionId,\n taskId: input.taskId,\n removedCreatedInSession: brokenCreatedInSession,\n createdInSessionReplacement: brokenCreatedInSession !== null ? sessionId : null,\n removedLinkedSessions: brokenLinkedSessions,\n occurredAt: input.occurredAt,\n }),\n ],\n });\n } catch (error: unknown) {\n if (error instanceof FailedToFinalizeError) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: error.targetEventIds[0] as PrefixedId<\"evt\">,\n sessionId: error.sessionId,\n phase: \"reconcile-finalize\",\n cause: error,\n });\n }\n throw error;\n }\n\n if (input._onPhaseCompleted !== undefined) {\n await input._onPhaseCompleted(\"phase-5-bulk-write\");\n }\n\n const anchorEventId = adHoc.targetEventIds[0] as PrefixedId<\"evt\">;\n\n const postSnapshot = await computeTaskMdSnapshot(paths, input.taskId);\n if (postSnapshot.mtimeMs !== preSnapshot.mtimeMs || postSnapshot.hash !== preSnapshot.hash) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: anchorEventId,\n sessionId: adHoc.sessionId,\n phase: \"reconcile-concurrent\",\n cause: new Error(\"task.md changed during reconcile\"),\n });\n }\n\n const repaired = buildReconciledDoc({\n currentDoc,\n brokenCreatedInSession,\n brokenLinkedSessions,\n reconcileSessionId: adHoc.sessionId,\n occurredAt: input.occurredAt,\n });\n try {\n await writeTaskFile(paths, input.taskId, repaired, { mode: \"overwrite\" });\n } catch (error: unknown) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: anchorEventId,\n sessionId: adHoc.sessionId,\n phase: \"reconcile\",\n cause: error,\n });\n }\n\n await safeUpdateTaskIndex(paths, {\n kind: \"update\",\n entry: buildTaskIndexEntry(repaired.task.task),\n });\n\n return {\n taskId: input.taskId,\n clean: false,\n brokenCreatedInSession,\n brokenLinkedSessions,\n reconcileSession: {\n sessionId: adHoc.sessionId,\n eventId: anchorEventId,\n },\n };\n}\n\n/**\n * Reconcile every task in `.basou/tasks/`. Continues on per-task failures so\n * an isolated {@link TaskWriteAfterEventError} does not stop the batch.\n * Malformed task.md files are skipped silently and excluded from `scanned`.\n */\nexport async function reconcileAllTasks(\n paths: BasouPaths,\n manifest: Manifest,\n input: ReconcileAllTasksInput,\n options: ReconcileAllTasksOptions = {},\n): Promise<ReconcileAllResult> {\n const taskIds = await enumerateTaskIds(paths);\n const results: ReconcileResult[] = [];\n const failed: ReconcileFailure[] = [];\n let scanned = 0;\n\n for (const id of taskIds) {\n // Probe readability first so malformed task.md does NOT inflate `scanned`\n // and never reaches the reconcile flow. The readTaskFile call is replayed\n // inside reconcileTask itself — re-reading is cheap and keeps reconcileTask's\n // contract single-purpose.\n try {\n await readTaskFile(paths, id);\n } catch {\n continue;\n }\n scanned += 1;\n\n try {\n const r = await reconcileTask(paths, manifest, {\n taskId: id as PrefixedId<\"task\">,\n occurredAt: input.occurredAt(),\n workingDirectory: input.workingDirectory,\n write: input.write,\n scope: \"all\",\n });\n if (options.includeClean === true || !r.clean) {\n results.push(r);\n }\n } catch (error: unknown) {\n const errorClass = error instanceof Error ? error.constructor.name : \"Error\";\n const phase = error instanceof TaskWriteAfterEventError ? error.phase : null;\n failed.push({\n taskId: id as PrefixedId<\"task\">,\n errorClass,\n phase,\n });\n }\n }\n\n return { results, failed, scanned };\n}\n\n// ============================================================================\n// Linkage refresh: events.jsonl → task.md `linked_sessions[]` forward sync\n// ============================================================================\n\n/**\n * Single-task linkage refresh result. In `write: false` mode this is a pure\n * dry-run report (no event, no task.md change); `addedLinkedSessions` and\n * `removedLinkedSessions` describe what would change. In `write: true` mode\n * the same fields describe what did change and `refreshSession` carries the\n * ad-hoc session + `task_linkage_refreshed` event ids that were minted.\n *\n * `clean === true` means the existing `task.md.linked_sessions[]` already\n * matches the union of `session.yaml.task_id` matches plus the anchor\n * (`created_in_session`) — no event fired, no overwrite.\n */\nexport type RefreshLinkageResult = {\n taskId: PrefixedId<\"task\">;\n clean: boolean;\n addedLinkedSessions: PrefixedId<\"ses\">[];\n removedLinkedSessions: PrefixedId<\"ses\">[];\n /** Number of entries in `linked_sessions[]` after the refresh would run. */\n finalCount: number;\n refreshSession: {\n sessionId: PrefixedId<\"ses\">;\n eventId: PrefixedId<\"evt\">;\n } | null;\n};\n\nexport type RefreshLinkageInput = {\n taskId: PrefixedId<\"task\">;\n occurredAt: string;\n workingDirectory: string;\n write: boolean;\n};\n\ntype DetectedLinkageDelta = {\n addedLinkedSessions: PrefixedId<\"ses\">[];\n removedLinkedSessions: PrefixedId<\"ses\">[];\n finalLinkedSessions: PrefixedId<\"ses\">[];\n};\n\n// Re-derive `linked_sessions[]` from the source of truth: every\n// `session.yaml` whose `task_id` points at this task, plus the\n// `created_in_session` anchor (which is preserved even if its session.yaml\n// no longer carries the task_id — that flavour of drift is the\n// `task reconcile` path's concern, not this one).\n//\n// `enumerateSessionDirs` already filters to dir-named-`ses_<ulid>` entries.\n// Sessions whose `session.yaml` is missing or schema-invalid are silently\n// skipped so a single broken session does not abort the workspace-wide\n// refresh; surfacing those is the responsibility of the session-integrity\n// tooling.\nasync function detectLinkageDelta(\n paths: BasouPaths,\n task: Task[\"task\"],\n): Promise<DetectedLinkageDelta> {\n const sessionIds = await enumerateSessionDirs(paths);\n const reachable = new Set<string>();\n for (const sid of sessionIds) {\n try {\n const doc = await readSessionYaml(paths, sid);\n if (doc.session.task_id === task.id) {\n reachable.add(sid);\n }\n } catch {\n // Missing / malformed session.yaml — skip. Surfacing those is the\n // responsibility of session-integrity tooling, not the linkage-refresh\n // path; a single corrupt session.yaml must not abort the workspace\n // scan.\n }\n }\n // The session ⇆ task anchor invariant\n // (`docs/spec/workspace.md#21-confirmed-invariants`) requires\n // `linked_sessions[]` to always contain `created_in_session`. Preserve it\n // here even if the session.yaml was hand-edited to clear task_id\n // (rare; handled by reconcile).\n const finalSet = new Set<string>(reachable);\n finalSet.add(task.created_in_session);\n\n const currentSet = new Set<string>(task.linked_sessions);\n const addedLinkedSessions: PrefixedId<\"ses\">[] = [];\n const removedLinkedSessions: PrefixedId<\"ses\">[] = [];\n for (const sid of finalSet) {\n if (!currentSet.has(sid)) addedLinkedSessions.push(sid as PrefixedId<\"ses\">);\n }\n for (const sid of currentSet) {\n if (!finalSet.has(sid)) removedLinkedSessions.push(sid as PrefixedId<\"ses\">);\n }\n // Stable ordering: ULID-ascending so two runs against the same workspace\n // produce identical event payloads (matters for replay determinism).\n addedLinkedSessions.sort();\n removedLinkedSessions.sort();\n const finalLinkedSessions = [...finalSet].sort() as PrefixedId<\"ses\">[];\n return { addedLinkedSessions, removedLinkedSessions, finalLinkedSessions };\n}\n\nfunction buildRefreshedDoc(input: {\n currentDoc: TaskDocument;\n finalLinkedSessions: ReadonlyArray<PrefixedId<\"ses\">>;\n refreshSessionId: PrefixedId<\"ses\">;\n occurredAt: string;\n}): TaskDocument {\n // Include the refresh session itself in `linked_sessions` (it is the\n // session that wrote the `task_linkage_refreshed` event, so it is by\n // definition linked). Deduplicate via a Set in case the ad-hoc session id\n // somehow already shows up in finalLinkedSessions (defensive).\n const merged = new Set<string>(input.finalLinkedSessions);\n merged.add(input.refreshSessionId);\n const linked = [...merged].sort() as PrefixedId<\"ses\">[];\n const next: Task = {\n ...input.currentDoc.task,\n task: {\n ...input.currentDoc.task.task,\n updated_at: input.occurredAt,\n linked_sessions: linked,\n },\n };\n return { task: next, body: input.currentDoc.body };\n}\n\n/**\n * Refresh `task.md.linked_sessions[]` so it matches the union of\n * `session.yaml.task_id` references in the workspace plus the\n * `created_in_session` anchor. In `write: false` this is a pure read-only\n * report; in `write: true` the diff is recorded as a\n * `task_linkage_refreshed` event inside a fresh ad-hoc session and the\n * task.md is overwritten with the new snapshot.\n *\n * Stages mirror `reconcileTask` so the operator gets the same\n * \"do-not-rerun\" hint shape on partial failure:\n * 1. Boundary parse\n * 2. Read task.md AND snapshot its mtime/hash from the same raw bytes\n * 3. Detect linkage delta (= scan workspace session.yaml)\n * 4. Early return when clean\n * 5. Mint ad-hoc session + `task_linkage_refreshed` event (catch\n * `FailedToFinalizeError` → `phase: \"linkage-refresh-finalize\"`)\n * 6. Re-snapshot task.md; if changed since stage 2 →\n * `phase: \"linkage-refresh-concurrent\"`\n * 7. Overwrite task.md; failure → `phase: \"linkage-refresh\"`\n *\n * The refresh event is distinct from `task_reconciled` (= broken-ref\n * cleanup, `.strict()` with broken-ref-specific fields) so each event\n * carries a single, focused audit story. Reusing `task_reconciled` here\n * would either redefine its semantics or require widening its strict\n * schema, both of which break replay determinism for older events.\n */\nexport async function refreshTaskLinkedSessions(\n paths: BasouPaths,\n manifest: Manifest,\n input: RefreshLinkageInput,\n): Promise<RefreshLinkageResult> {\n TaskIdSchema.parse(input.taskId);\n\n // Per-task lock spans the entire refresh window so the snapshot taken in\n // stage 2 cannot be invalidated by another writer the helper does not see;\n // the stage 6 mtime/hash probe still acts as a belt-and-braces guard\n // against drift from non-locked code paths (e.g. an external `vi` edit).\n const handle = await acquireLock(paths, \"task\", input.taskId);\n try {\n return await refreshTaskLinkedSessionsLocked(paths, manifest, input);\n } finally {\n await handle.release();\n }\n}\n\nasync function refreshTaskLinkedSessionsLocked(\n paths: BasouPaths,\n manifest: Manifest,\n input: RefreshLinkageInput,\n): Promise<RefreshLinkageResult> {\n const { doc: currentDoc, snapshot: preSnapshot } = await readTaskFileWithSnapshot(\n paths,\n input.taskId,\n );\n const { addedLinkedSessions, removedLinkedSessions, finalLinkedSessions } =\n await detectLinkageDelta(paths, currentDoc.task.task);\n\n if (addedLinkedSessions.length === 0 && removedLinkedSessions.length === 0) {\n return {\n taskId: input.taskId,\n clean: true,\n addedLinkedSessions: [],\n removedLinkedSessions: [],\n finalCount: finalLinkedSessions.length,\n refreshSession: null,\n };\n }\n\n if (!input.write) {\n return {\n taskId: input.taskId,\n clean: false,\n addedLinkedSessions,\n removedLinkedSessions,\n finalCount: finalLinkedSessions.length,\n refreshSession: null,\n };\n }\n\n // The refresh session is itself a new linked entry; account for it on\n // the event payload's `final_count` so the audit number matches the\n // post-write task.md. This is a +1 over the workspace-scan count.\n const finalCountWithRefreshSession = finalLinkedSessions.length + 1;\n\n let adHoc: Awaited<ReturnType<typeof createAdHocSessionWithEvent>>;\n try {\n adHoc = await createAdHocSessionWithEvent({\n paths,\n manifest,\n label: buildAdHocRefreshLinkageLabel(currentDoc.task.task.title),\n occurredAt: input.occurredAt,\n sessionSource: \"human\",\n workingDirectory: input.workingDirectory,\n invocation: {\n command: \"basou task refresh-linkage\",\n args: [input.taskId, \"--write\"],\n },\n taskId: input.taskId,\n targetEventBuilders: [\n (sessionId, eventId) =>\n buildTaskLinkageRefreshedEvent({\n eventId,\n sessionId,\n taskId: input.taskId,\n addedLinkedSessions,\n removedLinkedSessions,\n finalCount: finalCountWithRefreshSession,\n occurredAt: input.occurredAt,\n }),\n ],\n });\n } catch (error: unknown) {\n if (error instanceof FailedToFinalizeError) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: error.targetEventIds[0] as PrefixedId<\"evt\">,\n sessionId: error.sessionId,\n phase: \"linkage-refresh-finalize\",\n cause: error,\n });\n }\n throw error;\n }\n\n const anchorEventId = adHoc.targetEventIds[0] as PrefixedId<\"evt\">;\n\n const postSnapshot = await computeTaskMdSnapshot(paths, input.taskId);\n if (postSnapshot.mtimeMs !== preSnapshot.mtimeMs || postSnapshot.hash !== preSnapshot.hash) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: anchorEventId,\n sessionId: adHoc.sessionId,\n phase: \"linkage-refresh-concurrent\",\n cause: new Error(\"task.md changed during linkage refresh\"),\n });\n }\n\n const refreshed = buildRefreshedDoc({\n currentDoc,\n finalLinkedSessions,\n refreshSessionId: adHoc.sessionId,\n occurredAt: input.occurredAt,\n });\n try {\n await writeTaskFile(paths, input.taskId, refreshed, { mode: \"overwrite\" });\n } catch (error: unknown) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId: anchorEventId,\n sessionId: adHoc.sessionId,\n phase: \"linkage-refresh\",\n cause: error,\n });\n }\n\n await safeUpdateTaskIndex(paths, {\n kind: \"update\",\n entry: buildTaskIndexEntry(refreshed.task.task),\n });\n\n return {\n taskId: input.taskId,\n clean: false,\n addedLinkedSessions,\n removedLinkedSessions,\n finalCount: finalCountWithRefreshSession,\n refreshSession: {\n sessionId: adHoc.sessionId,\n eventId: anchorEventId,\n },\n };\n}\n\n// ============================================================================\n// editTask — field-level update (no event for pure title edits)\n// ============================================================================\n\nexport type EditTaskInput = {\n paths: BasouPaths;\n taskId: PrefixedId<\"task\">;\n /** New title; rejected when empty. Undefined leaves the field unchanged. */\n title?: string;\n /**\n * New status; routed through transition rules so the call rejects\n * invalid edges (e.g. `done -> planned`). Undefined leaves the field\n * unchanged.\n */\n newStatus?: TaskStatus;\n occurredAt: string;\n /**\n * Required when {@link newStatus} is provided — the status change fires\n * a `task_status_changed` event in a fresh ad-hoc session, which needs\n * a Manifest to seed the new session record. Title-only edits ignore\n * this field.\n */\n manifest?: Manifest;\n /** Working directory for the ad-hoc status-change session. */\n workingDirectory?: string;\n};\n\nexport type EditTaskResult = {\n taskId: PrefixedId<\"task\">;\n titleUpdated: boolean;\n statusUpdated: boolean;\n /** When {@link statusUpdated} is true, the previous status before the edit. */\n previousStatus: TaskStatus | null;\n /** When {@link statusUpdated} is true, the new status. */\n newStatus: TaskStatus | null;\n /** ad-hoc session minted when status was changed; null for title-only edits. */\n statusChangeSession: {\n sessionId: PrefixedId<\"ses\">;\n eventId: PrefixedId<\"evt\">;\n } | null;\n};\n\n/**\n * Update one or both of the user-editable fields on a task.md.\n *\n * - `title`: in-place overwrite of `task.md` only. v0.1 does not emit a\n * `task_title_changed` event — title changes are storage-level metadata\n * maintenance, not part of the audit trail.\n * - `newStatus`: routed through {@link updateTaskStatusWithEvent} so the\n * ALLOWED_TRANSITIONS gate is honored and a `task_status_changed` event is\n * appended to the audit trail.\n *\n * When both are supplied the status change runs first (= event committed)\n * and then the title overwrite runs against the freshly updated task.md\n * (= same `updated_at` from the status change). A failure of the\n * subsequent title overwrite leaves the status change committed; the\n * status-change side of an edit is the only side with an event, so the\n * audit trail is consistent regardless.\n */\nexport async function editTask(input: EditTaskInput): Promise<EditTaskResult> {\n TaskIdSchema.parse(input.taskId);\n if (input.title === undefined && input.newStatus === undefined) {\n throw new Error(\"Nothing to edit: provide --title or --status\");\n }\n if (input.title !== undefined) {\n TaskTitleSchema.parse(input.title);\n }\n\n let statusUpdated = false;\n let previousStatus: TaskStatus | null = null;\n let newStatus: TaskStatus | null = null;\n let statusChangeSession: EditTaskResult[\"statusChangeSession\"] = null;\n\n // Stage 1: status change (if any). Failure here exits with the existing\n // transition / not-found errors and leaves task.md untouched.\n if (input.newStatus !== undefined) {\n if (input.manifest === undefined || input.workingDirectory === undefined) {\n throw new Error(\"editTask requires manifest + workingDirectory when newStatus is supplied\");\n }\n const result = await updateTaskStatusWithEvent({\n mode: \"ad-hoc\",\n paths: input.paths,\n manifest: input.manifest,\n occurredAt: input.occurredAt,\n taskId: input.taskId,\n newStatus: input.newStatus,\n workingDirectory: input.workingDirectory,\n });\n statusUpdated = true;\n previousStatus = result.previousStatus;\n newStatus = result.newStatus;\n statusChangeSession = { sessionId: result.sessionId, eventId: result.eventId };\n }\n\n // Stage 2: title overwrite (if any). Re-read so the status change above\n // (which updated linked_sessions / updated_at) is preserved. The lock is\n // taken HERE (not around the whole function) because stage 1 calls\n // `updateTaskStatusWithEvent` which acquires the same per-task lock\n // internally — wrapping both stages in one outer lock would deadlock on\n // its own helper. Each stage being independently atomic is the operator-\n // visible invariant we care about, not stage-1-and-2 atomicity.\n let titleUpdated = false;\n if (input.title !== undefined) {\n const handle = await acquireLock(input.paths, \"task\", input.taskId);\n try {\n const doc = await readTaskFile(input.paths, input.taskId);\n if (doc.task.task.title !== input.title) {\n const next: Task = {\n ...doc.task,\n task: {\n ...doc.task.task,\n title: input.title,\n updated_at: input.occurredAt,\n },\n };\n await writeTaskFile(\n input.paths,\n input.taskId,\n { task: next, body: doc.body },\n { mode: \"overwrite\" },\n );\n await safeUpdateTaskIndex(input.paths, {\n kind: \"update\",\n entry: buildTaskIndexEntry(next.task),\n });\n titleUpdated = true;\n }\n } finally {\n await handle.release();\n }\n }\n\n return {\n taskId: input.taskId,\n titleUpdated,\n statusUpdated,\n previousStatus,\n newStatus,\n statusChangeSession,\n };\n}\n\n// ============================================================================\n// deleteTask — destructive removal with audit event\n// ============================================================================\n\nexport type DeleteTaskInput = {\n paths: BasouPaths;\n manifest: Manifest;\n taskId: PrefixedId<\"task\">;\n occurredAt: string;\n workingDirectory: string;\n};\n\nexport type DeleteTaskResult = {\n taskId: PrefixedId<\"task\">;\n title: string;\n sessionId: PrefixedId<\"ses\">;\n eventId: PrefixedId<\"evt\">;\n};\n\n/**\n * Hard-delete a task.md file with a `task_deleted` audit event.\n *\n * Sequence:\n * 1. Read task.md to capture the current title (which goes onto the\n * event payload so the audit record is self-describing even after\n * the file is gone).\n * 2. Mint an ad-hoc session, fire `task_deleted` as the target event.\n * The session's `task_id` is intentionally NOT pinned to the\n * to-be-deleted task — otherwise the audit session would carry a\n * broken reference the moment we unlink the file.\n * 3. Unlink `<paths.tasks>/<task_id>.md`.\n *\n * Failure of step 3 after the event is committed surfaces as a\n * {@link TaskWriteAfterEventError} with `phase: \"delete\"`; the operator\n * is told the event is durable but task.md still exists, and that a\n * manual `rm` (or a rerun) is required.\n *\n * v0.1 contract: no tombstone, no recovery. Restoring a deleted task is\n * not supported; the event payload (`task_id` + `title`) is the only\n * persistent record after the unlink succeeds.\n */\nexport async function deleteTask(input: DeleteTaskInput): Promise<DeleteTaskResult> {\n TaskIdSchema.parse(input.taskId);\n\n // Per-task lock keeps the read → audit event → unlink chain free of a\n // concurrent writer that could otherwise observe task.md after we read it\n // and before we delete it (e.g. another CLI running `task status`).\n const handle = await acquireLock(input.paths, \"task\", input.taskId);\n try {\n return await deleteTaskLocked(input);\n } finally {\n await handle.release();\n }\n}\n\nasync function deleteTaskLocked(input: DeleteTaskInput): Promise<DeleteTaskResult> {\n // Stage 1: capture the current title before mint.\n const doc = await readTaskFile(input.paths, input.taskId);\n const title = doc.task.task.title;\n\n // Stage 2: fire the audit event. NOTE we do NOT pass `taskId` to the\n // ad-hoc session — pinning the session to a task that is about to vanish\n // would create a guaranteed broken reference on session.yaml.task_id.\n const adHoc = await createAdHocSessionWithEvent({\n paths: input.paths,\n manifest: input.manifest,\n label: buildAdHocDeleteLabel(title),\n occurredAt: input.occurredAt,\n sessionSource: \"human\",\n workingDirectory: input.workingDirectory,\n invocation: {\n command: \"basou task delete\",\n args: [input.taskId, \"--yes\"],\n },\n targetEventBuilders: [\n (sessionId, eventId) =>\n buildTaskDeletedEvent({\n eventId,\n sessionId,\n taskId: input.taskId,\n title,\n occurredAt: input.occurredAt,\n }),\n ],\n });\n const eventId = adHoc.targetEventIds[0] as PrefixedId<\"evt\">;\n\n // Stage 3: unlink the file.\n try {\n await unlink(join(input.paths.tasks, `${input.taskId}.md`));\n } catch (error: unknown) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId,\n sessionId: adHoc.sessionId,\n phase: \"delete\",\n cause: error,\n });\n }\n\n await safeUpdateTaskIndex(input.paths, { kind: \"remove\", id: input.taskId });\n\n return {\n taskId: input.taskId,\n title,\n sessionId: adHoc.sessionId,\n eventId,\n };\n}\n\n// ============================================================================\n// archiveTask — move main/<id>.md to archive/<id>.md with audit event\n// ============================================================================\n\nexport type ArchiveTaskInput = {\n paths: BasouPaths;\n manifest: Manifest;\n taskId: PrefixedId<\"task\">;\n occurredAt: string;\n workingDirectory: string;\n};\n\nexport type ArchiveTaskResult = {\n taskId: PrefixedId<\"task\">;\n title: string;\n sessionId: PrefixedId<\"ses\">;\n eventId: PrefixedId<\"evt\">;\n};\n\n/**\n * Move a task.md file from `<paths.tasks>/<id>.md` to\n * `<paths.tasks>/archive/<id>.md` with a `task_archived` audit event.\n *\n * Sequence:\n * 1. Read task.md to capture the current title and existing content.\n * 2. Mint an ad-hoc session, fire `task_archived` as the target event.\n * The session's `task_id` IS pinned to the archived task — unlike\n * `task_deleted`, the task continues to exist (just at a new path),\n * so the session-task linkage stays a valid forward reference.\n * 3. Append the audit session to the task's `linked_sessions[]` and\n * overwrite the source task.md so the snapshot reflects the archive\n * session before the move.\n * 4. Ensure the archive directory exists.\n * 5. Rename main/<id>.md to archive/<id>.md (= atomic on the same fs).\n *\n * Failure modes after step 2 surface as\n * {@link TaskWriteAfterEventError} with `phase: \"archive\"`; the operator\n * is told the event is durable but the on-disk move is incomplete and\n * must be resolved manually (typically by rerunning `task archive`).\n */\nexport async function archiveTask(input: ArchiveTaskInput): Promise<ArchiveTaskResult> {\n TaskIdSchema.parse(input.taskId);\n\n // Per-task lock spans read → audit event → task.md overwrite → rename so\n // a concurrent writer cannot interleave between the linked_sessions\n // append and the move into archive/.\n const handle = await acquireLock(input.paths, \"task\", input.taskId);\n try {\n return await archiveTaskLocked(input);\n } finally {\n await handle.release();\n }\n}\n\nasync function archiveTaskLocked(input: ArchiveTaskInput): Promise<ArchiveTaskResult> {\n const doc = await readTaskFile(input.paths, input.taskId);\n const title = doc.task.task.title;\n\n const adHoc = await createAdHocSessionWithEvent({\n paths: input.paths,\n manifest: input.manifest,\n label: buildAdHocArchiveLabel(title),\n occurredAt: input.occurredAt,\n sessionSource: \"human\",\n workingDirectory: input.workingDirectory,\n invocation: {\n command: \"basou task archive\",\n args: [input.taskId, \"--yes\"],\n },\n taskId: input.taskId,\n targetEventBuilders: [\n (sessionId, eventId) =>\n buildTaskArchivedEvent({\n eventId,\n sessionId,\n taskId: input.taskId,\n title,\n occurredAt: input.occurredAt,\n }),\n ],\n });\n const eventId = adHoc.targetEventIds[0] as PrefixedId<\"evt\">;\n\n // Stage 3-5 share the same recovery contract: any failure surfaces as\n // phase \"archive\" so the operator gets a uniform \"rerun task archive\"\n // hint. Specific failure cases:\n // - 3: writeTaskFile (overwrite) — fs/yaml-serialize error\n // - 4: mkdir of archive dir — usually EACCES\n // - 5: rename across the same fs — EEXIST when archive/<id>.md is\n // already there, EACCES, or rare ENOSPC\n try {\n const linked = doc.task.task.linked_sessions;\n const merged = linked.includes(adHoc.sessionId) ? linked : [...linked, adHoc.sessionId];\n const next: Task = {\n ...doc.task,\n task: {\n ...doc.task.task,\n updated_at: input.occurredAt,\n linked_sessions: merged,\n },\n };\n await writeTaskFile(\n input.paths,\n input.taskId,\n { task: next, body: doc.body },\n { mode: \"overwrite\" },\n );\n\n await mkdir(archiveTasksDir(input.paths), { recursive: true });\n await rename(\n join(input.paths.tasks, `${input.taskId}.md`),\n join(archiveTasksDir(input.paths), `${input.taskId}.md`),\n );\n } catch (error: unknown) {\n throw new TaskWriteAfterEventError({\n taskId: input.taskId,\n eventId,\n sessionId: adHoc.sessionId,\n phase: \"archive\",\n cause: error,\n });\n }\n\n // Archived tasks live under tasks/archive/<id>.md, which enumerateTaskIds\n // ignores. Remove the entry from the active index so `task list` matches\n // disk reality.\n await safeUpdateTaskIndex(input.paths, { kind: \"remove\", id: input.taskId });\n\n return {\n taskId: input.taskId,\n title,\n sessionId: adHoc.sessionId,\n eventId,\n };\n}\n","import { z } from \"zod\";\nimport {\n IsoTimestampSchema,\n SchemaVersionSchema,\n SessionIdSchema,\n TaskIdSchema,\n WorkspaceIdSchema,\n} from \"./shared.schema.js\";\n\n/**\n * Task lifecycle states.\n *\n * The storage layer's `ALLOWED_TRANSITIONS` map (= source of truth in\n * `tasks.ts`) is the authoritative graph; the comment below is a snapshot.\n * `planned` reaches `done` / `cancelled` directly so tasks completed (or\n * abandoned) outside an explicit in-progress phase can close in a single\n * CLI call:\n *\n * planned → {in_progress | done | cancelled}\n * in_progress → {done | cancelled}\n * done / cancelled = terminal\n *\n * Self-edges are rejected so the audit trail stays monotonic.\n */\nexport const TaskStatusSchema = z.enum([\"planned\", \"in_progress\", \"done\", \"cancelled\"]);\n/** Inferred runtime type for {@link TaskStatusSchema}. */\nexport type TaskStatus = z.infer<typeof TaskStatusSchema>;\n\nconst TaskInnerSchema = z.object({\n id: TaskIdSchema,\n title: z.string().min(1),\n label: z.string().min(1).optional(),\n status: TaskStatusSchema,\n created_at: IsoTimestampSchema,\n updated_at: IsoTimestampSchema,\n workspace_id: WorkspaceIdSchema,\n /**\n * Session id that anchors this task. For freshly created tasks it is the\n * session that wrote the `task_created` event (= ad-hoc reconcile target\n * for ad-hoc paths, or the target session id for attach paths). After\n * `basou task reconcile --write` repairs a broken anchor the\n * value is replaced with the ad-hoc reconcile session id; the old broken\n * session_id is preserved on the `task_reconciled` event payload via\n * `removed_created_in_session` for audit. So this field always names a\n * reachable session, even after the original anchor is gone.\n */\n created_in_session: SessionIdSchema,\n /**\n * Snapshot of sessions linked to this task. The events.jsonl history is\n * the source of truth (see\n * `docs/spec/generated-markdown.md#105-decisionsmd-generation-principle`);\n * this field is maintained as a UX-only cache so editors can read the\n * task.md and immediately see related sessions. Defaults to `[]` for\n * backward compatibility.\n */\n linked_sessions: z.array(SessionIdSchema).default([]),\n});\n\n/**\n * Schema for the YAML front matter of `.basou/tasks/<task_id>.md`.\n *\n * The markdown body after the front matter is intentionally NOT modelled\n * here — it is free-form user-edited content. The storage layer splits\n * the file into `task` (this schema) and `body` (the trailing string).\n */\nexport const TaskSchema = z.object({\n schema_version: SchemaVersionSchema,\n task: TaskInnerSchema,\n});\n/** Inferred runtime type for {@link TaskSchema}. */\nexport type Task = z.infer<typeof TaskSchema>;\n","import { mkdir, rm } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { appendEvent, writeEventsBulk } from \"../events/event-writer.js\";\nimport { type PrefixedId, prefixedUlid } from \"../ids/ulid.js\";\nimport { findErrorCode } from \"../lib/error-codes.js\";\nimport { sanitizeWorkingDirectory } from \"../lib/path-sanitizer.js\";\nimport type { Event } from \"../schemas/event.schema.js\";\nimport type { Manifest } from \"../schemas/manifest.schema.js\";\nimport {\n type Session,\n SessionSchema,\n type SessionSourceKind,\n SessionSourceKindSchema,\n type SessionStatus,\n} from \"../schemas/session.schema.js\";\nimport { SessionIdSchema } from \"../schemas/shared.schema.js\";\nimport type { BasouPaths } from \"./basou-dir.js\";\nimport { readSessionYaml } from \"./sessions.js\";\nimport { linkYamlFile, overwriteYamlFile } from \"./yaml-store.js\";\n\n// ============================================================================\n// Finalization-failure error\n// ============================================================================\n\n/**\n * Thrown when the ad-hoc session was fully written to disk (4 lifecycle\n * events + N target events plus the initial `session.yaml`) but the final\n * `session.yaml` update to status `completed` failed. The caller can read\n * `sessionId` / `targetEventIds` to emit a retry-duplicate-prevention\n * warning, since the target events themselves are already persisted in\n * `events.jsonl`.\n *\n * `targetEventIds` is an array because a single ad-hoc session may carry\n * multiple target events (e.g. `task new --status done` fires both\n * `task_created` and `task_status_changed`). Callers that need a single\n * anchor id should use `targetEventIds[0]`, which by convention is the\n * primary event for the operation.\n */\nexport class FailedToFinalizeError extends Error {\n readonly sessionId: PrefixedId<\"ses\">;\n readonly targetEventIds: ReadonlyArray<PrefixedId<\"evt\">>;\n\n constructor(\n sessionId: PrefixedId<\"ses\">,\n targetEventIds: ReadonlyArray<PrefixedId<\"evt\">>,\n cause: unknown,\n ) {\n super(\"Failed to finalize ad-hoc session\", { cause });\n this.name = \"FailedToFinalizeError\";\n if (targetEventIds.length === 0) {\n // Defensive guard for direct (non-orchestrator) constructors. The\n // orchestrator already rejects an empty `targetEventBuilders` array\n // before any ID minting, but `FailedToFinalizeError` is a public\n // exported class and `error-render.ts` reads `targetEventIds[0]` as\n // the operator-facing anchor — an empty array there would surface as\n // `\"Recorded undefined ...\"`.\n throw new Error(\"FailedToFinalizeError requires at least one target event id\");\n }\n this.sessionId = sessionId;\n this.targetEventIds = targetEventIds;\n }\n}\n\n// ============================================================================\n// Ad-hoc session path\n// ============================================================================\n\nexport type CreateAdHocSessionInput = {\n paths: BasouPaths;\n manifest: Manifest;\n /** Pre-built session label (caller is responsible for truncation). */\n label: string;\n /** ISO timestamp shared across the 5 lifecycle/target events. */\n occurredAt: string;\n sessionSource: SessionSourceKind;\n workingDirectory: string;\n invocation: { command: string; args: string[] };\n /**\n * Optional task id to link this ad-hoc session to. When provided, both the\n * initial and the final `session.yaml` writes embed `task_id` so the\n * single-session-to-single-task invariant (see\n * `docs/spec/workspace.md#21-confirmed-invariants`) holds for task-flavoured\n * ad-hoc paths (`basou task new` / `task status` without `--session`).\n * Defaults to `null` so existing callers (decision / note) are unchanged.\n */\n taskId?: PrefixedId<\"task\">;\n /**\n * Builds the variant-specific target events. Each builder receives the\n * freshly minted session id and a freshly minted event id (one per\n * builder) so callers can fill in cross-reference fields (`decision_id`,\n * `body`, ...) without owning ID generation.\n *\n * The most common case is a single-element array (`[builder]`) for the\n * one-target-event flows (`basou decision record`, `basou session note`,\n * `basou task new --status planned`, `basou task status`,\n * `basou task reconcile`). Two-element arrays are used by\n * `basou task new --status done|cancelled` to emit `task_created` plus\n * an immediate `task_status_changed` in the same atomic bulk write.\n *\n * Must be non-empty; an empty array is rejected at the start of\n * {@link createAdHocSessionWithEvent}.\n */\n targetEventBuilders: ReadonlyArray<\n (sessionId: PrefixedId<\"ses\">, eventId: PrefixedId<\"evt\">) => Event\n >;\n};\n\nexport type CreateAdHocSessionResult = {\n sessionId: PrefixedId<\"ses\">;\n /**\n * Target event IDs in the order their builders were supplied. Length\n * equals `input.targetEventBuilders.length`. Callers that conceptually\n * have a single anchor event should use `targetEventIds[0]`.\n */\n targetEventIds: PrefixedId<\"evt\">[];\n /**\n * Lifecycle event IDs in chronological order:\n * `[started, status→running, status→completed, ended]`.\n * Target event IDs are reported separately in {@link targetEventIds}.\n */\n lifecycleEventIds: PrefixedId<\"evt\">[];\n};\n\n/**\n * Atomically create a fresh ad-hoc session that produces one or more target\n * events then immediately closes itself. The session lifecycle\n * (`initialized → running → completed`, see\n * `docs/spec/terminal-and-import.md#62-transition-diagram`) is honored:\n * `4 + N` events are\n * written in one bulk atomic pass (where N = number of target builders) and\n * `session.yaml` is written twice (`initialized` → `completed`).\n *\n * The single-target case (N = 1) covers `basou decision record`,\n * `basou session note`, `basou task new --status planned|in_progress`,\n * `basou task status`, and `basou task reconcile`. The two-target case\n * (N = 2) covers `basou task new --status done|cancelled` which fires\n * `task_created` followed immediately by `task_status_changed (planned → terminal)`\n * so the audit trail captures the implicit transition.\n *\n * Failures during `mkdir`, the initial `session.yaml` write, or the bulk\n * `events.jsonl` write trigger a best-effort `rm -rf` of the session\n * directory so partial ad-hoc sessions do not pollute the workspace.\n *\n * A failure on the final `session.yaml` status update is fatal but the\n * session directory is NOT cleaned up — `events.jsonl` is consistent and\n * carries the full lifecycle trail, so callers can reconcile manually. The\n * thrown {@link FailedToFinalizeError} carries the `sessionId` and\n * `targetEventIds` so the CLI layer can warn the user not to re-run the\n * command and duplicate the target events.\n *\n * Direct (non-CLI) callers are self-defended by zod boundary parses on\n * `sessionSource` and the initial session record.\n */\nexport async function createAdHocSessionWithEvent(\n input: CreateAdHocSessionInput,\n): Promise<CreateAdHocSessionResult> {\n // 1. core boundary parse — direct callers may pass arbitrary strings.\n SessionSourceKindSchema.parse(input.sessionSource);\n if (input.targetEventBuilders.length === 0) {\n throw new Error(\"Ad-hoc session requires at least one target event builder\");\n }\n\n // 2. ID minting. One target event id per builder; lifecycle ids are fixed.\n const sessionId = prefixedUlid(\"ses\");\n const startedEventId = prefixedUlid(\"evt\");\n const statusToRunningEventId = prefixedUlid(\"evt\");\n const targetEventIds = input.targetEventBuilders.map(() => prefixedUlid(\"evt\"));\n const statusToCompletedEventId = prefixedUlid(\"evt\");\n const endedEventId = prefixedUlid(\"evt\");\n\n // 3. Build the initial session record (status=initialized) and validate it\n // so a malformed input shape fails fast before any disk write.\n const initialSession: Session = SessionSchema.parse(\n buildInitialSession({\n sessionId,\n workspaceId: input.manifest.workspace.id,\n sourceKind: input.sessionSource,\n startedAt: input.occurredAt,\n label: input.label,\n workingDirectory: input.workingDirectory,\n invocation: input.invocation,\n taskId: input.taskId ?? null,\n }),\n );\n\n // 4. Create the session directory (recursive=true so a stripped-down\n // workspace with `.basou/sessions` missing still recovers).\n const sessionDir = join(input.paths.sessions, sessionId);\n try {\n await mkdir(sessionDir, { recursive: true });\n } catch (error: unknown) {\n throw new Error(\"Failed to create session directory\", { cause: error });\n }\n\n // 5. Initial session.yaml write (status=initialized).\n const sessionYamlPath = join(sessionDir, \"session.yaml\");\n try {\n await linkYamlFile(sessionYamlPath, initialSession);\n } catch (error: unknown) {\n await rm(sessionDir, { recursive: true, force: true }).catch(() => undefined);\n if (findErrorCode(error, \"EEXIST\")) {\n throw new Error(\"Session directory collision (retry the command)\", {\n cause: error,\n });\n }\n throw error;\n }\n\n // 6. events.jsonl bulk write — five events written atomically in a single\n // tmp+rename pass. A failure here removes the session directory so no\n // partial state survives (status=initialized + no events is not visible\n // in `basou session list`).\n try {\n const targetEvents: Event[] = input.targetEventBuilders.map((build, index) => {\n const targetEventId = targetEventIds[index] as PrefixedId<\"evt\">;\n return assertTargetEventIdentity(build(sessionId, targetEventId), sessionId, targetEventId);\n });\n const events: Event[] = [\n {\n schema_version: \"0.1.0\",\n id: startedEventId,\n session_id: sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"session_started\",\n },\n {\n schema_version: \"0.1.0\",\n id: statusToRunningEventId,\n session_id: sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"session_status_changed\",\n from: \"initialized\",\n to: \"running\",\n },\n ...targetEvents,\n {\n schema_version: \"0.1.0\",\n id: statusToCompletedEventId,\n session_id: sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"session_status_changed\",\n from: \"running\",\n to: \"completed\",\n },\n {\n schema_version: \"0.1.0\",\n id: endedEventId,\n session_id: sessionId,\n occurred_at: input.occurredAt,\n source: \"local-cli\",\n type: \"session_ended\",\n exit_code: 0,\n },\n ];\n await writeEventsBulk(sessionDir, events);\n } catch (error: unknown) {\n await rm(sessionDir, { recursive: true, force: true }).catch(() => undefined);\n throw error;\n }\n\n // 7. Finalize: overwrite session.yaml with status=completed + ended_at +\n // invocation.exit_code=0. Failure is fatal but events.jsonl is already\n // complete, so the directory is intentionally NOT removed — the caller\n // surfaces the partial state via FailedToFinalizeError.\n try {\n const finalSession: Session = SessionSchema.parse({\n ...initialSession,\n session: {\n ...initialSession.session,\n status: \"completed\" satisfies SessionStatus,\n ended_at: input.occurredAt,\n invocation: { ...initialSession.session.invocation, exit_code: 0 },\n },\n });\n await overwriteYamlFile(sessionYamlPath, finalSession);\n } catch (error: unknown) {\n throw new FailedToFinalizeError(sessionId, targetEventIds, error);\n }\n\n return {\n sessionId,\n targetEventIds,\n lifecycleEventIds: [\n startedEventId,\n statusToRunningEventId,\n statusToCompletedEventId,\n endedEventId,\n ],\n };\n}\n\nfunction buildInitialSession(input: {\n sessionId: PrefixedId<\"ses\">;\n workspaceId: PrefixedId<\"ws\">;\n sourceKind: SessionSourceKind;\n startedAt: string;\n label: string;\n workingDirectory: string;\n invocation: { command: string; args: string[] };\n taskId: PrefixedId<\"task\"> | null;\n}): Session {\n return {\n schema_version: \"0.1.0\",\n session: {\n id: input.sessionId,\n label: input.label,\n task_id: input.taskId,\n workspace_id: input.workspaceId,\n source: { kind: input.sourceKind, version: \"0.1.0\" },\n started_at: input.startedAt,\n status: \"initialized\",\n working_directory: sanitizeWorkingDirectory(input.workingDirectory, { homedir: homedir() }),\n invocation: { ...input.invocation, exit_code: null },\n related_files: [],\n events_log: \"events.jsonl\",\n },\n };\n}\n\n// ============================================================================\n// Attach path\n// ============================================================================\n\nexport type AttachableStatus = \"initialized\" | \"running\" | \"waiting_approval\";\n\nconst DEFAULT_ATTACHABLE_STATUSES: ReadonlySet<AttachableStatus> = new Set<AttachableStatus>([\n \"initialized\",\n \"running\",\n \"waiting_approval\",\n]);\n\nexport type AppendEventToExistingInput = {\n paths: BasouPaths;\n /** Already resolved via `resolveSessionId`; parsed at boundary again. */\n sessionId: PrefixedId<\"ses\">;\n attachableStatuses?: ReadonlySet<AttachableStatus>;\n eventBuilder: (eventId: PrefixedId<\"evt\">) => Event;\n};\n\nexport type AppendEventToExistingResult = {\n eventId: PrefixedId<\"evt\">;\n sessionStatus: SessionStatus;\n};\n\n/**\n * Read `session.yaml`, verify the session is in an attachable state, and\n * append a single event to its `events.jsonl`. `session.yaml` is NOT modified\n * so the caller can safely append `decision_recorded` / `note_added` without\n * mutating `related_files`, `summary`, or the session status.\n *\n * Race note: the status check and the event append are not atomic.\n * Between them another writer (e.g. `basou run claude-code` ending its\n * session) can flip the YAML to `completed` and append `session_ended`.\n * v0.1 accepts this race; the `events_say_ended_but_yaml_running`-style\n * suspect rule surfaces the inconsistency. Per-session locking is\n * deferred to a v0.3+ follow-up.\n */\nexport async function appendEventToExistingSession(\n input: AppendEventToExistingInput,\n): Promise<AppendEventToExistingResult> {\n // 1. Boundary parse (direct caller self-defense).\n SessionIdSchema.parse(input.sessionId);\n\n // 2. Read session.yaml.\n const sessionDoc = await readSessionYaml(input.paths, input.sessionId);\n const status = sessionDoc.session.status;\n\n // 3. Status check.\n if (status === \"imported\") {\n throw new Error(\"Cannot attach to imported session\");\n }\n const attachable = input.attachableStatuses ?? DEFAULT_ATTACHABLE_STATUSES;\n if (!attachable.has(status as AttachableStatus)) {\n throw new Error(`Session is not active: ${status}`);\n }\n\n // 4. Mint event ID and build payload.\n const eventId = prefixedUlid(\"evt\");\n const event = assertTargetEventIdentity(input.eventBuilder(eventId), input.sessionId, eventId);\n\n // 5. Append (appendEvent validates with EventSchema; bad payloads are\n // rejected with the fixed `\"Invalid Basou event payload\"` message).\n const sessionDir = join(input.paths.sessions, input.sessionId);\n await appendEvent(sessionDir, event);\n\n return { eventId, sessionStatus: status };\n}\n\n/**\n * Defensive check: a builder closure could in principle hand back\n * an event whose `id` or `session_id` differs from the orchestrator's\n * minted values. EventSchema only validates the shape, so this slip would\n * silently corrupt events.jsonl. Reject with a fixed pathless message so\n * direct-caller misuse never reaches disk.\n */\nfunction assertTargetEventIdentity(\n event: Event,\n expectedSessionId: PrefixedId<\"ses\">,\n expectedEventId: PrefixedId<\"evt\">,\n): Event {\n if (event.session_id !== expectedSessionId) {\n throw new Error(\"Target event session_id mismatch\");\n }\n if (event.id !== expectedEventId) {\n throw new Error(\"Target event id mismatch\");\n }\n return event;\n}\n","import { posix as path } from \"node:path\";\n\n/**\n * Options for {@link sanitizePath}. Both `workingDirectory` and `homedir`\n * are absolute POSIX paths the caller has already resolved (typically via\n * `process.cwd()` and `os.homedir()`). Callers are responsible for passing\n * fully normalised values; the sanitizer normalises them again internally\n * so a trailing slash or `.`-segment does not corrupt the prefix match.\n */\nexport type SanitizePathOptions = {\n /**\n * The session's working directory (= the `working_directory` field the\n * caller is about to write). Paths under this directory are rewritten\n * relative to it so the operator-private absolute prefix never leaks\n * into the workspace's persistent state.\n */\n workingDirectory: string;\n /**\n * The operator's home directory. Paths under this directory (but NOT\n * under `workingDirectory`) are rewritten with a `~/` prefix.\n */\n homedir: string;\n};\n\n/**\n * Rewrite an absolute path into a workspace-friendly form so the persisted\n * state of `.basou/` does not leak the operator's machine layout:\n *\n * 1. Paths under `opts.workingDirectory` become repository-relative\n * (e.g. `<wd>/src/x.ts` → `src/x.ts`, `<wd>` itself → `.`).\n * 2. Paths under `opts.homedir` (but not workingDirectory) become\n * tilde-prefixed (`/Users/u/notes/x.md` → `~/notes/x.md`,\n * `/Users/u` → `~`).\n * 3. Anything else — relative paths, system paths under `/etc/*`,\n * `..`-escapes from either base, paths that simply do not share a\n * prefix with either option — is returned verbatim (after `..`\n * normalisation). The sanitizer is intentionally non-redacting on\n * system paths so an operator who deliberately recorded a system\n * file (e.g. `/etc/hosts`) is not silently stripped of context.\n *\n * Hardening:\n * - A null byte in the input is rejected with `Invalid path: contains\n * null byte` (= POSIX path APIs treat \\0 as terminator and any path\n * containing one is malformed; we never accept it on the write side).\n * - `..` segments are resolved purely (no fs access) so the prefix\n * match cannot be defeated by `<wd>/../escape/x.ts` masquerading as\n * workingDirectory-internal.\n * - Backslashes are folded to forward slashes so a Windows-style input\n * can still be matched against POSIX bases. v0.3 targets macOS /\n * Linux only; full Windows support is a v0.4+ task.\n */\nexport function sanitizePath(rawPath: string, opts: SanitizePathOptions): string {\n if (rawPath.includes(\"\\0\")) {\n throw new Error(\"Invalid path: contains null byte\");\n }\n const normalized = path.normalize(rawPath.replace(/\\\\/g, \"/\"));\n const wd = path.normalize(opts.workingDirectory.replace(/\\\\/g, \"/\"));\n const home = path.normalize(opts.homedir.replace(/\\\\/g, \"/\"));\n\n // Only attempt prefix matching for absolute inputs; an already-relative\n // path stays as-is so write paths that pre-relativised do not get\n // mangled.\n if (!path.isAbsolute(normalized)) {\n return normalized;\n }\n\n // (1) workingDirectory-internal -> repo-relative.\n if (normalized === wd) return \".\";\n const wdRel = path.relative(wd, normalized);\n if (wdRel !== \"\" && !wdRel.startsWith(\"..\")) {\n return wdRel;\n }\n\n // (2) homedir-internal -> ~/...\n if (normalized === home) return \"~\";\n const homeRel = path.relative(home, normalized);\n if (homeRel !== \"\" && !homeRel.startsWith(\"..\")) {\n return `~/${homeRel}`;\n }\n\n // (3) preserve as-is.\n return normalized;\n}\n\n/**\n * Sanitize the `working_directory` field itself. This is a distinct entry\n * point because the field's own value is the workingDirectory of every\n * `related_files[]` entry written alongside it — running it through\n * {@link sanitizePath} with `opts.workingDirectory = rawPath` would\n * collapse the result to `\".\"` and lose the homedir-relative form the\n * spec requires.\n *\n * Strategy: bypass the workingDirectory rule entirely by passing a\n * sentinel that no real path can match. The homedir rule (rule 2) and\n * the preserve-as-is rule (rule 3) still apply, so:\n * - `/Users/u/projects/foo` → `~/projects/foo`\n * - `/Users/u` → `~`\n * - `/srv/work` → `/srv/work` (preserved, off-tree)\n *\n * Callers should still pass the live `homedir` so the rewrite uses the\n * real operator-private prefix.\n */\nexport function sanitizeWorkingDirectory(\n rawPath: string,\n opts: Pick<SanitizePathOptions, \"homedir\">,\n): string {\n // A sentinel that no real absolute path on disk can equal or be under.\n // `path.posix.normalize` collapses leading `/` so any sentinel must\n // remain non-prefixing post-normalisation; the sentinel below survives\n // normalisation as itself and never matches a real path.\n return sanitizePath(rawPath, {\n workingDirectory: \"/__basou_sentinel_never_match__\",\n homedir: opts.homedir,\n });\n}\n\n/** Result of {@link sanitizeRelatedFiles}. */\nexport type SanitizeRelatedFilesResult = {\n /** Sanitized path list (same length as the input). */\n sanitized: string[];\n /** Number of entries whose sanitized form differs from the input. */\n mutationCount: number;\n};\n\n/**\n * Apply {@link sanitizePath} to every entry of a `related_files[]` array\n * and report how many entries actually changed shape so callers (e.g. the\n * session-import CLI) can surface a single-line warning. The helper does\n * not deduplicate — callers already collect related_files into a Set\n * before serialising.\n */\nexport function sanitizeRelatedFiles(\n paths: ReadonlyArray<string>,\n opts: SanitizePathOptions,\n): SanitizeRelatedFilesResult {\n const sanitized: string[] = [];\n let mutationCount = 0;\n for (const p of paths) {\n const next = sanitizePath(p, opts);\n sanitized.push(next);\n if (next !== p) mutationCount += 1;\n }\n return { sanitized, mutationCount };\n}\n","import { readFile, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { findErrorCode } from \"../lib/error-codes.js\";\nimport { atomicCreate } from \"./atomic.js\";\nimport type { BasouPaths } from \"./basou-dir.js\";\n\n/**\n * The two lock scopes basou uses. `task` guards the read-modify-write window\n * around a single `task.md`; `session` guards the events.jsonl append plus\n * surrounding `session.yaml` mutation for a single session. Two scopes use\n * different lockfile names so they never collide on disk.\n */\nexport type LockScope = \"task\" | \"session\";\n\n/**\n * Any lock older than this is treated as stale and force-released even if the\n * holding pid is still alive. basou CLI invocations hold a lock for ms to a\n * few seconds at most, so an hour is a 10000x safety margin; the upper bound\n * is also our defence against pid reuse (a different process happening to\n * receive a long-dead pid).\n */\nconst STALE_LOCK_MAX_AGE_MS = 60 * 60 * 1000;\n\ntype LockFileBody = {\n pid: number;\n acquired_at: string;\n};\n\nexport type LockHandle = {\n /**\n * Release the lock by unlinking the lockfile. Best-effort: any unlink error\n * is swallowed so a doubled release does not raise, and disk state never\n * holds a stranded lockfile after the caller's `finally` block.\n */\n release: () => Promise<void>;\n};\n\n/**\n * Acquire an advisory lock at `<paths.locks>/<scope>_<id>.lock` for the\n * lifetime of the returned handle. Lockfile body records the holder's pid\n * and acquire timestamp so a competitor can detect stale locks left by a\n * SIGINT'd CLI run and recover automatically.\n *\n * Acquisition strategy:\n * 1. {@link atomicCreate} the lockfile (POSIX link(2) + EEXIST).\n * 2. On EEXIST, probe the existing lockfile via {@link isStaleLock}.\n * - If stale (= holder pid is dead or lock is older than\n * {@link STALE_LOCK_MAX_AGE_MS}), `unlink` the stale file and retry\n * the atomic create once.\n * - If still EEXIST after the retry (= another competitor won the race),\n * throw `\"Lock is held by another process\"`.\n * - If the holder is alive, throw `\"Lock is held by another process\"`\n * without retrying.\n *\n * The caller MUST call `release()` (typically from a `finally` block); the\n * `process.exit()` path or a fatal crash relies on stale-lock detection on\n * the next acquire to recover.\n */\nexport async function acquireLock(\n paths: BasouPaths,\n scope: LockScope,\n resourceId: string,\n): Promise<LockHandle> {\n const lockPath = lockfilePath(paths, scope, resourceId);\n const body: LockFileBody = {\n pid: process.pid,\n acquired_at: new Date().toISOString(),\n };\n const serialised = JSON.stringify(body);\n\n try {\n await atomicCreate(lockPath, serialised);\n } catch (error: unknown) {\n if (!findErrorCode(error, \"EEXIST\")) {\n throw error;\n }\n const stale = await isStaleLock(lockPath);\n if (!stale) {\n throw new Error(\"Lock is held by another process\", { cause: error });\n }\n // Best-effort cleanup of the stale lockfile, then a single retry. A\n // second EEXIST means another competitor beat us to the cleared lock;\n // surface that as a normal \"held\" failure rather than looping.\n await unlink(lockPath).catch(() => undefined);\n try {\n await atomicCreate(lockPath, serialised);\n } catch (retryError: unknown) {\n throw new Error(\"Lock is held by another process\", { cause: retryError });\n }\n }\n\n return {\n release: async () => {\n await unlink(lockPath).catch(() => undefined);\n },\n };\n}\n\n/**\n * Read the lockfile at `lockPath` and decide whether the holder is dead or\n * the lock is too old to trust. Used by {@link acquireLock} on EEXIST to\n * recover from SIGINT'd CLI runs that left the lockfile behind.\n *\n * Stale predicates (any of these = stale):\n * - lockfile body unreadable or malformed\n * - `acquired_at` is older than {@link STALE_LOCK_MAX_AGE_MS}\n * - `process.kill(pid, 0)` throws ESRCH (holder pid is dead)\n *\n * EPERM from `process.kill` means the pid is alive but owned by a different\n * uid; we treat that as alive so cross-user lockfile takeover does not happen\n * by accident.\n */\nasync function isStaleLock(lockPath: string): Promise<boolean> {\n let body: LockFileBody;\n try {\n const raw = await readFile(lockPath, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n if (typeof parsed !== \"object\" || parsed === null) return true;\n const candidate = parsed as Partial<LockFileBody>;\n if (typeof candidate.pid !== \"number\" || typeof candidate.acquired_at !== \"string\") {\n return true;\n }\n body = { pid: candidate.pid, acquired_at: candidate.acquired_at };\n } catch {\n // Unreadable lockfile (e.g. truncated mid-write) counts as stale so we\n // can recover instead of looping forever on EEXIST.\n return true;\n }\n const ageMs = Date.now() - Date.parse(body.acquired_at);\n if (!Number.isFinite(ageMs) || ageMs > STALE_LOCK_MAX_AGE_MS) {\n return true;\n }\n try {\n process.kill(body.pid, 0);\n return false;\n } catch (error: unknown) {\n if (findErrorCode(error, \"ESRCH\")) return true;\n // EPERM or any other surface — pid is alive (or unknown), keep the lock.\n return false;\n }\n}\n\nfunction lockfilePath(paths: BasouPaths, scope: LockScope, resourceId: string): string {\n // Strip the type prefix to keep the lockfile name compact (`task_01HX...` →\n // `01HX...`, `ses_01HX...` → `01HX...`). The scope literal at the start of\n // the filename keeps task/session lockfiles disjoint even when the ULID\n // tails happen to coincide.\n const sep = resourceId.indexOf(\"_\");\n const ulid = sep >= 0 ? resourceId.slice(sep + 1) : resourceId;\n return join(paths.locks, `${scope}_${ulid}.lock`);\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { findErrorCode } from \"../lib/error-codes.js\";\nimport {\n TASK_INDEX_SCHEMA_VERSION,\n type TaskIndex,\n type TaskIndexEntry,\n TaskIndexSchema,\n} from \"../schemas/task-index.schema.js\";\nimport { atomicReplace } from \"./atomic.js\";\nimport type { BasouPaths } from \"./basou-dir.js\";\n\n/**\n * Absolute path of the workspace's `tasks/index.json`. The index lives\n * INSIDE `<paths.tasks>` (not under `<paths.root>`) so a future\n * monorepo-style layout with multiple task families could carry its own\n * index without colliding at the basou root.\n */\nexport function taskIndexPath(paths: BasouPaths): string {\n return join(paths.tasks, \"index.json\");\n}\n\n/**\n * Read and validate `tasks/index.json`. Returns the parsed payload only\n * when the schema_version matches the current literal — a mismatch is\n * surfaced as a schema parse failure so the caller falls through to the\n * lazy-rebuild path.\n *\n * Error contract:\n * - ENOENT → throw `Error(\"Task index not found\", { cause })`\n * - JSON parse / schema fail / version mismatch → throw\n * `Error(\"Invalid task index\", { cause })`\n * - any other I/O failure → throw `Error(\"Failed to read task index\", { cause })`\n *\n * Callers should treat all three as \"rebuild from disk\"; the distinct\n * messages exist so debug output / dogfood notes can tell them apart.\n */\nexport async function readTaskIndex(paths: BasouPaths): Promise<TaskIndex> {\n const filePath = taskIndexPath(paths);\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf8\");\n } catch (error: unknown) {\n if (findErrorCode(error, \"ENOENT\")) {\n throw new Error(\"Task index not found\", { cause: error });\n }\n throw new Error(\"Failed to read task index\", { cause: error });\n }\n let parsedJson: unknown;\n try {\n parsedJson = JSON.parse(raw);\n } catch (error: unknown) {\n throw new Error(\"Invalid task index\", { cause: error });\n }\n const result = TaskIndexSchema.safeParse(parsedJson);\n if (!result.success) {\n throw new Error(\"Invalid task index\", { cause: result.error });\n }\n if (result.data.schema_version !== TASK_INDEX_SCHEMA_VERSION) {\n // Reject older / newer schema versions so a future bump triggers a\n // forced rebuild rather than silent migration.\n throw new Error(\"Invalid task index\", {\n cause: new Error(`Unsupported task index schema_version: ${result.data.schema_version}`),\n });\n }\n return result.data;\n}\n\n/**\n * Atomically write `tasks/index.json` with the given entries. Entries\n * are sorted by id (= ULID-ascending) so two rebuilds on the same disk\n * state produce byte-identical output and `git diff` stays clean.\n *\n * Caller-controlled `now` lets tests assert on `last_rebuilt_at`\n * without faking `Date`. When omitted the current wall clock is used.\n */\nexport async function rebuildTaskIndex(\n paths: BasouPaths,\n entries: ReadonlyArray<TaskIndexEntry>,\n now?: () => Date,\n): Promise<TaskIndex> {\n const sorted = [...entries].sort((a, b) => a.id.localeCompare(b.id));\n const payload: TaskIndex = {\n schema_version: TASK_INDEX_SCHEMA_VERSION,\n tasks: sorted,\n last_rebuilt_at: (now ?? (() => new Date()))().toISOString(),\n };\n // Self-defense — boundary-parse so a buggy caller cannot smuggle in\n // an invalid entry shape past the read-side schema check.\n TaskIndexSchema.parse(payload);\n await atomicReplace(taskIndexPath(paths), `${JSON.stringify(payload, null, 2)}\\n`);\n return payload;\n}\n\n/**\n * Mutation kind for {@link updateTaskIndex}. `add` and `update` carry a\n * full entry payload; `remove` carries only the id (the entry is gone\n * from disk by the time we write the index).\n *\n * archiveTask uses `remove` too: the archived task no longer participates\n * in the active task index because `enumerateTaskIds` (= the index's\n * read consumer) scans only `tasks/<id>.md`, not `tasks/archive/<id>.md`.\n */\nexport type TaskIndexOp =\n | { kind: \"add\"; entry: TaskIndexEntry }\n | { kind: \"update\"; entry: TaskIndexEntry }\n | { kind: \"remove\"; id: string };\n\n/**\n * Apply a single mutation to `tasks/index.json` and atomically rewrite\n * it. Falls through to {@link rebuildTaskIndex} when the current index is\n * missing / invalid so the first write after a workspace migration\n * still produces a valid file.\n *\n * Write failure (atomic-rename ENOSPC / EACCES etc.) is re-thrown\n * unwrapped so the caller (= each task write API) can decide whether to\n * surface it as a warning or escalate. The recommended policy in\n * `tasks.ts` is `console.warn(...)` plus keep the task.md write\n * successful (= index is a soft cache, not source of truth).\n */\nexport async function updateTaskIndex(\n paths: BasouPaths,\n op: TaskIndexOp,\n options?: { now?: () => Date },\n): Promise<TaskIndex> {\n const nowFn = options?.now ?? (() => new Date());\n let current: TaskIndex;\n try {\n current = await readTaskIndex(paths);\n } catch {\n // Index missing or invalid — rebuild empty before applying op.\n current = {\n schema_version: TASK_INDEX_SCHEMA_VERSION,\n tasks: [],\n last_rebuilt_at: nowFn().toISOString(),\n };\n }\n\n let nextTasks: TaskIndexEntry[];\n switch (op.kind) {\n case \"add\":\n nextTasks = current.tasks.some((t) => t.id === op.entry.id)\n ? current.tasks.map((t) => (t.id === op.entry.id ? op.entry : t))\n : [...current.tasks, op.entry];\n break;\n case \"update\":\n nextTasks = current.tasks.some((t) => t.id === op.entry.id)\n ? current.tasks.map((t) => (t.id === op.entry.id ? op.entry : t))\n : [...current.tasks, op.entry];\n break;\n case \"remove\":\n nextTasks = current.tasks.filter((t) => t.id !== op.id);\n break;\n }\n\n return await rebuildTaskIndex(paths, nextTasks, nowFn);\n}\n","import { z } from \"zod\";\nimport { IsoTimestampSchema, SchemaVersionSchema, TaskIdSchema } from \"./shared.schema.js\";\nimport { TaskStatusSchema } from \"./task.schema.js\";\n\n/**\n * Single entry inside `.basou/tasks/index.json`.\n *\n * Source of truth remains `task.md`; this is a derived cache populated\n * write-through on every task mutation (`createTask`,\n * `updateTaskStatusWithEvent`, `editTask`, `deleteTask`, `archiveTask`,\n * `reconcileTask`, `refreshTaskLinkedSessions`). The minimum field set\n * lets `basou task list` filter / sort without re-parsing every front\n * matter, while keeping the index small enough that rebuilds stay cheap.\n *\n * `label` is omitted when the task has no explicit label so the JSON\n * round-trips without storing `undefined` literals.\n */\nexport const TaskIndexEntrySchema = z\n .object({\n id: TaskIdSchema,\n status: TaskStatusSchema,\n label: z.string().min(1).optional(),\n updated_at: IsoTimestampSchema,\n })\n .strict();\nexport type TaskIndexEntry = z.infer<typeof TaskIndexEntrySchema>;\n\n/**\n * Top-level schema for `.basou/tasks/index.json`. `tasks[]` is the\n * compact projection used for fast enumeration; `last_rebuilt_at`\n * records the wall-clock moment of the latest full readdir rebuild so\n * a future migration / debugging tool can spot stale caches without\n * comparing every entry against disk.\n *\n * `schema_version` lets a future bump trigger a forced rebuild instead\n * of attempting silent schema migration — readTaskIndex returns the\n * parsed payload only when the version matches the current literal, so\n * a mismatch falls through to the lazy-rebuild path.\n */\nexport const TaskIndexSchema = z\n .object({\n schema_version: SchemaVersionSchema,\n tasks: z.array(TaskIndexEntrySchema),\n last_rebuilt_at: IsoTimestampSchema,\n })\n .strict();\nexport type TaskIndex = z.infer<typeof TaskIndexSchema>;\n\n/** Current schema version. Bump triggers a forced rebuild on next read. */\nexport const TASK_INDEX_SCHEMA_VERSION = \"0.1.0\" as const;\n","// `[1-9]\\d*` rejects \"0\" and leading zeros so that callers cannot smuggle in\n// a non-positive duration (which the underlying spawn validators would later\n// reject anyway). The unit is fixed to `ms`/`s`/`m`/`h`; days and weeks are\n// out of scope for v0.1.\nconst DURATION_RE = /^([1-9]\\d*)(ms|s|m|h)$/;\n\n/**\n * Parse a unit-suffixed duration string (e.g. `30s`, `5m`, `1h`, `100ms`)\n * into milliseconds.\n *\n * Rejects formats that cannot represent a positive, finite millisecond\n * value: malformed inputs, zero, leading-zero values, and computations that\n * overflow to `Infinity`. The returned number is always a positive integer.\n *\n * Supported units: `ms` (milliseconds), `s` (seconds), `m` (minutes),\n * `h` (hours).\n *\n * @param input duration string with required unit suffix\n * @returns duration in milliseconds (positive, finite)\n * @throws Error with message\n * `Invalid duration: <input>. Expected format: <positive-integer><unit> where unit is ms/s/m/h`\n * for format errors, or `Duration overflow: <input>` for non-finite results.\n */\nexport function parseDuration(input: string): number {\n const trimmed = input.trim();\n const match = DURATION_RE.exec(trimmed);\n if (!match) {\n throw new Error(\n `Invalid duration: ${trimmed}. Expected format: <positive-integer><unit> where unit is ms/s/m/h`,\n );\n }\n const value = Number(match[1]);\n const unit = match[2];\n let ms: number;\n switch (unit) {\n case \"ms\":\n ms = value;\n break;\n case \"s\":\n ms = value * 1000;\n break;\n case \"m\":\n ms = value * 60_000;\n break;\n case \"h\":\n ms = value * 3_600_000;\n break;\n default:\n // Unreachable per the regex; satisfy exhaustiveness analysis.\n throw new Error(`Invalid duration unit: ${unit}`);\n }\n if (!Number.isFinite(ms)) {\n throw new Error(`Duration overflow: ${trimmed}`);\n }\n return ms;\n}\n","import type { BasouPaths } from \"../storage/basou-dir.js\";\nimport { enumerateSessionDirs } from \"../storage/sessions.js\";\nimport { enumerateArchivedTaskIds, enumerateTaskIds } from \"../storage/tasks.js\";\n\n/**\n * Resolve a possibly-truncated session id prefix to a full session id by\n * scanning `<paths.sessions>/`. Existing message contract (carried over\n * from `packages/cli/src/commands/session.ts`) is\n * preserved exactly so callers that grep stderr keep working:\n *\n * - `\"Session id is empty\"`\n * - `\"Session not found: <input>\"`\n * - `\"Ambiguous session id '<input>': matched <N> sessions. Disambiguate\n * with a longer prefix.\"`\n */\nexport async function resolveSessionId(paths: BasouPaths, input: string): Promise<string> {\n return resolveIdInternal(paths, input, \"session\");\n}\n\n/**\n * Resolve a possibly-truncated task id prefix to a full task id by scanning\n * `<paths.tasks>/`. Mirrors {@link resolveSessionId} with the noun changed\n * to `task` in every error message.\n *\n * `options.includeArchived` extends the scan to `<paths.tasks>/archive/` so\n * read-only commands (e.g. `basou task show`) can address tasks that were\n * archived by `basou task archive`. Defaults to `false` so destructive flows\n * (status change, edit, delete, archive itself) cannot operate on archived\n * tasks accidentally.\n */\nexport async function resolveTaskId(\n paths: BasouPaths,\n input: string,\n options: { includeArchived?: boolean } = {},\n): Promise<string> {\n return resolveIdInternal(paths, input, \"task\", options);\n}\n\ntype IdKind = \"session\" | \"task\";\n\ntype KindConfig = {\n prefix: string;\n noun: string;\n nounPlural: string;\n capNoun: string;\n enumerate: (paths: BasouPaths) => Promise<string[]>;\n};\n\nconst KIND_CONFIG: Record<IdKind, KindConfig> = {\n session: {\n prefix: \"ses_\",\n noun: \"session\",\n nounPlural: \"sessions\",\n capNoun: \"Session\",\n enumerate: enumerateSessionDirs,\n },\n task: {\n prefix: \"task_\",\n noun: \"task\",\n nounPlural: \"tasks\",\n capNoun: \"Task\",\n enumerate: enumerateTaskIds,\n },\n};\n\nasync function resolveIdInternal(\n paths: BasouPaths,\n input: string,\n kind: IdKind,\n options: { includeArchived?: boolean } = {},\n): Promise<string> {\n const cfg = KIND_CONFIG[kind];\n const trimmed = input.trim();\n if (trimmed.length === 0) {\n throw new Error(`${cfg.capNoun} id is empty`);\n }\n const normalized = trimmed.startsWith(cfg.prefix) ? trimmed : `${cfg.prefix}${trimmed}`;\n if (normalized.length <= cfg.prefix.length) {\n throw new Error(`${cfg.capNoun} not found: ${input}`);\n }\n const primary = await cfg.enumerate(paths);\n // Merge in archived task ids when the caller opts in. Dedupe via a Set so\n // a single id appearing in both surfaces (shouldn't happen but defend\n // anyway) does not falsely register as ambiguous.\n const merged = new Set<string>(primary);\n if (kind === \"task\" && options.includeArchived === true) {\n for (const id of await enumerateArchivedTaskIds(paths)) {\n merged.add(id);\n }\n }\n if (merged.size === 0) {\n throw new Error(`${cfg.capNoun} not found: ${input}`);\n }\n const matches = [...merged].filter((e) => e.startsWith(normalized));\n if (matches.length === 0) {\n throw new Error(`${cfg.capNoun} not found: ${input}`);\n }\n if (matches.length > 1) {\n throw new Error(\n `Ambiguous ${cfg.noun} id '${input}': matched ${matches.length} ${cfg.nounPlural}. Disambiguate with a longer prefix.`,\n );\n }\n return matches[0] as string;\n}\n","import { type ChildProcess, spawn } from \"node:child_process\";\n\nimport { findErrorCode } from \"../storage/status.js\";\n\nimport type { ProcessRunner, RunOptions, RunResult } from \"./process-runner.js\";\n\nconst DEFAULT_KILL_GRACE_MS = 5_000;\n\n/**\n * Spawn-based ProcessRunner implementation.\n *\n * Behavior:\n * - `shell: false` and `detached: false`. The process group is not\n * detached, but the OS does not guarantee the child is reaped when\n * the parent terminates abruptly; callers handle SIGINT/SIGTERM/exit\n * hooks themselves.\n * - `capture: \"buffer\"` (default): `stdio: ['pipe', 'pipe', 'pipe']`,\n * stdout / stderr are decoded as UTF-8 and accumulated as full\n * strings (no streaming callbacks).\n * - `capture: \"none\"`: `stdio: ['inherit', 'inherit', 'inherit']`, the\n * child writes directly to the parent terminal in real time and\n * `RunResult.stdout` / `stderr` are empty strings. `stdin` is\n * incompatible with this mode (the child has no writable stdin pipe)\n * and the combination is rejected before spawn.\n * - `timeout_ms` and `AbortSignal` both trigger a two-stage kill:\n * `SIGTERM`, then `SIGKILL` after `DEFAULT_KILL_GRACE_MS` (5_000 ms).\n * - A non-zero `exit_code` does not throw; it is returned via\n * `RunResult`. Spawn-time errors throw with a pathless message and\n * the original error attached as `cause`.\n *\n * Error message contract: messages never include `cwd` or absolute\n * command paths. The original errno (and any nested wrapping) is\n * preserved on `Error.cause`, allowing callers to classify with\n * `findErrorCode` when needed.\n */\nexport class ChildProcessRunner implements ProcessRunner {\n async run(command: string, args: readonly string[], options: RunOptions): Promise<RunResult> {\n validateOptions(options);\n\n if (options.signal?.aborted) {\n throw new Error(\"Process aborted before spawn\", {\n cause: options.signal.reason,\n });\n }\n\n // Freeze the invocation snapshot at spawn time so the eventual RunResult\n // reflects the call as it was issued, even if the caller mutates `args`\n // or `options` afterward.\n const snapshotCommand = command;\n const snapshotArgs: readonly string[] = [...args];\n const snapshotCwd = options.cwd;\n const captureMode = options.capture ?? \"buffer\";\n\n const started_at = new Date();\n\n let child: ChildProcess;\n try {\n child = spawn(snapshotCommand, [...snapshotArgs], {\n cwd: snapshotCwd,\n env: options.env ?? process.env,\n stdio:\n captureMode === \"none\" ? [\"inherit\", \"inherit\", \"inherit\"] : [\"pipe\", \"pipe\", \"pipe\"],\n shell: false,\n detached: false,\n });\n } catch (error: unknown) {\n throw classifySpawnError(error);\n }\n\n // Notify caller that the child exists so they can wire parent-side\n // cleanup (e.g. an `exit` hook). The runner ignores any throw from\n // the callback; the caller is responsible for keeping it side-effect\n // safe.\n if (options.onSpawn) {\n try {\n options.onSpawn(child);\n } catch {\n // intentional: do not let onSpawn failures abort the run.\n }\n }\n\n let timeoutTimer: NodeJS.Timeout | null = null;\n let killTimer: NodeJS.Timeout | null = null;\n let killed = false;\n let settled = false;\n\n const triggerKill = (): void => {\n if (killed || child.exitCode !== null) return;\n killed = true;\n child.kill(\"SIGTERM\");\n killTimer = setTimeout(() => {\n if (child.exitCode === null) {\n child.kill(\"SIGKILL\");\n }\n }, DEFAULT_KILL_GRACE_MS);\n };\n\n // Attach the abort listener immediately, then re-check `aborted` to\n // close the window between spawn() returning and addEventListener.\n const onAbort = (): void => {\n triggerKill();\n };\n options.signal?.addEventListener(\"abort\", onAbort);\n if (options.signal?.aborted) {\n triggerKill();\n }\n\n let stdout = \"\";\n let stderr = \"\";\n if (captureMode === \"buffer\") {\n // stdio is ['pipe', 'pipe', 'pipe'] so stdout/stderr/stdin are non-null.\n child.stdout?.setEncoding(\"utf8\");\n child.stderr?.setEncoding(\"utf8\");\n child.stdout?.on(\"data\", (chunk: string) => {\n stdout += chunk;\n });\n child.stderr?.on(\"data\", (chunk: string) => {\n stderr += chunk;\n });\n\n if (options.stdin !== undefined) {\n child.stdin?.end(options.stdin);\n } else {\n child.stdin?.end();\n }\n }\n // capture: \"none\" leaves stdio inherited; stdout/stderr remain \"\".\n\n if (options.timeout_ms !== undefined) {\n timeoutTimer = setTimeout(triggerKill, options.timeout_ms);\n }\n\n const cleanup = (): void => {\n if (timeoutTimer !== null) clearTimeout(timeoutTimer);\n if (killTimer !== null) clearTimeout(killTimer);\n options.signal?.removeEventListener(\"abort\", onAbort);\n };\n\n return new Promise<RunResult>((resolve, reject) => {\n child.once(\"error\", (error: Error) => {\n if (settled) return;\n settled = true;\n cleanup();\n reject(classifySpawnError(error));\n });\n child.once(\"close\", (code: number | null, signal: NodeJS.Signals | null) => {\n if (settled) return;\n settled = true;\n cleanup();\n const ended_at = new Date();\n resolve({\n command: snapshotCommand,\n args: snapshotArgs,\n cwd: snapshotCwd,\n exit_code: code,\n signal,\n stdout,\n stderr,\n started_at: started_at.toISOString(),\n ended_at: ended_at.toISOString(),\n duration_ms: ended_at.getTime() - started_at.getTime(),\n pid: child.pid ?? null,\n });\n });\n });\n }\n}\n\nfunction validateOptions(options: RunOptions): void {\n if (\n options.timeout_ms !== undefined &&\n (!Number.isFinite(options.timeout_ms) || options.timeout_ms <= 0)\n ) {\n throw new Error(\"Invalid timeout_ms\");\n }\n if (options.capture === \"none\" && options.stdin !== undefined) {\n throw new Error('Combination of capture: \"none\" and stdin is not supported');\n }\n}\n\nfunction classifySpawnError(error: unknown): Error {\n if (findErrorCode(error, \"ENOENT\")) {\n return new Error(\"Command not found\", { cause: error });\n }\n return new Error(\"Failed to spawn child process\", { cause: error });\n}\n","import { z } from \"zod\";\nimport { IsoTimestampSchema, SchemaVersionSchema, WorkspaceIdSchema } from \"./shared.schema.js\";\n\nconst ProjectSchema = z.object({\n name: z.string().optional(),\n description: z.string().optional(),\n repository_url: z.string().nullable().optional(),\n});\n\nconst CapabilitiesSchema = z.object({\n enabled: z.array(z.string()),\n});\n\nconst ApprovalConfigSchema = z.object({\n required_for: z.array(z.string()).optional(),\n default_risk_level: z.enum([\"low\", \"medium\", \"high\", \"critical\"]),\n});\n\nconst ClaudeCodeAdapterConfigSchema = z.object({\n enabled: z.boolean(),\n config_path: z.string().optional(),\n});\n\nconst AdaptersSchema = z.object({\n \"claude-code\": ClaudeCodeAdapterConfigSchema,\n});\n\nconst GitConfigSchema = z.object({\n events_log: z.enum([\"ignore\", \"commit\"]).default(\"ignore\"),\n});\n\nconst WorkspaceMetaSchema = z.object({\n id: WorkspaceIdSchema,\n name: z.string().min(1),\n created_at: IsoTimestampSchema,\n updated_at: IsoTimestampSchema,\n});\n\n/**\n * Schema for `.basou/manifest.yaml`. The minimal manifest carries\n * schema_version, basou_version, workspace metadata, project info, enabled\n * capabilities, approval policy, adapter config, and git policy. The\n * `adapters.\"claude-code\"` key uses a hyphen; downstream code accesses it\n * via bracket notation.\n */\nexport const ManifestSchema = z.object({\n schema_version: SchemaVersionSchema,\n basou_version: z.literal(\"0.1.0\"),\n workspace: WorkspaceMetaSchema,\n project: ProjectSchema,\n capabilities: CapabilitiesSchema,\n approval: ApprovalConfigSchema,\n adapters: AdaptersSchema,\n git: GitConfigSchema,\n});\n\n/** Inferred runtime type for {@link ManifestSchema}. */\nexport type Manifest = z.infer<typeof ManifestSchema>;\n","import { z } from \"zod\";\nimport { EventSchema } from \"./event.schema.js\";\nimport {\n SessionMetricsSchema,\n SessionSourceKindSchema,\n SessionStatusSchema,\n} from \"./session.schema.js\";\nimport {\n IsoTimestampSchema,\n SessionIdSchema,\n TaskIdSchema,\n WorkspaceIdSchema,\n} from \"./shared.schema.js\";\n\n// Independent copy of SessionInnerSchema for import payloads. The differences\n// from session.schema.ts are deliberate:\n// - `id` is `SessionIdSchema.optional()` so format is validated when present\n// but the orchestrator discards it and assigns a fresh ULID.\n// - `status` / `source.kind` are validated against the canonical enums but\n// overwritten by the orchestrator (status -> \"imported\", source.kind\n// retained from input).\n// - `events_log` is plain `z.string().optional()`; the orchestrator forces\n// \"events.jsonl\" to block path traversal.\n// - `.strict()` rejects unknown session-level keys at parse time.\n//\n// Events strictness follows EventSchema as authored: `adapter_output` is\n// `.strict()`, the other 14 variants are permissive, and\n// `approval_requested.action` is `.passthrough()`. This keeps the spec's\n// additive-event-fields rule and round-trip imports of post-v0.1 events\n// compatible. A blanket strict wrap for every variant is deferred.\n//\n// `schema_version` at the top level is `z.string()` rather than the\n// `SchemaVersionSchema = z.literal(\"0.1.0\")` literal. The strict reject for\n// unsupported versions emits a dedicated `Unsupported import schema_version`\n// message from the orchestrator; a literal here would short-circuit the\n// branch and turn every mismatched version into the generic\n// `Invalid import payload`.\nexport const SessionInnerImportSchema = z\n .object({\n id: SessionIdSchema.optional(),\n label: z.string().optional(),\n task_id: TaskIdSchema.nullable().optional(),\n workspace_id: WorkspaceIdSchema,\n source: z.object({\n kind: SessionSourceKindSchema,\n version: z.literal(\"0.1.0\"),\n // Source-tool-native id (e.g. Claude Code session UUID), retained so\n // re-imports of the same source can be deduplicated.\n external_id: z.string().optional(),\n }),\n started_at: IsoTimestampSchema,\n ended_at: IsoTimestampSchema.optional(),\n status: SessionStatusSchema,\n working_directory: z.string().min(1),\n invocation: z.object({\n command: z.string().min(1),\n args: z.array(z.string()),\n exit_code: z.number().int().nullable(),\n }),\n related_files: z.array(z.string()).default([]),\n events_log: z.string().optional(),\n summary: z.string().nullable().optional(),\n metrics: SessionMetricsSchema.optional(),\n })\n .strict();\n\n/**\n * Schema for the round-trip JSON payload accepted by `basou session import\n * --format json`. The top level is `.strict()`; unknown keys at the outer\n * envelope are rejected.\n */\nexport const SessionImportPayloadSchema = z\n .object({\n schema_version: z.string(),\n session: SessionInnerImportSchema,\n events: z.array(EventSchema),\n })\n .strict();\n\n/** Inferred runtime type for {@link SessionImportPayloadSchema}. */\nexport type SessionImportPayload = z.infer<typeof SessionImportPayloadSchema>;\n/** Inferred runtime type for {@link SessionInnerImportSchema}. */\nexport type SessionInnerImportInput = z.infer<typeof SessionInnerImportSchema>;\n","import { join } from \"node:path\";\nimport { type ReplayWarning, replayEvents } from \"../events/event-replay.js\";\nimport type { Event } from \"../schemas/event.schema.js\";\nimport type {\n Session,\n SessionMetrics,\n SessionSourceKind,\n SessionStatus,\n} from \"../schemas/session.schema.js\";\nimport type { BasouPaths } from \"../storage/basou-dir.js\";\nimport { loadSessionEntries, type SessionSkipReason } from \"../storage/sessions.js\";\nimport {\n ACTIVE_GAP_CAP_MS,\n activeTimeFromTimestamps,\n type IntervalMs,\n type IsoInterval,\n intervalsIsoToMs,\n intervalsMsToIso,\n unionDurationMs,\n} from \"./active-time.js\";\n\n// Re-exported for callers that imported the cap from this module historically.\nexport { ACTIVE_GAP_CAP_MS };\n\n/**\n * Resolve the timezone used to bucket per-day stats. Native logs are UTC, so a\n * billing day needs an explicit timezone; default to the host's local zone.\n */\nfunction resolveTimeZone(timeZone: string | undefined): string {\n if (timeZone !== undefined && timeZone.length > 0) return timeZone;\n return Intl.DateTimeFormat().resolvedOptions().timeZone;\n}\n\nexport type WorkStatsInput = {\n paths: BasouPaths;\n /** Shared clock; running sessions are measured up to this instant. */\n now: Date;\n /**\n * IANA timezone used to bucket the per-day breakdown (logs are UTC, so a\n * billing day needs an explicit zone). Defaults to the host's local zone;\n * injectable for deterministic tests.\n */\n timeZone?: string;\n onWarning?: (warning: ReplayWarning, sessionId: string) => void;\n onSessionSkip?: (sessionId: string, reason: SessionSkipReason) => void;\n};\n\n/** Which measures are meaningful for a given session / source. */\nexport type MeasureAvailability = {\n /** Always true (started_at + now bound the span). */\n span: boolean;\n /**\n * `commandTimeMs` reflects real shell time. False for `claude-code-import`,\n * whose transcript carries no per-command duration (recorded as 0).\n */\n commandTime: boolean;\n /** At least one active interval could be measured (stored or event-derived). */\n activeTime: boolean;\n /** Token totals were captured (model-usage metrics present). */\n tokens: boolean;\n};\n\n/** Token rollup. Zero when not captured; `reasoning` is Codex-only. */\nexport type TokenTotals = {\n output: number;\n input: number;\n cached: number;\n reasoning: number;\n};\n\n/** How a session's active time was derived. */\nexport type ActiveTimeBasis = \"engaged-turns\" | \"events\";\n\nexport type SessionWorkStats = {\n sessionId: string;\n label: string | undefined;\n status: SessionStatus;\n sourceKind: SessionSourceKind;\n startedAt: string;\n endedAt: string | undefined;\n /** ended_at absent: span is measured to `now`. */\n open: boolean;\n sessionSpanMs: number;\n commandTimeMs: number;\n activeTimeMs: number;\n /**\n * How `activeTimeMs` / `activeIntervals` were derived: `engaged-turns` from\n * the engagement timestamps stored at import (captures conversation), or\n * `events` from the action-event stream (live sessions and pre-v2 imports).\n */\n activeTimeBasis: ActiveTimeBasis;\n /**\n * Merged active wall-clock ranges. Their summed duration equals\n * `activeTimeMs`; the aggregator unions them across sessions so overlapping\n * (concurrent) work is not double-counted in billable totals.\n */\n activeIntervals: IsoInterval[];\n commandCount: number;\n fileChangedCount: number;\n decisionCount: number;\n eventCount: number;\n tokens: TokenTotals;\n availability: MeasureAvailability;\n /** ended_at < started_at (clock skew): span was clamped to 0. */\n spanClamped: boolean;\n /** events.jsonl could not be read: action / time counts are 0 + untrustworthy. */\n eventsUnreadable: boolean;\n};\n\nexport type SourceWorkStats = {\n sourceKind: SessionSourceKind;\n sessionCount: number;\n sessionSpanMs: number;\n commandTimeMs: number;\n activeTimeMs: number;\n commandCount: number;\n fileChangedCount: number;\n decisionCount: number;\n eventCount: number;\n tokens: TokenTotals;\n /** Every session of this kind reports real command time. */\n commandTimeReliable: boolean;\n /** At least one session of this kind captured token totals. */\n tokensAvailable: boolean;\n};\n\nexport type StatusCount = { status: SessionStatus; count: number };\n\n/**\n * One calendar day of the time x volume billing view. `billableActiveTimeMs` is\n * the union of active intervals starting on this date (so per-day sums to the\n * de-duplicated workspace total); volume is attributed to each session's\n * `started_at` date.\n */\nexport type DayWorkStats = {\n /** Calendar date `YYYY-MM-DD` in the report timezone. */\n date: string;\n billableActiveTimeMs: number;\n sessionCount: number;\n commandCount: number;\n fileChangedCount: number;\n decisionCount: number;\n tokens: TokenTotals;\n};\n\nexport type WorkStatsTotals = {\n sessionCount: number;\n openSessionCount: number;\n sessionSpanMs: number;\n commandTimeMs: number;\n /** Naive sum of per-session active time; double-counts overlapping sessions. */\n activeTimeMs: number;\n /**\n * Billable active time: the UNION of every session's active intervals, so\n * concurrent sessions do not double-count human wall-clock. Equals\n * `activeTimeMs` when no sessions overlap, and is smaller when they do.\n */\n billableActiveTimeMs: number;\n commandCount: number;\n fileChangedCount: number;\n decisionCount: number;\n eventCount: number;\n tokens: TokenTotals;\n /** No `claude-code-import` sessions present, so command time is workspace-wide real. */\n commandTimeReliable: boolean;\n tokensAvailable: boolean;\n};\n\nexport type WorkStatsResult = {\n generatedAt: string;\n /** Idle-gap cap applied to active time (methodology lock). */\n activeGapCapMs: number;\n /** IANA timezone used to bucket {@link WorkStatsResult.byDay}. */\n timeZone: string;\n totals: WorkStatsTotals;\n /** Per session, started_at ascending (loadSessionEntries order). */\n sessions: SessionWorkStats[];\n bySource: SourceWorkStats[];\n byStatus: StatusCount[];\n /** Per-day time x volume billing view, date ascending. */\n byDay: DayWorkStats[];\n};\n\n// Fixed display order, mirroring the handoff renderer (+ archived appended).\nconst STATUS_ORDER: readonly SessionStatus[] = [\n \"completed\",\n \"failed\",\n \"running\",\n \"interrupted\",\n \"waiting_approval\",\n \"initialized\",\n \"imported\",\n \"archived\",\n];\n\n/**\n * Aggregate work + engaged-time across the workspace's sessions.\n *\n * Honesty note: this returns a LABELED SET of measures, not one number. Token\n * volume (when captured) is the most direct \"how much the AI produced\" signal.\n * The time measures are proxies, ordered from most to least billing-relevant:\n *\n * - `billableActiveTimeMs` (totals) is the headline for billing human harness\n * labor: the UNION of every session's active intervals, so two sessions run\n * concurrently do not bill the same wall-clock twice. `activeTimeMs` is the\n * naive sum, kept only to expose the overlap delta.\n * - Per-session active time is derived from the session's ENGAGED series. For\n * imported sessions this is the genuine engagement timestamps captured at\n * import (conversation turns plus action events), so design discussion that\n * produced few tool calls is still counted; idle gaps over `ACTIVE_GAP_CAP_MS`\n * (5 min) are not credited. Live sessions and pre-v2 imports lack that signal\n * and fall back to the action-event stream (`activeTimeBasis: \"events\"`).\n * - `sessionSpanMs` overcounts (includes idle) and `commandTimeMs` is\n * shell-execution only (0 for `claude-code-import`); both are kept as context.\n *\n * The per-day view buckets the union intervals by `timeZone` (logs are UTC, so\n * a billing day needs an explicit zone). A union interval crossing local\n * midnight is attributed to its start day; per-day time still sums to the\n * billable total. Availability flags let callers caveat each measure.\n *\n * Session enumeration goes through {@link loadSessionEntries} (the handoff /\n * decisions path), so `session.yaml`-broken sessions are skipped consistently.\n */\nexport async function computeWorkStats(input: WorkStatsInput): Promise<WorkStatsResult> {\n const { now } = input;\n const timeZone = resolveTimeZone(input.timeZone);\n // Surface events_jsonl_unreadable exactly once per session even when the\n // throw happens in our own replay loop below (verbatim from the renderers).\n const unreadableEmitted = new Set<string>();\n const wrappedSkip: (sid: string, reason: SessionSkipReason) => void = (sid, reason) => {\n if (reason === \"events_jsonl_unreadable\") unreadableEmitted.add(sid);\n input.onSessionSkip?.(sid, reason);\n };\n const loadOpts: Parameters<typeof loadSessionEntries>[1] = { now, onSkip: wrappedSkip };\n if (input.onWarning !== undefined) loadOpts.onWarning = input.onWarning;\n const entries = await loadSessionEntries(input.paths, loadOpts);\n\n const sessions: SessionWorkStats[] = [];\n for (const entry of entries) {\n const events: Event[] = [];\n let eventsUnreadable = false;\n try {\n for await (const ev of replayEvents(join(input.paths.sessions, entry.sessionId), {\n onWarning: (w) => input.onWarning?.(w, entry.sessionId),\n })) {\n events.push(ev);\n }\n } catch {\n eventsUnreadable = true;\n if (!unreadableEmitted.has(entry.sessionId)) {\n wrappedSkip(entry.sessionId, \"events_jsonl_unreadable\");\n }\n }\n sessions.push(\n sessionWorkStatsFromEvents(\n entry.sessionId,\n entry.session.session,\n events,\n now,\n eventsUnreadable,\n ),\n );\n }\n\n // Union every session's active intervals once; both the billable total and\n // the per-day view are attributed from the same merged ranges so they agree.\n const allIntervals: IntervalMs[] = [];\n for (const s of sessions) allIntervals.push(...intervalsIsoToMs(s.activeIntervals));\n const union = unionDurationMs(allIntervals);\n\n return {\n generatedAt: now.toISOString(),\n activeGapCapMs: ACTIVE_GAP_CAP_MS,\n timeZone,\n totals: computeTotals(sessions, union.ms),\n sessions,\n bySource: computeBySource(sessions),\n byStatus: computeByStatus(sessions),\n byDay: computeByDay(sessions, union.merged, timeZone),\n };\n}\n\n/**\n * Compute one session's work stats from its inner record + event list. Pure\n * and exported so a single-session surface (e.g. `basou session show`) can\n * reuse the exact same measures the workspace aggregator produces.\n */\nexport function sessionWorkStatsFromEvents(\n sessionId: string,\n inner: Session[\"session\"],\n events: ReadonlyArray<Event>,\n now: Date,\n eventsUnreadable = false,\n): SessionWorkStats {\n let commandCount = 0;\n let fileChangedCount = 0;\n let decisionCount = 0;\n let commandTimeMs = 0;\n const timestamps: number[] = [];\n for (const ev of events) {\n const t = Date.parse(ev.occurred_at);\n if (Number.isFinite(t)) timestamps.push(t);\n if (ev.type === \"command_executed\") {\n commandCount++;\n commandTimeMs += ev.duration_ms;\n } else if (ev.type === \"file_changed\") {\n fileChangedCount++;\n } else if (ev.type === \"decision_recorded\") {\n decisionCount++;\n }\n }\n const span = computeSpan(inner.started_at, inner.ended_at, now);\n const tokens = readTokens(inner.metrics);\n const active = resolveActiveTime(inner.metrics, timestamps);\n return {\n sessionId,\n label: inner.label,\n status: inner.status,\n sourceKind: inner.source.kind,\n startedAt: inner.started_at,\n endedAt: inner.ended_at,\n open: inner.ended_at === undefined,\n sessionSpanMs: span.ms,\n commandTimeMs,\n activeTimeMs: active.ms,\n activeTimeBasis: active.basis,\n activeIntervals: intervalsMsToIso(active.intervals),\n commandCount,\n fileChangedCount,\n decisionCount,\n eventCount: events.length,\n tokens,\n availability: {\n span: true,\n commandTime: inner.source.kind !== \"claude-code-import\",\n activeTime: active.intervals.length > 0,\n tokens: hasTokens(tokens),\n },\n spanClamped: span.clamped,\n eventsUnreadable,\n };\n}\n\n/**\n * Resolve a session's active time + intervals. Prefer the engaged-time\n * intervals stored at import (they capture conversation turns the event stream\n * misses); otherwise derive from the action-event timestamps. Either way\n * `ms` equals the summed interval duration.\n */\nfunction resolveActiveTime(\n metrics: SessionMetrics | undefined,\n eventTimestamps: number[],\n): { ms: number; intervals: IntervalMs[]; basis: ActiveTimeBasis } {\n const stored = metrics?.active_intervals;\n if (stored !== undefined && stored.length > 0) {\n const intervals = intervalsIsoToMs(stored);\n const ms = intervals.reduce((n, [start, end]) => n + (end - start), 0);\n return { ms, intervals, basis: \"engaged-turns\" };\n }\n const derived = activeTimeFromTimestamps(eventTimestamps, ACTIVE_GAP_CAP_MS);\n return { ms: derived.ms, intervals: derived.intervals, basis: \"events\" };\n}\n\nfunction computeSpan(\n startedAt: string,\n endedAt: string | undefined,\n now: Date,\n): { ms: number; clamped: boolean } {\n const start = Date.parse(startedAt);\n const end = endedAt !== undefined ? Date.parse(endedAt) : now.getTime();\n if (!Number.isFinite(start) || !Number.isFinite(end)) return { ms: 0, clamped: true };\n const raw = end - start;\n return raw < 0 ? { ms: 0, clamped: true } : { ms: raw, clamped: false };\n}\n\nfunction readTokens(metrics: SessionMetrics | undefined): TokenTotals {\n return {\n output: metrics?.output_tokens ?? 0,\n input: metrics?.input_tokens ?? 0,\n cached: metrics?.cached_input_tokens ?? 0,\n reasoning: metrics?.reasoning_output_tokens ?? 0,\n };\n}\n\nfunction hasTokens(t: TokenTotals): boolean {\n return t.output > 0 || t.input > 0 || t.cached > 0 || t.reasoning > 0;\n}\n\nfunction emptyTokens(): TokenTotals {\n return { output: 0, input: 0, cached: 0, reasoning: 0 };\n}\n\nfunction addTokens(a: TokenTotals, b: TokenTotals): void {\n a.output += b.output;\n a.input += b.input;\n a.cached += b.cached;\n a.reasoning += b.reasoning;\n}\n\nfunction computeTotals(\n sessions: readonly SessionWorkStats[],\n billableActiveTimeMs: number,\n): WorkStatsTotals {\n const tokens = emptyTokens();\n const totals: WorkStatsTotals = {\n sessionCount: sessions.length,\n openSessionCount: 0,\n sessionSpanMs: 0,\n commandTimeMs: 0,\n activeTimeMs: 0,\n billableActiveTimeMs,\n commandCount: 0,\n fileChangedCount: 0,\n decisionCount: 0,\n eventCount: 0,\n tokens,\n commandTimeReliable: true,\n tokensAvailable: false,\n };\n for (const s of sessions) {\n if (s.open) totals.openSessionCount++;\n totals.sessionSpanMs += s.sessionSpanMs;\n totals.commandTimeMs += s.commandTimeMs;\n totals.activeTimeMs += s.activeTimeMs;\n totals.commandCount += s.commandCount;\n totals.fileChangedCount += s.fileChangedCount;\n totals.decisionCount += s.decisionCount;\n totals.eventCount += s.eventCount;\n addTokens(tokens, s.tokens);\n if (!s.availability.commandTime) totals.commandTimeReliable = false;\n if (s.availability.tokens) totals.tokensAvailable = true;\n }\n return totals;\n}\n\nfunction computeBySource(sessions: readonly SessionWorkStats[]): SourceWorkStats[] {\n const map = new Map<SessionSourceKind, SourceWorkStats>();\n for (const s of sessions) {\n let row = map.get(s.sourceKind);\n if (row === undefined) {\n row = {\n sourceKind: s.sourceKind,\n sessionCount: 0,\n sessionSpanMs: 0,\n commandTimeMs: 0,\n activeTimeMs: 0,\n commandCount: 0,\n fileChangedCount: 0,\n decisionCount: 0,\n eventCount: 0,\n tokens: emptyTokens(),\n commandTimeReliable: true,\n tokensAvailable: false,\n };\n map.set(s.sourceKind, row);\n }\n row.sessionCount++;\n row.sessionSpanMs += s.sessionSpanMs;\n row.commandTimeMs += s.commandTimeMs;\n row.activeTimeMs += s.activeTimeMs;\n row.commandCount += s.commandCount;\n row.fileChangedCount += s.fileChangedCount;\n row.decisionCount += s.decisionCount;\n row.eventCount += s.eventCount;\n addTokens(row.tokens, s.tokens);\n if (!s.availability.commandTime) row.commandTimeReliable = false;\n if (s.availability.tokens) row.tokensAvailable = true;\n }\n return [...map.values()].sort((a, b) => a.sourceKind.localeCompare(b.sourceKind));\n}\n\nfunction computeByStatus(sessions: readonly SessionWorkStats[]): StatusCount[] {\n const counts = new Map<SessionStatus, number>();\n for (const s of sessions) counts.set(s.status, (counts.get(s.status) ?? 0) + 1);\n const ordered: StatusCount[] = [];\n for (const status of STATUS_ORDER) {\n const count = counts.get(status);\n if (count !== undefined && count > 0) ordered.push({ status, count });\n }\n return ordered;\n}\n\n/**\n * Build the per-day billing view. Time comes from the pre-merged union\n * intervals, attributed to each interval's start date so the per-day totals sum\n * exactly to `totals.billableActiveTimeMs`. Volume (tokens, action counts) is\n * attributed to each session's `started_at` date.\n */\nfunction computeByDay(\n sessions: readonly SessionWorkStats[],\n unionMerged: readonly IntervalMs[],\n timeZone: string,\n): DayWorkStats[] {\n const days = new Map<string, DayWorkStats>();\n const ensure = (date: string): DayWorkStats => {\n let day = days.get(date);\n if (day === undefined) {\n day = {\n date,\n billableActiveTimeMs: 0,\n sessionCount: 0,\n commandCount: 0,\n fileChangedCount: 0,\n decisionCount: 0,\n tokens: emptyTokens(),\n };\n days.set(date, day);\n }\n return day;\n };\n for (const [start, end] of unionMerged) {\n ensure(tzDate(start, timeZone)).billableActiveTimeMs += end - start;\n }\n for (const s of sessions) {\n const startedMs = Date.parse(s.startedAt);\n if (!Number.isFinite(startedMs)) continue;\n const day = ensure(tzDate(startedMs, timeZone));\n day.sessionCount++;\n day.commandCount += s.commandCount;\n day.fileChangedCount += s.fileChangedCount;\n day.decisionCount += s.decisionCount;\n addTokens(day.tokens, s.tokens);\n }\n return [...days.values()].sort((a, b) => a.date.localeCompare(b.date));\n}\n\n/** Calendar date (`YYYY-MM-DD`) of an instant in the given IANA timezone. */\nfunction tzDate(ms: number, timeZone: string): string {\n return new Intl.DateTimeFormat(\"en-CA\", {\n timeZone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n }).format(new Date(ms));\n}\n","import { lstat, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\n/**\n * Absolute paths to the standard `.basou/` directory layout, derived from a\n * given repository root. The shape mirrors the canonical `.basou/` tree\n * (see `docs/spec/workspace.md`). `root` is the `.basou/` directory itself\n * (i.e. `repositoryRoot/.basou`).\n *\n * `files` exposes the well-known top-level files inside `.basou/`. Each path\n * is computed but not created — they are written by their respective\n * subsystems (e.g. `writeManifest` for `manifest.yaml`).\n *\n * All fields are deeply readonly; consumers must not mutate the returned\n * object.\n */\nexport type BasouPaths = {\n readonly root: string;\n readonly sessions: string;\n readonly tasks: string;\n readonly approvals: {\n readonly pending: string;\n readonly resolved: string;\n };\n readonly locks: string;\n readonly logs: string;\n readonly raw: string;\n readonly tmp: string;\n readonly files: {\n readonly manifest: string;\n readonly status: string;\n readonly handoff: string;\n readonly decisions: string;\n };\n};\n\n/**\n * Compute absolute paths to the standard `.basou/` directory layout under\n * `repositoryRoot`. Pure: performs no I/O and is safe to call before the\n * directory exists.\n *\n * @param repositoryRoot Absolute path to the git repository root (the\n * parent directory of `.basou/`). Caller is responsible for resolving\n * `process.cwd()` or running `git rev-parse --show-toplevel` upstream;\n * this function does not validate that the path exists or is a git\n * repository.\n */\nexport function basouPaths(repositoryRoot: string): BasouPaths {\n const root = join(repositoryRoot, \".basou\");\n const approvalsBase = join(root, \"approvals\");\n return {\n root,\n sessions: join(root, \"sessions\"),\n tasks: join(root, \"tasks\"),\n approvals: {\n pending: join(approvalsBase, \"pending\"),\n resolved: join(approvalsBase, \"resolved\"),\n },\n locks: join(root, \"locks\"),\n logs: join(root, \"logs\"),\n raw: join(root, \"raw\"),\n tmp: join(root, \"tmp\"),\n files: {\n manifest: join(root, \"manifest.yaml\"),\n status: join(root, \"status.json\"),\n handoff: join(root, \"handoff.md\"),\n decisions: join(root, \"decisions.md\"),\n },\n };\n}\n\n// Labels for sub-paths inside `.basou/`. Used in pathless error messages so\n// the surface area for absolute-path leakage is bounded by this map.\nconst PATH_LABELS = {\n sessions: \".basou/sessions\",\n tasks: \".basou/tasks\",\n approvalsPending: \".basou/approvals/pending\",\n approvalsResolved: \".basou/approvals/resolved\",\n locks: \".basou/locks\",\n logs: \".basou/logs\",\n raw: \".basou/raw\",\n tmp: \".basou/tmp\",\n} as const;\n\n/**\n * Create the standard `.basou/` directory layout under `repositoryRoot`.\n *\n * Idempotent: a no-op on an already-initialized layout. Returns the resolved\n * {@link BasouPaths} so callers can immediately use them.\n *\n * Throws if `repositoryRoot/.basou` (or any required subdirectory) exists\n * but is not a directory, or if filesystem permissions prevent creation.\n * All thrown error messages are pathless; the original native error is\n * attached as `cause` for diagnostics.\n *\n * @param repositoryRoot Absolute path to the git repository root. See\n * {@link basouPaths} for the contract on this parameter.\n */\nexport async function ensureBasouDirectory(repositoryRoot: string): Promise<BasouPaths> {\n const paths = basouPaths(repositoryRoot);\n\n // lstat (not stat) so that a symlink at `.basou` is detected as a symlink\n // and rejected; following the link could place Basou state outside the\n // git repository root, violating the workspace-root invariant.\n let existing: Awaited<ReturnType<typeof lstat>> | undefined;\n try {\n existing = await lstat(paths.root);\n } catch (error: unknown) {\n if (!hasErrorCode(error) || error.code !== \"ENOENT\") {\n throw new Error(\"Failed to inspect .basou directory\", { cause: error });\n }\n }\n if (existing !== undefined && !existing.isDirectory()) {\n throw new Error(\"Basou root .basou exists but is not a directory\");\n }\n\n await Promise.all([\n mkdirLabeled(paths.sessions, PATH_LABELS.sessions),\n mkdirLabeled(paths.tasks, PATH_LABELS.tasks),\n mkdirLabeled(paths.approvals.pending, PATH_LABELS.approvalsPending),\n mkdirLabeled(paths.approvals.resolved, PATH_LABELS.approvalsResolved),\n mkdirLabeled(paths.locks, PATH_LABELS.locks),\n mkdirLabeled(paths.logs, PATH_LABELS.logs),\n mkdirLabeled(paths.raw, PATH_LABELS.raw),\n mkdirLabeled(paths.tmp, PATH_LABELS.tmp),\n ]);\n\n return paths;\n}\n\nasync function mkdirLabeled(target: string, label: string): Promise<void> {\n try {\n await mkdir(target, { recursive: true });\n } catch (error: unknown) {\n if (hasErrorCode(error) && (error.code === \"ENOTDIR\" || error.code === \"EEXIST\")) {\n throw new Error(`${label} exists but is not a directory`, { cause: error });\n }\n throw new Error(`Failed to create ${label}`, { cause: error });\n }\n}\n\nfunction hasErrorCode(error: unknown): error is Error & { code: string } {\n if (!(error instanceof Error)) return false;\n const codeProp = (error as unknown as Record<string, unknown>).code;\n return typeof codeProp === \"string\";\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nconst MARKER = \"# Basou - default ignore\";\n\n// Recommended .gitignore block (ignore + commit). The test asserts an\n// exact match against this spec string literal to detect spec drift.\nconst BASOU_GITIGNORE_BLOCK =\n \"# Basou - default ignore\\n\" +\n \".basou/logs/\\n\" +\n \".basou/raw/\\n\" +\n \".basou/tmp/\\n\" +\n \".basou/locks/\\n\" +\n \".basou/status.json\\n\" +\n \".basou/sessions/*/events.jsonl\\n\" +\n \".basou/sessions/*/artifacts/\\n\" +\n \".basou/approvals/pending/\\n\" +\n \".basou/approvals/resolved/\\n\" +\n \"\\n\" +\n \"# Basou - default commit\\n\" +\n \"# .basou/manifest.yaml\\n\" +\n \"# .basou/handoff.md\\n\" +\n \"# .basou/decisions.md\\n\" +\n \"# .basou/tasks/\\n\" +\n \"# .basou/sessions/*/session.yaml\\n\" +\n \"# .basou/sessions/*/transcript.md\\n\" +\n \"# .basou/sessions/*/changed-files.json\\n\";\n\nexport type AppendBasouGitignoreResult = {\n /** True if the block was appended (or the file was newly created). */\n readonly appended: boolean;\n};\n\n/**\n * Append Basou's default `.gitignore` block to `repositoryRoot/.gitignore`.\n *\n * The block contents are derived from the Basou v0.1 specification (the\n * standard ignore + commit recommendations). Callers must pass an absolute\n * path to a Git repository root.\n *\n * Behavior:\n * - If `.gitignore` does not exist, it is created with the Basou block.\n * - If a line starting with `# Basou - default ignore` is already present,\n * the file is left untouched and `appended: false` is returned\n * (idempotent).\n * - If `.gitignore` is a symlink, the link is followed and the target file\n * is updated. Symlinks are not rejected.\n *\n * On I/O failure throws Error with a pathless message\n * (`Failed to read .gitignore` / `Failed to write .gitignore`) and the\n * original native error attached as `cause`.\n */\nexport async function appendBasouGitignore(\n repositoryRoot: string,\n): Promise<AppendBasouGitignoreResult> {\n const gitignorePath = join(repositoryRoot, \".gitignore\");\n\n let body: string;\n let existed: boolean;\n try {\n body = await readFile(gitignorePath, \"utf8\");\n existed = true;\n } catch (error: unknown) {\n if (hasErrorCode(error) && error.code === \"ENOENT\") {\n body = \"\";\n existed = false;\n } else {\n throw new Error(\"Failed to read .gitignore\", { cause: error });\n }\n }\n\n if (existed && hasBasouMarker(body)) {\n return { appended: false };\n }\n\n const next = composeNextBody(body);\n try {\n await writeFile(gitignorePath, next, { encoding: \"utf8\" });\n } catch (error: unknown) {\n throw new Error(\"Failed to write .gitignore\", { cause: error });\n }\n return { appended: true };\n}\n\nfunction hasBasouMarker(body: string): boolean {\n for (const rawLine of body.split(\"\\n\")) {\n if (rawLine.trimEnd().startsWith(MARKER)) return true;\n }\n return false;\n}\n\nfunction composeNextBody(existing: string): string {\n if (existing.length === 0) return BASOU_GITIGNORE_BLOCK;\n const normalized = existing.endsWith(\"\\n\") ? existing : `${existing}\\n`;\n return `${normalized}\\n${BASOU_GITIGNORE_BLOCK}`;\n}\n\nfunction hasErrorCode(error: unknown): error is Error & { code: string } {\n if (!(error instanceof Error)) return false;\n return typeof (error as unknown as Record<string, unknown>).code === \"string\";\n}\n","import { lstat } from \"node:fs/promises\";\nimport { type PrefixedId, prefixedUlid } from \"../ids/ulid.js\";\nimport { type Manifest, ManifestSchema } from \"../schemas/manifest.schema.js\";\nimport type { BasouPaths } from \"./basou-dir.js\";\nimport { readYamlFile, writeYamlFile } from \"./yaml-store.js\";\n\n/**\n * Inputs for {@link createManifest}. Optional fields drop out of the\n * resulting Manifest entirely (they are not emitted as `null`/`undefined`\n * in YAML); pass `null` for `repositoryUrl` to keep an explicit `null`.\n */\nexport type CreateManifestInput = {\n workspaceName: string;\n projectName?: string;\n projectDescription?: string;\n repositoryUrl?: string | null;\n /** Override for tests; defaults to `new Date()`. */\n now?: Date;\n /** Override for tests; defaults to a freshly generated `ws_<ULID>`. */\n workspaceId?: PrefixedId<\"ws\">;\n};\n\n/**\n * Build a fresh Manifest object that satisfies the manifest schema's\n * minimum shape. Performs no I/O. Returned object is parse-validated by\n * `ManifestSchema`.\n */\nexport function createManifest(input: CreateManifestInput): Manifest {\n if (input.workspaceName.length === 0) {\n throw new Error(\"Workspace name is empty. Pass --name explicitly.\");\n }\n const now = (input.now ?? new Date()).toISOString();\n const workspaceId = input.workspaceId ?? prefixedUlid(\"ws\");\n\n const project: Manifest[\"project\"] = {\n ...(input.projectName !== undefined ? { name: input.projectName } : {}),\n ...(input.projectDescription !== undefined ? { description: input.projectDescription } : {}),\n ...(input.repositoryUrl !== undefined ? { repository_url: input.repositoryUrl } : {}),\n };\n\n const manifest: Manifest = {\n schema_version: \"0.1.0\",\n basou_version: \"0.1.0\",\n workspace: {\n id: workspaceId,\n name: input.workspaceName,\n created_at: now,\n updated_at: now,\n },\n project,\n capabilities: {\n enabled: [\"core\", \"claude-code-adapter\", \"terminal-recording\", \"git-capability\", \"approval\"],\n },\n approval: {\n required_for: [\"destructive_command\", \"external_send\"],\n default_risk_level: \"medium\",\n },\n adapters: {\n \"claude-code\": { enabled: true },\n },\n git: { events_log: \"ignore\" },\n };\n return ManifestSchema.parse(manifest);\n}\n\n/**\n * Write a Manifest to `paths.files.manifest`. Re-validates via\n * `ManifestSchema` before serialization.\n *\n * Refuses to overwrite an existing manifest unless `force: true`.\n */\nexport async function writeManifest(\n paths: BasouPaths,\n manifest: Manifest,\n options?: { force?: boolean },\n): Promise<void> {\n const force = options?.force === true;\n const validated = ManifestSchema.parse(manifest);\n\n if (!force) {\n let existed = false;\n try {\n await lstat(paths.files.manifest);\n existed = true;\n } catch (error: unknown) {\n if (!hasErrorCode(error) || error.code !== \"ENOENT\") {\n throw new Error(\"Failed to inspect existing manifest\", { cause: error });\n }\n }\n if (existed) {\n throw new Error(\"Already initialized. Use --force to overwrite.\");\n }\n }\n\n await writeYamlFile(paths.files.manifest, validated);\n}\n\n/**\n * Read and parse a Manifest from `paths.files.manifest`. Throws if the file\n * is missing or contents fail `ManifestSchema` validation.\n */\nexport async function readManifest(paths: BasouPaths): Promise<Manifest> {\n const raw = await readYamlFile(paths.files.manifest);\n return ManifestSchema.parse(raw);\n}\n\nfunction hasErrorCode(error: unknown): error is Error & { code: string } {\n if (!(error instanceof Error)) return false;\n return typeof (error as unknown as Record<string, unknown>).code === \"string\";\n}\n","import { readFile } from \"node:fs/promises\";\nimport { atomicReplace } from \"./atomic.js\";\n\n/** Marker line that begins the auto-generated region. */\nexport const GENERATED_START = \"<!-- BASOU:GENERATED:START -->\";\n/** Marker line that ends the auto-generated region. */\nexport const GENERATED_END = \"<!-- BASOU:GENERATED:END -->\";\n\n/**\n * Result of parsing a markdown body for the BASOU:GENERATED marker region.\n *\n * The spec mandates strict line-level matching (see\n * `docs/spec/generated-markdown.md#102-marker-convention`): a marker is\n * only recognized when an entire line is exactly the marker string.\n * Leading/trailing whitespace, comment compression, and BOM are treated as\n * legacy formats (`no_markers`) so that re-generation refuses to silently\n * overwrite a mismatched manual edit.\n */\nexport type MarkerSection =\n | { kind: \"ok\"; before: string; generated: string; after: string }\n | { kind: \"no_markers\" }\n | { kind: \"missing_start\" }\n | { kind: \"missing_end\" }\n | { kind: \"multiple_pairs\" }\n | { kind: \"wrong_order\" };\n\n/**\n * Read a markdown file as UTF-8 text. Returns `null` when the file does not\n * exist; throws `Error(\"Failed to read markdown file\", { cause })` for other\n * I/O failures (pathless contract — never embed the absolute path in the\n * thrown `message`).\n */\nexport async function readMarkdownFile(filePath: string): Promise<string | null> {\n try {\n return await readFile(filePath, \"utf8\");\n } catch (error: unknown) {\n if (hasErrorCode(error) && error.code === \"ENOENT\") return null;\n throw new Error(\"Failed to read markdown file\", { cause: error });\n }\n}\n\n/**\n * Atomically write a markdown body via {@link atomicReplace}. The shared\n * helper handles the tmp-file + rename sequence, `wx` collision guard, and\n * best-effort tmp cleanup on failure.\n *\n * On any failure the original error is re-thrown as\n * `Error(\"Failed to write markdown file\", { cause })` (pathless contract).\n */\nexport async function writeMarkdownFile(filePath: string, body: string): Promise<void> {\n try {\n await atomicReplace(filePath, body);\n } catch (error: unknown) {\n throw new Error(\"Failed to write markdown file\", { cause: error });\n }\n}\n\n/**\n * Parse a markdown body and identify the BASOU:GENERATED marker region.\n *\n * Returns one of six `kind` discriminants:\n * - `ok`: exactly one START line followed by exactly one END line in the\n * correct order. `before` / `generated` / `after` slice the original\n * text by character offsets so CRLF / LF are preserved verbatim outside\n * the marker region.\n * - `no_markers`: both START and END absent (legacy file / fresh write).\n * - `missing_start` / `missing_end`: exactly one of the pair is present.\n * - `multiple_pairs`: more than one START or END line.\n * - `wrong_order`: END appears before START.\n *\n * Matching is strict: leading/trailing whitespace, BOM, and comment\n * compression (`<!--BASOU:...-->`) all bypass the marker and are treated\n * as legacy content.\n */\nexport function parseMarkers(content: string): MarkerSection {\n // Split on either CRLF or LF so the line count is consistent regardless of\n // the file's line ending. The reconstruction step below slices the original\n // string by character offsets to preserve the actual line endings outside\n // the generated region.\n const lines = content.split(/\\r?\\n/);\n const startLines: number[] = [];\n const endLines: number[] = [];\n for (let i = 0; i < lines.length; i++) {\n if (lines[i] === GENERATED_START) startLines.push(i);\n else if (lines[i] === GENERATED_END) endLines.push(i);\n }\n if (startLines.length === 0 && endLines.length === 0) return { kind: \"no_markers\" };\n if (startLines.length === 0) return { kind: \"missing_start\" };\n if (endLines.length === 0) return { kind: \"missing_end\" };\n if (startLines.length >= 2 || endLines.length >= 2) return { kind: \"multiple_pairs\" };\n const startLineIdx = startLines[0] as number;\n const endLineIdx = endLines[0] as number;\n if (endLineIdx < startLineIdx) return { kind: \"wrong_order\" };\n\n // Walk the original string to find byte offsets of the marker lines. This\n // preserves CRLF vs LF in the surrounding text — splitting and re-joining\n // would normalize the line endings.\n const startOffset = lineStartOffset(content, startLineIdx);\n const endLineStart = lineStartOffset(content, endLineIdx);\n const startLineEnd = startOffset + GENERATED_START.length;\n const endLineEnd = endLineStart + GENERATED_END.length;\n\n const before = content.slice(0, startOffset);\n // The generated region is everything between the two marker lines,\n // exclusive of the marker line themselves but including the newline after\n // START and excluding the newline before END (so re-render can plug in\n // its own body without doubling separators).\n const afterStartNewline = skipOneNewline(content, startLineEnd);\n const beforeEndNewline = trimOneNewline(content, endLineStart);\n const generated = content.slice(afterStartNewline, beforeEndNewline);\n const after = content.slice(endLineEnd);\n return { kind: \"ok\", before, generated, after };\n}\n\n/**\n * Build the final markdown body by replacing the BASOU:GENERATED region.\n *\n * - `existing === null` (no file yet): return `<START>\\n<generated>\\n<END>\\n`.\n * - existing parses to `ok`: replace the marked region and keep everything\n * before START and after END untouched (preserving manual additions).\n * - any other parse result: throw a pathless error referencing `fileLabel`.\n *\n * The caller passes `fileLabel` (e.g. `\"handoff.md\"` or `\"decisions.md\"`)\n * so the error message is informative without leaking an absolute path.\n */\nexport function renderWithMarkers(\n existing: string | null,\n generated: string,\n fileLabel: string,\n): string {\n const normalized = generated.endsWith(\"\\n\") ? generated : `${generated}\\n`;\n if (existing === null) {\n return `${GENERATED_START}\\n${normalized}${GENERATED_END}\\n`;\n }\n const section = parseMarkers(existing);\n switch (section.kind) {\n case \"ok\":\n return `${section.before}${GENERATED_START}\\n${normalized}${GENERATED_END}${section.after}`;\n case \"no_markers\":\n throw new Error(`Markers missing in ${fileLabel}`);\n case \"missing_start\":\n case \"missing_end\":\n case \"multiple_pairs\":\n case \"wrong_order\":\n throw new Error(`Markers mismatched in ${fileLabel}`);\n }\n}\n\n/** Character offset of the first character of `lineIdx` (0-based). */\nfunction lineStartOffset(content: string, lineIdx: number): number {\n if (lineIdx === 0) return 0;\n let offset = 0;\n let line = 0;\n while (offset < content.length && line < lineIdx) {\n const ch = content[offset];\n if (ch === \"\\n\") {\n line += 1;\n offset += 1;\n } else if (ch === \"\\r\") {\n // CR or CRLF both count as a line terminator.\n offset += 1;\n if (content[offset] === \"\\n\") offset += 1;\n line += 1;\n } else {\n offset += 1;\n }\n }\n return offset;\n}\n\n/** Advance past one trailing `\\n` or `\\r\\n` if present. */\nfunction skipOneNewline(content: string, offset: number): number {\n if (content[offset] === \"\\r\" && content[offset + 1] === \"\\n\") return offset + 2;\n if (content[offset] === \"\\n\") return offset + 1;\n return offset;\n}\n\n/** Walk back past one leading `\\n` or `\\r\\n` if present. */\nfunction trimOneNewline(content: string, offset: number): number {\n if (offset >= 2 && content[offset - 2] === \"\\r\" && content[offset - 1] === \"\\n\")\n return offset - 2;\n if (offset >= 1 && content[offset - 1] === \"\\n\") return offset - 1;\n return offset;\n}\n\nfunction hasErrorCode(error: unknown): error is Error & { code: string } {\n if (!(error instanceof Error)) return false;\n const codeProp = (error as unknown as Record<string, unknown>).code;\n return typeof codeProp === \"string\";\n}\n","import { mkdir, rm } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { writeEventsBulk } from \"../events/event-writer.js\";\nimport { type PrefixedId, prefixedUlid } from \"../ids/ulid.js\";\nimport { findErrorCode } from \"../lib/error-codes.js\";\nimport { sanitizeRelatedFiles, sanitizeWorkingDirectory } from \"../lib/path-sanitizer.js\";\nimport type { Event } from \"../schemas/event.schema.js\";\nimport type { Manifest } from \"../schemas/manifest.schema.js\";\nimport type { Session, SessionSourceKind, SessionStatus } from \"../schemas/session.schema.js\";\nimport type {\n SessionImportPayload,\n SessionInnerImportInput,\n} from \"../schemas/session-import.schema.js\";\nimport { TaskIdSchema } from \"../schemas/shared.schema.js\";\nimport type { BasouPaths } from \"./basou-dir.js\";\nimport { enumerateTaskIds } from \"./tasks.js\";\nimport { linkYamlFile } from \"./yaml-store.js\";\n\n/**\n * Options for {@link importSessionFromJson}. All fields are optional.\n *\n * - `labelOverride` / `taskIdOverride` come from the CLI `--label` / `--task`\n * flags and win over the corresponding fields on the input payload.\n * - `dryRun` skips disk writes entirely and returns a preview result.\n */\nexport type ImportSessionOptions = {\n labelOverride?: string;\n taskIdOverride?: string;\n dryRun?: boolean;\n};\n\n/**\n * Result of a successful import. `finalStatus` is always the literal\n * `\"imported\"` (per the import-session lifecycle policy); `finalSourceKind`\n * mirrors the input's `session.source.kind` so round-trip imports preserve\n * provenance.\n *\n * `pathSanitizeReport` summarises how many path-shaped fields the importer\n * rewrote on the way in: `related_files[]` entries plus a single boolean\n * for `working_directory`. The CLI wrapper surfaces this as a one-line\n * stderr warning when the total is non-zero so the operator sees that\n * machine-private prefixes were stripped.\n */\nexport type ImportSessionResult = {\n sessionId: PrefixedId<\"ses\">;\n eventCount: number;\n finalStatus: SessionStatus;\n finalSourceKind: SessionSourceKind;\n pathSanitizeReport: {\n relatedFiles: number;\n workingDirectoryRewritten: boolean;\n };\n};\n\n/**\n * Import a round-trip JSON payload into `.basou/sessions/<new>/`. The caller\n * MUST validate the payload against {@link SessionImportPayloadSchema} first\n * and gate the `schema_version === \"0.1.0\"` literal check externally; this\n * function trusts both invariants.\n *\n * On success a fresh session ID is minted and a complete\n * `session.yaml` + `events.jsonl` pair is written atomically. On any post-\n * mkdir failure the session directory is removed best-effort so partial\n * imports do not leave `session_yaml_missing` half-states behind.\n *\n * Throws `Error` with one of the fixed messages enumerated by the import contract\n * §\"Error messages\" table; the original native error is attached as `cause`\n * for `--verbose` rendering.\n */\nexport async function importSessionFromJson(\n paths: BasouPaths,\n manifest: Manifest,\n payload: SessionImportPayload,\n options: ImportSessionOptions,\n): Promise<ImportSessionResult> {\n // Defense in depth: the CLI converter (parseTaskIdOverride) already gates\n // this, but a direct core API caller could still pass an arbitrary string.\n if (\n options.taskIdOverride !== undefined &&\n !TaskIdSchema.safeParse(options.taskIdOverride).success\n ) {\n throw new Error(`Invalid task_id: ${options.taskIdOverride}`);\n }\n\n // Reachability guard: rewriteEvents\n // preserves variant-specific task_id fields, so importing a session that\n // references a task absent from the local workspace would silently\n // install a dangling reference. Validate every task_id carrier:\n // task_created / task_status_changed / task_reconciled events plus the\n // effective session task_id (override-wins, matches buildSessionRecord\n // below).\n const effectiveSessionTaskId = options.taskIdOverride ?? payload.session.task_id ?? null;\n await assertImportedTaskReferencesAreReachable(paths, payload.events, effectiveSessionTaskId);\n\n const newSessionId = prefixedUlid(\"ses\");\n\n const rewrittenEvents = rewriteEvents(payload.events, newSessionId);\n assertChronologicalOrder(rewrittenEvents);\n\n const { record: sessionRecord, pathSanitizeReport } = buildSessionRecord(\n payload.session,\n manifest,\n newSessionId,\n options,\n );\n\n if (options.dryRun === true) {\n return {\n sessionId: newSessionId,\n eventCount: rewrittenEvents.length,\n finalStatus: \"imported\",\n finalSourceKind: sessionRecord.session.source.kind,\n pathSanitizeReport,\n };\n }\n\n // recursive: true lets a stripped-down workspace (manifest present but\n // `.basou/sessions` missing) recover instead of failing with ENOENT; ULID\n // collision on the new session dir itself is statistically impossible, so\n // the silent EEXIST on an existing directory is acceptable here. Concurrent\n // attempts to write the same session.yaml are caught by linkYamlFile below.\n const sessionDir = join(paths.sessions, newSessionId);\n try {\n await mkdir(sessionDir, { recursive: true });\n } catch (error: unknown) {\n throw new Error(\"Failed to create session directory\", { cause: error });\n }\n\n try {\n await writeEventsBulk(sessionDir, rewrittenEvents);\n } catch (error: unknown) {\n await rm(sessionDir, { recursive: true, force: true }).catch(() => undefined);\n throw error;\n }\n\n try {\n const sessionYamlPath = join(sessionDir, \"session.yaml\");\n await linkYamlFile(sessionYamlPath, sessionRecord);\n } catch (error: unknown) {\n await rm(sessionDir, { recursive: true, force: true }).catch(() => undefined);\n if (findErrorCode(error, \"EEXIST\")) {\n throw new Error(\"Session directory collision (retry the command)\", {\n cause: error,\n });\n }\n throw error;\n }\n\n return {\n sessionId: newSessionId,\n eventCount: rewrittenEvents.length,\n finalStatus: \"imported\",\n finalSourceKind: sessionRecord.session.source.kind,\n pathSanitizeReport,\n };\n}\n\n// Reachability guard: refuse any payload that\n// references task ids absent from the local workspace, across every carrier:\n// task_created / task_status_changed / task_reconciled events plus the\n// effective session task_id (= the override if supplied, otherwise the\n// imported session.yaml.task_id; matches buildSessionRecord's override-wins\n// semantics so we never reject on an id the final record will discard). The\n// fixed message is pathless-contract compliant; broken ids are not echoed\n// back so an adversarial payload cannot probe the local task namespace.\nasync function assertImportedTaskReferencesAreReachable(\n paths: BasouPaths,\n events: ReadonlyArray<Event>,\n effectiveSessionTaskId: string | null,\n): Promise<void> {\n const taskIdsToCheck = new Set<string>();\n for (const ev of events) {\n if (\n ev.type === \"task_created\" ||\n ev.type === \"task_status_changed\" ||\n ev.type === \"task_reconciled\" ||\n ev.type === \"task_linkage_refreshed\" ||\n ev.type === \"task_deleted\" ||\n ev.type === \"task_archived\"\n ) {\n taskIdsToCheck.add(ev.task_id);\n }\n }\n if (effectiveSessionTaskId !== null) {\n taskIdsToCheck.add(effectiveSessionTaskId);\n }\n if (taskIdsToCheck.size === 0) {\n // skip the tasks-dir scan when nothing references a task,\n // so imports that carry no task_id at all keep the original perf.\n return;\n }\n const knownTaskIds = new Set(await enumerateTaskIds(paths));\n for (const id of taskIdsToCheck) {\n if (!knownTaskIds.has(id)) {\n throw new Error(\"Imported session references unknown task_id\");\n }\n }\n}\n\n// Rewrite each event's `id` and `session_id` to brand-new values while\n// retaining every other field — including variant-specific cross-reference\n// IDs (approval_id, decision_id, task_id, file paths, raw_ref) — so that\n// chains like `approval_requested` -> `approval_approved` remain joinable on\n// the imported side. The events were already validated against EventSchema,\n// and prefixedUlid output satisfies EventIdSchema by construction, so the\n// rewritten events do not need to be re-parsed.\nfunction rewriteEvents(events: Event[], newSessionId: PrefixedId<\"ses\">): Event[] {\n return events.map((event) => ({\n ...event,\n id: prefixedUlid(\"evt\"),\n session_id: newSessionId,\n }));\n}\n\n// Enforce strict chronological order with same-ms duplicates allowed (`>=`).\n// Out-of-order events indicate an exporter bug or\n// hand-edited payload — refuse to silently sort.\nfunction assertChronologicalOrder(events: Event[]): void {\n for (let i = 1; i < events.length; i++) {\n const prevEvent = events[i - 1];\n const currEvent = events[i];\n if (prevEvent === undefined || currEvent === undefined) continue;\n const prev = Date.parse(prevEvent.occurred_at);\n const curr = Date.parse(currEvent.occurred_at);\n if (!Number.isFinite(prev) || !Number.isFinite(curr) || curr < prev) {\n throw new Error(\"Events are not in chronological order\");\n }\n }\n}\n\nfunction buildSessionRecord(\n input: SessionInnerImportInput,\n manifest: Manifest,\n newSessionId: PrefixedId<\"ses\">,\n options: ImportSessionOptions,\n): {\n record: Session;\n pathSanitizeReport: ImportSessionResult[\"pathSanitizeReport\"];\n} {\n // Sanitize before constructing the record so the operator-private\n // absolute prefix never reaches disk (= same write-time policy as\n // run.ts / exec.ts / ad-hoc-session.ts). We use the imported\n // working_directory itself as the base for related_files so paths\n // recorded relative to the original repo continue to read as\n // repo-internal; the working_directory field itself is sanitized via\n // the sentinel-based helper so the same value yields \"~/projects/foo\"\n // instead of collapsing to \".\".\n const home = homedir();\n const workingDirectoryRaw = input.working_directory;\n const workingDirectorySanitized = sanitizeWorkingDirectory(workingDirectoryRaw, {\n homedir: home,\n });\n const relatedSanitized = sanitizeRelatedFiles(input.related_files, {\n workingDirectory: workingDirectoryRaw,\n homedir: home,\n });\n\n const inner: Session[\"session\"] = {\n id: newSessionId,\n ...(options.labelOverride !== undefined || input.label !== undefined\n ? { label: options.labelOverride ?? input.label }\n : {}),\n task_id:\n options.taskIdOverride !== undefined\n ? (options.taskIdOverride as Session[\"session\"][\"task_id\"])\n : (input.task_id ?? null),\n workspace_id: manifest.workspace.id,\n source: input.source,\n started_at: input.started_at,\n ...(input.ended_at !== undefined ? { ended_at: input.ended_at } : {}),\n status: \"imported\",\n working_directory: workingDirectorySanitized,\n invocation: input.invocation,\n related_files: relatedSanitized.sanitized,\n events_log: \"events.jsonl\",\n summary: input.summary ?? null,\n ...(input.metrics !== undefined ? { metrics: input.metrics } : {}),\n };\n return {\n record: { schema_version: \"0.1.0\", session: inner },\n pathSanitizeReport: {\n relatedFiles: relatedSanitized.mutationCount,\n workingDirectoryRewritten: workingDirectorySanitized !== workingDirectoryRaw,\n },\n };\n}\n","/**\n * Version of the `@basou/core` package, aligned with `manifest.yaml`'s\n * `basou_version` field as defined in the Basou v0.1 specification.\n */\nexport const BASOU_CORE_VERSION = \"0.1.0\";\n\nexport type {\n ClaudeTranscriptRecord,\n ClaudeTranscriptToPayloadOptions,\n CommandLookup,\n} from \"./adapters/claude-code/index.js\";\nexport {\n CLAUDE_IMPORT_SOURCE,\n claudeCodeAdapterMetadata,\n claudeTranscriptToImportPayload,\n resolveClaudeCodeCommand,\n summarizeAdapterOutput,\n} from \"./adapters/claude-code/index.js\";\nexport type {\n CodexRolloutRecord,\n CodexRolloutToPayloadOptions,\n} from \"./adapters/codex/index.js\";\nexport { CODEX_IMPORT_SOURCE, codexRolloutToImportPayload } from \"./adapters/codex/index.js\";\nexport type { ApprovalLocation, LoadedApproval } from \"./approval/index.js\";\nexport { enumerateApprovals, isLazyExpired, loadApproval } from \"./approval/index.js\";\nexport type { DecisionsRendererInput, DecisionsRendererResult } from \"./decisions/index.js\";\nexport { renderDecisions } from \"./decisions/index.js\";\nexport type { ReplayOptions, ReplayWarning } from \"./events/index.js\";\nexport { appendEvent, readAllEvents, replayEvents, writeEventsBulk } from \"./events/index.js\";\nexport type { DiffResult, FileChange, FileChangeStatus } from \"./git/diff.js\";\nexport { getDiff } from \"./git/diff.js\";\nexport type { GitSnapshot } from \"./git/snapshot.js\";\nexport { getSnapshot, resolveRepositoryRoot, tryRemoteUrl } from \"./git/snapshot.js\";\nexport type { HandoffRendererInput, HandoffRendererResult } from \"./handoff/index.js\";\nexport { renderHandoff } from \"./handoff/index.js\";\nexport type { IdPrefix, PrefixedId } from \"./ids/ulid.js\";\nexport { ID_PREFIXES, isValidPrefixedId, prefixedUlid, ulid } from \"./ids/ulid.js\";\nexport { parseDuration } from \"./lib/duration.js\";\nexport { resolveSessionId, resolveTaskId } from \"./lib/id-resolver.js\";\nexport type { SanitizePathOptions, SanitizeRelatedFilesResult } from \"./lib/path-sanitizer.js\";\nexport {\n sanitizePath,\n sanitizeRelatedFiles,\n sanitizeWorkingDirectory,\n} from \"./lib/path-sanitizer.js\";\nexport { ChildProcessRunner } from \"./runtime/child-process-runner.js\";\nexport type {\n CaptureMode,\n ProcessRunner,\n RunOptions,\n RunResult,\n} from \"./runtime/process-runner.js\";\nexport type {\n AdapterOutputEvent,\n Approval,\n ApprovalApprovedEvent,\n ApprovalExpiredEvent,\n ApprovalRejectedEvent,\n ApprovalRequestedEvent,\n ApprovalStatus,\n CommandExecutedEvent,\n DecisionRecordedEvent,\n Event,\n FileChangedEvent,\n GitSnapshotEvent,\n Manifest,\n NoteAddedEvent,\n RiskLevel,\n Session,\n SessionEndedEvent,\n SessionImportPayload,\n SessionInnerImportInput,\n SessionMetrics,\n SessionSourceKind,\n SessionStartedEvent,\n SessionStatus,\n SessionStatusChangedEvent,\n StatusSnapshot,\n Task,\n TaskArchivedEvent,\n TaskCreatedEvent,\n TaskDeletedEvent,\n TaskLinkageRefreshedEvent,\n TaskReconciledEvent,\n TaskStatus,\n TaskStatusChangedEvent,\n} from \"./schemas/index.js\";\nexport {\n ApprovalIdSchema,\n ApprovalSchema,\n ApprovalStatusSchema,\n DecisionIdSchema,\n EventIdSchema,\n EventSchema,\n EventSourceSchema,\n IsoTimestampSchema,\n ManifestSchema,\n RiskLevelSchema,\n SchemaVersionSchema,\n SessionIdSchema,\n SessionImportPayloadSchema,\n SessionInnerImportSchema,\n SessionMetricsSchema,\n SessionSchema,\n SessionSourceKindSchema,\n SessionStatusSchema,\n StatusSchema,\n TaskIdSchema,\n TaskSchema,\n TaskStatusSchema,\n WorkspaceIdSchema,\n} from \"./schemas/index.js\";\nexport type {\n ActiveTimeBasis,\n DayWorkStats,\n MeasureAvailability,\n SessionWorkStats,\n SourceWorkStats,\n StatusCount,\n TokenTotals,\n WorkStatsInput,\n WorkStatsResult,\n WorkStatsTotals,\n} from \"./stats/index.js\";\nexport { ACTIVE_GAP_CAP_MS, computeWorkStats, sessionWorkStatsFromEvents } from \"./stats/index.js\";\nexport type {\n AppendBasouGitignoreResult,\n AppendEventToExistingInput,\n AppendEventToExistingResult,\n ArchiveTaskInput,\n ArchiveTaskResult,\n AttachableStatus,\n AttachTaskInput,\n AttachUpdateTaskStatusInput,\n BasouPaths,\n CreateAdHocSessionInput,\n CreateAdHocSessionResult,\n CreateAdHocTaskInput,\n CreateManifestInput,\n CreateTaskInput,\n CreateTaskResult,\n DeleteTaskInput,\n DeleteTaskResult,\n EditTaskInput,\n EditTaskResult,\n ImportSessionOptions,\n ImportSessionResult,\n LoadSessionEntriesOptions,\n LoadTaskEntriesOptions,\n LockHandle,\n LockScope,\n MarkerSection,\n ReconcileAllResult,\n ReconcileAllTasksInput,\n ReconcileAllTasksOptions,\n ReconcileFailure,\n ReconcileResult,\n ReconcileTaskInput,\n RefreshLinkageInput,\n RefreshLinkageResult,\n SessionEntry,\n SessionSkipReason,\n SuspectReason,\n TaskDocument,\n TaskSkipReason,\n TaskWriteAfterEventPhase,\n UpdateAdHocTaskStatusInput,\n UpdateTaskStatusInput,\n UpdateTaskStatusResult,\n WriteTaskFileMode,\n} from \"./storage/index.js\";\nexport {\n acquireLock,\n appendBasouGitignore,\n appendEventToExistingSession,\n archiveTask,\n assertBasouRootSafe,\n basouPaths,\n buildStatusSnapshot,\n classifySuspect,\n createAdHocSessionWithEvent,\n createManifest,\n createTaskWithEvent,\n deleteTask,\n editTask,\n ensureBasouDirectory,\n enumerateArchivedTaskIds,\n enumerateSessionDirs,\n enumerateTaskIds,\n FailedToFinalizeError,\n findErrorCode,\n GENERATED_END,\n GENERATED_START,\n importSessionFromJson,\n linkYamlFile,\n loadSessionEntries,\n loadTaskEntries,\n overwriteYamlFile,\n parseMarkers,\n readManifest,\n readMarkdownFile,\n readSessionYaml,\n readStatus,\n readTaskFile,\n readTaskFileWithArchiveFallback,\n readYamlFile,\n reconcileAllTasks,\n reconcileTask,\n refreshTaskLinkedSessions,\n renderWithMarkers,\n STUCK_THRESHOLD_MS,\n TaskWriteAfterEventError,\n updateTaskStatusWithEvent,\n writeManifest,\n writeMarkdownFile,\n writeStatus,\n writeTaskFile,\n writeYamlFile,\n} from \"./storage/index.js\";\n"],"mappings":"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","names":["resolve","path","readString","payload","isObject","commandExecutedEvent","sessionStartedEvent","sessionEndedEvent","readNonNegInt","baseEvent","z","z","join","join","z","z","join","readdir","join","z","z","readdir","join","join","path","join","join","z","z","stat","hasErrorCode","path","join","mkdir","readdir","readFile","rename","stat","unlink","join","z","z","z","join","join","readFile","unlink","join","unlink","readFile","ulid","join","readFile","join","z","z","join","readFile","DEFAULT_ATTACHABLE_STATUSES","z","join","readFile","readdir","stat","eventId","unlink","mkdir","rename","join","spawn","spawn","resolve","z","z","z","z","join","join","lstat","mkdir","join","hasErrorCode","readFile","writeFile","join","hasErrorCode","lstat","lstat","hasErrorCode","readFile","readFile","hasErrorCode","mkdir","rm","homedir","join","join","mkdir","rm","homedir"]}
|