diff --git a/apps/presentation/dashboard/smoke/goal-amendment-browser-smoke.mjs b/apps/presentation/dashboard/smoke/goal-amendment-browser-smoke.mjs new file mode 100644 index 0000000000..a34a634d19 --- /dev/null +++ b/apps/presentation/dashboard/smoke/goal-amendment-browser-smoke.mjs @@ -0,0 +1,207 @@ +import assert from 'node:assert/strict'; +import {spawn, spawnSync} from 'node:child_process'; +import {mkdtemp, mkdir, writeFile, rm} from 'node:fs/promises'; +import {tmpdir} from 'node:os'; +import {resolve} from 'node:path'; +import {createRequire} from 'node:module'; +import {createServer} from 'node:net'; +import {createInterface} from 'node:readline'; +import {testPythonCommand} from '../../../../scripts/test-python.mjs'; + +const repo = resolve(import.meta.dirname, '../../../..'); +const {chromium} = createRequire(resolve(repo, 'apps/presentation/dashboard/package.json'))('playwright'); +const output = resolve(repo, '.local/goal-amendment-browser'); +await mkdir(output, {recursive: true}); +const root = await mkdtemp(resolve(tmpdir(), 'loopx-amendment-browser-')); +const reservation = createServer(); +await new Promise(accept => reservation.listen(0, '127.0.0.1', accept)); +const port = reservation.address().port; +await new Promise(accept => reservation.close(accept)); +// Real project-register, fresh-authority owner, packaged assets and HTTP server. +// No status or provider fixture replaces a production response. Generic runtime +// reads remain unavailable for this explicit source-session profile. +const backend = testPythonCommand(String.raw` +import contextlib, io, json, signal, sys, tempfile +from pathlib import Path +from loopx.cli import main +from loopx.chat_server import serve_chat +from loopx.control_plane.effect_runtime import restart_effect_runtime +root = Path(sys.argv[1]); tempfile.tempdir = str(root) +registry = root / 'project/.loopx/registry.json'; runtime = root / 'runtime' +with contextlib.redirect_stdout(io.StringIO()) as output: + code = main(['--registry', str(registry), '--runtime-root', str(runtime), '--format', 'json', 'project', 'register', + '--project-id', 'governed-work', '--project-kind', 'work', '--knowledge-root', str(root / 'project'), + '--goal-id', 'amendment-goal', '--objective', 'Verify the public contract', '--non-goal', 'No remote changes', + '--acceptance', 'Independent validation passes', '--next-effect', 'Inspect the current contract', + '--stop-condition', 'Stop when the Goal is inactive', '--goal-instance-profile', 'source_session_v1', + '--operation-id', 'create-goal', '--registered-agent', 'agent-a', '--registered-agent', 'agent-b', '--canonical-storage', 'sqlite']) +assert code == 0, output.getvalue() +created = json.loads(output.getvalue()) +assert created['storage_initialization']['authority_initialized'] is True +(root / 'registration.json').write_text(json.dumps(created)) +# Synthetic typed observations, not model runs. The real replan reader derives +# the obligation; the fixture never supplies an arbitrary accepted obligation id. +runs = runtime / 'goals/amendment-goal/runs/index.jsonl' +runs.parent.mkdir(parents=True, exist_ok=True) +rows = [{'generated_at': f'2026-09-01T00:0{i}:00+00:00', 'goal_id': 'amendment-goal', + 'goal_ref': created['goal_ref'], 'agent_id': 'agent-a', 'classification': 'bounded_replan_progress', + 'turn_instance_id': f'synthetic-{i}', 'progress_observation': {'schema_version': 'typed_progress_observation_v0', + 'result_class': 'blocked', 'surface_id': 'contract', 'hypothesis_id': 'synthetic-gap', + 'probe_kind': 'check', 'evidence_ids': ['synthetic-evidence']}} for i in range(2)] +runs.write_text(''.join(json.dumps(row) + '\n' for row in rows)) +signal.signal(signal.SIGTERM, lambda *_: sys.exit(0)) +try: + serve_chat(registry_path=registry, runtime_root_override=runtime, scan_roots=[root / 'project'], port=int(sys.argv[2]), + goal_id='amendment-goal', codex_bin='missing-synthetic-codex', claude_bin='missing-synthetic-claude', kiro_cli_bin='missing-synthetic-kiro') +finally: + restart_effect_runtime() +`); +const child = spawn(backend.command, [...backend.args, root, String(port)], {cwd: root, env: {...process.env, LOOPX_USAGE_PING: '0', TMPDIR: root, + NODE_OPTIONS: `${process.env.NODE_OPTIONS ?? ''} --experimental-sqlite`}, stdio: ['ignore', 'pipe', 'pipe']}); +const lines = createInterface({input: child.stdout}); +let serverLog = ''; +child.stderr.on('data', chunk => {serverLog += String(chunk);}); +let browser, page; +try { + await new Promise((accept, reject) => { + const timer = setTimeout(() => reject(Error('Isolated server startup timed out')), 120_000); + child.once('exit', code => {clearTimeout(timer);reject(Error(`Server exited ${code}: ${serverLog}`));}); + lines.on('line', line => {if (line.startsWith('Serving LoopX Chat')) {clearTimeout(timer);accept();}}); + }); + browser = await chromium.launch({headless: true}); + page = await browser.newPage({viewport: {width: 1512, height: 1600}}); + await page.addInitScript(() => localStorage.setItem('loopx-pw-locale', 'zh-CN')); + const exceptions = []; page.on('pageerror', error => exceptions.push(error.message)); + const applies = []; let drop = true; + await page.route('**/api/chat/goal-amendment', async route => { + const request = route.request(); + if (request.method() !== 'POST' || !request.postDataJSON().execute) return route.continue(); + applies.push(request.postDataJSON()); + const response = await route.fetch(); + assert.equal((await response.json()).ok, true); + if (drop) {drop = false;await route.abort();} + else await route.fulfill({response}); + }); + const url = `http://127.0.0.1:${port}/chat/`; + await page.goto(url); + async function enter() { + await page.locator('.personal-goal-link', {hasText: 'amendment-goal'}).click(); + await page.getByRole('button', {name: 'Goal 设置', exact: true}).click(); + await page.getByRole('button', {name: '任务所有权', exact: true}).click(); + await page.getByText('目标约束与受治理变更', {exact: true}).click(); + await page.locator('.personal-cadence-readback strong').filter({hasText: 'Verify the public contract'}).waitFor(); + } + await enter(); + const panel = page.locator('details').filter({has: page.locator('summary', {hasText: '目标约束与受治理变更'})}); + await panel.getByRole('button', {name: '检查本次授权', exact: true}).click(); + await panel.getByRole('alert').waitFor(); // Required template/terminal fields cannot be omitted. + for (const [label, value] of [ + ['允许新增的工作', 'Verify the public contract'], ['动作类型', 'validate'], ['稳定目标标识', 'contract-check'], + ['写入范围,每行一项', 'tests/**'], ['所需能力,每行一项', 'filesystem_write'], ['终止条件,每行一项', 'Accepted result returned'], + ]) await panel.getByLabel(label, {exact: true}).fill(value); + await panel.getByRole('button', {name: '检查本次授权', exact: true}).click(); + await panel.getByRole('button', {name: '确认固定目标与授权', exact: true}).waitFor(); + assert.equal(applies.length, 0); + await panel.screenshot({path: resolve(output, 'desktop-preview.png')}); + await page.reload();await enter(); + assert.equal(await panel.getByLabel('允许新增的工作', {exact: true}).inputValue(), 'Verify the public contract'); + await panel.getByRole('button', {name: '确认固定目标与授权', exact: true}).click(); + await panel.getByRole('button', {name: '查阅原操作回执', exact: true}).waitFor(); + assert.equal(applies.length, 1); + await page.waitForFunction(() => [...document.querySelectorAll('button')].find(button => button.textContent === '查阅原操作回执')?.disabled === false); + await panel.screenshot({path: resolve(output, 'unknown-response.png')}); + await page.reload();await enter(); + await panel.getByRole('button', {name: '重试原操作', exact: true}).click(); + await panel.getByText('已固定目标约束 · 版本 1', {exact: true}).waitFor(); + await panel.getByRole('button', {name: '查阅原操作回执', exact: true}).waitFor({state: 'detached'}); + assert.equal(applies.length, 2);assert.deepEqual(applies[0], applies[1]); + await panel.getByText('agent-a · 尚未确认当前版本', {exact: true}).waitFor(); + function advancePeers(phase) { + const command = testPythonCommand(String.raw` +import contextlib, io, json, sys +from pathlib import Path +from loopx.cli import main +root = Path(sys.argv[1]); phase = sys.argv[2] +ref = json.loads((root / 'registration.json').read_text())['goal_ref'] +def call(operation, parameters=None, agent='agent-a', operation_id=None): + args = ['--registry', str(root / 'project/.loopx/registry.json'), '--format', 'json', + 'goal-amendment-proposal', '--governed', operation, '--goal-id', ref['goal_id'], + '--goal-instance-id', ref['goal_instance_id'], '--agent-id', agent] + if parameters is not None: + path = root / 'request.json'; path.write_text(json.dumps(parameters)); args += ['--request-json', str(path)] + if operation_id: args += ['--operation-id', operation_id, '--execute'] + with contextlib.redirect_stdout(io.StringIO()) as output: code = main(args) + assert code == 0, output.getvalue() + return json.loads(output.getvalue()) +def basis(view): + return {'base_goal_revision': view['intent']['goal_revision'], 'base_intent_digest': view['intent']['intent_digest'], 'base_work_digest': view['work_digest']} +view = call('inspect') +if phase == 'commit': + call('adopt', basis(view), operation_id='agent-initial-adoption') + proposal = call('propose', {**basis(view), 'proposal_id': 'contract-gap', 'template_id': 'owner-work', + 'evidence_refs': ['evidence:synthetic-gap'], 'replan_obligation_id': view['replan_observations'][0]['obligation_id']}, operation_id='agent-proposal') + result = call('commit', {'proposal_id': 'contract-gap', 'proposal_digest': proposal['proposal']['digest']}, operation_id='agent-commit') + assert result['replan_settlement'] == 'pending' +else: + view = call('adopt', basis(view), operation_id='agent-current-adoption')['current_projection'] + result = call('claim', {**basis(view), 'todo_id': view['intent']['created_todo_ids'][0], + 'expected_provider_revision': view['provider_revision'], 'lease_request': { + 'idempotency_key': 'new-work', 'expected_version': None, 'ttl_seconds': 300}}, operation_id='agent-claim') + call('adopt', basis(result['current_projection']), agent='agent-b', operation_id='peer-current-adoption') +print(json.dumps(call('inspect'))) +`); + const result = spawnSync(command.command, [...command.args, root, phase], {cwd: root, encoding: 'utf8', timeout: 60_000, + env: {...process.env, LOOPX_USAGE_PING: '0', TMPDIR: root, NODE_OPTIONS: `${process.env.NODE_OPTIONS ?? ''} --experimental-sqlite`}}); + assert.equal(result.status, 0, result.stdout + result.stderr); + return JSON.parse(result.stdout); + } + const committed = advancePeers('commit'); + assert.equal(committed.intent.goal_revision, 2); + await panel.getByRole('button', {name: '读取当前结果', exact: true}).click(); + await panel.getByText('已创建工作 1 项;提案 1 项。', {exact: true}).waitFor(); + await panel.getByText('agent-a · 仍待后续工作采纳与验证', {exact: true}).waitFor(); + const adopted = advancePeers('claim'); + assert.equal(adopted.replan_observations[0].state, 'settled'); + await panel.getByRole('button', {name: '读取当前结果', exact: true}).click(); + await panel.getByText('agent-b · 当前版本确认已记录', {exact: true}).waitFor(); + await panel.getByText('agent-a · 对应 replan 已由原有规则验证', {exact: true}).waitFor(); + await page.setViewportSize({width: 390, height: 1100}); + await panel.getByRole('button', {name: '读取当前结果', exact: true}).focus();await page.keyboard.press('Enter'); + await page.waitForFunction(() => [...document.querySelectorAll('button')].find(button => button.textContent === '读取当前结果')?.disabled === false); + await panel.getByText('已固定目标约束 · 版本 2', {exact: true}).waitFor(); + await panel.screenshot({path: resolve(output, 'mobile-readback.png')}); + assert.equal(await page.evaluate(() => document.documentElement.scrollWidth > innerWidth), false); + const view = await fetch(`http://127.0.0.1:${port}/api/chat/goal-amendment?goal_id=amendment-goal`).then(r => r.json()); + assert.equal(view.intent.goal_revision, 2);assert.equal(view.execution_authority_granted, false); + assert.equal(view.intent.created_todo_ids.length, 1); + const query = new URLSearchParams({goal_id: applies[0].goal_id, goal_instance_id: applies[0].goal_instance_id, operation_id: applies[0].operation_id}); + const receipt = await fetch(`http://127.0.0.1:${port}/api/chat/goal-amendment?${query}`).then(r => r.json()); + assert.equal(receipt.status, 'found');assert.equal(receipt.historical_only, true); + assert.equal(receipt.receipts[0].result.intent_digest, applies[0].preview_digest); + await page.close(); + page = await browser.newPage({viewport: {width: 1512, height: 1600}}); + await page.addInitScript(() => localStorage.setItem('loopx-pw-locale', 'en')); + await page.goto(url); + await page.locator('.personal-goal-link', {hasText: 'amendment-goal'}).click(); + await page.getByRole('button', {name: 'Goal settings', exact: true}).click(); + await page.getByRole('button', {name: 'Task ownership', exact: true}).click(); + await page.getByText('Goal commitments and governed changes', {exact: true}).click(); + await page.getByText('Goal commitments frozen · revision 2', {exact: true}).waitFor(); + await page.getByText('agent-b · Current basis acknowledged', {exact: true}).waitFor(); + await page.locator('details').filter({has: page.locator('summary', {hasText: 'Goal commitments and governed changes'})}).screenshot({path: resolve(output, 'english-readback.png')}); + assert.deepEqual(exceptions, []); + await writeFile(resolve(output, 'result.json'), JSON.stringify({ok: true, scope: 'Packaged Chat; real project registration, SQLite initialization, metadata discovery and amendment HTTP; no model calls', + generic_runtime_qualified: false, same_operation_replayed: true, peer_cli_commit_and_adoption: true, synthetic_progress_observations: true, view, receipt}, null, 2)); + console.log('Governed amendment browser passed: native registration, packaged UI, invalid input, preview/reload, dropped response, original replay, durable receipt, narrow viewport.'); +} catch (error) { + if (page) { + await writeFile(resolve(output, 'failure.txt'), await page.locator('body').innerText()); + await page.screenshot({path: resolve(output, 'failure.png'), fullPage: true}); + } + throw error; +} finally { + await browser?.close();child.kill('SIGTERM');lines.close(); + await new Promise(accept => child.exitCode !== null ? accept() : child.once('exit', accept)); + await writeFile(resolve(output, 'server.log'), serverLog); + await rm(root, {recursive: true, force: true}); +} diff --git a/apps/presentation/dashboard/src/data/goal-amendment.ts b/apps/presentation/dashboard/src/data/goal-amendment.ts new file mode 100644 index 0000000000..50f3c743ba --- /dev/null +++ b/apps/presentation/dashboard/src/data/goal-amendment.ts @@ -0,0 +1,48 @@ +import {z} from "zod"; +import {requestJson} from "./chat"; + +const goalRef = z.object({goal_id: z.string(), goal_instance_id: z.string()}); +const permissionScope = z.object({write_scopes: z.array(z.string()), capabilities: z.array(z.string())}); +const commitments = z.object({objective: z.string(), non_goals: z.array(z.string()), acceptance: z.array(z.string()), stop_condition: z.string()}); +const template = z.object({id: z.string(), digest: z.string(), todo: z.object({text: z.string(), action_kind: z.string(), target_key: z.string(), + required_write_scopes: z.array(z.string()), required_capabilities: z.array(z.string())})}); +export const amendmentViewSchema = z.object({ + ok: z.literal(true), schema_version: z.literal("goal_amendment_result_v0"), status: z.literal("loaded"), + goal_ref: goalRef, provider_revision: z.string(), work_digest: z.string(), current_lifetime: z.boolean(), + source_commitments: commitments.nullable(), execution_authority_granted: z.literal(false), + mutation_readiness: z.object({kind: z.enum(["admit", "reject"]), code: z.string().optional()}), + intent: z.object({goal_ref: goalRef, goal_revision: z.number().int().positive(), intent_digest: z.string(), + root_intent: z.object({permission_scope: permissionScope, objective: z.string(), non_goals: z.array(z.string()), acceptance: z.array(z.string()), stop_conditions: z.array(z.string()), terminal_conditions: z.array(z.string())}), + policy: z.object({eligible_agents: z.array(z.string()), templates: z.array(template), max_created_todos: z.number().int()}), + created_todo_ids: z.array(z.string()), proposals: z.array(z.object({proposal_id: z.string(), proposer_agent_id: z.string(), template_id: z.string(), base_goal_revision: z.number(), replan_obligation_id: z.string()})), + }).nullable(), + peer_adoption: z.array(z.object({agent_id: z.string(), based_on_goal_revision: z.number().nullable(), current: z.boolean()})), + replan_observations: z.array(z.object({obligation_id: z.string(), agent_id: z.string(), state: z.enum(["pending", "settled"])})), +}); +export type AmendmentView = z.infer; +const rootParametersSchema = z.object({expected_provider_revision: z.string(), permission_scope: permissionScope, + terminal_conditions: z.array(z.string()), policy: z.object({out_of_policy: z.literal("reject"), eligible_agents: z.array(z.string()), + max_created_todos: z.number().int(), templates: z.array(template.omit({digest: true}))})}); +export const rootRequestSchema = z.object({goal_id: z.string(), goal_instance_id: z.string(), operation_id: z.string(), + parameters: rootParametersSchema, preview_digest: z.string().optional()}); +export const savedRootRequestSchema = z.object({request: rootRequestSchema.extend({preview_digest: z.string().regex(/^[a-f0-9]{64}$/)}), attempted: z.boolean()}); +export type RootRequest = z.infer; +const resultSchema = z.object({ok: z.boolean(), schema_version: z.literal("goal_amendment_result_v0"), status: z.string(), + intent_digest: z.string().optional(), reason: z.string().optional(), reason_code: z.string().optional(), execution_authority_granted: z.literal(false), + current_projection: amendmentViewSchema.omit({ok: true}).optional()}); +export async function fetchAmendment(goalId: string): Promise { + const payload = await requestJson(`/api/chat/goal-amendment?${new URLSearchParams({goal_id: goalId})}`); + const outcome = resultSchema.parse(payload); + if (!outcome.ok) throw new Error(outcome.reason ?? "Goal amendment state is unavailable."); + return amendmentViewSchema.parse(payload); +} +export async function configureAmendment(request: RootRequest, execute: boolean) { + return resultSchema.parse(await requestJson("/api/chat/goal-amendment", {method: "POST", body: JSON.stringify({...request, execute})})); +} + +export async function fetchAmendmentReceipt(request: RootRequest) { + const query = new URLSearchParams({goal_id: request.goal_id, goal_instance_id: request.goal_instance_id, operation_id: request.operation_id}); + return z.object({ok: z.boolean(), status: z.string(), historical_only: z.literal(true), execution_authority_granted: z.literal(false), + receipts: z.array(z.object({operation_id: z.string(), result: z.object({goal_ref: goalRef, intent_digest: z.string().optional()})})).optional() + }).parse(await requestJson(`/api/chat/goal-amendment?${query}`)); +} diff --git a/apps/presentation/dashboard/src/features/personal-workspace/goal-amendment-settings.tsx b/apps/presentation/dashboard/src/features/personal-workspace/goal-amendment-settings.tsx new file mode 100644 index 0000000000..448b5235c2 --- /dev/null +++ b/apps/presentation/dashboard/src/features/personal-workspace/goal-amendment-settings.tsx @@ -0,0 +1,170 @@ +import {useEffect, useRef, useState} from "react"; +import {configureAmendment, fetchAmendment, fetchAmendmentReceipt, savedRootRequestSchema, type AmendmentView, type RootRequest} from "../../data/goal-amendment"; +import {useWorkspaceI18n} from "./i18n"; + +/** Operator setup and readback only. Peer adoption is recorded by the peer's + * own operation; this page never sends an Agent id to impersonate it. */ +export function GoalAmendmentSettings({goalId}: {goalId: string}) { + const {locale} = useWorkspaceI18n();const zh = locale === "zh-CN"; + const [open, setOpen] = useState(false), [view, setView] = useState(null); + const [busy, setBusy] = useState(false), [error, setError] = useState(null); + const [text, setText] = useState(""), [action, setAction] = useState(""), [target, setTarget] = useState(""); + const [writes, setWrites] = useState(""), [capabilities, setCapabilities] = useState(""), [terminal, setTerminal] = useState(""); + const [agents, setAgents] = useState([]), [limit, setLimit] = useState(1); + const [preview, setPreview] = useState(null); + const generation = useRef(0), inFlight = useRef(null); + const [attempted, setAttempted] = useState(false); + const [invalidSaved, setInvalidSaved] = useState(false); + const key = `loopx-root-intent:${goalId}`; + const lines = (value: string) => value.split("\n").map(v => v.trim()).filter(Boolean); + const wording = (cn: string, en: string) => zh ? cn : en; + + async function read() { + if (inFlight.current === generation.current) return; + const current = generation.current;inFlight.current = current;setBusy(true);setError(null); + try { + const value = await fetchAmendment(goalId); + if (current !== generation.current) return; + setView(value);setAgents(value.peer_adoption.map(row => row.agent_id)); + setInvalidSaved(true); + const raw = sessionStorage.getItem(key); + if (raw) { + const parsed = savedRootRequestSchema.safeParse(JSON.parse(raw)); + if (!parsed.success || parsed.data.request.goal_id !== goalId) {setInvalidSaved(true);throw new Error(wording("保存的操作信息不完整,无法安全恢复或创建新操作。", "Saved operation details are incomplete; recovery or a new operation cannot safely proceed."));} + const saved = parsed.data; + if (saved.request?.goal_id === goalId && typeof saved.request.preview_digest === "string") { + setPreview(saved.request);setAttempted(saved.attempted === true); + const parameters = saved.request.parameters, work = parameters.policy.templates[0]?.todo; + setText(work?.text ?? "");setAction(work?.action_kind ?? "");setTarget(work?.target_key ?? ""); + setWrites(parameters.permission_scope.write_scopes.join("\n"));setCapabilities(parameters.permission_scope.capabilities.join("\n")); + setTerminal(parameters.terminal_conditions.join("\n"));setAgents(parameters.policy.eligible_agents);setLimit(parameters.policy.max_created_todos); + } + } + setInvalidSaved(false); + } catch (cause) { + if (current === generation.current) setError(cause instanceof Error ? cause.message : wording("无法读取目标变更状态。", "Goal amendment state is unavailable.")); + } finally {if (inFlight.current === current) inFlight.current = null;if (current === generation.current) setBusy(false);} + } + useEffect(() => { + generation.current++;setView(null);setPreview(null);setAttempted(false);setInvalidSaved(false);setError(null); + if (open) void read(); + return () => {generation.current++;}; + // The target, not a language toggle, owns this observation. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [goalId, open]); + + function edit(change: () => void) { + if (attempted || invalidSaved) return; + change();setPreview(null);sessionStorage.removeItem(key);setError(null); + } + async function submit(apply: boolean) { + if (invalidSaved || inFlight.current === generation.current || !view || apply && !preview) return; + const token = generation.current;inFlight.current = token;setBusy(true);setError(null); + const request: RootRequest = apply ? preview! : {goal_id: goalId, goal_instance_id: view.goal_ref.goal_instance_id, + operation_id: `root:${crypto.randomUUID()}`, parameters: {expected_provider_revision: view.provider_revision, + permission_scope: {write_scopes: lines(writes), capabilities: lines(capabilities)}, terminal_conditions: lines(terminal), + policy: {out_of_policy: "reject", eligible_agents: agents, max_created_todos: limit, + templates: [{id: "owner-work", todo: {text: text.trim(), action_kind: action.trim(), target_key: target.trim(), + required_write_scopes: lines(writes), required_capabilities: lines(capabilities)}}]}}}; + try { + if (apply) { + sessionStorage.setItem(key, JSON.stringify({request, attempted: true}));setAttempted(true); + } + const result = await configureAmendment(request, apply); + if (token !== generation.current) return; + if (!result.ok || !result.intent_digest) { + if (result.reason_code === "goal_amendment_needs_rebase") { + // The pinned old provider basis cannot become current again. This + // definitive refusal permits a fresh preview; unknown outcomes do not. + sessionStorage.removeItem(key);setPreview(null);setAttempted(false); + const fresh = await fetchAmendment(goalId); + if (token === generation.current) setView(fresh); + } + throw new Error(result.reason ?? wording("配置未被接纳。", "Configuration was not admitted.")); + } + if (!apply) { + const saved = {...request, preview_digest: result.intent_digest}; + sessionStorage.setItem(key, JSON.stringify({request: saved, attempted: false}));setPreview(saved);setAttempted(false); + } else { + sessionStorage.removeItem(key);setPreview(null);setAttempted(false); + // Read the current owner state even when the write was historical replay. + const fresh = await fetchAmendment(goalId); + if (token === generation.current) setView(fresh); + } + } catch (cause) { + if (token === generation.current) setError(cause instanceof Error ? cause.message : wording("响应未知,请读取当前结果或重试原操作。", "Response unknown; read back or retry the original operation.")); + } finally {if (inFlight.current === token) inFlight.current = null;if (token === generation.current) setBusy(false);} + } + + async function recover() { + if (!preview || inFlight.current === generation.current) return; + const token = generation.current;inFlight.current = token;setBusy(true);setError(null); + try { + const found = await fetchAmendmentReceipt(preview); + if (token !== generation.current) return; + const receipt = found.receipts?.[0]; + if (found.status !== "found" || receipt?.operation_id !== preview.operation_id || receipt.result.intent_digest !== preview.preview_digest || receipt.result.goal_ref.goal_id !== preview.goal_id || receipt.result.goal_ref.goal_instance_id !== preview.goal_instance_id) { + throw new Error(wording("暂未确认原操作回执;不能据此断言没有提交。请重试原操作。", "The original receipt is not confirmed; absence does not prove no commit. Retry the original operation.")); + } + sessionStorage.removeItem(key);setPreview(null);setAttempted(false); + const fresh = await fetchAmendment(goalId); + if (token === generation.current) setView(fresh); + } catch (cause) {if (token === generation.current) setError(cause instanceof Error ? cause.message : "Readback failed");} + finally {if (inFlight.current === token) inFlight.current = null;if (token === generation.current) setBusy(false);} + } + + return
setOpen(e.currentTarget.open)}> + {wording("目标约束与受治理变更", "Goal commitments and governed changes")} + {open ?
+

{wording("先固定原始目标和允许的工作模板。Agent 可在授权范围内提出变更;变更记录不代表工作已执行或目标已完成。", "Freeze the original Goal and permitted work template. Agents may propose changes within this policy; a change record does not prove execution or Goal completion.")}

+ {view?.source_commitments ?
+ {view.source_commitments.objective} +

{wording("不包含:", "Excluded: ")}{view.source_commitments.non_goals.join("; ")}

+
    {view.source_commitments.acceptance.map(value =>
  • {value}
  • )}
+

{view.source_commitments.stop_condition}

+
: null} + {view && (!view.current_lifetime || !view.source_commitments || view.mutation_readiness.kind !== "admit") ?

+ {wording("当前目标实例或原始声明不可用于初始化;请由目标生命周期负责人处理。历史结果不会授予当前执行权限。", "The current Goal lifetime or original declaration cannot be used for initialization. Its lifecycle owner must resolve this; historical results grant no current execution authority.")} +

: null} + {view?.intent ? <> +

{wording(`已固定目标约束 · 版本 ${view.intent.goal_revision}`, `Goal commitments frozen · revision ${view.intent.goal_revision}`)}

+
    {view.intent.policy.templates.map(row =>
  • {row.todo.text}
  • )}
+

{wording("写入范围:", "Write scopes: ")}{view.intent.root_intent.permission_scope.write_scopes.join(", ") || wording("无", "None")}

+

{wording("所需能力:", "Required capabilities: ")}{view.intent.root_intent.permission_scope.capabilities.join(", ") || wording("无", "None")}

+

{wording("终止条件:", "Terminal conditions: ")}{view.intent.root_intent.terminal_conditions.join("; ")}

+

{wording("Agent 基线确认", "Agent basis acknowledgements")}

+
    {view.peer_adoption.map(row =>
  • {row.agent_id} · {row.current ? wording("当前版本确认已记录", "Current basis acknowledged") : wording("尚未确认当前版本", "Current basis not acknowledged")}
  • )}
+

{wording(`已创建工作 ${view.intent.created_todo_ids.length} 项;提案 ${view.intent.proposals.length} 项。`, `${view.intent.created_todo_ids.length} work items created; ${view.intent.proposals.length} proposals.`)}

+
    {view.replan_observations.map(row =>
  • {row.agent_id} · {row.state === "settled" ? wording("对应 replan 已由原有规则验证", "Replan validated by its existing owner") : wording("仍待后续工作采纳与验证", "Work adoption and validation remain pending")}
  • )}
+ : view?.current_lifetime && view.source_commitments && view.mutation_readiness.kind === "admit" ? <> +
+