# Feature Spec: Human Input Interaction Protocol for Agent Cluster (AOP)

> **Purpose of this document**: Define a first-class, deterministic protocol for cases where planner/builder/qa must pause and request human input (clarifications, approvals, permission overrides) before safe continuation.

**Version:** 1.2  
**Date:** 2026-03-17  
**Status:** Draft  
**Roadmap Mapping:** M43

---

## 0. Standards and Dependencies

### 0.1 Required Standards

All implementation MUST preserve:

- deterministic orchestration behavior and state transitions
- MCP/in-process tool contract parity
- strict schema validation (`validate:mcp-contracts`, `validate:architecture`)
- canonical runtime artifacts under `.aop/**`
- global topology invariants (`1 + 3N` active sessions)
- idempotent behavior for mutating tool calls (`operation_id` required)

### 0.2 Required Inputs

Implementing agents MUST read:

- `apps/control-plane/src/supervisor/worker-decision-loop.ts`
- `apps/control-plane/src/supervisor/run-coordinator.ts`
- `apps/control-plane/src/supervisor/planning-wave-executor.ts`
- `apps/control-plane/src/supervisor/build-wave-executor.ts`
- `apps/control-plane/src/supervisor/qa-wave-executor.ts`
- `apps/control-plane/src/core/kernel.ts`
- `apps/control-plane/src/core/constants.ts`
- `apps/control-plane/src/cli/status-command-handler.ts`
- `apps/control-plane/src/cli/send-command-handler.ts`
- `apps/control-plane/src/application/services/activity-monitor-service.ts`
- `agentic/orchestrator/tools/catalog.json`
- `agentic/orchestrator/schemas/state.schema.json`
- `agentic/orchestrator/schemas/index.schema.json`
- `agentic/orchestrator/policy.yaml`
- `agentic/orchestrator/schemas/policy.schema.json`

### 0.3 Scope

This spec defines the **shared human-input protocol baseline** for the orchestrator.

It covers:

- explicit question/answer protocol between agents and humans
- deterministic pause-and-resume behavior for features awaiting input
- durable question artifacts and tool contracts
- shared operator-visible readiness rules for CLI and dashboard surfaces
- dashboard/CLI retrieval and answer workflows
- regression coverage for question-blocked execution and resume
- provider compatibility requirements for automated interactive execution

Out of scope:

- external ticketing approval workflows (Jira/ServiceNow sync)
- cryptographic signature workflows for approvals
- model-vendor-specific conversational UX
- intake-specific requirement normalization artifacts (`spec.manifest.bootstrap.json`, `spec.manifest.verified.json`, `intake.review.json`)
- intake-only UI metadata and verified-manifest promotion rules; those belong to M59 and must extend this baseline rather than replace it

### 0.4 OOP and Architecture Guardrails (Mandatory)

Implementation for this spec MUST follow explicit object-oriented and architectural best practices:

- **KISS**: implement the smallest deterministic question/answer protocol that satisfies the contract.
- **SRP (Single Responsibility Principle)**: question persistence, state transitions, CLI handling, and status projection MUST remain in separate modules/services.
- **SOLID-oriented design** (applied pragmatically):
  - open/closed for new question types or answer validators without changing unrelated orchestration flow
  - interface segregation for question store/service/query ports
  - dependency inversion so supervisor/CLI depend on contracts, not storage details
- **Hexagonal architecture alignment**: domain/application logic must not depend directly on transport adapters (CLI/MCP/dashboard).
- **Composition over inheritance**: compose question validators/parsers/services rather than building inheritance-heavy hierarchies.
- **High cohesion / low coupling**: keep WorkerDecisionLoop routing, Kernel state mutation, and artifact I/O separated.

Code organization requirements for this spec:

- question lifecycle domain logic lives in dedicated services (`create`, `answer`, `list`, `expire`)
- feature status transitions (`awaiting_input` <-> resumed phase) are owned by kernel/application services, not CLI handlers
- CLI and dashboard adapters are thin and invoke tool/service contracts only
- provider-specific worker payload formats are normalized before domain question handling

---

## 1. Current Repository Baseline and Remaining Gaps

This spec predates a substantial amount of human-input implementation work. It must therefore be read as a **protocol refresh and consolidation spec**, not as a greenfield design.

### 1.1 Current Repository Baseline

The current repository already has the following baseline pieces:

1. `QuestionService` exists and persists `.aop/features/<feature_id>/questions.json`.
2. `feature.question_create`, `feature.question_list`, and `feature.question_answer` are already present in the MCP catalog and kernel wiring.
3. `WorkerDecisionLoop` already routes `REQUEST.action=ask_user_input` to `feature.question_create`.
4. `awaiting_input` already exists as a persisted feature status, and `state.md` already carries `human_input.*` summary fields.
5. The dashboard already exposes:
   - feature questions API route
   - question answer API route
   - focus-view `HumanInputPanel` that answers persisted questions
6. The `claude` provider already carries `--dangerously-skip-permissions` in automated run/send templates.

### 1.2 Remaining Gaps

Despite that baseline, the protocol is still not fully normalized:

1. this spec still describes several already-implemented protocol artifacts as if they are net-new
2. operator-visible **question readiness** is not centralized as one authoritative contract
3. dashboard/focus/CLI transport semantics are duplicated across specs instead of owned here
4. dedicated CLI question workflow is still missing even though the underlying tools exist
5. triage drawer still renders open questions passively instead of supporting first-class answers
6. activity projection still retains heuristic waiting-input fallbacks instead of relying only on persisted state where possible
7. `resume_status` baseline is too narrow for future pause/resume extensions such as `intake`
8. some names in this older spec drift from current implementation (`question_already_closed` vs older wording, existing tool/schema paths, current dashboard answer path)

### 1.3 Normative Role of This Spec

This spec is the authoritative baseline for:

- generic question lifecycle
- persisted pause/resume semantics
- ready-state visibility rules
- shared question payload baseline
- operator answer transport

Any later spec, including M59 intake, must extend this contract rather than silently fork it.

---

## 2. Objectives

### 2.1 Must-Have Outcomes

1. Agents can raise a structured blocking question via tool contract.
2. Feature enters explicit `awaiting_input` state until answered (or expired/escalated).
3. Human answers are persisted as first-class artifacts and fed back into role context.
4. Orchestrator does not run planner/build/qa waves for features with open blocking questions.
5. Answering a question deterministically resumes the correct phase.
6. `aop status` can show pending-question state without heuristic parsing.

### 2.2 Non-Goals

- replacing existing `aop send` and `aop attach` commands
- introducing a free-form chat broker between human and agents
- adding real-time websocket transport requirements to core runtime

---

## 3. Terminology and Invariants

### 3.1 New Terms

- **Blocking Question**: a question that must be answered before wave execution can continue.
- **Question Record**: durable structured artifact for one question lifecycle.
- **Awaiting Input State**: explicit feature status indicating orchestrator pause pending answer.

### 3.2 Invariants

1. Exactly zero or one **active blocking question** per feature at a time.
2. A feature in `awaiting_input` cannot execute planning/build/qa wave logic.
3. Answers are append-only in question history (no destructive rewrites).
4. Answer operations are idempotent by `operation_id`.
5. Resume phase is deterministic from recorded pre-question status.

---

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

### 4.1 Ask Flow

1. Worker emits:

```json
{
  "type": "REQUEST",
  "request": {
    "action": "ask_user_input",
    "question_type": "permission_override",
    "prompt": "Allow editing file outside planned file set: config/security/policy.yaml?",
    "details": {
      "requested_paths": ["config/security/policy.yaml"],
      "reason": "Fix required by failing gate"
    },
    "expected_answer": {
      "kind": "single_choice",
      "choices": ["approve", "deny", "needs_more_context"]
    },
    "blocking": true
  }
}
```

