<!-- task=m-iop-owned-single-request-agent-execution/07+04_workspace_catalog plan=1 tag=REVIEW_API milestone-task=workspace-binding -->

# Workspace Catalog Ownership and Boundedness Corrections

## For the Implementing Agent

Implement only the direct fixes and files named below. Run every verification command, fill the paired review stub with actual notes and stdout/stderr, keep both active files in place, and report ready for review. If blocked, record the exact blocker, attempted command/output, and resume condition only 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`; finalization belongs to the code-review skill.

## Background

The first review found that the catalog can be bypassed through direct `LoadFromConfig`, that a workspace lookup leaks a mutable store-owned record, and that admitted operations can have zero effective bounds. The contract also overstates secrecy by forbidding the future private Edge-Node capability payload that the approved SDD requires. This follow-up closes those exact ownership, boundedness, test, and documentation gaps without implementing workspace wire or execution.

## Archive Evidence Snapshot

- Prior plan: `agent-task/m-iop-owned-single-request-agent-execution/07+04_workspace_catalog/plan_local_G06_0.log`
- Prior review: `agent-task/m-iop-owned-single-request-agent-execution/07+04_workspace_catalog/code_review_cloud_G07_0.log`
- Verdict: FAIL with Required R1-R4, no Suggested or Nit findings.
- R1: direct `LoadFromConfig` accepts duplicate workspace refs and lacks the planned NodeStore regression tests.
- R2: `ResolveWorkspace` returns the store-owned `*NodeRecord`, allowing mutation of `Workspaces` after the lock is released.
- R3: zero limits admit enabled operations without effective byte or timeout bounds, contrary to the bounded-capability plan and SDD D06.
- R4: contract/spec/example wording incorrectly forbids the future dedicated Node-private capability payload required by SDD D03/D08.
- Reviewer verification: focused config, focused race, package, vet, and whitespace commands exited 0, but the NodeStore focused pattern reported `[no tests to run]`; `review_rework_count=1`, `evidence_integrity_failure=true`.
- Roadmap carryover: keep `milestone-task=workspace-binding`; this packet contributes the catalog foundation for S04 and does not assert the full Milestone Task complete.

## Finding Resolution Map

| Finding | Mode | Exact fix | Changed precondition |
|---------|------|-----------|----------------------|
| R1 | `direct-fix` | Canonicalize and reject empty/duplicate workspace refs in `apps/edge/internal/node/store.go`; add within-node and cross-node direct-load tests in `apps/edge/internal/node/store_test.go`. | Direct `LoadFromConfig` can no longer create ambiguous ownership. |
| R2 | `direct-fix` | Return a record copy whose workspace catalog and nested slices are deep-copied; test mutation of both lookup return values. | A lookup result no longer aliases the store-owned catalog. |
| R3 | `direct-fix` | Require positive effective limits for every enabled operation in `packages/go/config/load.go`; replace the unbounded omission test with per-operation rejection and boundary coverage; align config/contract/spec. | Every admitted enabled operation is bounded while an empty catalog remains compatible. |
| R4 | `direct-fix` | Narrow payload secrecy wording in `configs/edge.yaml`, `agent-contract/inner/edge-config-runtime-refresh.md`, and `agent-spec/runtime/provider-pool-config-refresh.md` to public/preset/provider payloads and state that dedicated Node-private transport is deferred. | Later SDD D03/D08 work is no longer prohibited by this packet's contract. |

## Analysis

### Files Read

- `AGENTS.md`
- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-ops/rules/common/rules-agent-spec.md`
- `agent-ops/rules/project/domain/edge/rules.md`
- `agent-ops/rules/project/domain/platform-common/rules.md`
- `agent-ops/rules/project/domain/testing/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/edge-smoke.md`
- `agent-test/local/platform-common-smoke.md`
- `agent-roadmap/phase/knowledge-tool-optimization-extension/PHASE.md`
- `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/iop-owned-single-request-agent-execution.md`
- `agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-owned-single-request-agent-execution/SDD.md`
- `agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/04+02_preset_refresh/complete.log`
- `agent-task/m-iop-owned-single-request-agent-execution/07+04_workspace_catalog/plan_local_G06_0.log`
- `agent-task/m-iop-owned-single-request-agent-execution/07+04_workspace_catalog/code_review_cloud_G07_0.log`
- `packages/go/config/load.go`
- `packages/go/config/workspace_config_test.go`
- `apps/edge/internal/node/store.go`
- `apps/edge/internal/node/store_test.go`
- `configs/edge.yaml`
- `agent-contract/index.md`
- `agent-contract/inner/edge-config-runtime-refresh.md`
- `agent-spec/index.md`
- `agent-spec/runtime/provider-pool-config-refresh.md`

