docs(agent-task): GLM Coding Plan 구현 계획을 추가한다
일반 API와 구독 endpoint가 동일 모델명으로 섞이지 않도록 profile, credential route, Edge 모델 매핑 및 검증 경계를 구현 전에 확정한다.
This commit is contained in:
parent
32c0754f91
commit
9b2fc2ae47
2 changed files with 811 additions and 0 deletions
298
agent-task/glm_coding_plan/CODE_REVIEW-cloud-G06.md
Normal file
298
agent-task/glm_coding_plan/CODE_REVIEW-cloud-G06.md
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
<!-- task=glm_coding_plan plan=0 tag=API -->
|
||||
|
||||
# Code Review Reference - 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.
|
||||
> 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-02
|
||||
task=glm_coding_plan, plan=0, tag=API
|
||||
|
||||
## 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-G06.md` → `code_review_cloud_G06_0.log` and `PLAN-local-G06.md` → `plan_local_G06_0.log`.
|
||||
3. If PASS, write `complete.log` and move active task directory to `agent-task/archive/YYYY/MM/glm_coding_plan/`. 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 |
|
||||
|------|---------|
|
||||
| API-1 - GLM General/Coding profile catalog and transport regression | [ ] |
|
||||
| API-2 - Managed GLM credential dual-profile route isolation | [ ] |
|
||||
| API-3 - Edge model-driven configuration and current spec | [ ] |
|
||||
| API-4 - SOPS-backed Coding Plan smoke and verification | [ ] |
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
- [ ] [API-1] Register distinct `glm` General API and `glm_coding` Coding Plan profiles, including tool-calling capability, and add literal config plus Node transport regressions.
|
||||
- [ ] [API-2] Permit one `glm/bearer` managed credential slot to bind independently to both GLM profiles and test exact route alias/resource-selector isolation.
|
||||
- [ ] [API-3] Document the model-driven two-provider Edge configuration and synchronize the current provider-pool spec without enabling Coding Plan or adding fallback.
|
||||
- [ ] [API-4] Extend the local Edge smoke profile with the existing SOPS-backed Coding Plan checks and run fresh focused, build, repository E2E, and redacted upstream verification.
|
||||
- [ ] 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.
|
||||
|
||||
- [ ] 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_G06_0.log`.
|
||||
- [ ] Archive active `PLAN-*-G??.md` to `plan_local_G06_0.log`.
|
||||
- [ ] 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/glm_coding_plan/` to `agent-task/archive/YYYY/MM/glm_coding_plan/` 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/glm_coding_plan/` 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
|
||||
|
||||
_Record any deviations from the plan and the rationale here._
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
_Record key design decisions here._
|
||||
|
||||
## Reviewer Checkpoints
|
||||
|
||||
- Verify `glm` remains bound to `https://api.z.ai/api/paas/v4` and `glm_coding` to `https://api.z.ai/api/coding/paas/v4`, with Bearer auth, models/chat/streaming/tool_calling, and no Responses operation/capability.
|
||||
- Verify the literal catalog/auth count tests and Node loopback fixture include `glm_coding` and do not derive expected values from production catalog data.
|
||||
- Verify one `glm/bearer` slot can store distinct `glm`/`glm_coding` routes with aliases `glm-5.1-api`/`glm-5.1-coding` and selectors `glm-api`/`glm-coding`, while another vendor is rejected.
|
||||
- Verify the Edge example never maps both providers under one external model ID, never enables a provider by default, never adds fallback, and records the Z.AI supported-use/proxy restriction without a credential.
|
||||
- Verify no config schema, proto, driver, Edge handler, contract, roadmap, SOPS file, or token source was changed outside the plan.
|
||||
- Verify the local smoke uses the SOPS scalar only through a mode-0600 temporary curl config, calls the Coding base URL only, removes temporary files, and records actual models/chat/SSE/tool evidence without response or credential leakage.
|
||||
- Verify focused tests use `-count=1`, `make build-edge` and `make test-e2e` actually ran, and every command's stdout/stderr is pasted below.
|
||||
|
||||
## Verification Results
|
||||
|
||||
> **[IMPLEMENTING AGENT]** Run every command exactly as written. Paste actual stdout/stderr under the matching `Actual Output` heading. If a command changes, record the replacement and reason in `Deviations from Plan` before pasting its output.
|
||||
|
||||
### API-1 Verification
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
gofmt -w packages/go/config/protocol_profile.go packages/go/config/protocol_profile_test.go apps/node/internal/adapters/openai_compat/protocol_profile_test.go
|
||||
go test -count=1 ./packages/go/config ./apps/node/internal/adapters/openai_compat
|
||||
```
|
||||
|
||||
Expected: both packages pass fresh; the loopback fixture records `/api/coding/paas/v4/chat/completions` and no live provider is called.
|
||||
|
||||
#### Actual Output
|
||||
|
||||
_Paste actual stdout/stderr here._
|
||||
|
||||
### API-2 Verification
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
gofmt -w apps/control-plane/internal/credentialstore/route.go apps/control-plane/internal/credentialstore/route_test.go
|
||||
go test -count=1 ./apps/control-plane/internal/credentialstore
|
||||
```
|
||||
|
||||
Expected: the GLM dual-profile route test passes and the existing incompatible-profile tests remain green.
|
||||
|
||||
#### Actual Output
|
||||
|
||||
_Paste actual stdout/stderr here._
|
||||
|
||||
### API-3 Verification
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
rg --sort path -n 'glm_coding|glm-5\.1-api|glm-5\.1-coding|glm-api|glm-coding' configs/edge.yaml agent-spec/runtime/provider-pool-config-refresh.md
|
||||
git diff --check -- configs/edge.yaml agent-spec/runtime/provider-pool-config-refresh.md
|
||||
```
|
||||
|
||||
Expected: both IDs/profiles appear with distinct provider mappings, and no whitespace error or secret value is present.
|
||||
|
||||
#### Actual Output
|
||||
|
||||
_Paste actual stdout/stderr here._
|
||||
|
||||
### API-4 Verification
|
||||
|
||||
Run the exact redacted command in Final Verification step 4 and copy the same command/criteria into the `GLM Coding Plan` section of `agent-test/local/edge-smoke.md`.
|
||||
|
||||
Expected: preflight passes without revealing the token; models, non-stream, stream, and function-call assertions pass against only `https://api.z.ai/api/coding/paas/v4`; the command prints `glm coding smoke: PASS` and removes temporary files.
|
||||
|
||||
#### Actual Output
|
||||
|
||||
_Paste actual stdout/stderr here._
|
||||
|
||||
### Final Verification
|
||||
|
||||
1. Confirm the checkout and external secret preflight. These commands must not print decrypted data:
|
||||
|
||||
```bash
|
||||
test "$(git branch --show-current)" = "feature/glm-coding-plan"
|
||||
test "$(git merge-base HEAD origin/dev)" = "32c0754f91b05ee95ab25b1062016d44fba18bf2"
|
||||
test "$(stat -c '%a' /config/.config/iop/secrets/dev-openai-toki.sops.yaml)" = "600"
|
||||
test "$(stat -c '%a' /config/.config/sops/age/keys.txt)" = "600"
|
||||
SOPS_AGE_KEY_FILE=/config/.config/sops/age/keys.txt sops filestatus /config/.config/iop/secrets/dev-openai-toki.sops.yaml | jq -e '.encrypted == true'
|
||||
test "$(SOPS_AGE_KEY_FILE=/config/.config/sops/age/keys.txt sops decrypt --extract '["tokens"]["glm-coding-plan"]' /config/.config/iop/secrets/dev-openai-toki.sops.yaml | wc -c)" -gt 1
|
||||
```
|
||||
|
||||
Expected: every command exits 0 and only the non-secret filestatus JSON is emitted.
|
||||
|
||||
2. Confirm formatting and run fresh focused tests:
|
||||
|
||||
```bash
|
||||
test -z "$(gofmt -d packages/go/config/protocol_profile.go packages/go/config/protocol_profile_test.go apps/control-plane/internal/credentialstore/route.go apps/control-plane/internal/credentialstore/route_test.go apps/node/internal/adapters/openai_compat/protocol_profile_test.go)"
|
||||
go test -count=1 ./packages/go/config ./apps/control-plane/internal/credentialstore ./apps/node/internal/adapters/openai_compat
|
||||
```
|
||||
|
||||
Expected: no gofmt diff and all three packages pass.
|
||||
|
||||
3. Run the Edge/config user-path build and repository-native full cycle:
|
||||
|
||||
```bash
|
||||
make build-edge
|
||||
make test-e2e
|
||||
```
|
||||
|
||||
Expected: Edge builds and the repository Edge-Node E2E exits 0. Record actual stdout/stderr; this verifies the existing user pipeline but does not claim a live Coding Plan proxy deployment.
|
||||
|
||||
4. Execute the redacted Coding Plan smoke. This command uses only the Coding endpoint and prints no response body or credential:
|
||||
|
||||
```bash
|
||||
bash <<'BASH'
|
||||
set -euo pipefail
|
||||
set +x
|
||||
|
||||
sops_file=/config/.config/iop/secrets/dev-openai-toki.sops.yaml
|
||||
age_file=/config/.config/sops/age/keys.txt
|
||||
base_url=https://api.z.ai/api/coding/paas/v4
|
||||
smoke_dir="$(mktemp -d /tmp/iop-glm-coding-smoke.XXXXXX)"
|
||||
cleanup() {
|
||||
rm -rf -- "$smoke_dir"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
test "$(stat -c '%a' "$sops_file")" = "600"
|
||||
test "$(stat -c '%a' "$age_file")" = "600"
|
||||
SOPS_AGE_KEY_FILE="$age_file" sops filestatus "$sops_file" | jq -e '.encrypted == true' >/dev/null
|
||||
token="$(SOPS_AGE_KEY_FILE="$age_file" sops decrypt --extract '["tokens"]["glm-coding-plan"]' "$sops_file")"
|
||||
test -n "$token"
|
||||
|
||||
umask 077
|
||||
auth_config="$smoke_dir/curl-auth.conf"
|
||||
printf 'header = "Authorization: Bearer %s"\nheader = "Content-Type: application/json"\n' "$token" >"$auth_config"
|
||||
unset token
|
||||
|
||||
curl --fail --silent --show-error --config "$auth_config" \
|
||||
--output "$smoke_dir/models.json" "$base_url/models"
|
||||
jq -e '(.data | type == "array") and any(.data[]; (.id | ascii_downcase) == "glm-5.1")' \
|
||||
"$smoke_dir/models.json" >/dev/null
|
||||
|
||||
jq -n '{
|
||||
model: "glm-5.1",
|
||||
messages: [{role: "user", content: "Reply exactly GLM_CODING_PLAN_OK"}],
|
||||
stream: false,
|
||||
max_tokens: 32
|
||||
}' >"$smoke_dir/chat.json"
|
||||
curl --fail --silent --show-error --config "$auth_config" \
|
||||
--request POST --data-binary "@$smoke_dir/chat.json" \
|
||||
--output "$smoke_dir/chat-response.json" "$base_url/chat/completions"
|
||||
jq -e '(.choices[0].message.content | type == "string" and length > 0) and (.usage.total_tokens | type == "number")' \
|
||||
"$smoke_dir/chat-response.json" >/dev/null
|
||||
|
||||
jq -n '{
|
||||
model: "glm-5.1",
|
||||
messages: [{role: "user", content: "Reply exactly GLM_CODING_PLAN_STREAM_OK"}],
|
||||
stream: true,
|
||||
max_tokens: 32
|
||||
}' >"$smoke_dir/stream.json"
|
||||
curl --fail --silent --show-error --no-buffer --config "$auth_config" \
|
||||
--request POST --data-binary "@$smoke_dir/stream.json" \
|
||||
--output "$smoke_dir/stream-response.txt" "$base_url/chat/completions"
|
||||
grep -Eq '^data: .+' "$smoke_dir/stream-response.txt"
|
||||
grep -Fq 'data: [DONE]' "$smoke_dir/stream-response.txt"
|
||||
|
||||
jq -n '{
|
||||
model: "glm-5.1",
|
||||
messages: [{role: "user", content: "Call emit_marker with marker GLM_CODING_PLAN_TOOL_OK. Do not answer directly."}],
|
||||
tools: [{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "emit_marker",
|
||||
description: "Emit the requested verification marker",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {marker: {type: "string"}},
|
||||
required: ["marker"]
|
||||
}
|
||||
}
|
||||
}],
|
||||
tool_choice: "auto",
|
||||
stream: false,
|
||||
max_tokens: 64
|
||||
}' >"$smoke_dir/tool.json"
|
||||
curl --fail --silent --show-error --config "$auth_config" \
|
||||
--request POST --data-binary "@$smoke_dir/tool.json" \
|
||||
--output "$smoke_dir/tool-response.json" "$base_url/chat/completions"
|
||||
jq -e 'any(.choices[0].message.tool_calls[]?; .function.name == "emit_marker")' \
|
||||
"$smoke_dir/tool-response.json" >/dev/null
|
||||
|
||||
printf 'glm coding smoke: PASS\n'
|
||||
BASH
|
||||
```
|
||||
|
||||
Expected: `/models`, non-stream Chat, SSE `[DONE]`, and one tool call pass at `https://api.z.ai/api/coding/paas/v4` only; the command prints `glm coding smoke: PASS` and removes all temporary files.
|
||||
|
||||
5. Inspect scope and secret safety:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
git diff --name-only -- packages/go/config/protocol_profile.go packages/go/config/protocol_profile_test.go apps/control-plane/internal/credentialstore/route.go apps/control-plane/internal/credentialstore/route_test.go apps/node/internal/adapters/openai_compat/protocol_profile_test.go configs/edge.yaml agent-spec/runtime/provider-pool-config-refresh.md
|
||||
rg --sort path -n 'glm_coding|glm-5\.1-api|glm-5\.1-coding' packages/go/config/protocol_profile.go packages/go/config/protocol_profile_test.go apps/control-plane/internal/credentialstore/route.go apps/control-plane/internal/credentialstore/route_test.go apps/node/internal/adapters/openai_compat/protocol_profile_test.go configs/edge.yaml agent-spec/runtime/provider-pool-config-refresh.md agent-test/local/edge-smoke.md
|
||||
```
|
||||
|
||||
Expected: only the claimed implementation files plus active review evidence are modified; GLM split identifiers are present; no token/API key value appears in the diff or command output.
|
||||
|
||||
After completing all code changes, fill implementation-owned sections in `CODE_REVIEW-*-G??.md`.
|
||||
|
||||
#### Actual Output
|
||||
|
||||
_Paste actual stdout/stderr for each numbered command here._
|
||||
|
||||
---
|
||||
|
||||
> **[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 |
|
||||
513
agent-task/glm_coding_plan/PLAN-local-G06.md
Normal file
513
agent-task/glm_coding_plan/PLAN-local-G06.md
Normal file
|
|
@ -0,0 +1,513 @@
|
|||
<!-- task=glm_coding_plan plan=0 tag=API -->
|
||||
|
||||
# Plan - GLM General API와 Coding Plan Profile 분리
|
||||
|
||||
## For the Implementing Agent
|
||||
|
||||
> `CODE_REVIEW-cloud-G06.md`의 implementation-owned section 작성은 구현의 필수 마지막 단계다.
|
||||
> 모든 검증을 실행하고 실제 구현 메모와 stdout/stderr를 채운 뒤 active PLAN/CODE_REVIEW 파일을 그대로 두고 review 준비 완료를 보고한다.
|
||||
> 최종 판정, archive, `complete.log` 작성은 code-review skill만 수행한다.
|
||||
> 차단되면 implementation-owned evidence field에 정확한 blocker, 시도한 명령/출력, 재개 조건만 기록한다. 사용자에게 질문하거나 user-input tool/control-plane stop file을 만들거나 다음 상태를 분류하지 않는다.
|
||||
|
||||
## Background
|
||||
|
||||
현재 built-in `glm` profile은 일반 종량제 endpoint만 가리키고 공식 function calling capability도 선언하지 않는다. Z.AI Coding Plan은 같은 Bearer/OpenAI Chat 계약을 사용하지만 별도 `/api/coding/paas/v4` endpoint와 구독 quota를 사용하므로 endpoint override만으로 합치면 과금 경로와 managed credential route가 모호해진다. `glm`과 `glm_coding`을 독립 profile로 유지하고 Edge 외부 model id도 provider별로 분리해 일반 API/Coding Plan 사이 자동 fallback이나 pool 혼합이 발생하지 않게 한다. Coding Plan은 기본 활성화하지 않으며 공식 지원 도구·개인 사용·proxy 제한을 운영 예시와 smoke에 명시한다.
|
||||
|
||||
## Analysis
|
||||
|
||||
### Files Read
|
||||
|
||||
- `AGENTS.md`
|
||||
- `.gitignore`
|
||||
- `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/platform-common/rules.md`
|
||||
- `agent-ops/rules/project/domain/control-plane/rules.md`
|
||||
- `agent-ops/rules/project/domain/node/rules.md`
|
||||
- `agent-ops/rules/project/domain/edge/rules.md`
|
||||
- `agent-ops/rules/project/domain/testing/rules.md`
|
||||
- `agent-ops/skills/common/router.md`
|
||||
- `agent-ops/skills/common/plan/SKILL.md`
|
||||
- `agent-ops/skills/common/update-test/SKILL.md`
|
||||
- `agent-ops/skills/common/finalize-task-routing/SKILL.md`
|
||||
- `agent-ops/skills/common/plan/templates/review-stub-template.md`
|
||||
- `agent-roadmap/ROADMAP.md`
|
||||
- `agent-roadmap/current.md`
|
||||
- `agent-roadmap/priority-queue.md`
|
||||
- `agent-roadmap/phase/operational-observability-provider-management/PHASE.md`
|
||||
- `agent-spec/index.md`
|
||||
- `agent-spec/runtime/provider-pool-config-refresh.md`
|
||||
- `agent-spec/input/openai-compatible-surface.md`
|
||||
- `agent-contract/index.md`
|
||||
- `agent-contract/inner/edge-config-runtime-refresh.md`
|
||||
- `agent-contract/outer/openai-compatible-api.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/platform-common-smoke.md`
|
||||
- `agent-test/local/control-plane-smoke.md`
|
||||
- `agent-test/local/node-smoke.md`
|
||||
- `agent-test/local/edge-smoke.md`
|
||||
- `packages/go/config/protocol_profile.go`
|
||||
- `packages/go/config/protocol_profile_test.go`
|
||||
- `apps/control-plane/internal/credentialstore/route.go`
|
||||
- `apps/control-plane/internal/credentialstore/route_test.go`
|
||||
- `apps/node/internal/adapters/openai_compat/provider.go`
|
||||
- `apps/node/internal/adapters/openai_compat/protocol_profile_test.go`
|
||||
- `configs/edge.yaml`
|
||||
|
||||
### SDD Criteria
|
||||
|
||||
not applicable. 이 작업은 현재 활성 Milestone과 무관한 기존 provider-profile/credential-route 기능의 소규모 후속이며, 사용자가 Milestone 생성이 불필요하다고 확정했다. Roadmap과 `agent-roadmap/current.md`는 수정하지 않는다.
|
||||
|
||||
### Verification Context
|
||||
|
||||
- Handoff: supplied. 사용자가 `token/.glm` credential로 Coding Plan endpoint 접속 확인, SOPS 저장, agent-test 절차 기록을 요청했고 이전 검증에서 `GET /models` 200(8 models), `glm-5.1` non-stream Chat 200, SSE 200/`[DONE]`, usage 응답을 확인했다. token 원문은 기록하지 않았다.
|
||||
- update-test resolve-context:
|
||||
- Rules State: usable.
|
||||
- Sources: `agent-test/local/rules.md`, `agent-test/local/platform-common-smoke.md`, `agent-test/local/control-plane-smoke.md`, `agent-test/local/node-smoke.md`, `agent-test/local/edge-smoke.md`.
|
||||
- Required repository commands: fresh `go test` for changed config/control-plane/node packages; `make build-edge`; `make test-e2e` for the touched Edge/config user path.
|
||||
- Cache: fresh required; all Go commands use `-count=1`.
|
||||
- Constraint: token/API key/private endpoint values must not appear in tracked files or stdout/stderr.
|
||||
- Repository baseline:
|
||||
- `go test -count=1 ./packages/go/config ./apps/control-plane/internal/credentialstore ./apps/node/internal/adapters/openai_compat`: PASS before changes.
|
||||
- The existing adapter already resolves profile-specific `models` and `chat_completions` operations; no new driver or adapter branch is required.
|
||||
- Official references reviewed on 2026-08-02:
|
||||
- `https://docs.z.ai/scenario-example/develop-tools/others` — Coding Plan base URL differs from General API.
|
||||
- `https://docs.z.ai/api-reference/introduction` — Bearer authentication and endpoint distinction.
|
||||
- `https://docs.z.ai/api-reference/llm/chat-completion` — Chat, stream, tools/tool calls.
|
||||
- `https://docs.z.ai/legal-agreement/subscription-terms` and `https://docs.z.ai/devpack/usage-policy` — supported-tool, personal-use, and proxy/sharing restrictions.
|
||||
- External Verification Preflight:
|
||||
- Runner/workdir: local shell, `/config/workspace/iop-s2`.
|
||||
- Branch/HEAD/dirty: `feature/glm-coding-plan`, `32c0754f91b05ee95ab25b1062016d44fba18bf2`, clean at plan preflight.
|
||||
- Source sync: `origin/dev` is the same commit; left/right delta `0 0`.
|
||||
- OS/arch: Linux/aarch64.
|
||||
- Tools: Go `go1.26.2 linux/arm64` at `/config/opt/go/bin/go` (module minimum is Go 1.24), SOPS 3.13.1, jq 1.7, curl 8.5.0, Python 3.12.3.
|
||||
- Config/evidence paths: repository `configs/edge.yaml`; encrypted SOPS `/config/.config/iop/secrets/dev-openai-toki.sops.yaml`; age key `/config/.config/sops/age/keys.txt`.
|
||||
- Secret state: SOPS filestatus reports `encrypted=true`; both files mode `0600`; `tokens.glm-coding-plan` decrypts to a non-empty value without printing it.
|
||||
- External host: `api.z.ai:443` reachable; unauthenticated `https://api.z.ai/api/coding/paas/v4/models` returns 401.
|
||||
- Binary/artifact/ports/process: direct provider smoke needs no IOP binary or listening port. `make build-edge` and `make test-e2e` build/start their own repository-native artifacts/runtime.
|
||||
- Mismatch handling: if branch/HEAD/source sync or secret modes differ, stop and restore the exact checkout/`0600` state before live smoke. Do not copy the secret into the repository.
|
||||
- Gaps: no repository implementation gap remains unidentified. A live IOP-as-proxy Coding Plan deployment is intentionally not part of completion because provider terms require an eligible supported use or separate authorization; the profile and example remain explicit opt-in.
|
||||
- Confidence: high. Official endpoint/auth/tool facts, successful live upstream evidence, current code, focused baseline tests, and repository-native smoke rules agree.
|
||||
- Maintenance: update-test-candidate by user request; extend the existing `edge-smoke` profile rather than creating a new test profile.
|
||||
|
||||
### Test Coverage Gaps
|
||||
|
||||
- Built-in catalog has no `glm_coding` ID, Coding Plan URL assertion, or explicit “no Responses” assertion. Add literal catalog/URL/capability tests.
|
||||
- `glm` omits `tool_calling` despite the official Chat API supporting tools. Assert both GLM profiles advertise it.
|
||||
- Provider-pool config has no regression proving two external model IDs select two distinct provider/profile IDs while both rewrite to `glm-5.1`. Add a YAML load test.
|
||||
- Node loopback operation fixtures cover only the general `glm` URL. Add `glm_coding` across JSON, SSE, and provider-error fixture outcomes.
|
||||
- Managed credential compatibility allows only `glm` for `glm/bearer`. Add a same-slot dual-profile route test including distinct aliases and exact resource selectors, plus a cross-vendor rejection.
|
||||
- Local Edge smoke has no SOPS-backed Coding Plan procedure or usage-policy guard. Add the exact redacted preflight and models/chat/stream/tool checks.
|
||||
- No real Edge proxy request is added to the completion gate; deterministic config/adapter/managed-route tests plus repository E2E cover IOP behavior, while direct authenticated upstream evidence covers the endpoint.
|
||||
|
||||
### Symbol References
|
||||
|
||||
none. No symbol is renamed or removed.
|
||||
|
||||
### Split Judgment
|
||||
|
||||
Single plan. The indivisible invariant is that one stable `glm_coding` profile identity must agree across the built-in catalog, managed credential allow-list, Node operation URL fixture, Edge provider/model example, spec, and smoke procedure. Splitting could leave a selectable profile without a compatible managed route or a credential route without the expected transport endpoint.
|
||||
|
||||
### Scope Rationale
|
||||
|
||||
- Do not add a protocol driver, config/schema field, protobuf message, adapter implementation, OpenAI handler branch, provider category, or automatic endpoint selection; existing `openai_chat`, `category: api`, profile resolution, and model-driven provider-pool routing already cover the behavior.
|
||||
- Do not change `agent-contract/inner/edge-config-runtime-refresh.md` or `agent-contract/outer/openai-compatible-api.md`. Their stable contracts already define profile selection, exact managed route binding, external `model` routing, and no-fallback behavior; this task only adds catalog data and examples.
|
||||
- Do not modify roadmap/Milestone state, SOPS/age files, `token/.glm`, or any tracked secret. The host-local encrypted token already exists and is verification input only.
|
||||
- Do not add `responses` to either GLM profile, infer a model list in code, combine `glm-api` and `glm-coding` beneath one external model ID, or fall back between General API and Coding Plan.
|
||||
- Do not enable the Coding Plan provider in active example config. All GLM provider/model entries remain comment-only and include the official usage restriction.
|
||||
|
||||
### Final Routing
|
||||
|
||||
- evaluation_mode: `first-pass`
|
||||
- finalizer: `finalize-task-policy.sh`, mode `pair`
|
||||
- Build closures: scope/context/verification/evidence/ownership/decision all `true`; capability gap none.
|
||||
- Build scores: scope_coupling=2, state_concurrency=0, blast_irreversibility=1, evidence_diagnosis=1, verification_complexity=2 -> G06.
|
||||
- Build route: base=`local-fit`, route=`local-fit`, lane=`local`, filename=`PLAN-local-G06.md`.
|
||||
- large_indivisible_context: `false`.
|
||||
- matched loop risk: `boundary_contract`; count=1; risk boundary=false.
|
||||
- recovery signals: review_rework_count=0, evidence_integrity_failure=false; recovery boundary=false.
|
||||
- Review closures: scope/context/verification/evidence/ownership/decision all `true`; capability gap none.
|
||||
- Review scores: scope_coupling=2, state_concurrency=0, blast_irreversibility=1, evidence_diagnosis=1, verification_complexity=2 -> G06.
|
||||
- Review route: `official-review`, lane=`cloud`, adapter=`codex`, model=`gpt-5.6-sol`, reasoning=`xhigh`, filename=`CODE_REVIEW-cloud-G06.md`.
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
- [ ] [API-1] Register distinct `glm` General API and `glm_coding` Coding Plan profiles, including tool-calling capability, and add literal config plus Node transport regressions.
|
||||
- [ ] [API-2] Permit one `glm/bearer` managed credential slot to bind independently to both GLM profiles and test exact route alias/resource-selector isolation.
|
||||
- [ ] [API-3] Document the model-driven two-provider Edge configuration and synchronize the current provider-pool spec without enabling Coding Plan or adding fallback.
|
||||
- [ ] [API-4] Extend the local Edge smoke profile with the existing SOPS-backed Coding Plan checks and run fresh focused, build, repository E2E, and redacted upstream verification.
|
||||
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.
|
||||
|
||||
### [API-1] Add the Coding Plan protocol profile and transport regression
|
||||
|
||||
#### Problem
|
||||
|
||||
`packages/go/config/protocol_profile.go:186-195` defines only the General API profile and omits `tool_calling`. `packages/go/config/protocol_profile_test.go:301-420` and `:689-717` therefore cannot detect a missing Coding Plan identity, wrong URL/auth, accidental Responses support, or catalog count drift. `apps/node/internal/adapters/openai_compat/protocol_profile_test.go:19-44` exercises only `/api/paas/v4/chat/completions`.
|
||||
|
||||
Current:
|
||||
|
||||
```go
|
||||
// packages/go/config/protocol_profile.go:186
|
||||
"glm": {
|
||||
Driver: ProtocolDriverOpenAIChat,
|
||||
BaseURL: "https://api.z.ai/api/paas/v4",
|
||||
// ...
|
||||
Capabilities: []string{"models", "chat", "streaming"},
|
||||
},
|
||||
```
|
||||
|
||||
#### Solution
|
||||
|
||||
Keep `glm` as General API, add its official `tool_calling` capability, and register a separate built-in `glm_coding` with the exact Coding Plan base URL. Both profiles expose only `models` and `chat_completions` operations with Bearer auth; neither exposes Responses.
|
||||
|
||||
Target:
|
||||
|
||||
```go
|
||||
"glm": {
|
||||
Driver: ProtocolDriverOpenAIChat,
|
||||
BaseURL: "https://api.z.ai/api/paas/v4",
|
||||
Operations: map[string]string{
|
||||
string(OperationModels): "/models",
|
||||
string(OperationChatCompletions): "/chat/completions",
|
||||
},
|
||||
Auth: ProtocolAuthConf{Header: "Authorization", Scheme: "Bearer"},
|
||||
Capabilities: []string{"models", "chat", "streaming", "tool_calling"},
|
||||
},
|
||||
"glm_coding": {
|
||||
Driver: ProtocolDriverOpenAIChat,
|
||||
BaseURL: "https://api.z.ai/api/coding/paas/v4",
|
||||
Operations: map[string]string{
|
||||
string(OperationModels): "/models",
|
||||
string(OperationChatCompletions): "/chat/completions",
|
||||
},
|
||||
Auth: ProtocolAuthConf{Header: "Authorization", Scheme: "Bearer"},
|
||||
Capabilities: []string{"models", "chat", "streaming", "tool_calling"},
|
||||
},
|
||||
```
|
||||
|
||||
Add literal tests that:
|
||||
|
||||
- include `glm_coding` in the exact built-in ID/auth matrix;
|
||||
- resolve General and Coding URLs to `/api/paas/v4/...` and `/api/coding/paas/v4/...` respectively;
|
||||
- assert both profiles have models/chat/streaming/tool_calling and lack Responses operation/capability;
|
||||
- load a provider-pool YAML with external `glm-5.1-api` -> provider `glm-api`/profile `glm` and `glm-5.1-coding` -> provider `glm-coding`/profile `glm_coding`, both rewriting to upstream `glm-5.1`, and assert no cross-mapping;
|
||||
- add `glm_coding` to the Node loopback operation fixture so JSON, SSE, and provider-error responses all hit the Coding Plan path with Bearer auth.
|
||||
|
||||
#### Modified Files and Checklist
|
||||
|
||||
- [ ] `packages/go/config/protocol_profile.go` — add `glm_coding` and `tool_calling` for both GLM profiles.
|
||||
- [ ] `packages/go/config/protocol_profile_test.go` — update exact catalog/auth matrices and add URL/capability/provider-pool isolation regressions.
|
||||
- [ ] `apps/node/internal/adapters/openai_compat/protocol_profile_test.go` — add the Coding Plan operation fixture and update the official-doc verification comment/date.
|
||||
|
||||
#### Test Strategy
|
||||
|
||||
Write tests. Extend `TestBuiltInProtocolProfileCatalog`, `TestBuiltInProtocolProfileURLs`, and `TestProtocolProfileBuiltInAuthMatrix`; add `TestBuiltInGLMProtocolProfiles` and `TestGLMProfilesRemainDistinctThroughProviderPoolConfig`; add `glm_coding` to `TestProtocolProfileOperationURLFixtures`. Use only literal expected values, temporary YAML, and `httptest.Server`—never production catalog values as expected fixtures or a live key.
|
||||
|
||||
#### Verification
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
gofmt -w packages/go/config/protocol_profile.go packages/go/config/protocol_profile_test.go apps/node/internal/adapters/openai_compat/protocol_profile_test.go
|
||||
go test -count=1 ./packages/go/config ./apps/node/internal/adapters/openai_compat
|
||||
```
|
||||
|
||||
Expected: both packages pass fresh; the loopback fixture records `/api/coding/paas/v4/chat/completions` and no live provider is called.
|
||||
|
||||
### [API-2] Bind the same GLM slot to isolated General and Coding routes
|
||||
|
||||
#### Problem
|
||||
|
||||
`apps/control-plane/internal/credentialstore/route.go:82-95` intentionally uses a closed compatibility matrix, but `glm/bearer` admits only `glm`. This prevents a managed GLM credential slot from selecting `glm_coding` and leaves no regression for same vendor/model with different endpoint profiles.
|
||||
|
||||
Current:
|
||||
|
||||
```go
|
||||
// apps/control-plane/internal/credentialstore/route.go:91
|
||||
"glm/bearer": {"glm": {header: "Authorization", scheme: "Bearer"}},
|
||||
```
|
||||
|
||||
#### Solution
|
||||
|
||||
Add `glm_coding` to the existing `glm/bearer` profile rule with the same exact Bearer declaration. Do not introduce a subscription credential kind: subscription/pay-go is a protocol-profile endpoint distinction, while the credential remains vendor `glm` and kind `bearer`.
|
||||
|
||||
Target:
|
||||
|
||||
```go
|
||||
"glm/bearer": {
|
||||
"glm": {header: "Authorization", scheme: "Bearer"},
|
||||
"glm_coding": {header: "Authorization", scheme: "Bearer"},
|
||||
},
|
||||
```
|
||||
|
||||
Add `TestGLMSlotSupportsGeneralAndCodingProfiles` beside the multi-profile MiniMax test at `apps/control-plane/internal/credentialstore/route_test.go:222-240`. Create one draft GLM Bearer slot, then create:
|
||||
|
||||
- alias `glm-5.1-api`, profile `glm`, upstream `glm-5.1`, selector `glm-api`;
|
||||
- alias `glm-5.1-coding`, profile `glm_coding`, upstream `glm-5.1`, selector `glm-coding`.
|
||||
|
||||
Assert both routes share the slot but preserve distinct profile/alias/selector values, listing returns both, and an OpenAI Bearer slot rejects `glm_coding` with `ErrIncompatibleProfile`.
|
||||
|
||||
#### Modified Files and Checklist
|
||||
|
||||
- [ ] `apps/control-plane/internal/credentialstore/route.go` — extend only the `glm/bearer` closed rule.
|
||||
- [ ] `apps/control-plane/internal/credentialstore/route_test.go` — add same-slot dual-profile and cross-vendor rejection coverage.
|
||||
|
||||
#### Test Strategy
|
||||
|
||||
Write `TestGLMSlotSupportsGeneralAndCodingProfiles` using the existing SQLite temp store, fake key registry, opaque envelope, and real catalog resolution. It must assert the stored route fields rather than only checking a nil error.
|
||||
|
||||
#### Verification
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
gofmt -w apps/control-plane/internal/credentialstore/route.go apps/control-plane/internal/credentialstore/route_test.go
|
||||
go test -count=1 ./apps/control-plane/internal/credentialstore
|
||||
```
|
||||
|
||||
Expected: the GLM dual-profile route test passes and the existing incompatible-profile tests remain green.
|
||||
|
||||
### [API-3] Show model-driven Edge selection and synchronize the current spec
|
||||
|
||||
#### Problem
|
||||
|
||||
`configs/edge.yaml:200-240` lists only `glm` and provides no safe example for choosing the endpoint through an external model ID. `agent-spec/runtime/provider-pool-config-refresh.md:91-116` describes generic model/profile routing but not the two stable GLM profiles or their no-fallback invariant.
|
||||
|
||||
Current:
|
||||
|
||||
```yaml
|
||||
# glm — openai_chat driver, https://api.z.ai/api/paas/v4
|
||||
```
|
||||
|
||||
#### Solution
|
||||
|
||||
Add `glm_coding` to the built-in list and a comment-only provider-pool example. External model IDs—not an extra request field—select separate provider IDs:
|
||||
|
||||
```yaml
|
||||
# models:
|
||||
# - id: "glm-5.1-api"
|
||||
# providers:
|
||||
# glm-api: "glm-5.1"
|
||||
# - id: "glm-5.1-coding"
|
||||
# providers:
|
||||
# glm-coding: "glm-5.1"
|
||||
# nodes:
|
||||
# - id: "node-glm-example"
|
||||
# providers:
|
||||
# - id: "glm-api"
|
||||
# type: "openai_api"
|
||||
# category: "api"
|
||||
# profile: "glm"
|
||||
# models: ["glm-5.1"]
|
||||
# capacity: 1
|
||||
# - id: "glm-coding"
|
||||
# type: "openai_api"
|
||||
# category: "api"
|
||||
# profile: "glm_coding"
|
||||
# models: ["glm-5.1"]
|
||||
# capacity: 1
|
||||
```
|
||||
|
||||
State beside the example that both provider IDs must never be placed under the same external model ID when endpoint/quota isolation is required, no General/Coding fallback exists, no raw key belongs in YAML, and Coding Plan may be enabled only for use allowed by current Z.AI terms. Update the current runtime spec feature/config/limitation/change-record sections with the same implemented facts; do not copy credential values or create a new contract.
|
||||
|
||||
#### Modified Files and Checklist
|
||||
|
||||
- [ ] `configs/edge.yaml` — document the built-in profile and the comment-only two-model/two-provider mapping with usage/fallback guardrails.
|
||||
- [ ] `agent-spec/runtime/provider-pool-config-refresh.md` — synchronize the implemented GLM profile/routing capability, no-fallback boundary, validation references, and 2026-08-02 change record.
|
||||
|
||||
#### Test Strategy
|
||||
|
||||
No separate prose-only test. API-1's `TestGLMProfilesRemainDistinctThroughProviderPoolConfig` loads the same mapping shape and is the executable contract. Review the tracked diff for raw-key patterns and ensure the example stays fully commented.
|
||||
|
||||
#### Verification
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
rg --sort path -n 'glm_coding|glm-5\.1-api|glm-5\.1-coding|glm-api|glm-coding' configs/edge.yaml agent-spec/runtime/provider-pool-config-refresh.md
|
||||
git diff --check -- configs/edge.yaml agent-spec/runtime/provider-pool-config-refresh.md
|
||||
```
|
||||
|
||||
Expected: both IDs/profiles appear with distinct provider mappings, and no whitespace error or secret value is present.
|
||||
|
||||
### [API-4] Record and execute the SOPS-backed Coding Plan smoke
|
||||
|
||||
#### Problem
|
||||
|
||||
`agent-test/local/edge-smoke.md:25-48` only says to use a provider-specific command. It does not record the existing encrypted token location, permission/encryption preflight, exact Coding Plan URL, chat/stream/tool acceptance checks, or the prohibition on printing credentials and silently switching to General API.
|
||||
|
||||
#### Solution
|
||||
|
||||
Update the existing local profile (it is intentionally gitignored/operator-local) and `last_rule_updated_at`. Add a `GLM Coding Plan` subsection containing:
|
||||
|
||||
- opt-in/terms precondition and a ban on shared/proxy production use without provider authorization;
|
||||
- exact SOPS/age paths, `0600` checks, `sops filestatus` encryption assertion, and non-empty scalar extraction without echo;
|
||||
- a mode-`0600` temporary curl config carrying the Bearer header so the raw key is absent from command arguments/output;
|
||||
- `GET /api/coding/paas/v4/models` with a `glm-5.1` assertion;
|
||||
- non-stream Chat with model/content/usage assertions;
|
||||
- SSE Chat with at least one `data:` event and terminal `data: [DONE]`;
|
||||
- an auto function-calling request whose response contains the declared function name;
|
||||
- cleanup and an explicit statement that this procedure never calls `/api/paas/v4` as fallback.
|
||||
|
||||
Keep all response artifacts in a `mktemp -d` directory outside the repository and print only a final PASS marker. Then run the repository commands and live smoke exactly as recorded.
|
||||
|
||||
#### Modified Files and Checklist
|
||||
|
||||
- [ ] `agent-test/local/edge-smoke.md` — add the redacted SOPS-backed Coding Plan preflight, calls, acceptance criteria, cleanup, terms guard, and date.
|
||||
|
||||
#### Test Strategy
|
||||
|
||||
Update the existing test profile; do not create a new profile or tracked secret fixture. The live check uses the user's host-local encrypted token, while repository behavior remains covered by API-1/API-2 loopback/config tests. If provider terms or token eligibility block the live call, record the exact blocker in review evidence and do not try the General API endpoint.
|
||||
|
||||
#### Verification
|
||||
|
||||
Run the exact redacted command in Final Verification step 4 and copy the same command/criteria into the `GLM Coding Plan` section of `agent-test/local/edge-smoke.md`.
|
||||
|
||||
Expected: preflight passes without revealing the token; models, non-stream, stream, and function-call assertions pass against only `https://api.z.ai/api/coding/paas/v4`; the command prints `glm coding smoke: PASS` and removes temporary files.
|
||||
|
||||
## Modified Files Summary
|
||||
|
||||
| File | Item |
|
||||
|------|------|
|
||||
| `packages/go/config/protocol_profile.go` | API-1 |
|
||||
| `packages/go/config/protocol_profile_test.go` | API-1 |
|
||||
| `apps/node/internal/adapters/openai_compat/protocol_profile_test.go` | API-1 |
|
||||
| `apps/control-plane/internal/credentialstore/route.go` | API-2 |
|
||||
| `apps/control-plane/internal/credentialstore/route_test.go` | API-2 |
|
||||
| `configs/edge.yaml` | API-3 |
|
||||
| `agent-spec/runtime/provider-pool-config-refresh.md` | API-3 |
|
||||
| `agent-test/local/edge-smoke.md` | API-4 |
|
||||
| `agent-task/glm_coding_plan/CODE_REVIEW-cloud-G06.md` | API-1, API-2, API-3, API-4 |
|
||||
|
||||
## Final Verification
|
||||
|
||||
1. Confirm the checkout and external secret preflight. These commands must not print decrypted data:
|
||||
|
||||
```bash
|
||||
test "$(git branch --show-current)" = "feature/glm-coding-plan"
|
||||
test "$(git merge-base HEAD origin/dev)" = "32c0754f91b05ee95ab25b1062016d44fba18bf2"
|
||||
test "$(stat -c '%a' /config/.config/iop/secrets/dev-openai-toki.sops.yaml)" = "600"
|
||||
test "$(stat -c '%a' /config/.config/sops/age/keys.txt)" = "600"
|
||||
SOPS_AGE_KEY_FILE=/config/.config/sops/age/keys.txt sops filestatus /config/.config/iop/secrets/dev-openai-toki.sops.yaml | jq -e '.encrypted == true'
|
||||
test "$(SOPS_AGE_KEY_FILE=/config/.config/sops/age/keys.txt sops decrypt --extract '["tokens"]["glm-coding-plan"]' /config/.config/iop/secrets/dev-openai-toki.sops.yaml | wc -c)" -gt 1
|
||||
```
|
||||
|
||||
Expected: every command exits 0 and only the non-secret filestatus JSON is emitted.
|
||||
|
||||
2. Confirm formatting and run fresh focused tests:
|
||||
|
||||
```bash
|
||||
test -z "$(gofmt -d packages/go/config/protocol_profile.go packages/go/config/protocol_profile_test.go apps/control-plane/internal/credentialstore/route.go apps/control-plane/internal/credentialstore/route_test.go apps/node/internal/adapters/openai_compat/protocol_profile_test.go)"
|
||||
go test -count=1 ./packages/go/config ./apps/control-plane/internal/credentialstore ./apps/node/internal/adapters/openai_compat
|
||||
```
|
||||
|
||||
Expected: no gofmt diff and all three packages pass.
|
||||
|
||||
3. Run the Edge/config user-path build and repository-native full cycle:
|
||||
|
||||
```bash
|
||||
make build-edge
|
||||
make test-e2e
|
||||
```
|
||||
|
||||
Expected: Edge builds and the repository Edge-Node E2E exits 0. Record actual stdout/stderr; this verifies the existing user pipeline but does not claim a live Coding Plan proxy deployment.
|
||||
|
||||
4. Execute the redacted Coding Plan smoke. This command uses only the Coding endpoint and prints no response body or credential:
|
||||
|
||||
```bash
|
||||
bash <<'BASH'
|
||||
set -euo pipefail
|
||||
set +x
|
||||
|
||||
sops_file=/config/.config/iop/secrets/dev-openai-toki.sops.yaml
|
||||
age_file=/config/.config/sops/age/keys.txt
|
||||
base_url=https://api.z.ai/api/coding/paas/v4
|
||||
smoke_dir="$(mktemp -d /tmp/iop-glm-coding-smoke.XXXXXX)"
|
||||
cleanup() {
|
||||
rm -rf -- "$smoke_dir"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
test "$(stat -c '%a' "$sops_file")" = "600"
|
||||
test "$(stat -c '%a' "$age_file")" = "600"
|
||||
SOPS_AGE_KEY_FILE="$age_file" sops filestatus "$sops_file" | jq -e '.encrypted == true' >/dev/null
|
||||
token="$(SOPS_AGE_KEY_FILE="$age_file" sops decrypt --extract '["tokens"]["glm-coding-plan"]' "$sops_file")"
|
||||
test -n "$token"
|
||||
|
||||
umask 077
|
||||
auth_config="$smoke_dir/curl-auth.conf"
|
||||
printf 'header = "Authorization: Bearer %s"\nheader = "Content-Type: application/json"\n' "$token" >"$auth_config"
|
||||
unset token
|
||||
|
||||
curl --fail --silent --show-error --config "$auth_config" \
|
||||
--output "$smoke_dir/models.json" "$base_url/models"
|
||||
jq -e '(.data | type == "array") and any(.data[]; (.id | ascii_downcase) == "glm-5.1")' \
|
||||
"$smoke_dir/models.json" >/dev/null
|
||||
|
||||
jq -n '{
|
||||
model: "glm-5.1",
|
||||
messages: [{role: "user", content: "Reply exactly GLM_CODING_PLAN_OK"}],
|
||||
stream: false,
|
||||
max_tokens: 32
|
||||
}' >"$smoke_dir/chat.json"
|
||||
curl --fail --silent --show-error --config "$auth_config" \
|
||||
--request POST --data-binary "@$smoke_dir/chat.json" \
|
||||
--output "$smoke_dir/chat-response.json" "$base_url/chat/completions"
|
||||
jq -e '(.choices[0].message.content | type == "string" and length > 0) and (.usage.total_tokens | type == "number")' \
|
||||
"$smoke_dir/chat-response.json" >/dev/null
|
||||
|
||||
jq -n '{
|
||||
model: "glm-5.1",
|
||||
messages: [{role: "user", content: "Reply exactly GLM_CODING_PLAN_STREAM_OK"}],
|
||||
stream: true,
|
||||
max_tokens: 32
|
||||
}' >"$smoke_dir/stream.json"
|
||||
curl --fail --silent --show-error --no-buffer --config "$auth_config" \
|
||||
--request POST --data-binary "@$smoke_dir/stream.json" \
|
||||
--output "$smoke_dir/stream-response.txt" "$base_url/chat/completions"
|
||||
grep -Eq '^data: .+' "$smoke_dir/stream-response.txt"
|
||||
grep -Fq 'data: [DONE]' "$smoke_dir/stream-response.txt"
|
||||
|
||||
jq -n '{
|
||||
model: "glm-5.1",
|
||||
messages: [{role: "user", content: "Call emit_marker with marker GLM_CODING_PLAN_TOOL_OK. Do not answer directly."}],
|
||||
tools: [{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "emit_marker",
|
||||
description: "Emit the requested verification marker",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {marker: {type: "string"}},
|
||||
required: ["marker"]
|
||||
}
|
||||
}
|
||||
}],
|
||||
tool_choice: "auto",
|
||||
stream: false,
|
||||
max_tokens: 64
|
||||
}' >"$smoke_dir/tool.json"
|
||||
curl --fail --silent --show-error --config "$auth_config" \
|
||||
--request POST --data-binary "@$smoke_dir/tool.json" \
|
||||
--output "$smoke_dir/tool-response.json" "$base_url/chat/completions"
|
||||
jq -e 'any(.choices[0].message.tool_calls[]?; .function.name == "emit_marker")' \
|
||||
"$smoke_dir/tool-response.json" >/dev/null
|
||||
|
||||
printf 'glm coding smoke: PASS\n'
|
||||
BASH
|
||||
```
|
||||
|
||||
Expected: `/models`, non-stream Chat, SSE `[DONE]`, and one tool call pass at `https://api.z.ai/api/coding/paas/v4` only; the command prints `glm coding smoke: PASS` and removes all temporary files.
|
||||
|
||||
5. Inspect scope and secret safety:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
git diff --name-only -- packages/go/config/protocol_profile.go packages/go/config/protocol_profile_test.go apps/control-plane/internal/credentialstore/route.go apps/control-plane/internal/credentialstore/route_test.go apps/node/internal/adapters/openai_compat/protocol_profile_test.go configs/edge.yaml agent-spec/runtime/provider-pool-config-refresh.md
|
||||
rg --sort path -n 'glm_coding|glm-5\.1-api|glm-5\.1-coding' packages/go/config/protocol_profile.go packages/go/config/protocol_profile_test.go apps/control-plane/internal/credentialstore/route.go apps/control-plane/internal/credentialstore/route_test.go apps/node/internal/adapters/openai_compat/protocol_profile_test.go configs/edge.yaml agent-spec/runtime/provider-pool-config-refresh.md agent-test/local/edge-smoke.md
|
||||
```
|
||||
|
||||
Expected: only the claimed implementation files plus active review evidence are modified; GLM split identifiers are present; no token/API key value appears in the diff or command output.
|
||||
|
||||
After completing all code changes, fill implementation-owned sections in `CODE_REVIEW-*-G??.md`.
|
||||
Loading…
Reference in a new issue