Address the P1 maintainability finding that trip-publish.js reimplemented
post-form.js's apiSend + login-expired error copy verbatim. Extract both into
js/src/api-utils.js and import from both entry points; esbuild inlines the
module into each bundle so there is no runtime coupling. Also drops the stale
data-trip-route reference from trip-publish.js's markup-contract comment.
Rebuilt js/trip-publish.js and js/post/post-form.js.
Verified: trip-publish suite 8/8; post suite unchanged (34 pass, same 6
owner-gate environmental fails as baseline — photo-editor specs that exercise
post-form's apiSend/apiErrorMsg all green).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mpdu3Dt1iVoozHwAMyjrbn
Add an owner-only publish switch to each /trips card. It POSTs to a new
entry-actions route that mutates trip.md `published` and invalidates the
page-tree cache so the listing, nav and home reflect the change on the
next load. Owner sees drafts (Draft badge); anon/non-owner unchanged.
- U1 EntryScopeGuard::resolveTripChild — resolve a slug to a direct child
of /trips (drafts included, for republish)
- U2 POST /api/v1/trip/{slug}/publish (setTripPublished) — owner-gated
write, strict is_bool body, header-mutation save, audit log
- U3 trip-publish-toggle partial + CSS (role=switch, Draft badge, visible
failure toast, ≥44px target)
- U4 owner-aware /trips listing + card restructure (toggle overlays cover
as a non-anchor sibling; works for coverless drafts)
- U5 home active-trip branch falls back when the active trip is unpublished
- U6 trip-publish.js (confirm/pending/optimistic/revert) + esbuild wiring
Cache note: an in-place frontmatter edit keeps the folder-check cache id,
and driver:auto uses APCu in web memory, so deleteAll()+invalidateCache()
is insufficient — the endpoint also calls apcu_clear_cache().
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mpdu3Dt1iVoozHwAMyjrbn