2. `WorkerDecisionLoop` routes this to `feature.question_create`.
3. Kernel persists question record and transitions feature state to `awaiting_input`.
4. Feature retains active session assignment but is skipped by phase executors.
5. Operator sees pending question in `aop status` and `aop questions`.

### 4.2 Answer Flow

1. Human answers via `aop answer --feature-id <id> --question-id <qid> --answer <json-or-text>`.
2. `feature.question_answer` persists answer and closes question.
3. Kernel restores feature status to recorded `resume_status`.
4. Next orchestration iteration resumes normal phase execution.
5. Answer is included in `feature.get_context` under `human_input.latest_answer`.

### 4.3 Timeout / Expiry Flow

1. If no answer before timeout (`policy.execution.human_input_timeout_ms`), question becomes `expired`.
2. Configured action applies (`block_feature` or `fail_feature`).
3. Expiry is logged in decisions and question history.

---

## 5. Contract and Schema Deltas

### 5.1 Feature Status Contract

Baseline status constant and schema enum:

- `awaiting_input`

State semantics:

- terminal: no
- wave-executable: no
- eligible for resume: yes (after question resolution)

### 5.2 State Frontmatter Additions (`state.schema.json`)

Add:

```yaml
human_input:
  open_question_id: string | null
  open_question_count: integer
  awaiting_since: date-time | null
  requested_by_role: string | null
  resume_status: string | null
```

Rules:

- `open_question_id` must be null when status is not `awaiting_input`.
- `resume_status` must be a resumable lifecycle phase supported by the runtime. Baseline values are `planning|building|qa|blocked|ready_to_merge`; future extension specs may add values such as `intake`.

### 5.3 Question Artifact Contract

Canonical per-feature artifact:

```text
.aop/features/<feature_id>/questions.json
```

Shape:

```json
{
  "version": 1,
  "feature_id": "feature_x",
  "items": [
    {
      "question_id": "q_01J...",
      "status": "open",
      "blocking": true,
      "question_type": "permission_override",
      "role": "builder",
      "session_id": "builder-feature_x-...",
      "prompt": "Allow editing ... ?",
      "details": { "requested_paths": ["..."] },
      "expected_answer": { "kind": "single_choice", "choices": ["approve", "deny"] },
      "created_at": "2026-03-04T22:00:00.000Z",
      "expires_at": "2026-03-04T22:15:00.000Z",
      "answer": null,
      "answered_at": null,
      "answered_by": null,
      "resume_status": "building"
    }
  ]
}
```

### 5.4 Tool Catalog Baseline

Canonical MCP tools:

1. `feature.question_create` (mutating)
2. `feature.question_list` (read)
3. `feature.question_answer` (mutating)

#### `feature.question_create` input

- `feature_id` (string)
- `role` (`planner|builder|qa|orchestrator`)
- `session_id` (string)
- `question_type` (`clarification|permission_override|external_decision|risk_ack`)
- `prompt` (string, min length 1)
- `details` (object, optional)
- `expected_answer` (object with `kind`, optional choices/schema)
- `blocking` (boolean, default true)
- `operation_id` (string, required)

Output:

- `question_id`
- `status`
- `feature_status`
- `resume_status`

#### `feature.question_list` input

- `feature_id` (string)
- `status` (`open|answered|expired|withdrawn|all`, default `open`)

Output:

- array of matching question records (stable sorted by `created_at` ascending)

#### `feature.question_answer` input

- `feature_id` (string)
- `question_id` (string)
- `answer` (string or object)
- `answered_by` (string; `human` or operator identity)
- `operation_id` (string, required)

Output:

- `question_id`
- `question_status` (`answered`)
- `feature_status` (resumed phase)
- `resumed` (boolean)
- `resume_status` (optional in baseline, recommended when richer UI flows need deterministic resume-target display)

### 5.5 Error Taxonomy Additions

Add:

- `question_not_found`
- `question_already_closed`
- `question_answer_invalid`
- `question_conflict_open`
- `question_answer_not_allowed_in_terminal_state`

---

