Skip to content

fix(planning): cover the person-held Reconfirm and fix the receipt-line race - #7301

Merged
kyle-sexton merged 3 commits into
mainfrom
test/interview-web-view-followups
Oct 12, 2026
Merged

kyle-sexton merged 3 commits into
mainfrom
test/interview-web-view-followups

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

No related issue: follow-ups to #7278 and #7280 (interview web view).

Summary

Adds the missing regression check for the person-held Reconfirm fix from #7278, and removes the cause of the intermittent ui_c.6 "the receipt line says the same" failure in surface.test.sh.

Fix

  • Reconfirm test. A new ui_c phase 1 check stales a someone else decides: Dana deferral, types Lee, the architect over it, Reconfirms, and expects the saved defer text someone else decides: Lee, the architect. With who: personOf(d) removed from reconfirmOf, it fails (the event text is Lee, the architect).
  • Receipt-line race (cause). The page's 2 s tick rendered the header, then called unheard() to decide whether to re-render the rail. deaf() is time-based, so when the 30 s threshold fell between those two calls the rail chip read "No session is listening" while the receipt line kept "Sent to Claude" until the next tick. The tick now renders the header, reads unheard() once, and on a flip redraws the rail and then the header; unheard() only turns true as time passes, so neither side can lead the other on that tick (the rail-first version still let the header lead, per the Codex review).
  • Check. The receipt-line check waits on its condition (up to 5 s) instead of reading the line once right after the chip changes. The assertion is unchanged.
  • surface.test.sh skip count 472 -> 473 for the new check; changelog fragment under .changes/planning/.

Verification

  • Reproduced the race by busy-waiting 1.5 s between renderHeader() and unheard() in a scratch copy: phase 6 failed 3/3 with [Sent to Claude: D1]. With the fixed page (delay moved between the rail and header), 3/3 pass; with the old page and the new check, 3/3 pass.
  • Reconfirm fix reverted locally: surface.test.sh failed the new check; restored.
  • surface.test.sh x3 on 2a0c89d: PASS=513 FAIL=0 SKIP=0 each run. On b7f10b6 (final tick): the 1.5 s delay before unheard() with the old single-read check passes 3/3; surface.test.sh PASS=513 FAIL=0 SKIP=0.
  • watch.test.sh PASS=28 FAIL=0; test_round 175 OK, test_server 194 OK (1 skip), test_exporters 171 OK, test_schema 25 OK.

Related

🤖 Generated with Claude Code

https://claude.ai/code/session_01C8UZGiNHApTFuNQ9zKFKk3

kyle-sexton and others added 2 commits October 12, 2026 02:48
…ine race

A Reconfirm of a someone-else-decides deferral with a new name typed over the
kept one now has a ui_c check that fails when reconfirmOf drops `who`.

The ui_c.6 receipt-line flake: the 2 s tick rendered the header before it
asked unheard(), so when the 30 s deaf threshold fell between the two calls
the rail chip read "No session is listening" while the receipt line still
read "Sent to Claude" until the next tick. The tick now renders the rail
first, and the check waits on the receipt line instead of reading it once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C8UZGiNHApTFuNQ9zKFKk3
@kyle-sexton
kyle-sexton marked this pull request as ready for review October 12, 2026 06:59
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-12T07:04:33.956205Z 2a0c89d Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@claude

claude Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 46s —— View job


Security review — no issues found

  • Gather context (PR diff, changed files)
  • Invoke /review:security-review
  • Post findings

Result: No security-relevant surface. The diff touches only a local test harness: a setInterval callback reorder in plugins/planning/surface/index.html (fixes a UI race between the rail chip and receipt line, no auth/data/trust-boundary change), a new regression check and a timing wait in tests/ui_c.js, a skip-count bump in surface.test.sh, and a changelog fragment. None of these introduce injection surfaces, touch authorization/access control, handle tokens or secrets, or modify GitHub Actions workflows or permissions. Per the skip gate, no further review or findings are warranted.

