<!-- task=dispatcher_parallel_limit plan=2 tag=REVIEW_API -->

# Repair Workspace-Global Capacity and Review-Preflight Admission

## For the Implementing Agent

> **[IMPLEMENTING AGENT — READ FIRST]** Filling implementation-owned sections in `CODE_REVIEW-*-G??.md` is mandatory. Run every verification command, paste actual notes/output into the review file, keep both active files in place, and report ready for review. Finalization is code-review-skill only. If blocked, record only the exact blocker, attempted commands/output, and resume condition in implementation-owned evidence fields. Do not ask the user, call user-input tools, create control-plane stop files, classify the next state, archive logs, or write `complete.log`.

## Background

The first `--max-parallel` implementation passes its added suite but does not enforce the physical-workspace cap for filtered orchestration state. Its capped review-preflight refill can also launch a review after shared setup failed and launch a worker without a write claim. Repair those production paths and replace the vacuous helper-level checks with deterministic scheduler regressions.

## Archive Evidence Snapshot

- Current FAIL pair will archive as `agent-task/dispatcher_parallel_limit/plan_local_G05_1.log` and `agent-task/dispatcher_parallel_limit/code_review_cloud_G05_1.log`.
- Verdict: FAIL with three Required findings and no Suggested or Nit findings.
- Required corrections: query live occupancy across every task state for filtered and dry-run invocations; block every ready review after shared preflight failure; refill only worker/self-check tasks through atomic write-claim admission; replace helper-only or vacuous scheduler tests.
- Fresh reviewer evidence:
  - An exact-scope lookup returned `GLOBAL_LOOKUP {}` while `GROUP_LOOKUP` returned the live task registered under another orchestration scope.
  - With three ready reviews, one worker, and `max_parallel=2`, the failed-preflight reproducer called the third review and started the refill worker with `claim_at_start=False`.
  - The planned focused suite passed 39 tests and the full dispatcher suite passed 302 tests, proving that the current assertions do not cover these paths.
- Read the two archived files above only if exact prior wording is required; all implementation facts are reproduced here.

## Analysis

### Files Read

- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-ops/rules/common/philosophy.md`
- `agent-ops/rules/project/domain/testing/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/testing-smoke.md`
- `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`
- `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`
- `agent-ops/skills/project/orchestrate-agent-task-loop/SKILL.md`
- `agent-task/dispatcher_parallel_limit/PLAN-local-G05.md`
- `agent-task/dispatcher_parallel_limit/CODE_REVIEW-cloud-G05.md`

### SDD Criteria

Not applicable. This remains a non-roadmap compatibility repair for the project dispatcher and does not complete a Milestone Task.

### Verification Context

- Handoff: the official review supplied raw findings, fresh reproducer output, and the exact current-pair archive identities. No external verification context was supplied.
- Environment: local checkout `/config/workspace/iop`, Python standard library only, no dependency change, credential, network, provider CLI, deployment, or external runtime.
- Repository-native evidence:
  - `python3 -m py_compile agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py` passed.
  - The focused dispatcher command passed 39 tests.
  - Full unittest discovery passed 302 tests in a fresh reviewer run.
  - Reviewer-owned temporary-workspace reproducers contradicted the claimed global-occupancy and preflight-refill behavior.
- Required constraints:
  - `max_parallel=0` stays unlimited.
  - Positive limits count unique worker, self-check, review, and verified external-active task attempts across the canonical physical workspace.
  - `--task-group` never narrows occupancy.
  - Dry-run computes the same occupancy without persisting state or launching providers.
  - Review ordering, restricted rescans, lifecycle claims, and non-terminal external-capacity waits remain intact.
  - Every dispatcher simulation denies real provider subprocesses at the class boundary.
- External Verification Preflight: not applicable. All verification remains in temporary local workspaces with fake runners.
- Confidence: high. Both defects are deterministic and have direct scheduler-level oracles.

### Test Coverage Gaps

- Workspace-wide occupancy: current cross-group tests mock the occupancy result instead of constructing state under separate orchestration scopes.
- Dry-run occupancy: the current test scans no selected-group task and succeeds through `unobserved-task-group`.
- Limit-one re-admission: the current test uses a second workspace and calls only `select_dispatch_candidates`, so it does not prove cached reclassification or scan count.
- Mixed-role cap: the current test counts selected tuples, not concurrently active task-stage attempts.
- Existing claim retention: the current test verifies the selected owner's claim, not a capacity-deferred task's pre-existing lifecycle claim.
- Review-preflight refill: the current test leaves every task in worker stage, so shared review setup is never exercised.
- Provider isolation: the new class lacks the testing-domain default provider-deny guard.

### Symbol References

No public symbol is renamed or removed. If a new workspace-wide live-attempt helper replaces exact-scope lookup at the capacity boundary, keep existing scoped callers unchanged unless their semantics require the global view.

### Split Judgment

Keep one plan. Workspace occupancy, bounded admission, preflight failure, claim acquisition, restricted reclassification, and their scheduler tests form one concurrency invariant. Splitting would permit an intermediate state that either exceeds the cap or launches an unclaimed task.

### Scope Rationale

Modify only the dispatcher capacity/preflight paths, their existing test module, and the active review evidence. Keep the documented `--max-parallel` contract unchanged. Exclude provider selector policy, provider-specific quotas, locator liveness rules, Go runtime configuration, roadmap state, deployments, and live provider smoke.

### Final Routing

- `evaluation_mode`: `isolated-reassessment`
- `finalizer`: `finalize-task-policy.sh` (`pair`)
- Build closures: `scope_closed=true`, `context_closed=true`, `verification_closed=true`, `evidence_trusted=true`, `ownership_closed=true`, `decision_closed=true`
- Build scores: scope `1`, state/concurrency `2`, blast/irreversibility `1`, evidence/diagnosis `1`, verification `1`; grade `G06`
- Build base/route: `local-fit` / `recovery-boundary`; lane `cloud`; canonical filename `PLAN-cloud-G06.md`
- Review closures: `scope_closed=true`, `context_closed=true`, `verification_closed=true`, `evidence_trusted=true`, `ownership_closed=true`, `decision_closed=true`
- Review scores: scope `1`, state/concurrency `2`, blast/irreversibility `1`, evidence/diagnosis `1`, verification `1`; grade `G06`
- Review route: `official-review`, lane `cloud`, Codex `gpt-5.6-sol` xhigh; canonical filename `CODE_REVIEW-cloud-G06.md`
- `large_indivisible_context=false`
- Positive loop risks: `temporal_state`, `concurrent_consistency`; count `2`
- Recovery signals: `review_rework_count=1`, `evidence_integrity_failure=true`
- Capability-gap evidence: none.

## Implementation Checklist

- [x] Make workspace-live capacity occupancy independent of orchestration scope and apply the same read-only calculation in dry-run.
- [x] Block every ready review after shared review preflight failure and refill only worker/self-check slots through deterministic atomic write-claim admission.
- [x] Replace helper-only and vacuous parallel-limit tests with deterministic scheduler regressions for cross-group occupancy, dry-run, mixed-role peak concurrency, cached re-admission, claim retention, and capped preflight refill.
- [x] Run the focused and full final verification commands and record fresh output.
- [x] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [REVIEW_API-1] Make live occupancy physical-workspace global

#### Problem

`orchestration_live_agent_processes()` at `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py:1080-1099` reads only `store.orchestration_tasks(scope)`. The capacity calculation at lines `6158-6171` passes `"__all__"`, but filtered runs register tasks under their task-group key, so another group's live attempt is invisible. The dry-run branch skips live occupancy entirely, contrary to the same-capacity preview contract.

#### Solution

Add a read-only workspace-wide live-attempt query that evaluates every persisted task state with the existing `external_active_is_live()` workspace identity checks. Keep the exact-scope helper for scoped reconciliation if needed. Use the global query when calculating `occupied_names` for both live and dry-run dispatch; dry-run must not call any mutating store method. Continue removing `finished_names` after active state is cleared and union the result with `running` to prevent double counting.

Before:

```python
# dispatch.py:6158-6171
workspace_live: dict[str, str] = {}
if not args.dry_run:
    workspace_live = orchestration_live_agent_processes(
        store, "__all__",
    )
    workspace_live = {
        name: detail
        for name, detail in workspace_live.items()
        if name not in finished_names
    }
