chore(agent-task): 완료 작업 로그를 아카이브한다

This commit is contained in:
toki 2026-08-09 11:00:51 +09:00
parent 1ed718cadc
commit 4dba4c430e
10 changed files with 593 additions and 6 deletions

View file

@ -62,16 +62,16 @@ Review completion means the following steps are finished:
> **[REVIEW AGENT ONLY]** This checklist is used only by the review agent.
> Implementing agents must not modify or check this section.
- [ ] Append one verdict of `PASS`, `WARN`, or `FAIL` and verified `review_rework_count`, `evidence_integrity_failure` to `Code Review Result`.
- [ ] Verify that verdict, `Dimension Assessment`, and Required/Suggested/Nit classifications match.
- [ ] Archive active `CODE_REVIEW-*-G??.md` to `code_review_cloud_G08_2.log`.
- [ ] Archive active `PLAN-*-G??.md` to `plan_cloud_G08_2.log`.
- [ ] Verify that the Agent-Ops managed block in `.gitignore` unignores `agent-task/**/*.md` and `agent-task/**/*.log` and ignores `agent-roadmap/current.md`.
- [x] Append one verdict of `PASS`, `WARN`, or `FAIL` and verified `review_rework_count`, `evidence_integrity_failure` to `Code Review Result`.
- [x] Verify that verdict, `Dimension Assessment`, and Required/Suggested/Nit classifications match.
- [x] Archive active `CODE_REVIEW-*-G??.md` to `code_review_cloud_G08_2.log`.
- [x] Archive active `PLAN-*-G??.md` to `plan_cloud_G08_2.log`.
- [x] Verify that the Agent-Ops managed block in `.gitignore` unignores `agent-task/**/*.md` and `agent-task/**/*.log` and ignores `agent-roadmap/current.md`.
- [ ] If PASS, write `complete.log` based on `agent-ops/skills/common/code-review/templates/complete-log-template.md` and leave no active `.md` files.
- [ ] If PASS, move active task directory `agent-task/single_request_plan_review_templates/` to `agent-task/archive/YYYY/MM/single_request_plan_review_templates/` and update this checklist at the final archive path.
- [ ] If PASS and task group is `m-<milestone-slug>`, preserve and report `milestone-task` metadata for runtime aggregation, without modifying roadmap or directly calling `update-roadmap`.
- [ ] If PASS for split work, remove empty active parent `agent-task/single_request_plan_review_templates/` or verify it was kept due to remaining siblings/files.
- [ ] If WARN/FAIL, write the next filesystem state matching code-review verdict and do not write `complete.log`.
- [x] If WARN/FAIL, write the next filesystem state matching code-review verdict and do not write `complete.log`.
## Deviations from Plan
@ -328,3 +328,21 @@ Resume condition — live qualification can run only after the operator does bot
| Reviewer Checkpoints | Fixed at stub creation | Pre-filled from plan |
| Verification Results (section headings + commands) | Fixed at stub creation | Implementing agent fills in command output only; command changes require a `Deviations from Plan` entry |
| Code Review Result | Review agent appends | Not included in stub |
## Code Review Result
- Overall Verdict: `FAIL`
- Dimension Assessment:
- Correctness: Fail
- Completeness: Fail
- Test coverage: Fail
- API contract: Fail
- Code quality: Pass
- Implementation deviation: Fail
- Verification trust: Fail
- Findings:
- Required R1 — `packages/go/config/load.go:72`, `packages/go/config/load.go:85`, `packages/go/config/load.go:697`, `packages/go/config/execution_preset_types.go:293`: the pointer fields distinguish an omitted key from an explicit string value, but mapstructure decodes a present YAML null (`plan_file: null`, `review_file: null`, or `~`) to the same nil pointer as omission. `resolveSingleRequestTemplates` therefore silently selects the built-in default for a key that was explicitly configured without a non-empty path, contradicting the plan invariant that field omission is the only fallback signal and the current spec requirement that configured paths be non-empty. Preserve raw nested key presence before mapstructure erases null, reject a present null for both fields with the existing field context before template filesystem access, and add Plan/Review null regressions while retaining omitted, empty-string, whitespace, relative-path, clone, and refresh-redaction coverage. Fresh reviewer evidence: `go test -count=1 ./packages/go/config -run 'TestLoadEdgeSingleRequestTemplates/explicitly_configured_empty_template_path_fails_closed/(null_plan_file|null_review_file)$' -v` failed because both null values loaded successfully; a separate read-only Viper probe confirmed `v.Get("execution_presets")` retains each present key with a nil raw value, so the defect is repository-fixable before strict decode. Both temporary reviewer tests were removed after reproduction.
- Routing Signals:
- `review_rework_count=3`
- `evidence_integrity_failure=true`
- Next Step: Invoke the plan skill in `prepare-follow-up` mode with Required R1 as a repository-owned direct fix, then archive this pair and materialize the freshly routed follow-up pair. Preserve the approved remote changed-package verification and exact operator-asset blocker, but do not create `USER_REVIEW.md` because raw Viper presence remains available to a safe repository fix. Do not write `complete.log`.

View file

@ -0,0 +1,269 @@
<!-- task=single_request_plan_review_templates plan=3 tag=REVIEW_API -->
# Code Review Reference - REVIEW_API
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> The task is NOT complete until every implementation-owned section below is filled in.
> Complete the `Implementation Checklist`; the final checklist item is mandatory before saving.
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
> Execute the plan's selected root cause, scope, files, and dependency decisions as written. Do not choose another owner, narrow/expand the write boundary, or replace a fix with another verification attempt.
> If implementation is blocked, record the exact blocker, attempted commands/output, and resume condition only in implementation-owned evidence fields.
> Do not ask the user directly, present choices, call user-input tools, create control-plane stop files, or classify the next state.
> Finalization (`Code Review Result`, log rename, `complete.log`, archive moves, `Review-Only Checklist`) is review-agent-only, even after compaction/resume.
> Follow the ownership table at the bottom of this file for which sections you own.
## Overview
date=2026-08-09 KST
task=single_request_plan_review_templates, plan=3, tag=REVIEW_API
## Archive Evidence Snapshot
- Prior task: `single_request_plan_review_templates`, plan `2`, tag `REVIEW_API`.
- Archived plan: `agent-task/single_request_plan_review_templates/plan_cloud_G08_2.log`.
- Archived review: `agent-task/single_request_plan_review_templates/code_review_cloud_G08_2.log`.
- Prior verdict: `FAIL`; Required R1 proves that present YAML null template keys collapse to nil pointers and silently select defaults.
- Fresh reviewer regression: the two null Plan/Review cases loaded successfully instead of returning the configured-field empty-path error. A separate Viper probe showed that `v.Get("execution_presets")` retains both keys with nil raw values, so validation can reject them before mapstructure decode.
- Existing formatting, focused, race, full Edge/Go, smoke self-test, empty/whitespace, clone, and refresh-redaction checks passed. The approved Darwin/arm64 changed-package evidence belongs to source commit `6f141b91`; the next implementation must synchronize and retest its new source commit. Live API qualification remains externally blocked by the absent operator config files and complete `IOP_SINGLE_REQUEST_SMOKE_*` inputs.
- Roadmap carryover: none; this is a non-milestone task.
## For the Review Agent
> **[REVIEW AGENT ONLY]** The finalization steps below are review-agent only. Implementing agents must not execute this section.
Compare implementation of each item against source files and verify that output in `Verification Results` matches code.
Review completion means the following steps are finished:
1. Append verdict and `review_rework_count` / `evidence_integrity_failure` routing signals.
2. Archive `CODE_REVIEW-cloud-G08.md` → `code_review_cloud_G08_3.log` and `PLAN-cloud-G08.md` → `plan_cloud_G08_3.log`.
3. If PASS, write `complete.log` and move active task directory to `agent-task/archive/YYYY/MM/single_request_plan_review_templates/`. If WARN/FAIL, fully write the next filesystem state required by the code-review skill.
4. If PASS and task group is `m-<milestone-slug>`, preserve the first-line `milestone-task` metadata in `complete.log` and report it for the runtime aggregation event. Roadmap state evaluation belongs to `sync-milestone-workstate`.
5. Check applicable `Review-Only Checklist` items at the final `.log` location before reporting.
---
## Implementation Item Completion
| Item | Status |
|------|---------|
| REVIEW_API-1 — Reject Present Null Template Paths Before Decode | [x] |
| REVIEW_API-2 — Cover Null Syntaxes And Preserve Neighboring Boundaries | [x] |
| REVIEW_API-3 — Produce Fresh Local And Remote Evidence | [x] |
## Implementation Checklist
- [x] [REVIEW_API-1] Reject present null Plan/Review template path keys from the raw Viper preset structure before mapstructure erases presence, while preserving true omission fallback.
- [x] [REVIEW_API-2] Add Plan/Review `null` and `~` regressions and retain empty/whitespace, omission, relative-path, clone, and refresh-redaction coverage.
- [x] [REVIEW_API-3] Run fresh focused, race, full, smoke-self-test, documentation, and synchronized Darwin/arm64 changed-package verification; retain the exact live-profile blocker if operator assets remain absent.
- [x] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.
## Review-Only Checklist
> **[REVIEW AGENT ONLY]** This checklist is used only by the review agent.
> Implementing agents must not modify or check this section.
- [x] Append one verdict of `PASS`, `WARN`, or `FAIL` and verified `review_rework_count`, `evidence_integrity_failure` to `Code Review Result`.
- [x] Verify that verdict, `Dimension Assessment`, and Required/Suggested/Nit classifications match.
- [x] Archive active `CODE_REVIEW-*-G??.md` to `code_review_cloud_G08_3.log`.
- [x] Archive active `PLAN-*-G??.md` to `plan_cloud_G08_3.log`.
- [x] Verify that the Agent-Ops managed block in `.gitignore` unignores `agent-task/**/*.md` and `agent-task/**/*.log` and ignores `agent-roadmap/current.md`.
- [x] If PASS, write `complete.log` based on `agent-ops/skills/common/code-review/templates/complete-log-template.md` and leave no active `.md` files.
- [x] If PASS, move active task directory `agent-task/single_request_plan_review_templates/` to `agent-task/archive/YYYY/MM/single_request_plan_review_templates/` and update this checklist at the final archive path.
- [ ] If PASS and task group is `m-<milestone-slug>`, preserve and report `milestone-task` metadata for runtime aggregation, without modifying roadmap or directly calling `update-roadmap`.
- [ ] If PASS for split work, remove empty active parent `agent-task/single_request_plan_review_templates/` or verify it was kept due to remaining siblings/files.
- [ ] If WARN/FAIL, write the next filesystem state matching code-review verdict and do not write `complete.log`.
## Deviations from Plan
없음.
## Key Design Decisions
- `rejectNullSingleRequestTemplatePaths`는 Viper raw `execution_presets` 구조에서 오직 존재하면서 값이 `nil`인 `single_request.templates.plan_file` 및 `review_file`만 거부한다. 중간 구조가 예상 형식이 아니거나 값이 non-nil이면 strict mapstructure decoder와 기존 template loader가 기존 오류 책임을 유지한다.
- 진짜 생략은 nil pointer fallback으로 보존한다. 따라서 Plan/Review 기본 템플릿의 독립 fallback, 기존 empty/whitespace rejection, 상대 경로 해석, clone isolation 및 digest-only refresh 분류는 변경하지 않았다.
## Reviewer Checkpoints
- Confirm raw Viper presence is inspected before strict mapstructure decode and only present nil `plan_file`/`review_file` keys are rejected by the new helper.
- Confirm omitted keys remain nil pointers and independently select built-in defaults, while explicit empty/whitespace strings still reach `loadTemplateFile` rejection.
- Confirm both fields reject both `null` and `~` with exact field context and before template filesystem access.
- Confirm optional path cells remain deep-cloned and config refresh still compares equal path values without exposing paths or template bodies.
- Confirm contracts/specs remain unchanged and accurately require non-empty configured paths with omission-only fallback.
- Confirm local and approved Darwin/arm64 evidence belongs to the exact repaired source commit, and absent operator assets are recorded only as an external live-profile blocker.
## Verification Results
Record actual stdout/stderr and exit codes for every command. Do not summarize or reconstruct output.
### REVIEW_API-1 — Raw presence admission
```bash
go test -count=1 ./packages/go/config -run 'TestLoadEdgeSingleRequestTemplates'
```
```text
exit=0
ok iop/packages/go/config 0.036s
```
### REVIEW_API-2 — Focused, clone, and refresh regressions
```bash
go test -count=1 ./packages/go/config -run 'TestLoadEdgeSingleRequestTemplates|TestExecutionSingleRequestTemplatePathPresenceClone'
go test -count=1 ./packages/go/config ./apps/edge/internal/configrefresh
```
```text
exit=0
ok iop/packages/go/config 0.033s
ok iop/packages/go/config 0.177s
ok iop/apps/edge/internal/configrefresh 0.037s
```
### REVIEW_API-3 — Full local verification
```bash
test -z "$(gofmt -l packages/go/config/load.go packages/go/config/model_execution_preset_config_test.go)"
git diff --check
go test -count=1 ./packages/go/config -run 'TestLoadEdgeSingleRequestTemplates|TestExecutionSingleRequestTemplatePathPresenceClone'
go test -count=1 ./packages/go/config ./apps/edge/internal/configrefresh
go test -race -count=1 ./packages/go/config ./apps/edge/internal/configrefresh
go test -count=1 ./apps/edge/... ./packages/go/...
make test-single-request-claude-smoke-self-test
rg --sort path -n 'Absolute and empty paths are rejected before any filesystem access|plan_file.*review_file.*non-empty paths' agent-spec/runtime/provider-pool-config-refresh.md
```
```text
exit=0
# test -z "$(gofmt -l packages/go/config/load.go packages/go/config/model_execution_preset_config_test.go)"
# git diff --check
# both commands produced no stdout/stderr
ok iop/packages/go/config 0.033s
ok iop/packages/go/config 0.177s
ok iop/apps/edge/internal/configrefresh 0.037s
ok iop/packages/go/config 1.879s
ok iop/apps/edge/internal/configrefresh 1.135s
ok iop/apps/edge/cmd/edge 0.181s
ok iop/apps/edge/internal/authprojection 0.049s
ok iop/apps/edge/internal/bootstrap 0.502s
ok iop/apps/edge/internal/configrefresh 0.093s
ok iop/apps/edge/internal/controlplane 6.625s
ok iop/apps/edge/internal/edgecmd 0.110s
ok iop/apps/edge/internal/edgevalidate 0.063s
ok iop/apps/edge/internal/events 0.043s
ok iop/apps/edge/internal/input 0.087s
ok iop/apps/edge/internal/input/a2a 0.076s
ok iop/apps/edge/internal/node 0.061s
ok iop/apps/edge/internal/openai 8.778s
ok iop/apps/edge/internal/opsconsole 0.085s
ok iop/apps/edge/internal/service 8.253s
ok iop/apps/edge/internal/transport 4.774s
ok iop/packages/go/audit 0.011s
ok iop/packages/go/auth 10.030s
ok iop/packages/go/config 0.161s
ok iop/packages/go/credentiallease 0.035s
? iop/packages/go/events [no test files]
ok iop/packages/go/execution 0.011s
ok iop/packages/go/hostsetup 0.009s
? iop/packages/go/jobs [no test files]
? iop/packages/go/metadata [no test files]
ok iop/packages/go/observability 0.036s
? iop/packages/go/policy [no test files]
ok iop/packages/go/singlerequesttemplate 0.008s
ok iop/packages/go/streamgate 0.885s
? iop/packages/go/version [no test files]
ok iop/packages/go/workspaceprotocol 0.023s
[single-request-claude-smoke] self-test passed: exact Claude base-route coverage, structured observation admission, child-only zero retry, authenticated model admission, closed failure classification, model/Edge/Node/runtime binding, zero-child preflight, derived verification, redaction, cleanup, signal handling, and atomic publication
134:| single-request effective templates | Optional `templates` (`plan_file`/`review_file`) load bounded Markdown relative to the directory containing `edge.yaml` only. Absolute and empty paths are rejected before any filesystem access; non-regular files, sizes over 8192 bytes, non-UTF-8 content, and invalid template grammar fail closed at load. Each file falls back to its built-in default independently, and refresh diff evidence reports SHA-256 digests only, never template paths or contents. |
182:- `execution_presets[].single_request.templates` is optional. `plan_file` and `review_file` must be non-empty paths relative to the directory containing `edge.yaml`; absolute and empty values are rejected before the loader opens anything. Each file is loaded as a regular file of at most 8192 valid UTF-8 bytes and must satisfy the documented template grammar; an omitted file falls back to its built-in default independently of the other. Refresh diff evidence reports only SHA-256 digests of the effective templates. Admission freezes the effective pair into the request binding, so a refreshed template reaches only requests admitted after the refresh.
```
### REVIEW_API-3 — Approved Darwin/arm64 profile and external gate
```bash
test "$(git branch --show-current)" = "feature/single-request-plan-review-templates"
git rev-parse HEAD
git push origin feature/single-request-plan-review-templates
ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && export PATH=/opt/homebrew/bin:$PATH && git fetch origin feature/single-request-plan-review-templates && git switch feature/single-request-plan-review-templates && git reset --hard origin/feature/single-request-plan-review-templates && test -z "$(git status --short)" && test "$(git rev-parse HEAD)" = "$(git rev-parse origin/feature/single-request-plan-review-templates)" && test "$(go env GOOS)/$(go env GOARCH)" = "darwin/arm64" && go version && claude --version && sops --version && make build-edge build-node && go test -count=1 ./packages/go/config ./apps/edge/internal/configrefresh && make test-single-request-claude-smoke-self-test'
ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && for file in dev-openai-toki.edge.yaml dev-openai-toki.sops.yaml; do if test -f "$file"; then echo "PRESENT $file"; else echo "MISSING $file"; fi; done && test -r /Users/toki/.config/sops/age/keys.txt && echo "PRESENT age identity"'
```
Tested commit: `1ed718cadcdb6264aff62ba75115f17f42899df1`.
```text
exit=0
git push origin feature/single-request-plan-review-templates
remote:
remote: Create a new pull request for 'feature/single-request-plan-review-templates':
remote: https://git.toki-labs.com/toki/iop/compare/main...feature/single-request-plan-review-templates
remote:
To https://git.toki-labs.com/toki/iop.git
8d0baac5..1ed718ca feature/single-request-plan-review-templates -> feature/single-request-plan-review-templates
exit=0
From https://git.toki-labs.com/toki/iop
* branch feature/single-request-plan-review-templates -> FETCH_HEAD
6f141b91..1ed718ca feature/single-request-plan-review-templates -> origin/feature/single-request-plan-review-templates
Already on 'feature/single-request-plan-review-templates'
Your branch is behind 'origin/feature/single-request-plan-review-templates' by 2 commits, and can be fast-forwarded.
(use "git pull" to update your local branch)
HEAD is now at 1ed718ca fix(single_request): 명시된 null 템플릿 경로를 생략과 구분한다
go version go1.26.3 darwin/arm64
2.1.177 (Claude Code)
sops 3.13.1
[info] a new version of sops (v3.13.3) is available, you can update by visiting: https://github.com/getsops/sops/releases/tag/v3.13.3
[warning] Note that in a future version, sops will no longer check whether the current version is the latest when asking for sops' version. If you want to explicitly check for the latest version, add the `--check-for-updates` option to `sops --version`. This will hide this deprecation warning and will always check, even if the default behavior changes in the future.
mkdir -p build/bin
GOOS=darwin GOARCH=arm64 go build -trimpath -o build/bin/iop-edge ./apps/edge/cmd/edge
mkdir -p build/bin
go build -trimpath -o build/bin/iop-node ./apps/node/cmd/node
ok iop/packages/go/config 0.643s
ok iop/apps/edge/internal/configrefresh 0.837s
./scripts/e2e-single-request-claude.sh --self-test
[single-request-claude-smoke] self-test passed: exact Claude base-route coverage, structured observation admission, child-only zero retry, authenticated model admission, closed failure classification, model/Edge/Node/runtime binding, zero-child preflight, derived verification, redaction, cleanup, signal handling, and atomic publication
exit=0
MISSING dev-openai-toki.edge.yaml
MISSING dev-openai-toki.sops.yaml
PRESENT age identity
```
Live qualification was not run: both approved operator configuration files are missing. Blocker/resume condition: restore `dev-openai-toki.edge.yaml` and `dev-openai-toki.sops.yaml` under `/Users/toki/agent-work/iop-dev`, then supply all twelve Makefile-defined `IOP_SINGLE_REQUEST_SMOKE_*` inputs before live qualification.
---
> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section?**
> If anything is blank, go back and fill it in before saving this file.
> Leave review-agent-only sections unchanged.
## Section Ownership
| Section | Owner | Note |
|---------|-------|------|
| Header comment, Overview, Review Agent Instructions | Fixed at stub creation | Implementing agent must not modify or execute these (archive, complete.log, and task-directory archive move are review-agent only) |
| Archive Evidence Snapshot | Fixed at stub creation from plan when present | Implementing agent uses it as default prior-loop context; read only the specific archive files cited there when more detail is required |
| Implementation Item Completion (item names) | Fixed at stub creation | Implementing agent checks `[ ]` → `[x]` only |
| Implementation Checklist (item text/order) | Fixed at stub creation from plan | Implementing agent checks `[ ]` → `[x]` only |
| Review-Only Checklist | Review agent only | Implementing agent must not modify or check this section |
| Deviations from Plan, Key Design Decisions | Implementing agent | Replace placeholder text with actual content |
| Reviewer Checkpoints | Fixed at stub creation | Pre-filled from plan |
| Verification Results (section headings + commands) | Fixed at stub creation | Implementing agent fills in command output only; command changes require a `Deviations from Plan` entry |
| Code Review Result | Review agent appends | Not included in stub |
## Code Review Result
- Overall Verdict: `PASS`
- Dimension Assessment:
- Correctness: Pass
- Completeness: Pass
- Test coverage: Pass
- API contract: Pass
- Code quality: Pass
- Implementation deviation: Pass
- Verification trust: Pass
- Findings: None
- Routing Signals:
- `review_rework_count=3`
- `evidence_integrity_failure=false`
- Next Step: Write `complete.log`, archive the active pair, and move the completed task directory under the current `YYYY/MM` archive path.

