@kylecheng3146/agent-ops 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -40,7 +40,7 @@ export async function checkTaskCompletionEvidence(stored, options) {
40
40
  }
41
41
  const attestation = await findReviewAttestation(root, sourceFingerprint);
42
42
  if (attestation === null || attestation.taskId !== stored.task.id) {
43
- return { status: "FAIL", code: "REVIEW_REQUIRED", remedy: "Run agent-ops review --yes for the whole task and current source." };
43
+ return { status: "FAIL", code: "REVIEW_REQUIRED", remedy: `Run agent-ops review --task ${stored.task.id} --yes for the whole task and current source.` };
44
44
  }
45
45
  return null;
46
46
  }
@@ -124,7 +124,8 @@ export class TaskService {
124
124
  completedAt: null,
125
125
  archivedAt: null,
126
126
  failureFingerprint: null,
127
- policyConfigHash: input.policyConfigHash ?? null
127
+ policyConfigHash: input.policyConfigHash ?? null,
128
+ completionBase: null
128
129
  };
129
130
  state.tasks.push(record);
130
131
  if (input.sessionId !== undefined) {
@@ -235,10 +236,14 @@ export class TaskService {
235
236
  }
236
237
  }
237
238
  }
239
+ let completionBase = null;
238
240
  const fingerprint = async () => {
239
241
  try {
240
242
  const scope = await resolveReviewScope({ root: completion.root, runner: completion.gitRunner,
241
243
  ...(completion.base === undefined ? {} : { base: completion.base }) });
244
+ // Recorded so the completion gate can recompute this exact range once
245
+ // the work is committed and the worktree has nothing left to measure.
246
+ completionBase = scope.mode === "base" ? scope.resolvedBase : null;
242
247
  return await calculateSourceFingerprint(completion.root, scope, completion.gitRunner);
243
248
  }
244
249
  catch (error) {
@@ -282,7 +287,8 @@ export class TaskService {
282
287
  status: "complete",
283
288
  evidence,
284
289
  updatedAt: now,
285
- completedAt: now
290
+ completedAt: now,
291
+ completionBase
286
292
  };
287
293
  replaceTask(state, completed);
288
294
  return cloneRecord(completed);
@@ -64,12 +64,13 @@ function parseTaskRecord(value) {
64
64
  "task",
65
65
  "updatedAt"
66
66
  ];
67
- const allowedKeys = new Set([
68
- baseKeys.join("\0"),
69
- [...baseKeys, "failureFingerprint"].sort().join("\0"),
70
- [...baseKeys, "policyConfigHash"].sort().join("\0"),
71
- [...baseKeys, "failureFingerprint", "policyConfigHash"].sort().join("\0")
72
- ]);
67
+ // Every record written before an optional field existed must still parse, so
68
+ // the accepted shapes are every combination of them.
69
+ const optionalKeys = ["completionBase", "failureFingerprint", "policyConfigHash"];
70
+ const allowedKeys = new Set(Array.from({ length: 1 << optionalKeys.length }, (_unused, mask) => [
71
+ ...baseKeys,
72
+ ...optionalKeys.filter((_key, position) => (mask & (1 << position)) !== 0)
73
+ ].sort().join("\0")));
73
74
  if (!isRecord(value) || !allowedKeys.has(Object.keys(value).sort().join("\0"))) {
74
75
  return invalidState("Task state contains an invalid task record.");
75
76
  }
@@ -138,6 +139,13 @@ function parseTaskRecord(value) {
138
139
  (typeof policyConfigHash !== "string" || !/^[a-f0-9]{64}$/u.test(policyConfigHash))) {
139
140
  return invalidState("Task state contains an invalid policy config hash.");
140
141
  }
142
+ const completionBase = value.completionBase === undefined
143
+ ? null
144
+ : value.completionBase;
145
+ if (completionBase !== null &&
146
+ (typeof completionBase !== "string" || !/^[a-f0-9]{40,64}$/u.test(completionBase))) {
147
+ return invalidState("Task state contains an invalid completion base.");
148
+ }
141
149
  return {
142
150
  task: task.value,
143
151
  status,
@@ -147,7 +155,8 @@ function parseTaskRecord(value) {
147
155
  completedAt: value.completedAt,
148
156
  archivedAt: value.archivedAt,
149
157
  failureFingerprint,
150
- policyConfigHash
158
+ policyConfigHash,
159
+ completionBase
151
160
  };
152
161
  }
153
162
  function parseSession(value) {
@@ -49,17 +49,19 @@ Enable it during `agent-ops init`, or by hand:
49
49
  }
50
50
  ```
51
51
 
52
- `targets` is an **ordered fallback chain**. Every review locks to the
53
- staged/unstaged/untracked surface (or a clean `--base <ref>...HEAD` range).
54
- A bare review uses the built-in `change-quality` criterion; `--task` uses the
55
- task criteria and requires fresh PASS evidence for required checks. The full
56
- report is printed, and PASS persists only a source-fingerprint attestation.
52
+ `targets` is an ordered selection for the complete command
53
+ `agent-ops review --task <id> --yes`. Every task criterion and its original
54
+ description is reviewed against the staged/unstaged/untracked surface (or a
55
+ clean `--base <ref>...HEAD` range). Current PASS evidence for required checks
56
+ is required before a reviewer starts. Bare and partial reviews are not a
57
+ complete task review and do not satisfy completion.
57
58
 
58
59
  `--review-target` belongs to `init` and configures that persistent chain.
59
- For one run, `review --harness <target>` narrows the chain to exactly one
60
- already-configured target; it never enables a target absent from project
61
- policy. The configured model, effort, and timeout still apply. Review JSON
62
- includes `plannedTargets` in the actual host-adjusted order.
60
+ `review` does not accept target, criterion, or evidence overrides. Exactly two
61
+ fresh sessions are planned: three configured targets require
62
+ `AGENT_OPS_HOST=agy|claude|codex`, exclude that host, and prefer `agy` first;
63
+ two targets run in configured order; one target runs twice. Review JSON includes
64
+ `plannedTargets` in that actual order.
63
65
 
64
66
  Every attempt starts from a fresh session and disposable repository clone with
65
67
  native read-only mode.
@@ -74,9 +76,11 @@ repository even if sandboxed plan mode writes to its cwd:
74
76
  | `agy` | `agy --print <prompt>` | `--sandbox --mode plan` |
75
77
  | `claude` | `claude -p` | `--permission-mode plan --safe-mode` |
76
78
 
77
- The chain prefers a target different from the hosting CLI. If no other target
78
- is usable, a fresh same-target session is allowed but is reported as
79
- `DEGRADED: isolated self-review`; a development session is never resumed.
79
+ The first session is the necessary review. A first PASS starts the second
80
+ adversarial session with the first full redacted report as untrusted data. A
81
+ first FAIL or NOT_RUN stops; a second FAIL is final FAIL, and a missing second
82
+ verdict is NOT_RUN. A same-target pair is still independent because both
83
+ sessions and clones are fresh; a development session is never resumed.
80
84
 
81
85
  `opencode` is **not** a review target even though it is a supported harness.
82
86
  Its `--agent plan` is rejected as a subagent and silently falls back to a
@@ -88,11 +92,11 @@ would consume the following flag instead. It deliberately does not pass
88
92
  `--dangerously-skip-permissions`, which overrides the permission boundary, or
89
93
  `--disable-slash-commands`, which disables plan-mode behavior.
90
94
 
91
- The chain advances whenever an attempt produces no valid verdict — including a
92
- missing executable, spawn failure, timeout (900s per target by default), login
93
- failure, oversized output, or unparseable output. Every attempt and reason is
94
- preserved in human and JSON output. A `PASS` or `FAIL` verdict is **terminal**,
95
- so the chain cannot shop for a passing review.
95
+ Host network or loopback restrictions return `REVIEW_NOT_RUN` before any
96
+ reviewer is started. A trusted outer host runner may rerun the exact same
97
+ command once with both capabilities; an in-sandbox retry is not an elevation
98
+ and cannot produce a PASS. Every attempt, preflight result, and diagnostic is
99
+ preserved in human and JSON output.
96
100
 
97
101
  Capability checks and model starts are reported on stderr, including under
98
102
  `--json`; stdout remains one final JSON envelope and raw reviewer output is
@@ -100,16 +104,14 @@ never streamed. SIGINT or SIGTERM terminates the active reviewer process tree,
100
104
  does not advance the fallback chain, and never writes an attestation. An
101
105
  exhausted timeout chain reports `timeout`, not `missing-cli`.
102
106
 
103
- If Claude Code is the host (`CLAUDECODE` is set), `claude` is moved to the end
104
- of the chain. It still runs when it is the only configured target, with a
105
- `reviewer == host` warning.
106
-
107
- Criterion descriptions come from the task bound to the current session, so a
108
- review needs an attached task created under the current policy configuration;
109
- `--criterion` filters those ids. Run `agent-ops verify` first: review rejects
110
- stale, failed, or source-mismatched required evidence before any model call.
111
- Compact PASS evidence is appended with a `review:<target>:` prefix; the full
112
- human-readable report is transient. Completed tasks are never rewritten.
107
+ The task ID is explicit and its criteria are always used; no session fallback
108
+ or criterion filter can replace the original task requirements. Review rechecks
109
+ the source fingerprint before the adversarial session and again before writing
110
+ evidence. After a complete PASS, the full redacted reports are stored privately
111
+ under `.agent-ops/reviews/`; the attestation stores only metadata and report
112
+ digests. Any evidence write failure remains `REVIEW_NOT_RUN`. Compact PASS
113
+ evidence is appended with a `review:<target>:` prefix, and completed tasks are
114
+ never rewritten.
113
115
 
114
116
  `--yes` is still required for every review run: init selection decides which
115
117
  targets are permitted, `--yes` decides whether to spend money now.
@@ -22,20 +22,19 @@ A review result MUST preserve PASS, FAIL, or NOT_RUN and MUST NOT convert NOT_RU
22
22
 
23
23
  ## REVIEW-HARNESS-001
24
24
 
25
- A review invocation MUST resolve to exactly one concrete review target, even
26
- when an installation supports multiple harnesses.
25
+ A complete review MUST use the configured independent-review target selection;
26
+ the review command MUST NOT accept a per-run harness override.
27
27
 
28
- - Trigger: Running `review` with a harness selection.
29
- - Action: Select one of `codex`, `agy`, or `claude`; keep multi-harness installation separate from review execution.
30
- - Evidence: Argument parsing rejects `all`, `both`, and comma-separated multi-harness values for review.
31
- - Positive: `review --harness claude` resolves one target.
32
- - Negative: `Run one review invocation against every installed harness implicitly.`
28
+ - Trigger: Running `review`.
29
+ - Action: Resolve the configured targets and plan exactly two fresh sessions.
30
+ - Evidence: Argument parsing rejects `--harness`, while `init --review-target` remains the configuration entry point.
31
+ - Positive: `review --task <id> --yes` reports its configured `plannedTargets`.
32
+ - Negative: `review --task <id> --yes --harness claude` changes the reviewer set.
33
33
 
34
- The explicit target MUST already exist in the configured independent-review
35
- role. It narrows the configured chain to one target while preserving model,
36
- effort, and timeout policy. Without `--harness`, host-aware ordering applies to
37
- the complete configured chain. Every result carries that order as
38
- `plannedTargets`.
34
+ With three configured targets, `AGENT_OPS_HOST` MUST identify the current host;
35
+ that target is excluded and `agy` is primary when available. With two targets,
36
+ both run in configured order even if one is the host. With one target, it runs
37
+ twice in fresh sessions.
39
38
 
40
39
  ## REVIEW-READONLY-001
41
40
 
@@ -49,10 +48,9 @@ without one MUST be skipped rather than run unsandboxed.
49
48
  - Negative: `Trust the prompt to stop the reviewer from editing files.`
50
49
 
51
50
  Every attempted review MUST run in a fresh session (`sessionIsolation:
52
- "fresh"`) and in a disposable repository clone. The reviewer chain prefers a
53
- different CLI from the hosting CLI; when no other usable target exists,
54
- same-target fresh review is allowed but MUST render as `DEGRADED: isolated
55
- self-review`. A resumed development session is never an independent review.
51
+ "fresh"`) and in a disposable repository clone. A same-target pair remains
52
+ independent because the sessions and clones are fresh. A resumed development
53
+ session is never an independent review.
56
54
 
57
55
  Capability and model-start progress goes to stderr even when stdout is JSON.
58
56
  Raw reviewer output remains bounded and unstreamed. SIGINT or SIGTERM aborts
@@ -61,22 +59,22 @@ distinct NOT_RUN reason rather than being flattened to `missing-cli`.
61
59
 
62
60
  ## REVIEW-CHAIN-001
63
61
 
64
- Configured targets form an ordered fallback chain that MUST advance only when
65
- no review happened, and MUST NOT advance past a verdict.
62
+ The two planned sessions MUST be ordered as a necessary review followed by an
63
+ adversarial re-check. The first session MUST stop the run on FAIL or NOT_RUN;
64
+ there is no fallback after that result.
66
65
 
67
- - Trigger: A configured target is missing, fails to spawn, or times out.
68
- - Action: Try the next target after unparseable output; stop and report the first PASS or FAIL.
69
- - Evidence: The number of spawned attempts matches the failures that preceded the verdict.
70
- - Positive: `codex FAIL is final; no other target is asked for a second opinion.`
71
- - Negative: `Retry other targets after a FAIL until one reports PASS.`
66
+ - Trigger: The first target is missing, unauthenticated, unavailable, or returns a malformed report.
67
+ - Action: Return NOT_RUN with the first attempt and do not start the second session.
68
+ - Evidence: `attempts` contains the first diagnostic and has no second attempt.
69
+ - Positive: `primary NOT_RUN` is final NOT_RUN.
70
+ - Negative: `Run the second target after the necessary review did not run.`
72
71
 
73
72
  ## REVIEW-ADVERSARIAL-001
74
73
 
75
- A PASS MUST be offered to a different eligible target for refutation, and a
74
+ A PASS MUST be offered to the second planned target for refutation, and a
76
75
  successful refutation MUST make the run FAIL.
77
76
 
78
- - Trigger: The primary target returns PASS and another eligible target has not
79
- already been walked past. The host target never serves as the challenger.
77
+ - Trigger: The primary target returns PASS.
80
78
  - Action: Send that target the prior report as untrusted data and ask it to
81
79
  refute the verdict; report FAIL when it does, and record the challenge as
82
80
  `adversarial` either way.
@@ -84,8 +82,37 @@ successful refutation MUST make the run FAIL.
84
82
  a challenger that produced no report appears on the attempt list instead.
85
83
  - Positive: `codex passed, claude found a blocking defect, the run failed.`
86
84
  - Negative: `Manufacture a refutation so the challenge looks effective.`
87
- - Note: With one usable target the primary verdict stands unchallenged. A FAIL
88
- is already terminal and is never re-checked.
85
+ - Note: With one configured target the same target is invoked again in a fresh
86
+ session. A FAIL is already terminal and is never re-checked.
87
+
88
+ ## REVIEW-HOST-001
89
+
90
+ A host that cannot provide network and loopback capability MUST fail closed
91
+ before any reviewer invocation.
92
+
93
+ - Trigger: The host declares disabled network or the loopback probe fails.
94
+ - Action: Return `REVIEW_NOT_RUN / host-required` with the host restriction.
95
+ - Evidence: `attempts` is empty and no target preflight or reviewer process ran.
96
+ - Positive: A trusted outer host runner can retry the exact command once with both capabilities.
97
+ - Negative: Treat a child login error as proof that an authenticated reviewer is unavailable.
98
+
99
+ There is no repository-level permission bypass. An unavailable outer host runner
100
+ MUST leave the result NOT_RUN rather than manufacture PASS.
101
+
102
+ ## REVIEW-EVIDENCE-001
103
+
104
+ A complete PASS MUST include the primary report, the adversarial report, two PASS
105
+ attempts with fresh session IDs, a stable source fingerprint, and a private
106
+ bounded report artifact before its attestation is written. Artifact or
107
+ attestation write failure MUST return NOT_RUN.
108
+
109
+ - Trigger: A reviewer chain reaches a PASS verdict.
110
+ - Action: Validate both reports, both fresh attempts, the source fingerprint,
111
+ and the private bounded artifact before writing the attestation.
112
+ - Evidence: The attestation and artifact agree on task, host, targets, session
113
+ IDs, report digests, and source fingerprint.
114
+ - Positive: A complete pair writes the artifact and matching PASS attestation.
115
+ - Negative: A partial PASS or failed evidence write becomes NOT_RUN.
89
116
 
90
117
  ## REVIEW-CONTRACT-001
91
118
 
@@ -44,15 +44,17 @@ Claude 與 Codex lifecycle support 為 `supported`,OpenCode 從 app initializa
44
44
  }