occupied_names = set(running) | set(workspace_live)
```

After:

```python
workspace_live = workspace_live_agent_processes(store)
workspace_live = {
    name: detail
    for name, detail in workspace_live.items()
    if name not in finished_names
}
occupied_names = set(running) | set(workspace_live)
```

The new helper must iterate the workspace's persisted task-state map rather than one orchestration key and must remain read-only.

#### Modified Files and Checklist

- [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`: add or adapt a workspace-wide live-attempt query using current workspace identity validation.
- [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`: use the global query for filtered, unfiltered, and dry-run capacity calculation without double-counting current futures.

#### Test Strategy

Add scheduler tests in `ParallelLimitSchedulingTest` that build separate orchestration-scope records in one `StateStore`, make the other-group task live through supported locator/PID seams, and assert a filtered run admits no new task at limit `1`. Add a dry-run variant that proves the same occupancy, reports the capacity waiter, launches no runner, and leaves the store snapshot byte-for-byte unchanged.

#### Verification

```bash
python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ParallelLimitSchedulingTest
```

Expected: the real-state cross-group and dry-run regressions pass without mocking the workspace-wide occupancy result.

### [REVIEW_API-2] Make preflight failure block reviews and atomically claim refills

#### Problem

The refill loop at `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py:6491-6533` iterates the unordered `capacity_waiting` set without filtering by stage and appends tasks directly to `candidates`. It therefore bypasses `select_dispatch_candidates()` claim acquisition. A deterministic reviewer reproducer with three review-stage tasks and one worker observed one review invocation after preflight failure and observed the worker enter `run_worker()` without a write claim.

#### Solution

When shared review preflight fails:

1. Apply the same blocker to every ready review, including reviews deferred only by capacity.
2. Retain claims only for reviews that already received admission slots, as required by the lifecycle contract; do not synthesize claims for never-admitted reviews.
3. Remove all reviews from refill eligibility.
4. Preserve stable scheduler order by deriving refill inputs from ordered `deferred` entries, not a set.
5. Pass only worker/self-check refill candidates through `select_dispatch_candidates(..., persist=True, available_slots=freed_slots)` so existing global claims, canonical write sets, and claim replacement are revalidated atomically.
6. Rebuild `capacity_waiting` from any remaining capacity-only refill deferrals and recompute the final batch snapshot from the actual launched candidates.

Before:

```python
# dispatch.py:6491-6533
if available_slots is not None and review_removed:
    freed = len(review_removed)
    refill_ready: list[tuple[Task, str]] = []
    for task_name in list(capacity_waiting):
        ...
        refill_ready.append((task_obj, current_stage))
    if refill_ready:
        candidates = candidates + refill_ready
```

After:

```python
ready_reviews = [
    (task, stage) for task, stage in ready if stage == "review"
]
refill_inputs = [
    (task, stage)
    for task, stage, reason in deferred
    if stage in {"worker", "selfcheck"}
    and reason.startswith("capacity waiting:")
]
refilled, refill_deferred, _ = select_dispatch_candidates(
    store,
    refill_inputs,
    persist=True,
    available_slots=freed_slots,
)
candidates.extend(refilled)
```

The implementation must also block the never-admitted ready reviews and preserve any selected review claim without allowing those reviews to launch.

#### Modified Files and Checklist

- [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`: block all ready reviews on shared preflight failure.
- [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`: refill only stable ordered worker/self-check candidates through normal persistent claim admission.
- [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`: rebuild waiting state and batch snapshots from the final admitted set.

#### Test Strategy

Add a scheduler regression with at least three tasks explicitly persisted in review stage plus one worker, `max_parallel=2`, and a failing `ensure_review_shared_state`. Assert zero `run_review` calls, one eligible worker launch, a valid worker write claim visible at runner entry, retained claims only for reviews that were originally admitted, no synthesized claim for the never-admitted review, stable refill order, and no peak-cap violation.

#### Verification

```bash
python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ParallelLimitSchedulingTest BlockerDrainTest ReviewSchedulingTest WriteSetTest
```

Expected: all ready reviews remain blocked after preflight failure, the refill worker/self-check owns a claim before launch, and prior unlimited behavior remains green.

### [REVIEW_API-3] Replace vacuous tests with scheduler-level concurrency evidence

#### Problem

Several tests in `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py:11456-11770` do not reach the behavior named by their test names:

- limit-one re-admission uses a second temporary workspace and direct selector calls;
- dry-run selects no `sim` task and returns through `unobserved-task-group`;
- cross-group occupancy replaces the helper with a constant;
- capped preflight never marks a task `worker_done`, so no review stage exists;
- peak concurrency and pre-existing capacity-deferred claim retention are not asserted.

The class also lacks the testing-domain default provider-deny guard.

#### Solution

Refactor `ParallelLimitSchedulingTest` around reusable temporary-workspace task/state builders and a class-level default provider-deny patch. Use fake role coroutines with `asyncio.Event` and active/peak counters. Assert scheduler scans, stage order, state snapshots, claim ownership at runner entry, exact return codes, and non-mutation in dry-run. Patch only runner seams required by each scenario; do not mock the behavior under test.

#### Modified Files and Checklist

- [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`: add a default provider subprocess/runner deny guard for every new test.
- [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`: replace limit-one and limit-two helper checks with real `dispatch_with_store` peak-concurrency and cached-reclassification scenarios.
- [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`: build real cross-group live state and dry-run state snapshots without mocking the workspace-global occupancy query.
- [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`: create actual review-stage preflight scenarios and assert claim ownership and zero review execution.
- [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`: verify negative and non-integer CLI inputs through the CLI/main exit-code boundary, not only the helper.

#### Test Strategy

Write all regressions in the existing test module. Use only standard-library temporary directories and fakes. The pass oracle is peak active task-stage attempts no greater than the configured limit, deterministic stage/start order, restricted scan count, correct workspace-global occupancy, exact claim snapshots, correct return/orchestration state, unchanged dry-run state, and zero provider execution.

#### Verification

```bash
python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ParallelLimitSchedulingTest
python3 -m unittest discover -s agent-ops/skills/project/orchestrate-agent-task-loop/tests -p 'test_*.py'
```

Expected: focused scheduler regressions and the complete dispatcher suite pass without a real provider process.

## Modified Files Summary

| File | Items |
|---|---|
| `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py` | REVIEW_API-1, REVIEW_API-2 |
| `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py` | REVIEW_API-1, REVIEW_API-2, REVIEW_API-3 |
| `agent-task/dispatcher_parallel_limit/CODE_REVIEW-cloud-G06.md` | REVIEW_API-1, REVIEW_API-2, REVIEW_API-3 |

## Final Verification

Run from `/config/workspace/iop`:

```bash
python3 -m py_compile agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py
python3 agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py --help | rg --fixed-strings -- '--max-parallel MAX_PARALLEL'
python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ParallelLimitSchedulingTest ReviewSchedulingTest WriteSetTest BlockerDrainTest DispatcherConvergenceSimulationTest
python3 -m unittest discover -s agent-ops/skills/project/orchestrate-agent-task-loop/tests -p 'test_*.py'
git diff --check
```

Expected: compilation and help checks pass; scheduler-level focused tests prove the global cap, dry-run, restricted re-admission, preflight blocking, and claim ownership; the complete dispatcher suite passes fresh; `git diff --check` prints nothing. Python test cache is not accepted.

After completing all code changes, fill implementation-owned sections in `CODE_REVIEW-*-G??.md`.
