Skip to content

Auto-apply repository topics to GitHub-synced projects - #340

Open
demo-zexuan wants to merge 9 commits into
alphaXiv:mainfrom
demo-zexuan:feat/github-repo-topics
Open

demo-zexuan wants to merge 9 commits into
alphaXiv:mainfrom
demo-zexuan:feat/github-repo-topics

Conversation

@demo-zexuan

@demo-zexuan demo-zexuan commented Sep 15, 2026

Copy link
Copy Markdown

Refs #339

Paper projects pile up as private repositories whose names all look alike, and GitHub search cannot tell them apart. The paper id is already stored locally, so publish it as repository topics and make projects findable with filtered search:

user:<account> topic:openresearch
user:<account> topic:arxiv-2401-12345
user:<account> topic:paper-repro

What it does

  • Adds openresearch to every synced project, plus arxiv-xxxx and paper-repro when the project comes from a paper. The arxiv id drops its version suffix, so v1 and v2 of the same paper share one topic instead of splitting search results.
  • Accepts extra topics on top, per project.
  • Auto-tagging is a toggle: per project at creation, editable later in project settings, and a separate default for new projects.
  • Backfills projects that already sync to GitHub, once, through the v2 schema migration. The migration only touches projects that are actually publishing, so a project someone deliberately un-synced is not re-tagged.

Design decisions worth reviewing

Merge instead of replace. PUT /repos/{owner}/{repo}/topics replaces the whole set, so publishing the computed set directly would wipe out topics added by hand on GitHub, and two projects pointing at the same repository would overwrite each other. set_repo_topics reads the repository's current topics and takes the union, skips the write when nothing changed, and never publishes an empty set. The trade-off: orx cannot remove a topic, so dropping one from a project leaves it on the repo until it is removed on GitHub.

Topic publication is best-effort. Topics are metadata on a push that already succeeded, so a GitHub failure there logs a warning rather than failing project creation or an experiment publish.

Points for the reviewer

  • The gh api topic calls are covered by unit tests at the pure-function and SQL level only — I have not exercised them against a live repository, so the PUT/GET round-trip is the main thing to verify.
  • The merge semantics mean removal is not supported yet (see above); say the word if you would rather have replace-with-managed-set instead.
  • Version is unchanged, so merging this does not cut a release.

Verification

cargo fmt --all --check and cargo clippy --all-targets -- -D warnings pass. cargo test passes except commands::up::tests::pty_relays_input_output_resize_exit_and_cancel, which fails in my environment because the sandbox denies openpty. UI tsc --noEmit, the i18n and style-token linters pass, and ui/dist is rebuilt.

Test Image

image image image

Paper projects pile up as private repositories whose names all look
alike, and GitHub search cannot tell them apart. The paper id is
already stored locally, so publish it as repository topics and make
projects findable by filtered search.

- Add openresearch to every synced project, plus arxiv-xxxx (version
  suffix dropped so v1 and v2 share a topic) and paper-repro when the
  project comes from a paper.
- Let people add their own topics on top, and turn the automatic ones
  off, per project and in the new-project defaults.
- Edit both in project settings, not only at creation time.
- Merge with the repository's existing topics rather than replacing
  them: a repository shared by several projects, or one whose owner
  added topics by hand, keeps them. Never publish an empty set.
- Backfill projects that already sync to GitHub, once, via the v2
  schema migration.

Publishing topics decorates a push that already succeeded, so a GitHub
failure there warns instead of failing project creation.

Refs alphaXiv#339
Copilot AI lite review requested due to automatic review settings September 15, 2026 04:52

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There are a few confirmed functional issues (topic editor state not resetting on project switch, defaults prompt overwriting auto-topics default, merge semantics risking topic loss on GET failure, and topic edits not being published immediately) that should be addressed before approval.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

This PR adds first-class support for publishing GitHub repository topics for GitHub-synced projects, including configurable defaults, per-project overrides, and a one-time backfill for already-synced projects via a schema migration. It extends both the UI and the local HTTP API / SQLite store so topics can be toggled and edited after project creation.

Changes:

  • Add repo-topic derivation/sanitization + best-effort publication (merge/union semantics) during GitHub sync/push flows.
  • Add per-project topic settings (auto-topics toggle + custom topics) and defaults for new projects, including a migration backfill.
  • Update UI flows (new project form + settings) and i18n strings to expose the new controls.
