Follow-on to the pilot (#387). Bundles the architectural defers that didn't fit the single-repo scope.
Decision log — 2026-06-14: Multi-repo architecture decided (§1): reusable workflow + thin per-repo caller stubs, callers pinned to the add-to-project/stable channel per the org reusable-workflow versioning standard. Not the webhook handler. Execution stays gated on the #414 30-day review (keep / expand / roll back); this issue is now design-complete and shovel-ready. See decision comment.
Why one PR for all of this
These four findings share one architectural answer (a generic 'reconcile content with project' function parameterized by content type and eligibility predicate). Solving them piecemeal would mean rewriting the reconcile machinery once per finding. Bundle them so the underlying mechanism gets designed once.
In scope
1. Multi-repo deployment — ✅ decided
Today the workflow only fires for events in .github. Other repos' qualifying issues/PRs are invisible.
Decision (2026-06-14): reusable workflow + thin per-repo caller stubs. Matches the established dependabot-rebase / auto-rebase / feature-ideation / dev-lead stub pattern; avoids the hosting decision a webhook handler would require.
-
Extract logic into add-to-project-reusable.yml (hosted in .github); scripts in .github/scripts/add-to-project/ stay, threaded via an agent_ref-style input pinned to the same channel.
-
The existing add-to-project.yml becomes the thin caller stub for this repo.
-
Each adopting repo gets a thin caller pinned once to the moving stable channel tag, per ci-standards.md → Reusable workflow versioning:
uses: petry-projects/.github/.github/workflows/add-to-project-reusable.yml@add-to-project/stable
Never @main, never a SHA, never a frozen @vX.Y.Z.
-
Release process: merge to main → cut immutable add-to-project/vX.Y.Z → promote stable (single gated hop today until ring channels exist). .github's own caller is Ring 0 / self-host dogfood.
-
Deployment: stubs rolled out via scripts/deploy-standard-workflows.sh; compliance audit check_centralized_workflow_stubs enforces the canonical pin.
Rejected alternative — org-level webhook handler: one deploy, no per-repo stubs, but requires a hosting decision and diverges from the established stub pattern.
2. Issue/PR cleanup-on-label-change (Codex P2 from PR #388 review)
process_issue_or_pr currently only ADDS; it never removes. If an item is added with dev-lead and later receives one of the excluded labels (or has dev-lead removed), the project item stays. Needs:
- Add
unlabeled to issue/PR triggers
reconcile_content_with_project that mirrors reconcile_discussion's shape, parameterized by content type + eligibility predicate
3. Hard-coded labels and category
evaluate_noise_gate hard-codes dev-lead + 4 excluded labels. reconcile_discussion hard-codes Ideas. For a second Project to use the same scripts, both must be env-driven. This is the enabler for the reusable extraction in §1 — the gate (dev-lead, the 4 excluded labels, the Ideas category) becomes reusable inputs so each caller passes its own values.
4. Fork-PR maintainer-label gate
pull_request_target gate evaluates the PR author's author_association — so fork PRs from FIRST_TIMER/CONTRIBUTOR authors are skipped even when a maintainer applies dev-lead. Fix: also accept events where the labeler (github.actor on a labeled event) is trusted, OR move trust to head_repository.fork == false.
Out of scope
- New views or fields on the existing project (those are content, not infra)
- Program boards for Compliance / Fleet Monitor — separate decision at 30-day review
Related
Follow-on to the pilot (#387). Bundles the architectural defers that didn't fit the single-repo scope.
Why one PR for all of this
These four findings share one architectural answer (a generic 'reconcile content with project' function parameterized by content type and eligibility predicate). Solving them piecemeal would mean rewriting the reconcile machinery once per finding. Bundle them so the underlying mechanism gets designed once.
In scope
1. Multi-repo deployment — ✅ decided
Today the workflow only fires for events in
.github. Other repos' qualifying issues/PRs are invisible.Decision (2026-06-14): reusable workflow + thin per-repo caller stubs. Matches the established
dependabot-rebase/auto-rebase/feature-ideation/dev-leadstub pattern; avoids the hosting decision a webhook handler would require.Extract logic into
add-to-project-reusable.yml(hosted in.github); scripts in.github/scripts/add-to-project/stay, threaded via anagent_ref-style input pinned to the same channel.The existing
add-to-project.ymlbecomes the thin caller stub for this repo.Each adopting repo gets a thin caller pinned once to the moving
stablechannel tag, per ci-standards.md → Reusable workflow versioning:Never
@main, never a SHA, never a frozen@vX.Y.Z.Release process: merge to
main→ cut immutableadd-to-project/vX.Y.Z→ promotestable(single gated hop today until ring channels exist)..github's own caller is Ring 0 / self-host dogfood.Deployment: stubs rolled out via
scripts/deploy-standard-workflows.sh; compliance auditcheck_centralized_workflow_stubsenforces the canonical pin.2. Issue/PR cleanup-on-label-change (Codex P2 from PR #388 review)
process_issue_or_prcurrently only ADDS; it never removes. If an item is added withdev-leadand later receives one of the excluded labels (or hasdev-leadremoved), the project item stays. Needs:unlabeledto issue/PR triggersreconcile_content_with_projectthat mirrorsreconcile_discussion's shape, parameterized by content type + eligibility predicate3. Hard-coded labels and category
evaluate_noise_gatehard-codesdev-lead+ 4 excluded labels.reconcile_discussionhard-codesIdeas. For a second Project to use the same scripts, both must be env-driven. This is the enabler for the reusable extraction in §1 — the gate (dev-lead, the 4 excluded labels, theIdeascategory) becomes reusableinputsso each caller passes its own values.4. Fork-PR maintainer-label gate
pull_request_targetgate evaluates the PR author'sauthor_association— so fork PRs fromFIRST_TIMER/CONTRIBUTORauthors are skipped even when a maintainer appliesdev-lead. Fix: also accept events where the labeler (github.actoron alabeledevent) is trusted, OR move trust tohead_repository.fork == false.Out of scope
Related