fix(scout,listing,dashboard): JSON fence parsing, inventory quantity coverage, per-listing publish + Etsy image panel#45
Merged
Conversation
…coverage, per-listing publish + Etsy image panel Scout: - Replace startswith-only code-fence stripper with a regex extractor that handles a prose preamble before the ```json fence and requires the closing fence on its own line (so a literal ``` inside a JSON value can't truncate the payload). Add a "raw JSON only, no fences" line to SYSTEM_PROMPT and a log.warning when the fallback fires. Add preamble + inline-backtick + bare-JSON unit tests. Error-row persistence behavior is unchanged. Listing (quantity regression coverage; source already correct): - Add an MSW handler for the Etsy inventory PUT that 400s when any offering lacks a numeric quantity > 0, and a unit test asserting every offering carries POD_VARIANT_QUANTITY (999) and round-trips EtsyInventoryInputSchema. Per-listing publish (cost-aware): - Export resumePublish from @presswork/listing and add it as a dashboard workspace dep (transpilePackages). New publishListingNow server action (owner-auth + pending_publish stale guard). Detail page gains a confirmed "Publish to Etsy ($0.20 listing fee)" button, locked while the agent runs; "Approve & publish" relabeled to "Approve". Compliance gates still run. Etsy image management: - Add getListingImages/deleteListingImage to shared (+ mock fixtures, 204 handling) and tighten offering quantity schema to .positive(). New dashboard API routes (GET/POST/DELETE) and EtsyImagePanel to view live Etsy images and add/delete from the on-file pool (design PNG + mockups). POST allowlists https on Supabase/Printify/Dynamic-Mockups hosts to prevent SSRF.
…can bundle resumePublish
The per-listing publish button imported `@presswork/listing`, whose exports map
pointed at raw TypeScript source. Next's webpack resolver then failed on the
package's NodeNext ".js" import specifiers ("Can't resolve './poller.js'"), and
a global extensionAlias workaround broke Next's own server chunk loading
("Cannot find module './901.js'").
Fix mirrors the proven @presswork/shared pattern:
- @presswork/listing exports now resolve `import` to compiled ./dist/*.js (types
still from ./src), so ".js" specifiers map to real emitted files — no bundler
resolution hacks.
- Add a dedicated ./publish subpath exporting only resumePublish, so the dashboard
doesn't bundle the agent poll-loop (poller.js); dashboard imports
@presswork/listing/publish.
- Dashboard gains predev/prebuild that run `tsc -b ../shared ../listing`, so dist
is always present before next dev/build (fresh clones, CI, Vercel).
- Revert the extensionAlias next.config change.
Regression tests (packages/listing/src/publish.test.ts): resumePublish stays a
callable export, the ./publish subpath export points at an existing file, and
publish.ts stays decoupled from the poller barrel.
Verified: `next build` green (/listings + /api/listings/[id]/etsy-images build),
listing 136 + dashboard 155 tests pass, typecheck + lint clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes from a bug-hunting session covering Scout, Listing, and the dashboard.
Scout — fenced JSON no longer fails parsing
_strip_code_fencesis now regex-based: handles a prose preamble before the```jsonfence and requires the closing fence on its own line, so a literal```inside a JSON value can't truncate the payload.SYSTEM_PROMPTand alog.warningwhen the fence fallback fires (observability on how often Claude disobeys).status='error'row persistence is intentional (observability + 7-day dedup backoff) and is unchanged.Listing —
Etsy 400: Missing input parameter: [quantity]@presswork/shareddist (source already sendsquantity: 999); fixed operationally by rebuilding the shared dist.POD_VARIANT_QUANTITY(999) and anEtsyInventoryInputSchemaround-trip.Per-listing, cost-aware publish
resumePublishis now exported from@presswork/listingand consumed by the dashboard (workspace dep +transpilePackages). Moving it into@presswork/sharedwas rejected because its cone pulls in the Anthropic SDK + compliance/copywriter domain logic.publishListingNowserver action (owner-auth +pending_publishstale-tab guard).Etsy image management panel
getListingImages/deleteListingImageadded to shared (+ mock fixtures, 204 No Content handling); offering quantity schema tightened to.positive().GET/POST/DELETEunderapp/api/listings/[id]/etsy-images/) andEtsyImagePanelto view images live on the Etsy listing and add/delete from the on-file pool (design PNG + mockups). v1 is add/delete only (no reorder).httpson Supabase / Printify / Dynamic-Mockups hosts (the URL is fetched server-side).Verification
Known follow-ups (non-blocking)
@presswork/listingexports point atsrc/*.tsvs shared'sdist/*.js— works today (only the dashboard consumes it via transpile) but should align before any plain-Node consumer imports it.PublisherErrorinpublishListingNowfor a friendlier message; surfaceretry_counton the publish card when >0; debounce the panel's post-action refetch; mock DELETE fixture should returnnull.dynamicMockupsTemplateinlistings.ts.🤖 Generated with Claude Code