View file

@ -0,0 +1,45 @@
<!-- task=single_request_plan_review_templates plan=3 tag=REVIEW_API -->
# Complete - single_request_plan_review_templates
## 완료 일시
2026-08-09
## 요약
4회 Plan/Review 루프에서 single-request Plan/Review 템플릿의 상대 경로·문법·presence 승인 경계를 보완했고, 최종 판정은 PASS다.
## 루프 이력
| Plan | Review | Verdict | 메모 |
|------|--------|---------|------|
| `plan_local_G07_0.log` | `code_review_cloud_G07_0.log` | FAIL | 상대 경로, 정확한 템플릿 문법, 결정적 경계 테스트, 계약·스펙 정합성 보완 필요 |
| `plan_cloud_G08_1.log` | `code_review_cloud_G08_1.log` | FAIL | 명시된 빈 문자열·공백 경로가 생략으로 접히는 presence 결함 발견 |
| `plan_cloud_G08_2.log` | `code_review_cloud_G08_2.log` | FAIL | YAML `null`·`~` 경로가 nil 포인터 생략으로 접히는 raw decode 결함 발견 |
| `plan_cloud_G08_3.log` | `code_review_cloud_G08_3.log` | PASS | raw Viper null presence 거부, 네 가지 회귀, 로컬·Darwin/arm64 검증 통과 |
## 구현/정리 내용
- `execution_presets[].single_request.templates.plan_file`과 `review_file`의 present-null 값을 strict preset decode 전에 raw Viper 구조에서 거부했다.
- 진짜로 생략된 키만 built-in 기본 템플릿 fallback을 선택하도록 기존 포인터·deep-clone 경계를 유지했다.
- Plan/Review 각각의 YAML `null`과 `~` 회귀를 추가하고 빈 문자열·공백·상대 경로·clone·refresh redaction 동작을 함께 검증했다.
- 현재 계약과 living spec이 이미 non-empty configured path와 omission-only fallback을 요구하므로 문서 변경은 필요하지 않았다.
## 최종 검증
- `go test -count=1 ./packages/go/config -run 'TestLoadEdgeSingleRequestTemplates|TestExecutionSingleRequestTemplatePathPresenceClone'` - PASS; null/tilde 네 변형을 포함한 loader·clone 회귀 통과
- `go test -count=1 ./packages/go/config ./apps/edge/internal/configrefresh` - PASS
- `go test -race -count=1 ./packages/go/config ./apps/edge/internal/configrefresh` - PASS
- `go test -count=1 ./apps/edge/... ./packages/go/...` - PASS
- `make test-single-request-claude-smoke-self-test` - PASS
- 승인된 Darwin/arm64 runner의 `1ed718cadcdb6264aff62ba75115f17f42899df1` source identity, 변경 패키지 테스트, smoke self-test - PASS
- live API qualification - BLOCKED; 승인된 operator config 두 파일과 Makefile 정의 `IOP_SINGLE_REQUEST_SMOKE_*` 입력 12개가 없어 요청을 실행하지 않음
## 잔여 Nit
- 없음
## 후속 작업
- 승인된 operator config 두 파일과 `IOP_SINGLE_REQUEST_SMOKE_*` 입력 12개가 복원되면 pre-existing live API qualification을 재실행한다.

View file

@ -0,0 +1,244 @@
<!-- task=single_request_plan_review_templates plan=3 tag=REVIEW_API -->
# Single-request Null Template Path Presence Repair - REVIEW_API
> **For the Implementing Agent:** Implement only the checked-in scope below. Run every verification command, fill the implementation-owned sections of `CODE_REVIEW-cloud-G08.md` with actual notes and raw output, keep the active pair in place, and report ready for review. Finalization belongs to the code-review skill. 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 presence-aware pointer repair rejects explicit empty and whitespace strings, but Viper preserves a present YAML null in the raw preset map while mapstructure collapses it to the same nil pointer as an omitted key. Consequently, `plan_file: null`, `review_file: null`, and `~` still select built-in defaults even though omission is the only permitted fallback signal. This follow-up closes that remaining raw-decode boundary without changing template runtime behavior or public APIs.
## Archive Evidence Snapshot
- Prior task: `single_request_plan_review_templates`, plan `2`, tag `REVIEW_API`.
- Archived plan: `agent-task/single_request_plan_review_templates/plan_cloud_G08_2.log`.
- Archived review: `agent-task/single_request_plan_review_templates/code_review_cloud_G08_2.log`.
- Prior verdict: `FAIL`; Required R1 proves that present YAML null template keys collapse to nil pointers and silently select defaults.
- Fresh reviewer regression: the two null Plan/Review cases loaded successfully instead of returning the configured-field empty-path error. A separate Viper probe showed that `v.Get("execution_presets")` retains both keys with nil raw values, so validation can reject them before mapstructure decode.
- Existing formatting, focused, race, full Edge/Go, smoke self-test, empty/whitespace, clone, and refresh-redaction checks passed. The approved Darwin/arm64 changed-package evidence belongs to source commit `6f141b91`; the next implementation must synchronize and retest its new source commit. Live API qualification remains externally blocked by the absent operator config files and complete `IOP_SINGLE_REQUEST_SMOKE_*` inputs.
- Roadmap carryover: none; this is a non-milestone task.
## Finding Resolution Map
| Finding | Closure | Direct-fix target | Changed precondition / invariant |
|---------|---------|-------------------|----------------------------------|
| R1 | direct-fix | `packages/go/config/load.go`, `packages/go/config/model_execution_preset_config_test.go` | The raw Viper preset map retains present null keys, so reject `plan_file`/`review_file` entries whose raw value is nil before mapstructure erases presence; only a truly absent key reaches nil-pointer fallback. |
## Analysis
### Files Read
- `agent-task/single_request_plan_review_templates/plan_cloud_G08_2.log`
- `agent-task/single_request_plan_review_templates/code_review_cloud_G08_2.log`
- `packages/go/config/load.go`
- `packages/go/config/execution_preset_types.go`
- `packages/go/config/model_execution_preset_config_test.go`
- `apps/edge/internal/configrefresh/classify.go`
- `apps/edge/internal/configrefresh/execution_preset_classify_test.go`
- `agent-contract/inner/edge-config-runtime-refresh.md`
- `agent-spec/runtime/provider-pool-config-refresh.md`
- `agent-test/local/platform-common-smoke.md`
- `agent-test/local/edge-smoke.md`
- `agent-test/local/testing-smoke.md`
### SDD Criteria
Not applicable. This non-milestone task repairs an existing config admission invariant and does not change the accepted architecture.
### Verification Context
- No separate handoff was supplied. Repository rules, the archived pair, current code/tests, contract/spec, and fresh reviewer commands provide the verification context.
- Fresh reviewer failure: `go test -count=1 ./packages/go/config -run 'TestLoadEdgeSingleRequestTemplates/explicitly_configured_empty_template_path_fails_closed/(null_plan_file|null_review_file)$' -v` returned exit 1 because both configurations loaded without error. The temporary cases were removed.
- Fresh Viper probe: `v.Get("execution_presets")` returned nested `map[string]interface{}` values containing `"plan_file": nil` and `"review_file": nil`. The temporary probe was removed. This proves a repository-owned pre-decode fix exists and avoids another unchanged-precondition verification loop.
- Fresh existing verification passed: formatting plus `git diff --check`; focused config tests; config/configrefresh package and race tests; `go test -count=1 ./apps/edge/... ./packages/go/...`; the deterministic single-request smoke self-test; and the spec guard.
- Fresh Go commands must use `-count=1`; cached output is not acceptable.
#### External Verification Preflight
- Runner: `toki@toki-labs.com`; workdir: `/Users/toki/agent-work/iop-dev`; required OS/arch: `darwin/arm64`; branch: `feature/single-request-plan-review-templates`.
- The prior changed-package run synchronized the remote checkout to `6f141b91`, built `build/bin/iop-edge` and `build/bin/iop-node`, and passed config/configrefresh plus the smoke self-test. The new implementation must push its own source commit and reset the dedicated remote checkout to that exact origin HEAD before rebuilding.
- Remote tools previously verified: Go `1.26.3`, Claude CLI `2.1.177`, SOPS `3.13.1`; SOPS age identity `/Users/toki/.config/sops/age/keys.txt` is readable.
- `dev-openai-toki.edge.yaml` and `dev-openai-toki.sops.yaml` remain absent, and the twelve `IOP_SINGLE_REQUEST_SMOKE_*` inputs are unavailable. Probe those assets only; do not claim or run live qualification unless every precondition is restored. Record the same restore/input resume condition if still blocked.
### Test Coverage Gaps
- Empty-string and whitespace-only values are covered for Plan and Review; present YAML null and `~` forms are not.
- Omitted fallback, relative success, optional-pointer clone isolation, and digest-only refresh classification are already covered and must stay green.
- No runtime or caller-visible behavior changes; downstream full suites guard compatibility.
### Symbol References
No symbol is renamed or removed. The fix stays inside `LoadEdge` raw preset admission and its existing config regression test.
### Split Judgment
Use one plan. Raw key presence, pointer fallback semantics, and the null regressions form one compact config invariant and cannot independently PASS if split.
### Scope Rationale
- Do not replace the current `*string` fields or change their clone behavior; they correctly preserve string presence and snapshot isolation.
- Do not change template grammar, path resolution, refresh summaries, execution stages, caller-visible APIs, contracts/specs, smoke scripts, or centrally managed Agent-Ops files.
- Do not broaden this repair to unrelated nullable config fields. Validate only `execution_presets[].single_request.templates.plan_file` and `.review_file` before strict preset decode.
- Do not modify dispatcher-owned `WORK_LOG.md`.
### 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`. Scores: scope `2`, state `1`, blast `1`, evidence `2`, verification `2`; base `local-fit`, final basis `recovery-boundary`; route `cloud/G08`, catalog `worker/cloud/G08`, filename `PLAN-cloud-G08.md`.
- Review closures: all true. Scores: scope `2`, state `1`, blast `1`, evidence `2`, verification `2`; basis `official-review`; route `cloud/G08`, catalog `review/cloud/G08`, filename `CODE_REVIEW-cloud-G08.md`.
- `large_indivisible_context=false`; positive loop risks: `boundary_contract`, `structured_interpretation`, `variant_product` (`3`).
- Recovery signals: `review_rework_count=3`, `evidence_integrity_failure=true`; capability gap: none.
## Implementation Checklist
- [x] [REVIEW_API-1] Reject present null Plan/Review template path keys from the raw Viper preset structure before mapstructure erases presence, while preserving true omission fallback.
- [x] [REVIEW_API-2] Add Plan/Review `null` and `~` regressions and retain empty/whitespace, omission, relative-path, clone, and refresh-redaction coverage.
- [x] [REVIEW_API-3] Run fresh focused, race, full, smoke-self-test, documentation, and synchronized Darwin/arm64 changed-package verification; retain the exact live-profile blocker if operator assets remain absent.
- [x] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.
### [REVIEW_API-1] Reject Present Null Template Paths Before Decode
**Problem**
`packages/go/config/load.go:72-91` reads raw `execution_presets` and immediately decodes it into pointer fields. Mapstructure maps a present raw nil to a nil pointer, so `resolveSingleRequestTemplates` at `packages/go/config/load.go:697-713` cannot distinguish `plan_file: null` or `review_file: ~` from an absent key and selects defaults.
**Solution**
- Before strict mapstructure decode, inspect the raw `[]interface{}` / nested `map[string]interface{}` structure that Viper already returns.
- For each preset, check only `single_request.templates.plan_file` and `.review_file`. If the key exists and its raw value is nil, return a deterministic error containing the preset index, exact field path, and `template path must not be empty`.
- Leave absent keys untouched so pointer nil remains the omission signal. Leave non-nil values to strict mapstructure decode and the existing `loadTemplateFile` trim/absolute/filesystem validation.
- If an intermediate value has another shape, let the existing strict decoder own the type error rather than silently normalizing it.
Before (`packages/go/config/load.go:72-86`):
```go
if v.InConfig("execution_presets") {
raw := v.Get("execution_presets")
var presets []ExecutionPreset
// ...
if err := decoder.Decode(raw); err != nil {
return nil, fmt.Errorf("execution_presets: %w", err)
}
}
```
After:
```go
if v.InConfig("execution_presets") {
raw := v.Get("execution_presets")
if err := rejectNullSingleRequestTemplatePaths(raw); err != nil {
return nil, err
}
// existing strict decode follows unchanged
}
```
**Modified Files and Checklist**
- [x] `packages/go/config/load.go` — add the exact raw presence validator and call it before preset decode.
**Test Strategy**
- A regression is required because this is the remaining boundary bug. REVIEW_API-2 extends the established loader test; no separate test package is needed.
**Verification**
```bash
go test -count=1 ./packages/go/config -run 'TestLoadEdgeSingleRequestTemplates'
```
Expected: present null/tilde paths fail with their exact field context, while omitted and valid relative paths still load.
### [REVIEW_API-2] Cover Null Syntaxes And Preserve Neighboring Boundaries
**Problem**
`packages/go/config/model_execution_preset_config_test.go:967-1007` covers explicit `""` and whitespace values but not YAML null forms, allowing pointer decode to re-collapse presence without a failing test.
**Solution**
- Extend the existing presence-boundary table with `null` and `~` for both `plan_file` and `review_file`.
- Require the exact Plan/Review field context, `template path must not be empty`, and absence of the temporary config directory from the error so rejection is proven before template filesystem access.
- Keep the existing omitted nil assertions, relative non-nil assertions, clone independence test, and config-refresh digest/path/body redaction assertions unchanged.
**Modified Files and Checklist**
- [x] `packages/go/config/model_execution_preset_config_test.go` — add four raw-null boundary cases to the established table.
**Test Strategy**
- Test both field axes and both supported YAML null spellings because the failure sits before typed decode.
- Re-run configrefresh tests to prove pointer comparison and redacted summaries remain unchanged.
**Verification**
```bash
go test -count=1 ./packages/go/config -run 'TestLoadEdgeSingleRequestTemplates|TestExecutionSingleRequestTemplatePathPresenceClone'
go test -count=1 ./packages/go/config ./apps/edge/internal/configrefresh
```
Expected: all null, empty, whitespace, omission, relative, clone, and refresh cases pass.
### [REVIEW_API-3] Produce Fresh Local And Remote Evidence
**Problem**
The active review claimed that every configured value remained distinguishable, but no null case exercised the raw-to-pointer boundary. The next evidence must identify the repaired source commit and keep the unavailable live profile separate from deterministic and remote changed-package success.
**Solution**
- Run every local command below fresh and paste actual stdout/stderr into the paired review file.
- Commit and push source/test changes, synchronize the dedicated Darwin/arm64 checkout to that exact origin commit, rebuild, and rerun changed packages plus the deterministic smoke self-test.
- Probe the approved operator files and age identity without creating or substituting assets. If inputs remain absent, record the blocker and resume condition; do not call it a live pass.
**Modified Files and Checklist**
- [x] `agent-task/single_request_plan_review_templates/CODE_REVIEW-cloud-G08.md` — record implementation decisions, source identity, exact local/remote output, and the external blocker.
**Test Strategy**
- No additional production test belongs to this item; REVIEW_API-2 owns the deterministic regression.
**Verification**
Run the `Final Verification` sequence below. Expected: every repository-owned command and synchronized remote changed-package check passes; unavailable live inputs remain an explicit external blocker.
## Modified Files Summary
| File | Item |
|---|---|
| `packages/go/config/load.go` | REVIEW_API-1 |
| `packages/go/config/model_execution_preset_config_test.go` | REVIEW_API-2 |
| `agent-task/single_request_plan_review_templates/CODE_REVIEW-cloud-G08.md` | REVIEW_API-3 |
## Final Verification
Run locally from the repository root after all source and test changes:
```bash
test -z "$(gofmt -l packages/go/config/load.go packages/go/config/model_execution_preset_config_test.go)"
git diff --check
go test -count=1 ./packages/go/config -run 'TestLoadEdgeSingleRequestTemplates|TestExecutionSingleRequestTemplatePathPresenceClone'
go test -count=1 ./packages/go/config ./apps/edge/internal/configrefresh
go test -race -count=1 ./packages/go/config ./apps/edge/internal/configrefresh
go test -count=1 ./apps/edge/... ./packages/go/...
make test-single-request-claude-smoke-self-test
rg --sort path -n 'Absolute and empty paths are rejected before any filesystem access|plan_file.*review_file.*non-empty paths' agent-spec/runtime/provider-pool-config-refresh.md
```
Expected: formatting and diff checks are silent; fresh focused, package, race, full, and smoke-self-test commands pass; the documentation guard prints the two unchanged contract statements.
After the source/test commit is pushed, synchronize and verify the approved Darwin/arm64 checkout against the exact pushed HEAD:
```bash
test "$(git branch --show-current)" = "feature/single-request-plan-review-templates"
git rev-parse HEAD
git push origin feature/single-request-plan-review-templates
ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && export PATH=/opt/homebrew/bin:$PATH && git fetch origin feature/single-request-plan-review-templates && git switch feature/single-request-plan-review-templates && git reset --hard origin/feature/single-request-plan-review-templates && test -z "$(git status --short)" && test "$(git rev-parse HEAD)" = "$(git rev-parse origin/feature/single-request-plan-review-templates)" && test "$(go env GOOS)/$(go env GOARCH)" = "darwin/arm64" && go version && claude --version && sops --version && make build-edge build-node && go test -count=1 ./packages/go/config ./apps/edge/internal/configrefresh && make test-single-request-claude-smoke-self-test'
ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && for file in dev-openai-toki.edge.yaml dev-openai-toki.sops.yaml; do if test -f "$file"; then echo "PRESENT $file"; else echo "MISSING $file"; fi; done && test -r /Users/toki/.config/sops/age/keys.txt && echo "PRESENT age identity"'
```
Expected: local/origin/remote source identities match; remote build, fresh changed-package tests, and self-test pass. If either operator file is missing or the twelve `IOP_SINGLE_REQUEST_SMOKE_*` inputs are unavailable, do not run live qualification. Record the exact resume condition: restore both files under `/Users/toki/agent-work/iop-dev` and supply all twelve Makefile-defined inputs.
After completing all code changes, fill implementation-owned sections in `CODE_REVIEW-*-G??.md`.

