/** * `map_in_flight_conflicts` — the cross-actor interference map (change: * add-cross-actor-interference-map, PARALLEL-WORK-COORDINATION proposal 4, the * team wedge). * * Generalizes `plan_parallel_work` (proposal 2) from "N agent tasks I am about to * dispatch" to "every change in flight right now" — local branches, open pull * requests, and caller-supplied agent task descriptors alike, within and across a * federation of repos. Each in-flight change becomes an actor-attributed node whose * footprint (proposal 1) is derived from its ACTUAL diff, and the same pairwise * hazard classifier (proposal 1) runs across all nodes. The output is a conclusion: * per change, the other in-flight changes it conflicts with, the hazard class, the * witnessing symbols, and a suggested landing order. * * Design invariants (mirror the proposal's Decision + Scope contract): * - **Read-only, stateless, no new graph.** A pure function of current git state + * the indexed graphs at call time. No watcher, no poll, no persisted conflict * store, no new node/edge schema — footprints ride existing primitives. * - **Observed, not declared.** A change's write-set comes from mapping its diff * hunks onto the enclosing symbols of the BASE snapshot, with a per-symbol * `writeMode` read off the diff itself: a symbol touched only by pure-insertion * hunks is an `append`, one touched by any deletion/modification is a `modify`. * This makes registry-collision resolution automatic — two PRs that each append a * disjoint entry to the same dispatcher resolve to `shared-append`, not a WAW, with * no `writeMode` declaration needed (the diffs are observable). * - **Honest about what it cannot see.** A PR whose diff cannot be fetched, a target * repo whose index is stale/missing, or a change whose symbols do not resolve is a * clearly-labeled "not assessed" node — NEVER a false "no conflict". * - **Advisory by default.** WAW conflicts are emitted as policy-shaped * `GovernanceFinding`s (`cross-actor-conflict`) so a caller/CI can classify them * with `resolveEnforcementClass` and choose to block; this tool blocks nothing. * - **Degrades to single-repo.** Federation is opt-in; with none configured the map * covers this repo's own branches, local PRs, and supplied descriptors. */ import { type WriteMember, type HazardVerdict, type TaskDescriptor } from './change-footprint.js'; import type { GovernanceFinding } from './enforcement-policy.js'; /** Upper bound on in-flight changes assessed in one call (branches + PRs + tasks). */ export declare const MAX_CHANGES = 40; export type ActorKind = 'branch' | 'pull-request' | 'agent-task'; /** A stable reason a change could not be structurally assessed — never a false "no conflict". */ export type NotAssessedReason = 'diff-unfetchable' | 'no-resolvable-symbols' | 'index-stale' | 'index-missing'; export interface InterferenceMapInput { directory: string; /** Git ref every change is diffed against (default: the repo's resolved default branch). */ baseRef?: string; /** Include local branches ahead of the base (default true). */ includeBranches?: boolean; /** Restrict branch enumeration to these branch names (default: all ahead of base). */ branches?: string[]; /** Include open pull requests via `gh` (default true; absent `gh` degrades with a caveat). */ includePullRequests?: boolean; /** Caller-supplied agent task descriptors that join the graph as first-class nodes. */ tasks?: TaskDescriptor[]; /** Cap on assessed changes (branches + PRs + tasks). Default {@link MAX_CHANGES}. */ maxChanges?: number; /** Forwarded to the footprint projection. */ readMaxDistance?: number; affectedMaxDepth?: number; ambientFanInPercentile?: number; /** Opt-in: extend the map across federated repos (.openlore/federation.json). */ federation?: boolean; /** Limit federation scope to these registry repo names (default: all resolvable). */ federationRepos?: string[]; } /** One in-flight change in the map (assessed or not), actor-attributed. */ export interface ChangeNode { actor: string; /** branch name, "PR #210", or the task id. */ ref: string; repo: string; kind: ActorKind; assessed: boolean; /** When assessed: the count of changed code files that produced the footprint. */ changedFiles?: number; /** When assessed: the size of the derived write-set. */ writeSetCount?: number; /** When not assessed: the stable reason + detail (never a false "no conflict"). */ reason?: NotAssessedReason; detail?: string; } /** One pairwise conflict between two in-flight changes (supporting evidence). */ export interface InterferenceConflict { a: { actor: string; ref: string; repo: string; }; b: { actor: string; ref: string; repo: string; }; hazard: HazardVerdict['kind']; direction?: HazardVerdict['direction']; crossRepo: boolean; /** Witnessing symbol ids (cross-repo: shared content-addressed stable ids), sorted. */ witnesses: string[]; /** Plain-language landing order suggestion. */ suggestion: string; } export interface InterferenceMap { baseRef: string; resolvedBaseRef: string; /** Repos assessed (this repo, plus any resolvable federated targets). */ repos: string[]; changeCount: number; assessedCount: number; notAssessedCount: number; changes: ChangeNode[]; /** Non-`none` pairwise verdicts, capped — see `conflictCount`. */ conflicts: InterferenceConflict[]; conflictCount: number; conflictsTruncated: boolean; /** WAW conflicts as policy-shaped findings a caller/CI can classify. Capped — see `findingCount`. */ findings: GovernanceFinding[]; findingCount: number; findingsTruncated: boolean; posture: 'advisory'; caveats: string[]; disclosure: string; headline: string; /** Set only when a large map was shrunk to fit the response budget. */ truncationNote?: string; } /** One unified-diff hunk, reduced to what the footprint needs (old-side lines + nature). */ export interface DiffHunk { /** First old-file line the hunk touches (1-based). For a pure insertion, the line it follows. */ oldStart: number; /** Count of old-file lines in the hunk (0 for a pure insertion). */ oldCount: number; /** True iff the hunk deletes or modifies existing lines (any `-` line) — i.e. not a pure append. */ hasDeletions: boolean; } /** A changed file's hunks plus its git status (the diff for one path). */ export interface FileHunks { path: string; status: 'added' | 'modified' | 'deleted' | 'renamed'; /** For a rename, the path the file lived at in the base ref (where its old content is). */ oldPath?: string; hunks: DiffHunk[]; } /** * Parse a unified diff (from `git diff` or `gh pr diff`) into per-file hunks, * recording only the old-side line ranges and whether each hunk deletes/modifies a * line. Deterministic and U0/U3-agnostic: a hunk's nature is read from whether it * carries any `-` line, so a context-bearing (default) diff and a `--unified=0` diff * classify identically. */ export declare function parseUnifiedDiff(patch: string): FileHunks[]; /** Base-snapshot symbols of a changed file, carrying line range + stable id for cross-repo match. */ export interface BaseSymbol { id: string; name: string; filePath: string; startLine: number; endLine: number; stableId?: string; } /** A write-set member enriched with its content-addressed stable id (for cross-repo matching). */ export interface FederatedWriteMember extends WriteMember { stableId?: string; } /** * The observed write-set of a change: each base symbol its diff hunks touch, with a * per-symbol writeMode read off the diff (a symbol touched only by pure-insertion * hunks is `append`; any deletion/modification makes it `modify`). `modify` * dominates `append` when a symbol has both kinds of hunk. Pure — the testable core. * * `baseSymbolsByFile` keys every changed CODE file (a file with no function symbols * maps to `[]`); a non-code file (docs/config) is absent, so it contributes nothing. * A hunk that touches no function symbol is a MODULE-SCOPE edit (a top-level registry * array/object literal has no function node). For a pure-insertion such hunk — the * canonical "two PRs each append a disjoint entry to the same registry" case — we add a * FILE-SCOPE write member so the collision is observed and resolves to `shared-append` * (mergeable), never a false WAW. A module-scope MODIFY is deliberately NOT attributed * to a file-scope member: at file granularity it would over-couple disjoint top-level * edits into a spurious WAW, and the proposal prefers a missed module-scope-modify * (rare) over a false "must serialize" (noisy). Function-body edits keep symbol * granularity. */ export declare function writeSetFromHunks(files: readonly FileHunks[], baseSymbolsByFile: ReadonlyMap): FederatedWriteMember[]; /** A raw in-flight change as gathered by a provider, before footprint derivation. */ export interface RawChange { actor: string; ref: string; repo: string; kind: ActorKind; /** Parsed diff hunks (empty when fetchError is set). */ files: FileHunks[]; /** Base-snapshot symbols by changed-file path (the provider does the re-parse I/O). */ baseSymbolsByFile: Map; /** Set when the change's diff could not be fetched → a "not assessed" node. */ fetchError?: string; /** Changed code files whose BASE content could not be read (their symbols are absent * from the write-set) — surfaced as a caveat so the partial assessment is honest. */ unreadableFiles?: string[]; } export interface InFlightProviders { /** Enumerate local branch changes ahead of base in a repo. */ enumerateBranches(repoPath: string, repoName: string, baseRef: string, only?: string[]): Promise; /** Enumerate open pull requests in a repo (via gh). Resolves to [] when gh is absent. */ enumeratePullRequests(repoPath: string, repoName: string, baseRef: string): Promise; /** Whether `gh` is available at all (drives the "PRs not enumerated" caveat). */ ghAvailable(repoPath: string): Promise; } export declare function computeInterferenceMap(input: InterferenceMapInput, providers?: InFlightProviders): Promise; /** MCP dispatch entry. */ export declare function handleMapInFlightConflicts(input: InterferenceMapInput): Promise; //# sourceMappingURL=interference-map.d.ts.map