# Feature Spec: Intentional Collision Override and Reconciler Handoff

> Purpose: Allow an operator to explicitly approve a known, intentional plan collision from the CLI without weakening default collision safety, while preserving enough structured intent for the reconciler agent to merge overlapping features correctly if git reconciliation later encounters conflicts.

**Version:** 1.0
**Date:** 2026-03-15
**Status:** Completed
**Roadmap Mapping:** M50
**Depends On:** Entry 69, Entry 90, Entry 91, `git-reconciliation-engine.md`

---

## 0. Scope and Standards

### 0.1 Primary User Job

When two active features intentionally modify the same file paths, the operator must be able to say:

1. this overlap is expected,
2. it is allowed for this specific feature pair,
3. the orchestrator may continue planning/building instead of blocking on `collision_detected`, and
4. if reconciliation later has to merge the overlapping edits, the reconciler must receive the original intent and approval rationale.

### 0.2 Concrete Failure Being Fixed

On **March 15, 2026**, feature `agentic_orchestrator_dashboard_command_center_redesign_spec` failed a `plan.update` with:

- `error.code: collision_detected`
- conflicting feature `agentic_orchestrator_feature_focus_view_ux_redesign_spec`
- overlapping paths:
  - `packages/web-dashboard/src/components/detail-panel.tsx`
  - `packages/web-dashboard/src/styles/dashboard.module.css`

This was a true collision according to the current plan rules, but it was also an intentional overlap. The orchestrator offered only three next actions:

1. acquire relevant lock and resubmit,
2. revise plan scope to avoid overlap,
3. create shared prerequisite feature.

None of those represent "this overlap is intentionally approved; keep going."

### 0.3 Design Standards

- Default behavior remains strict. Unapproved collisions still block exactly as they do today.
- Overrides must be explicit, pairwise, auditable, and narrow.
- Overrides must not silently disable protected-area checks or contract lock requirements.
- Reconciler handoff must use structured data, not only human prose.
- The solution must remain deterministic under index locking and optimistic plan concurrency.

---

## 1. Current Behavior and Gap

### 1.1 What Exists Today

Current collision behavior supports only two policy modes:

1. `collision_policy: reject`
   - immediately fail `plan.submit` / `plan.update` with `collision_detected`
2. `collision_policy: block`
   - place the colliding plan into `index.blocked_queue`
   - retry later only when the collision is no longer present

Current blocked-queue behavior is not a bypass. It is "wait until overlap disappears."

### 1.2 Why This Is Insufficient

Intentional overlaps are a real workflow in this repository:

- shared UX redesign work
- coordinated refactors across adjacent dashboard surfaces
- staged implementation where two specs intentionally touch the same component before reconciliation

In those cases, collision detection is correctly identifying risk, but the operator needs a controlled approval path rather than a hard block.

### 1.3 Reconciler Gap

The current reconciler receives:

- spec,
- plan,
- conflict metadata from `state.md`,
- diffs,
- conflicted files.

It does not receive:

- whether the overlap had been intentionally approved,
- which feature pair was approved,
- which file paths were intentionally shared,
- why the operator allowed the overlap,
- what behavior both features were expected to preserve.

That leaves the reconciler with only git conflict markers and generic feature intent, not the specific semantic instruction that the overlap was intentional.

---

## 2. Goals

### 2.1 Must Have

1. Add a CLI workflow to approve a specific active collision between two features.
2. Persist collision evidence even when policy is `reject`, so the CLI can act on the actual rejected collision.
3. Allow `plan.submit` / `plan.update` to proceed when the current collision is fully covered by an active override.
4. Keep the approval narrow by binding it to the feature pair and current collision fingerprint.
5. Record approval rationale and collision details in durable orchestrator artifacts.
6. Surface approved overlaps in status/reporting outputs.
7. Inject approved-collision context into reconciler handoff and prompt instructions.

### 2.2 Non-Goals

1. Do not add a global "disable collision detection" mode.
2. Do not auto-approve collisions based on heuristics.
3. Do not allow overrides to bypass:
   - protected-area rules,
   - patch plan conformance,
   - contract lock requirements,
   - gate execution.
4. Do not introduce a full semantic dependency system between features in this milestone.
5. Do not attempt to solve all multi-feature merge planning beyond the approved pairwise overlap use case.

---

## 3. Proposed User Experience

### 3.1 New CLI Command

Add a new top-level CLI command: `aop collision`

Supported actions:

1. `aop collision --allow --feature-id <A> --with-feature-id <B> --reason "<text>"`
2. `aop collision --list [--feature-id <A>]`
3. `aop collision --revoke --override-id <id>`

Optional flag:

- `--resume`
  - after recording the override, immediately retry the blocked feature or re-drive its pending collision record

### 3.2 Allow Command Semantics

`--allow` must not accept arbitrary free-form file lists in the first implementation.

Instead, it must resolve the current known collision from persisted collision evidence and approve exactly that collision:

1. locate the latest unresolved collision record for feature pair `<A,B>`,
2. verify that the collision currently exists,
3. compute or confirm the collision fingerprint,
4. create an active override bound to that fingerprint,
5. optionally retry the blocked submission when `--resume` is passed.

This avoids typo-prone, operator-authored path lists and keeps the approval tied to a real observed collision.

### 3.3 List Command Semantics

`--list` returns structured JSON with:

- override id,
- feature ids,
- status,
- created_at,
- approved collision fingerprint,
- approved file paths,
- reason,
- latest_seen_at,
- whether the override still matches a live collision,
- whether the override has already been applied successfully.

### 3.4 Revoke Command Semantics

`--revoke` marks the override inactive for future plan submissions.

Revocation does not retroactively invalidate already-accepted plans, but any later colliding plan revision must fail again unless a new matching override exists.

---

## 4. Persistence Model

### 4.1 Collision Records

Add durable collision records to the global index.

New `index.json` field:

```ts
type CollisionRecord = {
  collision_id: string;
  feature_id: string;
  with_feature_ids: string[];
  source_tool: 'plan.submit' | 'plan.update';
  plan_version: number;
  detected_at: string;
  collision_fingerprint: string;
  collisions: {
    files: Array<{ with_feature_id: string; paths: string[] }>;
    areas: Array<{ with_feature_id: string; overlaps: Array<[string, string]> }>;
    contracts: Array<{
      with_feature_id: string;
      contract: string;
      candidate_mode: string;
      other_mode: string;
    }>;
  };
  plan_snapshot: Record<string, unknown>;
  resolution: 'rejected' | 'blocked' | 'override_applied' | 'cleared' | 'superseded';
  resolution_details?: {
    override_id?: string;
    resolved_at?: string;
    note?: string;
  };
};
```

Behavior:

1. Every detected collision must create or update a collision record before returning an error.
2. This applies to both `reject` and `block` policies.
3. `blocked_queue` remains unchanged for `collision_policy: block`; collision records are additive audit state, not a replacement.

### 4.2 Collision Overrides

Add durable approved overrides to the global index.

New `index.json` field:

```ts
type CollisionOverride = {
  override_id: string;
  feature_ids: [string, string];
  created_at: string;
  created_by: string;
  rationale: string;
  approved_collision_fingerprint: string;
  approved_paths: string[];
  approved_with_feature_ids: string[];
  status: 'active' | 'stale' | 'revoked' | 'applied';
  last_seen_at?: string;
  applied_at?: string;
  source_collision_id: string;
};
```

Rules:

1. Feature ids must be stored in deterministic sorted order.
2. The override is valid only for the exact fingerprint captured at approval time.
3. If later plan changes alter the collision fingerprint, the override becomes `stale`, not silently reusable.
4. The override applies only to file and exclusive-area collisions approved by fingerprint.
5. Contract collisions remain non-overridable in this milestone.

### 4.3 Feature-Level Human Audit Trail

Whenever a collision record or override is created/applied/revoked, append a machine-readable log entry through `feature.log_append` or the equivalent state log path for both involved features.

Each entry must include:

- event type,
- feature pair,
- override id or collision id,
- fingerprint,
- approved paths,
- rationale if present,
- actor and timestamp.

---

## 5. Collision Evaluation Rules

### 5.1 Plan-Service Decision Flow

`PlanService.checkPlanCollision()` must change from:

1. detect collision,
2. reject or block,

to:

1. detect collision,
2. persist collision record,
3. reject immediately if any contract collision exists,
4. look for an active matching override,
5. if full collision set is covered by the override:
   - mark collision record `override_applied`,
   - mark override `applied`,
   - accept the plan,
6. otherwise continue existing `reject` or `block` behavior.

### 5.2 Matching Rules

An override matches only when all of the following are true:

1. the current feature pair matches the override feature pair,
2. current collision fingerprint matches `approved_collision_fingerprint`,
3. current collision contains no contract collisions,
4. override status is `active`,
5. the override has not been revoked or marked stale.

If any of those fail, the collision is treated as unapproved.

### 5.3 Staleness Rules

An override must be marked `stale` when:

1. the same feature pair collides again with a different fingerprint,
2. one of the two features is deleted,
3. one of the two plans is materially revised such that the original approved collision no longer exists.

Stale overrides remain in history but are not applied.

### 5.4 Relationship to Locks

Collision overrides do not change resource lock requirements.

Examples:

- If two plans overlap only on UI files, an approved override may allow both plans.
- If a plan also modifies `contracts.openapi` and requires the `openapi` lock, the override does not waive that lock.

---

## 6. CLI and Kernel API Surface

### 6.1 New Kernel Operations

Add kernel/service methods:

1. `collisionOverrideAllow(featureId, withFeatureId, reason, resume?)`
2. `collisionOverrideList(featureId?)`
3. `collisionOverrideRevoke(overrideId)`

Return envelope shape remains `{ ok, data/error }`.

### 6.2 Error Semantics

Add explicit error codes for the new flow:

- `collision_override_not_found`
- `collision_override_not_applicable`
- `collision_override_contract_forbidden`
- `collision_override_stale`
- `collision_record_not_found`

Suggested details:

- `retryable`
- `requires_human`
- `feature_id`
- `with_feature_id`
- `override_id`
- `collision_fingerprint`

### 6.3 CLI Output Contract

CLI responses should be structured, for example:

```json
{
  "ok": true,
  "data": {
    "command": "collision",
    "action": "allow",
    "override_id": "colovr_01...",
    "feature_ids": [
      "agentic_orchestrator_dashboard_command_center_redesign_spec",
      "agentic_orchestrator_feature_focus_view_ux_redesign_spec"
    ],
    "collision_fingerprint": "c9ac5e4c...",
    "approved_paths": [
      "packages/web-dashboard/src/components/detail-panel.tsx",
      "packages/web-dashboard/src/styles/dashboard.module.css"
    ],
    "resumed": true
  }
}
```

---

## 7. Reconciler Handoff Requirements

### 7.1 New Reconciler Context

When `git-reconciliation-service` dispatches a feature to the reconciler, it must include any active or previously applied intentional-collision metadata relevant to the feature.

Required handoff fields:

```ts
type ApprovedCollisionContext = {
  override_id: string;
  counterpart_feature_id: string;
  rationale: string;
  approved_paths: string[];
  approved_collision_fingerprint: string;
  source_collision_id: string;
  detected_at: string;
  applied_at?: string;
};
```

The reconciler should receive:

1. all active/applied overrides for the current feature,
2. the source collision record for each override,
3. the counterpart feature ids,
4. the approved overlapping paths,
5. the operator rationale.

### 7.2 Prompt Additions

Update the reconciler prompt so it explicitly understands:

1. some overlaps were intentionally approved,
2. those overlaps indicate both features are expected to survive the merge,
3. the operator rationale is authoritative context for what behavior must be preserved,
4. on approved paths, "drop one side and keep the other" is not acceptable unless clearly justified in the resolution log.

### 7.3 Resolution Guidance

For approved collisions, reconciler instructions must say:

- treat the approved overlap as a design constraint, not accidental churn,
- preserve the intended behavior of both features where technically feasible,
- document if one feature's intent can no longer be preserved because mainline semantics make the original overlap invalid.

### 7.4 Feature Log Requirements

Reconciler completion logs must include:

- whether approved-collision context was used,
- which override ids influenced the resolution,
- whether counterpart behavior was preserved,
- any residual compromise or follow-up work needed.

---

## 8. Reporting and Dashboard Requirements

### 8.1 Collision Reporting

`collisionsScan()` and feature summary/dashboard outputs must distinguish:

- unapproved active collisions,
- approved active collisions,
- stale overrides,
- revoked overrides.

### 8.2 Feature Summary Additions

`report.feature_summary` should include:

- active collision overrides for the feature,
- most recent collision records,
- whether the latest collision was approved, blocked, or rejected.

### 8.3 Dashboard Presentation

Dashboard work is not required in this milestone, but API/report payloads must be expanded so a later dashboard iteration can render:

- "approved overlap" badges,
- rationale tooltip,
- stale-override warning,
- counterpart feature link.

---

## 9. Implementation Plan

### 9.1 CLI

Update:

- `apps/control-plane/src/cli/types.ts`
- `apps/control-plane/src/cli/cli-argument-parser.ts`
- `apps/control-plane/src/interfaces/cli/bootstrap.ts`
- `apps/control-plane/src/cli/help-command-handler.ts`
- `AGENTS.md`
- `CLAUDE.md`

Add a new command handler:

- `apps/control-plane/src/cli/collision-command-handler.ts`

### 9.2 Core and Services

Update:

- `apps/control-plane/src/core/kernel.ts`
- `apps/control-plane/src/core/error-codes.ts`
- `apps/control-plane/src/application/services/plan-service.ts`
- `apps/control-plane/src/application/services/collision-queue-service.ts`
- `apps/control-plane/src/application/services/reporting-service.ts`
- `apps/control-plane/src/application/services/git-reconciliation-service.ts`

Add:

- `apps/control-plane/src/application/services/collision-override-service.ts`

### 9.3 Schemas and Defaults

Update:

- `agentic/orchestrator/schemas/index.schema.json`
- `config/agentic/orchestrator/policy.yaml` only if retention or behavior toggles are introduced
- `agentic/orchestrator/schemas/policy.schema.json` only if new config is introduced

### 9.4 Prompts

Update:

- `config/agentic/orchestrator/prompts/reconciler.system.md`
- `agentic/orchestrator/prompts/reconciler.system.md` if the repo keeps a template copy in parallel

### 9.5 Tests

Add or update tests for:

1. collision record persistence under `reject`
2. override creation from latest rejected collision
3. override match success for identical fingerprint
4. stale override rejection on changed fingerprint
5. contract collision remains blocked even with override
6. `--resume` retries accepted plan flow
7. reporting outputs include overrides and collision history
8. reconciler handoff includes approved collision context
9. CLI parser/help coverage for `aop collision`

---

## 10. Acceptance Criteria

- [ ] `aop collision --allow --feature-id A --with-feature-id B --reason "..."` creates an active override from the latest real collision record.
- [ ] A previously rejected `plan.update` with the same collision fingerprint succeeds after override approval.
- [ ] The same feature pair colliding with a different fingerprint is blocked again until a new approval is created.
- [ ] Contract collisions are never bypassed by this feature.
- [ ] Collision records are persisted under both `reject` and `block` modes.
- [ ] Approved collision metadata is visible in reporting outputs.
- [ ] Reconciler handoff contains approved-collision context, and the reconciler prompt instructs the agent to preserve both approved intents where feasible.
- [ ] Default behavior for all unapproved collisions remains unchanged.
- [ ] `npm run typecheck`, `npm run lint`, `npm test`, and `npm run verify` pass.

---

## 11. Risks and Mitigations

### 11.1 Risk: Over-Broad Override Becomes Silent Collision Disable

Mitigation:

- bind approval to exact observed fingerprint,
- do not allow manual wildcard paths in MVP,
- require explicit CLI reason text,
- mark overrides stale on fingerprint drift.

### 11.2 Risk: Reconciler Receives Too Little Context

Mitigation:

- pass structured override data and source collision record,
- explicitly update reconciler prompt,
- require resolution log to cite override usage.

### 11.3 Risk: Index Growth

Mitigation:

- collision records can later gain retention/compaction policy,
- this milestone may ship with bounded retention if needed, but correctness takes priority over early optimization.

---

## 12. Explicitly Rejected Alternatives

### 12.1 Global Policy Switch to Ignore Collisions

Rejected because it weakens determinism and removes human accountability.

### 12.2 Reusing `collision_policy: block` as the Approval Mechanism

Rejected because `block` only queues until the collision disappears. It does not express operator intent and does not help the reconciler.

### 12.3 Manual Path Entry in CLI for Initial Release

Rejected because it invites operator typos and mismatches between approved paths and the actual detected collision. Approval should be bound to persisted collision evidence.
