feat(dashboard): update tickets/ from GitHub without re-importing#1222
Merged
Conversation
Importing was a one-shot: the only way to pick up issues filed since was to run it again over a directory that already had tickets in it. The tickets view now offers "Update from GitHub" once there is something to update, and says when tickets/ last caught up. The update resumes rather than refills. It reads `lastImportedAt` from tickets/meta.json and asks GitHub only for what changed since, both issues and comments, then reconciles: an existing ticket is edited in place, the .spike.md and .plan.md written against it are kept, and a closed issue's ticket goes. With no stamp on record it falls back to bringing every open issue across, so a repo imported before this existed still works. The stamp is written by the agent, in the same commit as the tickets it describes, so a run whose work never landed cannot leave behind a claim that those issues were imported. The importer writes it too, which is what gives the first update somewhere to resume from. The timestamp recorded is the one taken before the fetch, not after: an issue edited mid-run is then picked up by the next update rather than falling into the gap. Closes #1208
Member
Author
|
The red The repo is right and issue #326 is stale by one character ( |
suleimansh
marked this pull request as ready for review
July 26, 2026 11:17
4 tasks
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.
Closes #1208
Answering the question in the OP first
Yes, the API has it. Two endpoints, and the update uses both:
gh issue list --state all --search "updated:>=<lastImportedAt>"for issues touched since the stamp.updated, notcreated, so an old issue that was edited or newly commented comes back too.gh api --paginate "repos/{owner}/{repo}/issues/comments?since=<lastImportedAt>"for the discussion, whichissues?since=does not return bodies for.What
The tickets view gets Update from GitHub once there is something to update, next to a line saying when
tickets/last caught up. The empty state keeps its Import tickets from GitHub button: same work, but "update" is a strange word for filling a directory that has never been filled.Its own preset (
update-tickets), not a flag on the import, because the two ask for different work. The import fills an empty directory. This one reconciles a full one:.spike.mdand.plan.md(those are our work, not GitHub's, and re-importing must not discard them)The stamp
tickets/meta.jsonholds{"lastImportedAt": "<ISO 8601>"}, exactly as the OP suggested.Written by the agent, not by the framework, which is the part worth arguing about. The file then travels in the same commit as the tickets it describes, through the same merge, so a run whose work never landed cannot leave behind a stamp claiming those issues were imported. A framework-written stamp would have had to guess whether the agent's import succeeded.
The timestamp is taken before the fetch, not after. An issue edited while the update runs is then picked up by the next update instead of falling into the gap between the fetch and the stamp. Re-importing one issue is cheap; silently missing one is not.
The importer writes the stamp too, so the first update has somewhere to resume from instead of re-reading the whole repo.
Framework-side,
readTicketsMetaanswers{}for every way the file can be unusable (absent, unreadable, not JSON, not an object, no key, wrong type, unparseable date). It is written by an agent and rendered into the UI, so "we do not know when this last synced" is the only safe failure, and it is pinned by a test with a case per shape.Verified in the real UI
Isolated
HOME, a scratch git repo with one ticket, the worktree build on a spare port,claudestripped fromPATH:Updated from GitHub 3h ago+ the buttonmeta.jsondeleted:No record of an import yet, button still offeredRun now=> shows new session instead of the newly created session #1191's rule)The prompt sent is pinned as
presets.updateTickets.render()verbatim in the panel test, which is where the drift risk actually is.Tests
the-framework1371 pass / 1 fail,framework-dashboard512 pass / 0 fail, full monorepo typecheck 22/22.The one failure is the same #1122 re-home flake (#1165) that hit #1220, on files this branch does not touch.
Three catalog invariants moved rather than broke, and they are worth a look in review since each states a rule:
update-ticketsLAUNCHER_PRESETSgains it, so it is offered from the launcher as well as the panel