## 6. Runtime Behavior Changes

### 6.1 WorkerDecisionLoop

Add handling for `REQUEST.action=ask_user_input`:

- call `feature.question_create`
- mark request handled
- append note to feature log with question id

Unknown `REQUEST.action` remains no-op but MUST log a warning note for observability.

### 6.2 RunCoordinator and Wave Executors

When feature status is `awaiting_input`:

- planning/build/qa executors must skip worker execution and gates
- no automatic transition to `blocked` or `failed` unless timeout policy triggers
- feature remains in the current runtime scope (session cluster intact)

### 6.3 Context Enrichment

`feature.get_context` adds:

```yaml
human_input:
  open_questions: [...]
  latest_answer: {...} | null
```

Rules:

- planner/builder/qa receive at most last `N` answered questions (`N` configurable, default 3)
- open questions included only for same feature

### 6.4 Activity State Determinism

The protocol should prefer persisted status/state over heuristic `status_reason` parsing:

- if state status is `awaiting_input`, `activity_state` reports `waiting_input`
- heuristic fallback remains only for backward compatibility during migration window

### 6.5 Question Readiness Contract

A question is operator-visible and answerable only when it is persisted and the feature state reflects the pause.

Baseline ready-state condition:

```text
question_ready =
  exists(open question record)
  AND feature.status == awaiting_input
  AND feature.human_input.open_question_id == question.question_id
```

Normative consequences:

- transient model output alone is never sufficient
- malformed or failed `REQUEST` handling must not create operator-visible question state
- dashboard, focus view, and future CLI surfaces must all use this same persisted source of truth
- phase-specific specs may add stricter predicates; for example M59 intake additionally requires `question.resume_status == intake`

---

## 7. Operator Surfaces

### 7.1 Shared Transport Baseline

The generic protocol baseline now assumes these operator-facing paths exist or will be normalized here:

- dashboard:
  - feature questions route
  - question answer route
  - focus-view `HumanInputPanel`
- future CLI:
  - `aop questions --feature-id <id> [--status open|answered|expired|withdrawn|all]`
  - `aop answer --feature-id <id> --question-id <qid> --answer <value> [--answered-by <id>]`

Behavior:

- all answer paths terminate in `feature.question_answer`
- all read paths derive from persisted `questions.json` and `state.md`
- transport wrappers may differ, but the underlying lifecycle is shared

### 7.2 Status and Summary Baseline

`aop status` and dashboard summary payloads must expose at least:

- `open_question_count`
- `open_question_id`
- `awaiting_since`
- `requested_by_role`
- `resume_status`

### 7.3 Dashboard Behavior Baseline

At baseline, the dashboard must support:

- rendering persisted open questions
- showing `Awaiting Input` / waiting-input states from persisted state
- answering a blocking question from at least one first-class surface

The current focus-view answer path satisfies the last requirement today. Additional surfaces, such as the triage drawer, should be treated as parity work rather than protocol invention.

---

## 8. Planner / Builder / QA Role Semantics

### 8.1 Planner

Planner may ask blocking clarification questions when spec ambiguities prevent safe plan updates.

### 8.2 Builder

Builder may ask blocking permission questions for out-of-plan or protected path changes.

### 8.3 QA

QA may ask blocking risk-acknowledgement questions when gate failures imply high-risk tradeoffs.

### 8.4 Orchestrator

Orchestrator does not synthesize answers. It only records question lifecycle and enforces state transitions.

---

## 9. Policy Contract

Add under `policy.execution`:

```yaml
human_input:
  enabled: true
  timeout_ms: 900000
  on_timeout: block_feature # block_feature | fail_feature
  max_open_questions_per_feature: 1
  context_answer_history_limit: 3
```

Rules:

- if `enabled=false`, `feature.question_create` returns a structured policy/configuration error; current baseline may surface `invalid_argument` until a more specific code is introduced
- `max_open_questions_per_feature=1` is enforced strictly in v1

---

## 10. Edge-Case Matrix (Normative)

1. Duplicate create requests with same `operation_id`:

- replay exact prior success envelope.

2. New question when an open blocking question already exists:

- reject with `question_conflict_open`.

3. Answer submitted for unknown question id:

- reject with `question_not_found`.

4. Answer submitted for already answered question:

- return idempotent success if same operation replay, else `question_already_closed`.

5. Feature reaches terminal status while question open:

- auto-close question as `withdrawn` and retain history.

6. Resume status no longer valid at answer time:

- fallback resume to `planning` and log deterministic reason.

7. Multi-project mode:

- question artifacts must resolve under selected project root only.

8. Stale runtime takeover:

- open questions persist and remain answerable post-takeover.

9. Manual `aop send` while awaiting input:

- allowed; does not auto-answer question.

10. Missing session cluster while answering:

- answer still persists and feature resumes by state transition; session recreation handled by normal runtime recovery.

---

## 11. Implementation Plan

This plan reflects the **current repository baseline**.

### M43.0 Provider Compatibility Regression Guard

- preserve `--dangerously-skip-permissions` on automated `claude` run/send templates
- preserve provider parity with `codex` and `copilot`
- treat this as regression protection, not new design work

### M43.1 Protocol Baseline Hardening

- keep `QuestionService`, question artifacts, and MCP tools as the canonical persistence boundary
- align spec, schemas, error names, and output shapes to current implementation
- extend resumable-state contract so later specs can add `intake`

### M43.2 Shared Ready-State and Payload Baseline

- define one authoritative question-ready contract for operator-visible work
- normalize baseline dashboard / route / summary payload requirements here
- require later specs to add only delta fields

### M43.3 Operator Workflow Parity

- add dedicated CLI `questions` / `answer` workflow
- extend the dashboard triage drawer so it can answer blocking questions directly
- keep focus-view `HumanInputPanel` on the same backend answer path

### M43.4 Deterministic Projection Cleanup

- reduce waiting-input heuristics where persisted state already exists
- make status and activity reporting prefer `awaiting_input` + `human_input.*`

### M43.5 Verify Path Coverage

- add or update unit/integration/dashboard tests and include them in `npm run verify`

---

## 12. File-Level Change Targets

- `apps/control-plane/src/providers/provider-defaults.ts` — preserve automated `claude` run/send compatibility guardrails.
- `apps/control-plane/src/application/services/question-service.ts` — baseline protocol semantics, resumable-state widening, answer output alignment.
- `apps/control-plane/src/core/questions.ts` — canonical question record / summary shape.
- `apps/control-plane/src/core/constants.ts`
- `apps/control-plane/src/core/kernel.ts`
- `apps/control-plane/src/supervisor/worker-decision-loop.ts`
- `apps/control-plane/src/supervisor/planning-wave-executor.ts`
- `apps/control-plane/src/supervisor/build-wave-executor.ts`
- `apps/control-plane/src/supervisor/qa-wave-executor.ts`
- `apps/control-plane/src/application/services/activity-monitor-service.ts`
- `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/cli/questions-command-handler.ts` (new)
- `apps/control-plane/src/cli/answer-command-handler.ts` (new)
- `apps/control-plane/src/interfaces/cli/bootstrap.ts`
- `apps/control-plane/src/cli/status-command-handler.ts`
- `agentic/orchestrator/tools/catalog.json`
- `agentic/orchestrator/tools/schemas/input/feature.question_create.input.schema.json`
- `agentic/orchestrator/tools/schemas/output/feature.question_create.output.schema.json`
- `agentic/orchestrator/tools/schemas/input/feature.question_list.input.schema.json`
- `agentic/orchestrator/tools/schemas/output/feature.question_list.output.schema.json`
- `agentic/orchestrator/tools/schemas/input/feature.question_answer.input.schema.json`
- `agentic/orchestrator/tools/schemas/output/feature.question_answer.output.schema.json`
- `agentic/orchestrator/schemas/state.schema.json`
- `agentic/orchestrator/schemas/policy.schema.json`
- `agentic/orchestrator/policy.yaml`
- `packages/web-dashboard/src/app/api/features/[id]/questions/route.ts`
- `packages/web-dashboard/src/app/api/features/[id]/questions/[questionId]/answer/route.ts`
- `packages/web-dashboard/src/components/human-input-panel.tsx`
- `packages/web-dashboard/src/components/dashboard/triage-drawer.tsx`
- `packages/web-dashboard/src/lib/types.ts`
- `packages/web-dashboard/src/lib/aop-client.ts`
- `apps/control-plane/test/question-service.spec.ts`
- `apps/control-plane/test/worker-decision-loop.spec.ts`
- `apps/control-plane/test/kernel-tool-wiring.spec.ts`
- `apps/control-plane/test/cli.unit.spec.ts`
- `apps/control-plane/test/status-command.spec.ts`
- `packages/web-dashboard/test/**` (question/triage/focus coverage)

---

## 13. Test Plan (Must Be in `npm run verify` Path)

### 13.1 Unit

- `claude` provider automated run/send templates retain `--dangerously-skip-permissions` and remain aligned with current provider policy
- question create transitions state to `awaiting_input`
- answer transitions to recorded `resume_status`
- duplicate open question is rejected
- answer idempotency and replay behavior
- ready-state projection requires persisted open question + `awaiting_input` state

### 13.2 Supervisor Integration

- feature in `awaiting_input` is skipped by all wave executors
- answering question resumes the next iteration in the recorded phase

### 13.3 CLI Integration

- `aop questions` returns open questions sorted deterministically
- `aop answer` closes question and reports resumed status

### 13.4 MCP Contract

- question tools validate schema and role authorization
- mutating tools enforce `operation_id`

### 13.5 Dashboard / Focus Regression

- focus-view `HumanInputPanel` answers via the persisted question-answer route
- triage drawer answer controls use the same answer path once parity work lands
- dashboard never renders transient non-persisted questions as actionable work

### 13.6 Regression

- GIVEN builder asks permission override WHEN unanswered THEN no further patch/gate actions execute for that feature
- GIVEN answer is provided WHEN next iteration runs THEN builder resumes and can continue gate repair

---

## 14. Acceptance Criteria

1. The spec reflects the current repository baseline rather than re-specifying already-landed behavior as future work.
2. Agent-generated blocking questions remain first-class persisted artifacts.
3. Feature status transitions to `awaiting_input` deterministically.
4. No planning/building/qa waves execute for features with open blocking question.
5. Human answer flow is deterministic, idempotent, and auditable.
6. Feature resumes from recorded phase after answer.
7. There is one authoritative question-ready contract based on persisted state, and later specs reuse it.
8. Dashboard and CLI answer flows terminate in `feature.question_answer`.
9. `npm run verify` fails if awaiting-input pause/resume invariants regress.
10. Implementation boundaries follow KISS/SRP/SOLID/Hexagonal requirements from Section 0.4.

---

## 15. Risks and Mitigations

- Risk: `--dangerously-skip-permissions` on `claude` provider removes interactive approval prompts for automated sessions.
  - Mitigation: semantically, the orchestrator already provides equivalent protection in automated interactive execution — plan validation, kernel validation, and gate checks remain the security boundary. The flag is consistent with how `codex` and `copilot` providers are already configured. This spec only requires deterministic automated compatibility; it does not rely on a specific `attach` permission model.

- Risk: status-model expansion (`awaiting_input`) causes unintended branch regressions.
  - Mitigation: add explicit status transition tests in all wave executors and coordinator.

- Risk: unanswered questions stall active slots indefinitely.
  - Mitigation: policy timeout with deterministic expiry action and status reporting.

- Risk: users bypass structured answers with ad hoc `send`.
  - Mitigation: keep `send` but require `question_answer` to clear blocking state.

- Risk: tool-surface growth increases contract maintenance load.
  - Mitigation: strict MCP schema/metadata validation and parity tests.
