Project 8: An Upstream Contribution

2–4 weeks · ●●●●○ · touches everything, plus code review by strangers

The only project here whose acceptance criterion is decided by someone else. That is the point.


1. The Problem

You have built a terminal, and you understand the domain. That understanding is now worth something to projects with real users — but a private repository helps nobody, and code that has never been reviewed by a maintainer has never been tested against the standard that matters.


2. Why It Is Hard

Not technically. Socially and procedurally.

ProblemDetail
Finding the right issueToo small is trivial; too large will not be merged from a stranger
Their code, not yoursYou must match a style, an architecture, and a set of constraints you did not choose
Maintainers are busyA PR that requires them to teach you the codebase will sit
Terminals are conservativeA compatibility regression affects thousands of people; the bar is correctly high
Review is uncomfortableSomeone will tell you your approach is wrong, in public, and they may be right
LatencyWeeks between rounds is normal. Plan for it rather than being surprised.

3. Choosing the Contribution

The strong candidates

ProjectLanguageGood contributionsWhy it is approachable
vte (Alacritty's parser)RustParser edge cases, spec conformance, testsSmall, focused, well-tested, and you just wrote one
alacritty_terminalRustScreen-model bugs, escape-sequence supportSeparated from the app; changes are contained
zellijRustLayouts, plugins, bug fixesYoung, active, welcoming to newcomers
WezTerm (termwiz, portable-pty)RustTerminal library work, PTY edge casesWell-scoped sub-crates
footCPerformance, Wayland, protocol supportSmall and readable; a responsive maintainer
GhosttyZiglibghostty API surface, protocol supportThe core/platform split needs API work
unicode-widthRustWidth-table correctionsTiny diffs, enormous blast radius — everything depends on it
terminfo / ncursesC + dataDatabase correctionsUnglamorous, genuinely valuable, and undersupplied

What makes a good first contribution

   ✓ A bug YOU hit, with a reproduction you already have
   ✓ A conformance gap your Project 7 suite found
   ✓ A missing escape sequence with a clear specification
   ✓ A test for existing untested behavior
   ✓ Documentation of something that confused you — while it is still fresh
   ✓ A performance fix WITH a benchmark

   ✗ A large refactor
   ✗ A new feature nobody asked for
   ✗ Reformatting
   ✗ "I rewrote your parser"
   ✗ Anything touching a compatibility guarantee without discussion first

Tip: The best source of first contributions is your own Milestone 14 compatibility matrix and your Project 7 conformance findings. You already have reproductions, you already know the expected behavior, and you already know why it matters. That is 80% of a good bug report, and a good bug report is often more valuable to a maintainer than a patch.


4. Milestones

#GoalOutput
1Choose a project; read its CONTRIBUTING, its issue tracker, and its last 20 merged PRsA note on what their PRs look like
2Build it from source, run its tests, make a trivial local changeYou can iterate
3Choose an issue and comment on it before writing codeMaintainer acknowledgement, or a redirect
4A minimal reproduction, in their test frameworkA failing test
5The fix, plus the test, plus a changelog entry if they keep oneA PR
6Respond to review; iterateMerged, or a clear "not now" with a reason

Milestone 3 is the one people skip, and it is the highest-value one. A comment saying "I hit this, here is my reproduction, I intend to fix it by X — does that approach sound right?" saves you from writing the wrong patch and tells the maintainer you are worth engaging with.


5. The Standard Your PR Must Meet

Terminal projects are conservative because a regression breaks people's daily tools. Assume the bar is high.

   □ ONE logical change. Not a bug fix plus a refactor plus a rename.
   □ A test that FAILS before and PASSES after.
   □ Their style. Run their formatter, their linter, their full test suite.
   □ A commit message that explains WHY, not just what.
   □ A description with: the symptom, the reproduction, the root cause, the fix,
     and how you tested it.
   □ A note on compatibility risk, explicitly. Even if it is "none."
   □ No unrelated whitespace changes. None.
   □ Their changelog/DCO/sign-off conventions, if they have them.

The commit message

   parser: handle CSI intermediates before the private marker

   The parser accepted `CSI ? 1 h` but rejected `CSI ! ? p`, because the
   private-marker check ran before the intermediate check. Per ECMA-48 §5.4,
   intermediate bytes (0x20-0x2F) may precede the final byte in any CSI
   sequence, and xterm accepts this ordering.

   Observed with <program>, which emits `CSI ! p` (DECSTR) after a private
   sequence; the soft reset was silently dropped.

   Adds a regression test covering both orderings.

   Fixes #1234

Note what it contains: the symptom, the rule with a citation, the real-world trigger, the test, and the issue reference. Not "fix parser bug."


6. Working With Review

The part that is genuinely uncomfortable, and the part with the most transferable value.

FeedbackThe wrong responseThe right response
"This should be done differently"Defend the original at lengthAsk what constraint you missed, then decide
"Add a test for X""It is covered by Y"Add the test. They know their regression history.
"This breaks compatibility with Z""Z is obsolete"They maintain it; take the constraint seriously
Silence for two weeksBump every dayOne polite ping after ~2 weeks. Maintainers are volunteers.
"We do not want this feature"ArgueAsk what would be wanted. Often something adjacent is.
A style nitIgnore itFix it. It costs nothing and signals you are easy to work with.

Note: "Not now" is a normal and legitimate outcome. Terminal maintainers say it a lot, usually for good reasons — compatibility risk, maintenance burden, or a plan you cannot see. A rejected PR that taught you their constraints is still a successful project for your purposes. Record the reason in your write-up; it is more interesting than a merge.


7. If You Cannot Find an Issue: Make One

A high-quality bug report is a legitimate deliverable for this project, and often more useful than a patch.

## Summary
`CSI 38:2::255:0:0m` (colon-form truecolor with an empty colour-space id) is
parsed as SGR 38 followed by SGR 2 (dim), producing dim default-colour text
instead of red.

## Reproduction
    printf '\033[38:2::255:0:0mred\033[0m\n'

Expected: red text (matches xterm, kitty, foot, WezTerm).
Actual:   dim default-coloured text.

Recording attached (asciinema v2), plus the raw byte stream.

## Root cause
`csi_param()` treats ':' as equivalent to ';' (src/parser.rs, the parameter
accumulator), so sub-parameters are flattened into separate parameters. The SGR
handler then sees [38, 2, 255, 0, 0] as five independent parameters rather than
one parameter with five sub-parameters.

Per ITU-T T.416 and xterm's ctlseqs, ':' introduces sub-parameters and the empty
field is the (omitted) colour-space id.

## Impact
Any program emitting the standard colon form renders wrongly. This includes
<list>. The semicolon form works, which is why it has gone unnoticed.

## Suggested fix
Track sub-parameter boundaries in the parameter accumulator. I have a patch and
can open a PR if the approach sounds right.

## Environment
<version>, <os>, TERM=xterm-256color

That report contains: a minimal reproduction, the expected/actual behavior with cross-terminal evidence, the root cause with a file reference, a specification citation, an impact statement, and an offer. A maintainer can act on it in ten minutes.


8. Known Traps

TrapDetail
Writing the patch before commenting on the issueYou may build the wrong thing, or duplicate someone's work
A PR that mixes concernsReviewers will ask you to split it; do it yourself first
Not running their full test suiteThe CI failure is a bad first impression
Ignoring CONTRIBUTING.mdIt exists because they got tired of asking
Assuming your architecture is theirsYour crate boundaries are yours. Match theirs.
Taking review personallyIt is about the code, and they are protecting users
Disappearing after review commentsThe single most common way a good PR dies
Over-explaining in the PR descriptionSymptom, cause, fix, test. Four paragraphs.
Reformatting "while I was in there"Makes the diff unreviewable

Deliverables

  • A project chosen, with a written note on why and what its PRs look like.
  • Built from source; its tests run locally.
  • An issue identified, commented on before coding.
  • A minimal reproduction, in their framework.
  • A PR meeting all eight checklist items above.
  • Review responded to, with iterations.
  • A write-up including the reviewer's comments verbatim and what you learned from them.
  • If not merged: an honest account of why, and whether you agree.

Why This Is the Last Project

Everything else in this curriculum you could grade yourself. The rubric is honest, but you are still marking your own work.

A maintainer reviewing your patch is the first time your understanding meets a standard you did not set — and that is the actual transition this whole curriculum is aimed at. The code you wrote is practice. The review is the exam.


Return to the portfolio, or to the capstone.