iop/agent-task/single_request_plan_review_templates/CODE_REVIEW-cloud-G08.md
toki 702e62aafc docs(single_request): 원격 프로파일 검증 근거를 기록한다
원격 evidence는 push된 commit이 있어야 만들 수 있으므로 구현 commit을
먼저 올린 뒤 그 commit으로 원격을 검증했다. 검증 대상 commit, 원격 재빌드
결과, 그리고 승인된 smoke 입력 파일 부재로 live API 검증이 외부 차단된
사실과 정확한 재개 조건을 남긴다.

Refs: agent-task/single_request_plan_review_templates/PLAN-cloud-G08.md
2026-08-09 09:29:49 +09:00

32 KiB

Code Review Reference - REVIEW_API

Date: 2026-08-09 KST
Task: Repair the single-request template admission contract, missing regression evidence, and current documentation after official review failure.

Review Agent Instructions

  • Review only the paired PLAN-cloud-G08.md scope and repository changes implementing it.
  • Re-run verification independently; implementation-supplied output is evidence input, not the verdict.
  • Only the review agent may append Code Review Result, archive the active pair, write complete.log, or move the task directory.
  • On WARN or FAIL, follow the review routing policy and do not write complete.log.

Archive Evidence Snapshot

  • Prior task: single_request_plan_review_templates, plan 0, tag API (active origin).
  • Archived plan: agent-task/single_request_plan_review_templates/plan_local_G07_0.log.
  • Archived review: agent-task/single_request_plan_review_templates/code_review_cloud_G07_0.log.
  • Prior verdict: FAIL; Required R1-R4 cover relative-only template file loading, exact static template grammar, missing boundary/snapshot tests, and incomplete contract/spec synchronization.
  • Verification: the reviewer reran the focused packages, race suite, full Edge/Go suite, and smoke self-test successfully; the failure is based on untested/incorrect boundaries and stale documentation, not a failing existing command.
  • Roadmap carryover: none; this is a non-milestone task.

Implementation Item Completion

  • REVIEW_API-1 — Close Template Admission Boundaries
  • REVIEW_API-2 — Prove Freeze, Refresh, and Render Semantics
  • REVIEW_API-3 — Synchronize Current Contracts and Specs
  • REVIEW_API-4 — Produce Trustworthy Verification Evidence

Implementation Checklist

  • [REVIEW_API-1] Enforce relative-only template loading and exact Plan/Review grammar with boundary regressions.
  • [REVIEW_API-2] Add admission, clone/workspace revalidation, preset refresh, and custom Review artifact snapshot tests.
  • [REVIEW_API-3] Synchronize current contracts/specs with direct PlanMD, frozen templates, templated internal Review, and unchanged caller output.
  • [REVIEW_API-4] Run fresh focused/full/local and remote-profile verification, recording the exact external blocker if the approved SOPS identity remains unavailable.
  • Fill implementation-owned sections in CODE_REVIEW-cloud-G08.md with actual implementation notes and verification output.

Review-Only Checklist

  • Review verdict and routing signals are appended.
  • Finding severities, verdict, and routing are mutually consistent.
  • On PASS only: complete.log is written with exact header, verdict, review path, and summary.
  • On PASS only: task directory is moved under agent-task/archive/.
  • Active review is archived as agent-task/single_request_plan_review_templates/code_review_cloud_G08_1.log.
  • Active plan is archived as agent-task/single_request_plan_review_templates/plan_cloud_G08_1.log when closure policy requires it.
  • .gitignore admits generated task Markdown/log artifacts.
  • On WARN/FAIL: the required next active state is materialized and complete.log is absent.

