Skip to content

fix(pixel): a stopped turn links an earlier preview only when it continues that project - #6760

Open
Lightheartdevs wants to merge 1 commit into
mainfrom
claude/pixel-stop-preview-same-project
Open

Lightheartdevs wants to merge 1 commit into
mainfrom
claude/pixel-stop-preview-same-project

Conversation

@Lightheartdevs

Copy link
Copy Markdown
Collaborator

Problem

Every Pixel turn is seeded with the chat's latest publication (state.workspaceLastVerifiedPreview ??= previousPreview in observeRun). The seeding comment says that snapshot "neither verifies current files nor grants continuation scope", but two delivery paths still used it for any turn that needed a preview:

  • progressStopPreview (tool-loop fuse stops, the answer turn's allowed URL, and deliveryVerificationForRun), and
  • the no-publication branch of verificationForRun ("Your last published preview is still available").

So a stopped turn working on a new project linked the earlier project's preview and carried its receipt. Portal treats that receipt as the turn's own publication: it puts a publication card on the message, and that card decides which project the chat is grouped under.

Round-114 evidence (Qwen3.5-9B, main 5afd46c, mac-mini target)

The chat's turns, in order (round-114/mac-mini/pixel-portal-evidence/):

  1. website-create: published site-52af184a....
  2. website-edit ("Update that same website ..."): published site-6ba777bafd52589578b82c5a, relativeDirectory fleet-qualification-6419c769b873.
  3. files-code: "In a separate workspace directory fleet-qualification-6419c769b873-calc ...". The model wrote ...-calc/solve.py with literal \n, then repeated the same write until the fuse stopped the run. It never read, wrote or published anything in the website directory.
  4. coding-v1: "In a new workspace project fleet-qualification-6419c769b873-coding ...". Writes and unittest runs happened only in ...-coding, and the fuse stopped the run.

Both stopped turns (files-code.answer.txt, coding-v1.answer.txt) got the same reply:

This response was stopped after repeated tool failures or attempts without progress. ...

[Open last published preview](http://site-6ba777bafd52589578b82c5a.localhost:9437/site-6ba777bafd52589578b82c5a/)

This is the last verified publication, not proof that all requested work completed.

The terminal SSE chunk of both turns (files-code.sse.body line 45, coding-v1.sse.body line 118) carried pixel.preview = {relativeDirectory: "fleet-qualification-6419c769b873", siteId: "site-6ba777bafd52589578b82c5a", ...}. That is the website's receipt, delivered as the publication of a calc turn and a coding turn.

Fix

lastVerifiedPreviewForTurn(state) in plugin/tool-loop-guard.mjs returns the last verified preview only in two cases:

  • this run published it. workspacePreviewVerifiedDirectory is set only when this run's own publication is later invalidated.
  • this turn continues that project. It counts as continuing when any of these holds:
    • this turn's preview target workspacePreviewDirectory is that directory. It is set by "update that same website" continuation, a named-preview request, a successful mutation inside the published directory, or a publication attempt.
    • the owner named that directory, or its parent, in /workspace/... (workspaceTaskDirectory).
    • every successful write and edit this turn is in that project's tree. This uses the same ancestry rule as the historical-entry readback, now shared as inPublishedProjectTree. It covers sources next to a generated public/ output.

In every other case the stop drops both the link text and the receipt. progressStopPreview and the verificationForRun branch both use the helper. In that branch the coding-stop text and the failed or pending check text are now reported whether or not a link is offered. Before, they appeared only next to the historical link, so a preview-required turn with no publication and no same-project snapshot showed only the generic "did not create/verify a preview" text.

The positive case is unchanged: an "Update that same website" turn that stops still offers its last verified preview and receipt.

Portal

I checked dashboard/src/pages/Pixel.jsx and did not change the dashboard.

  • finishAttempt sets message.publication, and calls setPreview, only when a pixel.preview frame parses (parseVerifiedPreviewFrame).
  • With no frame the stopped message gets no publication card and no beforePublication. conversationProject falls back to earlier messages.
  • The workspace panel keeps showing the chat's current preview, which is the website from turn 2. That is chat-level workspace state; it does not attribute the preview to the stopped turn.
  • Ingress (host/pixel_ingress.mjs completionSse) emits the pixel marker only when verification.preview is set, so dropping the receipt in the guard removes the frame.

Tests

  • tests/progress_finalization.test.mjs

    • New replay of the round: run 1 publishes night-garden. Run 2 in the same session then works in night-garden-calc (with a write, and before any write) or night-garden-coding and exhausts the fuse. Both the stop delivery and the verdict have no link and no receipt. The answer-turn variant composes without the old URL.
    • New positive control: run 2 edits night-garden ("Update that same website", and a direct edit of night-garden/index.html) and stops. The link and receipt are kept.
    • callTool and verifiedPreview take an optional context.
  • tests/tool_loop_guard.test.mjs

    • Three existing tests asserted the leak: "Build and publish another website in a new directory" kept the old site, and a /workspace/code coding stop linked log-viewer-lab. They now expect no link for the other project. They add same-project cases where the link is kept (continuation prompt; a coding stop in /workspace/log-viewer-lab), and still require the coding-stop and failed-check text.
    • New test: a stop keeps expense-report/public for a source edit in expense-report/, and drops it for another-project/ and for the prefix sibling expense-report-v2/.
  • Red check against main's plugin with these tests: 5 fail.

    • a stop in a different project neither links nor carries the earlier publication
    • a stop offers the historical publication only for source edits in that project's tree
    • historical preview cannot hide a later coding stop from the final reply
    • later failed preview work retains only the same session and project's historical publication
    • progress stops scope historical preview delivery to the current owner request

    The positive control passes on both main and this branch.

  • Full Linux pixel-agent suite: head 3206 tests / 3168 pass / 0 fail. Head merged with main: same.

Composition with open PRs

🤖 Generated with Claude Code

Every turn is seeded with the chat's latest publication as historical
evidence. A stopped turn that required a preview then linked that snapshot
and carried its receipt, even when the turn worked on a different project,
so Portal showed another project's website as the stopped turn's own
publication.

A stop (and the no-publication preview verdict) now offers the last
verified preview only when this run published it or this turn continues
that project: the preview target or owner-named directory is that project,
or every current mutation lies in its tree. Otherwise both the link text
and the receipt are dropped; the coding-stop and failed/pending check
outcomes are still reported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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