Skip to content

Add a sync of shared fields across the Interra boards - #1

Merged
timlichtenberg merged 2 commits into
mainfrom
tl/board-sync
Oct 14, 2026
Merged

timlichtenberg merged 2 commits into
mainfrom
tl/board-sync

Conversation

@timlichtenberg

Copy link
Copy Markdown
Member

What this does

Adds a small Python package and a scheduled workflow that keep shared fields equal across the Interra Management board, the Interra Collaboration board, the PROTEUS Development Roadmap and the org issue fields of each issue. When a value changes in one place, the next run writes it to the others.

Why

The same dates, priorities and statuses are kept by hand on three boards and on the issues themselves, and they drift apart. The roadmap views are only useful when start and target dates agree everywhere.

Changes

  • board_sync/core.py: the sync rules. Newest change wins; a value that every place already shows wins without a conflict; a missing field, a field created again or an emptied field never clears the other places; an issue that leaves a board and comes back starts from its old record.
  • board_sync/github.py: GraphQL reads of the three boards and of the org issue fields, with change times from the issue timeline, and the four write mutations. Retries on server errors, network errors and rate limits; a list longer than one page stops the read.
  • board_sync/__main__.py: dry run by default; with --apply a run stops above 20 new writes until a person approves the plan, puts writes that failed before last, parks writes that keep failing, saves its state even when it is cut short, and prints only counts, field names and error types to the log.
  • scripts/store_state.sh: stores the state, snapshot and report of a run in the private board-sync-state repository; the files of the run replace the stored ones whole.
  • .github/workflows/sync.yml: runs every 30 minutes on main as a GitHub App and pins every action by commit.
  • .github/workflows/tests.yml: lint and tests on every pull request, with a coverage floor of 95%.
  • README.md: the rules, the setup and the exit codes. .gitignore keeps local run files with private board data out of commits.

Testing

  • 134 tests, 99% line and branch coverage; each of the safety rules was checked by breaking it on purpose and seeing a test fail.
  • The store script is tested against real local git repositories: a first run without a state file, a conflicting remote change, no change, a push that is rejected once, a failed commit, and a folder that is not a repository.
  • A read-only run against the live boards: 293 issues, 1688 planned writes, 36 conflicts, no issue data in the log output.
  • The four mutations were sent with ids that do not exist and reached the id lookup, so their shape matches the live schema. A missing board was checked against the live API, which answers with a NOT_FOUND error that the read turns into a named error.
  • Not tested yet: a real write through the App, and the workflow on GitHub. Before the first run, the App, the environment and the deploy key need to be set up as in the README.

I want the fields that the Interra boards, the PROTEUS roadmap and the issues share to stay equal, so that a change in one place shows up in the others. Start date, target date and priority are synced in all four places: the Interra Management board, the Interra Collaboration board, the PROTEUS roadmap and the org issue fields of each issue. Workload is synced on the three boards and sets the Effort issue field, and status, category, sub-team and release are synced between the two Interra boards.

The package reads all places, decides per issue and field which value changed last, and writes that value to the other places. A state file in a private repository records what each place showed in the previous run, so a cleared value can be told apart from one that was never set. A scheduled workflow runs it every 30 minutes as a GitHub App; the public log shows counts, field names and error types only.
A NOT_FOUND error deeper in a board read could null a value, which would then read as empty and spread as a clear. The board read now accepts NOT_FOUND only at the board or org itself, which is how a missing board answers. The tests also check that the page cursor is passed on, and the README setup step names the protection rule this repository uses.
@timlichtenberg
timlichtenberg merged commit ecba9d3 into main Oct 14, 2026
1 check passed
@timlichtenberg
timlichtenberg deleted the tl/board-sync branch October 14, 2026 20:58
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.

1 participant