Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
81f1e80
Merge reviewed local Goal amendment prerequisite
songoow Oct 12, 2026
cc17e90
Merge reviewed request publication prerequisite
songoow Oct 12, 2026
b80418f
Merge corrected exact-profile prerequisite coverage
songoow Oct 13, 2026
fbc5bbe
Merge current main before request recovery qualification
songoow Oct 13, 2026
bdf603e
feat(collaboration): reconcile request revisions with governed Goal e…
songoow Oct 13, 2026
74ff13b
test(collaboration): qualify request cancellation and revision recovery
songoow Oct 13, 2026
c0b68cb
docs(collaboration): describe source request control and recovery
songoow Oct 13, 2026
e95bf7d
Merge current main for final request control delivery
songoow Oct 13, 2026
1e60496
refactor(collaboration): type source and receipt adapters
songoow Oct 13, 2026
41b22a5
fix(collaboration): return interim progress for each request revision
songoow Oct 13, 2026
1675f57
fix(collaboration): scope revision guidance to controlled request reads
songoow Oct 13, 2026
3a8a6b5
Merge current main and refresh source registry I/O census
songoow Oct 13, 2026
6637b26
Merge current main for PR #6357 conflict reconciliation
songoow Oct 13, 2026
60cdcc0
Merge latest main and regenerate registry I/O census for request corr…
songoow Oct 13, 2026
6c5ee46
Merge main and regenerate registry I/O census for request controls
songoow Oct 14, 2026
9df4a0c
Preserve injected replan configuration across request amendment callers
songoow Oct 14, 2026
75002e1
Verify complete request retention before sending corrections
songoow Oct 14, 2026
4746c41
Group request-specific source reader assertions with collaboration
songoow Oct 14, 2026
2ee697f
Merge current main into request correction recovery
songoow Oct 14, 2026
6a9dca3
test(digest): pin request_control as a canonical digest consumer
songoow Oct 14, 2026
a46eabd
fix(dashboard): use defined workspace tokens in request controls
songoow Oct 14, 2026
d88a36c
Merge current main into request correction recovery
songoow Oct 14, 2026
9c61426
Merge current main into request correction recovery
songoow Oct 14, 2026
40c8bc4
Merge current main into request correction recovery
songoow Oct 14, 2026
d93f313
Merge commit 'a59aed703374040240f23b2e51ddaf209fcbacc4' into codex/re…
songoow Oct 15, 2026
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
141 changes: 141 additions & 0 deletions apps/presentation/dashboard/smoke/request-control-browser-smoke.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Real source registry, canonical SQLite, Chat entrance, request controls and Agent CLI.
// Synthetic progress only; no model runs or user runtime activation.
import assert from 'node:assert/strict';
import {spawn} from 'node:child_process';
import {readFile,mkdtemp,mkdir,rm} from 'node:fs/promises';
import {tmpdir} from 'node:os';
import {resolve} from 'node:path';
import {createServer} from 'node:net';
import {createInterface} from 'node:readline';
import {testPythonCommand} from '../../../../scripts/test-python.mjs';
import {launchBrowser,loadPlaywright} from '../../../../examples/dashboard-browser-smoke-support.mjs';
const repo=resolve(import.meta.dirname,'../../../..');
const output=resolve(repo,'output/playwright/request-control');await mkdir(output,{recursive:true});
const root=await mkdtemp(resolve(tmpdir(),'rc-'));
const socket=createServer();await new Promise(r=>socket.listen(0,'127.0.0.1',r));const port=socket.address().port;await new Promise(r=>socket.close(r));
const command=testPythonCommand(await readFile(resolve(repo,'apps/presentation/dashboard/smoke/request-control-http-fixture.py'),'utf8'),{repoRoot:repo});
const child=spawn(command.command,[...command.args,root,String(port)],{cwd:root,env:{...process.env,LOOPX_USAGE_PING:'0',TMPDIR:root,NODE_OPTIONS:'--experimental-sqlite'},stdio:['pipe','pipe','pipe']});
let stderr='';child.stderr.on('data',x=>stderr+=String(x));
const lines=createInterface({input:child.stdout});const replies=[];const waiting=[];
let readyResolve,readyReject;const ready=new Promise((r,j)=>{readyResolve=r;readyReject=j;});
lines.on('line',line=>{if(line.startsWith('Serving LoopX Chat'))readyResolve();if(line.startsWith('FIXTURE ')){const v=JSON.parse(line.slice(8));if(waiting.length)waiting.shift()(v);else replies.push(v);}});
child.once('exit',code=>readyReject(Error(`fixture exited ${code}: ${stderr}`)));
async function fixture(op){const result=new Promise(r=>replies.length?r(replies.shift()):waiting.push(r));child.stdin.write(op+'\n');const value=await result;assert.equal(value.fixture_error,undefined);return value;}
let browser,page;
try{
let timer; try {await Promise.race([ready,new Promise((_,j)=>{timer=setTimeout(()=>j(Error('startup timeout '+stderr)),90000);})]);} finally {clearTimeout(timer);}
browser=await launchBrowser(loadPlaywright().chromium);page=await browser.newPage({viewport:{width:1440,height:1100}});
await page.addInitScript(()=>!localStorage.getItem('loopx-pw-locale')&&localStorage.setItem('loopx-pw-locale','zh-CN'));
const errors=[];page.on('pageerror',e=>errors.push(e.message));
const writes=[];let drop=true;let droppedResolve;const dropped=new Promise(r=>{droppedResolve=r;});
await page.route('**/api/chat/request-control',async route=>{
if(route.request().method()!=='POST'||route.request().postDataJSON().execute!==true)return route.continue();
const body=route.request().postDataJSON();writes.push(body);
const response=await route.fetch();
if(drop&&body.operation==='revise'&&response.ok()){drop=false;await route.abort();droppedResolve();}else await route.fulfill({response});
});
await page.goto(`http://127.0.0.1:${port}/chat/`);
async function enter(){
await page.getByRole('navigation',{name:'管家视图'}).getByRole('button',{name:/^(Chat|对话)$/}).click();
await page.getByText('Please verify this contract. Preserve the constraints and return findings.',{exact:true}).waitFor();
const card=page.locator('.personal-collaboration').first();
await card.getByText('查看交办内容',{exact:true}).click();
const open=card.getByRole('button',{name:'修正或取消请求',exact:true});
if(await open.count())await open.click();
await card.locator('.personal-request-control').waitFor();
return card;
}
let card=await enter();let panel=card.locator('.personal-request-control');
await panel.getByRole('button',{name:'修正内容',exact:true}).click();
await panel.getByLabel('背景与补充',{exact:true}).fill('Use corrected evidence for this contract');
// A successful storage call is not proof that the whole original request can
// survive a lost reply and reload. Fault only browser retention; the real
// request/Goal owners and the response-loss route above stay in place.
await page.evaluate(()=>{
const set=Storage.prototype.setItem,get=Storage.prototype.getItem;
window.requestRetentionFault='drop';
Storage.prototype.setItem=function(key,value){
if(key.startsWith('loopx.request-control.')){
if(window.requestRetentionFault==='drop')return;
if(window.requestRetentionFault==='changed-content'){
const saved=JSON.parse(value);saved.parameters.brief.context='Different retained content';
return set.call(this,key,JSON.stringify(saved));
}
}
return set.call(this,key,value);
};
Storage.prototype.getItem=function(key){
if(key.startsWith('loopx.request-control.')&&window.requestRetentionFault==='unreadable')throw Error('Storage read unavailable');
return get.call(this,key);
};
});
for(const fault of ['drop','changed-content','unreadable']){
await page.evaluate(value=>{window.requestRetentionFault=value;},fault);
await panel.getByRole('button',{name:'保存修正',exact:true}).click();
await panel.getByText('无法保存恢复信息,本次操作尚未发送。',{exact:true}).waitFor();
assert.equal(writes.length,0,'unrecoverable request must never execute');
assert.equal((await fixture('inspect')).revision,1);
assert.equal(await panel.getByLabel('背景与补充',{exact:true}).inputValue(),'Use corrected evidence for this contract');
assert.equal(await panel.getByLabel('背景与补充',{exact:true}).isEnabled(),true);
assert.equal(await panel.getByRole('button',{name:'重试原操作',exact:true}).count(),0);
}
await panel.screenshot({path:resolve(output,'retention-unavailable.png')});
await page.evaluate(()=>{
window.requestRetentionFault=null;
for(const key of Object.keys(sessionStorage))if(key.startsWith('loopx.request-control.'))sessionStorage.removeItem(key);
});
await page.reload();card=await enter();panel=card.locator('.personal-request-control');
assert.equal(await panel.getByRole('button',{name:'重试原操作',exact:true}).count(),0);
assert.equal((await fixture('inspect')).revision,1);
await panel.getByRole('button',{name:'修正内容',exact:true}).click();
await panel.getByLabel('背景与补充',{exact:true}).fill('Use corrected evidence for this contract');
await panel.getByRole('button',{name:'保存修正',exact:true}).click();
await dropped;
await page.waitForFunction(()=>[...document.querySelectorAll('button')].find(b=>b.textContent==='重试原操作')?.disabled===false);
assert.equal((await fixture('inspect')).revision,2);
await page.reload();card=await enter();panel=card.locator('.personal-request-control');
await panel.getByRole('button',{name:'重试原操作',exact:true}).click();
await panel.getByRole('button',{name:'修正内容',exact:true}).waitFor();
assert.deepEqual(writes[0],writes[1]);assert.equal((await fixture('inspect')).revision,2);
await panel.getByRole('button',{name:'修正内容',exact:true}).click();
await panel.getByLabel('背景与补充',{exact:true}).fill('A stale editor must not overwrite another correction');
await fixture('correct');
await panel.getByRole('button',{name:'保存修正',exact:true}).click();
await panel.getByText('请求已有新修订。已重新读取,请基于当前内容修改。',{exact:true}).waitFor();
await panel.getByRole('button',{name:'修正内容',exact:true}).click();
assert.equal(await panel.getByLabel('背景与补充',{exact:true}).inputValue(),'Correction from another authorized view');
await panel.getByRole('button',{name:'收起编辑',exact:true}).click();
await fixture('receiver');await fixture('interim');
await page.reload();card=await enter();panel=card.locator('.personal-request-control');
await page.getByText('Checking the corrected request',{exact:true}).waitFor();
await fixture('reserve');
await panel.getByRole('button',{name:'重新读取',exact:true}).click();
await panel.getByText('目标变更仍待核对',{exact:true}).waitFor();
await panel.screenshot({path:resolve(output,'pending-effect.png')});
const failedRead=async route=>route.abort();
await page.route('**/api/chat/request-control?*',failedRead);
await panel.getByRole('button',{name:'重新读取',exact:true}).click();
await page.waitForFunction(()=>![...document.querySelectorAll('.personal-request-control button')].some(b=>b.textContent==='取消请求'));
assert.equal(await panel.getByRole('button',{name:'取消请求',exact:true}).count(),0);
await page.unroute('**/api/chat/request-control?*',failedRead);
await panel.getByRole('button',{name:'重新读取',exact:true}).click();
await panel.getByRole('button',{name:'取消请求',exact:true}).waitFor();
await panel.getByRole('button',{name:'取消请求',exact:true}).click();
await panel.getByText('请求已取消',{exact:true}).waitFor();
assert.equal((await fixture('inspect')).terminal_effects[0].terminal.outcome,'aborted');
await panel.screenshot({path:resolve(output,'cancelled.png')});
await page.screenshot({path:resolve(output,'whole-viewport.png')});
await page.setViewportSize({width:390,height:844});
assert.ok(await page.locator('body').evaluate(e=>e.scrollWidth<=innerWidth+1));
await panel.screenshot({path:resolve(output,'narrow.png')});
await page.evaluate(()=>localStorage.setItem('loopx-pw-locale','en'));
await page.reload();
await page.getByRole('navigation',{name:'Manager view'}).getByRole('button',{name:'Chat',exact:true}).click();
const english=page.locator('.personal-collaboration').first();
await english.getByText('View delegation details',{exact:true}).click();
await english.getByText('Request cancelled',{exact:true}).waitFor();
await english.screenshot({path:resolve(output,'english.png')});
assert.deepEqual(errors,[]);
console.log('PASS: real source registration, normal Chat entrance, silent-drop/changed-content/unreadable retention refuses execution and preserves draft, original request correction, unknown reply/reload/exact retry, stale edit, independent receiver CLI, reserved effect and cancellation terminal readback.');
}catch(error){if(page){await page.screenshot({path:resolve(output,'failure.png'),fullPage:true}).catch(()=>{});await (await import('node:fs/promises')).writeFile(resolve(output,'failure.txt'),await page.locator('body').innerText());}throw error;}
finally{await page?.unrouteAll({behavior:"ignoreErrors"}).catch(()=>{});await browser?.close();child.kill('SIGTERM');await new Promise(r=>child.exitCode!==null?r():child.once('exit',r));lines.close();await rm(root,{recursive:true,force:true});}
Loading
Loading