Skip to content

update: render organically-filed issues into an unplanned region (#3) - #5

Merged
williamdemeo merged 2 commits into
mainfrom
4-organically-filed-issues
Aug 16, 2026
Merged

williamdemeo merged 2 commits into
mainfrom
4-organically-filed-issues

Conversation

@williamdemeo

Copy link
Copy Markdown
Owner

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

  • New region rule: <!-- 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.
  • Mirror content can never become plan structure: mask_unplanned_regions() blanks region interiors (offset-preserving, markers intact) before plan parsing and before lint's heading scans. An organic title like Issue M9-9: fix parser cannot 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.
  • Visibility guard: update warns on stderr when organically-filed issues exist but the file has no unplanned region.
  • Behavior change, pinned: a planning issue whose [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).
  • Titles are now marker-neutralized in both renderers (bodies already were) — closes the title half of an earlier review note.
  • Worked example and the roadmap-upkeep skill document the region; Issue gains a read-side milestone_title field; engine VERSION → 0.3.0.

Acceptance (issue #3's criterion, verified on live data)

Read-only run against formalverification/agda-native-air on a copy of its plan file with an empty unplanned region 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 --check current. After this merges, air's plan gains the region for real (one-line edit + make project-update on 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 --check stability. nix flake check (sandboxed) green; ruff/shellcheck clean.

AI-assisted development: Claude Fable 5 (Anthropic)

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)

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 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 unplanned generated 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 unplanned region 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.

Comment on lines +221 to +224
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}")

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
@williamdemeo
williamdemeo merged commit 6bacda7 into main Aug 16, 2026
4 checks passed
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.

update: render organically-filed issues (no [MN-k] prefix) into a generated region

2 participants