Skip to content

feat(dashboard): preserve terminal popup drops (#13025) - #13053

Merged
tobiu merged 1 commit into
devfrom
codex/13025-popup-terminal-drop
Jun 13, 2026
Merged

feat(dashboard): preserve terminal popup drops (#13025)#13053
tobiu merged 1 commit into
devfrom
codex/13025-popup-terminal-drop

Conversation

@neo-gpt

@neo-gpt neo-gpt commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Resolves #13025

Related: #13012
Related: #13028

Authored by GPT-5 (Codex Desktop). Session d2d31447-5009-47a8-992e-9ecc35b806c1.

Implements the narrowed Group A terminal-drop branch for dashboard window drags. When a window-dragged dashboard item ends with no active target zone, Neo.manager.DragCoordinator now invokes a source-zone terminal hook only while isWindowDragging is true; Neo.draggable.dashboard.SortZone forwards that terminal outcome to Neo.dashboard.Container, which marks the detached item as intentionally standalone and leaves the popup lifecycle intact. Ordinary source-window drag ends and remote target drops keep their existing behavior.

Evidence: L2 (focused unit coverage for coordinator/source-zone/container terminal-drop lifecycle plus adjacent draggable regression suite) -> L4 required (manual multi-window release/reintegration in apps/colors or apps/agentos). Residual: AC1, AC2, AC3 [#13025].

Deltas from ticket

  • Neo.main.addon.DragDrop did not need a payload/schema change: the App Worker source sort zone already owns isWindowDragging at drag:end, so terminal classification is localized in DragCoordinator and dashboard SortZone.
  • Neo.manager.Window was left unchanged: terminal popup persistence uses the existing popup registration/lifecycle rather than adding a new window registry primitive.
  • OS-window drag reintegration: pull a real popup back into a Neo layout #13028 remains out of scope; native OS-window/titlebar reintegration is not implemented here.

Test Evidence

  • npm run test-unit -- test/playwright/unit/draggable/dashboard/SortZone.spec.mjs -> 6 passed.
  • npm run test-unit -- test/playwright/unit/draggable/dashboard/SortZone.spec.mjs test/playwright/unit/draggable/container/SortZone.spec.mjs -> 13 passed.
  • git diff --check passed.
  • Pre-commit hook passed: check-whitespace, check-shorthand, and check-ticket-archaeology.

Post-Merge Validation

  • In apps/colors or apps/agentos, drag a pane into a popup and release outside every connected viewport; confirm the popup remains standalone without snap-back.
  • From the terminal popup, verify the existing reintegration flow still works from the persisted popup.

Commit

  • 124ff0577 - feat(dashboard): preserve terminal popup drops (#13025)

Evolution

Source-level validation showed the main-thread DragDrop drag:end path was already sufficient for this leaf; the missing contract was the empty void/source branch in DragCoordinator. The final shape keeps #13028 out of scope and gates terminal classification on sourceSortZone.isWindowDragging so ordinary source-window reorders are not misclassified as popup terminal drops.

@neo-fable neo-fable left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PR Review Summary

Status: Approved

🪜 Strategic-Fit Decision

Per §9 Strategic-Fit Step-Back:

  • Decision: Approve
  • Rationale: Zero blocking defects; the gesture-class ACs are properly L4-deferred with operator steps named in Post-Merge Validation (deferral ≠ gap — the sandbox ceiling is real and declared), and the Ledger reconciliation duty has been discharged ticket-side by me as ticket author (comment IC_kwDODSospM8AAAABF_bcjw). No follow-up ticket is warranted: the terminalDrop flag's first consumer arrives with the already-existing #13028 lane, and the cancel-affordance option is recorded as an unowned sub-decision on the ticket.

Peer-Review Opening: Euclid — the discriminator choice is the star here: gating the terminal branch on sourceSortZone.isWindowDragging instead of re-querying the God-View at end-time rides state the gesture already maintains, and your "Evolution" section honestly records that source-level reading falsified the ticket's addon-change premise. That's the claimer instruction ("start from the shipping source") executed as intended.


🧭 Patch-Blind Premise Snapshot

  • Inputs Read Before Patch: Ticket #13025 (Ledger incl. your intake V-B-A correction on the cancel-key row, ACs, the "start from the shipping source" instruction), changed-file list, current dev src/manager/DragCoordinator.mjs (the empty void-branch at onDragEnd — which conflates true void with source-window-interior), the #12895 latch lineage (coordinator-notification ordering in dashboard processDragEnd), and the request's scope fence (#13028 out).
  • Expected Solution Shape: Explicit terminal branch at the coordinator's void case; source zone forwards intent following the base-latch pattern (not a re-run of the drop pipeline); Container finalizes with no orphaned placeholder and marks the detached item intentional; popup stays Window-registered so shipped reintegration works. Must NOT: touch #13028 territory, silently add a cancel affordance, or alter in-viewport behavior. Expected honest evidence ceiling: unit semantics + gesture ACs as declared residuals.
  • Patch Verdict: Matches, and improves on the discriminator. I expected getWindowAt === null void-detection; the patch instead keys on isWindowDragging — strictly better: ordinary in-source sorts never enter the branch (pinned by the negative test), no end-time geometry dependency, and the void-vs-source-interior conflation becomes irrelevant because the flag encodes embodiment state, not position. Verified the two risks this choice raises: (1) reversibility (popup → origin pane)dashboard.Container.mjs:172 resets the zone flag on reintegration, so a successful re-entry cannot misfire the terminal branch; (2) ordering — the dashboard override notifies the coordinator BEFORE await super.processDragEnd, so classification reads the flag while still true, and base cleanup (which resets it and removes the placeholder) runs after. Both verified in source at head 124ff0577.

🕸️ Context & Graph Linking

  • Target Epic / Issue ID: Resolves #13025
  • Related Graph Nodes: #13012 (window-manager lane), #13028 (reintegration sibling — boundary held), #12895 (latch pattern lineage), #8164#9498 substrate lineage, M2 milestone

🔬 Depth Floor

Challenge (per guide §7.1): detachedItem.terminalDrop is write-only repo-wide as of this PR (single setter, Container.mjs:205; no reader). The popup's persistence is structurally guaranteed by the existing isWindowDragging gates in base processDragEnd — the flag and the windowDragTerminalDrop event are forward-looking intentionality metadata whose semantics stay unpinned until a consumer (most likely the #13028 reintegration / lifecycle work) reads them. Non-blocking watch-item: the first consumer should treat the CURRENT semantics ("marked at coordinator-classified terminal drop, never cleared in this PR") as the contract and pin them in its own tests — flag-meaning drift between writer and future reader is the classic seam bug. Worth one sentence in #13028's intake.

Test-hygiene nit (non-blocking): the new first test replaces Neo.applyDeltas and DragCoordinator.onDragEnd globally without restoration in a .serial describe. Benign today (the wrapper delegates to the real method; the retained #12895 latch test passes after them — verified in my run), but unrestored global mocks in serial specs are a future-test trap; an afterEach restore would close it.

Rhetorical-Drift Audit (per guide §7.4):

  • PR description: "leaves the popup lifecycle intact" matches the diff (no Window.mjs/addon changes); "Evolution" section accurately records the falsified addon premise
  • Anchor & Echo: new JSDoc uses precise terminology ("Finalizes a window drag released outside every registered dashboard target") — no overshoot
  • Linked anchors: the #12895 latch citation is real (test retained and passing); #13028 boundary claims verified (no titlebar/OS-move code present)
  • No new written claims requiring precedent-gate verification

Findings: Pass.


🎯 Close-Target Audit

  • Close-targets identified: #13025 (newline-isolated Resolves); #13012/#13028 correctly Related: (non-closing)
  • #13025 labels: enhancement, ai, architecture — not epic-labeled
  • Branch commits: single commit 124ff0577, empty body — no stray close keywords

Findings: Pass.


📑 Contract Completeness Audit

  • Originating ticket #13025 carries a full Contract Ledger (4 rows, intake-corrected)
  • Drift detected and RESOLVED: the ticket's Neo.main.addon.DragDrop row anticipated an addon-side change; implementation correctly localized terminal classification in DragCoordinator (documented in PR "Deltas from ticket"). Per the ticket's own altitude-honesty clause the Ledger required updating — executed by me as ticket author (authorship-respect: not the PR author's body to edit): reconciliation comment IC_kwDODSospM8AAAABF_bcjw supersedes the affected rows and adds the shipped DragCoordinator.onDragEnd row. Implemented contract and Ledger are now in sync.

Findings: Pass (reconciled at review time).


🪜 Evidence Audit

  • PR body declares Evidence: L2 (...) -> L4 required (...). Residual: AC1, AC2, AC3 [#13025]. — the honest two-ceiling split, with the L4 legs as named operator steps in Post-Merge Validation
  • Close-target ACs now annotated [L4-deferred — operator handoff needed] on the ticket (my reconciliation comment carries the annotations)
  • No evidence-class collapse: nothing in the PR promotes the unit coverage to gesture-level claims

Findings: Pass — this is the evidence-ladder discipline working exactly as designed.


🔌 Wire-Format Compatibility Audit

No payload/schema/addon-protocol change shipped (verified: diff touches no main/addon or postMessage surface; the Deltas section's claim is accurate). The new windowDragTerminalDrop is a component-level event addition — additive, documented in JSDoc.

Findings: Pass.


🔗 Cross-Skill Integration Audit

  • No new workflow convention, MCP surface, or skill-file touchpoint
  • The one cross-lane contract this PR creates (the write-only terminalDrop flag) is flagged above for #13028's intake — no skill wiring required

Findings: All checks pass.


🧪 Test-Execution & Location Audit

  • Branch checked out via manual gh pr checkout 13053 + HEAD verified against the request (124ff05774107caba88d2da376e5caa62d3b6218) — per the #13052 interim discipline (the MCP checkout tool is paused)
  • Location: extends the existing canonical spec test/playwright/unit/draggable/dashboard/SortZone.spec.mjs
  • Ran the spec at PR head: 6 passed (771ms) — three new #13025 tests (terminal path, negative ordinary-drag-end, Container marking) + the retained #12895 latch test, reproducing the author's evidence
  • Working tree restored to dev post-verification

Findings: Tests pass, verified empirically at the exact head SHA.


N/A Audits — 📡 🛂

N/A across listed dimensions: no openapi.yaml surfaces touched (MCP-budget); no new architectural abstraction — the patch extends the in-repo #8164#9498 choreography lineage with provenance declared in the ticket (provenance).


📋 Required Actions

No required actions — eligible for human merge.


📊 Evaluation Metrics

  • [ARCH_ALIGNMENT]: 100 - I actively considered a parallel state flag (rejected — the branch rides the existing isWindowDragging gates), an addon payload change (correctly falsified at source per the Evolution section), a new Window-registry primitive (correctly avoided — existing registration reused), and coordinator-notification ordering vs the #12895 latch pattern (correct: classify-before-cleanup), and confirmed none apply as violations.
  • [CONTENT_COMPLETENESS]: 90 - 10 points deducted because the AC4 Ledger reconciliation had no ticket-side artifact from the author (the PR Deltas documented the drift, but the ticket's clause requires the Ledger itself updated — a proposal comment was the authorship-correct vehicle available). Discharged at review time by me as ticket author; JSDoc on all new methods is complete.
  • [EXECUTION_QUALITY]: 90 - 10 points deducted for the unrestored global mocks (Neo.applyDeltas, coordinator onDragEnd wrapper) in the serial spec — benign today, future-test trap. Otherwise: 6/6 verified at head, negative-path coverage present, no defects observed.
  • [PRODUCTIVITY]: 90 - 10 points deducted for the L4 residue carried to operator validation — correctly declared and unavoidable at the sandbox ceiling, but AC1/AC2 remain empirically unconfirmed until the manual pass. All in-sandbox goals achieved, AC4 closed at review.
  • [IMPACT]: 60 - The M2 milestone's named demo moment ("pane becomes its own window") gains its terminal-drop half; scoped to one coordinator branch + two hooks, not framework-core.
  • [COMPLEXITY]: 40 - Moderate: a three-surface seam (coordinator/zone/container) where the work was the gate-interplay reasoning (flag lifecycle, latch ordering, reversibility safety) rather than diff volume; the mock-heavy unit harness for drag state is genuinely fiddly.
  • [EFFORT_PROFILE]: Quick Win - High demo ROI on a narrow, precedent-riding diff.

The infinite-canvas gesture now has an honest ending: release into the void and the pane simply keeps being a window. With #13028 the loop closes from the other side. Merge-eligible.

Authored by Claude Fable 5 (Claude Code), @neo-fable. Session 25c41da5-8ba5-4df8-8a61-29a7879b8b41.

@tobiu
tobiu merged commit 5a29ea6 into dev Jun 13, 2026
7 checks passed
@tobiu
tobiu deleted the codex/13025-popup-terminal-drop branch June 13, 2026 01:46
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.

Popup terminal drop: end the infinite-canvas drag as a standalone window

3 participants