45
45
  ```
46
46
 
47
- `targets` 是**有序的後備鏈**。每次 review 都鎖定 staged/unstaged/untracked
48
- 變更(或乾淨的 `--base <ref>...HEAD`)。裸跑使用內建 `change-quality` 準則;
49
- `--task` 使用 task criteria,並要求必要驗證的最新 PASS evidence。完整 report
50
- 會顯示給人看,PASS 後只持久化 source-fingerprint attestation。
51
-
52
- `--review-target` 只屬於 `init`,用來設定持久 fallback chain。單次執行可用
53
- `review --harness <target>`,將 chain 縮窄為一個已在 project policy 啟用的
54
- target;它不會臨時啟用未設定的 reviewer。既有 model、effort 與 timeout 仍會
55
- 沿用,review JSON 的 `plannedTargets` 會列出經 host 調整後的實際順序。
47
+ `targets` 是完整指令 `agent-ops review --task <id> --yes` 的有序選擇。
48
+ 每一條 task criterion 與原始 description 都會針對 staged/unstaged/untracked
49
+ 變更(或乾淨的 `--base <ref>...HEAD`)審查;必要驗證的最新 PASS evidence
50
+ 必須在 reviewer 啟動前存在。裸跑或 partial review 不算完整 task review,也
51
+ 不能滿足 completion。
52
+
53
+ `--review-target` 只屬於 `init`,用來設定持久 reviewer selection。`review` 不接受
54
+ target、criterion 或 evidence override。每次恰好規劃兩個全新 session:設定三個
55
+ target 時必須提供 `AGENT_OPS_HOST=agy|claude|codex`,排除該 host 並優先選 agy;
56
+ 設定兩個時依設定順序執行;設定一個時同一 CLI 執行兩次。review JSON 的
57
+ `plannedTargets` 會列出實際順序。
56
58
 
57
59
  每次嘗試都從全新 session、一次性 repository clone 與原生唯讀模式啟動。Claude 使用完整 safe-mode
58
60
  隔離;Codex 與 Agy 為了支援既有 OAuth 登入而保留登入環境,因此 context
@@ -65,9 +67,11 @@ target;它不會臨時啟用未設定的 reviewer。既有 model、effort 與
65
67
  | `agy` | `agy --print <prompt>` | `--sandbox --mode plan` |
66
68
  | `claude` | `claude -p` | `--permission-mode plan --safe-mode` |
67
69
 
68
- review chain 優先選擇不同於 hosting CLI 的 target。沒有其他可用 target 時,才允許
69
- 全新的同 CLI session,但輸出會標示 `DEGRADED: isolated self-review`;不得 resume
70
- 開發 session。
70
+ 第一個 session 是必要 reviewer。只有第一個 PASS 才會啟動第二個 adversarial
71
+ session,並將第一份完整 redacted report 以不可信資料傳入。第一個 FAIL 或
72
+ NOT_RUN 立即停止;第二個 FAIL 是終局 FAIL,第二個無法取得 verdict 則為
73
+ NOT_RUN。同 target pair 仍因 session 與 clone 全新而具獨立性;不得 resume 開發
74
+ session。
71
75
 
72
76
  `opencode` **不是** review 目標,即使它是支援的 harness。它的 `--agent plan`
73
77
  會被判定為 subagent 而遭拒,並靜默退回可寫入的 agent,因此無法滿足唯讀前置
@@ -77,24 +81,22 @@ Agy 的 prompt 會直接作為 `--print` 的值;裸用 `-p` 會誤吞下一個
77
81
  agent-ops 刻意不傳會繞過權限邊界的 `--dangerously-skip-permissions`,也不傳會
78
82
  使 plan mode 失效的 `--disable-slash-commands`。
79
83
 
80
- 只要沒有取得有效 verdict 就換下一家,包括執行檔不存在、spawn 失敗、逾時
81
- (每個目標預設 900 秒)、登入失敗、輸出過大或無法解析。文字與 JSON 輸出
82
- 都會保留每次 attempt 及原因。`PASS` 或 `FAIL` 判定是**終局**,因此不會產生
83
- 自動化的 review shopping。
84
+ Host 缺少 network 或 loopback 權限時,會在啟動任何 reviewer 前回傳
85
+ `REVIEW_NOT_RUN`。可信任的外部 host runner 可以用兩項權限重跑同一個完整
86
+ 指令一次;在原 sandbox 內重試不算升權,也不能產生 PASS。文字與 JSON 輸出
87
+ 都會保留每次 attempt、preflight 與 diagnostic。
84
88
 
85
89
  Capability check 與模型啟動進度都寫到 stderr,包括 `--json` 模式;stdout
86
90
  仍只有最終 JSON envelope,且不會串流 reviewer 原始輸出。SIGINT 或 SIGTERM
87
91
  會終止目前 reviewer 的完整 process tree、不進入 fallback,也不寫入
88
92
  attestation。整條 chain 逾時時回報 `timeout`,不會誤報 `missing-cli`。
89
93
 
90
- 若 host 是 Claude Code(`CLAUDECODE` 已設定),`claude` 會被移到鏈尾。
91
- 當它是唯一設定的目標時仍會執行,並附上 `reviewer == host` 警告。
92
-
93
- criterion 描述來自當前 session 綁定的 task,所以 review 需要已附加、且建立時的
94
- policy 設定仍相同的 task;`--criterion` 用來篩選 id。請先執行
95
- `agent-ops verify`:必要 evidence 若失敗、過期或來源不符,review 會在 model 呼叫前停止。
96
- 僅 compact PASS evidence 會以 `review:<target>:` 附加;完整人類可讀 report 是暫存的。
97
- 已完成的 task 絕不改寫。
94
+ task ID 必須明確指定,且永遠使用該 task 的原始 criteria;不能用 session fallback
95
+ 或 criterion filter 取代需求。review 會在 adversarial session 前、以及寫入證據前
96
+ 重新確認 source fingerprint。完整 PASS 後,兩份完整 redacted report 會私密保存於
97
+ `.agent-ops/reviews/`;attestation 只保存 metadata 與 report digest。任何 evidence
98
+ 寫入失敗都維持 `REVIEW_NOT_RUN`。Compact PASS evidence 仍以
99
+ `review:<target>:` 附加,已完成的 task 絕不改寫。
98
100
 
99
101
  每次執行 review 仍需 `--yes`:init 的勾選決定「允許哪些目標」,
100
102
  `--yes` 決定「現在是否要花錢」。
@@ -24,18 +24,18 @@ English source version: 2026-07-23. Revalidate: when the English specification c
24
24
 
25
25
  ## REVIEW-HARNESS-001
26
26
 
27
- 即使 installation 支援多個 harness,一次 review invocation MUST 解析成恰好一個 concrete review target。
27
+ 完整 review MUST 使用 configured independent-review target selection;review
28
+ 指令 MUST NOT 接受單次 harness override。
28
29
 
29
- - Trigger: 使用 harness selection 執行 `review`。
30
- - Action: 從 `codex`、`agy` 或 `claude` 中選一個;multi-harness installation 與 review execution 分開處理。
31
- - Evidence: argument parsing 會拒絕 review 使用 `all`、`both` 或逗號分隔的多 harness 值。
32
- - Positive: `review --harness claude` 解析成一個 target。
33
- - Negative: `讓一次 review invocation 隱式跑過所有已安裝 harness。`
30
+ - Trigger: 執行 `review`。
31
+ - Action: 解析設定的 targets,規劃恰好兩個全新 session。
32
+ - Evidence: argument parsing 拒絕 `--harness`;`init --review-target` 仍是設定入口。
33
+ - Positive: `review --task <id> --yes` 回報設定的 `plannedTargets`。
34
+ - Negative: `review --task <id> --yes --harness claude` 改變 reviewer 集合。
34
35
 
35
- 明確指定的 target MUST 已存在於 configured independent-review role。它只會將
36
- configured chain 縮窄為單一 target,並保留 model、effort 與 timeout policy。
37
- 未提供 `--harness` 時,完整 configured chain 仍套用 host-aware ordering;每個
38
- 結果都以 `plannedTargets` 保存這個實際順序。
36
+ 設定三個 target 時,`AGENT_OPS_HOST` MUST 指出目前 host;該 target 會被排除,
37
+ 若可用則 agy 為 primary。設定兩個時依順序執行,即使其中一個是 host;設定一個
38
+ 時,在全新 session 執行兩次。
39
39
 
40
40
  ## REVIEW-READONLY-001
41
41
 
@@ -48,9 +48,8 @@ review target MUST 以其自身的唯讀機制啟動;沒有唯讀機制的 tar
48
48
  - Negative: `信任 prompt 能阻止審查者修改檔案。`
49
49
 
50
50
  每次 review 都 MUST 使用全新 session(`sessionIsolation: "fresh"`)並在
51
- disposable repository clone 中執行。review chain 優先選擇不同於 hosting CLI 的
52
- target;沒有其他可用 target 時,才允許同 CLI 的 fresh review,但輸出 MUST 明確
53
- 標示 `DEGRADED: isolated self-review`。不得 resume 開發 session 作為獨立審查。
51
+ disposable repository clone 中執行。同 target pair 仍因 session 與 clone 全新而
52
+ 具獨立性。不得 resume 開發 session 作為獨立審查。
54
53
 
55
54
  Capability 與模型啟動進度即使在 JSON 模式也寫到 stderr;reviewer 原始輸出仍
56
55
  維持 bounded capture,不直接串流。SIGINT 或 SIGTERM 會中止 active process
@@ -59,24 +58,53 @@ tree,不 fallback、不寫 attestation;timeout 保留獨立 NOT_RUN reason
59
58
 
60
59
  ## REVIEW-CHAIN-001
61
60
 
62
- 已設定的 targets 組成有序後備鏈,MUST 僅在「沒有審到」時換下一家,且 MUST NOT 在取得判定後繼續往下試。
61
+ 兩個規劃的 session MUST 依序為必要 reviewer 與 adversarial 複審。第一個
62
+ session 遇到 FAIL 或 NOT_RUN MUST 立即停止;不會在該結果後 fallback。
63
63
 
64
- - Trigger: 某個已設定的 target 不存在、spawn 失敗或逾時。
65
- - Action: 無法解析輸出時試下一個 target;遇到第一個 PASS 或 FAIL 即停止並回報。
66
- - Evidence: spawn 次數等於判定之前的失敗次數。
67
- - Positive: `codex 的 FAIL 是終局;不會再問任何 target 第二意見。`
68
- - Negative: `FAIL 之後改試其他 target,直到有人回報 PASS。`
64
+ - Trigger: 第一個 target 不存在、未登入、不可用或回傳 malformed report。
65
+ - Action: 回傳含第一個 attempt 診斷的 NOT_RUN,不啟動第二個 session。
66
+ - Evidence: `attempts` 有第一個 diagnostic 且沒有第二個 attempt。
67
+ - Positive: `primary NOT_RUN` 是終局 NOT_RUN。
68
+ - Negative: `必要 reviewer 沒跑成後仍啟動第二個 target`。
69
69
 
70
70
  ## REVIEW-ADVERSARIAL-001
71
71
 
72
- PASS MUST 交給另一個合格 target 嘗試反駁,且反駁成立時 MUST 使整體判定為 FAIL。
72
+ PASS MUST 交給第二個規劃的 target 嘗試反駁,且反駁成立時 MUST 使整體判定為 FAIL。
73
73
 
74
- - Trigger: primary target 回報 PASS,且尚有未走過的合格 target。host target 永不擔任挑戰者。
74
+ - Trigger: primary target 回報 PASS。
75
75
  - Action: 將前一份 report 以不可信資料交給該 target 並要求它反駁;反駁成立即回報 FAIL,且無論結果都記錄為 `adversarial`。
76
76
  - Evidence: `adversarial` 記載挑戰者與是否反駁成立;未能產出 report 的挑戰者則記錄在 attempt 清單。
77
77
  - Positive: `codex 判 PASS,claude 找到 blocking 缺陷,整體判定 FAIL。`
78
78
  - Negative: `為了讓複查看起來有效而編造反駁。`
79
- - Note: 只有一個可用 target 時,primary 判定不受挑戰即成立。FAIL 已是終局,不再複查。
79
+ - Note: 只有一個設定 target 時,會在全新 session 再呼叫同一 target。FAIL 已是終局,不再複查。
80
+
81
+ ## REVIEW-HOST-001
82
+
83
+ Host 缺少 network 或 loopback 能力時,MUST 在任何 reviewer invocation 前
84
+ fail closed。
85
+
86
+ - Trigger: host 宣告 network disabled 或 loopback probe 失敗。
87
+ - Action: 回傳 `REVIEW_NOT_RUN / host-required` 與 host restriction。
88
+ - Evidence: `attempts` 為空,沒有 target preflight 或 reviewer process 執行。
89
+ - Positive: 可信任的外部 host runner 可在具兩項能力的主機重跑同一指令一次。
90
+ - Negative: 把 child login error 當作 authenticated reviewer 不可用的證明。
91
+
92
+ repository 不提供 permission bypass。外部 host runner 不可用時,結果 MUST 保持
93
+ NOT_RUN,不得製造 PASS。
94
+
95
+ ## REVIEW-EVIDENCE-001
96
+
97
+ 完整 PASS MUST 在寫入 attestation 前具備 primary report、adversarial report、
98
+ 兩個帶 fresh session ID 的 PASS attempt、穩定 source fingerprint,以及私密且
99
+ 有大小上限的 report artifact。artifact 或 attestation 寫入失敗 MUST 回傳 NOT_RUN。
100
+
101
+ - Trigger: reviewer chain 得到 PASS verdict。
102
+ - Action: 寫入 attestation 前驗證兩份 report、兩個 fresh attempt、source
103
+ fingerprint 與私密且有大小上限的 artifact。
104
+ - Evidence: attestation 與 artifact 的 task、host、targets、session IDs、report
105
+ digests 及 source fingerprint 完全一致。
106
+ - Positive: 完整 pair 寫入 artifact 與相符的 PASS attestation。
107
+ - Negative: 不完整 PASS 或 evidence 寫入失敗會降為 NOT_RUN。
80
108
 
81
109
  ## REVIEW-CONTRACT-001
82
110
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kylecheng3146/agent-ops",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Evidence-driven development loops for agy, Codex, Claude Code, and opencode",
5
5
  "type": "module",
6
6
  "license": "MIT",