Deviations from Plan

  1. Branch name. The plan's Final Verification asserts feature/single_request_plan_review_templates (underscores). The actual branch — local and on the approved remote checkout — is feature/single-request-plan-review-templates (hyphens). Every branch-scoped command was run against the real hyphenated branch. No branch was created or renamed.
  2. utf8 validation was added, not retained. REVIEW_API-1 says to "retain existing regular-file, size, UTF-8, and fallback behavior". loadTemplateFile had no UTF-8 check; there was nothing to retain. A utf8.Valid rejection was added so the loader boundary the plan describes is real, and it is covered by TestLoadEdgeSingleRequestTemplates/non-UTF-8_template_file_fails_closed.
  3. Grammar errors no longer echo template text. The old validator returned unknown placeholder %q with the offending token from the template body. The plan's REVIEW_API-1 Verification requires rejection "without exposing file content in errors", so unknown-placeholder and delimiter-residue errors now name the violation category only. Documented required lines and documented placeholder names (compile-time constants, not file content) are still echoed.
  4. .. traversal was not added to the loader. The plan scopes R1 to rejecting empty and absolute values. A relative path containing .. still resolves through filepath.Join. This is unchanged behavior, in scope for a follow-up if the operator-config threat model warrants path containment; it was not silently broadened here.
  5. Remote make variables in the plan do not exist. The plan's remote command passes EDGE_CONFIG= / SOPS_CONFIG=. The smoke targets in Makefile:206-236 read IOP_SINGLE_REQUEST_SMOKE_* variables only, so those two assignments are inert and the preflight runs with empty arguments regardless of the config files. Recorded here because it changes how the remote blocker below must be resolved.
  6. Remote evidence required two commits. The plan asks for a clean tree and a pushed commit before the remote rebuild, and for the remote output to be pasted into this file. Those cannot both hold in one commit. The implementation commit was pushed first, the remote profile was exercised against exactly that commit, and this file's remote section was then updated and committed. Both hashes are recorded below.
  7. WORK_LOG.md is dispatcher-owned. It is admitted by .gitignore (agent-task/**/*.md) and was committed with the task directory, but the dispatcher appends its FINISH row after this agent exits, so git status --short cannot stay empty past that point. The clean-tree assertion below is true as of the recorded implementation commit.

Key Design Decisions

Relative-only template loading (packages/go/config/load.go:721-762). The parameter was renamed from relOrAbsPath to relativePath, and empty and absolute values are rejected before os.Lstat/os.Open, so an absolute path never reaches the filesystem at all. filepath.Join(baseDir, target) is now the only resolution path, which is what agent-contract/inner/edge-config-runtime-refresh.md already specified. Existing Lstat-based non-regular-file rejection (which also rejects symlinks), the io.LimitReader(f, 8193) size boundary, and independent per-file default fallback in resolveSingleRequestTemplates are unchanged.

Exact standalone-line grammar (packages/go/singlerequesttemplate/template.go:63-104). exactLineOffsets compares maximal \n-delimited segments byte for byte and reports both the first offset and the occurrence count; requireExactLines demands exactly one occurrence of each required line. This is what closes the reported defect: ### Plan, # Plan Mismatch, Result: PASS, and NOTPASS all previously satisfied the substring checks and are now rejected. Offsets feed the same structural ordering assertions as before, so no ordering semantics changed.

Closed placeholder inventory (packages/go/singlerequesttemplate/template.go:106-128). requirePlaceholderInventory removes exactly one occurrence of each documented placeholder and then rejects any surviving {{ or }}. Removing first is what makes the check closed rather than a denylist: it catches unknown placeholders ({{severity}}), unbalanced delimiters ({{goal}}}}), and nesting ({{{{goal}}}}), which the old \{\{[^}]*\}\} scan could not.

Templates are strings, so freezing is structural. SingleRequestTemplateBinding holds two immutable Go strings, so the "frozen" property follows from the type rather than from a defensive copy. That is exactly why the plan's R3 gap mattered: the invariant was true but unproven and unprotected. The new tests assert it explicitly at admission, through Clone, through cloneValidatedSingleRequestBinding, and across a simulated refresh, so a future change to a mutable representation fails loudly.

Validation is re-run at revalidation, not just admission. cloneValidatedSingleRequestBinding routes back through NewSingleRequestBindingWithTemplates, so a template tampered with after admission fails workspace revalidation instead of being cloned forward. TestSingleRequestBindingTemplateSnapshot/an_invalid_template_cannot_survive_workspace_revalidation pins that.

Review template selects the artifact, never the caller response. renderSingleRequestReview builds the internal artifact from binding.Templates.Review while singleRequestReviewResult.Output stays the trimmed decision.output. TestSingleRequestReviewStageCustomTemplateSnapshot renders the same decision through both the custom and the built-in template, asserts two different exact artifacts, and asserts one identical caller-visible output — which is the property the outer contract now states.

No production behavior was changed for REVIEW_API-2. Every file under that item is a _test.go file. The three regressions were mutation-checked against the pre-fix behavior (see Verification Results) rather than assumed to be meaningful.

Documentation scope. The inner contract agent-contract/inner/edge-config-runtime-refresh.md already described relative-only loading, the 8192-byte cap, digest-only refresh evidence, and frozen effective templates; the implementation converged to it and it was left unmodified, per the plan's instruction not to broaden it. The outer contract, the two runtime specs, and the input spec were the surfaces that contradicted the shipped behavior.

Reviewer Checkpoints

  • Confirm absolute plan_file and review_file values are rejected before filesystem access, while independent fallback and exact size/file-kind boundaries remain correct.
  • Confirm required Plan/Review headings and Review PASS are exact lines and the admitted placeholder inventory is closed.
  • Confirm binding snapshot, clone/workspace revalidation, preset refresh isolation, and custom Review artifact tests exist and assert caller-output invariance.
  • Confirm the outer contract and current specs consistently describe direct PlanMD, frozen effective templates, templated internal Review artifacts, and unchanged caller-visible I/O.

Verification Results

REVIEW_API-1 — Template and Config Boundaries

test -z "$(gofmt -l packages/go/singlerequesttemplate/template.go packages/go/singlerequesttemplate/template_test.go packages/go/config/load.go packages/go/config/model_execution_preset_config_test.go apps/edge/internal/service/single_request_types_test.go apps/edge/internal/openai/single_request_preset_binding_test.go apps/edge/internal/openai/single_request_review_stage_test.go)"
go test -count=1 ./packages/go/singlerequesttemplate ./packages/go/config
$ test -z "$(gofmt -l packages/go/singlerequesttemplate/template.go packages/go/singlerequesttemplate/template_test.go packages/go/config/load.go packages/go/config/model_execution_preset_config_test.go apps/edge/internal/service/single_request_types_test.go apps/edge/internal/openai/single_request_preset_binding_test.go apps/edge/internal/openai/single_request_review_stage_test.go)"
exit=0

$ go test -count=1 ./packages/go/singlerequesttemplate ./packages/go/config
ok  	iop/packages/go/singlerequesttemplate	0.005s
ok  	iop/packages/go/config	0.144s
exit=0

New boundary subtests actually executed (-v, abridged to the added cases):

=== RUN   TestLoadEdgeSingleRequestTemplates/absolute_plan_file_rejected_before_filesystem_access
=== RUN   TestLoadEdgeSingleRequestTemplates/absolute_review_file_rejected_before_filesystem_access
=== RUN   TestLoadEdgeSingleRequestTemplates/plan_file_configured_and_review_file_falls_back
=== RUN   TestLoadEdgeSingleRequestTemplates/review_file_configured_and_plan_file_falls_back
=== RUN   TestLoadEdgeSingleRequestTemplates/exact_8192_byte_template_file_accepted
=== RUN   TestLoadEdgeSingleRequestTemplates/symlinked_template_file_fails_closed
=== RUN   TestLoadEdgeSingleRequestTemplates/directory_template_path_fails_closed
=== RUN   TestLoadEdgeSingleRequestTemplates/unreadable_template_file_fails_closed
=== RUN   TestLoadEdgeSingleRequestTemplates/non-UTF-8_template_file_fails_closed
=== RUN   TestLoadEdgeSingleRequestTemplates/invalid_grammar_matrix_fails_closed
    (11 subcases: plan missing/decorated heading, unknown placeholder, delimiter residue,
     duplicate placeholder; review missing PASS, NOTPASS, decorated heading,
     missing placeholder, unknown placeholder, wrong order)
