You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Replace today's "open a PR against project_ideas.md" intake with a GitHub Issue Form + a small set of GitHub Actions workflows that:
lower the barrier for maintainers/mentors to submit a proposal (web form, no git knowledge),
validate the submission automatically (format checks now; LFID/email verification once we coordinate with the LFX platform team),
track each program (not each PR) through the lifecycle on Project board #92,
produce a machine-readable artifact the LFX platform team can ingest in bulk instead of us re-keying every field by hand.
This issue is a discussion document. Feedback welcome from maintainers, mentors, the LFX Mentorship platform team, and CNCF program admins. See §10 How to give feedback at the bottom.
Authored by @nate-double-u with assistance from Copilot CLI.
1. Problem
The current intake process for each LFX Mentorship term requires maintainers and prospective mentors to open pull requests against programs/lfx-mentorship/<year>/<term>/project_ideas.md, adding a free-form markdown block per program. This is painful in three ways:
High barrier for proposers. Maintainers must understand git/PR mechanics to propose a program, even when they have no other reason to touch the repo.
High overhead for the program admin. Each accepted program must be re-keyed (or copy-pasted) into the LFX Mentorship platform by hand, field by field, from prose markdown.
Mismatched lifecycle tracking.Project board #92 tracks each PR through an 11-stage status pipeline, but the actual unit of work is a program — and a single PR often contains several programs. The board and the reality drift apart after merge.
2. Goals
Lower the barrier for maintainers/mentors to submit a program proposal.
Produce a machine-readable artifact per term that the LFX platform team can ingest in bulk (or that we can ingest via an API, eventually).
Track each program (not each PR) through the existing project-board lifecycle.
Catch the most common data-quality failures — wrong mentor email, malformed upstream URL — before a human reviews the proposal.
3. Non-goals
Replacing the LFX Mentorship platform itself.
Automating selection of mentees (still owned by mentors / LFX).
Building anything Outreachy- or GSoC-scale (custom web app, applicant DB).
4. Proposed approach
4.1 Intake: GitHub Issue Form
A new .github/ISSUE_TEMPLATE/lfx-program-proposal.yml form replaces the
near-empty existing template. Each submitted issue represents one program proposal.
Fields (subject to refinement once LFX import schema is known):
Field
Type
Required
Notes
CNCF Project
dropdown
✓
Generated from cncf/landscape. See §4.2.
Term
dropdown
✓
Current + next two terms. Value is the bare token (e.g. 2026 Term 1); months shown as helper text.
Program Name
input
✓
Bare middle of LFX Program Name; export composes the full convention. See §4.1.2.
Program Description
textarea
✓
Up to 3000 characters. Maps directly to LFX's single Program Description field. Use placeholder/sample text in the form to prompt for both Description (what the program is) and Expected Outcomes (what success looks like) within the single field — see §4.1.5.
Technologies
input
✓
Comma-separated. Maps to LFX Step 1 "Technologies" chips.
Required/Desirable Skills
input
✗
Comma-separated. Maps to LFX Step 2 "Required and/or desirable skills and training" chips. Leave blank if same as Technologies (and keep the checkbox above checked).
Skills same as Technologies?
checkboxes
✗
Single checkbox, default checked. If checked, the field below may be left blank and the export uses Technologies for both. See §4.1.4.
Mentors
textarea
✓
One mentor per line. Format: Full Name | @github-handle | lfid-email. The first line is the primary mentor; subsequent lines are additional mentors. See §4.1.3 for rationale and validation.
Upstream Issue URL
input
✓
Exactly one URL. If the program covers multiple issues, create an umbrella issue upstream and link to that. Validation rejects multiple URLs. Maps to LFX's Repository URL field on export — see §4.1.1.
Application Requirements
checkboxes
✗
Common patterns: writing sample, project proposal, code/portfolio link. All treated as required. See §4.7.
Additional Application Requirements
textarea
✗
Free-form for anything not covered above. Configured manually on LFX as required items.
Auto-applied labels on submission: lfx mentorship, proposal, needs-validation.
Why one issue per program (not one issue per maintainer with multiple programs):
The project-board lifecycle is per-program. Bundling forces multiple programs into one row and re-creates today's mismatch.
4.1.1 Mapping to the LFX platform wizard
The form fields above are designed to populate LFX's program-creation wizard
(see screenshots attached to this issue). Important constraints learned from
the LFX UI:
LFX wizard field
LFX limit / type
Our form field
Notes
Program Name
100 chars, plain text
Program Name (bare middle only — see below)
Form collects only the program name; export composes CNCF - <Project name>: <program name> (<term>).
Linux Foundation Open Source Project
dropdown, parent foundation only
(none — implicit)
"CNCF Project" on our form is for our own labeling, not an LFX field.
Technologies (chips)
multi-chip
Technologies
Parsed from comma-separated input.
Program Description
3000 chars, rich text
Program Description
1:1 mapping. The form field has the same 3000-char limit as LFX. Validation enforces it.
Repository URL
URL, required
Upstream Issue URL
The form's Upstream Issue URL field flows into LFX's Repository URL slot. This is what gives applicants a single landing point for the work — the issue (or umbrella issue, §4.1) is more useful than the project's root repo URL, which they can find from the Website URL anyway.
Website URL
URL, required
(auto-populated from landscape)
Required by LFX. Auto-pulled from cncf/landscape for the selected project. If landscape is missing the value, fix it upstream (in cncf/landscape) — don't add a manual-override field on our form.
CII / OpenSSF Best Practices Project ID
numeric, optional
(auto-populated from landscape)
Code of Conduct URL
URL, optional
(auto-populated from landscape, fallback to CNCF CoC)
Auto-pulled from cncf/landscape for the selected project. If landscape doesn't have one, default to the CNCF Code of Conduct — every CNCF project is bound by it regardless.
Required and/or desirable skills and training (chips)
multi-chip
Required/Desirable Skills (or = Technologies via checkbox)
Two separate fields; "Skills same as Technologies?" checkbox lets the proposer signal the common case (same set) without retyping. See §4.1.4.
Program Terms
list of {Name (50 char), Start Month, Start Year, End Month, End Year}
Term (dropdown)
Our dropdown selects one term; export translates to LFX's Term object.
Standard Prerequisites (Resume, Cover Letter, School Enrollment, Participation Permission, Coding Challenge)
toggle on/off, each with editable description
Application Requirements (checkboxes)
Our "Project Proposal" maps to enabling LFX Cover Letter. See §4.7.
Standard course prerequisite (LFC102 / Inclusive Community)
fixed name/description, "requires file upload" flag, term-wide due date
(injected automatically, term-wide)
Mandatory for every mentee per #1866; not maintainer-authored. Surfaced on the form as an informational note; injected into the export from one constant. Shipped in #1905. See §4.6.
Custom Prerequisite
Name 20 chars, Description 500 chars, "requires file upload" flag
Per-program, maintainer-authored
Optional per-program task (e.g. Writing Sample). Shares the term-wide due date. See §4.6 / §4.7.
Note on scope/sizing fields: CNCF LFX programs are full-time (~350 hours
across the term), so we deliberately don't ask for Difficulty or
Time Commitment on the form.
These are valuable for our own intake and project-board tracking, and the mentor list also seeds LFX's post-approval Mentors tab.
4.1.2 Program Name composition
CNCF programs follow a strict naming convention on LFX:
CNCF - <Project name>: <program name> (<term>)
Example: CNCF - LitmusChaos: Add Prometheus Metrics to Control Plane Service (2026 Term 1)
The fixed scaffolding consumes 23 characters plus the project name (and term):
CNCF - (7)
: (2)
( + ) around the term (3)
<term> itself (e.g. 2026 Term 1 = 11)
So for the typical <term> value of YYYY Term N (11 chars), the available
budget for the bare program name depends on the project name length:
Project name
Project name length
Program-name budget (within LFX's 100-char limit)
OTel
4
73
Koordinator
11
66
LitmusChaos
11
66
OpenTelemetry
13
64
kubernetes-sigs/headlamp
24
53
Form treatment: the issue form collects only the bare middle in the
"Program Name" field. The CNCF Project (§4.2 dropdown) and Term (§4.1
dropdown) supply the surrounding pieces. The export workflow (§4.5)
composes the full LFX Program Name value at export time. The validation
workflow (§4.3) checks that the bare program name fits within the budget for
the selected project and warns the author with a precise character count if
not. This both prevents truncation (as seen in the screenshot, where 98/100
chars chopped the front of "Native Support for AI Agent...") and eliminates
the naming-convention drift visible across recent PRs (Term 1 vs T1,
missing year, missing "CNCF -" prefix, etc.).
The Term dropdown values (§4.1) should produce just the <term> token
expected by the convention (e.g. 2026 Term 1), not a longer label including
months. If you want the months visible to the form author, render them as
helper text below the dropdown rather than as part of the option string.
4.1.3 Mentors field shape
We need three things per mentor: full name, GitHub handle, and the
email address tied to their LFID (transitional — once we can look up LFIDs
directly, the email column becomes the LFID).
GitHub Issue Forms don't support repeating field groups, so a variable-length
list of structured records has to live in a single textarea. We use one line
per mentor with a pipe-separated format:
Full Name | @github-handle | lfid-email
Example:
Jane Doe | @janedoe | jane@example.com
Sam Lee | @samlee | sam@example.com
The first line is the primary mentor; subsequent lines are additional
mentors. There is exactly one form field for both — no separate "Primary
Mentor" block — which keeps the data shape consistent across mentor counts
and avoids the awkward "why does the primary mentor get three fields but the
additional mentors only get a textarea" inconsistency.
Why pipe-separated, not comma-separated: names contain commas
("Smith, John, PhD"); pipes don't appear in real names, GitHub handles, or
emails, so parsing is unambiguous.
Validation rules (also reflected in §4.3):
At least one line (primary mentor required)
Each line has exactly 3 pipe-separated fields after trimming whitespace
Field 1 (name): non-empty
Field 2 (handle): matches @[A-Za-z0-9-]{1,39} (GitHub handle rules)
Field 3 (email): matches email regex
No duplicate handles or emails across rows
Dependency on the LFX team: the email-vs-LFID part of this design is
contingent on coordination with the LFX Mentorship platform team. Today
we collect "the email address used with their LFID" because that's what
we can verify by hand. Once LFX exposes an LFID lookup (see §5 Ask #1),
we'd switch this column to LFID directly and drop the email collection.
The textarea shape is designed to make that swap trivial — same field,
different per-line format.
4.1.4 Technologies vs. Required/Desirable Skills
The LFX wizard exposes two chip-style fields that look similar but live in
different wizard steps (see screenshots, §4.1.1):
Technologies (Step 1, Program Details): the technical surface area of
the program — languages, frameworks, platforms.
Required and/or desirable skills and training (Step 2, Program Setup):
what an applicant should know coming in.
In the common case these are the same set ("if the program is in Go on
Kubernetes, applicants need Go and Kubernetes"). In the less-common case
they diverge — e.g. a documentation program might be about a particular
codebase (Technologies = the project's language/stack) but only require
prose skills of the applicant (Skills = "technical writing, Markdown").
We collect both fields so we can populate the LFX wizard accurately, but
add a single checkbox "Skills same as Technologies?" (default checked)
so the proposer doesn't retype the same set in 90% of cases. Form behavior:
Checkbox checked + Required/Desirable Skills blank → export uses
Technologies for both wizard fields
Checkbox checked + Required/Desirable Skills also filled → validation
warns about the contradiction (likely user error); admin clarifies
Checkbox unchecked + Required/Desirable Skills blank → validation fails
("either check the box or fill in the field")
Checkbox unchecked + Required/Desirable Skills filled → export uses each
field as given
Issue Forms can't conditionally show/hide fields, so both inputs are always
visible; the checkbox is a signal the workflow reads at validation and
export time, not a UX trigger.
Open question carried forward (§5 Ask #7): whether these are truly
two LFX fields or one rendered twice. If LFX confirms they're the same
field, we can collapse this back to a single input and drop the checkbox.
4.1.5 Program Description prompt
LFX has one combined Program Description field (3000 chars). Mentors vary
on how they'd want to split that — some lead with outcomes, some with
context, some weave them together. Rather than impose a structure, we
collect a single field but use the form's placeholder/sample text to
prompt for both halves so nothing important gets dropped:
# .github/ISSUE_TEMPLATE/lfx-program-proposal.yml (excerpt)
- type: textareaid: program_descriptionattributes:
label: Program Descriptiondescription: | Up to 3000 characters. This is what applicants will see on LFX. Cover both what the program is about and what success looks like — use the structure below or your own.placeholder: | ## Description What the program is about, the problem it solves, and the context a candidate needs to understand the work. ## Expected outcomes What "done" looks like at the end of the term — concrete deliverables, contributions, or capabilities the mentee should have produced.value: ""validations:
required: true
The placeholder text appears greyed-out inside the textarea until the
proposer starts typing — so it shows the suggested structure without
forcing it. The description (above the textarea) gives the rules.
Validation enforces the 3000-char hard limit (§4.3) but doesn't enforce the
structure — proposers can use the suggested headings, their own headings,
or none at all.
4.2 CNCF Project dropdown — keeping it current
GitHub Issue Form dropdowns are static — options are baked into the form's
YAML at commit time, not fetched at issue-creation time. To keep the CNCF
Project dropdown in sync with reality without manual edits, a scheduled
workflow regenerates the form's options: block from the canonical landscape
data.
Source:landscape.yml in cncf/landscape.
Filter to category CNCF Graduated, Incubating, and Sandbox (i.e., active
CNCF-hosted projects), excluding archived projects and the many landscape
entries that are not CNCF-hosted (member companies, end-user companies, etc.).
That yields ~200 projects, well within issue-form dropdown limits.
Workflow shape:
Trigger: weekly cron + workflow_dispatch for ad-hoc refresh.
Steps: download landscape.yml → filter → diff against current options block in lfx-program-proposal.yml → if changed, rewrite the YAML and open a PR (or auto-commit, if low-risk).
Bot commits use the Assisted-by: trailer per CNCF convention.
Edge cases:
CNCF project sub-projects sometimes use LFX Mentorship. Real example: PR #1861 added a sigs.k8s.io/node-readiness-controller proposal. The dropdown should include an explicit Other / not yet listed option that reveals (via the validation workflow's bot comment) a freeform follow-up where the proposer explains. Avoids forcing edge cases through an overly-narrow dropdown.
Naming canonicalization. Use the landscape's name: field as-is so naming flows downstream consistently. (Examples in recent PRs include both kgateway and Kgateway — pick the landscape spelling and stick to it.)
Regeneration noise. Only commit when the filtered list actually changes.
4.3 Validation: GitHub Actions workflow
.github/workflows/lfx-proposal-validate.yml triggers on issues.opened / issues.edited for issues carrying the lfx mentorship + proposal labels.
It performs:
Format checks (immediate, no external deps):
Email regex on each mentor's email (transitional — see §4.1.3 on switching to LFID)
URL regex on upstream issue, plus a check that exactly one URL is provided (umbrella the work upstream if it spans multiple issues)
GitHub-handle regex on each mentor's handle
Per-line shape check on the Mentors textarea (3 pipe-separated fields per row; at least 1 row; no duplicate handles or emails — see §4.1.3)
Program Name length within the per-project budget (§4.1.2)
Program Description ≤ 3000 characters (LFX limit, §4.1.1)
Technologies / Skills consistency (§4.1.4): if "Skills same as Technologies?" is unchecked, Required/Desirable Skills must be non-empty; if checked, Required/Desirable Skills should be blank (warn if both are filled)
LFX checks (stretch — requires LFX cooperation; see "Asks of LFX" below):
Verify each mentor email maps to a registered LFID on the LFX Mentorship platform
Verify the CNCF project name is recognized on the LFX side
CNCF checks (no external dependencies; details in §4.3.1–§4.3.4):
Per-project per-term proposal quota (§4.3.1)
Maintainer / ContribEx approval status (§4.3.2)
Mentor confirmation tracking (§4.3.3)
CNCF Mentorship admin approval (§4.3.4)
Output:
Bot comment summarizing ✅ pass / ❌ block / ⚠️ warning checks. Upserted (one comment per issue, edited in place on re-validation).
Labels: validation-passed / validation-failed. needs-validation removed once a verdict exists.
Implementation note (security):
A draft of the validation workflow uses peter-murray/issue-forms-body-parser@v4.1.0 for body parsing. Recommend inlining the parser (~30 lines inside actions/github-script) for the production version to avoid a third-party supply-chain dependency in a workflow that will hold the LFX_API_TOKEN secret. If we keep the third-party action, pin to a full commit SHA, not a tag.
4.3.1 Per-project per-term proposal quota
CNCF projects are currently capped at 4–5 program proposals per term
(subject to change in future terms). Today this is enforced by program-admin
attention; we can move it into validation:
Source of truth: a small config file in this repo, e.g. programs/lfx-mentorship/quotas.yml with a global default and per-project
overrides:
default_per_project_per_term: 5overrides:
kubernetes: 8# large project with many SIGsopen-telemetry: 6
Check: on issues.opened / issues.edited, count open proposal issues
with the same <CNCF Project> + <Term> labels. If count > quota, label over-quota and post a comment listing the existing proposals so the
project can decide which to withdraw.
Override: an admin can remove the over-quota label manually after
confirming the exception is intentional.
Tone: this should be a warning, not a hard block — the goal is to
surface the situation, not to litigate it via a bot.
4.3.2 Maintainer / project approval verification
Each program needs at least one approval from a maintainer of the CNCF
project, or from an equivalent project-governance role for larger projects
(e.g., a Kubernetes SIG-ContribEx lead, or an OpenTelemetry SIG/Governance
Committee liaison).
Sources of truth (checked in order):
The approvals workflow uses a four-tier authorization chain. It checks
each tier in order and stops at the first match:
.project maintainers — if the project's GitHub org has adopted the CNCF .project tooling,
the workflow fetches {org}/.project/maintainers.yaml and checks the project-maintainers team members list. This is only attempted when the
landscape sync has flagged has_dot_project: true for the project in projects.yml.
cncf/foundation/project-maintainers.csv — cncf/foundation/blob/main/project-maintainers.csv
(columns: lifecycle stage, Project, Maintainer Name, Company, Github Name,
OWNERS link). Parse rows scoped to the selected project; the Github Name
column gives us the candidate approver list.
Global approvers — also in approvers.yml. These handles can /approve for any project (useful for CNCF staff and cross-project
liaisons):
global_approvers:
- nate-double-u
- dkrook
Verification mechanism: a comment containing /approve (Prow-style)
from an authorized user. The workflow scans every line of the comment for
the command (so context text before the /approve is fine). Unauthorized
users who attempt /approve receive a reply explaining why it didn't work
and listing the recognized approver sources.
Workflow behavior:
On issue_comment.created, the workflow detects /approve and checks
the commenter against the four-tier auth chain above.
If authorized, the workflow applies the Maintainer/Contribex Approved
label and posts a confirmation comment.
If unauthorized, the workflow replies explaining that the commenter isn't
recognized as a maintainer for that project and lists the sources checked.
The proposer's own /approve is permitted if they appear in any of the
four tiers — maintainers often self-propose.
4.3.3 Mentor confirmation
Every mentor named on the proposal — primary plus any additional — needs to
confirm in writing that they're willing to mentor for this term. Today this
happens implicitly (mentor opens or co-authors the PR), or they 👍 or
approve the PR. With the issue-form flow, the proposer may not be one of
the mentors, so we need an explicit confirmation step.
Verification mechanism: a comment containing /confirm from a mentor
whose GitHub handle is listed in the Mentors field of the issue body. The
workflow parses the Mentors table to extract GitHub handles and checks the
commenter against that list.
Workflow behavior:
On issue_comment.created, the workflow detects /confirm and checks
whether the commenter's GitHub handle appears in the Mentors field.
If recognized, the workflow applies the Mentors Confirmed label and
posts a confirmation comment. (Current MVP confirms on any single
mentor's /confirm; tracking per-mentor confirmation status is a future
enhancement.)
If the commenter isn't listed as a mentor, the workflow replies explaining
why it didn't work.
Identity check: confirmation must come from the GitHub handle listed on
the form. If @alice is listed and @alice2 comments /confirm, that
doesn't count — this catches typos in the form before they propagate to LFX.
Workflow behavior:
On issues.opened and on issues.edited (when the mentor list changes),
the bot posts a comment that @-mentions every listed mentor and asks each
of them to either 👍-react on the issue or comment /confirm.
The bot tracks which mentors have confirmed. As long as one or more
confirmations are missing, the issue stays in Awaiting approvals / confirmations and the bot's tracker comment is upserted with the current
state ("✅ confirmed: @A, @b — ⏳ pending: @c").
Once all listed mentors have confirmed, apply the existing Mentors Confirmed label.
If the mentor list is later edited to add a new mentor, the Mentors Confirmed label is cleared and the bot re-pings just the new
names.
Identity check: confirmation must come from the GitHub handle listed on
the form. If @alice is listed and @alice2 reacts, that doesn't count —
this catches typos in the form before they propagate to LFX.
4.3.4 CNCF Mentorship admin approval
The final gate before a proposal moves to "CNCF Approved" is sign-off from
the CNCF Mentorship admin team.
Source of truth: the global_approvers list in programs/lfx-mentorship/automation/approvers.yml. (Current MVP; can
migrate to a GitHub team like cncf/mentorship-admins later.)
Workflow behavior:
The bot does not ping admins automatically — admin review is intentional,
not automatic. Admins find proposals via the project board's Awaiting approvals / confirmations and approved/confirmed columns.
An admin signals approval via a /cncf-approve comment.
On detection, the bot checks that both Maintainer/Contribex Approved
and Mentors Confirmed labels are present. If either is missing, it
replies with a warning explaining which gate(s) are still open, and does not apply the label.
If both gates are satisfied, the bot applies the CNCF Approved label,
which moves the issue to the CNCF Approved column on the project board
(§4.4).
Sequencing summary (all gated on validation passing):
After a program is created on the LFX platform, a CNCF admin records the URL it
returns by commenting /lfx-url <url> on the proposal issue. This replaces the
manual "edit the export files by hand and drag the board card" step. The handler
lives in the same issue_comment workflow as /approve, /confirm, and /cncf-approve.
Source of truth for authorization: the same global_approvers allowlist as /cncf-approve (§4.3.4).
Workflow behavior:
Authorization — the commenter must be in global_approvers
(programs/lfx-mentorship/automation/approvers.yml).
Precondition — the issue must carry the Exported label; otherwise the
bot replies that the program must be exported first.
URL validation — the argument must be a syntactically valid URL. If the
host is not mentorship.lfx.linuxfoundation.org, the bot warns but proceeds
(catches paste mistakes without blocking a legitimate redirect or host
change).
Record on the issue — the bot posts a parseable comment
(LFX URL recorded from @user: <url>), the durable source of truth, mirroring
the approval-record pattern the export reads back (§4.5).
Update the files automatically — the URL is filled into that term's lfx-export.json, README.md, and lfx-tracking.csv, landing the change in
an accumulating per-term pull request. The file generation is shared with the
export (§4.5) via tested libraries so both produce identical output.
Advance the board — the bot explicitly moves the card Exported → Posted to LFX (§4.4).
4.4 Lifecycle: Project board automation
A second workflow keeps Project #92 (and successor boards each term) in sync
with the issue's state, mirroring today's manual flow:
Status
Trigger
Inbox
issues.opened with proposal label → add to project, set Status
Auto when a PR touching the term README that closes this issue merges
Open for Applications
Scheduled / manual — driven by LFX calendar
Applications Closed
Scheduled / manual
Closed
Issue closed
Ownership boundary. Automatic board sync stops at Exported. Posted to LFX and the columns beyond it stay admin-owned: validation, approvals,
board-sync, and export never advance a card into them or pull a manually
advanced card back. The /lfx-url command (§4.3.5) is the single intentional
exception — an explicit, admin-invoked Exported → Posted to LFX advance. A
posted issue still carries the Exported label, so this regression guard must
remain, or the normal status cascade would map the card back to Exported.
Setup instructions for the Project v2 token:
The board sync workflow requires a PAT stored as the PROJECT_TOKEN repo
secret. The token type differs between dev and prod because fine-grained
tokens don't yet support Projects permission for user-owned projects.
Development (nate-double-u/mentoring fork) — classic PAT
Expiration: 90 days (set a calendar reminder to rotate)
Resource owner:cncf (requires org admin approval)
Repository access: select cncf/mentoring
Permissions:
Organization → Projects: Read and write
Generate, get org admin approval if required, and store as a repo secret:
gh secret set PROJECT_TOKEN --repo cncf/mentoring
Why the difference? Fine-grained PATs don't yet expose the Projects
permission for user-owned projects (only org-owned). Classic PATs have a
broad project scope that works for both. Once GitHub adds fine-grained
support for user-owned projects, the dev setup can switch to match prod.
Long-term: migrate to a GitHub App with projects: write to avoid
PAT expiration and single-user bus factor.
4.5 Export: machine-readable artifact, human-readable README, and tracking CSV for LFX
A manual workflow_dispatch workflow walks all open issues with Proposal + CNCF Approved labels for a given term and produces three outputs:
lfx-export.json — structured JSON for the LFX platform team to
bulk-import. One record per program with all fields parsed from the issue
form (project metadata, mentors, prerequisites, etc.). The schema should
mirror the LFX bulk-import schema as closely as possible — this is the
single most important thing to confirm with LFX up front. See §4.1.1
for the field-mapping inferred from the LFX wizard UI.
Mentor identities are included in the export (parsed from the textarea
per §4.1.3, primary = first row).
For fields auto-populated from cncf/landscape (Website URL, CoC URL,
Logo, OpenSSF Best Practices ID), the export workflow looks up the
selected CNCF project at export time and injects the values.
Includes an lfx_url field, populated from the issue's recorded LFX URL
(§4.3.5) when present and left empty otherwise.
README.md — human-readable accepted-programs list in the existing
format used by cncf/mentoring. Generated from the same issue data.
Includes:
Term header, status, duration
Table of Contents (nested: CNCF Project → program titles)
Accepted Projects section with full details per program (description
that covers both what the program is about and expected outcomes,
recommended skills, technologies, mentors, upstream issue, and LFX URL).
The LFX URL: line is populated from the issue's recorded URL (§4.3.5)
when present, otherwise TBD.
lfx-tracking.csv — flat CSV for importing into the program admin's
tracking spreadsheet. Columns:
Column
Notes
PROJECT
Full LFX program name (CNCF - <Project>: <Program> (<Term>))
LFX URL
Populated from the issue's recorded LFX URL (§4.3.5) when present; blank otherwise
Upstream issue
From the form
(empty)
Spacer column
mentor count
Number of mentors listed
Mentor 1 … Mentor 4
Name
Mentor 1 GitHub … Mentor 4 GitHub
GitHub handle
Mentor 1 email address … Mentor 4 email address
Email
Up to 4 mentors per program (flattened into columns). Programs with
fewer mentors leave the extra columns blank.
Both files are written to programs/lfx-mentorship/<year>/<term>/ and the
workflow opens a PR for review.
Issue notifications: After generating the export files, the workflow:
Adds the Exported label to each included issue
Comments on each issue with a link to the export files on the PR branch
This keeps mentors and maintainers informed without requiring them to
watch the repo's Actions tab.
Stretch: replace the export PR with a direct API call to LFX
(POST /programs) once such an endpoint exists. The intermediate file is
still useful as an audit trail.
LFX programs can attach prerequisite tasks with a name, due date, description,
and an optional "requires file upload" flag. LFX provides a standard prerequisite
library (Resume, Cover Letter, School Enrollment Verification, Participation
Permission from school/employer, Coding Challenge), each toggle-able and with an
editable description. Custom Prerequisites can also be added (name capped at 20
chars, description at 500 chars; these limits are tight, see §6 ask). LFX accepts
several prerequisites per program, so a standing requirement and a per-program
custom task can coexist.
Standard course prerequisite (LFC102 / Inclusive Community). Per #1866, every
CNCF mentee must complete the Inclusive Open Source Community Orientation
(LFC102)
course. This is CNCF policy, uniform across every program in a term, and is not
authored by maintainers. LFX exposes exactly four fields for a prerequisite (name,
capped at 20 chars; a due date; a description; and a "requires file upload"
checkbox), so the values below are used verbatim:
Prerequisite name: Inclusive Community
Requires file upload: Yes
Due date: the term-wide prerequisite due date (see below)
Description (verbatim):
Please upload the completion certificate for the Inclusive Open Source Community Orientation (LFC102) course: https://training.linuxfoundation.org/training/inclusive-open-source-community-orientation-lfc102/
Once completed you can find the certificate in your openprofile.dev dashboard under Training & Certifications -> Certificates of Completion.
Due dates are term-wide. One due date applies to every prerequisite task in a
term (LFC102 and any per-program custom task), set once by the admin at upload time
rather than captured per-prerequisite on the form. Last term's value was 2026-05-19
(roughly two weeks after program start).
4.6a. Shipped: single source of truth, wired into intake and export (#1905). The
LFC102 task lives as one constant in the automation lib
(programs/lfx-mentorship/automation/lib/prerequisites.js):
Form: an informational note at the end of the proposal form's Prerequisites
section tells maintainers every mentee must complete LFC102. It is added
automatically; maintainers do not author it. It is a note, not a checkbox: the
requirement is mandatory, and GitHub issue forms cannot pre-check or lock a box.
Export: each program's prerequisites.standard_courses in the export JSON
always includes the LFC102 task, materialized from the constant. It is
deliberately not written to the README or tracking CSV; mentor awareness comes
from the form note.
Upload (Phase 2): the upload will map the JSON prerequisites to LFX tasks and
stamp them with the term-wide due date supplied as a workflow input, so the LFC102
task lands on every program.
4.6b. Distinct from application requirements. Prerequisites (this section) are
tasks the mentee completes after selection, with due dates. Application requirements
(§4.7) are things applicants submit during application, before selection: writing
samples, project proposals, etc. The two are configured separately on LFX and serve
different purposes.
4.6c. LF Education integration (stretch). LFC102 is issued by LF Training &
Certification (training.linuxfoundation.org) and surfaces on openprofile.dev. If the
LFX team can wire LFX to LF Education to check completion programmatically, keyed on
the mentee's LFID, we can drop the manual certificate-PDF review entirely. This is
the LF-Education-side ask worth coordinating with the LFX team on.
The remaining manual steps (configuring the due date and checking the certificate)
persist until the LFX-side asks land (see "Asks of LFX" -> ask #6).
4.7 Per-program application requirements
Unlike prerequisites (§4.6a, term-wide and uniform), application requirements
are configured per program by the maintainer to filter / evaluate applicants before selection. The most common patterns surfaced so far:
Writing sample (file upload) — common for technical-writing programs.
Project proposal (file upload or long-text) — some mentors want
applicants to articulate how they'd approach the work before being selected.
Code sample or portfolio link — common for senior-skewing programs.
All application requirements are treated as required (hard gate;
applicant cannot submit without satisfying them). We deliberately do not offer
an "optional" variant: if it's optional, it isn't a requirement, and offering
the choice multiplies administrative overhead without changing outcomes.
Mapping to LFX standard prerequisites:
LFX provides built-in toggle-able application items (see §4.6 / screenshots).
Our form's checkboxes should map to those rather than create parallel custom
prerequisites where a standard one already exists:
Our checkbox
LFX equivalent
Notes
Project proposal (file upload)
Standard: Cover Letter
LFX's Cover Letter has a built-in structured prompt (motivation, experience, goals). Customize the prompt at export time if needed.
Code sample or portfolio link
Standard: Coding Challenge
URL-based; works for portfolio links too.
Writing sample (file upload)
Custom Prerequisite
No LFX standard for this. Add as a Custom Prerequisite with name "Writing Sample" and "requires file upload" flag set.
Other / describe below
Custom Prerequisite
Manually configured.
Form treatment: structured checkboxes (one per common pattern) plus an
"Other / describe below" textarea escape hatch for items that don't fit the
common patterns. The textarea is also treated as describing required items.
Export treatment: the export schema (§4.5) should set the corresponding
LFX standard prerequisite toggles where they exist, and emit a Custom
Prerequisite block where they don't. All entries are implicitly required.
LFX-side ask: confirm that the bulk-import schema can set application
questions per program (it almost certainly can, since the admin UI does — but
worth verifying before we design the form fields to match). See "Asks of LFX"
ask #2.
4.8 Human-readable view
Today's project_ideas.md is replaced by the auto-generated README.md
from §4.5. There is no separate "proposed ideas" file in the new flow — the
issue form is the proposal, and the exported README is the accepted-programs
list.
Source of truth is the issues; the markdown file is a convenience index.
README.md is generated by the export workflow (§4.5), not hand-edited.
The Description field in the README covers both what the program is about
and expected outcomes (merged into a single "Program Description" in the
issue form, matching the LFX platform's single description field).
5. Asks of LFX (in priority order)
These determine how much of the above we can actually automate.
LFID/email lookup endpoint. GET https://api.../v1/users?email=... returning { lfid, verified, accepted_tos } or 404. Why first: this single endpoint eliminates the most common day-of-launch failure (mentor email mismatch) and is the most user-visible win.
Documented bulk-import schema for programs.
CSV or JSON, with field names, character/byte limits (see §4.1.1 for our
reverse-engineered field map and pain points like the 100-char Program Name
and 20-char Custom Prerequisite Name), required/optional, enum values for
term / standard prerequisites. Must include support for per-program
prerequisites (§4.6a, both standard-toggles and custom slots) and
confirmation that the standard prerequisites (Resume, Cover Letter, etc.)
can be enabled via the import. Even without an API, this lets us generate
exactly the file you need.
Project-create / program-create API. POST /programs accepting the schema from (2). Lets us skip the manual upload entirely.
Test/staging environment for the above. The validation workflow shouldn't hit prod LFX on every issue edit.
Webhooks for state changes (program.approved, mentor.added, applications.opened, applications.closed). Lets us drop the manual Status flips on the project board.
LF Education integration for prerequisite verification.
The LFC102 ("Inclusive Open Source Community Orientation") certificate is
currently uploaded as a PDF by mentees and verified by hand. If LFX can
query LF Education / openprofile.dev for course completion status keyed on
LFID, we can mark the prerequisite complete automatically. This likely
requires LFX-team coordination with LF Training & Certification rather than
something they own end-to-end. See §4.6 for context.
Field-level confirmations from the LFX wizard. Specifically:
Confirm: are "Technologies" (Program Details) and "Required and/or desirable skills and training" (Program Setup) two genuinely independent fields on the back end, or one field rendered twice? Our form treats them as independent with a "same as Technologies" shortcut (§4.1.4); if they're actually the same field on the LFX side, we can simplify.
Can the Custom Prerequisite Name limit (20 chars, currently breaking on "Inclusive Community" at 19/20) and Description limit (500 chars) be raised?
Can the Program Name limit (100 chars) be raised, given common naming conventions consume ~16 chars on the term suffix alone?
Operational asks (not strictly LFX)
Service token / API key, with documented rate limits and rotation policy.
Confirmation that it's OK to comment on a public issue with the result of "email X is/is-not registered on LFX" — or whether we should keep that to a private signal.
6. Open questions
Approval expression mechanism. §4.3.2 leans toward 👍 reactions or /approve comments from candidate approvers. Other valid options: require
a comment containing the literal word "approve" (looser, more false
positives), or open a parallel PR for each issue and use PR review-approve
(heavier). 👍//approve feels like the right balance, but worth socializing.
Quota enforcement vs. warning. §4.3.1 treats over-quota as a warning,
not a hard block. If project admins want a hard block, the workflow can
refuse to advance the issue past Inbox until the count is back in range.
ContribEx-equivalent registry maintenance. The programs/lfx-mentorship/approvers.yml overrides file (§4.3.2) needs to
be kept current. Recommend treating it like CODEOWNERS — a small admin
task, edited via PR, reviewed by program admin and the affected project's
liaison.
Multi-term programs. LFX models a single program as hosting multiple
terms (you Add Term to an existing program rather than creating a new
program each term — see §4.1.1). CNCF currently treats each term as a fresh
unit (per-term project_ideas.md, separate project board per term). Worth
deciding whether to align with LFX's model — would meaningfully reduce
per-term setup effort but changes the lifecycle picture.
Program-specific extra prerequisites. Some projects may want to attach
their own prerequisite tasks beyond the term-wide Inclusive Community
requirement (e.g. "complete project X's onboarding tutorial"). Recommend not adding this to the v1 issue form — keep the form tight, handle extras
as a follow-up issue comment that the admin configures manually in LFX. If
this becomes common, add an optional textarea field in v2.
Application-requirement enumeration. v1 form covers writing sample /
project proposal / code sample, all treated as required. If maintainers
routinely ask for things outside that set (e.g. specific assessments,
language proficiency proofs), promote them from the "Other" textarea to
first-class checkboxes in v2. Worth reviewing after one term.
File-upload destination. LFX's application form handles applicant file
uploads natively. Confirm with LFX that uploaded writing samples / proposals
remain accessible to mentors and to the program admin during selection
(and what the retention policy is post-selection).
One issue per program vs. one issue per maintainer batch. Recommend per-program for lifecycle clarity, but maintainers proposing many programs may push back. Mitigate with a gh-based CLI helper or a "duplicate this issue" link in the bot comment.
Migration of in-flight terms. Don't migrate 2026 Term 2 mid-flight; cut over for 2026 Term 3 or 2027 Term 1.
GSoC reuse. GSoC has its own template (PROJECT_IDEA_TEMPLATE.md) with subtly different fields (e.g. requires ≥2 mentors). Worth deciding whether GSoC gets its own form, or one form with a "program type" dropdown.
Notification fatigue. Every issue edit re-runs validation and may re-comment. Mitigate by upserting the comment (one comment per issue, edited in place) and only re-validating on edits to the relevant fields.
Spam / abuse. Public issue forms invite drive-by submissions. The required-approval gates (§4.3.2) act as the real spam filter — no proposal moves past Awaiting Maintainer/Contribex Approval without a 👍 / /approve from a known project maintainer or fallback approver. Admin triage on Inbox catches the rest. May want CODEOWNERS-style auto-assign of admin reviewers.
How /lfx-url materializes committed files (resolved). Via an
accumulating per-term pull request, not direct commits: main is
branch-protected and the repo's automation is already PR-based. /lfx-url
records the URL and advances the board immediately; the file change lands when
the term's PR merges (minor, acceptable skew). Alternatives rejected: direct
commit to main (branch protection, no review), and deferring to the next
export run (leaves files stale, misses the goal of an automatic update).
7. Risks & mitigations
Risk
Mitigation
LFX team can't / won't expose APIs
All format checks still work; admin still has to manually verify mentor LFIDs (status quo, but with structured input — still a win)
Maintainers prefer the PR flow ("muscle memory")
Keep PRs working as fallback for one term; deprecate after one full cycle
Bot comments get noisy on edited issues
Upsert single comment; debounce on field-level diff
CODEOWNERS-style PR review; fall back to project-maintainers.csv if file missing or project not listed
Maintainer CSV format changes upstream
Pin to a known-good revision; test on dependabot-style update
8. Implementation status
Phase 1 (issue-form intake through export) is merged into cncf/mentoring and
running. Form submission, validation, /approve (maintainer), /confirm (every
listed mentor), /cncf-approve (admin), project-board sync, and a single-PR export
(JSON artifact, generated README index, tracking CSV) run the full lifecycle. The
shared logic lives in a CI-tested lib (node --test).
End-to-end test on the fork: a full proposal walked through every gate
LFX URL command (/lfx-url), §4.3.5 — record the URL on the issue, fill it into the term's export files via a per-term PR, and advance the board Exported → Posted to LFX
Changes from the spec above, now that it is built: one standing board rather than a
per-term board; every listed mentor must /confirm; and the legacy project_ideas.md
pull-request flow is retired in favor of the form.
9. Recommended next steps
Cutover term chosen: 2026 Term 3 (Sep-Nov), the first term on the new system.
Project-board token strategy resolved: classic PAT (dev) and fine-grained PAT (prod), resolved per repository via board.json. A GitHub App remains the long-term goal.
Build Phase 2: automate the upload to LFX, developed on the fork against the LFX dev environment first.
10. How to give feedback
👍 / 👎 reactions on this issue if you broadly support / oppose the direction.
Comment with your role (maintainer / mentor / LFX team / admin) and any specific concerns. If your feedback targets one section, please reference the section number (e.g. "re §4.7 application requirements: …").
For LFX platform team: I'd love a steer on §5 asks — even an early "this one's feasible / this one isn't" is hugely valuable for prioritizing the build.
For maintainers / mentors: thoughts on whether the form fields in §4.1 capture what you'd actually want to express, and whether the application-requirement defaults in §4.7 match the patterns you use.
TL;DR
Replace today's "open a PR against
project_ideas.md" intake with a GitHub Issue Form + a small set of GitHub Actions workflows that:This issue is a discussion document. Feedback welcome from maintainers, mentors, the LFX Mentorship platform team, and CNCF program admins. See §10 How to give feedback at the bottom.
Authored by @nate-double-u with assistance from Copilot CLI.
1. Problem
The current intake process for each LFX Mentorship term requires maintainers and prospective mentors to open pull requests against
programs/lfx-mentorship/<year>/<term>/project_ideas.md, adding a free-form markdown block per program. This is painful in three ways:2. Goals
3. Non-goals
4. Proposed approach
4.1 Intake: GitHub Issue Form
A new
.github/ISSUE_TEMPLATE/lfx-program-proposal.ymlform replaces thenear-empty existing template. Each submitted issue represents one program proposal.
Fields (subject to refinement once LFX import schema is known):
cncf/landscape. See §4.2.2026 Term 1); months shown as helper text.Full Name | @github-handle | lfid-email. The first line is the primary mentor; subsequent lines are additional mentors. See §4.1.3 for rationale and validation.Auto-applied labels on submission:
lfx mentorship,proposal,needs-validation.Why one issue per program (not one issue per maintainer with multiple programs):
The project-board lifecycle is per-program. Bundling forces multiple programs into one row and re-creates today's mismatch.
4.1.1 Mapping to the LFX platform wizard
The form fields above are designed to populate LFX's program-creation wizard
(see screenshots attached to this issue). Important constraints learned from
the LFX UI:
CNCF - <Project name>: <program name> (<term>).cncf/landscapefor the selected project. If landscape is missing the value, fix it upstream (incncf/landscape) — don't add a manual-override field on our form.cncf/landscapefor the selected project. If landscape doesn't have one, default to the CNCF Code of Conduct — every CNCF project is bound by it regardless.These are valuable for our own intake and project-board tracking, and the mentor list also seeds LFX's post-approval Mentors tab.
4.1.2 Program Name composition
CNCF programs follow a strict naming convention on LFX:
Example:
CNCF - LitmusChaos: Add Prometheus Metrics to Control Plane Service (2026 Term 1)The fixed scaffolding consumes 23 characters plus the project name (and term):
CNCF -(7):(2)(+)around the term (3)<term>itself (e.g.2026 Term 1= 11)So for the typical
<term>value ofYYYY Term N(11 chars), the availablebudget for the bare program name depends on the project name length:
Form treatment: the issue form collects only the bare middle in the
"Program Name" field. The CNCF Project (§4.2 dropdown) and Term (§4.1
dropdown) supply the surrounding pieces. The export workflow (§4.5)
composes the full LFX Program Name value at export time. The validation
workflow (§4.3) checks that the bare program name fits within the budget for
the selected project and warns the author with a precise character count if
not. This both prevents truncation (as seen in the screenshot, where 98/100
chars chopped the front of "Native Support for AI Agent...") and eliminates
the naming-convention drift visible across recent PRs (
Term 1vsT1,missing year, missing "CNCF -" prefix, etc.).
The Term dropdown values (§4.1) should produce just the
<term>tokenexpected by the convention (e.g.
2026 Term 1), not a longer label includingmonths. If you want the months visible to the form author, render them as
helper text below the dropdown rather than as part of the option string.
4.1.3 Mentors field shape
We need three things per mentor: full name, GitHub handle, and the
email address tied to their LFID (transitional — once we can look up LFIDs
directly, the email column becomes the LFID).
GitHub Issue Forms don't support repeating field groups, so a variable-length
list of structured records has to live in a single textarea. We use one line
per mentor with a pipe-separated format:
Example:
The first line is the primary mentor; subsequent lines are additional
mentors. There is exactly one form field for both — no separate "Primary
Mentor" block — which keeps the data shape consistent across mentor counts
and avoids the awkward "why does the primary mentor get three fields but the
additional mentors only get a textarea" inconsistency.
Why pipe-separated, not comma-separated: names contain commas
("Smith, John, PhD"); pipes don't appear in real names, GitHub handles, or
emails, so parsing is unambiguous.
Validation rules (also reflected in §4.3):
@[A-Za-z0-9-]{1,39}(GitHub handle rules)4.1.4 Technologies vs. Required/Desirable Skills
The LFX wizard exposes two chip-style fields that look similar but live in
different wizard steps (see screenshots, §4.1.1):
the program — languages, frameworks, platforms.
what an applicant should know coming in.
In the common case these are the same set ("if the program is in Go on
Kubernetes, applicants need Go and Kubernetes"). In the less-common case
they diverge — e.g. a documentation program might be about a particular
codebase (Technologies = the project's language/stack) but only require
prose skills of the applicant (Skills = "technical writing, Markdown").
We collect both fields so we can populate the LFX wizard accurately, but
add a single checkbox "Skills same as Technologies?" (default checked)
so the proposer doesn't retype the same set in 90% of cases. Form behavior:
Technologies for both wizard fields
warns about the contradiction (likely user error); admin clarifies
("either check the box or fill in the field")
field as given
Issue Forms can't conditionally show/hide fields, so both inputs are always
visible; the checkbox is a signal the workflow reads at validation and
export time, not a UX trigger.
4.1.5 Program Description prompt
LFX has one combined Program Description field (3000 chars). Mentors vary
on how they'd want to split that — some lead with outcomes, some with
context, some weave them together. Rather than impose a structure, we
collect a single field but use the form's placeholder/sample text to
prompt for both halves so nothing important gets dropped:
The
placeholdertext appears greyed-out inside the textarea until theproposer starts typing — so it shows the suggested structure without
forcing it. The
description(above the textarea) gives the rules.Validation enforces the 3000-char hard limit (§4.3) but doesn't enforce the
structure — proposers can use the suggested headings, their own headings,
or none at all.
4.2 CNCF Project dropdown — keeping it current
GitHub Issue Form dropdowns are static — options are baked into the form's
YAML at commit time, not fetched at issue-creation time. To keep the CNCF
Project dropdown in sync with reality without manual edits, a scheduled
workflow regenerates the form's
options:block from the canonical landscapedata.
Source:
landscape.ymlincncf/landscape.Filter to category
CNCF Graduated, Incubating, and Sandbox(i.e., activeCNCF-hosted projects), excluding archived projects and the many landscape
entries that are not CNCF-hosted (member companies, end-user companies, etc.).
That yields ~200 projects, well within issue-form dropdown limits.
Workflow shape:
workflow_dispatchfor ad-hoc refresh.landscape.yml→ filter → diff against current options block inlfx-program-proposal.yml→ if changed, rewrite the YAML and open a PR (or auto-commit, if low-risk).Assisted-by:trailer per CNCF convention.Edge cases:
sigs.k8s.io/node-readiness-controllerproposal. The dropdown should include an explicitOther / not yet listedoption that reveals (via the validation workflow's bot comment) a freeform follow-up where the proposer explains. Avoids forcing edge cases through an overly-narrow dropdown.name:field as-is so naming flows downstream consistently. (Examples in recent PRs include bothkgatewayandKgateway— pick the landscape spelling and stick to it.)4.3 Validation: GitHub Actions workflow
.github/workflows/lfx-proposal-validate.ymltriggers onissues.opened/issues.editedfor issues carrying thelfx mentorship+proposallabels.It performs:
Format checks (immediate, no external deps):
LFX checks (stretch — requires LFX cooperation; see "Asks of LFX" below):
CNCF checks (no external dependencies; details in §4.3.1–§4.3.4):
Output:
validation-passed/validation-failed.needs-validationremoved once a verdict exists.Implementation note (security):
A draft of the validation workflow uses
peter-murray/issue-forms-body-parser@v4.1.0for body parsing. Recommend inlining the parser (~30 lines insideactions/github-script) for the production version to avoid a third-party supply-chain dependency in a workflow that will hold theLFX_API_TOKENsecret. If we keep the third-party action, pin to a full commit SHA, not a tag.4.3.1 Per-project per-term proposal quota
CNCF projects are currently capped at 4–5 program proposals per term
(subject to change in future terms). Today this is enforced by program-admin
attention; we can move it into validation:
programs/lfx-mentorship/quotas.ymlwith a global default and per-projectoverrides:
issues.opened/issues.edited, count open proposal issueswith the same
<CNCF Project>+<Term>labels. Ifcount > quota, labelover-quotaand post a comment listing the existing proposals so theproject can decide which to withdraw.
over-quotalabel manually afterconfirming the exception is intentional.
surface the situation, not to litigate it via a bot.
4.3.2 Maintainer / project approval verification
Each program needs at least one approval from a maintainer of the CNCF
project, or from an equivalent project-governance role for larger projects
(e.g., a Kubernetes SIG-ContribEx lead, or an OpenTelemetry SIG/Governance
Committee liaison).
Sources of truth (checked in order):
The approvals workflow uses a four-tier authorization chain. It checks
each tier in order and stops at the first match:
.projectmaintainers — if the project's GitHub org has adopted theCNCF
.projecttooling,the workflow fetches
{org}/.project/maintainers.yamland checks theproject-maintainersteam members list. This is only attempted when thelandscape sync has flagged
has_dot_project: truefor the project inprojects.yml.cncf/foundation/project-maintainers.csv—cncf/foundation/blob/main/project-maintainers.csv(columns: lifecycle stage, Project, Maintainer Name, Company, Github Name,
OWNERS link). Parse rows scoped to the selected project; the
Github Namecolumn gives us the candidate approver list.
Per-project fallbacks —
programs/lfx-mentorship/automation/approvers.yml:Global approvers — also in
approvers.yml. These handles can/approvefor any project (useful for CNCF staff and cross-projectliaisons):
Verification mechanism: a comment containing
/approve(Prow-style)from an authorized user. The workflow scans every line of the comment for
the command (so context text before the
/approveis fine). Unauthorizedusers who attempt
/approvereceive a reply explaining why it didn't workand listing the recognized approver sources.
Workflow behavior:
issue_comment.created, the workflow detects/approveand checksthe commenter against the four-tier auth chain above.
Maintainer/Contribex Approvedlabel and posts a confirmation comment.
recognized as a maintainer for that project and lists the sources checked.
/approveis permitted if they appear in any of thefour tiers — maintainers often self-propose.
4.3.3 Mentor confirmation
Every mentor named on the proposal — primary plus any additional — needs to
confirm in writing that they're willing to mentor for this term. Today this
happens implicitly (mentor opens or co-authors the PR), or they 👍 or
approve the PR. With the issue-form flow, the proposer may not be one of
the mentors, so we need an explicit confirmation step.
Verification mechanism: a comment containing
/confirmfrom a mentorwhose GitHub handle is listed in the Mentors field of the issue body. The
workflow parses the Mentors table to extract GitHub handles and checks the
commenter against that list.
Workflow behavior:
issue_comment.created, the workflow detects/confirmand checkswhether the commenter's GitHub handle appears in the Mentors field.
Mentors Confirmedlabel andposts a confirmation comment. (Current MVP confirms on any single
mentor's
/confirm; tracking per-mentor confirmation status is a futureenhancement.)
why it didn't work.
Identity check: confirmation must come from the GitHub handle listed on
the form. If
@aliceis listed and@alice2comments/confirm, thatdoesn't count — this catches typos in the form before they propagate to LFX.
Workflow behavior:
issues.openedand onissues.edited(when the mentor list changes),the bot posts a comment that @-mentions every listed mentor and asks each
of them to either 👍-react on the issue or comment
/confirm.confirmations are missing, the issue stays in
Awaiting approvals / confirmationsand the bot's tracker comment is upserted with the currentstate ("✅ confirmed: @A, @b — ⏳ pending: @c").
Mentors Confirmedlabel.Mentors Confirmedlabel is cleared and the bot re-pings just the newnames.
Identity check: confirmation must come from the GitHub handle listed on
the form. If
@aliceis listed and@alice2reacts, that doesn't count —this catches typos in the form before they propagate to LFX.
4.3.4 CNCF Mentorship admin approval
The final gate before a proposal moves to "CNCF Approved" is sign-off from
the CNCF Mentorship admin team.
Source of truth: the
global_approverslist inprograms/lfx-mentorship/automation/approvers.yml. (Current MVP; canmigrate to a GitHub team like
cncf/mentorship-adminslater.)Workflow behavior:
not automatic. Admins find proposals via the project board's
Awaiting approvals / confirmationsandapproved/confirmedcolumns./cncf-approvecomment.Maintainer/Contribex Approvedand
Mentors Confirmedlabels are present. If either is missing, itreplies with a warning explaining which gate(s) are still open, and does
not apply the label.
CNCF Approvedlabel,which moves the issue to the
CNCF Approvedcolumn on the project board(§4.4).
Sequencing summary (all gated on validation passing):
Awaiting Maintainer/Contribex Approval+Awaiting Mentor Confirmation/approvecomment (§4.3.2)Maintainer/Contribex Approved/confirmcomment (§4.3.3)Mentors Confirmed/cncf-approvecomment (§4.3.4)CNCF Approved4.3.5 LFX URL recording
After a program is created on the LFX platform, a CNCF admin records the URL it
returns by commenting
/lfx-url <url>on the proposal issue. This replaces themanual "edit the export files by hand and drag the board card" step. The handler
lives in the same
issue_commentworkflow as/approve,/confirm, and/cncf-approve.Source of truth for authorization: the same
global_approversallowlist as/cncf-approve(§4.3.4).Workflow behavior:
global_approvers(
programs/lfx-mentorship/automation/approvers.yml).Exportedlabel; otherwise thebot replies that the program must be exported first.
host is not
mentorship.lfx.linuxfoundation.org, the bot warns but proceeds(catches paste mistakes without blocking a legitimate redirect or host
change).
(
LFX URL recorded from @user: <url>), the durable source of truth, mirroringthe approval-record pattern the export reads back (§4.5).
lfx-export.json,README.md, andlfx-tracking.csv, landing the change inan accumulating per-term pull request. The file generation is shared with the
export (§4.5) via tested libraries so both produce identical output.
Exported → Posted to LFX(§4.4).4.4 Lifecycle: Project board automation
A second workflow keeps Project #92 (and successor boards each term) in sync
with the issue's state, mirroring today's manual flow:
issues.openedwithproposallabel → add to project, set Statusvalidation-passedset, butMaintainer/Contribex Approvedand/orMentors Confirmedmissing (§4.3.2, §4.3.3)Maintainer/Contribex ApprovedandMentors Confirmedpresentcncf-approvedlabel applied via §4.3.4Exportedlabel + comments on each issue linking to the export files/lfx-url <url>command after the program is created on LFX (§4.3.5)Ownership boundary. Automatic board sync stops at
Exported.Posted to LFXand the columns beyond it stay admin-owned: validation, approvals,board-sync, and export never advance a card into them or pull a manually
advanced card back. The
/lfx-urlcommand (§4.3.5) is the single intentionalexception — an explicit, admin-invoked
Exported→Posted to LFXadvance. Aposted issue still carries the
Exportedlabel, so this regression guard mustremain, or the normal status cascade would map the card back to
Exported.Setup instructions for the Project v2 token:
The board sync workflow requires a PAT stored as the
PROJECT_TOKENreposecret. The token type differs between dev and prod because fine-grained
tokens don't yet support Projects permission for user-owned projects.
Development (nate-double-u/mentoring fork) — classic PAT
MENTORING_PROJECT_BOARD_DEVproject(Full control of projects)gh secret set PROJECT_TOKEN --repo nate-double-u/mentoringProduction (cncf/mentoring) — fine-grained PAT
MENTORING_PROJECT_BOARD_PRODcncf(requires org admin approval)cncf/mentoringgh secret set PROJECT_TOKEN --repo cncf/mentoring4.5 Export: machine-readable artifact, human-readable README, and tracking CSV for LFX
A manual
workflow_dispatchworkflow walks all open issues withProposal+CNCF Approvedlabels for a given term and produces three outputs:lfx-export.json— structured JSON for the LFX platform team tobulk-import. One record per program with all fields parsed from the issue
form (project metadata, mentors, prerequisites, etc.). The schema should
mirror the LFX bulk-import schema as closely as possible — this is the
single most important thing to confirm with LFX up front. See §4.1.1
for the field-mapping inferred from the LFX wizard UI.
per §4.1.3, primary = first row).
cncf/landscape(Website URL, CoC URL,Logo, OpenSSF Best Practices ID), the export workflow looks up the
selected CNCF project at export time and injects the values.
lfx_urlfield, populated from the issue's recorded LFX URL(§4.3.5) when present and left empty otherwise.
README.md— human-readable accepted-programs list in the existingformat used by
cncf/mentoring. Generated from the same issue data.Includes:
that covers both what the program is about and expected outcomes,
recommended skills, technologies, mentors, upstream issue, and LFX URL).
The
LFX URL:line is populated from the issue's recorded URL (§4.3.5)when present, otherwise
TBD.lfx-tracking.csv— flat CSV for importing into the program admin'stracking spreadsheet. Columns:
PROJECTCNCF - <Project>: <Program> (<Term>))LFX URLUpstream issuementor countMentor 1…Mentor 4Mentor 1 GitHub…Mentor 4 GitHubMentor 1 email address…Mentor 4 email addressUp to 4 mentors per program (flattened into columns). Programs with
fewer mentors leave the extra columns blank.
Both files are written to
programs/lfx-mentorship/<year>/<term>/and theworkflow opens a PR for review.
Issue notifications: After generating the export files, the workflow:
Exportedlabel to each included issueThis keeps mentors and maintainers informed without requiring them to
watch the repo's Actions tab.
Stretch: replace the export PR with a direct API call to LFX
(
POST /programs) once such an endpoint exists. The intermediate file isstill useful as an audit trail.
4.6 Mentee prerequisites & LF Education integration
LFX programs can attach prerequisite tasks with a name, due date, description,
and an optional "requires file upload" flag. LFX provides a standard prerequisite
library (Resume, Cover Letter, School Enrollment Verification, Participation
Permission from school/employer, Coding Challenge), each toggle-able and with an
editable description. Custom Prerequisites can also be added (name capped at 20
chars, description at 500 chars; these limits are tight, see §6 ask). LFX accepts
several prerequisites per program, so a standing requirement and a per-program
custom task can coexist.
Standard course prerequisite (LFC102 / Inclusive Community). Per #1866, every
CNCF mentee must complete the Inclusive Open Source Community Orientation
(LFC102)
course. This is CNCF policy, uniform across every program in a term, and is not
authored by maintainers. LFX exposes exactly four fields for a prerequisite (name,
capped at 20 chars; a due date; a description; and a "requires file upload"
checkbox), so the values below are used verbatim:
Description (verbatim):
Due dates are term-wide. One due date applies to every prerequisite task in a
term (LFC102 and any per-program custom task), set once by the admin at upload time
rather than captured per-prerequisite on the form. Last term's value was 2026-05-19
(roughly two weeks after program start).
4.6a. Shipped: single source of truth, wired into intake and export (#1905). The
LFC102 task lives as one constant in the automation lib
(
programs/lfx-mentorship/automation/lib/prerequisites.js):section tells maintainers every mentee must complete LFC102. It is added
automatically; maintainers do not author it. It is a note, not a checkbox: the
requirement is mandatory, and GitHub issue forms cannot pre-check or lock a box.
prerequisites.standard_coursesin the export JSONalways includes the LFC102 task, materialized from the constant. It is
deliberately not written to the README or tracking CSV; mentor awareness comes
from the form note.
stamp them with the term-wide due date supplied as a workflow input, so the LFC102
task lands on every program.
4.6b. Distinct from application requirements. Prerequisites (this section) are
tasks the mentee completes after selection, with due dates. Application requirements
(§4.7) are things applicants submit during application, before selection: writing
samples, project proposals, etc. The two are configured separately on LFX and serve
different purposes.
4.6c. LF Education integration (stretch). LFC102 is issued by LF Training &
Certification (training.linuxfoundation.org) and surfaces on openprofile.dev. If the
LFX team can wire LFX to LF Education to check completion programmatically, keyed on
the mentee's LFID, we can drop the manual certificate-PDF review entirely. This is
the LF-Education-side ask worth coordinating with the LFX team on.
The remaining manual steps (configuring the due date and checking the certificate)
persist until the LFX-side asks land (see "Asks of LFX" -> ask #6).
4.7 Per-program application requirements
Unlike prerequisites (§4.6a, term-wide and uniform), application requirements
are configured per program by the maintainer to filter / evaluate applicants
before selection. The most common patterns surfaced so far:
applicants to articulate how they'd approach the work before being selected.
All application requirements are treated as required (hard gate;
applicant cannot submit without satisfying them). We deliberately do not offer
an "optional" variant: if it's optional, it isn't a requirement, and offering
the choice multiplies administrative overhead without changing outcomes.
Mapping to LFX standard prerequisites:
LFX provides built-in toggle-able application items (see §4.6 / screenshots).
Our form's checkboxes should map to those rather than create parallel custom
prerequisites where a standard one already exists:
Form treatment: structured checkboxes (one per common pattern) plus an
"Other / describe below" textarea escape hatch for items that don't fit the
common patterns. The textarea is also treated as describing required items.
Export treatment: the export schema (§4.5) should set the corresponding
LFX standard prerequisite toggles where they exist, and emit a Custom
Prerequisite block where they don't. All entries are implicitly required.
LFX-side ask: confirm that the bulk-import schema can set application
questions per program (it almost certainly can, since the admin UI does — but
worth verifying before we design the form fields to match). See "Asks of LFX"
ask #2.
4.8 Human-readable view
Today's
project_ideas.mdis replaced by the auto-generatedREADME.mdfrom §4.5. There is no separate "proposed ideas" file in the new flow — the
issue form is the proposal, and the exported README is the accepted-programs
list.
README.mdis generated by the export workflow (§4.5), not hand-edited.and expected outcomes (merged into a single "Program Description" in the
issue form, matching the LFX platform's single description field).
5. Asks of LFX (in priority order)
These determine how much of the above we can actually automate.
LFID/email lookup endpoint.
GET https://api.../v1/users?email=...returning{ lfid, verified, accepted_tos }or 404.Why first: this single endpoint eliminates the most common day-of-launch failure (mentor email mismatch) and is the most user-visible win.
Documented bulk-import schema for programs.
CSV or JSON, with field names, character/byte limits (see §4.1.1 for our
reverse-engineered field map and pain points like the 100-char Program Name
and 20-char Custom Prerequisite Name), required/optional, enum values for
term / standard prerequisites. Must include support for per-program
prerequisites (§4.6a, both standard-toggles and custom slots) and
confirmation that the standard prerequisites (Resume, Cover Letter, etc.)
can be enabled via the import. Even without an API, this lets us generate
exactly the file you need.
Project-create / program-create API.
POST /programsaccepting the schema from (2). Lets us skip the manual upload entirely.Test/staging environment for the above. The validation workflow shouldn't hit prod LFX on every issue edit.
Webhooks for state changes (
program.approved,mentor.added,applications.opened,applications.closed). Lets us drop the manual Status flips on the project board.LF Education integration for prerequisite verification.
The LFC102 ("Inclusive Open Source Community Orientation") certificate is
currently uploaded as a PDF by mentees and verified by hand. If LFX can
query LF Education / openprofile.dev for course completion status keyed on
LFID, we can mark the prerequisite complete automatically. This likely
requires LFX-team coordination with LF Training & Certification rather than
something they own end-to-end. See §4.6 for context.
Field-level confirmations from the LFX wizard. Specifically:
Operational asks (not strictly LFX)
6. Open questions
/approvecomments from candidate approvers. Other valid options: requirea comment containing the literal word "approve" (looser, more false
positives), or open a parallel PR for each issue and use PR review-approve
(heavier). 👍/
/approvefeels like the right balance, but worth socializing.not a hard block. If project admins want a hard block, the workflow can
refuse to advance the issue past
Inboxuntil the count is back in range.programs/lfx-mentorship/approvers.ymloverrides file (§4.3.2) needs tobe kept current. Recommend treating it like CODEOWNERS — a small admin
task, edited via PR, reviewed by program admin and the affected project's
liaison.
terms (you
Add Termto an existing program rather than creating a newprogram each term — see §4.1.1). CNCF currently treats each term as a fresh
unit (per-term
project_ideas.md, separate project board per term). Worthdeciding whether to align with LFX's model — would meaningfully reduce
per-term setup effort but changes the lifecycle picture.
their own prerequisite tasks beyond the term-wide Inclusive Community
requirement (e.g. "complete project X's onboarding tutorial"). Recommend
not adding this to the v1 issue form — keep the form tight, handle extras
as a follow-up issue comment that the admin configures manually in LFX. If
this becomes common, add an optional textarea field in v2.
project proposal / code sample, all treated as required. If maintainers
routinely ask for things outside that set (e.g. specific assessments,
language proficiency proofs), promote them from the "Other" textarea to
first-class checkboxes in v2. Worth reviewing after one term.
uploads natively. Confirm with LFX that uploaded writing samples / proposals
remain accessible to mentors and to the program admin during selection
(and what the retention policy is post-selection).
gh-based CLI helper or a "duplicate this issue" link in the bot comment.PROJECT_IDEA_TEMPLATE.md) with subtly different fields (e.g. requires ≥2 mentors). Worth deciding whether GSoC gets its own form, or one form with a "program type" dropdown.Awaiting Maintainer/Contribex Approvalwithout a 👍 //approvefrom a known project maintainer or fallback approver. Admin triage onInboxcatches the rest. May want CODEOWNERS-style auto-assign of admin reviewers./lfx-urlmaterializes committed files (resolved). Via anaccumulating per-term pull request, not direct commits:
mainisbranch-protected and the repo's automation is already PR-based.
/lfx-urlrecords the URL and advances the board immediately; the file change lands when
the term's PR merges (minor, acceptable skew). Alternatives rejected: direct
commit to
main(branch protection, no review), and deferring to the nextexport run (leaves files stale, misses the goal of an automatic update).
7. Risks & mitigations
programs/lfx-mentorship/quotas.yml, §4.3.1)programs/lfx-mentorship/approvers.yml, §4.3.2;admins.yml, §4.3.4)dependabot-style update8. Implementation status
Phase 1 (issue-form intake through export) is merged into
cncf/mentoringandrunning. Form submission, validation,
/approve(maintainer),/confirm(everylisted mentor),
/cncf-approve(admin), project-board sync, and a single-PR export(JSON artifact, generated README index, tracking CSV) run the full lifecycle. The
shared logic lives in a CI-tested lib (
node --test).lfx-program-proposal.yml), §4.1landscape-projects-sync.yml), §4.2; includes.projectdetection; ported from inline Python to JS (refactor: port landscape-projects-sync automation from Python to JS #1903)lfx-proposal-validate.yml), §4.3lfx-proposal-approvals.yml), §4.3.2 to 4.3.4 (/approve,/confirm,/cncf-approve; four-tier auth)lfx-proposal-board-sync.yml), §4.4lfx-export.yml), §4.5 (JSON, README, tracking CSV)terms.ymlapprovers.yml,projects.yml,quotas.yml,terms.yml,board.json, labelsboard.jsonso dev and prod run identical codeprograms/lfx-mentorship/automation/lib, refactor: extract LFX proposal automation into a tested lib #1897)/lfx-url), §4.3.5 — record the URL on the issue, fill it into the term's export files via a per-term PR, and advance the boardExported → Posted to LFXChanges from the spec above, now that it is built: one standing board rather than a
per-term board; every listed mentor must
/confirm; and the legacyproject_ideas.mdpull-request flow is retired in favor of the form.
9. Recommended next steps
board.json. A GitHub App remains the long-term goal.10. How to give feedback