File summaries
File Description
ui/src/components/SettingsPage.tsx Adds defaults toggle + per-project topic editor UI and wires it to the API.
ui/src/components/NewProjectForm.tsx Adds project-creation fields for auto-topics + custom topics.
ui/src/api.ts Extends API types and PATCH payload to include topic settings; extends project defaults payload.
ui/messages/en.json Adds strings for topic toggles/editor.
ui/messages/zh-CN.json Adds strings for topic toggles/editor.
ui/messages/fa.json Adds strings for topic toggles/editor.
ui/dist/index.html Rebuilds UI bundle reference after UI changes.
src/local/github.rs Implements topic normalization, topic derivation, and GitHub topic GET/PUT merge logic via gh api.
src/commands/up.rs Extends create/update project API and triggers topic sync after GitHub push/sync operations.
src/store.rs Adds SQLite columns for topic settings and schema migration/backfill for already-synced projects.
src/local/model.rs Adds new fields to LocalProject plus defaulting and row decoding for the new columns.
src/local/projects.rs Stores topic settings on project creation.
src/telemetry.rs Adds persisted default for auto-topics-for-new-projects.
src/config.rs Exposes getters/setters for the new auto-topics default.
src/local/starter.rs Updates test fixtures to use Default::default() for new columns.
src/local/resolve.rs Updates test fixtures to use Default::default() for new columns.
src/local/opencode.rs Updates test fixtures to use Default::default() for new columns.
src/local/experiments.rs Updates test fixtures to use Default::default() for new columns.
src/local/demo.rs Updates demo seed/test fixtures to use Default::default() for new columns.
src/local/chat/mod.rs Updates test fixtures to use Default::default() for new columns.
Review details
  • Files reviewed: 19/21 changed files
  • Comments generated: 4
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/local/github.rs
Comment thread src/commands/up.rs
Comment thread ui/src/components/SettingsPage.tsx Outdated
Comment thread ui/src/components/SettingsPage.tsx Outdated
- Abort topic publication when the repository's current topics cannot be
  read. Treating an unreadable list as empty let a transient GET failure
  turn the merge into a replace, deleting the topics it exists to keep.
- Publish topics right after a settings edit instead of waiting for the
  next push, so the editor has a visible effect.
- Drop the auto-topics flag from the publish guard. It decided whether
  anything was worth publishing, which meant custom topics never reached
  the repository while automatic tagging was off.
- Re-seed the topics editor when the project changes; it was seeded once,
  so switching projects could show and then save the previous project's
  topics onto the new one.
- Keep the auto-topics default when answering the one-time sync prompt,
  which only asks about syncing.
The publish guard used it to decide whether to sync; that guard now
checks whether the computed topic set is empty, so nothing calls it.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The UI changes introduce a couple of concrete accessibility/UX correctness issues (control labeling and dirty-state normalization) that should be fixed before approval.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 19/21 changed files
  • Comments generated: 4
  • Review effort level: Lite

Comment thread ui/src/components/NewProjectForm.tsx Outdated
Comment thread ui/src/components/NewProjectForm.tsx
Comment thread ui/src/components/SettingsPage.tsx Outdated
Comment thread ui/src/components/SettingsPage.tsx
- Wrap the new-project topics checkbox in a label so the visible text
  names the control and the whole row is clickable.
- Give the extra-topics field an accessible name; it relied on its
  placeholder, which disappears once the user types.
- Point the per-project topics switch at the repository-topics label
  instead of the new-project defaults one, which described the wrong
  thing.
- Compare the parsed topic sets when deciding dirty state. Comparing
  raw strings treated "a,b" and "a, b" as an edit and sent a pointless
  PATCH.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There are two verified correctness/operational issues in the new defaults/topic publication flows that can cause unintended settings overwrites and unnecessary repeated GitHub API writes.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 19/22 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment thread src/local/github.rs
Comment thread ui/src/components/SettingsPage.tsx
- Skip the topic PUT by comparing the stored and merged sets rather than
  their order. GitHub need not echo our PUT order back, so the previous
  check would have re-written topics on every push and burned API calls.
- Drop the cached prompt-seen flag from the auto-topics toggle. That
  handler only owns the auto-topics default, and re-sending a stale flag
  could undo a one-time prompt another flow had just answered.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The GitHub-sync default prompt flow updates server settings but does not update/invalidate the cached project-defaults query, which can leave the UI stale and cause later toggles to accidentally overwrite the sync default.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 19/22 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread ui/src/components/SettingsPage.tsx