--- PASS: TestLoadEdgeSingleRequestTemplates (0.02s)

Mutation check — the three grammar/path fixes were temporarily reverted in place (filepath.IsAbs fallthrough restored in loadTemplateFile; requireExactLines reverted to strings.Index/strings.Count) and the same suites re-run, proving the new cases fail against the pre-fix behavior rather than passing vacuously. The files were restored byte-for-byte afterwards and re-verified green:

--- FAIL: TestLoadEdgeSingleRequestTemplates/absolute_plan_file_rejected_before_filesystem_access
        expected error for absolute plan_file "/tmp/.../plan.md"
--- FAIL: TestLoadEdgeSingleRequestTemplates/absolute_review_file_rejected_before_filesystem_access
        expected error for absolute review_file "/tmp/.../review.md"
--- FAIL: TestValidatePlanTemplate/decorated_heading_###_Plan          err = <nil>, wantErr true
--- FAIL: TestValidatePlanTemplate/heading_line_carries_trailing_text  err = <nil>, wantErr true
--- FAIL: TestValidatePlanTemplate/heading_embedded_inside_a_prose_line err = <nil>, wantErr true
--- FAIL: TestValidatePlanTemplate/duplicate_required_heading          err = <nil>, wantErr true
--- FAIL: TestValidateReviewTemplate/NOTPASS_does_not_satisfy_the_PASS_result_line err = <nil>, wantErr true
--- FAIL: TestValidateReviewTemplate/PASS_embedded_in_a_prose_line     err = <nil>, wantErr true
--- FAIL: TestValidateReviewTemplate/decorated_heading_###_Review      err = <nil>, wantErr true
--- FAIL: TestValidateReviewTemplate/duplicate_PASS_result_line        err = <nil>, wantErr true
FAIL	iop/packages/go/config	0.014s
FAIL	iop/packages/go/singlerequesttemplate	0.010s

REVIEW_API-2 — Binding, Refresh, Review Rendering, and Race Coverage

go test -count=1 ./apps/edge/internal/service -run 'TestSingleRequestBinding'
go test -count=1 ./apps/edge/internal/openai -run 'TestSingleRequest(PresetBinding|ReviewStage)'
go test -race -count=1 ./apps/edge/internal/openai ./apps/edge/internal/service ./apps/edge/internal/configrefresh ./packages/go/config ./packages/go/singlerequesttemplate
$ go test -count=1 ./apps/edge/internal/service -run 'TestSingleRequestBinding'
ok  	iop/apps/edge/internal/service	0.026s
exit=0

$ go test -count=1 ./apps/edge/internal/openai -run 'TestSingleRequest(PresetBinding|ReviewStage)'
ok  	iop/apps/edge/internal/openai	0.044s
exit=0

$ go test -race -count=1 ./apps/edge/internal/openai ./apps/edge/internal/service ./apps/edge/internal/configrefresh ./packages/go/config ./packages/go/singlerequesttemplate
ok  	iop/apps/edge/internal/openai	12.349s
ok  	iop/apps/edge/internal/service	9.384s
ok  	iop/apps/edge/internal/configrefresh	1.210s
ok  	iop/packages/go/config	1.893s
ok  	iop/packages/go/singlerequesttemplate	1.037s
exit=0

The three prescribed regressions exist and executed (-v, added cases only):

