Skip to content

feat: sync act-board Project fields and snapshot.json - #71

Merged
WilcoFiers merged 2 commits into
mainfrom
issue-65-project-sync
Sep 18, 2026
Merged

WilcoFiers merged 2 commits into
mainfrom
issue-65-project-sync

Conversation

@WilcoFiers

Copy link
Copy Markdown
Member

Closes #65

Adds Projects v2 field sync and a snapshot writer so the weekly board job can update Status without rewriting unchanged values.

  • yarn sync-act-board-project writes Status (required) and optional numeric/date/URL fields. Missing optional fields are skipped with a warning; a missing Status field fails the command.
  • yarn write-act-board-snapshot writes data/snapshot.json only when the classifier JSON changed.
  • The GitHub Action that runs this weekly lives in act-board (separate PR).

Test plan

  • yarn test src/approval-report/__tests__/sync-project.test.ts
  • No live Project API required for these tests

Made with Cursor

Add a Projects v2 field sync and snapshot writer so the weekly board job can update Status without rewriting unchanged values.

Co-authored-by: Cursor <cursoragent@cursor.com>
@WilcoFiers

Copy link
Copy Markdown
Member Author

PR Review: #71 — feat: sync act-board Project fields and snapshot.json

Branch: issue-65-project-sync → main · Head: 4b444ed · Size: +1247 / −19 across 10 files
Verified locally: npx tsc --noEmit clean · npx eslint clean · npx jest sync-project.test.ts 7/7 passing

This is well-structured work: the dependency-injected ActBoardProjectClient mirrors the ActBoardGitHubClient pattern from the existing act-board.ts, the CLI shape matches upsert-act-board.ts, and extracting loadRuleApprovalRows into a shared module is a genuine improvement over the duplicated readRows it replaces. The GraphQL mutations themselves (addProjectV2ItemById, updateProjectV2ItemFieldValue with ProjectV2FieldValue!, clearProjectV2ItemFieldValue) are all correct, and the inline fragments on ProjectV2ItemFieldSingleSelectValue / NumberValue / DateValue / TextValue resolve field against the right concrete types.

Two issues below are correctness defects. Per the issue context, the weekly workflow lives in act-board PR #3 and is out of scope here — but the CLI surface lines up with that workflow.

Secrets check: clean. GITHUB_TOKEN is only ever read from the environment and passed to Octokit, never logged or written.


Critical

1. fieldValues(first: 20) is unpaginated, and a real board item will likely exceed 20 values — silently defeating skip-unchanged

sync-project.ts#L544

The items connection is correctly paginated with pageInfo, but the nested fieldValues connection requests only the first 20 nodes and never asks for pageInfo. Built-in Project fields occupy slots alongside custom ones; union members without fragments still consume the 20-node budget. Missing values make fieldValueMatches treat fields as changed and rewrite them every run. Raise the limit and paginate fieldValues the same way items is paginated.

2. Board-issue selection diverges from upsertActBoardIssues, so the Project can be synced against a closed duplicate

sync-project.ts#L134

This loop sorts issues by number ascending and keeps the first match per rule id, ignoring state. selectManagedIssues in act-board.ts prefers an open issue over a lower-numbered closed one. Export selectManagedIssues and reuse it here. Add a test: closed #10 + open #20 → manage the open issue.


Important

3. fields(first: 50) is also unpaginated — paginate project fields so a missing Status is a real miss, not a truncated query.

4. dataType is read but never validated — if an optional field has the wrong type, warn-and-skip instead of aborting mid-sync.

5. Field resolution runs per row — resolve the field plan once; optionalFieldsSkipped should count fields, not fields × rows. Update the test that currently asserts toBe(9) for a single row.

6. No per-row error isolation — wrap each row so one GraphQL failure does not drop the accumulated result; exit non-zero if any row failed.

7. The Octokit GraphQL layer is untested — add mocked octokit.graphql tests for pagination and mutations, following act-board.test.ts.


Suggestions

8. Move RULE_STATUSES to the test fixture or make it exhaustive against RuleStatus.

9. Split snapshot tests into snapshot.test.ts; cover loadRuleApprovalRows rejection of malformed JSON.

10. Project-not-found error should mention token/project-access as well as ACT_BOARD_PROJECT_NUMBER; document that --projectOwner is org-only.

11. Count rowsWithoutIssue in the result summary.

12. Reject non-numeric --projectNumber (do not parse "12abc" as 12); normalize both sides of date compare; only increment fieldsUpdated when a mutation actually ran.

13. snapshotsEqual key-order skip can freeze stale formatting — prefer byte-stable formatSnapshotJson compare, or document the intent.

14. .worktrees/ in .gitignore is fine; no change required.

Recommended Action: Request Changes

The two Critical items are silent-failure modes in the weekly job. Address all findings in this comment.

Paginate project metadata and values, validate field plans once, and
isolate per-row failures while preserving complete sync results. Reuse
managed issue selection and expand tests for GraphQL and snapshots.

Co-authored-by: Cursor <cursoragent@cursor.com>
@WilcoFiers
WilcoFiers merged commit 0e5820e into main Sep 18, 2026
1 check passed
@WilcoFiers
WilcoFiers deleted the issue-65-project-sync branch September 18, 2026 10:46
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.

AFK: Weekly Action to sync Project fields and snapshot.json

1 participant