Repository navigation
update: render organically-filed issues into an unplanned region (#3) - #5
Conversation
Issues filed directly on GitHub — no [MN-k] title prefix — were invisible to the generated regions even when they carried labels and GitHub milestones; in agda-native-air that hid the entire active agda-mcp hardening wave (its #68). A region marked BEGIN/END GENERATED: unplanned now mirrors them: grouped by the GitHub milestone title verbatim (no-milestone group last, rendered under #### headers), sorted by ascending issue number within a group, in the same block shape as planning issues minus the id — the [MN-k] prefix stays the stable-identifier contract of the milestone regions, so here the GitHub number is the only handle. Update warns on stderr when unplanned issues exist but the file has no such region. The safety half of the design: the region's content is GitHub-owned mirror text, so nothing inside it may ever be read back as plan STRUCTURE. mask_unplanned_regions() blanks region interiors (offset-preserving, marker lines intact) before parse_project_plan and before lint's heading scans — an organic title like 'Issue M9-9: ...' cannot become a phantom plan issue, and one merely starting with 'Issue' cannot trip the malformed-heading lint. Issue records carry the new read-side milestone_title field; titles are now marker-neutralized in both renderers (bodies already were). Behavior change pinned by test: a planning issue whose prefix was stripped on GitHub now surfaces in the unplanned region instead of vanishing from the file (it still matches by recorded number on populate, so no duplicates). Verified against live agda-native-air data (read-only, on a copy of its plan): the #68 wave — 24 organic blocks across two milestone groups — renders and is --check-stable across consecutive runs, which is the issue's acceptance criterion. Suite 115 -> 132; example plan and the roadmap-upkeep skill document the region; engine VERSION 0.3.0. AI-assisted: Claude Fable 5 (Anthropic)
There was a problem hiding this comment.
Pull request overview
Adds first-class support for “organically-filed” GitHub issues (no parseable [MN-k] prefix) by rendering them into a dedicated generated unplanned region, while ensuring mirrored content cannot be re-interpreted as plan structure during parse/lint. This extends the project-plan template so mature repos with existing issue workflows remain visible and --check stable.
Changes:
- Add an
unplannedgenerated region renderer (grouped by GitHub milestone title, sorted by issue number) plus a stderr warning when unplanned issues exist but the region is missing. - Mask
unplannedregion interiors during plan parsing and lint heading scans to prevent mirrored GitHub content from injecting plan structure. - Expand offline test coverage, update documentation/skill guidance, and bump engine version to
0.3.0.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| scripts/VERSION | Bumps engine version to reflect the new behavior. |
| scripts/gh_project_update.py | Implements grouping/rendering for the unplanned region and adds the missing-region warning. |
| scripts/_gh_project_lib.py | Adds milestone_title on read-side issues and masks unplanned region interiors during parse/lint. |
| scripts/tests/test_integration.py | Adds end-to-end coverage for organic issue rendering + masking + --check stability. |
| scripts/tests/test_gh_project_update.py | Adds unit tests for grouping/rendering/warning behavior for unplanned. |
| scripts/tests/test_gh_project_lint.py | Ensures lint marker/heading checks behave correctly with mirrored unplanned content. |
| scripts/tests/test_gh_project_lib.py | Updates fixtures and adds tests for masking behavior and region parsing. |
| docs/GITHUB_PROJECT.md | Documents the new unplanned region and adds a placeholder block to the template plan. |
| .claude/skills/roadmap-upkeep/SKILL.md | Documents upkeep workflow for organically-filed issues via the unplanned region. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| for title, issues in unplanned: | ||
| header = title if title is not None else "(no milestone)" | ||
| blocks = "\n---\n\n".join(render_unplanned_issue(i) for i in issues) | ||
| parts.append(f"#### {header}\n\n{blocks}") |
There was a problem hiding this comment.
Confirmed — and reproduced before fixing: the mid-line marker in a #### group header closes the region early, after which the file's real END marker hard-fails parse_file with 'no matching BEGIN', so the next update/lint breaks outright. Fixed in 41c5277: milestone titles are neutralized like titles and bodies. Verifying the fix surfaced one more instance of the same class that the review didn't flag: label names were rendered verbatim in both renderers, and GitHub label names allow arbitrary punctuation up to 50 characters — the marker string is 33 — with backticks offering no protection since the marker regexes match raw text. Every piece of dynamic GitHub content in rendered output (bodies, issue titles, milestone titles, label names) now passes through neutralize_markers; assignee logins are alphanumeric-plus-hyphen and cannot carry markers. Three regression tests added, including a round trip proving an assembled region with a hostile milestone title re-parses intact (suite 132 → 134).
Copilot's review of PR #5 was right: render_unplanned_region() wrote GitHub milestone titles verbatim into the #### group headers, and the marker regexes are not line-anchored — a title containing '<!-- END GENERATED: unplanned -->' becomes a real marker mid-line. Reproduced before fixing: the region closes early and the file's true END marker then hard-fails parse_file ('no matching BEGIN'), so the next update/lint breaks outright. Fixing it surfaced one more instance of the same class: label names are rendered verbatim in BOTH renderers, GitHub label names allow arbitrary punctuation up to 50 chars (the marker string is 33), and backticks are no protection since the regexes match raw text. All dynamic GitHub content in rendered output — bodies, titles, milestone titles, label names — now passes through neutralize_markers; assignee logins are alphanumeric-plus-hyphen and cannot carry markers. Three regression tests, including a round trip proving an assembled region with a hostile milestone title re-parses intact. Suite 132 -> 134. AI-assisted: Claude Fable 5 (Anthropic)
Closes #3.
(Branch is named
4-…per request; the issue is #3 — GitHub's shared numbering gave #4 to the engine-packaging PR.)What changes
<!-- BEGIN GENERATED: unplanned -->mirrors every issue whose GitHub title has no parseable[MN-k]prefix — grouped by GitHub milestone title verbatim (####group headers, no-milestone group last), sorted by issue number, same block shape as planning issues minus the id.[MN-k]remains the stable-identifier contract of the milestone regions.mask_unplanned_regions()blanks region interiors (offset-preserving, markers intact) before plan parsing and before lint's heading scans. An organic title likeIssue M9-9: fix parsercannot become a phantom plan issue on reparse, and one merely starting with "Issue" cannot trip the malformed-heading lint. Integration-tested with exactly that adversarial title: the updated file lints clean and a re-populate dry-run plans zero creations.unplannedregion.[MN-k]prefix is stripped on GitHub now surfaces in the unplanned region rather than silently vanishing from the file (populate still matches it by recorded(#N), so no duplicates).roadmap-upkeepskill document the region;Issuegains a read-sidemilestone_titlefield; engineVERSION→ 0.3.0.Acceptance (issue #3's criterion, verified on live data)
Read-only run against
formalverification/agda-native-airon a copy of its plan file with an emptyunplannedregion appended: the region filled with 24 organic blocks across two milestone groups — including #68 and its entire agda-mcp hardening wave (#66, #69–#74, …) — and a second run reports--checkcurrent. After this merges, air's plan gains the region for real (one-line edit +make project-updateon its PR #93 branch).Tests
Suite 115 → 132, all offline: grouping/order determinism, block shape, empty placeholder, adversarial-title masking (lib, lint, and end-to-end), missing-region warning, closed-state drift round trip, and
--checkstability.nix flake check(sandboxed) green; ruff/shellcheck clean.AI-assisted development: Claude Fable 5 (Anthropic)