# Feature Spec: Add Feature to Active Execution (AOP)

> **Purpose of this document**: Define a deterministic mechanism for adding new feature work to an already-running `aop` execution without allowing multiple same-repo supervisor runs.

**Version:** 1.0  
**Date:** 2026-03-14  
**Status:** Draft  
**Roadmap Mapping:** M49

---

## 0. Standards and Dependencies

### 0.1 Required Standards

All implementation MUST preserve:

- single active supervisor ownership per repository
- deterministic orchestration and normalized error envelopes
- canonical runtime artifacts under `.aop/**`
- existing topology invariants (`1 + 3N` logical sessions)
- existing feature-level collision detection and lock behavior
- MCP/in-process parity for any new mutating/read operations
- `operation_id` idempotency on mutating tool calls
- coverage thresholds: Lines >=90%, Branches >=90%, Functions >=90%, Statements >=90%

### 0.2 Required Inputs

Implementing agents MUST read:

- `apps/control-plane/src/cli/run-command-handler.ts`
- `apps/control-plane/src/cli/resume-command-handler.ts`
- `apps/control-plane/src/cli/status-command-handler.ts`
- `apps/control-plane/src/cli/cli-argument-parser.ts`
- `apps/control-plane/src/cli/help-command-handler.ts`
- `apps/control-plane/src/interfaces/cli/bootstrap.ts`
- `apps/control-plane/src/supervisor/runtime.ts`
- `apps/control-plane/src/supervisor/run-coordinator.ts`
- `apps/control-plane/src/supervisor/session-orchestrator.ts`
- `apps/control-plane/src/core/kernel.ts`
- `apps/control-plane/src/core/path-layout.ts`
- `apps/control-plane/src/core/runtime-sessions.ts`
- `apps/control-plane/src/application/services/run-lease-service.ts`
- `apps/control-plane/src/application/services/instance-isolation-service.ts`
- `apps/control-plane/src/application/services/feature-lifecycle-service.ts`
- `apps/control-plane/src/application/services/feature-send-message-service.ts`
- `agentic/orchestrator/tools/catalog.json`
- `agentic/orchestrator/schemas/index.schema.json`
- `agentic/orchestrator/policy.yaml`
- `agentic/orchestrator/schemas/policy.schema.json`

### 0.3 Scope

This spec implements:

- a new CLI mechanism to add one or more features to an already-running execution
- a durable runtime control artifact for enqueueing add-feature requests
- supervisor polling/application of new requests during a live run
- deterministic duplicate handling and request disposition tracking
- status/dashboard visibility into pending and applied add-feature requests

Out of scope:

- multiple same-repo active supervisor runs
- per-added-feature provider/model/execution-mode overrides
- indefinite idle keepalive after a run has drained all work
- replacing existing plan collision, blocked queue, or shared reconciler behavior

### 0.4 OOP and Architecture Guardrails (Mandatory)

Implementation MUST follow:

- **KISS**: keep one active run lease model; add a control-plane enrollment path instead of redesigning run ownership.
- **SRP**: CLI parsing, request persistence, supervisor polling, and status projection MUST remain in separate modules/services.
- **SOLID-oriented design**:
  - open/closed for future control request types beyond `add_features`
  - interface segregation between request store/query APIs and supervisor application APIs
  - dependency inversion so CLI/supervisor depend on contracts, not artifact-file details
- **Hexagonal alignment**: control request persistence belongs in application/core services, not directly in CLI handlers.
- **Composition over inheritance**: compose a dedicated execution-control service into kernel/supervisor flows.
- **High cohesion / low coupling**: feature initialization and worker cluster startup stay in existing lifecycle/session orchestrator paths.

---

## 1. Problem Statement and Decision

### 1.1 Current Behavior

Today, `aop run` accepts a feature set only at supervisor startup. A second CLI invocation in the same repository fails with `run_already_active` because repository ownership is intentionally single-run.

That is correct for run ownership, but it creates an operator gap:

1. the user cannot add newly-prioritized work to a healthy in-flight supervisor
2. the only current workaround is waiting for the active run to finish or forcing an unsafe second run
3. the runtime already supports multiple feature clusters inside a single supervisor, but only from the initial queue

### 1.2 Design Decision

This spec keeps the existing single active run per repository and adds a first-class **feature enrollment** mechanism into that run.

The chosen approach is:

- preserve one active run lease
- preserve one active global orchestrator session and one shared reconciler lane
- allow later CLI invocations to append new features to the active run's execution queue
- reuse the current `RunCoordinator` and `SessionOrchestrator` feature-cluster lifecycle

### 1.3 Rejected Alternative

Same-repo multi-run concurrency is explicitly not the chosen design for this milestone because it would require redesigning:

- run lease ownership semantics
- feature-to-session routing for `attach` / `send` / `resume`
- stale takeover semantics
- shared reconciler-lane ownership across multiple active runs
- runtime session lookup and status/reporting semantics

This spec is the lower-risk, architecture-aligned solution to the user problem.

---

## 2. Objectives

### 2.1 Must-Have Outcomes

1. A user can add new work to an active run without starting a second supervisor.
2. Added features are persisted durably and applied deterministically by the active supervisor.
3. Added features inherit the active run's provider/model/execution-mode/runtime policy.
4. Existing `max_active_features` behavior remains authoritative.
5. Existing plan collision and lock semantics remain unchanged.
6. Operators can observe request status (`pending`, `applied`, `rejected`) via CLI status output and runtime artifacts.

### 2.2 Non-Goals

- allowing multiple active runs for one repository
- hot-swapping providers or execution mode for only one added feature
- bypassing canonical spec ingestion
- starting worker clusters outside the supervisor-controlled rebalance flow

---

## 3. User Stories and Acceptance Scenarios

### User Story 1 — Add a New Feature While a Run Is Active

An operator has an active `aop run` in progress and wants to start work on a second feature without restarting the supervisor.

**Acceptance Scenarios**:

1. **Given** a run is active and feature capacity is available, **When** `aop add --fi <spec>` is invoked, **Then** the request is accepted and the feature is initialized into the active execution on the next supervisor poll cycle.
2. **Given** a run is active but all feature slots are full, **When** `aop add --fi <spec>` is invoked, **Then** the request is accepted and the feature is appended to the run queue until `rebalanceActiveFeatures()` promotes it.
3. **Given** multiple features are supplied, **When** the add request is processed, **Then** features are enqueued in deterministic order by request time then `feature_id`.

### User Story 2 — Reject or Skip Invalid Enrollment

An operator submits a feature that is already being handled or cannot be legally enrolled.

**Acceptance Scenarios**:

1. **Given** a requested feature is already active in the run, **When** the add request is applied, **Then** that feature is skipped with reason `already_active`.
2. **Given** a requested feature is already queued in the run, **When** the add request is applied, **Then** that feature is skipped with reason `already_queued`.
3. **Given** a requested feature is already merged or terminal, **When** the add request is applied, **Then** that feature is skipped with a deterministic terminal-state reason.

### User Story 3 — No Active Run Means No Enrollment

An operator tries to add work when no active supervisor owns the repository.

**Acceptance Scenarios**:

1. **Given** no fresh run lease exists, **When** `aop add` is invoked, **Then** the command fails with `run_not_active`.
2. **Given** a stale run lease exists, **When** `aop add` is invoked, **Then** the command fails with `run_not_active` and does not create an orphan control request.
3. **Given** the supervisor has already released its run lease, **When** `aop add` is invoked, **Then** the command fails immediately rather than writing a request that nobody will process.

### User Story 4 — Request Visibility

An operator needs to know whether a submitted add request was applied.

**Acceptance Scenarios**:

