Skip to content

[236] Web-shell STAC write path — Vite middleware POST/PUT (phase 1) + IndexedDB browser persistence (phase 2) #572

Description

@IanMayo

Problem

The web-shell host is currently read-only for the STAC catalog. apps/web-shell/src/mocks/stacService.ts reads item.json and GeoJSON data via the /stac-store/ Vite middleware, but has no write path. Storyboard captures (and any other plot mutations) made in web-shell live only in getSessionStore() and vanish on reload — surfaced today by the FR-WEB-029a session-only badge from #215's storyboard capture spec.

This blocks cross-host parity: VS Code persists captures via apps/vscode/src/services/sceneThumbnailService.ts (Node fs); web-shell can't.

Proposed Solution — Two-Phase Delivery

Phase 1 — Vite middleware POST/PUT (this spec, primary deliverable)

Extend stacStoreMiddleware in apps/web-shell/vite.config.ts to handle write verbs against STAC_STORE_ROOT:

  • PUT /stac-store/{path}/item.json — atomic write of merged item JSON
  • PATCH /stac-store/{path}/item.json — server-side shallow merge of properties + assets (parity with stacService.updateItemMetadata from docs: Add 001-document-debrief-algorithms feature specification #193)
  • POST /stac-store/{path}/{asset.png} — write binary asset bytes (thumbnails, scene PNGs)
  • PUT /stac-store/{path}/{data.geojson} — write GeoJSON data
  • POST /stac-store/{itemPath}/item.json — create new STAC item (for future tool outputs)
  • DELETE /stac-store/{path}/... — remove asset/item (symmetry with create)

Replace the mocks/stacService.ts read-only contract with a real WebShellStacService matching the VS Code service surface. Lift the shared write logic into a host-agnostic STAC asset writer module so both VS Code (sceneThumbnailService) and web-shell middleware reuse the merge/atomic-write semantics.

Active in dev (vite dev), preview (vite preview), and Heroku Review Apps. Production GitHub Pages bundle continues to ship the read-only behaviour until phase 2.

Phase 2 — IndexedDB browser persistence (separate follow-up spec)

Production GitHub Pages deployment persists writes to per-origin IndexedDB. No File System Access API (Firefox/Safari support gaps, simpler permission UX). Includes an explicit "Export catalog as zip" action so users can extract their captures.

Phase 2 is scoped as a placeholder follow-up backlog item, not delivered in this spec. The host-adaptor write contract designed in phase 1 must be IDB-implementable.

Scope

In scope (phase 1):

  • All STAC write operations VS Code currently performs:
  • New STAC item creation (forward-compatible with debrief-calc tool outputs writing derived plots)
  • A host-agnostic STAC asset writer module shared with VS Code's sceneThumbnailService
  • A new WebShellStacService replacing the mock, exposing updateItemMetadata, writeAsset, createItem, deleteItem

Out of scope (phase 1):

  • Production browser persistence (deferred to phase 2 backlog item)
  • File System Access API
  • ETag / optimistic concurrency / file locks
  • Cross-host conflict reconciliation

Success Criteria

  • Web-shell on vite dev persists Storyboard/Scene captures across reloads (FR-WEB-029a session-only badge no longer applies in dev/preview)
  • Heroku Review App for any PR shows captured Storyboards surviving full-page reloads
  • Same suite of write operations available in web-shell as in VS Code (Storyboard capture, metadata patch, GeoJSON write, create new item)
  • Shared STAC asset writer module — VS Code sceneThumbnailService and web-shell middleware both call it
  • No regression in existing mocks/stacService.ts read paths or VS Code write paths
  • Constitution amendment ADR landed before merge (see Constraints)
  • E2E test: web-shell capture → reload → capture still present

Constraints

Constitution amendment required (Article IV)

The user explicitly chose to propose a Constitution amendment rather than treat this as a narrow exception or wrap writes in a Python service. The amendment carves out a 'persistence-host abstraction' pattern formally acknowledging that browser-only deployments cannot route writes through Python services (offline-first goal in Article I, GitHub Pages target). The amendment must land — or be explicitly approved as in-flight — before this spec merges.

The amendment frames the existing #215 host-adaptor exception as the first instance of the pattern and authorises further hosts (web-shell phase 1 middleware, phase 2 IDB) to implement it.

Conflict handling — last-write-wins

Mirrors current VS Code sceneThumbnailService behaviour (no ETag/lock checks today). Document a clear constraint in the spec and in the dev-environment README:

Don't run web-shell vite dev and VS Code Extension Host against the same STAC store concurrently. Writes are not coordinated; the last writer wins and silently overwrites the other. Use separate STAC_STORE_PATH env vars if you need both hosts up at once.

Phase 2's IndexedDB transactions serialise multi-tab writes naturally — no extra work in this spec.

Existing dependencies

  • Reuses #174 host-adaptor pattern (/stac-store/ middleware already in place for reads)
  • Builds on #215 storyboard capture & maintenance UX (FR-WEB-029a is the concrete unblock)
  • Extends #193 updateItemMetadata semantics to web-shell

Out of Scope

  • Phase 2 IndexedDB implementation (separate backlog item to be created on phase 1 close)
  • File System Access API path (deliberately rejected during interview — Firefox/Safari support, permission UX)
  • Optimistic concurrency, ETag, file locks, single-writer enforcement (last-write-wins chosen)
  • Wrapping writes in a Python debrief-stac-write service (deliberately rejected during interview in favour of Constitution amendment)
  • Cross-host real-time sync between VS Code and web-shell

Estimate

5–8 dev-days for phase 1 (per original backlog estimate). Phase 2 is a separate follow-up.


Originally captured via /idea --defer as backlog item #236, interview completed via /interview 2026-05-01.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions