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,7 @@
---
bump: none
---

### Changed

- surface-journey test: the shell's applies and handles between page phases run `round` as a module, which reuses its cached bytecode instead of recompiling `round.py` on each call, and the pause before the last Notes reply waits only for the next whole second.
14 changes: 10 additions & 4 deletions plugins/planning/surface/surface-checks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -363,14 +363,16 @@ print(json.dumps({"live": len(live), "answered": sum(r[1] == "answered" for r in
for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
sed "s/__PORT__/$jport/; s/__PHASE__/$n/" tests/ui_journey.js >"$tmp/uj$n.js"
done
# The repeated writes run round as a module from this directory: Python then reuses its cached
# bytecode, where round.sh runs round.py as a script that recompiles all of it on every call.
jhandle() {
local s
read -r -a s <<<"$(unhandled "$j")"
[[ "${#s[@]}" -eq 0 ]] || jtime handle bash "$here/round.sh" --dir "$j" handle --seq "${s[@]}" >/dev/null
[[ "${#s[@]}" -eq 0 ]] || jtime handle "$py" -m round --dir "$j" handle --seq "${s[@]}" >/dev/null
}
japply() { # name ops-json
printf '%s' "$2" >"$j/ops/$1.json"
jtime apply bash "$here/round.sh" --dir "$j" apply --file "$j/ops/$1.json" >/dev/null || bad "journey: ops $1 refused"
jtime apply "$py" -m round --dir "$j" apply --file "$j/ops/$1.json" >/dev/null || bad "journey: ops $1 refused"
}
jrun() { run_code "$tmp/uj$1.js" "$tmp/uj$1.out"; }
jrun 1
Expand Down Expand Up @@ -460,8 +462,12 @@ print(json.dumps({"live": len(live), "answered": sum(r[1] == "answered" for r in
"constraints": "Builds stop at ten minutes and share one cache."}}]}'
# A Notes reply after the restatement: the restate notice outranks it in phase 16.
japply n2 '{"ops": [{"op": "note-reply", "needsAnswer": true, "text": "Anything else before the Brief?"}]}'
# An ordinary Claude note after it must not unpin the question.
jtime sleep sleep 1
# An ordinary Claude note after it must not unpin the question. Notes carry whole-second times,
# so waiting for the next second to start gives it a later time than n2, as a full second would.
local now=${EPOCHREALTIME:-} us wait=1 # bash 4 has no EPOCHREALTIME: keep the full second
us=${now#*.}
[[ "$us" =~ ^[0-9]{6}$ && "$us" != 000000 ]] && wait=$(printf '0.%06d' "$((1000000 - 10#$us))")
jtime sleep sleep "$wait"
japply n3 '{"ops": [{"op": "note-reply", "text": "The restatement is posted."}]}'
jrun 16
# Phase 17 confirms the third restatement. The shell then posts a status and the finish op,
Expand Down
10 changes: 8 additions & 2 deletions plugins/planning/surface/tests/ui_journey.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ async page => { // the user journey in order on one page, no reload after phase
const h2 = await lineIs(status.text) ? await head() : {};
ok("a status set after the newest unhandled event is not dimmed", h2.line === "Checked the cache key" && h2.old === false && h2.work === "Claude is working on Q99, Q1", JSON.stringify(h2));
status = null;
// h2 came from the 3 s poll; a visible tab's visibilitychange reads /api/state at once, so h3 need not wait for the next poll
await tab.evaluate(() => document.dispatchEvent(new Event("visibilitychange")));
await tab.waitForFunction(() => document.getElementById("claudeLine").hidden || document.getElementById("clText").textContent !== "Checked the cache key", null, {timeout: 8000}).catch(() => {});
const h3 = await head();
ok("clearing the status leaves the working line unchanged", h3.line !== "Checked the cache key" && h3.work === "Claude is working on Q99, Q1", JSON.stringify(h3));
Expand Down Expand Up @@ -713,10 +715,14 @@ async page => { // the user journey in order on one page, no reload after phase
await page.request.get((await page.evaluate(() => location.origin)) + "/api/wait?after=0&timeout=1", {headers: {"X-Interview-Token": await token()}}); // delivers it: a wake is in flight
await until(() => /^Claude is working on /.test(document.getElementById("workLine").textContent), 5000); // the page has seen the delivery, the state queued() reads
ok("the first note posted", sent.ok(), sent.status());
await page.click("#title"); await page.keyboard.press("l"); await page.click("#flyClose", {timeout: 2000}).catch(() => {});
// l toggles Activity, left open since phase 10: close it only when this press opened it
await page.click("#title"); await page.keyboard.press("l"); if (await page.$eval("#fly", el => el.classList.contains("open"))) await page.click("#flyClose");
await page.evaluate(() => document.querySelector('[data-panel="notes"]').click()); await settle(300);
// The next state frame re-renders the Notes panel, emptying the toast, so record every text it shows.
await page.evaluate(() => { const seen = window.__noteToasts = []; const mo = window.__noteToastObserver = new MutationObserver(() => { const t = (document.getElementById("noteToast") || {}).textContent; if (t && seen[seen.length - 1] !== t) seen.push(t); }); mo.observe(document.body, {subtree: true, childList: true, characterData: true}); });
await page.fill("#noteText", "second note"); await page.click("[data-sendnote]");
ok("a save while a wake is in flight says queued, Claude is busy", await until(() => /Queued, Claude is busy/.test((document.getElementById("noteToast") || {}).textContent || ""), 4000), await text("#noteToast"));
ok("a save while a wake is in flight says queued, Claude is busy", await until(() => window.__noteToasts.some(t => /Queued, Claude is busy/.test(t)), 4000), JSON.stringify(await page.evaluate(() => window.__noteToasts)));
await page.evaluate(() => window.__noteToastObserver.disconnect()); // the page lives on through phase 20
await page.click("#flyClose", {timeout: 2000}).catch(() => {});
}
if (PHASE === 17) { // after the wrap-up the user confirms the third restatement; the shell has written nothing since phase 16
Expand Down
Loading