1. **Given** an add request is newly submitted, **When** `aop status` is run before the supervisor applies it, **Then** the output includes a pending execution request summary.
2. **Given** an add request has been applied, **When** `aop status` is run, **Then** the output includes the applied request id and per-feature outcome.
3. **Given** an add request was rejected, **When** `aop status` is run, **Then** the output includes the rejection code and message.

---

## 4. CLI and UX Contract

### 4.1 New CLI Command

Add a new CLI command:

```bash
aop add --fi <spec.md>
aop add --fl <folder>
aop add --resume <feature-id>
```

The command MUST reuse the same canonical spec resolution/ingestion rules as `aop run` for spec inputs.
The `--resume` path MUST target an already-initialized non-terminal feature and MUST enqueue it into the active execution without requiring spec-path ingestion.

### 4.2 Allowed and Disallowed Flags

Allowed:

- `--fi` / `-fi`
- `--fl` / `-fl`
- `--resume <feature-id>` for existing feature state
- `--feature-id` only if already supported by the same resolver path for canonicalization

Disallowed for `aop add`:

- provider override flags
- role-specific provider overrides
- execution-mode override flags
- takeover flags
- transport-specific behavior differences

Reason: added features are joining an already-running supervisor and therefore must inherit the active run configuration exactly.

`--resume` MUST be mutually exclusive with `--fi` / `--fl`.

### 4.3 Response Contract

Successful enqueue response:

```json
{
  "ok": true,
  "data": {
    "command": "add",
    "run_id": "run:123",
    "request_id": "exec_req_01J...",
    "request_status": "pending",
    "requested_features": [
      { "feature_id": "feature_a", "spec_path": "spec-files/outstanding/feature_a.md" }
    ]
  }
}
```

The command acknowledges durable submission, not immediate cluster startup.

### 4.4 Error Contract

Add or reuse deterministic errors:

- `run_not_active` — no fresh active run lease exists
- `execution_request_target_mismatch` — request targets the wrong run or instance
- `duplicate_execution_request` — same `operation_id` replay with mismatched payload

If the repository still uses a generic existing error code set, these semantics MUST still be reflected in normalized details.

---

## 5. End-to-End Lifecycle (Normative)

### 5.1 Submission Flow

1. User runs `aop add --fi <spec>`.
2. CLI resolves and ingests specs using the existing run-path ingestion logic.
3. CLI verifies a fresh active run lease exists for the current instance.
4. CLI submits a durable `add_features` execution request with `operation_id`.
5. Request is persisted as `pending`.
6. CLI returns `ok: true` with `request_id` and `run_id`.

### 5.2 Supervisor Application Flow

1. Active supervisor polls for pending execution requests at deterministic control points.
2. Requests are read in sorted order by `requested_at`, then `request_id`.
3. For each requested feature:
   - if already active, skip
   - if already queued, skip
   - if already assigned a session cluster, skip
   - if already terminal/merged, skip
   - otherwise append to the in-memory run queue
4. Request is marked `applied` with per-feature outcome.
5. Existing `rebalanceActiveFeatures()` logic later creates planner/builder/qa sessions when capacity permits.

### 5.3 Final Shutdown Rule

The supervisor MUST perform one final poll for pending execution requests before releasing the run lease and exiting.

This spec does **not** require an indefinite idle keepalive window. If the run has fully shut down, subsequent `aop add` calls fail with `run_not_active`.

---

## 6. Runtime Artifact Contract

### 6.1 New Artifact

Add a per-instance runtime artifact:

```text
.aop/runtime/<instanceId>/execution-control.json
```

Protected by the existing index lock (`withIndexLock`) to preserve atomic coordination with run-lease state.

### 6.2 Artifact Shape

```json
{
  "version": 1,
  "run_id": "run:123",
  "owner_instance_id": "abcd1234ef56",
  "updated_at": "2026-03-14T20:00:00.000Z",
  "items": [
    {
      "request_id": "exec_req_01J...",
      "request_type": "add_features",
      "status": "pending",
      "requested_at": "2026-03-14T20:00:00.000Z",
      "processed_at": null,
      "requested_by": "cli:add",
      "operation_id": "execution_request_add__...",
      "features": [
        {
          "feature_id": "feature_a",
          "spec_path": "spec-files/outstanding/feature_a.md"
        }
      ],
      "outcome": null
    }
  ]
}
```

Applied outcome shape:

```json
{
  "queued": ["feature_a"],
  "skipped": [{ "feature_id": "feature_b", "reason": "already_active" }]
}
```

Rejected outcome shape:

```json
{
  "code": "run_not_active",
  "message": "No active run is available for feature enrollment"
}
```

### 6.3 Schema Additions

Add:

- `agentic/orchestrator/schemas/execution-control.schema.json`
- tool schemas for any new runtime execution-request tools

Validation rules:

1. `run_id` and `owner_instance_id` MUST match the active lease at submission time.
2. `status` MUST be one of `pending|applied|rejected`.
3. `processed_at` MUST be non-null only when status is `applied` or `rejected`.
4. `features[].feature_id` MUST use canonical feature-id format.

---

## 7. Tool and Service Contract

### 7.1 New Application Service

Create `ExecutionControlService` responsible for:

- submitting add-feature requests
- reading/listing execution requests
- draining pending requests for the active run
- marking requests applied/rejected

This service MUST own the runtime artifact contract.

### 7.2 New Tool Surface

Add runtime-oriented tools:

1. `runtime.execution_request_submit` (mutating, requires `operation_id`)
2. `runtime.execution_request_list` (read)

The supervisor may call the underlying service directly, but CLI and MCP/in-process surfaces MUST stay aligned through the same service contract.

### 7.3 Submission Input Contract

```json
{
  "run_id": "run:123",
  "owner_instance_id": "abcd1234ef56",
  "request_type": "add_features",
  "features": [{ "feature_id": "feature_a", "spec_path": "spec-files/outstanding/feature_a.md" }],
  "operation_id": "execution_request_add__..."
}
```

### 7.4 Supervisor Drain Contract

Internal drain/apply logic MUST be idempotent:

- replaying an already-applied request must not duplicate queue entries
- the same feature may appear in multiple requests, but only the first successful enrollment changes run state

---

## 8. Supervisor Integration

### 8.1 Poll Points

The active supervisor MUST poll pending execution requests:

1. once after run startup and lease acquisition, before the first rebalance
2. once per orchestration iteration before `rebalanceActiveFeatures()`
3. once after wave completion before checking for loop exit
4. once immediately before final run shutdown

### 8.2 Queue Semantics

Applied requests MUST append new features to `state.queue` only. They MUST NOT directly create worker clusters.

Reason: all cluster creation must continue to flow through [`rebalanceActiveFeatures()`](/home/erictaurone/workspace/Aequitas/projects/Agentic-Orchestrator/apps/control-plane/src/supervisor/run-coordinator.ts#L424) so existing invariants remain intact.

### 8.3 Active Run Inheritance

Added features inherit:

- active run `run_id`
- provider/model/runtime selection
- execution mode
- max-active-features limit
- existing reconciler-lane ownership model

No new per-feature runtime config is introduced.

---

## 9. File-Level Change Targets

### 9.1 New Files

- `apps/control-plane/src/application/services/execution-control-service.ts`
- `apps/control-plane/test/execution-control-service.spec.ts`
- `apps/control-plane/src/cli/add-command-handler.ts`
- `agentic/orchestrator/schemas/execution-control.schema.json`
- tool input/output schemas for runtime execution-request submission/listing

### 9.2 Modified Files

- `apps/control-plane/src/cli/types.ts`
- `apps/control-plane/src/cli/cli-argument-parser.ts`
- `apps/control-plane/src/cli/help-command-handler.ts`
- `apps/control-plane/src/interfaces/cli/bootstrap.ts`
- `apps/control-plane/src/cli/status-command-handler.ts`
- `apps/control-plane/src/cli/run-command-handler.ts`
- `apps/control-plane/src/core/path-layout.ts`
- `apps/control-plane/src/core/kernel.ts`
- `apps/control-plane/src/supervisor/run-coordinator.ts`
- `apps/control-plane/src/supervisor/runtime.ts`
- `apps/control-plane/src/supervisor/types.ts`
- `agentic/orchestrator/tools/catalog.json`
- `agentic/orchestrator/schemas/policy.schema.json` only if a small polling/grace configuration is added
- `AGENTS.md`
- `CLAUDE.md`

---

## 10. Implementation Milestones

### M49.1 — Control Artifact and Service

- add execution-control runtime artifact and schema
- implement `ExecutionControlService`
- add kernel delegation methods

### M49.2 — CLI Add Command

- add `aop add` command and parser/bootstrap/help wiring
- reuse canonical spec resolution/ingestion from `run`
- submit durable add-feature requests

### M49.3 — Supervisor Polling and Apply

- integrate deterministic polling into `RunCoordinator`
- append newly accepted features into the live queue
- persist per-request outcome

### M49.4 — Status and Observability

- surface pending/applied/rejected requests in `aop status`
- expose request summaries in dashboard/report payloads as needed

### M49.5 — Regression Coverage

- command-handler tests
- service tests
- supervisor integration tests
- status/reporting tests

---

## 11. Acceptance Criteria

Implementation is complete only when all of the following are true:

1. `aop add --fi <spec>` succeeds during an active run and persists a `pending` request.
2. The active supervisor applies that request without restarting.
3. The added feature receives a normal planner/builder/qa cluster through existing queue rebalance logic.
4. Duplicate additions are skipped deterministically and reported in request outcome.
5. `aop add` fails cleanly when no active run exists.
6. `aop status` shows execution-request visibility.
7. Existing `aop run`, `resume`, `attach`, `send`, collision handling, and reconciler behavior do not regress.
8. `npm run test`, `npm run lint`, `npm run typecheck`, and `npm run validate:mcp-contracts` pass.

---

## 12. Risks and Mitigations

### Risk 1 — Queue Duplication or Race Conditions

**Risk:** repeated add requests could duplicate queue entries.

**Mitigation:** persist requests under lock, apply them idempotently, and dedupe against active/queued/session-assigned features before enqueueing.

### Risk 2 — Orphan Requests After Run Exit

**Risk:** a request could be written just as the supervisor is shutting down.

**Mitigation:** require a fresh active lease at submission time and perform one final supervisor poll before lease release. If no active lease exists, reject submission.

### Risk 3 — Configuration Drift

**Risk:** users may expect added features to use different providers or execution modes.

**Mitigation:** make inheritance explicit in the CLI contract and reject override flags for `aop add`.

### Risk 4 — Architecture Drift Toward Multi-Run Semantics

**Risk:** implementation could accidentally reintroduce same-repo multi-run complexity.

**Mitigation:** keep single-run ownership as an explicit invariant and route all added work through the one active supervisor queue.

---

## 13. Verification Plan

- `apps/control-plane/test/execution-control-service.spec.ts`
  - request submission, idempotency, status transition, stale-run rejection
- `apps/control-plane/test/supervisor.spec.ts`
  - active run accepts added feature and later starts its cluster
  - added feature queues behind `max_active_features`
  - duplicate add request does not duplicate queue/session assignment
- `apps/control-plane/test/cli.unit.spec.ts`
  - `aop add` parser/help/bootstrap wiring
  - provider/execution-mode override rejection
- `apps/control-plane/test/status-command-handler.spec.ts`
  - execution-request visibility in status output

Documentation-only note: if policy/config fields are introduced, update `validate:architecture` expectations and relevant docs in the same implementation change.