The one-time prompt updated the server but left getProjectDefaultsQuery
holding the pre-answer values. The settings page and the new-project form
both read that cache, so a later toggle would send the stale value back
and undo the answer. Write the response into the shared cache instead.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The project-defaults endpoint currently overwrites the independent auto-topics default even when the client doesn’t send it, which can unintentionally clobber user preferences (forward/backward compatibility issue).

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 19/22 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment thread src/commands/up.rs Outdated
Comment thread ui/src/components/SettingsPage.tsx Outdated
Sending an absent auto-topics field is not neutral: the handler derived a
value from the sync flag and stored it, so a client that only toggled
syncing froze the auto-topics default at that answer for good. Persist the
field only when the client sends it and let the existing fallback track
the sync default for anyone who never chose one.

The sync toggle in the defaults tab now omits the field for the same
reason, and the stale-cache comment on the auto-topics toggle says what
the handler actually sends.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There are at least two concrete behavioral bugs (old-style arXiv topic normalization dropping archive prefixes, and project creation persisting hidden topic fields when GitHub sync is off) that should be corrected before approval.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 19/22 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment thread src/local/github.rs Outdated
Comment thread ui/src/components/NewProjectForm.tsx Outdated
- Preserve the archive prefix of old-style ids: hep-th/9901001 now yields
  arxiv-hep-th-9901001 instead of arxiv-9901001, which several archives
  could share.
- Make the parser total. It previously sliced at fixed byte offsets and
  split on the last slash, so a multi-byte or odd id could panic or index
  past the end. It now walks character boundaries and validates the id
  shape, returning no topic when the id cannot be understood, because
  publishing topics must never take a successful push down with it.
- Understand URL forms uniformly: query strings, fragments, /pdf/ links
  and a trailing .pdf all resolve to the same topic, and the version
  suffix is dropped whether or not the extension is still attached.
- Stop sending topic fields when GitHub sync is off in the new-project
  form. They are hidden in that state, so the request was persisting
  values the user could not see.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There are a few concrete issues (accessibility labeling ambiguity in the topic editor UI and a redundant settings.json read) that should be addressed before approval.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (1)

ui/src/components/SettingsPage.tsx:3197

  • This input’s aria-label is the generic "Repository topics" (also used by the toggle above). For accessibility, the field should be labeled as the extra/comma-separated topics input so it’s distinguishable from the toggle.
                      aria-label={m.settings_page_repository_topics()}
  • Files reviewed: 19/22 changed files
  • Comments generated: 3
  • Review effort level: Lite

Comment thread src/telemetry.rs
Comment thread ui/src/components/NewProjectForm.tsx Outdated
Comment thread ui/src/components/SettingsPage.tsx Outdated
- Derive the auto-topics fallback from the same settings load instead of
  calling back into github_for_new_projects(). The second read was extra
  I/O and could see a different file if settings.json was rewritten in
  between.
- Name the new-project topics input after the field itself rather than
  the generic "Repository topics" label.
- Give the per-project topics switch its own label, so it is no longer
  announced with the same name as the text field below it.
@demo-zexuan

demo-zexuan commented Sep 15, 2026

Copy link
Copy Markdown
Author

I've addressed all 18 findings from the review so far. One correction for the record, though: every one of them landed in code this PR introduces (the topic helpers in github.rs, sync_project_topics and the settings endpoint in up.rs, github_auto_topics_for_new_projects in telemetry.rs, and the topic editor in the two UI components), not in pre-existing code. So I don't want to file them under "historical".

Where I do agree is on scope. The first three rounds found real correctness problems, and I fixed all of them — worth calling out that the strictest one was mine to own: treating an unreadable topics list as empty turned the merge into a replace, and the publish guard gated on the auto-topics flag, which meant custom topics never reached the repository while automatic tagging was off.

Since then the findings have shifted to accessibility and code hygiene in the same new code (label wording, duplicate aria-labels, a stale-cache echo, a double settings read, a misleading comment). Those are all fixed too, but I'm going to stop following the loop here rather than keep growing this PR: each round of fixes has been producing new reviewable surface of its own, and the diff is already larger than the feature warrants.

If anything else comes up that is a correctness problem rather than polish, I'll of course fix it. Otherwise I'd rather let the maintainers' review be the judge of the remaining nits, instead of expanding this PR further.

One thing I can't do myself: CI has been queued on every push and has not run once (action_required, 0s) — first-time contributor workflows need a maintainer to approve them. Locally cargo fmt --check, cargo clippy --all-targets -- -D warnings, tsc --noEmit, the i18n and style linters and vite build all pass; the only local test failure is a PTY test that needs openpty, which this sandbox denies. So the required fmt, clippy, test and version sanity checks are still unverified on GitHub.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants