Skip to content

Windows: prompt attachments parity for the New Node draft flow - #415

Merged
coneilen merged 2 commits into
mainfrom
coneilen-microsoft-windows-prompt-attachments
Sep 22, 2026
Merged

coneilen merged 2 commits into
mainfrom
coneilen-microsoft-windows-prompt-attachments

Conversation

@coneilen

Copy link
Copy Markdown
Collaborator

Summary

Windows equivalent of macOS's ProjectFeature+Attachments.swift / NodeDraftAttachments.swift: attach supported files and images from a native multi-select file picker into the New Node dialog, ingest them with size/type validation, show name/type/size with removal before submit, persist staged assets to a bounded per-draft attachments directory, and carry attachment references in the typed node draft and daemon wire payload in the same PromptAttachment shape Swift's NodeDraft decodes.

  • DraftAttachments.zig (new): extension/size validation, project-path slugging, attachments-directory resolution, file ingestion, directory cleanup, and [image #N] token generation and removal-with-renumbering, ported from the macOS reference. 8 unit tests.
  • FilePicker.c / .h (new): IFileOpenDialog-based multi-select picker with an extension filter matching the supported types, mirroring the existing FolderPicker.c.
  • Forms.zig: NodeDraft gains node_id / attachment_paths / attachment_ids / attachment_count; validateNode bounds attachment_count; new public generateDraftId lets a draft's id be chosen before its dialog opens (so attachments are filed under the id the node will carry), hardened against same-tick collisions with a process-lifetime counter.
  • Wire.zig: commandGraphCreateNodeFull encodes attachments as [{"id":...,"path":...}], always an array, matching Swift PromptAttachment's Codable shape exactly.

Validation

RED: git show a4aafc5:graphcode-windows/src/Wire.zig and a4aafc5:graphcode-windows/src/Forms.zig -> no attachment ingestion path existed: NodeDraft carried no attachment fields, commandGraphCreateNodeFull emitted no attachments key, and the New Node dialog had no picker, so nothing could round-trip into Swift PromptAttachment
GREEN: zig test graphcode-windows/src/DraftAttachments.zig -> 8 tests passed covering extension and size validation, project-path slugging, attachments-directory resolution, ingestion, cleanup, and image-token renumbering
REGRESSION: zig test graphcode-windows/src/Forms.zig and zig test graphcode-windows/src/Wire.zig -> existing Forms and Wire suites passed with the new node_id/attachment fields and the always-array attachments encoding

Coverage limits — read before marking any row Validated

The native IFileOpenDialog picker cannot be driven headlessly in CI, so the live picker interaction is not proven by this PR. What is proven is everything behind it: validation, slugging, directory resolution, ingestion, cleanup, token renumbering, draft field plumbing, and the exact wire encoding. The picker invocation itself remains unproven and its parity row must stay Partial until a live walkthrough exists.

Authoritative build and test results for this branch come from this PR's own windows-shell job under the repo-pinned Zig 0.15.2. Local Zig 0.16 is a different toolchain and has already produced misleading results elsewhere in this effort, so CI governs.

Co-authored-by: Copilot App 223556219+Copilot@users.noreply.github.com

@coneilen
coneilen force-pushed the coneilen-microsoft-windows-prompt-attachments branch 5 times, most recently from 0e9d754 to a089e8f Compare September 22, 2026 20:55
Windows equivalent of macOS's ProjectFeature+Attachments.swift /
NodeDraftAttachments.swift: attach supported files/images from a native
multi-select file picker into the New Node dialog, ingest them with
size/type validation, display name/type/size with removal before submit,
persist staged assets to a bounded per-draft attachments directory, and
carry attachment references in the typed node draft and daemon wire
payload in the same PromptAttachment shape Swift's NodeDraft decodes.

- DraftAttachments.zig (new): extension/size validation, project-path
  slugging, attachments-directory resolution, file ingestion, directory
  cleanup, and [image #N] token generation/removal-with-renumbering,
  ported from the macOS reference. 8 unit tests.
- FilePicker.c/.h (new): IFileOpenDialog-based multi-select file picker
  with an extension filter matching the supported types, mirroring the
  existing FolderPicker.c.
- Forms.zig: NodeDraft gains node_id/attachment_paths/attachment_ids/
  attachment_count; validateNode bounds attachment_count; new public
  generateDraftId lets a draft's id be chosen before its dialog opens
  (needed so attachments can be filed under the id the node will
  eventually carry), hardened against same-tick collisions with a
  process-lifetime counter.
- Wire.zig: commandGraphCreateNodeFull now encodes "attachments" as
  [{"id":...,"path":...}], always an array, matching Swift
  PromptAttachment's Codable shape exactly.
- DaemonClient.zig: sendCreateNodeDraft prefers draft.node_id when the
  dialog staged at least one attachment, instead of always generating a
  fresh id at send time.
- App.zig: createNode pre-generates the draft id before opening the
  dialog so attachments can be filed under it while the dialog is open.
- NativeForms.zig: adds an Attachments section to the node dialog as an
  independent custom-drawn section (list, Attach/Remove buttons, help
  text) appended after the generic field loop, rather than renumbering
  the existing worktree/subgraph/createdBy pass-through field indices -
  this keeps every existing field index, label, and values[] slot
  untouched. Handles attach/remove, [image #N] token insertion into the
  active brief field, lazy per-draft directory creation, listbox
  refresh, and cleanup of staged files on cancel or post-validation
  failure.
- windows-tests/GraphCommandInteropTests.swift + new fixture: decodes a
  node draft with an attachment through the real Swift NodeDraft/
  PromptAttachment Codable types and PromptAttachments.resolving.

Diffs to Wire.zig/NativeForms.zig/DaemonClient.zig were kept to their
minimal logical changes (not a whole-file zig fmt reformat), since these
files were not already zig-fmt-clean under the pinned 0.15.2 formatter
and another branch may also be editing NativeForms.zig.

Testing:
- zig test src\Forms.zig: 59/59 passed (Forms + GraphModel + Wire,
  including new attachments wire-encoding and generateDraftId tests).
- zig test src\NativeForms.zig -lc (with a stub winghostty header to get
  past cImport, since this environment has no pinned Winghostty
  provider): 92/92 passed, including new tests for buildNodeDraft's
  attachment/node_id carry-through and attachment section visibility.

Known blockers / remaining scope:
- Full exe build and WindowsShell/UIA live-automation evidence require
  bootstrapping a pinned Winghostty provider worktree
  (Tools/windows/bootstrap.ps1), not available in this environment.
- Clipboard paste and drag-drop attachment support were not implemented;
  left as documented remaining scope rather than partially faked.
- Swift interop test run (windows-tests/GraphCommandInteropTests.swift)
  could not be executed locally: SwiftPM dependency resolution hits an
  environment-enforced git safe.bareRepository=explicit policy that
  blocks fetching tags from the cached bare dependency repos. The new
  fixture/test were written to the same pattern as the existing passing
  cases and should decode identically in CI.

Signed-off-by: Colin Neilens <coneilen@microsoft.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@coneilen
coneilen force-pushed the coneilen-microsoft-windows-prompt-attachments branch from a089e8f to d726575 Compare September 22, 2026 22:31
Keep form validation single-pass while classifying Forms.FormError results at the App boundary, so invalid drafts retain the user-facing validation message rather than looking like dialog creation failed.

Signed-off-by: Colin Neilens <coneilen@microsoft.com>

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@coneilen
coneilen merged commit b974769 into main Sep 22, 2026
10 checks passed
coneilen added a commit that referenced this pull request Sep 22, 2026
The prompt-attachments slice (PR #415) landed the native picker, staged
ingestion, and daemon wire encoding, but the parity matrix had no row or
evidence mentioning attachments at all, so the ledger under-recorded what
actually shipped.

Fold the evidence into the existing Node creation sheet row, where the
macOS attachment affordance lives, and keep the row Partial: clipboard
paste and drag-and-drop are still unimplemented, and the live file-picker
interaction is not exercised by the UI Automation gate, so picker behavior
rests on unit coverage rather than live evidence.

Documentation only; no status upgrades and no code changes.

Signed-off-by: Colin Neilens <coneilen@microsoft.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.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