Skip to content

feat(lists): finish GitHub-backed list creation and add Refresh from GitHub #51

Open

Description

@Adron

Summary

The macOS New List sheet shows GitHub source fields that do nothing:

App/Features/Lists/NewListSheetView.swift:85 — Section("GitHub source (optional)")
App/Features/Lists/NewListSheetView.swift:98 — Text("GitHub source fields will be sent in a future update.")

So a user can fill in a repo and get a plain local list. The web, meanwhile, has a full GitHub-backed list flow, and the client already ships the reading half (issue browser, create-issue-from-message, close/reopen, labels/assignees — G4 + the PR #24 route fixes).

The missing piece is creation and refresh.

What the web does (/help/lists)

Creating:

  • Prerequisite: GitHub connected with the Issues scope. "If you already linked GitHub for sign-in, you may need to click Reconnect for GitHub Issues to grant access."
  • On the GitHub-backed List tab, select a repository you have access to (a picker, not a free-text field)
  • List title defaults to the repo name
  • Optional parent list; optional Public toggle

Behaviour once created:

  • Rows map to issues (title, body, labels, assignees, state). Adding a row creates an issue; editing a row updates it; deleting a row closes it.
  • Schema is fixed — Edit changes only the parent list. "You cannot modify the issue fields … they are defined by GitHub."
  • Refresh from GitHub pulls the latest issues (the list caches locally).
  • Visibility is re-read from GitHub on every sync (create, manual refresh, scheduled sync).

Live facts (2026-09-07)

GET /api/github/repos        → 200 (test account is now GitHub-linked)  but the list is EMPTY
GET /api/github/orgs         → 200  []
GET /api/auth/github/status  → 200  {"configured":true,"clientId":"Ov23li9eXYK1i6psJW6G",
                                     "manageOrgAccessUrl":"https://github.com/settings/connections/applications/…"}

⚠️ The test account has no accessible repos, so the repo-picker payload and the githubSource-on-create request shape cannot be exercised end-to-end today. Before building, either grant the .env test account access to a repo, or capture the web app's own network calls for a create. Do not guess the create-time field names — work-consolidation.md P3-C records githubSource: { owner, repo, path, ref } as unconfirmed.

Also unconfirmed (P3-C)

The refresh metadata the UI needs — lastRefreshedAt, refreshStatus (idle|pending|failed), refreshError — is modelled in the domain as GitHubListSource but has never been seen on the wire. Probe before rendering a refresh state, or render only what is actually returned.

Division of labor

Kit

  • Accept githubSource on POST /api/lists once the shape is confirmed.
  • A repo-picker request builder over GET /api/github/repos (already present — confirm the populated shape against a repo-bearing account).
  • Whatever route drives Refresh from GitHub; if none exists, file the backend ask rather than inventing one.
  • Contract tests against a repo-bearing account.

Domain

  • Extend list creation with a GitHub source, gated on the subscriber entitlement and on a GitHub identity with Issues scope.
  • Model the fixed-schema rule so the schema editor refuses field edits on a GitHub-backed list and offers only the parent-list change.
  • Refresh action + refresh state, only for the fields the probe confirms.

App

  • Replace the dead GitHub section in NewListSheetView with a real repo picker populated from the account's repos, plus the title default.
  • An inline Reconnect for GitHub Issues CTA when the identity lacks the scope — reuse the OAuth flow from the Integrations issue rather than building a second one.
  • Refresh from GitHub in the list toolbar, with its state surfaced.
  • Schema editor: parent-list-only mode for GitHub-backed lists, with an explanation rather than a silently disabled form.

Tests

  • happy — pick a repo, create the list, rows arrive as issues
  • invalid — GitHub not linked, or linked without Issues scope → the reconnect CTA, no create attempt
  • upstream-failure — the repo list 500s → the picker shows an error and the local-list tab still works
  • boundary — an account with zero accessible repos (today's test account) → an honest empty picker, not a spinner

Acceptance criteria

  • No control in the New List sheet promises something "in a future update".
  • A GitHub-backed list created on the Mac behaves the same as one created on the web.
  • Full E2E gate green.

Notes

New finding from the 2026-09-07 parity sweep. Depends on P3-C (create-time and refresh shapes) and shares the OAuth scope-upgrade flow with the Integrations issue. Size M.

Activity

  1. added
    enhancementNew feature or request
    parityWeb-parity gap with the InterlinedList web app
    on Sep 7, 2026
  2. Adron commented on Sep 16, 2026

    @Adron
    MemberAuthor

    Blocked — and the blocker is one account permission, not code

    Re-probed 2026-09-15. Every other prerequisite is now in place; this is waiting on one thing.

    What is ready

    What blocks it

    GET /api/github/repos → 200 []
    GET /api/github/orgs  → 200 []
    

    The test account can see no repositories. So:

    • the repo picker payload has never been observed — the populated row shape is unknown
    • the githubSource create-time field names cannot be exercised end to end (work-consolidation.md P3-C records {owner, repo, path, ref} as unconfirmed)
    • lastRefreshedAt / refreshStatus / refreshError have never been seen on the wire, so a refresh-state UI would be rendering invented fields

    Building a picker and a create body against an empty array is exactly how the G21 link-metadata and G25 org-member defects happened: green tests against fabricated fixtures, a feature that never worked.

    What we need from you

    Either grant the .env contract-test account (messenger@interlinedlist.com) access to at least one repository — a throwaway public repo with two or three issues is enough — or capture the web app's own network calls for a GitHub-backed list creation and paste them here:

    1. the request body the web sends to POST /api/lists when creating from a repo
    2. one row of GET /api/github/repos populated
    3. a list object for a GitHub-backed list, so the refresh metadata field names are visible

    Option 1 is better: it also unblocks the Refresh from GitHub route, which this issue notes may not exist at all — and that cannot be determined from an account with no repos.

    One thing that can ship without it

    The New List sheet still says "GitHub source fields will be sent in a future update." If you would rather not wait, that dead section can be removed and replaced with an honest "connect a repository on the web for now" pointer — this issue's acceptance criterion "no control in the New List sheet promises something in a future update" would then be met. Say the word and I will do that as a small PR.

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

    enhancementNew feature or requestparityWeb-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