### SDD Criteria

- SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-owned-single-request-agent-execution/SDD.md`, status `[승인됨]`, lock released.
- Milestone metadata remains `milestone-task=workspace-binding`.
- Target scenario: S04 requires approved workspace identity to fail closed before execution; this packet supplies the operator catalog and ownership boundary, while path/symlink admission remains deferred.
- Evidence Map: S04 expects workspace route/path/symlink admission evidence. This follow-up requires deterministic direct-load uniqueness, immutable lookup, and effective-bound tests as catalog evidence without claiming the later path/symlink executor evidence.
- D03 and D08 require a dedicated Mac Node-owned typed boundary; R4 documentation changes preserve that later private transport instead of prohibiting it.
- D06 requires bounded request-scoped execution; R3 prevents the catalog from admitting enabled operations with no effective bound.

### Verification Context

- No separate verification handoff was supplied. Repository-native sources were `agent-test/local/rules.md`, `edge-smoke.md`, `platform-common-smoke.md`, the archived predecessor completion, package manifests, and the active plan commands.
- Local preflight: `/config/workspace/iop-s0`, Go `go1.26.2 linux/arm64`, shared dirty worktree with unrelated sibling packet changes.
- Fresh reviewer commands passed for config race, NodeStore/config-refresh race, package regression, vet, and `git diff --check`; the NodeStore focused package printed `[no tests to run]`, proving the required test gap.
- External verification is not required: this packet changes only catalog validation/store semantics and explicitly does not implement Mac filesystem, wire, process, or coordinator execution.
- Precondition `04+02_preset_refresh` is satisfied by `agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/04+02_preset_refresh/complete.log`.
- Confidence: high; every finding has a direct source path and deterministic unit/race oracle.

### Test Coverage Gaps

- Direct `LoadFromConfig` workspace ref uniqueness: uncovered; add canonical within-node and cross-node duplicate cases.
- `ResolveWorkspace` owner/catalog immutability: uncovered; mutate the returned workspace and returned record catalog, then re-resolve.
- Missing effective limit for each enabled operation: current test explicitly accepts all-zero limits; replace it with read/list/write/command rejection cases while retaining lower/upper boundary success tests.
- Payload-boundary wording: no executable behavior; verify deterministic searches and keep runtime deferral explicit.

### Symbol References

- No symbol is renamed or removed. `ResolveWorkspace` has no call sites outside `apps/edge/internal/node/store.go`, so its copy semantics can be corrected without caller migration.

### Split Judgment

- Keep one follow-up packet: uniqueness, immutable lookup, effective bounds, and the matching contract wording form one compact workspace-catalog admission invariant. Splitting would allow a misleading intermediate contract.
- Runtime predecessor `04` is already satisfied by the exact archived `complete.log` above; directory dependency `07+04` remains unchanged.

### Scope Rationale

- Include only NodeStore workspace ownership/copy behavior, config effective-bound validation, targeted tests, and the three existing operator-facing contract/example documents.
- Exclude protobuf, Edge-Node workspace requests/results, Mac filesystem containment, symlink checks, process execution, coordinator admission, and all unrelated shared-worktree changes; those remain later packets.

### Final Routing

- `evaluation_mode=isolated-reassessment`; finalizer `finalize-task-policy.sh` in `pair` mode.
- Build closures are all true; scores `2/1/2/1/1 = G07`, base `local-fit`, final route `recovery-boundary` because `review_rework_count=1` and `evidence_integrity_failure=true`; lane `cloud`, filename `PLAN-cloud-G07.md`.
- Build signals: `large_indivisible_context=false`; matched loop risks `boundary_contract`, `structured_interpretation`, `concurrent_consistency`, `variant_product` (4); risk and recovery boundaries matched; no capability gap.
- Review closures are all true; scores `2/1/2/1/1 = G07`; route `official-review`, lane `cloud`, filename `CODE_REVIEW-cloud-G07.md`.

## Dependencies and Execution Order

1. Preserve the completed `04+02_preset_refresh` dependency evidence.
2. Fix direct-load uniqueness and lookup aliasing together, then add the NodeStore regressions.
3. Restore effective operation bounds and config tests.
4. Synchronize example, contract, and living spec wording, then run the full verification set.

## Implementation Checklist

- [ ] Enforce canonical unique workspace refs in direct `LoadFromConfig` and ensure `ResolveWorkspace` returns no mutable workspace-catalog aliases.
- [ ] Require positive effective bounds for every enabled workspace operation while preserving existing absolute maxima and empty-catalog compatibility.
- [ ] Align the config example, inner contract, and living spec with effective bounds and the deferred private Node payload boundary without claiming wire or executor implementation.
- [ ] Add targeted NodeStore/config regressions and run focused race, package, vet, documentation, formatting, and whitespace verification.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

## Implementation Plan

### [REVIEW_API-1] Close direct-load and lookup ownership gaps

**Problem**

- `apps/edge/internal/node/store.go:129` validates node token/alias/id but never canonicalizes or deduplicates `Workspaces`, so direct callers bypass the global ref invariant.
- `apps/edge/internal/node/store.go:121` returns the store-owned record pointer after releasing the read lock, exposing its `Workspaces` slice to mutation.

**Solution**

Before (`apps/edge/internal/node/store.go:129`):

```go
func LoadFromConfig(defs []config.NodeDefinition) (*NodeStore, error) {
    s := NewNodeStore()
    seenToken := make(map[string]bool)
```

After, preserve the API while adding canonical ownership validation and reusable workspace cloning:

```go
func LoadFromConfig(defs []config.NodeDefinition) (*NodeStore, error) {
    s := NewNodeStore()
    seenWorkspaceRef := make(map[string]struct{})
    // Trim each copied ref, reject empty/duplicate canonical refs globally,
    // and store only deep-copied workspace definitions.
}

func (s *NodeStore) ResolveWorkspace(ref string) (*NodeRecord, config.WorkspaceDefinition, error) {
    // Return a record copy with a deep-copied Workspaces catalog plus a
    // separately deep-copied matching definition; never return rec directly.
}
```

Do not broaden this packet into changing existing `FindByID`, `FindByToken`, provider, adapter, or runtime ownership semantics.

**Modified Files and Checklist**

- [ ] `apps/edge/internal/node/store.go` — canonical direct-load ref validation and non-aliasing workspace clone helpers.
- [ ] `apps/edge/internal/node/store_test.go` — direct duplicate/missing lookup and nested mutation-isolation regressions.

**Test Strategy**

- Add `TestLoadFromConfig_WorkspaceRefValidation` with within-node, cross-node, whitespace-canonical duplicate, and valid unique cases.
- Add `TestNodeStore_ResolveWorkspaceImmutableCopies` that mutates the source config after load, the returned definition, nested command args/env/operations, and the returned owner's `Workspaces`, then re-resolves and asserts the stored catalog and owner identity are unchanged.
- Add a missing-ref assertion. Keep tests in the existing external `node_test` package so only exported behavior is exercised.

**Verification**

- `go test -race ./apps/edge/internal/node -run 'Test(LoadFromConfig.*Workspace|NodeStore.*Workspace)' -count=1`
- Expected: named tests execute (no `[no tests to run]`), direct duplicates fail, and all mutation attempts remain isolated.

### [REVIEW_API-2] Restore effective bounds and the private payload contract

**Problem**

- `packages/go/config/load.go:634` skips validation for zero values, admitting enabled operations with no effective bound.
- `packages/go/config/workspace_config_test.go:966` codifies the unauthorized all-zero behavior as backward compatibility.
- `agent-contract/inner/edge-config-runtime-refresh.md:71` prohibits all runtime payloads even though the approved SDD requires a later private typed Edge-Node capability input.

**Solution**

Before (`packages/go/config/load.go:634`):

```go
if ws.MaxReadBytes != 0 && (ws.MaxReadBytes < 1 || ws.MaxReadBytes > maxByteLimit) {
    return fmt.Errorf("...", ws.MaxReadBytes)
}
```

After, first retain the absolute range checks, then require effective positive fields for enabled operations:

```go
// read requires max_read_bytes; write requires max_write_bytes;
// list/command require max_output_bytes; command also requires
// max_command_timeout_ms. Empty workspaces remain compatible.
```

Update the example, contract, and spec to describe those effective requirements. State that raw roots/templates never enter execution presets, caller-visible responses, provider requests, or public metadata; the dedicated Node-private config/admission transport required by SDD D03/D08 is deferred and not implemented here.

**Modified Files and Checklist**

- [ ] `packages/go/config/load.go` — enforce positive effective operation bounds plus current absolute maxima.
- [ ] `packages/go/config/workspace_config_test.go` — replace unbounded omission acceptance with per-operation missing/negative rejection and valid boundary cases.
- [ ] `configs/edge.yaml` — document required bounds and the precise private/public payload boundary.
- [ ] `agent-contract/inner/edge-config-runtime-refresh.md` — align normative schema/boundary wording.
- [ ] `agent-spec/runtime/provider-pool-config-refresh.md` — synchronize current implementation and explicit later-wire deferral.

**Test Strategy**

- Extend `TestLoadEdgeWorkspaceCatalogRejectsInvalid` with missing effective bound cases for read, list output, write, command output, and command timeout, plus negative values.
- Keep `TestLoadEdgeWorkspaceCatalog` valid fixtures bounded for every enabled operation.
- Retain exact lower (`1`) and upper (1 GiB / one hour) acceptance assertions.

**Verification**

- `go test -race ./packages/go/config -run '^TestLoadEdgeWorkspaceCatalog' -count=1`
- `rg --sort path -n 'workspaces|workspace_ref|effective|Node-private|restart_required' configs/edge.yaml agent-contract/inner/edge-config-runtime-refresh.md agent-spec/runtime/provider-pool-config-refresh.md`
- Expected: every enabled operation has an effective positive bound, maxima still hold, and docs allow only the deferred private Node boundary.

## Modified Files Summary

| File | Item |
|------|------|
| `apps/edge/internal/node/store.go` | REVIEW_API-1 |
| `apps/edge/internal/node/store_test.go` | REVIEW_API-1 |
| `packages/go/config/load.go` | REVIEW_API-2 |
| `packages/go/config/workspace_config_test.go` | REVIEW_API-2 |
| `configs/edge.yaml` | REVIEW_API-2 |
| `agent-contract/inner/edge-config-runtime-refresh.md` | REVIEW_API-2 |
| `agent-spec/runtime/provider-pool-config-refresh.md` | REVIEW_API-2 |
| `agent-task/m-iop-owned-single-request-agent-execution/07+04_workspace_catalog/CODE_REVIEW-cloud-G07.md` | REVIEW_API-1, REVIEW_API-2 |

## Final Verification

1. `test -f agent-task/m-iop-owned-single-request-agent-execution/04+02_preset_refresh/complete.log || test "$(compgen -G 'agent-task/archive/*/*/m-iop-owned-single-request-agent-execution/04+02_preset_refresh/complete.log' | wc -l)" -eq 1`
2. `test -z "$(gofmt -l packages/go/config/load.go packages/go/config/workspace_config_test.go apps/edge/internal/node/store.go apps/edge/internal/node/store_test.go)"`
3. `go test -race ./packages/go/config -run '^TestLoadEdgeWorkspaceCatalog' -count=1`
4. `go test -race ./apps/edge/internal/node -run 'Test(LoadFromConfig.*Workspace|NodeStore.*Workspace)' -count=1`
5. `go test -race ./apps/edge/internal/configrefresh -run '^TestClassifyWorkspace' -count=1`
6. `go test ./packages/go/config ./apps/edge/internal/node ./apps/edge/internal/configrefresh -count=1`
7. `go test ./packages/go/... ./apps/edge/... -count=1`
8. `go vet ./packages/go/... ./apps/edge/...`
9. `rg --sort path -n 'workspaces|workspace_ref|effective|Node-private|restart_required' configs/edge.yaml agent-contract/inner/edge-config-runtime-refresh.md agent-spec/runtime/provider-pool-config-refresh.md`
10. `git diff --check`

Expected: the predecessor remains unique; named NodeStore tests execute; direct ref ownership and lookup copies are immutable; every enabled operation is effectively bounded; config/Edge regressions and vet pass; documentation preserves only the deferred private Node capability boundary. All Go test commands use `-count=1`; cached output is not acceptable.

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