@kyle-sexton
kyle-sexton marked this pull request as draft October 12, 2026 07:00
@kyle-sexton
kyle-sexton marked this pull request as ready for review October 12, 2026 07:00
@github-actions

github-actions Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

claude-security-review has reviewed this pull request through b7f10b6; a later push is reviewed from there.

@claude

claude Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 4m 31s —— View job


Code review complete

  • Gather context (PR diff, changed files, CLAUDE.md/REVIEW.md)
  • Understand request (CI code-review lane)
  • Review changed files for correctness/maintainability
  • Check paved-path / standards alignment
  • Post findings

Scope: .changes/planning/fix-interview-web-view-followups-3c7d91a2.md, plugins/planning/surface/index.html, plugins/planning/surface/surface.test.sh, plugins/planning/surface/tests/ui_c.js (diff vs origin/main).

Findings: none. Traced both fixes against the code they touch:

  • Reconfirm regression test (tests/ui_c.js#L239-L245): confirmed reconfirmOf's who: personOf(d) (index.html#L1044) is exactly what send(...) (index.html#L1676) branches on to rebuild "someone else decides: " + named(note) vs. reusing the stale keep text — the new check exercises exactly that branch and would fail without the who field, matching the PR description.
  • Receipt-line race fix (index.html#L2116): unheard() is monotonic (only flips false→true with elapsed time absent other state changes), so reordering the tick to update the rail before renderHeader() means the header's unheard() read can no longer be "behind" a rail read taken earlier in the same tick — it eliminates the specific direction of the race described (chip shows "No session is listening" while the receipt line still lags). The updated test's waitForFunction (5 s) correctly replaces a single read, giving the DOM update time to propagate rather than asserting instantaneously.
  • surface.test.sh's browser=473 skip-count bump matches the one new ok( added in ui_c.js; I could not independently execute the suite (no playwright-cli in this environment) to confirm the author's reported PASS=513 FAIL=0 run — noting that as author-claimed, unverified here.
  • Changelog fragment format and phrasing are consistent with sibling fragments in .changes/planning/.

No paved-path row exists in docs/standards/README.md, so no paved-path check applies. Nothing in REVIEW.md's code-review-lane "always check" items (audit logging, cross-record atomicity, Actions dependency bumps) is implicated by this diff.

@claude

claude Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 52s —— View job


Security review (/review:security-review)

  • Gather context (PR diff, changed files)
  • Invoke /review:security-review skill and follow its procedure
  • Post findings

Scope: 4 changed files — a CSS/JS tweak to the setInterval tick ordering in plugins/planning/surface/index.html, a new Playwright regression check and a flaky-wait fix in plugins/planning/surface/tests/ui_c.js, a skip-count bump in surface.test.sh, and a changelog fragment.

Result: no security issues found.

  • The only functional code change is reordering two existing function calls inside an existing setInterval callback (index.html#L2116) — it changes when the rail vs. header re-render relative to each other, with no new data flow, no new input handling, and no change to what gets rendered or how. No injection, auth, or trust-boundary surface is touched.
  • The remaining changes are test-only: a new regression assertion and a waitForFunction-based poll replacing a single read, both exercising existing app behavior.
  • No CLAUDE.md/AGENTS.md/rules/hooks files are touched, so the instruction-surface-deletion check doesn't apply here.
  • No GitHub Actions workflow files are in scope.

No findings to report.
· branch test/interview-web-view-followups

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2a0c89d016

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread plugins/planning/surface/index.html Outdated
@github-actions

github-actions Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

claude-review has reviewed this pull request through b7f10b6; a later push is reviewed from there.

… tick

Rendering the rail before the header still let the header lead the rail
when unheard() was false at the check and turned true inside
renderHeader(). The tick now renders the header, reads unheard() once,
and on a flip redraws the rail and then the header.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C8UZGiNHApTFuNQ9zKFKk3
@claude

claude Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 49s —— View job


Security review (/review:security-review) — incremental lane

  • Gather context (incremental diff since 2a0c89d, scoped to plugins/planning/surface/index.html)
  • Invoke /review:security-review and follow its procedure
  • Post findings

Scope: per the lane header, only what changed since 2a0c89d in plugins/planning/surface/index.html (read from .claude-lane/incremental.diff, confirmed against git diff 2a0c89d..HEAD). All other files in this PR were reviewed at 2a0c89d and are unchanged since.

Change reviewed: index.html#L2116 — in response to the Codex finding that the prior ordering could let the header lead the rail chip across the 30 s boundary, the tick now takes a single unheard() snapshot (u) per tick and, on a flip, redraws the rail then re-renders the header from that same snapshot, so both surfaces reflect one unheard() read on the tick that matters.

Result: no security issues found.

  • This is a reorder/snapshot change inside an existing setInterval callback in a local test-harness UI page. No new input handling, no new data flow, no auth/access-control, injection, or secrets/token surface is touched by this hunk.
  • It's a pure UI-consistency fix (which read of a time-based boolean feeds which re-render) with no externally-controllable input — unheard()/deaf() derive from local clock/session state already present before this diff, not from new untrusted data.
  • No CLAUDE.md/AGENTS.md/rules/hooks files are touched, so the instruction-surface-deletion check doesn't apply.
  • No GitHub Actions workflow files are in scope.

Per the skip gate, nothing further to flag here. (Note: whether this new ordering is functionally correct — i.e., fully closes the race Codex flagged — is a correctness question for the code-review lane, not a security one; not assessed here.)
· branch test/interview-web-view-followups

@claude

claude Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 2m 2s —— View job


Code review complete (incremental: 2a0c89d → b7f10b6)

  • Gather context (incremental diff, last-reviewed commit)
  • Review the only changed hunk in plugins/planning/surface/index.html
  • Check paved-path standards index (no rows apply)
  • Post findings

Scope: incremental diff only — plugins/planning/surface/index.html lines 2116-2118 (the setInterval tick), per the lane's scoped review of commits since 2a0c89d. All other files were reviewed at 2a0c89d and are unchanged since.

Findings: none.

This commit replaces the previous ordering (flagged by Codex and independently by @kyle-sexton's own review comment on 2a0c89d: the rail-decision-then-header order could still let the header lead the rail chip by one tick when the 30 s unheard() boundary falls between the two reads) with:

setInterval(() => { if (!S.loaded) return; renderHeader(); const u = unheard(); if (u !== wasUnheard) { wasUnheard = u; renderRail(); renderHeader(); } }, 2000);

(index.html#L2116)

Traced it against unheard()/deaf() (index.html#L825-L835), railItem's chip read (#L758), and working()'s receipt-line read (#L851):

  • u is now a single snapshot taken once per tick, after the unconditional header render.
  • On a flip tick, renderRail() and the second renderHeader() run back-to-back, synchronously, with no yield to the event loop between them — both recompute unheard() fresh, and since no realistic amount of wall-clock time elapses between two synchronous calls, both land on the same side of the 30 s boundary. The first renderHeader() call's output (which may reflect the pre-flip state) is unconditionally overwritten by the second before any paint occurs, so there's no user-visible inconsistency.
  • This closes the specific reverse-race Codex and the author's own comment identified on the prior commit: neither side can lead the other once a flip is detected, because both are redrawn from the same post-flip snapshot in the same synchronous turn.
  • Pre-existing internal non-atomicity within a single renderHeader() call (conn() and working()/claudeLine() each call unheard() independently) is untouched by this diff and out of scope.

No paved-path row in docs/standards/README.md applies to this file.
· branch test/interview-web-view-followups

@kyle-sexton
kyle-sexton added this pull request to the merge queue Oct 12, 2026
Merged via the queue into main with commit f7beb80 Oct 12, 2026
49 of 50 checks passed
@kyle-sexton
kyle-sexton deleted the test/interview-web-view-followups branch October 12, 2026 07:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant