diff --git a/docs/architecture/rfcs/ledger/typescript-control-plane-migration-v0/2026-11-02-progress-review-qualification.md b/docs/architecture/rfcs/ledger/typescript-control-plane-migration-v0/2026-11-02-progress-review-qualification.md new file mode 100644 index 0000000000..927a8bb91c --- /dev/null +++ b/docs/architecture/rfcs/ledger/typescript-control-plane-migration-v0/2026-11-02-progress-review-qualification.md @@ -0,0 +1,95 @@ +# Progress-review receipt qualification (2026-11-02) + +## Owner boundary + +This checkpoint moves the complete loaded-receipt qualification decision from +`capabilities/progress_review/context.py` to +`control_plane/work_items/progress_review_evidence.ts`, exposed through one +`progress_review.context.qualify` request. The existing built-in progress-review +capability retains its provider boundary; this adds no capability or provider. +The TS owner composes `inspectLocalGoalAcceptance` and +`progressReviewCriterionBasis` in process, caches each Todo inspection for the +batch, compares canonical bindings, withdraws obsolete signals, selects the +newest revision from the first qualified receipt, and applies the manual pin. +The Python loader's `progress_review_receipt_order_key` is the single ordering +owner: its stable newest-first output also preserves first-maximal tie behavior. +TS preserves that input order rather than sorting again. +These are successive reads, **not an atomic snapshot**. One unavailable Todo +cannot suppress other Todos. Exact Goal-instance refusal remains with the +acceptance owner. Operator studies and legacy receipts retain their semantics. + +## Economics and retained Python + +Instrumentation on both File and SQLite measures context-read crossings: + +| Scenario | Before | After | +| --- | ---: | ---: | +| Off / no receipts / unavailable receipt store | 0 | 0 | +| Repeated canonical receipts for one Todo | 5 | 3 | +| Canonical receipts for two Todos | 6 | 3 | +| Removed criterion / newer receipt outside pin | 3 | 2 | +| Unavailable Todo plus current Todo | 5 | 3 | +| Wrong Goal instance | 2 | 2 | +| Operator study plus legacy receipt | 1 | 2 | + +The general qualification boundary changes E + T + R to E + 1; E still +counts per-receipt evidence-scope decoding during receipt IO. Off and empty +paths do not call the batch. Manual/legacy-only batches and refused-instance +batches do not necessarily save crossings. No latency or long-horizon benefit +is claimed. The manual/legacy case rises from 1 to 2 because evidence-scope +IO still costs one call and every nonempty batch adds qualification. With no +`goal_acceptance` binding and no pin, no TS qualification call is semantically +needed; this slice reports that waste rather than duplicating the TS rule in +Python to bypass it. Product code changes by +111/−53 physical lines, net +58. +The Python context adapter adds 32 lines of transport/codec composition while +removing 50; handler registration adds one bridge line. The bounded net +increase buys one complete decision owner rather than another leaf facade. + +Python deletes the Todo cache, acceptance inspection orchestration, criterion +comparison, stale rewriting, newest selection and pin filtering. It retains +receipt file IO and persisted-format/signal codecs, policy decoding, read-error +projection and summary composition for existing status, attention, refresh and +Chat callers. Selected source indices let the transport retain historical +numeric facts (integral floats and large integers) without repeating decisions. +The response omits the unused qualified array and computes selected source +indices directly. Sequence tokens and BigInt ordering are removed; arbitrary +integer ordering remains in the Python loader's single ordering owner. The acceptance +inspection facade still serves CLI, Chat and delegation; the criterion-basis +operation still serves the optional observer's canonical-basis reads. Neither +is an unused compatibility export. + +## Qualification and exit + +Before product changes, the test setup pins a synthetic relative acceptance +argv before contract hashing. File and SQLite pass the unchanged characterization +three consecutive times; the complete per-scenario context and status rendering +fixture is then frozen at 841 lines, with no output normalizer. It covers off, +empty, repeated/multiple Todos, wrong instance, removed criterion, unavailable +inspection/store, manual/legacy observations and pin drift. Separate numeric IO +and run-order tests protect the transport and selection boundaries. + +The existing Python context adapter and this bridge operation exit when status, +attention, refresh-state and Chat consume the native receipt read/summary owner +without Python receipt IO or composition. Retire the temporary characterization +and crossing fixtures at that same verified caller cutover, preserving semantic +negative tests. This slice neither changes a public journey nor qualifies an +installed distribution, provider performance or long-horizon outcomes. + +## Review repair validation + +The fake loader now uses the real loader's ordering helper; both frozen JSON +fixtures remain byte-identical (SHA-256 checked). A TS test proves that newest +follows input order even when run metadata suggests another order. Existing +Python run-order and pin characterization remains green. + +- Ruff: 2 changed Python files pass; pytest: 77 passed, 0 failed. +- Requested Node tests: 39 passed, 0 failed; control-plane typecheck passes. +- Semantic vocabulary smoke passes (29/29 vocabularies, 56/56 owner symbols). +- CLI base/head parity: File and SQLite, JSON and Markdown (4 comparisons). + Markdown is byte-identical; JSON differs only in source read clocks/ages, + which also vary on base-to-base reads. Progress-review payloads are unchanged. +- `git diff --check` passes. No generated `uv.lock` retained. All changes remain + unstaged; no commit, push or PR update performed. + +Validation uses this checkout's installed `.venv` after verifying import +provenance; `uv run` could not acquire its default cache lock in the sandbox. diff --git a/docs/architecture/rfcs/ledger/typescript-control-plane-migration-v0/2026-11-02-progress-review-qualification.zh-CN.md b/docs/architecture/rfcs/ledger/typescript-control-plane-migration-v0/2026-11-02-progress-review-qualification.zh-CN.md new file mode 100644 index 0000000000..5de02a478c --- /dev/null +++ b/docs/architecture/rfcs/ledger/typescript-control-plane-migration-v0/2026-11-02-progress-review-qualification.zh-CN.md @@ -0,0 +1,81 @@ +# 进度评审 receipt 资格判定(2026-11-02) + +## 所有者边界 + +本检查点将完整的已加载 receipt 资格判定,从 +`capabilities/progress_review/context.py` 迁至 +`control_plane/work_items/progress_review_evidence.ts`,通过一次 +`progress_review.context.qualify` 请求暴露。现有内置 progress-review capability +保留 provider 边界;不新增 capability 或 provider。 +TS 所有者在进程内组合 `inspectLocalGoalAcceptance` 和 +`progressReviewCriterionBasis`,在批次内缓存每个 Todo 的检查,比较规范绑定,撤回 +过期信号,以首个资格判定后的 receipt 选择最新 revision,并应用人工 pin。 +Python 加载器的 `progress_review_receipt_order_key` 是唯一排序所有者:稳定的 +最新优先输出同时保留并列时首个最大值的行为。TS 保持输入顺序,不再排序。 +这些是先后发生的读取,**不是原子快照**。单个 Todo 不可用不会抑制其他 Todo。 +精确 Goal 实例的拒绝仍由 acceptance 所有者负责。操作员研究与旧 receipt 的语义不变。 + +## 成本与保留的 Python + +File 和 SQLite 上的插桩测得 context 读取跨运行时次数: + +| 场景 | 之前 | 之后 | +| --- | ---: | ---: | +| 关闭 / 无 receipt / receipt 存储不可用 | 0 | 0 | +| 同一 Todo 的重复规范 receipt | 5 | 3 | +| 两个 Todo 的规范 receipt | 6 | 3 | +| criterion 被删除 / 较新 receipt 在 pin 之外 | 3 | 2 | +| 不可用 Todo 与当前 Todo 混合 | 5 | 3 | +| Goal 实例不符 | 2 | 2 | +| 操作员研究与旧 receipt 混合 | 1 | 2 | + +通用资格判定边界由 E + T + R 变为 E + 1;E 仍包括 receipt IO 中逐条执行的 +证据 scope 解码。关闭和空路径不调用批次。纯人工/旧 receipt 批次及实例被拒绝的 +批次不一定减少跨运行时次数。不声称延迟或长程收益。人工/旧 receipt 场景从 +1 增至 2,因为证据 scope IO 仍需一次调用,每个非空批次又增加一次资格判定。 +没有 `goal_acceptance` 绑定且没有 pin 时,语义上不需要 TS 资格判定调用;本次 +报告该浪费,不在 Python 中复制 TS 规则来绕过调用。产品代码物理行数 +为 +111/−53,净增 58 行。Python context adapter 增加 32 行传输/codec 组合, +删除 50 行;handler 注册增加一行 bridge。此有界净增长换来一个完整判定所有者, +而不是再添一个叶子 facade。 + +Python 删除 Todo 缓存、acceptance 检查编排、criterion 比较、stale 改写、最新 +revision 选择及 pin 过滤。保留 receipt 文件 IO、持久化格式/信号 codec、policy +解码、读取错误投影和摘要组合,服务现有 status、attention、refresh 与 Chat 调用者。 +所选源索引使传输可保留历史数值事实(整数值浮点及大整数),而不重复判定规则。 +响应移除未使用的 qualified 数组,并直接计算所选源索引。删除 sequence token +和 BigInt 排序;任意精度整数排序仍由 Python 加载器这一唯一排序所有者负责。acceptance 检查 facade 仍服务 CLI、Chat +和 delegation;criterion-basis 操作仍服务可选 observer 的规范 basis 读取。 +二者均不是无调用者的兼容导出。 + +## 验证与退出条件 + +修改产品代码前,测试 setup 在合同哈希之前固定合成的相对 acceptance argv。 +File 和 SQLite 在未改动产品代码上连续三轮通过 characterization,随后冻结完整的 +逐场景 context 和 status 渲染 fixture,共 841 行,不使用输出 normalizer。 +覆盖关闭、空集合、重复/多个 Todo、实例错误、criterion 删除、检查/存储不可用、 +人工/旧观察与 pin 漂移。独立数值 IO 和运行排序测试保护传输与选择边界。 + +当 status、attention、refresh-state 和 Chat 不再依赖 Python receipt IO 或组合, +直接消费原生 receipt 读取/摘要所有者时,删除现有 Python context adapter 及此 +bridge 操作。在同一次经过验证的调用者切换中删除临时 characterization 与跨运行时 +次数 fixture,保留语义反例测试。本 slice 不改变公开用户旅程,也不证明已安装 +发行版、provider 性能或长程成果。 + +## 评审修复验证 + +模拟加载器现复用真实加载器的排序 helper;两个冻结 JSON fixture 的 SHA-256 +保持不变。TS 测试证明即使运行元数据暗示其他顺序,最新值仍遵循输入顺序。 +既有 Python 运行排序及 pin characterization 继续通过。 + +- Ruff:2 个变更 Python 文件通过;pytest:77 通过,0 失败。 +- 指定 Node 测试:39 通过,0 失败;control-plane typecheck 通过。 +- 语义词汇 smoke 通过(29/29 vocabulary,56/56 owner symbol)。 +- CLI base/head:File 与 SQLite 的 JSON、Markdown 共 4 项比较通过。 + Markdown 字节一致;JSON 仅源读取时钟/年龄不同,base 重复读取也会变化。 + progress-review payload 不变。 +- `git diff --check` 通过;不保留生成的 `uv.lock`。全部修改保持 unstaged, + 未 commit、push 或更新 PR。 + +验证使用已核对导入来源的本 checkout `.venv`;`uv run` 在 sandbox 中无法 +取得默认 cache lock。 diff --git a/docs/architecture/rfcs/typescript-control-plane-migration-v0.md b/docs/architecture/rfcs/typescript-control-plane-migration-v0.md index 0bb0622ea4..e15cae687c 100644 --- a/docs/architecture/rfcs/typescript-control-plane-migration-v0.md +++ b/docs/architecture/rfcs/typescript-control-plane-migration-v0.md @@ -2843,3 +2843,4 @@ with shared write/read status-phase checks. See the [2026-10-02 checkpoint](ledg - Complete global summary and gate read transactions: [2026-11-02 checkpoint](ledger/typescript-control-plane-migration-v0/2026-11-02-global-manager-reads.md). - Complete global-risk read ownership: [2026-11-02](ledger/typescript-control-plane-migration-v0/2026-11-02-global-risks-read.md). - Complete durable-completion evidence read: [2026-11-02 owner and crossings ledger](ledger/typescript-control-plane-migration-v0/2026-11-02-completion-evidence-read.md). +- Progress-review receipt qualification: [2026-11-02 checkpoint](ledger/typescript-control-plane-migration-v0/2026-11-02-progress-review-qualification.md). diff --git a/docs/architecture/rfcs/typescript-control-plane-migration-v0.zh-CN.md b/docs/architecture/rfcs/typescript-control-plane-migration-v0.zh-CN.md index 6a6e54ce0d..dffdbb1c8e 100644 --- a/docs/architecture/rfcs/typescript-control-plane-migration-v0.zh-CN.md +++ b/docs/architecture/rfcs/typescript-control-plane-migration-v0.zh-CN.md @@ -2108,3 +2108,4 @@ SQLite 中位数略升,收益不能外推为全面加速。这只资格化本 - 完整全局摘要与门禁读取事务:[2026-11-02 检查点](ledger/typescript-control-plane-migration-v0/2026-11-02-global-manager-reads.zh-CN.md)。 - 完整全局风险读取所有权:[2026-11-02](ledger/typescript-control-plane-migration-v0/2026-11-02-global-risks-read.zh-CN.md)。 - 完整 durable-completion 证据读取:[2026-11-02 owner 与调用边界记录](ledger/typescript-control-plane-migration-v0/2026-11-02-completion-evidence-read.zh-CN.md)。 +- 进度评审 receipt 资格判定:[2026-11-02 检查点](ledger/typescript-control-plane-migration-v0/2026-11-02-progress-review-qualification.zh-CN.md)。 diff --git a/loopx/capabilities/progress_review/context.py b/loopx/capabilities/progress_review/context.py index 66ba9f909c..a34792d726 100644 --- a/loopx/capabilities/progress_review/context.py +++ b/loopx/capabilities/progress_review/context.py @@ -25,7 +25,6 @@ def external_progress_review_context( from .policy import progress_review_goal_policy from .receipt import ( load_progress_review_receipts, - progress_review_receipt_order_key, progress_review_receipt_summary, ) @@ -33,60 +32,27 @@ def external_progress_review_context( goal_id = str(goal.get("id") or "").strip() if policy["mode"] == "off" or runtime_root is None or not goal_id: return None - from ...control_plane.effect_runtime import effect_runtime_result, EffectRuntimeRemoteError - from ...control_plane.goals.goal_ref_validation import exact_goal_ref + from ...control_plane.effect_runtime import ( + EffectRuntimeRemoteError, + effect_runtime_projection_result, + ) read_error = None try: loaded, rejected = load_progress_review_receipts(Path(runtime_root), goal_id) except (OSError, ValueError, EffectRuntimeRemoteError): loaded, rejected = [], 0 read_error = "receipt_store_unavailable" - # Only newly scoped canonical observations need this current-owner check. - # Old receipts and manual studies retain their existing policy semantics. - current_by_todo = {} - qualified = [] - for receipt in loaded: - binding = receipt.get("evidence_scope", {}).get("criterion_binding", {}) - if binding.get("origin") == "goal_acceptance": - todo_id = binding["todo_id"] - if todo_id not in current_by_todo: - try: - goal_instance_id = goal.get("goal_instance_id") - observation = effect_runtime_result("goal.acceptance.inspect", { - "goal_id": goal_id, "runtime_root": str(runtime_root), "todo_id": todo_id, - **({"goal_ref": exact_goal_ref(goal_id, goal_instance_id)} - if goal_instance_id is not None else {}), - }) - current_by_todo[todo_id] = observation.get("completion_requirements") - except (OSError, ValueError, EffectRuntimeRemoteError): - current_by_todo[todo_id] = None - try: - requirements = current_by_todo[todo_id] - selected = effect_runtime_result("progress_review.criterion_basis", { - "requirements": requirements, "criterion_ids": binding["criterion_ids"], "acceptance": [], - "goal_id": goal_id, "agent_id": receipt["run"]["agent_id"], - })["binding"] if requirements is not None else None - current = selected == binding - except (ValueError, EffectRuntimeRemoteError): - current = False - receipt = {**receipt, "criterion_current": current} - if not current: - receipt.update(status="stale", reason="criterion_basis_unavailable", - drift_signal={"noul": None, "choice": None}) - qualified.append(receipt) - loaded = qualified - pinned = policy.get("contract_revision") - # Newest by run order, never by the observer's local sequence counter. - newest = max(loaded, key=progress_review_receipt_order_key, default=None) - newest_revision = str(newest["contract_revision"]) if newest else None - if pinned: - # Only receipts bound to the pinned revision are current evidence; the - # pin is manual, so a newer receipt under another revision means the - # observer basis moved and the Goal owner must re-pin (or not). - receipts = [item for item in loaded if item["contract_revision"] == pinned] - stale = len(loaded) - len(receipts) + sum(item.get("criterion_current") is False for item in receipts) - else: - receipts, stale = loaded, sum(item.get("criterion_current") is False for item in loaded) + # Empty IO results need no qualification; all nonempty decisions belong to TS. + qualification = effect_runtime_projection_result("progress_review.context.qualify", { + "goal_id": goal_id, "runtime_root": str(runtime_root), + "goal_instance_id": goal.get("goal_instance_id"), + "receipts": loaded, + "contract_revision": policy.get("contract_revision"), + }) if loaded else None + receipts = [ + _receipt_number_codec(row, loaded[index]) + for row, index in zip(qualification["selected"], qualification["selected_indices"], strict=True) + ] if qualification else [] result = { "policy": policy, "receipts": receipts, @@ -94,8 +60,8 @@ def external_progress_review_context( receipts, policy=policy, rejected=rejected, - stale=stale, - newest_contract_revision=newest_revision, + stale=qualification["stale"] if qualification else 0, + newest_contract_revision=qualification["newest_contract_revision"] if qualification else None, ), } result["summary"]["read_state"] = "unavailable" if read_error else "observed" if loaded else "missing" @@ -104,4 +70,23 @@ def external_progress_review_context( return result +def _receipt_number_codec(value: Any, source: Any) -> Any: + """Qualification never edits numeric facts; retain their Python IO spelling. + + JSON numbers cannot carry integral floats or arbitrary integer precision + through JavaScript. Selected source indices preserve those historical facts + without repeating any qualification or selection decision here. + """ + if type(value) in (int, float) and type(source) in (int, float): + return source + if isinstance(value, dict) and isinstance(source, dict): + return {key: _receipt_number_codec(child, source.get(key)) for key, child in value.items()} + if isinstance(value, list) and isinstance(source, list): + return [ + _receipt_number_codec(child, original) + for child, original in zip(value, source, strict=True) + ] + return value + + __all__ = ["external_progress_review_context"] diff --git a/loopx/control_plane/effect_runtime_handlers.ts b/loopx/control_plane/effect_runtime_handlers.ts index b98ba473d4..a149390d3f 100644 --- a/loopx/control_plane/effect_runtime_handlers.ts +++ b/loopx/control_plane/effect_runtime_handlers.ts @@ -628,6 +628,7 @@ export function createEffectRuntimeHandlers( ["collaboration.delegation.result_use_limits", lazyHandler(() => import("./collaboration/delegation_result_use.ts"), ({delegationResultUseLimits}) => delegationResultUseLimits)], ["collaboration.delegation.result_use", lazyHandler(() => import("./collaboration/delegation_result_use.ts"), ({qualifyDelegationResultUse}) => qualifyDelegationResultUse)], ["progress_review.evidence_scope", lazyHandler(() => import("./work_items/progress_review_evidence.ts"), ({progressReviewEvidenceScope}) => progressReviewEvidenceScope)], + ["progress_review.context.qualify", lazyHandler(() => import("./work_items/progress_review_evidence.ts"), ({qualifyProgressReviewContext}) => qualifyProgressReviewContext)], ["progress_review.criterion_basis", lazyHandler(() => import("./work_items/progress_review_evidence.ts"), ({progressReviewCriterionBasis}) => progressReviewCriterionBasis)], ["collaboration.delegation.adoption", lazyHandler(() => import("./collaboration/delegation.ts"), ({recordDelegationAdoption}) => recordDelegationAdoption)], [ diff --git a/loopx/control_plane/effect_runtime_snapshot_methods.json b/loopx/control_plane/effect_runtime_snapshot_methods.json index 7bc340b13d..54e13485ce 100644 --- a/loopx/control_plane/effect_runtime_snapshot_methods.json +++ b/loopx/control_plane/effect_runtime_snapshot_methods.json @@ -21,6 +21,7 @@ "goal.checkpoint_read_context.commit", "goal.checkpoint_read_context.inspect_replay", "performance_diagnosis.inspect", + "progress_review.context.qualify", "state.recovery.archive.verify", "state.recovery.manifest.verify", "runtime.checkpoint_artifacts" diff --git a/loopx/control_plane/work_items/progress_review_evidence.ts b/loopx/control_plane/work_items/progress_review_evidence.ts index f00d752acb..57f4a443d2 100644 --- a/loopx/control_plane/work_items/progress_review_evidence.ts +++ b/loopx/control_plane/work_items/progress_review_evidence.ts @@ -1,6 +1,8 @@ /** Criterion selection from authorized current facts; observation grants no authority. */ +import {isDeepStrictEqual} from "node:util"; +import {inspectLocalGoalAcceptance} from "../goals/acceptance_authority.ts"; import type {JsonObject} from "../effect_program.ts"; -import {requireJsonObject} from "../runtime_decode.ts"; +import {jsonObject, requireJsonObject, requireNonEmptyString} from "../runtime_decode.ts"; import {EffectRuntimeRequestError} from "../effect_runtime_errors.ts"; import {canonicalAuthoritySha256} from "../coordination/authority_store_codec.ts"; import {BARE_SHA256_PATTERN} from "../content_digest.ts"; @@ -43,7 +45,9 @@ export function progressReviewCriterionBasis(params: JsonObject): JsonObject { export function progressReviewEvidenceScope(params: JsonObject): JsonObject { const scope = requireJsonObject(params.scope, "progress-review evidence scope"); const binding = requireJsonObject(scope.criterion_binding, "progress-review criterion binding"); - const fail = (ok: boolean) => {if (!ok) throw new EffectRuntimeRequestError("invalid bounded progress-review evidence scope");}; + const fail = (ok: boolean) => { + if (!ok) throw new EffectRuntimeRequestError("invalid bounded progress-review evidence scope"); + }; fail(scope.coverage === "declared_file_net_change" && Array.isArray(scope.files) && scope.files.length <= 32 && scope.files.every(ref => typeof ref === "string" && ref.length > 0 && ref.length <= 1024 && !ref.startsWith("/") && !ref.includes("\\") && !ref.split("/").includes(".."))); @@ -61,8 +65,79 @@ export function progressReviewEvidenceScope(params: JsonObject): JsonObject { && binding.criterion_ids.every(id => typeof id === "string" && id.length > 0 && id.length <= 200)); const run = requireJsonObject(params.run, "criterion-bound run identity"); fail(params.goal_id === binding.goal_id && run.agent_id === binding.agent_id && run.todo_id === binding.todo_id); - for (const key of ["goal_id", "agent_id", "todo_id", "contract_digest", "contract_revision", "todo_semantic_digest", "criterion_ids"]) + for (const key of ["goal_id", "agent_id", "todo_id", "contract_digest", "contract_revision", + "todo_semantic_digest", "criterion_ids"]) { normalized[key] = binding[key]; + } } return {scope: {criterion_binding: normalized, coverage: scope.coverage, files: scope.files}}; } + +/** + * Qualify a batch in the Python loader's newest-first order. Its + * progress_review_receipt_order_key is the single ordering owner; the first + * qualified receipt is newest, including stable ties. Per-Todo reads are + * isolated, not an atomic snapshot. + */ +export async function qualifyProgressReviewContext(params: JsonObject): Promise { + const goalId = requireNonEmptyString(params.goal_id, "progress-review goal_id"); + const root = requireNonEmptyString(params.runtime_root, "progress-review runtime_root"); + if (!Array.isArray(params.receipts)) { + throw new EffectRuntimeRequestError("progress-review receipts must be an array"); + } + if (params.contract_revision !== null && typeof params.contract_revision !== "string") { + throw new EffectRuntimeRequestError("progress-review contract_revision must be a string or null"); + } + const currentByTodo = new Map(); + const selected: JsonObject[] = []; + const selectedIndices: number[] = []; + let newest: JsonObject | null = null; + let stale = 0; + const pinned = params.contract_revision; + for (const [index, raw] of params.receipts.entries()) { + let receipt = requireJsonObject(raw, "loaded progress-review receipt"); + const binding = jsonObject(jsonObject(receipt.evidence_scope)?.criterion_binding); + if (binding?.origin === "goal_acceptance") { + const todoId = requireNonEmptyString(binding.todo_id, "criterion binding todo_id"); + if (!currentByTodo.has(todoId)) { + let requirements: unknown = null; + try { + const observation = await inspectLocalGoalAcceptance({goal_id: goalId, runtime_root: root, todo_id: todoId, + ...(params.goal_instance_id == null ? {} : { + goal_ref: {goal_id: goalId, goal_instance_id: params.goal_instance_id}, + })}); + requirements = observation.completion_requirements ?? null; + } catch { + // One unavailable Todo must not suppress other current observations. + } + currentByTodo.set(todoId, requirements); + } + let current = false; + try { + const requirements = currentByTodo.get(todoId); + if (requirements !== null) { + const selected = progressReviewCriterionBasis({requirements, criterion_ids: binding.criterion_ids, + acceptance: [], goal_id: goalId, agent_id: requireJsonObject(receipt.run, "receipt run").agent_id}).binding; + current = isDeepStrictEqual(selected, binding); + } + } catch { + // Removed criteria and unavailable bases withdraw both drift signals. + } + receipt = {...receipt, criterion_current: current}; + if (!current) { + receipt = {...receipt, status: "stale", reason: "criterion_basis_unavailable", + drift_signal: {noul: null, choice: null}}; + } + } + if (newest === null) newest = receipt; + if (!pinned || receipt.contract_revision === pinned) { + selected.push(receipt); + selectedIndices.push(index); + if (receipt.criterion_current === false) stale += 1; + } else { + stale += 1; + } + } + return {selected, selected_indices: selectedIndices, stale, + newest_contract_revision: newest?.contract_revision ?? null}; +} diff --git a/tests/control_plane/test_progress_review_qualification_characterization.json b/tests/control_plane/test_progress_review_qualification_characterization.json new file mode 100644 index 0000000000..9312343d7e --- /dev/null +++ b/tests/control_plane/test_progress_review_qualification_characterization.json @@ -0,0 +1,841 @@ +{ + "empty": { + "context": { + "policy": {"contract_revision": null, "drift_threshold": 2, "mode": "shadow", "schema_version": "progress_review_policy_v0", "signal": "noul"}, + "receipts": [], + "summary": { + "authority": "none", + "contract_revision": null, + "drift_counts": {"choice": 0, "noul": 0}, + "drift_threshold": 2, + "latest": null, + "mode": "shadow", + "pending_receipts": 0, + "read_state": "missing", + "receipt_count": 0, + "rejected_receipts": 0, + "schema_version": "progress_review_status_v0", + "signal": "noul", + "stale_receipts": 0, + "status_counts": {} + } + }, + "markdown": "# LoopX Status\n\n- ok: `None`\n- registry: `None`\n- runtime_root: `None`\n- goals: `None`\n- runs: `None`\n- projection: 🔴 no projection envelope; freshness and coverage unknown\n- contract: ok=None, errors=None, warnings=None, checks=None\n- global_registry: available=None, ok=None, findings=None, high=None, action=None, info=None\n\n## Attention Queue\n- summary: items=None, needs_user_or_controller=None, needs_controller=None, needs_codex=None, watching_external_evidence=None, watching_monitor=None\n- `None`: status=None phase=None waiting_on=None severity=None source=None\n - project_asset_source: project_asset\n - project_asset: owner= gate= stop=\n\n## Run History\n- summary: goals=None, runs=None\n- none", + "status_review": { + "authority": "none", + "contract_revision": null, + "drift_counts": {"choice": 0, "noul": 0}, + "drift_threshold": 2, + "latest": null, + "mode": "shadow", + "pending_receipts": 0, + "read_state": "missing", + "receipt_count": 0, + "rejected_receipts": 0, + "schema_version": "progress_review_status_v0", + "signal": "noul", + "stale_receipts": 0, + "status_counts": {} + } + }, + "manual_legacy": { + "context": { + "policy": {"contract_revision": null, "drift_threshold": 2, "mode": "shadow", "schema_version": "progress_review_policy_v0", "signal": "noul"}, + "receipts": [ + { + "authority": "none", + "contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "drift_signal": {"choice": true, "noul": true}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": {"coverage": "declared_file_net_change", "criterion_binding": {"criteria_sha256": "b081817a898310d49f65b65f567c59324cbf8b598aa11f5359b8955f1133728a", "origin": "operator_study"}, "files": []}, + "goal_id": "synthetic-managed-research", + "judgments": {"choice": {"increment": "no_new_evidence", "relation": "off_goal"}, "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04}}, + "label_probability_threshold": 0.6, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "reason": null, + "receipt_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "recorded_at": 1700000000.0, + "run": {"agent_id": "worker", "generated_at": "2026-09-21T00:00:01Z", "todo_id": null, "turn_instance_id": "turn-1"}, + "schema_version": "progress_review_receipt_v0", + "sequence": 0, + "signal_rule_version": "progress_review_signal_rule_v1", + "status": "completed", + "timing_ns": {"assessment_total": 1200000}, + "usage": {"input_tokens": 1000} + }, + { + "authority": "none", + "contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "drift_signal": {"choice": true, "noul": true}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "goal_id": "synthetic-managed-research", + "judgments": {"choice": {"increment": "no_new_evidence", "relation": "off_goal"}, "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04}}, + "label_probability_threshold": 0.6, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "reason": null, + "receipt_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "recorded_at": 1700000000.0, + "run": {"agent_id": "worker", "generated_at": "2026-09-21T00:00:01Z", "todo_id": null, "turn_instance_id": "turn-1"}, + "schema_version": "progress_review_receipt_v0", + "sequence": 0, + "signal_rule_version": "progress_review_signal_rule_v1", + "status": "completed", + "timing_ns": {"assessment_total": 1200000}, + "usage": {"input_tokens": 1000} + } + ], + "summary": { + "authority": "none", + "contract_revision": null, + "drift_counts": {"choice": 2, "noul": 2}, + "drift_threshold": 2, + "latest": { + "drift_signal": {"choice": true, "noul": true}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": {"coverage": "declared_file_net_change", "criterion_binding": {"criteria_sha256": "b081817a898310d49f65b65f567c59324cbf8b598aa11f5359b8955f1133728a", "origin": "operator_study"}, "files": []}, + "judgments": {"choice": {"increment": "no_new_evidence", "relation": "off_goal"}, "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04}}, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "run": {"agent_id": "worker", "generated_at": "2026-09-21T00:00:01Z", "todo_id": null, "turn_instance_id": "turn-1"}, + "status": "completed" + }, + "mode": "shadow", + "newest_receipt_contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "pending_receipts": 0, + "read_state": "observed", + "receipt_count": 2, + "rejected_receipts": 0, + "schema_version": "progress_review_status_v0", + "signal": "noul", + "stale_receipts": 0, + "status_counts": {"completed": 2} + } + }, + "markdown": "# LoopX Status\n\n- ok: `None`\n- registry: `None`\n- runtime_root: `None`\n- goals: `None`\n- runs: `None`\n- projection: 🔴 no projection envelope; freshness and coverage unknown\n- contract: ok=None, errors=None, warnings=None, checks=None\n- global_registry: available=None, ok=None, findings=None, high=None, action=None, info=None\n\n## Attention Queue\n- summary: items=None, needs_user_or_controller=None, needs_controller=None, needs_codex=None, watching_external_evidence=None, watching_monitor=None\n- `None`: status=None phase=None waiting_on=None severity=None source=None\n - project_asset_source: project_asset\n - project_asset: owner= gate= stop=\n\n## Run History\n- summary: goals=None, runs=None\n- none", + "status_review": { + "authority": "none", + "contract_revision": null, + "drift_counts": {"choice": 2, "noul": 2}, + "drift_threshold": 2, + "latest": { + "drift_signal": {"choice": true, "noul": true}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": { + "coverage": "declared_file_net_change", + "criterion_binding": {"criteria_sha256": "b081817a898310d49f65b65f567c59324cbf8b598aa11f5359b8955f1133728a", "origin": "operator_study"}, + "files": [] + }, + "judgments": { + "choice": {"increment": "no_new_evidence", "relation": "off_goal"}, + "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04} + }, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "run": {"agent_id": "worker", "generated_at": "2026-09-21T00:00:01Z", "todo_id": null, "turn_instance_id": "turn-1"}, + "status": "completed" + }, + "mode": "shadow", + "newest_receipt_contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "pending_receipts": 0, + "read_state": "observed", + "receipt_count": 2, + "rejected_receipts": 0, + "schema_version": "progress_review_status_v0", + "signal": "noul", + "stale_receipts": 0, + "status_counts": {"completed": 2} + } + }, + "multiple": { + "context": { + "policy": {"contract_revision": null, "drift_threshold": 2, "mode": "shadow", "schema_version": "progress_review_policy_v0", "signal": "noul"}, + "receipts": [ + { + "authority": "none", + "contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "criterion_current": true, + "drift_signal": {"choice": true, "noul": true}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": {"coverage": "declared_file_net_change", "criterion_binding": {"agent_id": "analyst", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "b60202ec386f3ec5e8e1ffcc90bf62eed6d821f16f1e42c17400fb389dc258de", "criterion_ids": ["analyst-initial"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_analyst-initial", "todo_semantic_digest": "fcc5a4e2170eed7f6449935470ea194fc631d0c2c1a4377b63b28781b7aa100c"}, "files": ["output.json"]}, + "goal_id": "synthetic-managed-research", + "judgments": {"choice": {"increment": "no_new_evidence", "relation": "off_goal"}, "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04}}, + "label_probability_threshold": 0.6, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "reason": null, + "receipt_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "recorded_at": 1700000000.0, + "run": {"agent_id": "analyst", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_analyst-initial", "turn_instance_id": "turn-1"}, + "schema_version": "progress_review_receipt_v0", + "sequence": 0, + "signal_rule_version": "progress_review_signal_rule_v1", + "status": "completed", + "timing_ns": {"assessment_total": 1200000}, + "usage": {"input_tokens": 1000} + }, + { + "authority": "none", + "contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "criterion_current": true, + "drift_signal": {"choice": true, "noul": true}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": {"coverage": "declared_file_net_change", "criterion_binding": {"agent_id": "reviewer", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "e69a1d751162826af65dab9f4321d060014a20dbd595c152eff875feeac208d0", "criterion_ids": ["reviewer-corrected"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_reviewer-corrected", "todo_semantic_digest": "e6ac0dece9ade5c66e58eab709422248fcb8fa9fbb2812cffd2af46b55ecd3ed"}, "files": ["output.json"]}, + "goal_id": "synthetic-managed-research", + "judgments": {"choice": {"increment": "no_new_evidence", "relation": "off_goal"}, "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04}}, + "label_probability_threshold": 0.6, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "reason": null, + "receipt_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "recorded_at": 1700000000.0, + "run": {"agent_id": "reviewer", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_reviewer-corrected", "turn_instance_id": "turn-1"}, + "schema_version": "progress_review_receipt_v0", + "sequence": 0, + "signal_rule_version": "progress_review_signal_rule_v1", + "status": "completed", + "timing_ns": {"assessment_total": 1200000}, + "usage": {"input_tokens": 1000} + } + ], + "summary": { + "authority": "none", + "contract_revision": null, + "drift_counts": {"choice": 2, "noul": 2}, + "drift_threshold": 2, + "latest": { + "criterion_current": true, + "drift_signal": {"choice": true, "noul": true}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": {"coverage": "declared_file_net_change", "criterion_binding": {"agent_id": "analyst", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "b60202ec386f3ec5e8e1ffcc90bf62eed6d821f16f1e42c17400fb389dc258de", "criterion_ids": ["analyst-initial"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_analyst-initial", "todo_semantic_digest": "fcc5a4e2170eed7f6449935470ea194fc631d0c2c1a4377b63b28781b7aa100c"}, "files": ["output.json"]}, + "judgments": {"choice": {"increment": "no_new_evidence", "relation": "off_goal"}, "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04}}, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "run": {"agent_id": "analyst", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_analyst-initial", "turn_instance_id": "turn-1"}, + "status": "completed" + }, + "mode": "shadow", + "newest_receipt_contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "pending_receipts": 0, + "read_state": "observed", + "receipt_count": 2, + "rejected_receipts": 0, + "schema_version": "progress_review_status_v0", + "signal": "noul", + "stale_receipts": 0, + "status_counts": {"completed": 2} + } + }, + "markdown": "# LoopX Status\n\n- ok: `None`\n- registry: `None`\n- runtime_root: `None`\n- goals: `None`\n- runs: `None`\n- projection: 🔴 no projection envelope; freshness and coverage unknown\n- contract: ok=None, errors=None, warnings=None, checks=None\n- global_registry: available=None, ok=None, findings=None, high=None, action=None, info=None\n\n## Attention Queue\n- summary: items=None, needs_user_or_controller=None, needs_controller=None, needs_codex=None, watching_external_evidence=None, watching_monitor=None\n- `None`: status=None phase=None waiting_on=None severity=None source=None\n - project_asset_source: project_asset\n - project_asset: owner= gate= stop=\n\n## Run History\n- summary: goals=None, runs=None\n- none", + "status_review": { + "authority": "none", + "contract_revision": null, + "drift_counts": {"choice": 2, "noul": 2}, + "drift_threshold": 2, + "latest": { + "criterion_current": true, + "drift_signal": {"choice": true, "noul": true}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": { + "coverage": "declared_file_net_change", + "criterion_binding": {"agent_id": "analyst", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "b60202ec386f3ec5e8e1ffcc90bf62eed6d821f16f1e42c17400fb389dc258de", "criterion_ids": ["analyst-initial"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_analyst-initial", "todo_semantic_digest": "fcc5a4e2170eed7f6449935470ea194fc631d0c2c1a4377b63b28781b7aa100c"}, + "files": ["output.json"] + }, + "judgments": { + "choice": {"increment": "no_new_evidence", "relation": "off_goal"}, + "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04} + }, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "run": {"agent_id": "analyst", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_analyst-initial", "turn_instance_id": "turn-1"}, + "status": "completed" + }, + "mode": "shadow", + "newest_receipt_contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "pending_receipts": 0, + "read_state": "observed", + "receipt_count": 2, + "rejected_receipts": 0, + "schema_version": "progress_review_status_v0", + "signal": "noul", + "stale_receipts": 0, + "status_counts": {"completed": 2} + } + }, + "off": {"context": null, "markdown": "# LoopX Status\n\n- ok: `None`\n- registry: `None`\n- runtime_root: `None`\n- goals: `None`\n- runs: `None`\n- projection: 🔴 no projection envelope; freshness and coverage unknown\n- contract: ok=None, errors=None, warnings=None, checks=None\n- global_registry: available=None, ok=None, findings=None, high=None, action=None, info=None\n\n## Attention Queue\n- summary: items=None, needs_user_or_controller=None, needs_controller=None, needs_codex=None, watching_external_evidence=None, watching_monitor=None\n- `None`: status=None phase=None waiting_on=None severity=None source=None\n - project_asset_source: legacy/raw fallback; owner/gate/stop are not project_asset-backed\n\n## Run History\n- summary: goals=None, runs=None\n- none", "status_review": null}, + "outside_pin": { + "context": { + "policy": {"contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", "drift_threshold": 2, "mode": "assist", "schema_version": "progress_review_policy_v0", "signal": "noul"}, + "receipts": [ + { + "authority": "none", + "contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "criterion_current": true, + "drift_signal": {"choice": true, "noul": true}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": {"coverage": "declared_file_net_change", "criterion_binding": {"agent_id": "analyst", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "b60202ec386f3ec5e8e1ffcc90bf62eed6d821f16f1e42c17400fb389dc258de", "criterion_ids": ["analyst-initial"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_analyst-initial", "todo_semantic_digest": "fcc5a4e2170eed7f6449935470ea194fc631d0c2c1a4377b63b28781b7aa100c"}, "files": ["output.json"]}, + "goal_id": "synthetic-managed-research", + "judgments": {"choice": {"increment": "no_new_evidence", "relation": "off_goal"}, "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04}}, + "label_probability_threshold": 0.6, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "reason": null, + "receipt_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "recorded_at": 1700000000.0, + "run": {"agent_id": "analyst", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_analyst-initial", "turn_instance_id": "turn-1"}, + "schema_version": "progress_review_receipt_v0", + "sequence": 0, + "signal_rule_version": "progress_review_signal_rule_v1", + "status": "completed", + "timing_ns": {"assessment_total": 1200000}, + "usage": {"input_tokens": 1000} + } + ], + "summary": { + "authority": "none", + "contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "drift_counts": {"choice": 1, "noul": 1}, + "drift_threshold": 2, + "latest": { + "criterion_current": true, + "drift_signal": {"choice": true, "noul": true}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": {"coverage": "declared_file_net_change", "criterion_binding": {"agent_id": "analyst", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "b60202ec386f3ec5e8e1ffcc90bf62eed6d821f16f1e42c17400fb389dc258de", "criterion_ids": ["analyst-initial"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_analyst-initial", "todo_semantic_digest": "fcc5a4e2170eed7f6449935470ea194fc631d0c2c1a4377b63b28781b7aa100c"}, "files": ["output.json"]}, + "judgments": {"choice": {"increment": "no_new_evidence", "relation": "off_goal"}, "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04}}, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "run": {"agent_id": "analyst", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_analyst-initial", "turn_instance_id": "turn-1"}, + "status": "completed" + }, + "mode": "assist", + "newest_receipt_contract_revision": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "pending_receipts": 0, + "read_state": "observed", + "rebind_hint": "newer_receipts_under_unpinned_revision", + "receipt_count": 1, + "rejected_receipts": 0, + "schema_version": "progress_review_status_v0", + "signal": "noul", + "stale_receipts": 1, + "status_counts": {"completed": 1} + } + }, + "markdown": "# LoopX Status\n\n- ok: `None`\n- registry: `None`\n- runtime_root: `None`\n- goals: `None`\n- runs: `None`\n- projection: 🔴 no projection envelope; freshness and coverage unknown\n- contract: ok=None, errors=None, warnings=None, checks=None\n- global_registry: available=None, ok=None, findings=None, high=None, action=None, info=None\n\n## Attention Queue\n- summary: items=None, needs_user_or_controller=None, needs_controller=None, needs_codex=None, watching_external_evidence=None, watching_monitor=None\n- `None`: status=None phase=None waiting_on=None severity=None source=None\n - project_asset_source: project_asset\n - project_asset: owner= gate= stop=\n\n## Run History\n- summary: goals=None, runs=None\n- none", + "status_review": { + "authority": "none", + "contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "drift_counts": {"choice": 1, "noul": 1}, + "drift_threshold": 2, + "latest": { + "criterion_current": true, + "drift_signal": {"choice": true, "noul": true}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": { + "coverage": "declared_file_net_change", + "criterion_binding": {"agent_id": "analyst", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "b60202ec386f3ec5e8e1ffcc90bf62eed6d821f16f1e42c17400fb389dc258de", "criterion_ids": ["analyst-initial"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_analyst-initial", "todo_semantic_digest": "fcc5a4e2170eed7f6449935470ea194fc631d0c2c1a4377b63b28781b7aa100c"}, + "files": ["output.json"] + }, + "judgments": { + "choice": {"increment": "no_new_evidence", "relation": "off_goal"}, + "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04} + }, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "run": {"agent_id": "analyst", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_analyst-initial", "turn_instance_id": "turn-1"}, + "status": "completed" + }, + "mode": "assist", + "newest_receipt_contract_revision": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "pending_receipts": 0, + "read_state": "observed", + "rebind_hint": "newer_receipts_under_unpinned_revision", + "receipt_count": 1, + "rejected_receipts": 0, + "schema_version": "progress_review_status_v0", + "signal": "noul", + "stale_receipts": 1, + "status_counts": {"completed": 1} + } + }, + "removed": { + "context": { + "policy": {"contract_revision": null, "drift_threshold": 2, "mode": "shadow", "schema_version": "progress_review_policy_v0", "signal": "noul"}, + "receipts": [ + { + "authority": "none", + "contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "criterion_current": false, + "drift_signal": {"choice": null, "noul": null}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": {"coverage": "declared_file_net_change", "criterion_binding": {"agent_id": "analyst", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "b60202ec386f3ec5e8e1ffcc90bf62eed6d821f16f1e42c17400fb389dc258de", "criterion_ids": ["removed"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_analyst-initial", "todo_semantic_digest": "fcc5a4e2170eed7f6449935470ea194fc631d0c2c1a4377b63b28781b7aa100c"}, "files": ["output.json"]}, + "goal_id": "synthetic-managed-research", + "judgments": {"choice": {"increment": "no_new_evidence", "relation": "off_goal"}, "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04}}, + "label_probability_threshold": 0.6, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "reason": "criterion_basis_unavailable", + "receipt_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "recorded_at": 1700000000.0, + "run": {"agent_id": "analyst", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_analyst-initial", "turn_instance_id": "turn-1"}, + "schema_version": "progress_review_receipt_v0", + "sequence": 0, + "signal_rule_version": "progress_review_signal_rule_v1", + "status": "stale", + "timing_ns": {"assessment_total": 1200000}, + "usage": {"input_tokens": 1000} + } + ], + "summary": { + "authority": "none", + "contract_revision": null, + "drift_counts": {"choice": 0, "noul": 0}, + "drift_threshold": 2, + "latest": { + "criterion_current": false, + "drift_signal": {"choice": null, "noul": null}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": {"coverage": "declared_file_net_change", "criterion_binding": {"agent_id": "analyst", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "b60202ec386f3ec5e8e1ffcc90bf62eed6d821f16f1e42c17400fb389dc258de", "criterion_ids": ["removed"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_analyst-initial", "todo_semantic_digest": "fcc5a4e2170eed7f6449935470ea194fc631d0c2c1a4377b63b28781b7aa100c"}, "files": ["output.json"]}, + "judgments": {"choice": {"increment": "no_new_evidence", "relation": "off_goal"}, "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04}}, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "run": {"agent_id": "analyst", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_analyst-initial", "turn_instance_id": "turn-1"}, + "status": "stale" + }, + "mode": "shadow", + "newest_receipt_contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "pending_receipts": 0, + "read_state": "observed", + "receipt_count": 1, + "rejected_receipts": 0, + "schema_version": "progress_review_status_v0", + "signal": "noul", + "stale_receipts": 1, + "status_counts": {"stale": 1} + } + }, + "markdown": "# LoopX Status\n\n- ok: `None`\n- registry: `None`\n- runtime_root: `None`\n- goals: `None`\n- runs: `None`\n- projection: 🔴 no projection envelope; freshness and coverage unknown\n- contract: ok=None, errors=None, warnings=None, checks=None\n- global_registry: available=None, ok=None, findings=None, high=None, action=None, info=None\n\n## Attention Queue\n- summary: items=None, needs_user_or_controller=None, needs_controller=None, needs_codex=None, watching_external_evidence=None, watching_monitor=None\n- `None`: status=None phase=None waiting_on=None severity=None source=None\n - project_asset_source: project_asset\n - project_asset: owner= gate= stop=\n\n## Run History\n- summary: goals=None, runs=None\n- none", + "status_review": { + "authority": "none", + "contract_revision": null, + "drift_counts": {"choice": 0, "noul": 0}, + "drift_threshold": 2, + "latest": { + "criterion_current": false, + "drift_signal": {"choice": null, "noul": null}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": { + "coverage": "declared_file_net_change", + "criterion_binding": {"agent_id": "analyst", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "b60202ec386f3ec5e8e1ffcc90bf62eed6d821f16f1e42c17400fb389dc258de", "criterion_ids": ["removed"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_analyst-initial", "todo_semantic_digest": "fcc5a4e2170eed7f6449935470ea194fc631d0c2c1a4377b63b28781b7aa100c"}, + "files": ["output.json"] + }, + "judgments": { + "choice": {"increment": "no_new_evidence", "relation": "off_goal"}, + "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04} + }, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "run": {"agent_id": "analyst", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_analyst-initial", "turn_instance_id": "turn-1"}, + "status": "stale" + }, + "mode": "shadow", + "newest_receipt_contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "pending_receipts": 0, + "read_state": "observed", + "receipt_count": 1, + "rejected_receipts": 0, + "schema_version": "progress_review_status_v0", + "signal": "noul", + "stale_receipts": 1, + "status_counts": {"stale": 1} + } + }, + "repeated": { + "context": { + "policy": {"contract_revision": null, "drift_threshold": 2, "mode": "shadow", "schema_version": "progress_review_policy_v0", "signal": "noul"}, + "receipts": [ + { + "authority": "none", + "contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "criterion_current": true, + "drift_signal": {"choice": true, "noul": true}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": {"coverage": "declared_file_net_change", "criterion_binding": {"agent_id": "analyst", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "b60202ec386f3ec5e8e1ffcc90bf62eed6d821f16f1e42c17400fb389dc258de", "criterion_ids": ["analyst-initial"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_analyst-initial", "todo_semantic_digest": "fcc5a4e2170eed7f6449935470ea194fc631d0c2c1a4377b63b28781b7aa100c"}, "files": ["output.json"]}, + "goal_id": "synthetic-managed-research", + "judgments": {"choice": {"increment": "no_new_evidence", "relation": "off_goal"}, "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04}}, + "label_probability_threshold": 0.6, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "reason": null, + "receipt_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "recorded_at": 1700000000.0, + "run": {"agent_id": "analyst", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_analyst-initial", "turn_instance_id": "turn-1"}, + "schema_version": "progress_review_receipt_v0", + "sequence": 0, + "signal_rule_version": "progress_review_signal_rule_v1", + "status": "completed", + "timing_ns": {"assessment_total": 1200000}, + "usage": {"input_tokens": 1000} + }, + { + "authority": "none", + "contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "criterion_current": true, + "drift_signal": {"choice": true, "noul": true}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": {"coverage": "declared_file_net_change", "criterion_binding": {"agent_id": "analyst", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "b60202ec386f3ec5e8e1ffcc90bf62eed6d821f16f1e42c17400fb389dc258de", "criterion_ids": ["analyst-initial"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_analyst-initial", "todo_semantic_digest": "fcc5a4e2170eed7f6449935470ea194fc631d0c2c1a4377b63b28781b7aa100c"}, "files": ["output.json"]}, + "goal_id": "synthetic-managed-research", + "judgments": {"choice": {"increment": "no_new_evidence", "relation": "off_goal"}, "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04}}, + "label_probability_threshold": 0.6, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "reason": null, + "receipt_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "recorded_at": 1700000000.0, + "run": {"agent_id": "analyst", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_analyst-initial", "turn_instance_id": "turn-1"}, + "schema_version": "progress_review_receipt_v0", + "sequence": 0, + "signal_rule_version": "progress_review_signal_rule_v1", + "status": "completed", + "timing_ns": {"assessment_total": 1200000}, + "usage": {"input_tokens": 1000} + } + ], + "summary": { + "authority": "none", + "contract_revision": null, + "drift_counts": {"choice": 2, "noul": 2}, + "drift_threshold": 2, + "latest": { + "criterion_current": true, + "drift_signal": {"choice": true, "noul": true}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": {"coverage": "declared_file_net_change", "criterion_binding": {"agent_id": "analyst", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "b60202ec386f3ec5e8e1ffcc90bf62eed6d821f16f1e42c17400fb389dc258de", "criterion_ids": ["analyst-initial"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_analyst-initial", "todo_semantic_digest": "fcc5a4e2170eed7f6449935470ea194fc631d0c2c1a4377b63b28781b7aa100c"}, "files": ["output.json"]}, + "judgments": {"choice": {"increment": "no_new_evidence", "relation": "off_goal"}, "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04}}, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "run": {"agent_id": "analyst", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_analyst-initial", "turn_instance_id": "turn-1"}, + "status": "completed" + }, + "mode": "shadow", + "newest_receipt_contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "pending_receipts": 0, + "read_state": "observed", + "receipt_count": 2, + "rejected_receipts": 0, + "schema_version": "progress_review_status_v0", + "signal": "noul", + "stale_receipts": 0, + "status_counts": {"completed": 2} + } + }, + "markdown": "# LoopX Status\n\n- ok: `None`\n- registry: `None`\n- runtime_root: `None`\n- goals: `None`\n- runs: `None`\n- projection: 🔴 no projection envelope; freshness and coverage unknown\n- contract: ok=None, errors=None, warnings=None, checks=None\n- global_registry: available=None, ok=None, findings=None, high=None, action=None, info=None\n\n## Attention Queue\n- summary: items=None, needs_user_or_controller=None, needs_controller=None, needs_codex=None, watching_external_evidence=None, watching_monitor=None\n- `None`: status=None phase=None waiting_on=None severity=None source=None\n - project_asset_source: project_asset\n - project_asset: owner= gate= stop=\n\n## Run History\n- summary: goals=None, runs=None\n- none", + "status_review": { + "authority": "none", + "contract_revision": null, + "drift_counts": {"choice": 2, "noul": 2}, + "drift_threshold": 2, + "latest": { + "criterion_current": true, + "drift_signal": {"choice": true, "noul": true}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": { + "coverage": "declared_file_net_change", + "criterion_binding": {"agent_id": "analyst", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "b60202ec386f3ec5e8e1ffcc90bf62eed6d821f16f1e42c17400fb389dc258de", "criterion_ids": ["analyst-initial"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_analyst-initial", "todo_semantic_digest": "fcc5a4e2170eed7f6449935470ea194fc631d0c2c1a4377b63b28781b7aa100c"}, + "files": ["output.json"] + }, + "judgments": { + "choice": {"increment": "no_new_evidence", "relation": "off_goal"}, + "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04} + }, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "run": {"agent_id": "analyst", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_analyst-initial", "turn_instance_id": "turn-1"}, + "status": "completed" + }, + "mode": "shadow", + "newest_receipt_contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "pending_receipts": 0, + "read_state": "observed", + "receipt_count": 2, + "rejected_receipts": 0, + "schema_version": "progress_review_status_v0", + "signal": "noul", + "stale_receipts": 0, + "status_counts": {"completed": 2} + } + }, + "unavailable_inspection": { + "context": { + "policy": {"contract_revision": null, "drift_threshold": 2, "mode": "shadow", "schema_version": "progress_review_policy_v0", "signal": "noul"}, + "receipts": [ + { + "authority": "none", + "contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "criterion_current": false, + "drift_signal": {"choice": null, "noul": null}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": {"coverage": "declared_file_net_change", "criterion_binding": {"agent_id": "analyst", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "b60202ec386f3ec5e8e1ffcc90bf62eed6d821f16f1e42c17400fb389dc258de", "criterion_ids": ["analyst-initial"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_missing", "todo_semantic_digest": "fcc5a4e2170eed7f6449935470ea194fc631d0c2c1a4377b63b28781b7aa100c"}, "files": ["output.json"]}, + "goal_id": "synthetic-managed-research", + "judgments": {"choice": {"increment": "no_new_evidence", "relation": "off_goal"}, "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04}}, + "label_probability_threshold": 0.6, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "reason": "criterion_basis_unavailable", + "receipt_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "recorded_at": 1700000000.0, + "run": {"agent_id": "analyst", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_missing", "turn_instance_id": "turn-1"}, + "schema_version": "progress_review_receipt_v0", + "sequence": 0, + "signal_rule_version": "progress_review_signal_rule_v1", + "status": "stale", + "timing_ns": {"assessment_total": 1200000}, + "usage": {"input_tokens": 1000} + }, + { + "authority": "none", + "contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "criterion_current": true, + "drift_signal": {"choice": true, "noul": true}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": {"coverage": "declared_file_net_change", "criterion_binding": {"agent_id": "reviewer", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "e69a1d751162826af65dab9f4321d060014a20dbd595c152eff875feeac208d0", "criterion_ids": ["reviewer-corrected"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_reviewer-corrected", "todo_semantic_digest": "e6ac0dece9ade5c66e58eab709422248fcb8fa9fbb2812cffd2af46b55ecd3ed"}, "files": ["output.json"]}, + "goal_id": "synthetic-managed-research", + "judgments": {"choice": {"increment": "no_new_evidence", "relation": "off_goal"}, "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04}}, + "label_probability_threshold": 0.6, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "reason": null, + "receipt_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "recorded_at": 1700000000.0, + "run": {"agent_id": "reviewer", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_reviewer-corrected", "turn_instance_id": "turn-1"}, + "schema_version": "progress_review_receipt_v0", + "sequence": 0, + "signal_rule_version": "progress_review_signal_rule_v1", + "status": "completed", + "timing_ns": {"assessment_total": 1200000}, + "usage": {"input_tokens": 1000} + } + ], + "summary": { + "authority": "none", + "contract_revision": null, + "drift_counts": {"choice": 1, "noul": 1}, + "drift_threshold": 2, + "latest": { + "criterion_current": false, + "drift_signal": {"choice": null, "noul": null}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": {"coverage": "declared_file_net_change", "criterion_binding": {"agent_id": "analyst", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "b60202ec386f3ec5e8e1ffcc90bf62eed6d821f16f1e42c17400fb389dc258de", "criterion_ids": ["analyst-initial"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_missing", "todo_semantic_digest": "fcc5a4e2170eed7f6449935470ea194fc631d0c2c1a4377b63b28781b7aa100c"}, "files": ["output.json"]}, + "judgments": {"choice": {"increment": "no_new_evidence", "relation": "off_goal"}, "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04}}, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "run": {"agent_id": "analyst", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_missing", "turn_instance_id": "turn-1"}, + "status": "stale" + }, + "mode": "shadow", + "newest_receipt_contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "pending_receipts": 0, + "read_state": "observed", + "receipt_count": 2, + "rejected_receipts": 0, + "schema_version": "progress_review_status_v0", + "signal": "noul", + "stale_receipts": 1, + "status_counts": {"completed": 1, "stale": 1} + } + }, + "markdown": "# LoopX Status\n\n- ok: `None`\n- registry: `None`\n- runtime_root: `None`\n- goals: `None`\n- runs: `None`\n- projection: 🔴 no projection envelope; freshness and coverage unknown\n- contract: ok=None, errors=None, warnings=None, checks=None\n- global_registry: available=None, ok=None, findings=None, high=None, action=None, info=None\n\n## Attention Queue\n- summary: items=None, needs_user_or_controller=None, needs_controller=None, needs_codex=None, watching_external_evidence=None, watching_monitor=None\n- `None`: status=None phase=None waiting_on=None severity=None source=None\n - project_asset_source: project_asset\n - project_asset: owner= gate= stop=\n\n## Run History\n- summary: goals=None, runs=None\n- none", + "status_review": { + "authority": "none", + "contract_revision": null, + "drift_counts": {"choice": 1, "noul": 1}, + "drift_threshold": 2, + "latest": { + "criterion_current": false, + "drift_signal": {"choice": null, "noul": null}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": { + "coverage": "declared_file_net_change", + "criterion_binding": {"agent_id": "analyst", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "b60202ec386f3ec5e8e1ffcc90bf62eed6d821f16f1e42c17400fb389dc258de", "criterion_ids": ["analyst-initial"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_missing", "todo_semantic_digest": "fcc5a4e2170eed7f6449935470ea194fc631d0c2c1a4377b63b28781b7aa100c"}, + "files": ["output.json"] + }, + "judgments": { + "choice": {"increment": "no_new_evidence", "relation": "off_goal"}, + "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04} + }, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "run": {"agent_id": "analyst", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_missing", "turn_instance_id": "turn-1"}, + "status": "stale" + }, + "mode": "shadow", + "newest_receipt_contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "pending_receipts": 0, + "read_state": "observed", + "receipt_count": 2, + "rejected_receipts": 0, + "schema_version": "progress_review_status_v0", + "signal": "noul", + "stale_receipts": 1, + "status_counts": {"completed": 1, "stale": 1} + } + }, + "unavailable_store": { + "context": { + "policy": {"contract_revision": null, "drift_threshold": 2, "mode": "shadow", "schema_version": "progress_review_policy_v0", "signal": "noul"}, + "receipts": [], + "summary": { + "authority": "none", + "contract_revision": null, + "drift_counts": {"choice": 0, "noul": 0}, + "drift_threshold": 2, + "latest": null, + "mode": "shadow", + "pending_receipts": 0, + "read_error": "receipt_store_unavailable", + "read_state": "unavailable", + "receipt_count": 0, + "rejected_receipts": 0, + "schema_version": "progress_review_status_v0", + "signal": "noul", + "stale_receipts": 0, + "status_counts": {} + } + }, + "markdown": "# LoopX Status\n\n- ok: `None`\n- registry: `None`\n- runtime_root: `None`\n- goals: `None`\n- runs: `None`\n- projection: 🔴 no projection envelope; freshness and coverage unknown\n- contract: ok=None, errors=None, warnings=None, checks=None\n- global_registry: available=None, ok=None, findings=None, high=None, action=None, info=None\n\n## Attention Queue\n- summary: items=None, needs_user_or_controller=None, needs_controller=None, needs_codex=None, watching_external_evidence=None, watching_monitor=None\n- `None`: status=None phase=None waiting_on=None severity=None source=None\n - project_asset_source: project_asset\n - project_asset: owner= gate= stop=\n\n## Run History\n- summary: goals=None, runs=None\n- none", + "status_review": { + "authority": "none", + "contract_revision": null, + "drift_counts": {"choice": 0, "noul": 0}, + "drift_threshold": 2, + "latest": null, + "mode": "shadow", + "pending_receipts": 0, + "read_error": "receipt_store_unavailable", + "read_state": "unavailable", + "receipt_count": 0, + "rejected_receipts": 0, + "schema_version": "progress_review_status_v0", + "signal": "noul", + "stale_receipts": 0, + "status_counts": {} + } + }, + "wrong_instance": { + "context": { + "policy": {"contract_revision": null, "drift_threshold": 2, "mode": "shadow", "schema_version": "progress_review_policy_v0", "signal": "noul"}, + "receipts": [ + { + "authority": "none", + "contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "criterion_current": false, + "drift_signal": {"choice": null, "noul": null}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": {"coverage": "declared_file_net_change", "criterion_binding": {"agent_id": "analyst", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "b60202ec386f3ec5e8e1ffcc90bf62eed6d821f16f1e42c17400fb389dc258de", "criterion_ids": ["analyst-initial"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_analyst-initial", "todo_semantic_digest": "fcc5a4e2170eed7f6449935470ea194fc631d0c2c1a4377b63b28781b7aa100c"}, "files": ["output.json"]}, + "goal_id": "synthetic-managed-research", + "judgments": {"choice": {"increment": "no_new_evidence", "relation": "off_goal"}, "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04}}, + "label_probability_threshold": 0.6, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "reason": "criterion_basis_unavailable", + "receipt_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "recorded_at": 1700000000.0, + "run": {"agent_id": "analyst", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_analyst-initial", "turn_instance_id": "turn-1"}, + "schema_version": "progress_review_receipt_v0", + "sequence": 0, + "signal_rule_version": "progress_review_signal_rule_v1", + "status": "stale", + "timing_ns": {"assessment_total": 1200000}, + "usage": {"input_tokens": 1000} + } + ], + "summary": { + "authority": "none", + "contract_revision": null, + "drift_counts": {"choice": 0, "noul": 0}, + "drift_threshold": 2, + "latest": { + "criterion_current": false, + "drift_signal": {"choice": null, "noul": null}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": {"coverage": "declared_file_net_change", "criterion_binding": {"agent_id": "analyst", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "b60202ec386f3ec5e8e1ffcc90bf62eed6d821f16f1e42c17400fb389dc258de", "criterion_ids": ["analyst-initial"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_analyst-initial", "todo_semantic_digest": "fcc5a4e2170eed7f6449935470ea194fc631d0c2c1a4377b63b28781b7aa100c"}, "files": ["output.json"]}, + "judgments": {"choice": {"increment": "no_new_evidence", "relation": "off_goal"}, "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04}}, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "run": {"agent_id": "analyst", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_analyst-initial", "turn_instance_id": "turn-1"}, + "status": "stale" + }, + "mode": "shadow", + "newest_receipt_contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "pending_receipts": 0, + "read_state": "observed", + "receipt_count": 1, + "rejected_receipts": 0, + "schema_version": "progress_review_status_v0", + "signal": "noul", + "stale_receipts": 1, + "status_counts": {"stale": 1} + } + }, + "markdown": "# LoopX Status\n\n- ok: `None`\n- registry: `None`\n- runtime_root: `None`\n- goals: `None`\n- runs: `None`\n- projection: 🔴 no projection envelope; freshness and coverage unknown\n- contract: ok=None, errors=None, warnings=None, checks=None\n- global_registry: available=None, ok=None, findings=None, high=None, action=None, info=None\n\n## Attention Queue\n- summary: items=None, needs_user_or_controller=None, needs_controller=None, needs_codex=None, watching_external_evidence=None, watching_monitor=None\n- `None`: status=None phase=None waiting_on=None severity=None source=None\n - project_asset_source: project_asset\n - project_asset: owner= gate= stop=\n\n## Run History\n- summary: goals=None, runs=None\n- none", + "status_review": { + "authority": "none", + "contract_revision": null, + "drift_counts": {"choice": 0, "noul": 0}, + "drift_threshold": 2, + "latest": { + "criterion_current": false, + "drift_signal": {"choice": null, "noul": null}, + "event_id": "ce36863f51b6baf9d16397ffb3e9af506b284a816f72d487e55943c1fd974d6d", + "evidence_id": "5108deb71ee1d00d8e14ad48f2ddee3dca264528a5ae802ac5a682ee11ecc0d7", + "evidence_scope": { + "coverage": "declared_file_net_change", + "criterion_binding": {"agent_id": "analyst", "contract_digest": "8159a3de1f613826a31b541cc5fe0eed6b24efa01994d740d865bcfe979fa58b", "contract_revision": 1, "criteria_sha256": "b60202ec386f3ec5e8e1ffcc90bf62eed6d821f16f1e42c17400fb389dc258de", "criterion_ids": ["analyst-initial"], "goal_id": "synthetic-managed-research", "origin": "goal_acceptance", "todo_id": "todo_analyst-initial", "todo_semantic_digest": "fcc5a4e2170eed7f6449935470ea194fc631d0c2c1a4377b63b28781b7aa100c"}, + "files": ["output.json"] + }, + "judgments": { + "choice": {"increment": "no_new_evidence", "relation": "off_goal"}, + "noul": {"behavior_change": 0.05, "evidence_increment": 0.1, "serves_acceptance": 0.04} + }, + "model": "fixture-v1", + "question_version": "scoped-progress-sentinel-v2", + "run": {"agent_id": "analyst", "generated_at": "2026-09-21T00:00:01Z", "todo_id": "todo_analyst-initial", "turn_instance_id": "turn-1"}, + "status": "stale" + }, + "mode": "shadow", + "newest_receipt_contract_revision": "f0369fccc1c2c86117197ae78432722d31ed600d0e3b6f6bd6908fd79f91d793", + "pending_receipts": 0, + "read_state": "observed", + "receipt_count": 1, + "rejected_receipts": 0, + "schema_version": "progress_review_status_v0", + "signal": "noul", + "stale_receipts": 1, + "status_counts": {"stale": 1} + } + } +} diff --git a/tests/control_plane/test_progress_review_qualification_characterization.py b/tests/control_plane/test_progress_review_qualification_characterization.py new file mode 100644 index 0000000000..16e8fa149a --- /dev/null +++ b/tests/control_plane/test_progress_review_qualification_characterization.py @@ -0,0 +1,197 @@ +"""Complete public-safe readback parity; retire crossing pins with the Python facade.""" +from collections import Counter +from copy import deepcopy +from datetime import datetime, timedelta, timezone +from difflib import unified_diff +import hashlib +import json +from pathlib import Path + +import pytest + +from test_local_delegation import demo + +from test_local_delegation import service as delegation_service +from test_progress_review_criterion_binding import bind_exact_goal +from tests.capabilities.test_progress_review import receipt + +service = delegation_service +FIXTURE = Path(__file__).with_suffix('.json') + + +def fixture_json(value, depth=0): + """Expand structural objects, keeping scalar records together for review.""" + compact = json.dumps(value, sort_keys=True, ensure_ascii=False) + children = value.values() if isinstance(value, dict) else value if isinstance(value, list) else [] + if depth >= 5 or not any(isinstance(child, (dict, list)) and child for child in children): + return compact + pad = " " * (depth + 1) + if isinstance(value, dict): + rows = [pad + json.dumps(key) + ": " + fixture_json(child, depth + 1) + for key, child in sorted(value.items())] + return "{\n" + ",\n".join(rows) + "\n" + " " * depth + "}" + return "[\n" + ",\n".join(pad + fixture_json(child, depth + 1) for child in value) + "\n" + " " * depth + "]" + + +@pytest.fixture(autouse=True) +def stable_acceptance_setup(monkeypatch): + """Pin the declared command before bootstrap hashes it; never normalize output. + + This read-only qualification test does not execute the acceptance command. + A relative synthetic argv keeps contract and Todo digests independent of + both the temporary home and the interpreter installation. + """ + original_write = demo.write + + def write(path, value): + if path.name == "bootstrap.json": + for criterion in value["document"]["criteria"]: + criterion["validation_argv"] = ["python3", "validation/acceptance.py", ".", + *criterion["validation_argv"][3:]] + original_write(path, value) + + monkeypatch.setattr(demo, "write", write) + + +def test_qualification_characterization(service, monkeypatch): + from loopx.capabilities.progress_review import receipt as receipt_module + from loopx.capabilities.progress_review.context import external_progress_review_context + from loopx.control_plane import effect_runtime + from loopx.control_plane.work_items.project_asset import attach_active_state_project_asset_fields + from loopx.presentation.renderers.status_markdown import render_status_markdown + + _, runner = service + goal = bind_exact_goal(runner) + goal['control_plane'] = {'progress_review': {'mode': 'shadow'}} + original = effect_runtime.effect_runtime_result + rows = [] + for todo, criterion in [('todo_analyst-initial', 'analyst-initial'), ('todo_reviewer-corrected', 'reviewer-corrected')]: + requirements = original('goal.acceptance.inspect', { + 'goal_id': runner.goal_id, 'runtime_root': str(runner.root), 'todo_id': todo, + 'goal_ref': {key: goal[key] for key in ('goal_instance_id',)} | {'goal_id': runner.goal_id}, + })['completion_requirements'] + binding = original('progress_review.criterion_basis', {'requirements': requirements, + 'criterion_ids': [criterion], 'acceptance': [], 'goal_id': runner.goal_id, 'agent_id': todo.split('_')[1].split('-')[0]})['binding'] + row = receipt(goal_id=runner.goal_id, evidence_scope={'criterion_binding': binding, + 'coverage': 'declared_file_net_change', 'files': ['output.json']}) + row['run'].update(todo_id=todo, agent_id=binding['agent_id']) + rows.append(row) + manual = receipt(goal_id=runner.goal_id, evidence_scope={'criterion_binding': original( + 'progress_review.criterion_basis', {'requirements': None, 'acceptance': ['Synthetic criterion']})['binding'], + 'coverage': 'declared_file_net_change', 'files': []}) + removed = deepcopy(rows[0]) + removed['evidence_scope']['criterion_binding']['criterion_ids'] = ['removed'] + unavailable = deepcopy(rows[0]) + unavailable['evidence_scope']['criterion_binding']['todo_id'] = 'todo_missing' + unavailable['run']['todo_id'] = 'todo_missing' + newer = receipt(goal_id=runner.goal_id, contract_revision='b' * 64, sequence=0) + newer['run']['generated_at'] = '2026-09-22T00:00:00Z' + scenarios = { + 'off': ({'id': runner.goal_id}, [rows[0]]), + 'empty': (goal, []), + 'repeated': (goal, [rows[0], rows[0]]), + 'multiple': (goal, rows), + 'wrong_instance': ({**goal, 'goal_instance_id': 'ginst_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'}, [rows[0]]), + 'removed': (goal, [removed]), + 'unavailable_inspection': (goal, [unavailable, rows[1]]), + 'manual_legacy': (goal, [manual, receipt(goal_id=runner.goal_id)]), + 'outside_pin': ({**goal, 'control_plane': {'progress_review': {'mode': 'assist', + 'contract_revision': rows[0]['contract_revision']}}}, [rows[0], newer]), + 'unavailable_store': (goal, None), + } + captures = {} + counts = {} + for name, (current_goal, observations) in scenarios.items(): + calls = Counter() + def tracked(method, params, **kwargs): + calls[method] += 1 + return original(method, params, **kwargs) + def load(*args): + if observations is None: + raise OSError('synthetic unavailable store') + normalized = [receipt_module.normalize_progress_review_receipt(deepcopy(row)) for row in observations] + normalized.sort(key=receipt_module.progress_review_receipt_order_key, reverse=True) + return normalized, 0 + with monkeypatch.context() as patch: + patch.setattr(effect_runtime, 'effect_runtime_result', tracked) + patch.setattr(receipt_module, 'load_progress_review_receipts', load) + context = external_progress_review_context(current_goal, runner.root) + item = {'id': runner.goal_id, 'project_asset': {}} + attach_active_state_project_asset_fields(item, external_progress_review=context) + payload = {'attention_queue': {'items': [item]}} + # The two public JSON locations must carry the complete same summary. + summary = context['summary'] if context else None + assert item.get('external_progress_review') == summary + assert item['project_asset'].get('external_progress_review') == summary + captures[name] = {'context': context, + 'status_review': item.get('external_progress_review'), + 'markdown': render_status_markdown(payload)} + counts[name] = dict(calls) + actual = fixture_json(captures) + "\n" + assert actual == FIXTURE.read_text(), ''.join(unified_diff(FIXTURE.read_text().splitlines(True), actual.splitlines(True), fromfile='frozen', tofile='unchanged-rerun')) + baseline_counts = json.loads(FIXTURE.with_name(FIXTURE.stem + '_crossings.json').read_text()) + expected = { + name: ({**({'progress_review.evidence_scope': row['progress_review.evidence_scope']} + if 'progress_review.evidence_scope' in row else {}), + 'progress_review.context.qualify': 1} + if name not in ('off', 'empty', 'unavailable_store') else {}) + for name, row in baseline_counts.items() + } + assert counts == expected + print('CROSSINGS', json.dumps(counts, sort_keys=True)) + + +def test_qualification_retains_historical_numeric_io(service): + from loopx.capabilities.progress_review.context import external_progress_review_context + from loopx.capabilities.progress_review.receipt import ( + load_progress_review_receipts, + write_progress_review_receipt, + ) + + _, runner = service + goal = {'id': runner.goal_id, 'control_plane': {'progress_review': {'mode': 'shadow'}}} + observed = receipt(goal_id=runner.goal_id, label_probability_threshold=1.0, + timing_ns={'assessment_total': 2**60 + 1}, usage={'input_tokens': 2**60 + 3}, + judgments={'choice': {'relation': 'off_goal', 'increment': 'no_new_evidence'}, + 'noul': {'behavior_change': 0.0, 'serves_acceptance': 0.0, + 'evidence_increment': 0.0}}) + write_progress_review_receipt(runner.root, runner.goal_id, observed) + loaded, _ = load_progress_review_receipts(runner.root, runner.goal_id) + result = external_progress_review_context(goal, runner.root) + assert json.dumps(result['receipts'], sort_keys=True) == json.dumps(loaded, sort_keys=True) + assert result['summary']['latest']['drift_signal'] == {'choice': True, 'noul': True} + + +def test_large_legal_receipt_batch_uses_the_existing_snapshot_transport(service): + from loopx.capabilities.progress_review.context import external_progress_review_context + from loopx.capabilities.progress_review.receipt import write_progress_review_receipt + from loopx.control_plane import effect_runtime + + _, runner = service + goal = {'id': runner.goal_id, 'control_plane': {'progress_review': {'mode': 'shadow'}}} + binding = effect_runtime.effect_runtime_result('progress_review.criterion_basis', { + 'requirements': None, 'acceptance': ['Synthetic large-batch criterion'], + })['binding'] + files = [f'dir/{index:02d}/' + 'x' * 1_017 for index in range(32)] + for index in range(256): + observed = receipt( + goal_id=runner.goal_id, + event_id=hashlib.sha256(f'event-{index}'.encode()).hexdigest(), + evidence_id=hashlib.sha256(f'evidence-{index}'.encode()).hexdigest(), + contract_revision=hashlib.sha256(f'contract-{index}'.encode()).hexdigest(), + sequence=index, + evidence_scope={ + 'criterion_binding': binding, + 'coverage': 'declared_file_net_change', + 'files': files, + }, + ) + observed['run']['generated_at'] = ( + datetime(2026, 9, 21, tzinfo=timezone.utc) + timedelta(seconds=index) + ).strftime('%Y-%m-%dT%H:%M:%SZ') + write_progress_review_receipt(runner.root, runner.goal_id, observed) + result = external_progress_review_context(goal, runner.root) + assert len(result['receipts']) == 256 + assert result['receipts'][0]['contract_revision'] == hashlib.sha256( + b'contract-255' + ).hexdigest() diff --git a/tests/control_plane/test_progress_review_qualification_characterization_crossings.json b/tests/control_plane/test_progress_review_qualification_characterization_crossings.json new file mode 100644 index 0000000000..a7679083df --- /dev/null +++ b/tests/control_plane/test_progress_review_qualification_characterization_crossings.json @@ -0,0 +1,37 @@ +{ + "empty": {}, + "manual_legacy": { + "progress_review.evidence_scope": 1 + }, + "multiple": { + "goal.acceptance.inspect": 2, + "progress_review.criterion_basis": 2, + "progress_review.evidence_scope": 2 + }, + "off": {}, + "outside_pin": { + "goal.acceptance.inspect": 1, + "progress_review.criterion_basis": 1, + "progress_review.evidence_scope": 1 + }, + "removed": { + "goal.acceptance.inspect": 1, + "progress_review.criterion_basis": 1, + "progress_review.evidence_scope": 1 + }, + "repeated": { + "goal.acceptance.inspect": 1, + "progress_review.criterion_basis": 2, + "progress_review.evidence_scope": 2 + }, + "unavailable_inspection": { + "goal.acceptance.inspect": 2, + "progress_review.criterion_basis": 1, + "progress_review.evidence_scope": 2 + }, + "unavailable_store": {}, + "wrong_instance": { + "goal.acceptance.inspect": 1, + "progress_review.evidence_scope": 1 + } +} diff --git a/tests/control_plane_ts/progress_review_evidence.test.ts b/tests/control_plane_ts/progress_review_evidence.test.ts index 02fb5e1b98..94f2272f81 100644 --- a/tests/control_plane_ts/progress_review_evidence.test.ts +++ b/tests/control_plane_ts/progress_review_evidence.test.ts @@ -1,12 +1,15 @@ import test from "node:test"; import assert from "node:assert/strict"; -import {progressReviewCriterionBasis, progressReviewEvidenceScope} from "../../loopx/control_plane/work_items/progress_review_evidence.ts"; +import { + progressReviewCriterionBasis, progressReviewEvidenceScope, qualifyProgressReviewContext, +} from "../../loopx/control_plane/work_items/progress_review_evidence.ts"; const identity = {goal_id: "goal", agent_id: "agent"}; const run = {agent_id: "agent", todo_id: "task"}; const requirements = {todo_id: "task", contract_revision: 1, contract_digest: "a".repeat(64), todo_semantic_digest: "b".repeat(64), criteria: [{id: "check", description: "Prove the declared postcondition"}]}; test("canonical criteria come from the task's current scope, not operator assertions", () => { - const result = progressReviewCriterionBasis({...identity, requirements, criterion_ids: ["check"], acceptance: ["Claim success"]}); + const result = progressReviewCriterionBasis({...identity, requirements, + criterion_ids: ["check"], acceptance: ["Claim success"]}); assert.deepEqual(result.acceptance, ["Prove the declared postcondition"]); assert.equal((result.binding as any).origin, "goal_acceptance"); assert.throws(() => progressReviewCriterionBasis({...identity, requirements, criterion_ids: ["another"]})); @@ -26,3 +29,29 @@ test("canonical criteria cannot be used for a different goal, agent or task", () {goal_id: "goal", run: {...run, agent_id: "other"}}, {goal_id: "goal", run: {}}]) assert.throws(() => progressReviewEvidenceScope({...changed, scope})); }); + +test("newest follows loader input order without reinterpreting run metadata", async () => { + const first = {contract_revision: "first", run: {generated_at: "2026-01-01"}, sequence: 0}; + const second = {contract_revision: "second", run: {generated_at: "2026-01-02"}, sequence: 99}; + for (const receipts of [[first, second], [second, first]]) { + const before = structuredClone(receipts); + const result = await qualifyProgressReviewContext({goal_id: "goal", runtime_root: "/synthetic", + receipts, contract_revision: "second"}); + assert.equal(result.newest_contract_revision, receipts[0].contract_revision); + assert.deepEqual(result.selected, [second]); + assert.deepEqual(result.selected_indices, [receipts.indexOf(second)]); + assert.equal(result.stale, 1); + assert.deepEqual(receipts, before); + } + assert.deepEqual(await qualifyProgressReviewContext({goal_id: "goal", runtime_root: "/synthetic", + receipts: [], contract_revision: null}), + {selected: [], selected_indices: [], stale: 0, newest_contract_revision: null}); +}); + +test("batch rejects malformed transport without inspecting authority", async () => { + for (const patch of [{receipts: null}, {receipts: [null]}, {goal_id: ""}, + {runtime_root: null}, {contract_revision: 1}]) { + await assert.rejects(qualifyProgressReviewContext({goal_id: "goal", runtime_root: "/synthetic", + receipts: [], contract_revision: null, ...patch})); + } +});