View file

@ -15,3 +15,14 @@
| 9 | 26-08-09 09:33:32 KST | START | single_request_plan_review_templates/CODE_REVIEW-cloud-G08.md | 1 | review | 0 | codex/gpt-5.6-sol | running | /config/workspace/iop-s2/.git/agent-task-dispatcher/runs/20260809T093332+0900__single_request_plan_review_templates__p1__review__a00/locator.json |
| 10 | 26-08-09 09:57:23 KST | FINISH | single_request_plan_review_templates/CODE_REVIEW-cloud-G08.md | 1 | review | 0 | codex/gpt-5.6-sol | succeeded:0 | /config/workspace/iop-s2/.git/agent-task-dispatcher/runs/20260809T093332+0900__single_request_plan_review_templates__p1__review__a00/locator.json |
| 11 | 26-08-09 09:57:55 KST | START | single_request_plan_review_templates/PLAN-cloud-G08.md | 2 | worker | 0 | claude/claude-opus-5 | running | /config/workspace/iop-s2/.git/agent-task-dispatcher/runs/20260809T095755+0900__single_request_plan_review_templates__p2__worker__a00/locator.json |
| 12 | 26-08-09 10:11:57 KST | FINISH | single_request_plan_review_templates/PLAN-cloud-G08.md | 2 | worker | 0 | claude/claude-opus-5 | failed:provider-quota:0 | /config/workspace/iop-s2/.git/agent-task-dispatcher/runs/20260809T095755+0900__single_request_plan_review_templates__p2__worker__a00/locator.json |
| 13 | 26-08-09 10:11:57 KST | START | single_request_plan_review_templates/PLAN-cloud-G08.md | 2 | worker | 1 | codex/gpt-5.6-terra | running | /config/workspace/iop-s2/.git/agent-task-dispatcher/runs/20260809T101157+0900__single_request_plan_review_templates__p2__worker__a01/locator.json |
| 14 | 26-08-09 10:14:18 KST | FINISH | single_request_plan_review_templates/PLAN-cloud-G08.md | 2 | worker | 1 | codex/gpt-5.6-terra | succeeded:0 | /config/workspace/iop-s2/.git/agent-task-dispatcher/runs/20260809T101157+0900__single_request_plan_review_templates__p2__worker__a01/locator.json |
| 15 | 26-08-09 10:14:19 KST | START | single_request_plan_review_templates/CODE_REVIEW-cloud-G08.md | 2 | review | 0 | codex/gpt-5.6-sol | running | /config/workspace/iop-s2/.git/agent-task-dispatcher/runs/20260809T101419+0900__single_request_plan_review_templates__p2__review__a00/locator.json |
| 16 | 26-08-09 10:28:44 KST | FINISH | single_request_plan_review_templates/CODE_REVIEW-cloud-G08.md | 2 | review | 0 | codex/gpt-5.6-sol | succeeded:0 | /config/workspace/iop-s2/.git/agent-task-dispatcher/runs/20260809T101419+0900__single_request_plan_review_templates__p2__review__a00/locator.json |
| 17 | 26-08-09 10:29:19 KST | START | single_request_plan_review_templates/PLAN-cloud-G08.md | 3 | worker | 0 | claude/claude-opus-5 | running | /config/workspace/iop-s2/.git/agent-task-dispatcher/runs/20260809T102919+0900__single_request_plan_review_templates__p3__worker__a00/locator.json |
| 18 | 26-08-09 10:35:54 KST | FINISH | single_request_plan_review_templates/PLAN-cloud-G08.md | 3 | worker | 0 | claude/claude-opus-5 | failed:provider-quota:1 | /config/workspace/iop-s2/.git/agent-task-dispatcher/runs/20260809T102919+0900__single_request_plan_review_templates__p3__worker__a00/locator.json |
| 19 | 26-08-09 10:35:54 KST | START | single_request_plan_review_templates/PLAN-cloud-G08.md | 3 | worker | 1 | codex/gpt-5.6-terra | running | /config/workspace/iop-s2/.git/agent-task-dispatcher/runs/20260809T103554+0900__single_request_plan_review_templates__p3__worker__a01/locator.json |
| 20 | 26-08-09 10:44:05 KST | FINISH | single_request_plan_review_templates/PLAN-cloud-G08.md | 3 | worker | 1 | codex/gpt-5.6-terra | succeeded:0 | /config/workspace/iop-s2/.git/agent-task-dispatcher/runs/20260809T103554+0900__single_request_plan_review_templates__p3__worker__a01/locator.json |
| 21 | 26-08-09 10:44:05 KST | START | single_request_plan_review_templates/CODE_REVIEW-cloud-G08.md | 3 | review | 0 | codex/gpt-5.6-sol | running | /config/workspace/iop-s2/.git/agent-task-dispatcher/runs/20260809T104405+0900__single_request_plan_review_templates__p3__review__a00/locator.json |
| 22 | 26-08-09 10:55:37 KST | FINISH | single_request_plan_review_templates/CODE_REVIEW-cloud-G08.md | 3 | review | 0 | codex/gpt-5.6-sol | succeeded:0 | /config/workspace/iop-s2/.git/agent-task-dispatcher/runs/20260809T104405+0900__single_request_plan_review_templates__p3__review__a00/locator.json |