Skip to content

Proposal: streamline LFX Mentorship program intake #1883

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

  1. Authorization — the commenter must be in global_approvers
    (programs/lfx-mentorship/automation/approvers.yml).
  2. Precondition — the issue must carry the Exported label; otherwise the
    bot replies that the program must be exported first.
  3. 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).
  4. 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).
  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.
  6. 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
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 Admin-invoked /lfx-url <url> command after the program is created on LFX (§4.3.5)
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

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

  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.
    • Includes an lfx_url field, populated from the issue's recorded LFX URL
      (§4.3.5) when present and left empty otherwise.
  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, and LFX URL).
      The LFX URL: line is populated from the issue's recorded URL (§4.3.5)
      when present, otherwise TBD.
  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 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 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 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.

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

  • Issue form (lfx-program-proposal.yml), §4.1
  • CNCF Project dropdown sync (landscape-projects-sync.yml), §4.2; includes .project detection; ported from inline Python to JS (refactor: port landscape-projects-sync automation from Python to JS #1903)
  • Validation workflow (lfx-proposal-validate.yml), §4.3
  • Approvals workflow (lfx-proposal-approvals.yml), §4.3.2 to 4.3.4 (/approve, /confirm, /cncf-approve; four-tier auth)
  • Board sync workflow (lfx-proposal-board-sync.yml), §4.4
  • Export workflow (lfx-export.yml), §4.5 (JSON, README, tracking CSV)
  • Term dropdown, single source of truth in terms.yml
  • Config files: approvers.yml, projects.yml, quotas.yml, terms.yml, board.json, labels
  • Standing project board, resolved per repository via board.json so dev and prod run identical code
  • CI-tested automation lib (programs/lfx-mentorship/automation/lib, refactor: extract LFX proposal automation into a tested lib #1897)
  • LFC102 standard prerequisite (Add LFC102 as a standard mentee prerequisite #1905), §4.6; form note plus export injection from a single constant
  • 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
  • Phase 2, automate the LFX upload: push the export to the LFX platform instead of re-keying it by hand (§5 asks Add links to linkerd issues #2 and Flush out proposals for OpenTracing projects. #3)

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

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

Projects

Status
🏗 Work In progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions