Skip to content

feat: enforce R-FC-013 (open item in past cycle -> current cycle) hourly - #57

Merged
BigLep merged 5 commits into
masterfrom
feat/r-fc-013-past-cycle
Aug 22, 2026
Merged

BigLep merged 5 commits into
masterfrom
feat/r-fc-013-past-cycle

Conversation

@BigLep

@BigLep BigLep commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds R-FC-013: any open issue or PR on the board whose Cycle is set to an already-ended iteration gets moved to the current cycle, so stale cycle assignments don't understate what's actually in flight this cycle.
  • Implemented as PastCycleRule in foc-mechanical-rules, alongside the existing R-FC-012 (CycleRule) — shares the iteration-lookup logic (refactored into get_current_and_past_cycle_titles) and the same mutation-log-guard pattern, so a human who deliberately moves an item back to a past cycle won't get overridden.
  • Registered in registry.py, so it runs automatically in the existing hourly GitHub Actions workflow — no workflow changes needed.
  • One board query per run already returns each item's Cycle value (via has:cycle + Cycle field), so no per-item reads are added; the only per-item API calls are writes, and only for items actually being moved.

Test plan

  • uv run pytest -m "not integration" — all 32 tests pass, including 8 new tests for PastCycleRule (move-to-current, already-current skip, future-cycle skip, human-reversion flag, non-blocking prior history for a different cycle, no-active-cycle error, dry-run, isinstance(Rule))
  • Live dry-run against the FOC board (uv run foc-mechanical-rules --dry-run) once merged/deployed — no GITHUB_TOKEN was available in this session to verify against real board data

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com

https://claude.ai/code/session_015WuAyMAjh1sno5spL7R3fV

Adds a new mechanical rule alongside R-FC-012: any open issue or PR whose
Cycle points at an already-ended iteration gets moved to the current cycle,
so stale cycle assignments don't hide in-flight work from cycle planning.
Reuses R-FC-012's iteration-fetch and mutation-log-guard pattern so a human
who deliberately moves an item back to a past cycle won't get overridden.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015WuAyMAjh1sno5spL7R3fV
@BigLep
BigLep requested a review from rjan90 as a code owner August 22, 2026 17:34
Copilot AI lite review requested due to automatic review settings August 22, 2026 17:34
@FilOzzy FilOzzy added this to FOC Aug 22, 2026
@github-project-automation github-project-automation Bot moved this to 📌 Triage in FOC Aug 22, 2026
GitHub's ProjectV2IterationField.configuration.iterations only returns
current and future iterations -- completed ones live under the separate
completedIterations field. A live dry-run against the real board showed
every past-cycle item being classified as "not a past iteration (future or
unknown)" because we never fetched that history. Combine both lists in
_fetch_iterations so R-FC-013 can actually see past cycles; R-FC-012 is
unaffected since it only ever needs the iteration containing today.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015WuAyMAjh1sno5spL7R3fV
@BigLep

BigLep commented Aug 22, 2026

Copy link
Copy Markdown
Contributor Author

R-FC-013 dry-run against the live board

Ran uv run foc-mechanical-rules --dry-run against the real FOC board (auth via gh auth token) to preview R-FC-013's mutations before this merges.

Found a real bug in the process: the first dry-run showed 0 items ever matching "past cycle" -- GitHub's GraphQL schema splits current/future iterations from completed ones (configuration.iterations vs configuration.completedIterations), and the query only fetched the former, so no genuinely past cycle was ever visible. Fixed in the latest commit (now fetches and merges both).

Result after the fix: current cycle is 202609-1 (started today, 2026-08-22). Every open item still holding the just-ended 202608-2 cycle is now correctly detected as past and would move forward. Because the cycle boundary crossed today, this is a one-time-flavored flood -- 179 items, all 202608-2 -> 202609-1, 3 skipped (already in 202609-1).

All 179 mutations R-FC-013 would apply (dry-run, nothing changed on the board)

R-FC-013 (cycle) — applied=179, skipped=3

Rule: https://github.com/FilOzone/tpm-utils/blob/master/foc-board-rules/field-completeness.md#r-fc-013-open-items-in-a-past-cycle-should-move-to-the-current-cycle

  • filecoin-project/filecoin-pin#637 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/dealbot#606 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#78 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-services#369 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/tpm-utils#2 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#362 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/dealbot#427 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#417 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#439 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#457 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#458 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#470 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/early-repair#19 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-cloud#306 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-cloud#300 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-cloud#295 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-cloud#281 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/curio#1264 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/early-repair#26 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/early-repair#27 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/early-repair#28 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/early-repair#30 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-services#503 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-cloud#325 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-cloud#326 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin-website#206 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-services#531 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/dealbot#605 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-cloud#335 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-docs#2470 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-docs#2469 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#599 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-cloud#341 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-services#540 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/early-repair#40 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/early-repair#41 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/early-repair#42 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/early-repair#43 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/synapse-sdk#861 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/synapse-sdk#862 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-pay-explorer#237 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-pay-explorer#238 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#691 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#670 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#667 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#664 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#657 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#654 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#653 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#652 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#649 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#648 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#646 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#607 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#605 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#323 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/contracts#17 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#615 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-pay-explorer#244 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-services#546 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#703 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/infra#323 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/infra#330 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/dealbot#647 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/infra#332 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/pdp-explorer#145 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#704 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/synapse-sdk#887 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/solstice#5 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/builtin-actors#1764 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/lotus#13720 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/solstice#9 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/solstice#7 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#705 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/dealbot#656 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/dealbot#657 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/dealbot#658 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-pay-explorer#277 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/pdp-explorer#153 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/pdp-explorer#154 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/pdp-explorer#155 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/ipfs2foc#62 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/infra#322 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/team-skills#9 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-cloud#346 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/team-skills#10 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-services#563 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#707 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/synapse-sdk#905 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/foc-devnet#167 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#708 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#710 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-pay-explorer#294 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/synapse-sdk#908 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/curio#1405 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#711 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/curio#1410 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#712 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/dealbot#678 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/dealbot#679 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/dealbot#680 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/curio#1422 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#650 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filbeam/worker#713 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/solstice#24 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/curio#1424 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/infra#364 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#652 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/curio#1426 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/foc-devnet#170 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/curio#1429 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/curio#1430 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/solstice#25 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#653 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#655 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#657 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#656 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#654 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/synapse-sdk#917 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/pdp#302 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/synapse-sdk#921 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/curio#1431 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/dealbot#682 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/dealbot#683 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/synapse-sdk#922 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/curio#1440 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/foc-observer#102 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/infra#367 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/curio#1441 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/synapse-sdk#923 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/early-repair#57 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-services#587 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/pdp#304 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#658 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-pay-explorer#320 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-pay-explorer#321 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#659 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#660 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#661 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#662 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#663 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#664 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/dealbot#684 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/pdp-explorer#167 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/pdp-explorer#168 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/curio#1443 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/dealbot#685 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/curio#1444 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-services#591 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/squid-evm-funding#27 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/builtin-actors#1762 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/FIPs#1278 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/solstice#26 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/solstice#27 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/curio#1446 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/synapse-sdk#925 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/curio#1447 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/synapse-sdk#927 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/synapse-sdk#928 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/dealbot#686 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-pay-explorer#328 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-services#597 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin#668 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-pay-explorer#331 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-pay-explorer#332 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/curio#1448 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/curio#1449 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/foc-devnet#179 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/curio#1450 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/foc-devnet#180 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/foc-devnet#181 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-pay-explorer#339 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/filecoin-pay-explorer#340 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/synapse-sdk#930 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • filecoin-project/filecoin-pin-website#234 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/synapse-sdk#931 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/synapse-sdk#932 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/synapse-sdk#933 applied: 202608-2 -> 202609-1 (dry-run: would move)
  • FilOzone/synapse-sdk#934 applied: 202608-2 -> 202609-1 (dry-run: would move)

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.

Pull request overview

Adds hourly R-FC-013 enforcement to move open items from completed cycles into the current cycle.

Changes:

  • Adds and registers PastCycleRule with mutation-log safeguards.
  • Refactors cycle lookup and adds tests.
  • Updates mechanical-rule and board documentation.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
File Review summary
foc-mechanical-rules/tests/test_past_cycle_rule.py Adds coverage for R-FC-013 behavior.
foc-mechanical-rules/README.md Documents the new rule and safeguards.
foc-mechanical-rules/foc_mechanical_rules/rules/cycle.py Critical (4): completed iterations are not queried. Critical (3): draft notes are included despite the issue/PR scope. Moderate (4): mutations add per-item reads. Moderate (2): mutation logs use a stale cycle value instead of the API-observed value.
foc-mechanical-rules/foc_mechanical_rules/registry.py Critical (1): overlapping workflow runs can race because no concurrency or durable locking is configured.
foc-board-rules/future-ideas.md Updates the implemented-rule inventory.
foc-board-rules/field-completeness.md Defines canonical R-FC-013 behavior.
Suppressed comments (1)

foc-mechanical-rules/tests/test_past_cycle_rule.py:32

  • The fixture puts both ended cycles under configuration.iterations, but the actual GraphQL contract used elsewhere in this repo returns ended cycles under configuration.completedIterations (github-projects-client/github_projects_client/fields.py:31-42). That lets these tests pass while the implementation ignores completed cycles—the exact production failure. Put the two past entries in completedIterations and leave only active/future entries in iterations so this test exercises the real response shape.
                    # GitHub's GraphQL schema splits current/future iterations
                    # from completed ones -- ``iterations`` alone never
                    # includes a truly past cycle. Mirror that split here so
                    # a regression back to reading only ``iterations`` fails
                    # this test instead of passing by accident.

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

Comment thread foc-mechanical-rules/foc_mechanical_rules/registry.py
Comment thread foc-mechanical-rules/foc_mechanical_rules/rules/cycle.py Outdated
Comment thread foc-mechanical-rules/foc_mechanical_rules/rules/cycle.py Outdated
Comment thread foc-mechanical-rules/foc_mechanical_rules/rules/cycle.py
Comment thread foc-mechanical-rules/foc_mechanical_rules/rules/cycle.py Outdated
- Serialize the hourly workflow (concurrency group, cancel-in-progress:
  false) so overlapping runs can't race on the shared mutation-log cache.
- Skip draft notes: has:cycle matches any board item, including notes
  with no linked repo issue/PR, which had no Repository/Id to mutate.
- Pass the item's node ID (already returned by list_items) instead of
  "owner/repo#number" to set_field_value, skipping its internal per-item
  get_item lookup and recording the API-observed old_value instead of
  the value seen at selection time (which race-guard PRs correctly
  distrust).

Verified against the live board (dry-run): same 179 applied / 3 skipped
as before this commit, confirming the refactor didn't change outcomes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015WuAyMAjh1sno5spL7R3fV
@BigLep

BigLep commented Aug 22, 2026

Copy link
Copy Markdown
Contributor Author

Addressed all 5 Copilot review findings (concurrency race, draft-note guard, per-item read via node ID, stale old_value, and the already-fixed completedIterations gap) — see 3398cb3 and inline replies. Re-ran the dry-run against the live board after the fixes: same 179 applied / 3 skipped, confirming the refactor didn't change outcomes.

BigLep and others added 2 commits August 22, 2026 11:19
Validating one new rule against the live board (e.g. this PR's R-FC-013)
meant reading its section out of a combined log for every registered rule.
--rule R-FC-013 (repeatable) filters to just the named rule(s); an unknown
id exits with a list of known ones instead of silently running everything.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015WuAyMAjh1sno5spL7R3fV
…-012

- Add a README "API call pattern per rule" table (select() cost, per-run
  memoized reads, per-item reads/writes) so the call shape of each rule
  is reasoned about wholistically instead of read out of the source.
  Points out R-PR-001's per-PR REST reads as a known, currently-acceptable
  gap with no batched equivalent.
- Apply R-FC-013's node-ID mutation fix to R-FC-012 (CycleRule) too, for
  the same reason: skips set_field_value_bulk's per-item get_item lookup.
- Log batch mutations (github_projects_client already batches GraphQL
  mutations 25-at-a-time, but every rule always calls it with a 1-item
  list) as a future-ideas.md follow-up -- it requires restructuring the
  shared Rule.run()/apply_one contract, so it's out of scope here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015WuAyMAjh1sno5spL7R3fV
@BigLep
BigLep merged commit e07bfa0 into master Aug 22, 2026
7 checks passed
@BigLep
BigLep deleted the feat/r-fc-013-past-cycle branch August 22, 2026 18:25
@github-project-automation github-project-automation Bot moved this from 📌 Triage to 🎉 Done in FOC Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 🎉 Done

Development

Successfully merging this pull request may close these issues.

3 participants