Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Packaged reader + production Chat HTTP, delegation workers and independent checks.
import assert from "node:assert/strict";
import {spawn} from "node:child_process";
import {once} from "node:events";
import {createInterface} from "node:readline";
import {mkdir} from "node:fs/promises";
import {resolve} from "node:path";
import {resolveTestPython} from "../../../../scripts/test-python.mjs";
import {launchBrowser, loadPlaywright, waitForHttp} from "../../../../examples/dashboard-browser-smoke-support.mjs";
process.env.LOOPX_PERSONAL_WORKSPACE_PACKAGED = "1";
const {repoRoot, outputDir, port, startServer} = await import("../../../../examples/personal-workspace-browser/fixture.mjs");
const {openWorkspacePage} = await import("../../../../examples/personal-workspace-browser/scenario-context.mjs");
const fixture = spawn(resolveTestPython({repoRoot}), ["-u", "tests/presentation/progress_review_evidence_fixture.py"], {cwd: repoRoot, stdio: ["pipe", "pipe", "pipe"]});
const exited = once(fixture, "exit");
const lines = createInterface({input: fixture.stdout});
const iterator = lines[Symbol.asyncIterator]();
let stderr = "";
fixture.stderr.on("data", chunk => {stderr += chunk;});
async function next() {const line = await iterator.next(); assert.equal(line.done, false, stderr); return JSON.parse(line.value);}
async function command(value) {fixture.stdin.write(`${value}\n`); return next();}
let server, browser, workspace;
try {
const {origin, goal_id} = await next();
server = await startServer();
const url = `http://127.0.0.1:${port}/chat/?statusUrl=/status.json`;
await waitForHttp(url);
browser = await launchBrowser(loadPlaywright().chromium);
workspace = await openWorkspacePage({newPage: options => browser.newPage({locale: "zh-CN", ...options})}, url);
const {page, api} = workspace;
await page.locator(".personal-goal-link", {hasText: "Product Release"}).click();
await mkdir(outputDir, {recursive: true});
await page.setViewportSize({width: 390, height: 844});
await page.route("**/api/chat/goal-configuration?*", async route => {
const response = await page.request.get(`${origin}/api/chat/goal-configuration?goal_id=${encodeURIComponent(goal_id)}`);
return route.fulfill({response});
});
await page.getByRole("button", {name: "Goal 设置", exact: true}).click();
await page.getByRole("button", {name: "能力中心", exact: true}).click();
await page.getByRole("button", {name: /进展评估哨兵/}).click();
const review = page.getByRole("region", {name: "审查证据读回"});
await review.getByText("绑定任务的规范验收条款", {exact: true}).waitFor();
await review.getByText("与目标的关系: off_goal", {exact: true}).waitFor();
await review.getByText("新证据增量: new_evidence", {exact: true}).waitFor();
await review.getByText("仅观察声明文件在两个检查点之间的净变化", {exact: true}).waitFor();
const coverage = review.getByText("仅观察声明文件在两个检查点之间的净变化", {exact: true});
await coverage.scrollIntoViewIfNeeded();
await review.getByText("观察范围与条款版本", {exact: true}).click();
await review.locator("code").scrollIntoViewIfNeeded();
await page.screenshot({path: resolve(outputDir, "progress-review-mixed-dimensions-mobile.png")});
assert.equal(await page.evaluate(() => document.documentElement.scrollWidth > window.innerWidth), false);
await page.setViewportSize({width: 1280, height: 960});
await review.scrollIntoViewIfNeeded();
await page.screenshot({path: resolve(outputDir, "progress-review-mixed-dimensions-desktop.png")});
await command("review-task-edit");
await review.getByRole("button", {name: "重新读取审查证据", exact: true}).click();
await review.getByText("条款或任务关联已变化,旧判断不可作为当前依据。", {exact: true}).waitFor();
await review.getByText("最新审查: stale", {exact: true}).waitFor();
await command("review-missing");
await review.getByRole("button", {name: "重新读取审查证据", exact: true}).click();
await review.getByText("尚无审查回执。", {exact: true}).waitFor();
assert.equal(await review.getByText("与目标的关系: off_goal", {exact: true}).count(), 0);
console.log("PASS: packaged scoped shadow readback, separate dimensions, changed-task withdrawal and missing evidence via production HTTP");
} catch (error) {
if (workspace) console.error({body: (await workspace.page.locator("body").innerText()).slice(-3000), errors: workspace.errors});
throw error;
} finally {
await workspace?.close(); await browser?.close(); server?.kill("SIGTERM");
fixture.stdin.end(); const [code] = await exited; lines.close(); assert.equal(code, 0, stderr);
}
15 changes: 15 additions & 0 deletions apps/presentation/dashboard/src/data/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1650,6 +1650,20 @@ export const capabilityConfigurationEditorSchema = z.object({
read_only_reason: z.string().optional(),
});

export const progressReviewObservationSchema = z.object({
mode: z.string(), read_state: z.enum(["observed", "missing", "unavailable"]), read_error: z.string().optional(),
receipt_count: z.number(), rejected_receipts: z.number(), stale_receipts: z.number(),
contract_revision: z.string().nullable(), authority: z.literal("none"),
latest: z.object({status: z.string(), event_id: z.string(), evidence_id: z.string(),
judgments: z.object({choice: z.object({relation: z.string().nullable(), increment: z.string().nullable()}).nullable(),
noul: z.object({serves_acceptance: z.number().nullable(), evidence_increment: z.number().nullable(), behavior_change: z.number().nullable()}).nullable()}),
criterion_current: z.boolean().optional(),
evidence_scope: z.object({coverage: z.literal("declared_file_net_change"), files: z.array(z.string()),
criterion_binding: z.object({origin: z.enum(["operator_study", "goal_acceptance"]), criteria_sha256: z.string(),
todo_id: z.string().optional(), contract_digest: z.string().optional(), contract_revision: z.number().optional(),
criterion_ids: z.array(z.string()).optional()})}).optional(),
}).nullable(),
});
export const capabilityConfigurationCatalogSchema = z.object({
schema_version: z.literal("capability_configuration_catalog_v0"),
capabilities: z.array(z.object({
Expand All @@ -1675,6 +1689,7 @@ export const capabilityConfigurationCatalogSchema = z.object({
effective_revision: z.string(),
}).optional(),
documentation: z.record(z.string(), z.unknown()).optional(),
observation: progressReviewObservationSchema.optional(),
context_contribution: z.object({
supported_phases: z.array(z.enum(["before_plan", "before_delegate", "after_delegate_result"])),
target: z.literal("coordinator"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import { AlertTriangle, Code2, LoaderCircle, RefreshCw } from "lucide-react";

import {
Expand Down Expand Up @@ -273,6 +273,33 @@ function CapabilityCatalog({ callbacks, catalog, goalId, notification, onApplied
{localizedSelected.capability_id === "content_ops" ? <ContentReferenceWorkbench key={goalId} /> : <CapabilityEditorStatus available={editorAvailable} t={t}
description={readOnlyReason} />}

{selected.capability_id === "progress_review" && selected.observation ? <section className="personal-capability-observation" aria-label={locale === "zh-CN" ? "审查证据读回" : "Review evidence readback"}>
<h3>{locale === "zh-CN" ? "审查证据读回" : "Review evidence readback"}</h3>
<button type="button" onClick={onApplied}>{locale === "zh-CN" ? "重新读取审查证据" : "Recheck review evidence"}</button>
<p>{selected.observation.read_state === "unavailable" ? (locale === "zh-CN" ? "审查存储不可读,当前判断未知。" : "Review storage unavailable; current judgment unknown.")
: selected.observation.read_state === "missing" ? (locale === "zh-CN" ? "尚无审查回执。" : "No review receipts yet.")
: `${selected.observation.receipt_count} ${locale === "zh-CN" ? "条已读取回执" : "loaded receipts"}`}</p>
<p>{locale === "zh-CN" ? "过期 / 拒收" : "Stale / rejected"}: {selected.observation.stale_receipts} / {selected.observation.rejected_receipts}</p>
{selected.observation.latest ? <>
<p>{locale === "zh-CN" ? "最新审查" : "Latest review"}: {selected.observation.latest.status}</p>
<p>{locale === "zh-CN" ? "与目标的关系" : "Relation to goal"}: {selected.observation.latest.judgments.choice?.relation ?? "unknown"}</p>
<p>{locale === "zh-CN" ? "新证据增量" : "Evidence increment"}: {selected.observation.latest.judgments.choice?.increment ?? "unknown"}</p>
{selected.observation.latest.evidence_scope ? <>
<p>{selected.observation.latest.evidence_scope.criterion_binding.origin === "goal_acceptance"
? (locale === "zh-CN" ? "绑定任务的规范验收条款" : "Bound to canonical task acceptance criteria")
: (locale === "zh-CN" ? "依据操作者研究条款;不证明规范验收关联" : "Operator study basis; canonical acceptance binding is unverified")}</p>
{selected.observation.latest.criterion_current === false ? <p role="alert">{locale === "zh-CN" ? "条款或任务关联已变化,旧判断不可作为当前依据。" : "Criteria or task binding changed; the old judgment is unavailable for current use."}</p> : null}
<p>{locale === "zh-CN" ? "仅观察声明文件在两个检查点之间的净变化" : "Observed only the net change of declared files between two checkpoints"}</p>
<details><summary>{locale === "zh-CN" ? "观察范围与条款版本" : "Observed scope and criterion version"}</summary>
<code>{selected.observation.latest.evidence_scope.criterion_binding.criteria_sha256}</code>
<p>{selected.observation.latest.evidence_scope.criterion_binding.criterion_ids?.join(", ")}</p>
<p>{selected.observation.latest.evidence_scope.files.join(", ")}</p>
</details>
</> : <p>{locale === "zh-CN" ? "旧回执未记录所选条款及文件覆盖范围。" : "Legacy receipt did not record selected criteria or file coverage."}</p>}
</> : null}
<p>{locale === "zh-CN" ? "此观察不证明任务完成;未命中漂移条件也不等于工作符合目标。" : "This observation does not prove completion; a drift condition not met does not establish on-goal work."}</p>
</section> : null}

{localizedSelected.capability_id === "lark_event_inbox" ? (
<section className="personal-capability-linked-setting">
<div>
Expand Down Expand Up @@ -361,18 +388,23 @@ export function GoalCapabilitySettings({ callbacks, goalId, notification, onChan
const [inspection, setInspection] = useState<GoalConfigurationInspection | null>(null);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const readGeneration = useRef(0);

function load() {
if (!goalId) return;
const generation = ++readGeneration.current;
setLoading(true);
setError(null);
// Withdraw the previous observation during a recheck; preserve navigation.
setInspection(current => current ? {...current, capability_catalog: {...current.capability_catalog,
capabilities: current.capability_catalog.capabilities.map(item => item.observation ? {...item, observation: undefined} : item)}} : null);
void fetchGoalConfiguration(goalId)
.then(setInspection)
.catch((reason: unknown) => setError(reason instanceof Error ? reason.message : t("capabilities.loadFailed")))
.finally(() => setLoading(false));
.then(value => {if (generation === readGeneration.current) setInspection(value);})
.catch((reason: unknown) => {if (generation === readGeneration.current) setError(reason instanceof Error ? reason.message : t("capabilities.loadFailed"));})
.finally(() => {if (generation === readGeneration.current) setLoading(false);});
}

useEffect(load, [goalId]);
useEffect(() => {setInspection(null); load(); return () => {readGeneration.current++;};}, [goalId]);

if (!goalId) {
return <p className="personal-capability-empty">{t("capabilities.chooseGoal")}</p>;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,7 @@ contracts; passing them does not establish live model correction, arbitrary
remote exactly-once effects or production recovery time. Diagnostics deployment,
overhead and long-running non-interference need separate evidence.

The RFCs specify three remaining integration gaps in these partially implemented
paths:
The RFCs track three integration paths with separate implementation checkpoints:

1. [Current-use checks for explicit delegation ancestry](rfcs/shared-goal-alignment-and-governed-amendment-v0.md#38-invalid-evidence-and-affected-consumers):
refuse new dependent use when its declared basis is unavailable, preserving
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Note 能保留尝试过的路径与下一步,但 summary 仍由发送者提供

这些测试设计包含失败、陈旧来源、输入改变和 effect 中断等负例。Fixture provider 与一次性本地状态验证各自合同;通过不等于真实模型纠错、任意远端 exactly-once 或生产恢复时间合格。诊断部署、开销与长时间 non-interference 需要独立证据。

RFC 明确了这些部分实现路径中仍需补齐的三项整合缺口:
RFC 将以下三项整合路径分别记录为独立 checkpoint:

1. [显式委派来源链的当前使用检查](rfcs/shared-goal-alignment-and-governed-amendment-v0.zh-CN.md#38-失效证据与受影响消费者):声明依据不可用时拒绝新的依赖使用,保留历史完成,限制遍历与验证成本。
2. [原任务修复与复验](rfcs/composable-state-machines-recovery-verification-v0.zh-CN.md#基于源码的实现顺序):把独立失败依据传入可操作恢复路径,按当前 criterion 复验,再对账原效果。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,15 +304,13 @@ lease/GoalRef fence, followed by M3's actual delivery callers.

### Source-grounded implementation sequence

These are unimplemented composition deltas within existing M2/M3 and roadmap
R2/R3/R4, not new milestones. Source owners were rechecked at
`44931b6d22a50b949d43354e6ea498fb6b68d231`.
The table tracks independent composition boundaries within M2/M3 and roadmap R2/R3/R4. Candidate checkpoints do not establish installed behavior or full M2 acceptance. The source baseline is `233cc76fd22760947d73e1501032b8b77e28148b`.

| Bounded outcome | Existing entry and owner | Current gap and decisive exit |
| Bounded outcome | Existing entry and owner | Current checkpoint and decisive exit |
| --- | --- | --- |
| Unavailable declared ancestry prevents new dependent use | `Delegations._read_current/start`, `delegation_results.require_dependencies/adoption_evidence`, `delegation.ts` | Alignment §3.8: source → A → B with A input invalidated must refuse current dependent use even if A output is unchanged. Qualify real read/start/adopt/settlement and packaged evidence readback; keep historical completion. |
| Independent check failure reaches an actionable original-task recovery journey | `executor._task_validation_stage`, `ValidatedTurnReceipt`, canonical `turn_loop_controller_contract_v0.json`, `turn_journal.ts` | Preserve qualified failure scope and repair/replan detail; complete bounded repair or verifier-only retry, current validation and original-effect settlement. Host assertions cannot become trusted validation. |
| Optional semantic review explains its evidence and coverage | Existing progress-review receipt/loader and canonical Goal acceptance inspection | Bind a selected criterion and evidence coverage in shadow readback; show missing/stale basis and separate judgment dimensions. Model quality and intervention qualify separately. |
| Shadow review explains criteria and coverage | progress-review receipt/context, `progress_review_evidence.ts`, canonical acceptance inspect | Select current canonical task criteria; exact GoalRef preserves instance identity, while changed tasks or recreated instances withdraw old judgments. Independent older cores retain only the legacy manual-study shape; canonical scope never downgrades. Show separate dimensions, declared-file net change, missing evidence and unreadable storage. Default off and existing assist trigger rules retain their semantics. |

For the second outcome, `_task_validation_stage` already saves the independent
result and blocks settlement. `ValidatedTurnReceipt` omits that validation's
Expand Down Expand Up @@ -345,6 +343,15 @@ complete the journey. Bound repeated verification and source-chain traversal
using measured workloads. Roll back code through its owner while retaining
receipts, completed effects and unresolved recovery obligations.

An optional `acceptance_scope` refers only to registry/runtime paths inside the
selected workspace. Criteria come from the current owner; private commands and
paths stay out of model questions. Hashes, versions and declared coverage are
observations, not proof of model correctness, a durable business checkpoint or
whole-task completion. Existing team evidence and capability settings carry App
readback, and CLI/MCP share the owner; no Lark-specific protocol is introduced.
Live correction, remote exactly-once, takeover, observer quality and intervention
retain their existing RFC acceptance owners.

## 12. Open decisions

The first M2 implementation must choose its smallest sufficient exploration
Expand Down
Loading
Loading