@basou/core 0.26.0 → 0.27.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 CHANGED
@@ -4068,6 +4068,103 @@ declare function planRename(input: {
4068
4068
  oldIsAnchor?: boolean;
4069
4069
  }): RenamePlan;
4070
4070
 
4071
+ /**
4072
+ * Retrofit an existing repo's hand-authored `AGENTS.md` into the project's
4073
+ * "saddle" topology. The greenfield flow (`project new` → `project derive`)
4074
+ * assumes the canonical instruction file is born in the anchor at
4075
+ * `agents/<repo>/AGENTS.md`; but a repo that was developed BEFORE adoption
4076
+ * carries its instructions as a plain regular file at `<repo>/AGENTS.md`. The
4077
+ * other generators never relocate it — `project symlinks` is non-destructive and
4078
+ * skips an occupied path, `project preset` would create a near-empty canonical
4079
+ * beside the orphaned prose. Retrofit is the one missing migration step: it moves
4080
+ * that regular file to the anchor canonical and leaves a symlink in its place, so
4081
+ * the prose is preserved at the single source of truth and `project derive` can
4082
+ * finish the wiring (CLAUDE.md / Copilot spokes, `.gitignore`, the preset block).
4083
+ *
4084
+ * Pure: it CLASSIFIES already-gathered facts (is the file a regular file? does the
4085
+ * destination canonical already exist?) into one action. The realpath / lstat /
4086
+ * move / symlink I/O is the caller's job. Non-destructive by contract: it only
4087
+ * relocates a genuine regular-file AGENTS.md into a FREE canonical slot; an
4088
+ * existing canonical (which would be clobbered), an already-wired symlink, an
4089
+ * absent file, the anchor itself, or an unreachable/undeclared repo all yield a
4090
+ * refuse/skip — never a move.
4091
+ */
4092
+ /**
4093
+ * On-disk state of the repo's own `AGENTS.md` (the file to relocate), as seen by
4094
+ * `lstat` (never following the link). `regular-file` is the only relocatable
4095
+ * state; `symlink` means it is already wired, `absent` means there is nothing to
4096
+ * move, and `blocked` means the path could not be inspected (a non-ENOENT error)
4097
+ * so it must not be mistaken for relocatable.
4098
+ */
4099
+ type RetrofitAgentsState = "regular-file" | "symlink" | "absent" | "blocked";
4100
+ /** The single action retrofit will take for the repo. */
4101
+ type RetrofitAction = "relocate" | "skip" | "refuse";
4102
+ /** Why {@link classifyRetrofit} chose its action (machine-stable; the caller renders prose). */
4103
+ type RetrofitReason =
4104
+ /** relocate: a regular-file AGENTS.md with a free destination canonical. */
4105
+ "ok"
4106
+ /** refuse: the repo is not in the declared roster. */
4107
+ | "not-declared"
4108
+ /** refuse: the path is the project anchor (it owns the canonical directly — nothing to relocate). */
4109
+ | "anchor"
4110
+ /** refuse: the path does not resolve / is not a git repo. */
4111
+ | "unreachable"
4112
+ /** refuse: the AGENTS.md path could not be inspected (a non-ENOENT lstat error). */
4113
+ | "blocked"
4114
+ /** refuse: the destination canonical already exists (relocating would clobber it). */
4115
+ | "canonical-exists"
4116
+ /** skip: AGENTS.md is already a symlink (likely already wired — idempotent). */
4117
+ | "already-symlink"
4118
+ /** skip: there is no AGENTS.md to relocate. */
4119
+ | "absent";
4120
+ /** The gathered facts for the one repo being retrofitted. Pure inputs — no I/O. */
4121
+ type RetrofitFacts = {
4122
+ /** The repo's roster path (relative to the anchor), echoed in the report. */
4123
+ path: string;
4124
+ /** True when the path is declared in the manifest roster. */
4125
+ declared: boolean;
4126
+ /** True when the path resolves to the anchor itself. */
4127
+ isAnchor: boolean;
4128
+ /** False when the path does not resolve / is not a git repo. */
4129
+ reachable: boolean;
4130
+ /** The repo basename used for the anchor canonical `agents/<canonicalName>/AGENTS.md`. */
4131
+ canonicalName: string;
4132
+ /** On-disk state of the repo's own `AGENTS.md`. */
4133
+ agentsState: RetrofitAgentsState;
4134
+ /** True when the destination canonical already exists (moving would clobber it). */
4135
+ canonicalExists: boolean;
4136
+ /**
4137
+ * The repo's spoke instruction files (`CLAUDE.md`, `.github/copilot-instructions.md`)
4138
+ * that are regular files — they would block clean wiring (`project symlinks`
4139
+ * skips an occupied path), so they are surfaced as a manual checklist. Never
4140
+ * moved by retrofit (they are spokes to AGENTS.md, not separate canonicals).
4141
+ */
4142
+ regularSpokes: string[];
4143
+ };
4144
+ /** The classified outcome for the repo. */
4145
+ type RetrofitPlan = {
4146
+ path: string;
4147
+ action: RetrofitAction;
4148
+ reason: RetrofitReason;
4149
+ /** The repo basename used for the canonical (echoed for the report). */
4150
+ canonicalName: string;
4151
+ /** The anchor-relative destination `agents/<canonicalName>/AGENTS.md`; set only when `action` is `relocate`. */
4152
+ canonicalPath?: string;
4153
+ /** Spoke instruction files that are regular files (reported, never moved). */
4154
+ regularSpokes: string[];
4155
+ };
4156
+ /**
4157
+ * Classify the retrofit facts into one action. Refusals are checked first, in a
4158
+ * fixed precedence so the outcome is deterministic when several guardrails could
4159
+ * apply: undeclared → anchor → unreachable → uninspectable AGENTS.md. Then the
4160
+ * idempotent skips (already a symlink, or absent — nothing to move). Only a
4161
+ * genuine regular-file AGENTS.md reaches the relocate decision, and even then a
4162
+ * pre-existing destination canonical refuses (relocating would clobber it).
4163
+ * `regularSpokes` is echoed in every outcome (it is advisory, relevant whenever a
4164
+ * relocate or skip leaves the operator to tidy the spokes).
4165
+ */
4166
+ declare function classifyRetrofit(facts: RetrofitFacts): RetrofitPlan;
4167
+
4071
4168
  /**
4072
4169
  * Plan the agent instruction-file symlinks a declared repo needs (the
4073
4170
  * generation step that follows `basou project gitignore` in the "saddle"
@@ -5622,4 +5719,4 @@ declare function overwriteYamlFile(filePath: string, value: unknown): Promise<vo
5622
5719
  */
5623
5720
  declare const BASOU_CORE_VERSION = "0.1.0";
5624
5721
 
5625
- export { ACTIVE_GAP_CAP_MS, AGENT_INFRA_DIRS, type ActiveTimeBasis, type AdapterOutputEvent, type AdoptCandidate, type AdoptCandidateKind, type AppendBasouGitignoreOptions, type AppendBasouGitignoreResult, type AppendEventToExistingInput, type AppendEventToExistingResult, type Approval, type ApprovalApprovedEvent, type ApprovalExpiredEvent, ApprovalIdSchema, type ApprovalLocation, type ApprovalRejectedEvent, type ApprovalRequestedEvent, ApprovalSchema, type ApprovalStatus, ApprovalStatusSchema, type ArchivePlan, type ArchiveTaskInput, type ArchiveTaskResult, type AttachTaskInput, type AttachUpdateTaskStatusInput, type AttachableStatus, BASOU_CORE_VERSION, type BasouPaths, type BulkChainResult, CLAUDE_IMPORT_SOURCE, CODEX_IMPORT_SOURCE, type CaptureMode, type ChainBreakReason, type ChainTailState, type ChainVerdict, type ChainVerdictStatus, type ChainedEvents, ChildProcessRunner, type CitedReview, type ClaudeTranscriptRecord, type ClaudeTranscriptToPayloadOptions, type CodexRolloutRecord, type CodexRolloutToPayloadOptions, type CommandExecutedEvent, type CommandLookup, type CreateAdHocSessionInput, type CreateAdHocSessionResult, type CreateAdHocTaskInput, type CreateManifestInput, type CreateTaskInput, type CreateTaskResult, DEFAULT_STOP_HOOK_MIN_EDITS, type DayWorkStats, DecisionIdSchema, type DecisionRecordedEvent, type DecisionsRendererInput, type DecisionsRendererResult, type DeleteTaskInput, type DeleteTaskResult, type DiffResult, type EditTaskInput, type EditTaskResult, type Event, EventIdSchema, EventSchema, EventSourceSchema, type ExistingViewLink, FailedToFinalizeError, type FederatedRoot, type FileChange, type FileChangeStatus, type FileChangedEvent, GENERATED_END, GENERATED_START, type GitSnapshot, type GitSnapshotEvent, type GitignorePlanSummary, type HandoffRendererInput, type HandoffRendererResult, ID_PREFIXES, type IdPrefix, type ImportSessionOptions, type ImportSessionResult, type InstructionFileFact, type InstructionSymlinkFact, type InstructionSymlinkState, IsoTimestampSchema, JSON_SCHEMA_VERSION, type JsonSchemaArtifact, type LoadFederatedOptions, type LoadSessionEntriesOptions, type LoadTaskEntriesOptions, type LoadedApproval, type LockHandle, type LockScope, type Manifest, ManifestSchema, type MarkerSection, type Markers, type MeasureAvailability, type NoteAddedEvent, type OrientationRendererInput, type OrientationRendererResult, type OrientationSummary, PROTOCOL_END, PROTOCOL_START, type PrefixedId, type PresetAction, type PresetCollision, type PresetMarkerConflict, type PresetMarkerKind, type PresetPlanSummary, type PresetRepo, type ProcessRunner, type PublishKind, type PublishTarget, type RechainOptions, type RechainResult, type ReconcileAllResult, type ReconcileAllTasksInput, type ReconcileAllTasksOptions, type ReconcileFailure, type ReconcileResult, type ReconcileTaskInput, type RefreshLinkageInput, type RefreshLinkageResult, type ReimportOptions, type ReimportResult, type RenamePlan, type ReplayOptions, type ReplayWarning, type RepoEntry, type RepoGitignoreFacts, type RepoGitignorePlan, type RepoLanguage, type RepoPresetFacts, type RepoPresetPlan, type RepoSymlinkFacts, type RepoSymlinkPlan, type RepoVisibility, type RepoWiringFacts, type ReportApprovalItem, type ReportData, type ReportDecisionItem, type ReportRendererInput, type ReportRendererResult, type ReportSessionItem, type ReportTaskItem, type ReviewGapRepoSummary, type ReviewGapUnit, type ReviewGapVerdict, type ReviewGapsInput, type ReviewGapsSummary, type RiskLevel, RiskLevelSchema, type RosterAdoptionPlan, type RosterDriftSummary, type RunOptions, type RunResult, STUCK_THRESHOLD_MS, type SanitizePathOptions, type SanitizeRelatedFilesResult, SchemaVersionSchema, type Session, type SessionEndedEvent, type SessionEntry, SessionIdSchema, type SessionImportPayload, SessionImportPayloadSchema, type SessionInnerImportInput, SessionInnerImportSchema, type SessionIntegrity, SessionIntegritySchema, type SessionMetrics, SessionMetricsSchema, SessionSchema, type SessionSkipReason, type SessionSourceKind, SessionSourceKindSchema, type SessionStartedEvent, type SessionStatus, type SessionStatusChangedEvent, SessionStatusSchema, type SessionWorkStats, type SourceRootScope, type SourceRootsReconcile, type SourceWorkStats, type StatusCount, StatusSchema, type StatusSnapshot, type StopHookEvaluation, type StopHookEvaluationInput, type StopHookSilentReason, type SuspectReason, type SymlinkCollision, type SymlinkConflict, type SymlinkPlanSummary, type Task, type TaskArchivedEvent, type TaskCreatedEvent, type TaskDeletedEvent, type TaskDocument, TaskIdSchema, type TaskLinkageRefreshedEvent, type TaskReconciledEvent, TaskSchema, type TaskSkipReason, type TaskStatus, type TaskStatusChangedEvent, type TaskStatusCount, TaskStatusSchema, TaskWriteAfterEventError, type TaskWriteAfterEventPhase, type TokenTotals, type UpdateAdHocTaskStatusInput, type UpdateTaskStatusInput, type UpdateTaskStatusResult, type ViewCollision, type ViewConflict, type ViewLinkState, type ViewRepoFact, type ViewStrayUnknown, type WiringRisk, type WiringSummary, type WorkStatsInput, type WorkStatsResult, type WorkStatsTotals, WorkspaceIdSchema, type WorkspaceViewPlan, type WriteEventsBulkOptions, type WriteTaskFileMode, acquireLock, appendBasouGitignore, appendChainedEvent, appendChainedEventLocked, appendEvent, appendEventToExistingSession, archiveTask, assertBasouRootSafe, basouPaths, buildJsonSchemas, buildStatusSnapshot, chainEvents, chainRawJsonLines, classifyFilesBySourceRoot, classifySuspect, claudeCodeAdapterMetadata, claudeTranscriptToImportPayload, codexRolloutToImportPayload, computeWorkStats, createAdHocSessionWithEvent, createManifest, createTaskWithEvent, deleteTask, editTask, ensureBasouDirectory, enumerateApprovals, enumerateArchivedTaskIds, enumerateSessionDirs, enumerateTaskIds, evaluateStopHook, finalizeSessionYaml, findErrorCode, findReviewGaps, formatDurationMs, genesisHash, getDiff, getSnapshot, importSessionFromJson, inspectChainTail, isGitNotFound, isImportDerivedSource, isLazyExpired, isRenderable, isValidPrefixedId, lineHash, linkYamlFile, loadApproval, loadFederatedSessionEntries, loadSessionEntries, loadTaskEntries, normalizeRepoKey, normalizeRepoPath, overwriteYamlFile, parseDuration, parseMarkers, pathBasename, planArchive, planGitignore, planRename, planRosterAdoption, planWorkspaceView, prefixedUlid, readAllEvents, readManifest, readMarkdownFile, readSessionYaml, readStatus, readTaskFile, readTaskFileWithArchiveFallback, readYamlFile, rechainSessionInPlace, reconcileAllTasks, reconcileSourceRoots, reconcileTask, refreshTaskLinkedSessions, reimportPreservingId, removeMarkerSection, renderDecisions, renderHandoff, renderOrientation, renderPresetBlock, renderReport, renderWithMarkers, replayEvents, resolveBasouRepositoryRoot, resolveClaudeCodeCommand, resolveRepositoryRoot, resolveSessionId, resolveTaskId, safeSimpleGit, sanitizePath, sanitizeRelatedFiles, sanitizeWorkingDirectory, serializeEventLine, serializeJsonSchema, sessionWorkStatsFromEvents, summarizeAdapterOutput, summarizeOrientation, summarizePresetPlan, summarizeRosterDrift, summarizeSymlinkPlan, summarizeWiring, tryRemoteUrl, ulid, unknownManifestKeys, updateTaskStatusWithEvent, verifyEventsChain, writeEventsBulk, writeManifest, writeMarkdownFile, writeStatus, writeTaskFile, writeYamlFile };
5722
+ export { ACTIVE_GAP_CAP_MS, AGENT_INFRA_DIRS, type ActiveTimeBasis, type AdapterOutputEvent, type AdoptCandidate, type AdoptCandidateKind, type AppendBasouGitignoreOptions, type AppendBasouGitignoreResult, type AppendEventToExistingInput, type AppendEventToExistingResult, type Approval, type ApprovalApprovedEvent, type ApprovalExpiredEvent, ApprovalIdSchema, type ApprovalLocation, type ApprovalRejectedEvent, type ApprovalRequestedEvent, ApprovalSchema, type ApprovalStatus, ApprovalStatusSchema, type ArchivePlan, type ArchiveTaskInput, type ArchiveTaskResult, type AttachTaskInput, type AttachUpdateTaskStatusInput, type AttachableStatus, BASOU_CORE_VERSION, type BasouPaths, type BulkChainResult, CLAUDE_IMPORT_SOURCE, CODEX_IMPORT_SOURCE, type CaptureMode, type ChainBreakReason, type ChainTailState, type ChainVerdict, type ChainVerdictStatus, type ChainedEvents, ChildProcessRunner, type CitedReview, type ClaudeTranscriptRecord, type ClaudeTranscriptToPayloadOptions, type CodexRolloutRecord, type CodexRolloutToPayloadOptions, type CommandExecutedEvent, type CommandLookup, type CreateAdHocSessionInput, type CreateAdHocSessionResult, type CreateAdHocTaskInput, type CreateManifestInput, type CreateTaskInput, type CreateTaskResult, DEFAULT_STOP_HOOK_MIN_EDITS, type DayWorkStats, DecisionIdSchema, type DecisionRecordedEvent, type DecisionsRendererInput, type DecisionsRendererResult, type DeleteTaskInput, type DeleteTaskResult, type DiffResult, type EditTaskInput, type EditTaskResult, type Event, EventIdSchema, EventSchema, EventSourceSchema, type ExistingViewLink, FailedToFinalizeError, type FederatedRoot, type FileChange, type FileChangeStatus, type FileChangedEvent, GENERATED_END, GENERATED_START, type GitSnapshot, type GitSnapshotEvent, type GitignorePlanSummary, type HandoffRendererInput, type HandoffRendererResult, ID_PREFIXES, type IdPrefix, type ImportSessionOptions, type ImportSessionResult, type InstructionFileFact, type InstructionSymlinkFact, type InstructionSymlinkState, IsoTimestampSchema, JSON_SCHEMA_VERSION, type JsonSchemaArtifact, type LoadFederatedOptions, type LoadSessionEntriesOptions, type LoadTaskEntriesOptions, type LoadedApproval, type LockHandle, type LockScope, type Manifest, ManifestSchema, type MarkerSection, type Markers, type MeasureAvailability, type NoteAddedEvent, type OrientationRendererInput, type OrientationRendererResult, type OrientationSummary, PROTOCOL_END, PROTOCOL_START, type PrefixedId, type PresetAction, type PresetCollision, type PresetMarkerConflict, type PresetMarkerKind, type PresetPlanSummary, type PresetRepo, type ProcessRunner, type PublishKind, type PublishTarget, type RechainOptions, type RechainResult, type ReconcileAllResult, type ReconcileAllTasksInput, type ReconcileAllTasksOptions, type ReconcileFailure, type ReconcileResult, type ReconcileTaskInput, type RefreshLinkageInput, type RefreshLinkageResult, type ReimportOptions, type ReimportResult, type RenamePlan, type ReplayOptions, type ReplayWarning, type RepoEntry, type RepoGitignoreFacts, type RepoGitignorePlan, type RepoLanguage, type RepoPresetFacts, type RepoPresetPlan, type RepoSymlinkFacts, type RepoSymlinkPlan, type RepoVisibility, type RepoWiringFacts, type ReportApprovalItem, type ReportData, type ReportDecisionItem, type ReportRendererInput, type ReportRendererResult, type ReportSessionItem, type ReportTaskItem, type RetrofitAction, type RetrofitAgentsState, type RetrofitFacts, type RetrofitPlan, type RetrofitReason, type ReviewGapRepoSummary, type ReviewGapUnit, type ReviewGapVerdict, type ReviewGapsInput, type ReviewGapsSummary, type RiskLevel, RiskLevelSchema, type RosterAdoptionPlan, type RosterDriftSummary, type RunOptions, type RunResult, STUCK_THRESHOLD_MS, type SanitizePathOptions, type SanitizeRelatedFilesResult, SchemaVersionSchema, type Session, type SessionEndedEvent, type SessionEntry, SessionIdSchema, type SessionImportPayload, SessionImportPayloadSchema, type SessionInnerImportInput, SessionInnerImportSchema, type SessionIntegrity, SessionIntegritySchema, type SessionMetrics, SessionMetricsSchema, SessionSchema, type SessionSkipReason, type SessionSourceKind, SessionSourceKindSchema, type SessionStartedEvent, type SessionStatus, type SessionStatusChangedEvent, SessionStatusSchema, type SessionWorkStats, type SourceRootScope, type SourceRootsReconcile, type SourceWorkStats, type StatusCount, StatusSchema, type StatusSnapshot, type StopHookEvaluation, type StopHookEvaluationInput, type StopHookSilentReason, type SuspectReason, type SymlinkCollision, type SymlinkConflict, type SymlinkPlanSummary, type Task, type TaskArchivedEvent, type TaskCreatedEvent, type TaskDeletedEvent, type TaskDocument, TaskIdSchema, type TaskLinkageRefreshedEvent, type TaskReconciledEvent, TaskSchema, type TaskSkipReason, type TaskStatus, type TaskStatusChangedEvent, type TaskStatusCount, TaskStatusSchema, TaskWriteAfterEventError, type TaskWriteAfterEventPhase, type TokenTotals, type UpdateAdHocTaskStatusInput, type UpdateTaskStatusInput, type UpdateTaskStatusResult, type ViewCollision, type ViewConflict, type ViewLinkState, type ViewRepoFact, type ViewStrayUnknown, type WiringRisk, type WiringSummary, type WorkStatsInput, type WorkStatsResult, type WorkStatsTotals, WorkspaceIdSchema, type WorkspaceViewPlan, type WriteEventsBulkOptions, type WriteTaskFileMode, acquireLock, appendBasouGitignore, appendChainedEvent, appendChainedEventLocked, appendEvent, appendEventToExistingSession, archiveTask, assertBasouRootSafe, basouPaths, buildJsonSchemas, buildStatusSnapshot, chainEvents, chainRawJsonLines, classifyFilesBySourceRoot, classifyRetrofit, classifySuspect, claudeCodeAdapterMetadata, claudeTranscriptToImportPayload, codexRolloutToImportPayload, computeWorkStats, createAdHocSessionWithEvent, createManifest, createTaskWithEvent, deleteTask, editTask, ensureBasouDirectory, enumerateApprovals, enumerateArchivedTaskIds, enumerateSessionDirs, enumerateTaskIds, evaluateStopHook, finalizeSessionYaml, findErrorCode, findReviewGaps, formatDurationMs, genesisHash, getDiff, getSnapshot, importSessionFromJson, inspectChainTail, isGitNotFound, isImportDerivedSource, isLazyExpired, isRenderable, isValidPrefixedId, lineHash, linkYamlFile, loadApproval, loadFederatedSessionEntries, loadSessionEntries, loadTaskEntries, normalizeRepoKey, normalizeRepoPath, overwriteYamlFile, parseDuration, parseMarkers, pathBasename, planArchive, planGitignore, planRename, planRosterAdoption, planWorkspaceView, prefixedUlid, readAllEvents, readManifest, readMarkdownFile, readSessionYaml, readStatus, readTaskFile, readTaskFileWithArchiveFallback, readYamlFile, rechainSessionInPlace, reconcileAllTasks, reconcileSourceRoots, reconcileTask, refreshTaskLinkedSessions, reimportPreservingId, removeMarkerSection, renderDecisions, renderHandoff, renderOrientation, renderPresetBlock, renderReport, renderWithMarkers, replayEvents, resolveBasouRepositoryRoot, resolveClaudeCodeCommand, resolveRepositoryRoot, resolveSessionId, resolveTaskId, safeSimpleGit, sanitizePath, sanitizeRelatedFiles, sanitizeWorkingDirectory, serializeEventLine, serializeJsonSchema, sessionWorkStatsFromEvents, summarizeAdapterOutput, summarizeOrientation, summarizePresetPlan, summarizeRosterDrift, summarizeSymlinkPlan, summarizeWiring, tryRemoteUrl, ulid, unknownManifestKeys, updateTaskStatusWithEvent, verifyEventsChain, writeEventsBulk, writeManifest, writeMarkdownFile, writeStatus, writeTaskFile, writeYamlFile };
package/dist/index.js CHANGED
@@ -5628,6 +5628,30 @@ function planRename(input) {
5628
5628
  };
5629
5629
  }
5630
5630
 
5631
+ // src/project/retrofit.ts
5632
+ var CANONICAL_FILE = "AGENTS.md";
5633
+ function classifyRetrofit(facts) {
5634
+ const base = {
5635
+ path: facts.path,
5636
+ canonicalName: facts.canonicalName,
5637
+ regularSpokes: facts.regularSpokes
5638
+ };
5639
+ if (!facts.declared) return { ...base, action: "refuse", reason: "not-declared" };
5640
+ if (facts.isAnchor) return { ...base, action: "refuse", reason: "anchor" };
5641
+ if (!facts.reachable) return { ...base, action: "refuse", reason: "unreachable" };
5642
+ if (facts.agentsState === "blocked") return { ...base, action: "refuse", reason: "blocked" };
5643
+ if (facts.agentsState === "symlink")
5644
+ return { ...base, action: "skip", reason: "already-symlink" };
5645
+ if (facts.agentsState === "absent") return { ...base, action: "skip", reason: "absent" };
5646
+ if (facts.canonicalExists) return { ...base, action: "refuse", reason: "canonical-exists" };
5647
+ return {
5648
+ ...base,
5649
+ action: "relocate",
5650
+ reason: "ok",
5651
+ canonicalPath: `agents/${facts.canonicalName}/${CANONICAL_FILE}`
5652
+ };
5653
+ }
5654
+
5631
5655
  // src/project/roster.ts
5632
5656
  function summarizeRosterDrift(input) {
5633
5657
  const captured = new Set((input.sourceRoots ?? []).map(normalizeRelativePath));
@@ -7644,6 +7668,7 @@ export {
7644
7668
  chainEvents,
7645
7669
  chainRawJsonLines,
7646
7670
  classifyFilesBySourceRoot,
7671
+ classifyRetrofit,
7647
7672
  classifySuspect,
7648
7673
  claudeCodeAdapterMetadata,
7649
7674
  claudeTranscriptToImportPayload,