Skip to content

Proposal: streamline LFX Mentorship program intake #1883

@nate-double-u

Description

@nate-double-u

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:

  • 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:

  1. 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.
  2. 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.
  3. 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.
Program Logo file upload, JPG/PNG/SVG, ≤2MB, 420×420 recommended (auto-fetched from landscape) Landscape stores SVGs.
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.
Custom Prerequisite Name 20 chars, Description 500 chars, Due Date, "requires file upload" flag (term-wide, configured by admin) LFC102 / Inclusive Community lives here. See §4.6.

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: textarea
  id: program_description
  attributes:
    label: Program Description
    description: |
      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: 5
    overrides:
      kubernetes: 8       # large project with many SIGs
      open-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:

  1. .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.

  2. 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.

  3. Per-project fallbacksprograms/lfx-mentorship/automation/approvers.yml:

    kubernetes:
      fallback_teams:
        - kubernetes/sig-contribex
    open-telemetry:
      fallback_teams:
        - open-telemetry/governance-committee
      fallback_handles:
        - maryliag    # GC liaison for mentorship
  4. 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:

  1. On issue_comment.created, the workflow detects /approve and checks
    the commenter against the four-tier auth chain above.
  2. If authorized, the workflow applies the Maintainer/Contribex Approved
    label and posts a confirmation comment.
  3. If unauthorized, the workflow replies explaining that the commenter isn't
    recognized as a maintainer for that project and lists the sources checked.
  4. 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:

  1. On issue_comment.created, the workflow detects /confirm and checks
    whether the commenter's GitHub handle appears in the Mentors field.
  2. 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.)
  3. 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:

  1. 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.
  2. 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").
  3. Once all listed mentors have confirmed, apply the existing
    Mentors Confirmed label.
  4. 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:

  1. 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.
  2. An admin signals approval via a /cncf-approve comment.
  3. 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.
  4. 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):

Gate Who Mechanism Label applied
Validation bot format checks (§4.3) Awaiting Maintainer/Contribex Approval + Awaiting Mentor Confirmation
Project approval maintainer (4-tier auth) /approve comment (§4.3.2) Maintainer/Contribex Approved
Mentor confirmation each named mentor /confirm comment (§4.3.3) Mentors Confirmed
CNCF admin approval mentorship admin / global approver /cncf-approve comment (§4.3.4) CNCF Approved

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
Awaiting approvals/confirmations validation-passed set, but Maintainer/Contribex Approved and/or Mentors Confirmed missing (§4.3.2, §4.3.3)
approved/confirmed Both Maintainer/Contribex Approved and Mentors Confirmed present
CNCF Approved cncf-approved label applied via §4.3.4
Exported Export workflow (§4.5) adds Exported label + comments on each issue linking to the export files
Posted to LFX Manual, or auto when the export PR (see §4.5) referencing this issue merges
LFX Approved Manual — future automation pending LFX webhook/API
Mentors added Manual — future automation pending LFX API
Listed in README 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

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

  1. Go to Settings → Tokens (classic)
  2. Note: MENTORING_PROJECT_BOARD_DEV
  3. Expiration: 90 days
  4. Scopes: check project (Full control of projects)
  5. Generate and store as a repo secret:
    gh secret set PROJECT_TOKEN --repo nate-double-u/mentoring

Production (cncf/mentoring) — fine-grained PAT

  1. Go to Settings → Fine-grained tokens
  2. Token name: MENTORING_PROJECT_BOARD_PROD
  3. Expiration: 90 days (set a calendar reminder to rotate)
  4. Resource owner: cncf (requires org admin approval)
  5. Repository access: select cncf/mentoring
  6. Permissions:
    • Organization → Projects: Read and write
  7. 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:

  1. 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.
  2. 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, LFX URL
      placeholder)
  3. 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 Blank at export time; filled in after programs are created on LFX
    Upstream issue From the form
    (empty) Spacer column
    mentor count Number of mentors listed
    Mentor 1Mentor 4 Name
    Mentor 1 GitHubMentor 4 GitHub GitHub handle
    Mentor 1 email addressMentor 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:

  1. Adds the Exported label to each included issue
  2. 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.

4.6 Mentee prerequisites & LF Education integration

LFX programs can attach per-program 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).

CNCF currently applies at least one custom prerequisite uniformly across all
programs in a term:

Prerequisite name: Inclusive Community
Due date: ~2 weeks after program start (e.g. 2026-05-19 for Term 2)
Description: Upload completion certificate for Inclusive Open Source Community Orientation (LFC102). Certificate is found in the mentee's openprofile.dev dashboard under Training & Certifications → Certificates of Completion.
Requires file upload: ✓ Yes

Today this is configured by hand on each program in the LFX admin UI, and verified
by hand by the program admin checking that an uploaded PDF matches what LFC102
issues. Two integration opportunities:

4.6a. Bulk-set prerequisites in the export schema (§4.5).
Whatever schema we agree with the LFX team should include a prerequisites: [...]
block per program (or a "default prerequisites" table at the term level), so the
Inclusive Community task is set automatically on every imported 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 ↔ 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.

This affects only the LFX-side ask list (see "Asks of LFX" → ask #6 below); no
change to the intake issue form is needed in v1, since maintainers don't author
this field.

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.

  1. 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.

  2. 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.

  3. Project-create / program-create API.
    POST /programs accepting the schema from (2). Lets us skip the manual upload entirely.

  4. Test/staging environment for the above. The validation workflow shouldn't hit prod LFX on every issue edit.

  5. Webhooks for state changes (program.approved, mentor.added, applications.opened, applications.closed). Lets us drop the manual Status flips on the project board.

  6. 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.

  7. 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.

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
Project-board automation breaks if PAT expires Use GitHub App; alert on workflow failure
Third-party action compromised Inline the parser, or pin to full SHA
Landscape regeneration commits churn Diff before commit; only update on real changes
Quota config drift (programs/lfx-mentorship/quotas.yml, §4.3.1) Treat like a config doc; review at term-rollover; default applies if file missing
Approver registry drift (programs/lfx-mentorship/approvers.yml, §4.3.2; admins.yml, §4.3.4) 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 — Update implementation status

Replace the checklist with:

  • Issue form (lfx-program-proposal.yml) — §4.1
  • CNCF Project dropdown sync (landscape-projects-sync.yml) — §4.2, includes .project detection
  • Validation workflow (lfx-proposal-validate.yml) — §4.3 (format checks, label management, tracker comment)
  • Approvals workflow (lfx-proposal-approvals.yml) — §4.3.2–4.3.4 (/approve, /confirm, /cncf-approve slash commands with four-tier auth)
  • Config filesapprovers.yml, projects.yml, label config
  • Project board — created with Status + Cohort fields
  • Export workflow (lfx-export.yml) — §4.5 (generates lfx-export.json, README.md, and lfx-tracking.csv)
  • Term dropdown sync — single source of truth in terms.yml, synced to issue form + export workflow by landscape sync
  • Board sync workflow (lfx-proposal-board-sync.yml) — §4.4 (labels → Project v2 Status field, inline sync in validation + approvals workflows)
  • End-to-end test — file a real test issue and walk it through all gates

9. Recommended next steps

Don't merge anything to cncf/mentoring yet — this is a discussion artifact, not a proposal to ship.

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.

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

Status

🏗 Work In progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions