Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions Governance/processes/ONBOARDING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Contributor Onboarding Process

## Purpose

This process defines how new contributors are onboarded in TeachLink Web: the
steps they follow, the resources they receive, and who owns onboarding. It makes
the path from interest to productive contribution explicit and keeps it
self-contained in the `Governance/` folder.

## Scope

This process applies to people becoming contributors as defined in
`Governance/roles/CONTRIBUTOR.md`. It complements `CONTRIBUTING.md`,
`Governance/policies/FIRST_TIME_CONTRIBUTOR.md`, and
`Governance/policies/GOOD_FIRST_ISSUE.md`. It focuses on the human onboarding
experience (orientation, resources, and clarity of expectations).

## Onboarding Steps

New contributors follow these steps:

1. **Orientation.** Review `CONTRIBUTING.md` and the Code of Conduct
(`Governance/CODE_OF_CONDUCT.md`) to understand expectations and norms.
2. **Find a suitable issue.** Look for issues labelled as good first issues
per `Governance/policies/GOOD_FIRST_ISSUE.md` or discuss a proposed change in
an issue before starting work.
3. **Get assigned.** An issue must be assigned before opening a pull request, as
described in `CONTRIBUTING.md`.
4. **Set up locally.** Follow project setup instructions in `README.md` and any
environment notes referenced by the issue.
5. **Implement and test.** Make a small, focused change on a feature branch,
following project standards and quality gates (`type-check`, `lint`, `build`,
`test`, `security-audit`).
6. **Submit for review.** Open a pull request that references and closes the
assigned issue, responding to review feedback until approved and merged.
7. **Follow-up and recognition.** After merge, contributors are acknowledged
according to project practices and may explore the next steps on the
contributor ladder.

## Resources a New Contributor Receives

New contributors receive the following resources to be successful:

- **Clear entry points.** Guidance via `Governance/policies/GOOD_FIRST_ISSUE.md`
and `Governance/policies/FIRST_TIME_CONTRIBUTOR.md`.
- **Project documentation.** Access to `CONTRIBUTING.md`, `README.md`, and
relevant `Governance/` documents for context.
- **Feedback loops.** Timely review feedback on pull requests and responses to
questions in issue discussions, consistent with
`Governance/policies/REVIEW_SLA.md` and `Governance/processes/TRIAGE.md`.
- **Mentorship guidance.** Direction to maintainers or experienced contributors
when questions arise, without assuming private hand-holding.

## Who Owns Onboarding

- **Maintainers.** Own the onboarding process and ensure it remains accurate
and accessible.
- **Triage/experienced contributors.** Help label and recommend good first
issues, provide clarifying feedback, and support newcomers in public forums.
- **New contributors.** Own their learning by reading the provided resources,
asking questions in public, and following the agreed steps.

## Ownership

Maintainers own this process and updates to it. Changes to this document are
proposed in a pull request that touches only the `Governance/` folder.

## Success

This process succeeds when new contributors can move from interest to their
first merged pull request with minimal friction, when expectations are clear,
and when onboarding is consistent across contributors.

## Regression Tests

Regression coverage for this process lives in
`Governance/processes/ONBOARDING.test.ts`. It verifies the document structure,
required sections, line length, absence of placeholders, references to key
policies and resources, and the explicit ownership model.

## Revision History

| Version | Date | Change | Author |
| ------- | ---------- | ---------------- | --------------------- |
| 1.0 | 2026-09-28 | Initial version. | TeachLink maintainers |
99 changes: 99 additions & 0 deletions Governance/processes/ONBOARDING.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* Regression tests for the Contributor Onboarding Process
* (Governance/processes/ONBOARDING.md).
*/
import { describe, expect, it } from 'vitest';
import { readFileSync } from 'node:fs';
import path from 'node:path';

const DOC_PATH = path.resolve(__dirname, 'ONBOARDING.md');
const doc = readFileSync(DOC_PATH, 'utf8');

function plainProse(markdown: string): string {
return markdown
.replace(/`([^`]*)`/g, '$1')
.replace(/\*\*([^*]*)\*\*/g, '$1')
.replace(/\[([^\]]*)\]\(([^)]*)\)/g, '$1 $2')
.replace(/\s+/g, ' ')
.toLowerCase();
}

const prose = plainProse(doc);
const sections = [...doc.matchAll(/^## (.+)$/gm)].map((match) => match[1]);

function sectionBody(title: string): string {
const start = doc.indexOf(`## ${title}\n`);
expect(start, `section "${title}" is missing`).toBeGreaterThanOrEqual(0);
const next = doc.indexOf('\n## ', start + 1);
const body = next === -1 ? doc.slice(start) : doc.slice(start, next);
return plainProse(body);
}

describe('ONBOARDING process document structure', () => {
it('is titled "Contributor Onboarding Process"', () => {
expect(doc.startsWith('# Contributor Onboarding Process\n')).toBe(true);
});

it('keeps the canonical governance document sections', () => {
expect(sections).toEqual([
'Purpose',
'Scope',
'Onboarding Steps',
'Resources a New Contributor Receives',
'Who Owns Onboarding',
'Ownership',
'Success',
'Regression Tests',
'Revision History',
]);
});

it('has no unresolved template placeholders', () => {
expect(doc).not.toMatch(/TBD|TODO|FIXME|<[a-z-]+>|XXX/);
});

it('stays within the house documentation line width (max 82 columns)', () => {
const longest = Math.max(...doc.split('\n').map((line) => line.length));
expect(longest).toBeLessThanOrEqual(82);
});
});

describe('onboarding process content requirements', () => {
it('lists onboarding steps clearly', () => {
const body = sectionBody('Onboarding Steps');
expect(body).toContain('orientation');
expect(body).toContain('find a suitable issue');
expect(body).toContain('get assigned');
expect(body).toContain('set up locally');
expect(body).toContain('implement and test');
expect(body).toContain('submit for review');
});

it('specifies resources received', () => {
const body = sectionBody('Resources a New Contributor Receives');
expect(body).toContain('clear entry points');
expect(body).toContain('project documentation');
expect(body).toContain('feedback loops');
expect(body).toContain('mentorship guidance');
});

it('defines ownership model', () => {
const body = sectionBody('Who Owns Onboarding');
expect(body).toContain('maintainers');
expect(body).toContain('new contributors');
});

it('references key policies and resources', () => {
expect(prose).toContain('governance/roles/contributor.md');
expect(prose).toContain('contributing.md');
expect(prose).toContain('governance/policies/first_time_contributor.md');
expect(prose).toContain('governance/policies/good_first_issue.md');
expect(prose).toContain('governance/policies/review_sla.md');
expect(prose).toContain('governance/processes/triage.md');
});

it('pins regression coverage to companion test', () => {
const body = sectionBody('Regression Tests');
expect(body).toContain('governance/processes/onboarding.test.ts');
});
});
75 changes: 75 additions & 0 deletions Governance/roles/EMERITUS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Emeritus Role

## Purpose

This document defines emeritus status in TeachLink Web: what it means to be
emeritus, which privileges are retained and which are removed, and how the
status is granted. It closes a governance gap by making this status explicit and
self-contained in the `Governance/` folder.

## Scope

Emeritus status applies to people who previously held a defined role in
`Governance/roles/` (for example contributor, issue triager, moderator,
maintainer, or treasurer) and have stepped down from active duty while wishing
to remain associated with the project in a limited capacity. It does not create
a new voting role and does not override other policies.

## Emeritus Status

Being emeritus means a person is no longer actively responsible for the duties
of their former role, but the project recognizes their past service. The status
is honorary and opt-in: it is granted at the request of the former role holder
or by mutual agreement when stepping down.

## Retained and Removed Privileges

- **Retained.** Emeritus members may be listed in project recognition
materials per `Governance/RECOGNITION.md`, receive appropriate attribution,
and continue to be consulted informally if they consent. They may also
participate in discussions as community members.
- **Removed.** Emeritus members do not hold the duties, access rights, or
decision authority of the former role unless explicitly reappointed. They are
not counted toward quorum for decisions reserved to maintainers or other role
holders unless a specific policy states otherwise. Any system access tied to
the role is revoked or converted to community-level access as appropriate.

## How the Status Is Granted

- **Request.** A person eligible for emeritus status (or their designee in
consultation with maintainers) requests it when stepping down from an active
role. The request may be part of a nomination/removal outcome or a separate
request recorded in a public issue.
- **Process.** The maintainers record the transition in a public issue,
confirm the retained/removed privileges with the individual, and update role
records to reflect emeritus status. When stepping down from a role governed
by `Governance/processes/NOMINATION.md`, the nomination outcome may document
emeritus status.
- **Documentation.** The decision and rationale are recorded in the public
thread with the individual's consent. Recognition follows
`Governance/RECOGNITION.md`.

## Ownership

Maintainers own this role definition and updates to it. Changes to this
document are proposed in a pull request that touches only the `Governance/`
folder.

## Success

This status succeeds when transitions are clear and respectful, when service is
recognized without implying ongoing responsibility, and when former role holders
understand exactly what remains and what does not.

## Regression Tests

Regression coverage for this role lives in
`Governance/roles/EMERITUS.test.ts`. It verifies the document structure,
required sections, line length, absence of placeholders, references to
recognition and nomination processes, and the explicit grant process.

## Revision History

| Version | Date | Change | Author |
| ------- | ---------- | ---------------- | --------------------- |
| 1.0 | 2026-09-28 | Initial version. | TeachLink maintainers |
94 changes: 94 additions & 0 deletions Governance/roles/EMERITUS.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* Regression tests for the Emeritus Role
* (Governance/roles/EMERITUS.md).
*/
import { describe, expect, it } from 'vitest';
import { readFileSync } from 'node:fs';
import path from 'node:path';

const DOC_PATH = path.resolve(__dirname, 'EMERITUS.md');
const doc = readFileSync(DOC_PATH, 'utf8');

function plainProse(markdown: string): string {
return markdown
.replace(/`([^`]*)`/g, '$1')
.replace(/\*\*([^*]*)\*\*/g, '$1')
.replace(/\[([^\]]*)\]\(([^)]*)\)/g, '$1 $2')
.replace(/\s+/g, ' ')
.toLowerCase();
}

const prose = plainProse(doc);
const sections = [...doc.matchAll(/^## (.+)$/gm)].map((match) => match[1]);

function sectionBody(title: string): string {
const start = doc.indexOf(`## ${title}\n`);
expect(start, `section "${title}" is missing`).toBeGreaterThanOrEqual(0);
const next = doc.indexOf('\n## ', start + 1);
const body = next === -1 ? doc.slice(start) : doc.slice(start, next);
return plainProse(body);
}

describe('EMERITUS role document structure', () => {
it('is titled "Emeritus Role"', () => {
expect(doc.startsWith('# Emeritus Role\n')).toBe(true);
});

it('keeps the canonical governance document sections', () => {
expect(sections).toEqual([
'Purpose',
'Scope',
'Emeritus Status',
'Retained and Removed Privileges',
'How the Status Is Granted',
'Ownership',
'Success',
'Regression Tests',
'Revision History',
]);
});

it('has no unresolved template placeholders', () => {
expect(doc).not.toMatch(/TBD|TODO|FIXME|<[a-z-]+>|XXX/);
});

it('stays within the house documentation line width (max 82 columns)', () => {
const longest = Math.max(...doc.split('\n').map((line) => line.length));
expect(longest).toBeLessThanOrEqual(82);
});
});

describe('emeritus role content requirements', () => {
it('defines emeritus status as honour-of-active-duty', () => {
const body = sectionBody('Emeritus Status');
expect(body).toContain('no longer actively responsible');
expect(body).toContain('honorary and opt-in');
});

it('defines retained and removed privileges', () => {
const body = sectionBody('Retained and Removed Privileges');
expect(body).toContain('retained');
expect(body).toContain('removed');
expect(body).toContain('attribution');
expect(body).toContain('decision authority');
expect(body).toContain('not counted toward quorum');
});

it('defines how status is granted', () => {
const body = sectionBody('How the Status Is Granted');
expect(body).toContain('request');
expect(body).toContain('process');
expect(body).toContain('documentation');
expect(body).toContain('public issue');
});

it('references recognition and nomination processes', () => {
expect(prose).toContain('governance/recognition.md');
expect(prose).toContain('governance/processes/nomination.md');
});

it('pins regression coverage to companion test', () => {
const body = sectionBody('Regression Tests');
expect(body).toContain('governance/roles/emeritus.test.ts');
});
});
Loading
Loading