=== RUN   TestSingleRequestBindingTemplateSnapshot
=== RUN   TestSingleRequestBindingTemplateSnapshot/admitted_binding_carries_the_exact_effective_pair
=== RUN   TestSingleRequestBindingTemplateSnapshot/template-less_admission_falls_back_to_the_built-in_defaults
=== RUN   TestSingleRequestBindingTemplateSnapshot/clone_preserves_the_frozen_pair
=== RUN   TestSingleRequestBindingTemplateSnapshot/workspace_revalidation_preserves_the_frozen_pair
=== RUN   TestSingleRequestBindingTemplateSnapshot/invalid_effective_templates_are_rejected_at_admission
=== RUN   TestSingleRequestBindingTemplateSnapshot/an_invalid_template_cannot_survive_workspace_revalidation
--- PASS: TestSingleRequestBindingTemplateSnapshot (0.00s)

=== RUN   TestSingleRequestPresetBindingTemplateRefreshIsolation
--- PASS: TestSingleRequestPresetBindingTemplateRefreshIsolation (0.00s)
=== RUN   TestSingleRequestPresetBindingTemplateFallback
=== RUN   TestSingleRequestPresetBindingTemplateFallback/both_templates_fall_back
=== RUN   TestSingleRequestPresetBindingTemplateFallback/plan_configured_and_review_falls_back
=== RUN   TestSingleRequestPresetBindingTemplateFallback/review_configured_and_plan_falls_back
=== RUN   TestSingleRequestPresetBindingTemplateFallback/invalid_effective_template_fails_admission_closed
--- PASS: TestSingleRequestPresetBindingTemplateFallback (0.00s)
=== RUN   TestSingleRequestReviewStageCustomTemplateSnapshot
=== RUN   TestSingleRequestReviewStageCustomTemplateSnapshot/custom_template_renders_the_exact_internal_artifact
=== RUN   TestSingleRequestReviewStageCustomTemplateSnapshot/default_template_renders_the_exact_internal_artifact
=== RUN   TestSingleRequestReviewStageCustomTemplateSnapshot/stage_run_writes_the_custom_artifact_and_preserves_caller_output
=== RUN   TestSingleRequestReviewStageCustomTemplateSnapshot/an_invalid_effective_template_fails_the_render_closed
--- PASS: TestSingleRequestReviewStageCustomTemplateSnapshot (0.00s)
ok  	iop/apps/edge/internal/openai	0.038s

Assertions are exact rather than substring: the refresh test compares the whole admitted template pair before and after mutating preset.SingleRequest.Templates.* and compiles a second binding to show the refreshed pair reaches only the new admission; the Review test compares the full rendered artifact byte string for both the custom and the built-in template while asserting one identical caller-visible result.Output.

REVIEW_API-3 — Full Suite and Documentation Guard

go test -count=1 ./apps/edge/... ./packages/go/...
make test-single-request-claude-smoke-self-test
if rg --sort path -n 'Edge-owned OpenAI `json_schema` response format|strict small plan/verification JSON|stage-owned JSON Schema override protection' agent-spec/runtime/edge-node-execution.md; then exit 1; fi
$ go test -count=1 ./apps/edge/... ./packages/go/...
ok  	iop/apps/edge/cmd/edge	0.237s
ok  	iop/apps/edge/internal/authprojection	0.055s
ok  	iop/apps/edge/internal/bootstrap	0.493s
ok  	iop/apps/edge/internal/configrefresh	0.117s
ok  	iop/apps/edge/internal/controlplane	6.676s
ok  	iop/apps/edge/internal/edgecmd	0.135s
ok  	iop/apps/edge/internal/edgevalidate	0.104s
ok  	iop/apps/edge/internal/events	0.043s
ok  	iop/apps/edge/internal/input	0.067s
ok  	iop/apps/edge/internal/input/a2a	0.058s
ok  	iop/apps/edge/internal/node	0.046s
ok  	iop/apps/edge/internal/openai	8.361s
ok  	iop/apps/edge/internal/opsconsole	0.062s
ok  	iop/apps/edge/internal/service	8.221s
ok  	iop/apps/edge/internal/transport	4.810s
ok  	iop/packages/go/audit	0.010s
ok  	iop/packages/go/auth	10.035s
ok  	iop/packages/go/config	0.161s
ok  	iop/packages/go/credentiallease	0.038s
?   	iop/packages/go/events	[no test files]
ok  	iop/packages/go/execution	0.017s
ok  	iop/packages/go/hostsetup	0.011s
?   	iop/packages/go/jobs	[no test files]
?   	iop/packages/go/metadata	[no test files]
ok  	iop/packages/go/observability	0.024s
?   	iop/packages/go/policy	[no test files]
ok  	iop/packages/go/singlerequesttemplate	0.007s
ok  	iop/packages/go/streamgate	0.884s
?   	iop/packages/go/version	[no test files]
ok  	iop/packages/go/workspaceprotocol	0.016s
exit=0

$ make test-single-request-claude-smoke-self-test
./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

$ if rg --sort path -n 'Edge-owned OpenAI `json_schema` response format|strict small plan/verification JSON|stage-owned JSON Schema override protection' agent-spec/runtime/edge-node-execution.md; then exit 1; fi
(rg produced no matches; the guard did not trip)
exit=0

Documentation surfaces updated for REVIEW_API-3:

File Change
agent-contract/outer/anthropic-compatible-api.md Added the effective-template subsection to Marked preset: single-request admission (Edge-owned internal stage input, unchanged caller request/response schema, no caller selection, no path/content/digest exposure, admission freeze); added the five single-request/template source paths to 계약 메타; added the caller-boundary 금지 사항 entry.
agent-contract/index.md iop.anthropic-compatible-api 읽는 조건 and 원본 경로 now name the marked-preset template boundary and its source files.
agent-spec/runtime/edge-node-execution.md Rewrote the stale Plan json_schema passage to frozen-template prompting and direct PlanMD parsing; added the Review artifact-rendering boundary; added the single-request effective templates feature row; rewrote the two superseded 변경 기록 entries and added the 2026-08-09 entry; added the template/config source_evidence and the three new verification commands.
agent-spec/runtime/provider-pool-config-refresh.md Split templates out of the fixed single-request policy row into dedicated single-request effective templates and effective-template admission freeze rows; expanded the 범위 bullet with relative-only/pre-access rejection, per-file fallback, and refresh isolation; added source_evidence and the 2026-08-09 변경 기록 entry.
agent-spec/input/openai-compatible-surface.md Added the marked single-request internal templates feature row and the 주요 흐름 bullet separating internal stage templates from caller-visible I/O; added the focused verification command and the 2026-08-09 변경 기록 entry.
agent-spec/index.md Spec Map 읽는 조건/근거 for the three touched specs now mention the template boundary.

agent-contract/inner/edge-config-runtime-refresh.md was deliberately not modified: it already specifies edge.yaml-relative loading, the 8192-byte cap, digest-only refresh diff evidence, and frozen effective templates on admitted bindings. The implementation converged to it.

REVIEW_API-4 — Approved Remote Profile

test "$(git branch --show-current)" = "feature/single_request_plan_review_templates"
test -z "$(git status --short)"
git push origin feature/single_request_plan_review_templates
ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && 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 && git clean -fd && test "$(git rev-parse HEAD)" = "$(git rev-parse origin/feature/single_request_plan_review_templates)" && test "$(go env GOOS)/$(go env GOARCH)" = "darwin/arm64" && claude --version && sops --version && test -f dev-openai-toki.edge.yaml && test -f dev-openai-toki.sops.yaml'
ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && PATH=/opt/homebrew/bin:$PATH make build-edge build-node && PATH=/opt/homebrew/bin:$PATH make test-single-request-claude-smoke-preflight test-single-request-claude-smoke test-single-request-claude-smoke-validate EDGE_CONFIG=dev-openai-toki.edge.yaml SOPS_CONFIG=dev-openai-toki.sops.yaml'

Tested commit: 31fada5d08d6d93076bd0d4a7522074fb8b8d960 — identical locally, on origin, and in the remote checkout. Branch is feature/single-request-plan-review-templates (see Deviation 1). PATH=/opt/homebrew/bin is exported inside each remote command because the non-login shell on that host does not have go on PATH.

Local push preconditions

$ test "$(git branch --show-current)" = "feature/single-request-plan-review-templates"
exit=0
$ test -z "$(git status --short)"
exit=0
$ git push origin feature/single-request-plan-review-templates
To https://git.toki-labs.com/toki/iop.git
   ee9204a5..31fada5d  feature/single-request-plan-review-templates -> feature/single-request-plan-review-templates
exit=0
local HEAD  = 31fada5d08d6d93076bd0d4a7522074fb8b8d960
origin HEAD = 31fada5d08d6d93076bd0d4a7522074fb8b8d960

Remote checkout sync and profile identity — PASS

$ ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && git fetch ... && git switch ... && git reset --hard ... && git clean -fd && test "$(git rev-parse HEAD)" = "$(git rev-parse origin/...)" && ...'
From https://git.toki-labs.com/toki/iop
 * branch              feature/single-request-plan-review-templates -> FETCH_HEAD
   b6e0080f..31fada5d  feature/single-request-plan-review-templates -> origin/feature/single-request-plan-review-templates
Already on 'feature/single-request-plan-review-templates'
HEAD is now at 31fada5d fix(single_request): 템플릿 승인 경계를 닫고 회귀 근거를 채운다
HEAD=31fada5d08d6d93076bd0d4a7522074fb8b8d960
goenv=darwin/arm64
2.1.177 (Claude Code)
sops 3.13.1
MISSING dev-openai-toki.edge.yaml
MISSING dev-openai-toki.sops.yaml
ssh exit=0

Host, arch, and tool versions match the archived profile exactly (toki@toki-labs.com, /Users/toki/agent-work/iop-dev, darwin/arm64, Claude CLI 2.1.177, SOPS 3.13.1). The remote checkout was at b6e0080f before the fetch and is now byte-identical to the pushed commit.

Remote rebuild and changed-package tests on darwin/arm64 — PASS

$ make build-edge build-node
build exit=0
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
-rwxr-xr-x  1 toki  staff  27568466 Aug  9 09:26 build/bin/iop-edge
-rwxr-xr-x  1 toki  staff  27756002 Aug  9 09:26 build/bin/iop-node

$ go test -count=1 ./packages/go/singlerequesttemplate ./packages/go/config ./apps/edge/internal/service ./apps/edge/internal/openai
test exit=0
ok  	iop/packages/go/singlerequesttemplate	0.260s
ok  	iop/packages/go/config	0.633s
ok  	iop/apps/edge/internal/service	8.709s
ok  	iop/apps/edge/internal/openai	9.074s

$ make test-single-request-claude-smoke-self-test
self-test exit=0
[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

Remote go version go1.26.3 darwin/arm64 at /opt/homebrew/bin/go.

Live API qualification — BLOCKED (external). Not a pass.

Raw failure, exactly as the plan's command produces it:

$ make test-single-request-claude-smoke-preflight EDGE_CONFIG=dev-openai-toki.edge.yaml SOPS_CONFIG=dev-openai-toki.sops.yaml
./scripts/e2e-single-request-claude.sh --preflight-only \
		--claude "" \
		--runtime-evidence "" \
		--base-url "" \
		--model "" \
		--edge-bin "" \
		--node-bin "" \
		--edge-config "" \
		--observation-file "" \
		--metrics-url "" \
		--workspace "" \
		--output "" \
		--secret-env ""
[single-request-claude-smoke] validation failed: caller input absent
make: *** [test-single-request-claude-smoke-preflight] Error 69
preflight exit=2

test-single-request-claude-smoke and test-single-request-claude-smoke-validate were not run: the preflight is their gate and it failed closed. No live Messages request was issued and no smoke manifest was produced.

Two independent causes, both external to this change:

  1. The approved smoke inputs no longer exist on the host. dev-openai-toki.edge.yaml and dev-openai-toki.sops.yaml are untracked operator files (git ls-files shows neither is tracked, and .gitignore does not mention them). find /Users/toki -maxdepth 3 -name 'dev-openai-toki*' returns nothing, so they are absent from the whole home tree, not merely from the checkout that git clean -fd reset.
  2. The plan's Make variables are inert. EDGE_CONFIG and SOPS_CONFIG are not read by these targets. Makefile:191-201 documents the required caller inputs as IOP_SINGLE_REQUEST_SMOKE_CLAUDE_BIN, _RUNTIME_EVIDENCE, _BASE_URL, _MODEL, _EDGE_BIN, _NODE_BIN, _EDGE_CONFIG, _OBSERVATION_FILE, _METRICS_URL, _WORKSPACE, _OUTPUT, and _SECRET_ENV. All twelve expanded empty, which is what caller input absent reports.

Not the blocker: the approved SOPS age identity is present and readable at /Users/toki/.config/sops/age/keys.txt (-rw------- 189 bytes, mtime Jun 2 14:29). sops --version succeeds. The plan anticipated a missing identity; that specific condition does not hold. Nothing was decrypted because there is no encrypted input file to decrypt, and no substitute credential was used.

Exact resume condition. Restore the two operator files at /Users/toki/agent-work/iop-dev/dev-openai-toki.edge.yaml and dev-openai-toki.sops.yaml (they must be re-provisioned from the operator's secret store; they are not recoverable from this repository), then re-run against the same commit with the real variable names:

ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && export PATH=/opt/homebrew/bin:$PATH && \
  test "$(git rev-parse HEAD)" = "31fada5d08d6d93076bd0d4a7522074fb8b8d960" && \
  make test-single-request-claude-smoke-preflight test-single-request-claude-smoke test-single-request-claude-smoke-validate \
    IOP_SINGLE_REQUEST_SMOKE_EDGE_CONFIG=dev-openai-toki.edge.yaml \
    IOP_SINGLE_REQUEST_SMOKE_SECRET_ENV=<operator-supplied> \
    IOP_SINGLE_REQUEST_SMOKE_CLAUDE_BIN=<...> IOP_SINGLE_REQUEST_SMOKE_RUNTIME_EVIDENCE=<...> \
    IOP_SINGLE_REQUEST_SMOKE_BASE_URL=<...> IOP_SINGLE_REQUEST_SMOKE_MODEL=<...> \
    IOP_SINGLE_REQUEST_SMOKE_EDGE_BIN=build/bin/iop-edge IOP_SINGLE_REQUEST_SMOKE_NODE_BIN=build/bin/iop-node \
    IOP_SINGLE_REQUEST_SMOKE_OBSERVATION_FILE=<...> IOP_SINGLE_REQUEST_SMOKE_METRICS_URL=<...> \
    IOP_SINGLE_REQUEST_SMOKE_WORKSPACE=<...> IOP_SINGLE_REQUEST_SMOKE_OUTPUT=<...>'

Scope of the block. This change does not touch the smoke harness, the Messages wire, provider routing, or credential handling. The behavior it repairs — template loading, template grammar, admission freezing, and artifact rendering — is fully covered by the deterministic suites above, which pass identically on this container (linux) and on the approved darwin/arm64 host. What remains unqualified is the pre-existing external live-API path, which was already qualified at S12 and is unaffected by this diff. No prior output was reused and no local result is being presented as a live pass.


[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 archive, complete.log, or task-directory moves.
Archive Evidence Snapshot Fixed at stub creation from plan Implementing agent reads only the cited evidence needed for this follow-up.
Implementation Item Completion Fixed item names Implementing agent checks [ ] to [x] only after actual completion.
Implementation Checklist Fixed text/order from plan Implementing agent checks [ ] to [x] only after actual completion.
Review-Only Checklist Review agent only Implementing agent must not modify or check this section.
Deviations from Plan, Key Design Decisions Implementing agent Replace placeholders with actual content.
Reviewer Checkpoints Fixed at stub creation Review agent verifies independently.
Verification Results Fixed headings and commands Implementing agent fills actual output only; command changes require a deviation entry.
Code Review Result Review agent appends Not included in this stub.