Skip to content

blocked: backend asks - following feed, session-only routes, article_series 502, shape confirmations #58

Description

@Adron

Summary

A single place to record and act on the remaining backend dependencies. Each carries a paste-ready prompt for the InterlinedList API session (base URL https://interlinedlist.com). None of these can be closed from the client.

Nothing here blocks the rest of the parity work — every other open parity issue is client-buildable today. This issue exists so the asks are tracked rather than rediscovered on each sweep.


1. Following / home feed — the one high-impact blocker

Re-verified still broken 2026-09-05. An authenticated GET /api/messages?limit=50 and GET /api/messages?limit=50&scope=following return the identical 50 messages from the identical author set, a month after the first report. MessagesService.timeline short-circuits .following to an empty "coming soon" page (MessagesService.swift:364); the All/Mine/Following picker is fully wired and flips one branch the moment the feed exists.

This also blocks the Following Only option in the viewing-preference work.

PROMPT: Add a followed-accounts timeline feed. Preferred: extend GET /api/messages with ?scope=following (or add GET /api/feed/following), returning only messages authored by accounts the caller follows, using the same paginated envelope as GET /api/messages (same limit/offset/hasMore shape). Bearer auth. Document it. Note: as of 2026-09-05 the live server silently ignores scope/feed/following/filter on GET /api/messages, so this needs a real implementation, not just docs. The macOS client already has the UI wired and flips one branch to consume it.


2. Session-only routes lock out every Bearer client

The live OpenAPI spec declares 45 operations as x-auth-type: session — "requires the web session cookie only; a Bearer token is not accepted". Setting aside admin, cron, Stripe, and architecture-aggregates, these are the ones a native app legitimately needs:

Route What it costs the native client
POST /api/lists/invite/{token} Cannot accept a list email invite
POST /api/documents/invite/{token} Cannot accept a document email invite
POST /api/lists/shared/{token} Cannot claim an editor/admin list share link
POST /api/documents/shared/{token} Cannot claim an editor/admin document share link
POST /api/auth/send-verification-email Cannot offer Resend verification email
GET /api/user/engagement No engagement stats (confirmed 401 under Bearer, 2026-09-07)
GET/PUT /api/user/dashboard-layout Dashboard is unbuildable
GET/PUT /api/user/front-wall-layout Front wall is unbuildable
GET /api/widgets/*, and by extension the widget surface Widgets are unbuildable
GET /api/auth/accounts, POST /api/auth/switch, POST /api/auth/remove-account Multi-account switching is unbuildable

The read halves (GET /api/lists/invite/{token} etc.) are public, so the client can show an invite landing page and then hand the user to the browser to accept — a poor but honest experience.

Note the spec's own prose is stale here: its description lists "exports, organizations, LinkedIn targets, GitHub helpers, message dig, message edit" as session-only, but the per-operation x-auth-type values show all of those as sync-token. Worth flagging in the same ask.

PROMPT: The macOS client authenticates with a Bearer sync token (POST /api/auth/sync-token). Several operations are declared x-auth-type: session and reject Bearer, which makes them unreachable for native and CLI clients. Please make the following Bearer-reachable, or document a supported alternative: (1) the claim/accept halves of the invite and share-link flows — POST /api/lists/invite/{token}, POST /api/documents/invite/{token}, POST /api/lists/shared/{token}, POST /api/documents/shared/{token}. Accepting an invite is documented as always free, so there is no entitlement reason for the restriction. (2) POST /api/auth/send-verification-email, so a native app can offer the documented "Resend verification email" action. (3) GET /api/user/engagement. Separately: the OpenAPI info.description lists exports, organizations, LinkedIn targets, GitHub helpers, message dig, and message edit as session-only, but their per-operation x-auth-type is sync-token — please reconcile the prose with the operation values.


3. article_series returns 502 provider_error on every attempt

Found 2026-09-05, re-confirmed 2026-09-06. POST /api/ai/suggest {"feature":"article_series"} fails with {"error":"The AI provider rejected the request.","code":"provider_error"} on three separate attempts across two days, with different briefs, all well over the ten-word minimum. The same account, key, and model succeed for writing_assist, message_series, powered_template, and powered_document — so it is not entitlement, quota, or input length. The macOS client blames the provider rather than the user, so it degrades honestly, but Article Series cannot work for any client until this is fixed.

PROMPT: POST /api/ai/suggest with {"feature":"article_series","input":"<a multi-sentence brief>"} returns 502 {"error":"The AI provider rejected the request.","code":"provider_error"} every time, for a subscriber whose other AI features (writing_assist, message_series, powered_template, powered_document) all succeed on the same request path. Reproduced three times across 2026-09-05 and 2026-09-06 with different briefs. Please check the article_series prompt/schema construction and what the provider is actually rejecting — likely a malformed tool/response schema for that feature specifically — and confirm the artifact shape it should return (the other features return {artifact:{kind,…}}).


4. GitHub issue shapes still cannot be exercised end-to-end

The test account is GitHub-linked now (GET /api/github/repos → 200, up from the old 400 "not linked"), but the repo list comes back empty and GET /api/github/orgs returns [] — so issue create/update/comment request and response envelopes remain unverified. The routes are settled (PR #24 repointed the client at the flat three-segment paths); only the payloads are unknown.

This one may not need the backend at all — granting the .env test account access to a single repository would unblock it locally. Try that first.


5. Smaller confirmations and additive niceties

Each is worked around today; these make the client correct or cheaper. Additive fields are always safe — the client decodes by name and ignores unknowns.

  • Auth decision on GET /api/messages (returns 200 with public content and no Authorization header). No client change either way; needs a documented decision. (P1-F)
  • Follow action returns followedBy — the client decodes {follow:{status}} and needs a second GET /api/follow/{userId}/status call. A relationship block on the follow/unfollow/approve/reject responses removes a round-trip. (P2-B)
  • Typed notification kinds + routePath — deep-linking works via a client-side typed projection; a stable routePath collapses it to a plain URL and unblocks APNs push routing. (P2-C)
  • Markdown export — /api/exports/* is CSV-only, so the client renders Markdown itself, costing N+1 refetches for bulk export. (P2-F)
  • Schema DSL select/markdown token spec — Field:select(a|b|c) is a client convention, still API-unconfirmed. This blocks the per-field validation work. (P2-G)
  • Document version / ETag for sync conflict detection. (P3-A)
  • folderId on sync-response documents, including preserved copies and deleted documents. (P3-B)
  • GitHub-backed list refresh metadata + githubSource on create — lastRefreshedAt, refreshStatus, refreshError; blocks part of the GitHub-backed-lists work. (P3-C)
  • RateLimit-* headers universally — currently only on POST /api/messages and POST /api/documents/sync. (P3-E)
  • List clone-with-rows — "save to my lists" copies title/description/schema but no rows, because no clone endpoint exists. (P3-G)

Full paste-ready prompts for each live in work-consolidation.md §2c.


Acceptance criteria

  • Each numbered item is either resolved upstream or explicitly deferred with a reason.
  • When one lands, the client-side issue it blocks is unblocked and linked here.

Notes

Consolidates work-consolidation.md §2a and §2c. Re-verify before acting — three items previously filed as backend blockers (P1-H2, P2-I, P3-D) turned out to be client-side errors and were fixed in PR #24/#25. Probe first, ask second.


Additions from the 2026-09-15 sweep

Three asks were resolved without a backend conversation, and two new ones are added. Recording both halves, because "we thought this was blocked and it was not" is as useful to the next sweep as the remaining blocks.

Resolved by probe — remove from the blocked list

Was blocked on Answer
The notification tray showing read history (#80) scope=all already exists, returns read + unread, and honours limit. No ask needed.
A DM notification producer (#77) /api/user/notification-preferences lists direct_message as a first-class event, so the server emits them. A mapping change, not an ask.
The list-schema DSL encoding (P2-G, blocking #50 item 1) Confirmed. It is not DSL syntax — validationRules {min,max,minLength,maxLength,pattern,options}, helpText, placeholder, isRequired, isVisible, visibilityCondition are first-class fields. Captured in docs/spikes/list-schema-wire-shapes.md.

New ask 1 — clearing a profile location (filed as #91)

A profile location can be set through PATCH /api/user/update and cleared through nothing. null, "", "null", false and out-of-range values are all 400; clearLocation, location:null and cleared:true all return 200 and change nothing; DELETE /api/user/location is 404.

These coordinates are published on a public profile, so a field that can be set and not unset is a privacy defect. This blocks #57's setter entirely — shipping set-without-clear would make macOS a way to publish an approximate home location that the user cannot take back.

The ask: accept null on latitude/longitude and treat the pair as cleared, or add DELETE /api/user/location, or name the mechanism the web uses if one exists.

⚠️ The probe left a location set on the .env recon account (47.6062 / −122.3321) that it could not undo. That is the bug reproduced; it needs clearing out of band.

New ask 2 — a populated DM items[] (for #76)

GET /api/dm/conversations is still empty on the recon account, so the populated shape has never been observed and the decoder stays deliberately permissive.

The ask is small: one real items[] row pasted into #76. The alternative — a second recon account — is worth having anyway, since list sharing, org membership and follow requests all have "the other person" paths that are untestable end to end for the same reason.

Shape confirmations this sweep supplied

Several of this issue's "shape confirmations" no longer need asking; the captures are in docs/spikes/list-schema-wire-shapes.md and in the contract tests added by PRs #87, #93 and #96 — GET /api/lists/{id}, GET /api/lists/{id}/schema, PUT /api/lists/{id}/schema, GET /api/users/{u}/lists/{id}, GET /api/user/identities, and the notification row shapes.

Activity

added
parityWeb-parity gap with the InterlinedList web app
blockedCannot proceed — backend-gated or spike-first
on Sep 7, 2026

Adron commented on Sep 13, 2026

@Adron
MemberAuthor

Additional diagnostic for article_series (§3) — recovered from an unmerged docs branch

Captured 2026-09-06 on branch docs/parity-refresh-2026-09 (commit cc57a46), which is being retired without merging. This detail exists nowhere else and is worth handing to the backend:

The 502 is slow. The 2026-09-06 re-probe took roughly four minutes to return, where earlier attempts failed fast. That was the fourth attempt overall and the first from a logged-in browser session.

Why it matters: a slow failure points at a server-side timeout or provider retry loop, not a fast schema rejection. If the article_series prompt/schema were simply malformed, the provider would reject it immediately — as it does for other malformed inputs. A multi-minute hang before provider_error suggests the request is being accepted, dispatched, and then timing out or exhausting retries.

Suggested addition to the backend prompt: ask them to check the article_series path for a retry/backoff loop or an unbounded provider timeout, not only the schema construction.

Adron commented on Sep 16, 2026

@Adron
MemberAuthor

Two asks added by the 2026-09-15 sweep

Both are new, both block real work, and neither is a shape confirmation — they are missing routes.

1. No list-templates route (blocks item 3 of #50)

/help/lists documents a From Template tab on the New List page: "start from a pre-defined schema instead of building one field by field."

Searched all 233 paths in the live spec. The only template routes are:

GET  /api/documents/templates
POST /api/documents/from-template
POST /api/documents/templates/seed-defaults

There is no list equivalent, and no ListTemplate schema under components.schemas.

The ask: is there a route serving pre-defined list schemas? If not, is one planned — and what shape would a template take (a bare schema DSL object, or a schema plus seed rows)? PR #101 ships the rest of #50 and leaves this one item out rather than half-building a tab over a route that does not exist.

2. No device-token registration route (blocks the client half of #59)

The APNs spike needs somewhere to send a device token after UNUserNotificationCenter registration. Searched the spec: no /api/devices, no /api/push, no token-registration operation anywhere.

The ask: does a device-token registration route exist? If not, the APNs spike is blocked on the backend as well as on hardware, and the client-side preparation offered on #59 has nowhere to hand its token.


Resolved without an ask — remove these from the list

Three things previously believed to need the backend turned out not to:

Was Now
The tray showing read history (#80) scope=all already exists, returns read + unread, honours limit. Fixed in PR #89.
A DM notification producer (#77) /api/user/notification-preferences lists direct_message as a first-class event. A mapping change, not an ask.
The list-schema validation encoding (P2-G, blocking #50 item 1) Confirmed, and it is not DSL syntax — validationRules {min, max, minLength, maxLength, pattern}, helpText, placeholder, isRequired, isVisible are first-class fields. Captured in docs/spikes/list-schema-wire-shapes.md.

Shape confirmations this sweep supplied

Several entries under "shape confirmations" no longer need asking. Captured payloads and contract tests now exist for GET /api/lists/{id}, GET/PUT /api/lists/{id}/schema, POST /api/lists, GET /api/users/{u}/lists/{id}, GET /api/user/identities, GET /api/notifications, and the whole /api/user/app-settings/* family — see PRs #87, #93, #96 and #102.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    blockedCannot proceed — backend-gated or spike-firstparityWeb-parity gap with the InterlinedList web app

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions