Bootstrap org-wide defaults: community health, AI-agent policy, reusable workflows#1
Open
bryanfawcett wants to merge 26 commits intomainfrom
Open
Bootstrap org-wide defaults: community health, AI-agent policy, reusable workflows#1bryanfawcett wants to merge 26 commits intomainfrom
bryanfawcett wants to merge 26 commits intomainfrom
Conversation
Bootstraps org-wide standardization:
- profile/README.md renders the org landing page at
github.com/nyuchitech and links to the community health docs.
- README.md now documents what lives here and how GitHub applies
these defaults to every repo in the org.
- workflow-templates/ adds three reusable CI templates shown in
the "New workflow" UI for every repo in the org:
* ci-nextjs-monorepo - Turborepo + pnpm, affected-only via
turbo --filter=...[base].
* ci-rust-monorepo - Cargo workspace gated on Rust changes;
fmt, clippy, nextest, build, doc with Swatinem/rust-cache.
* ci-python-monorepo - uv workspace with two-layer change
detection: workspace-wide ruff/mypy plus a per-package
pytest matrix driven by diff against the base SHA.
Each template ships a .properties.json for GitHub's workflow
picker. All three pass actionlint.
- CODE_OF_CONDUCT.md adopts Contributor Covenant v2.1 by reference (links to the canonical text rather than inlining it), defines scope as all repos under the org, and routes reports through GitHub's native primitives: private security advisories for repo-level reports and GitHub's abuse form for platform-level issues. No email addresses or personal contacts. - profile/README.md: removed the nyuchi.com link that was not sourced from anything in the repo.
SECURITY.md covers: - Two private reporting channels: [email protected] and GitHub's private security advisory flow. Public issues/PRs for vulnerabilities are explicitly discouraged. - What to include in a report so we can reproduce and triage. - Response commitments: 3 business days to acknowledge, 10 to triage, and a 90-day default coordinated-disclosure window. - Scope rules: in-scope = any non-archived repo in the org plus its releases and build infra; out-of-scope = third-party deps, GitHub itself, DoS, scanner-only reports. - Safe-harbor language for good-faith research.
- SUPPORT.md routes users to the right channel: Discussions for questions, Issues (with templates) for bugs and features, SECURITY.md for vulnerabilities, CODE_OF_CONDUCT.md for conduct reports, CONTRIBUTING.md for contributions. Sets expectations about response time, scope, and no commercial support. - README.md is restructured with a status column (shipped vs. planned) so the table reflects what is actually on disk today and what is still coming. Also adds the workflow-template inventory, including the three monorepo CI templates already shipped and the CodeQL / dependency-review / PR title lint / stale templates still to come.
Combines the grounded description from services.nyuchi.com with the frontier-infrastructure framing provided by org leadership: - Lead: "An infrastructure company building frontier technology for Africa's unique economies." Tagline: "Ubuntu - I am because we are." - "What we build" pillars: Web2->Web3->quantum; on-device, local- first, edge compute and storage for the AGI era; platforms built for communities and shaped by the Ubuntu philosophy. - Ecosystem sections updated to reflect the actual product surface named on services.nyuchi.com: Nyuchi Learning, MailSense, Workspace Tools, and the @nyuchi/* component packages under the Nyuchi brand; Mukoko News under the Mukoko brand. - Links to services.nyuchi.com for the full product catalog. No personal information. Invented URL (nyuchi.com) was already removed in a prior commit; this version only links to services.nyuchi.com, which is sourced from the user.
Pulled grounded content from mukoko.com, nyuchi.com,
design.nyuchi.com, and a scan of the org's 20 public repos:
- Tagline now includes the Shona form: "Ndiri nekuti tiri."
- Top-of-page links to services.nyuchi.com, mukoko.com, and
design.nyuchi.com.
- Added a "Where that shows up in code" section with three groups:
- Frontier infrastructure: `ntl` (Rust signal-based data transfer
layer) and `siafudb` (C++ embedded property graph database for
device, edge, and Web3) - literal examples of the on-device/
local-first/edge pillar.
- Platforms: Mukoko (with its mission quote verbatim from
mukoko.com) and its app family (News, Lingo, Weather, Nhimbe);
`learning`; `shamwari-ai` (described using the repo's own
wording).
- Design: pointer to design.nyuchi.com with "Five African
Minerals" palette and APCA Lc 90+ contrast, and a link to the
`design-portal` repo.
- License section now reflects reality: the org uses a mix of MIT,
Apache 2.0, and occasionally GPL; always check the repo's LICENSE.
Content is quote-accurate to the sources; no invented claims, no
personal info, no emojis.
Covers the strict rules that apply to every repo in the org, with explicit language that individual repos may add stricter rules but may never relax these: - Conventional Commits 1.0 for every commit and every PR title, with the full allowed-types table, scope guidance, and breaking- change conventions. Enforced by the pr-title-lint workflow. - Signed commits required on every merge to main (GPG or SSH, so long as GitHub marks the commit Verified). Branch protection blocks unsigned commits. - DCO 1.1 sign-off required on every commit via `git commit -s`. Clarifies the difference between `-S` (sign) and `-s` (sign off) since contributors routinely confuse them. - Branch naming: `<type>/<kebab-desc>`, lowercase, under 50 chars. Reserves `release/`, `hotfix/`, and `claude/` prefixes. - PR rules: rebase on base, small diffs, squash-merge default, required green CI, one approving review (two for security- sensitive or cross-repo infra changes). - License-neutral section: the org uses a mix of MIT, Apache 2.0, and occasionally GPL; contributions are licensed under the repo's declared license, with explicit reminders about Apache NOTICE files and per-file headers. - Dependency rules tie into the planned dependency-review workflow. - Pointers back to CODE_OF_CONDUCT.md, SECURITY.md, and SUPPORT.md. Also flips CONTRIBUTING's status in README.md from planned to shipped and removes the placeholder "once shipped" wording in the "Contributing to this repo" section.
Ship the org-wide defaults for issue and PR forms. Any public repo under nyuchitech without its own versions now inherits these. Issue templates: - `.github/ISSUE_TEMPLATE/config.yml` disables blank issues and surfaces four routing links from the "New issue" picker: docs repo, support knowledge base, org Discussions, and SECURITY.md (with an explicit "do NOT open a public issue" note). - `.github/ISSUE_TEMPLATE/bug_report.yml` is a structured form with preflight checks (de-dup, read docs, not a vuln), summary, reproduction steps, expected vs actual behaviour, environment (generic enough to cover TS, Rust, Python, C++, Astro repos), logs, additional context, and a Code of Conduct agreement. Title prefilled with `bug: ` so issues are Conventional-Commits-shaped. - `.github/ISSUE_TEMPLATE/feature_request.yml` separates problem from proposal (users routinely conflate them), asks for alternatives considered, a rough scope dropdown, a breaking- change checkbox, an optional "willing to contribute" signal, and CoC agreement. Title prefilled with `feat: `. Pull request template: - `.github/PULL_REQUEST_TEMPLATE.md` with a type-of-change checklist mapped to the Conventional Commits types from CONTRIBUTING.md, a breaking-change section, a test-plan section, and a hard checklist mirroring CONTRIBUTING.md's merge requirements (signed commits, DCO sign-off, branch naming, rebased, tests, docs, runtime-dependency justification, breaking-change footer). Every checklist item links back to the exact CONTRIBUTING.md anchor. All three issue YAML files parsed with PyYAML to confirm well-formed syntax before commit. Also flips the four rows in README.md's status table from planned to shipped.
… lint, docs/MDX CI Completes the workflow-templates catalogue. Each template ships with its matching .properties.json so the display name, description, categories, and file-pattern hints render correctly in every org repo under Actions -> New workflow. codeql.yml Security-extended + security-and-quality query suites over JS/TS, Python, and C/C++ (the three languages CodeQL supports stably that we actually use). Runs on PR, push to main, and weekly cron. Rust is intentionally excluded - CodeQL does not yet support it stably; the Rust CI template handles clippy + cargo-audit instead. Header comment tells users to remove matrix entries for languages their repo does not use. dependency-review.yml Blocks PRs that introduce dependencies with moderate-or-higher vulnerabilities. Posts a summary comment on failure only. License gating is deliberately NOT enforced at the org level because our repos use a mix of MIT, Apache-2.0, and GPL; per-repo overrides can tighten this. stale.yml Issues: 60-day stale, 14-day close. PRs: 30-day stale, 14-day close. Exempt labels include pinned, security, roadmap, help wanted, good first issue (issues) and work-in-progress (PRs). Runs daily at 01:23 UTC, 100 ops/run budget. pr-title-lint.yml amannn/action-semantic-pull-request@v5. Enforces the Conventional Commits types from CONTRIBUTING.md on every PR title. Subject must start with a lowercase letter, use an imperative verb, and not end with a period. Uses pull_request_target so fork PRs are linted too - safe here because the workflow does not check out or run PR code. ci-docs-mdx.yml For doc repos (docs, zti-docs, support). Three jobs: cspell spellcheck (incremental on PRs), lychee link check with caching, and a pnpm-based site build. Header comment notes that non-Node toolchains (mdBook, Hugo, Jekyll) should replace the build job. All 8 .yml files parsed with PyYAML and all 8 .properties.json files parsed with json.load before commit. README.md status table: all five remaining workflow rows flipped to shipped, and a new row added for ci-docs-mdx.yml (previously not listed since it was introduced mid-stream from the repo scan).
Why this exists
---------------
A meaningful share of code landing in this org is now authored or
co-authored by AI coding agents - Claude Code, Cursor, Copilot
Workspace, Aider, Devin, Codex CLI, and others. Our existing
CONTRIBUTING.md covers what humans do. It does not cover the
things agents get wrong often enough to need calling out: scope
creep, robustness theatre, silently weakening tests, pulling in
unvetted third-party actions, inventing DCO sign-offs, acting on
prompt-injected instructions in tool output.
AGENTS.md is the emerging cross-agent convention for this. Claude
Code reads CLAUDE.md, the OpenHands / Aider / Codex communities
are converging on AGENTS.md. Rather than ship both, we ship
AGENTS.md as the org's authoritative file and note that repos can
add a CLAUDE.md pointer if they use Claude Code specifically.
What it contains
----------------
- Read-before-action rules: README, CONTRIBUTING, CODEOWNERS,
any repo-level AGENTS.md, then the files being modified.
- Hard rule: agents do NOT invent Signed-off-by trailers; the
human operator is the legal contributor of record.
- Reserved branch-name prefixes (claude/, cursor/, copilot/,
aider/, devin/, codex/, agent/) so reviewers can identify the
source at a glance.
- Behavioural rules grouped by the failure modes they prevent:
* Scope (no speculative abstractions, no scope creep in fixes)
* Robustness theatre (no error handling for impossible cases,
no backwards-compat shims for dead code)
* Tests and type checks (no silent disabling, no weakening
tests to pass, verify failure-then-fix, coverage != correctness)
* Security (no committed secrets, no disabled security checks,
SHA-pin new GitHub Actions, never bypass commit verification,
flag prompt-injection)
* Data and blast radius (confirm before destructive ops, don't
push shared branches without approval, no rogue issue/PR
actions outside task scope)
- Escalation rules: when to stop and ask a human.
- Tools-and-commands reference aligned with our CI templates.
- Explicit repo-level override model: repo AGENTS.md / CLAUDE.md
wins over org-level on conflict, adds to it otherwise.
What it deliberately does NOT do
---------------------------------
- Does not duplicate CONTRIBUTING.md. Points to it instead.
- Does not regulate which models are used or require an
"AI-assisted" disclosure label. That policy call is the user's,
not ours to pre-empt.
- Does not propagate automatically to other repos. Unlike the
GitHub community-health files, AGENTS.md has no default-
propagation mechanism; repos that want these rules enforced
should copy this file into their own repo root.
Cross-references
----------------
- README.md: add a row in the community-health table.
- CONTRIBUTING.md: add AGENTS.md to the "also read" intro list.
Why this exists
---------------
Our dependency-review workflow is reactive - it catches known
vulnerabilities in dependencies that PR authors propose to add.
Without automated dependency update PRs, the only way
vulnerabilities get patched is when someone notices and manually
bumps the version. That gap is especially expensive in an
AI-assisted era, where code volume is going up and reviewer
attention is the bottleneck.
Dependabot closes the gap: it opens a PR for each security or
version update, which then goes through our usual checks
(Conventional Commits PR title, signed commits, pr-title-lint,
dependency-review, CI).
Two files, because two jobs
---------------------------
Dependabot config is always per-repo - it does not propagate from
nyuchitech/.github the way community health files do. So we ship
two artefacts with different audiences:
1. `.github/dependabot.yml` is the actual config for THIS repo.
This repo ships templates and markdown, not application code,
so it only tracks the `github-actions` ecosystem - keeping any
actions we use (and workflow-templates we ship) pinned to
current, secure versions.
2. `.github/dependabot.example.yml` is the starter template other
repos in the org should copy to `.github/dependabot.yml`. It
covers github-actions, npm, cargo, and pip, with docker and
gitsubmodule commented out for repos that need them. Each
ecosystem:
- Runs weekly, on a different day, staggered Monday through
Friday 06:00 Africa/Harare, so no single repo drowns in
Dependabot PRs on the same morning.
- Uses `build:` as the Conventional Commits prefix so our
pr-title-lint workflow accepts Dependabot's PR titles
without intervention.
- Groups minor and patch updates so reviewers see one PR per
category instead of one per dependency.
Notable decisions
-----------------
- Africa/Harare timezone (UTC+02:00) across the board, matching
the org's primary operating region.
- npm ignore list pins `next`, `react`, and `react-dom` to
deliberate major upgrades. Framework majors break too much to
land via Dependabot.
- uv.lock is not yet natively supported by Dependabot. The Python
CI template already regenerates the lock on pyproject.toml
changes, so Dependabot's pip bumps flow through correctly.
What this deliberately does NOT do
----------------------------------
- Does not enable Dependabot security updates separately. Those
are configured at the repo's Settings -> Security level in
GitHub's UI, not via this file. ORG_SETTINGS.md (next commit in
this series) documents that expectation.
- Does not enable Renovate. Dependabot is first-party to GitHub
and integrates with branch protection without a token; Renovate
is more configurable but adds operational surface we don't need
yet.
- Does not pre-approve Dependabot PRs. Auto-merge for Dependabot
is a per-repo call that some teams want for patch-only bumps;
that's covered as a configurable option in ORG_SETTINGS.md.
Cross-references
----------------
- README.md: new "Automation config" section with rows for both
files.
- AGENTS.md already requires agents to SHA-pin NEW actions they
introduce. Dependabot handles the ongoing version-tracking for
everything that's already there.
Why this exists
---------------
CONTRIBUTING.md says "The CODEOWNERS file in each repo tells you
who reviews PRs." AGENTS.md says "when agents open PRs, CODEOWNERS
is how you guarantee a *human* gets the notification." Both
references pointed to something that didn't exist. This commit
makes them real.
CODEOWNERS matters more in the AI-assisted era, not less: when a
PR can land without any single human having chosen to look at it,
the only safety net is automatic review-assignment. CODEOWNERS is
that safety net.
Two files, because two jobs
---------------------------
CODEOWNERS, like dependabot.yml, does NOT propagate from
nyuchitech/.github. It's per-repo. So we ship two artefacts:
1. `.github/CODEOWNERS` is this repo's actual ownership mapping.
Default owner is @nyuchitech/maintainers. Tighter ownership on:
- security-adjacent files (SECURITY.md, AGENTS.md,
ORG_SETTINGS.md): maintainers + @nyuchitech/security.
- CI / automation (workflow-templates/, dependabot*): add
@nyuchitech/platform.
- /profile/ (public landing page): add @nyuchitech/marketing
because it's a content / brand artefact, not just code.
2. `CODEOWNERS.example` at the repo root is the starter template
other repos should copy to their own `.github/CODEOWNERS`. It
covers:
- Source layouts common across our stacks (src, packages,
apps, crates, lib).
- CI and infrastructure directories (.github/workflows,
infra, terraform, k8s, scripts).
- Lockfiles (pnpm-lock, Cargo.lock, uv.lock, poetry.lock) -
unexpected lockfile churn is often where supply-chain
surprises show up, so we route those through platform.
- Documentation (docs/, *.md, *.mdx).
- Security-sensitive paths (/**/secrets/**, /**/auth/**,
/**/crypto/**) always gated on the security team.
Notable decisions
-----------------
- Team handles (@nyuchitech/maintainers, /security, /platform,
/docs, /marketing) are the INTENDED names, called out as such
in both files. If the org's actual team handles differ, rename
before enabling "Require review from Code Owners" in branch
protection, or GitHub will not be able to resolve the
reviewers.
- Lockfiles are deliberately in the platform-team bucket, not the
default-owner bucket. Reviewing a lockfile diff is a supply-
chain task, not a feature-code task.
- No individual contributor handles are used - ownership is team-
based so vacations and handovers don't block merges.
What this deliberately does NOT do
----------------------------------
- Does not enable "Require review from Code Owners" in branch
protection. That's an org/repo setting, documented in
ORG_SETTINGS.md (next commit in this series).
- Does not try to cover every project layout. Repos with unusual
directory structures should extend the example rather than
treating it as complete.
- Does not auto-propagate. Each repo must copy the template. A
follow-up could script the initial seed across the org's 20
repos, but that's an adoption task, not a defaults task.
Cross-references
----------------
- README.md: new rows in the "Automation config" section.
- AGENTS.md § "Before you touch the code" already tells agents to
read CODEOWNERS; this commit gives them something to read.
Why this exists
---------------
A significant part of our security and review posture lives in
GitHub's UI settings, not in any file:
- Who can create repos.
- Which actions and reusable workflows are allowed.
- What branch protection on main requires (signed commits, linear
history, required status checks, CODEOWNERS review, no bypass
for admins).
- What secret scanning / push protection / private vulnerability
reporting is enabled.
- What org secrets exist and what they're scoped to.
Those settings are trivial to drift out of, easy to forget on a new
repo, and impossible to review because they live in a web UI that
nobody looks at. Every other policy artefact in this repo
(CONTRIBUTING, AGENTS, CODEOWNERS, dependabot) references branch
protection or secret scanning being enabled - without documenting
WHERE that happens and WHAT state it should be in, those references
are load-bearing prose pointing at vapour.
ORG_SETTINGS.md makes the intended state explicit and auditable.
What it contains
----------------
- Org-level settings: member privileges, required 2FA, Actions
allow-list, workflow default permissions, domain verification,
org-wide security features (dependency graph, Dependabot alerts,
secret scanning + push protection, private vulnerability
reporting, CodeQL).
- Repo-level defaults: default branch, issues/discussions/wiki
expectations, merge settings (squash-merge default, auto-delete
head branches).
- Branch protection for main: the full rule set, including
require-signed-commits, require-linear-history, required reviews
(1 default, 2 for .github / security / infra), dismiss-stale-
approvals, require-CODEOWNERS-review, require-conversation-
resolution, required status checks listed by workflow name,
block-force-pushes, apply-to-admins-too.
- Tag protection: v* tags require maintainer approval.
- Secrets: which org secrets exist, what they're used for, and the
explicit rule that long-lived cloud credentials DO NOT go in
Actions secrets - OIDC federation instead.
- Enforcement and audit: today manual, migrate to Rulesets, longer-
term manage via Terraform or probot/settings. Quarterly audit.
Notable decisions
-----------------
- Wiki is disabled org-wide. We keep documentation in repos where
it's reviewed like code, not in parallel wikis that drift.
- "Do not allow bypass" applies to admins too. Admins being able
to push directly to main defeats the point of branch protection.
- Actions are allow-listed, not wide-open. The allow-list names
only actions we actually use (the ones referenced in our
workflow templates plus a few standards like github/codeql-
action).
- "Allow GitHub Actions to create and approve pull requests" is
explicitly DISABLED. A malicious action that could self-approve
its own PR would bypass all our review gates at once.
- No long-lived cloud credentials in Actions secrets. OIDC
federation only.
What this deliberately does NOT do
----------------------------------
- Does not apply any settings. It describes the intended state;
applying it is a manual operation against the GitHub UI (or a
follow-up terraform/ruleset PR).
- Does not enumerate every per-repo deviation. Repos that
legitimately need to differ (e.g. allowing rebase-merge for a
release-train workflow) should note the deviation in their own
README or repo settings docs.
- Does not try to be a GitHub config reference. It only covers
the settings WE care about; everything else defaults to
GitHub's own defaults.
This commit closes the loop on the four follow-ups flagged in the
AI-era review: AGENTS.md, Dependabot, CODEOWNERS, and documented
org settings are now all in place.
Cross-references
----------------
- README.md: new "Operational docs" section.
- CODEOWNERS gates changes to ORG_SETTINGS.md on both
@nyuchitech/maintainers and @nyuchitech/security, matching its
security-adjacent role.
Why this exists
---------------
The workflow templates under workflow-templates/ solve "scaffold me
a CI workflow." They work via copy-on-init: GitHub copies the file
into the repo's .github/workflows/ and from then on the repo owns
that copy. This is great for repos that want to pin the behaviour
and pick up changes deliberately. It's bad for the org as a whole:
when we fix a bug in the Rust template, the 5 repos that already
adopted it do not get the fix unless each maintainer re-copies it.
Reusable workflows solve the same problem from the other side: the
logic lives in ONE place (this repo's .github/workflows/), and
consuming repos reference it with `uses:`. Fixes propagate to every
adopter automatically. The cost is that every caller is coupled to
this repo's main branch (or a pinned SHA).
Rather than pick one model and force every repo into it, we ship
BOTH and document the tradeoff. Each repo chooses per workflow:
- Want to pin the behaviour? Use the starter template. It's a
copy. Upgrades are explicit.
- Want to stay in lockstep with the org? Call the reusable
workflow. Upgrades are automatic. Breakage is also automatic
when we change something.
What's in this commit
---------------------
Eight reusable workflows under .github/workflows/, one per starter
template. Each preserves the exact behaviour of the matching
template (same tools, same commands, same options) but re-shaped
for workflow_call with explicit `inputs:` and `secrets:` blocks.
CI workflows:
reusable-ci-nextjs-monorepo.yml
Inputs: tasks (space-separated, default "lint typecheck test
build"), node-version-file (default .nvmrc).
Secrets: TURBO_TOKEN, TURBO_TEAM (both optional).
Behaviour: matrix job per task, turbo filter auto-computed
from PR base SHA or HEAD^1.
reusable-ci-rust-monorepo.yml
Input: toolchain (default stable).
Behaviour: unchanged from the starter - paths-filter gate,
fmt + clippy + nextest + build + doc jobs with rust-cache.
reusable-ci-python-monorepo.yml
No inputs - convention-based. Assumes uv workspace at the
repo root with .python-version, uv.lock, packages/* layout.
Behaviour: unchanged from the starter - two-layer change
detection, ruff + mypy + per-package pytest matrix.
reusable-ci-docs-mdx.yml
Inputs: build-command (default "pnpm build"),
node-version-file (default .nvmrc), files-glob (default
"**/*.{md,mdx}"). Behaviour: cspell incremental on PRs,
lychee link check with cache, site build.
Security and policy workflows:
reusable-codeql.yml
REQUIRED input: languages - a JSON array of
{language, build-mode} objects. Expressed this way so callers
only declare the languages their repo actually uses, without
us having to ship separate one-language reusables.
reusable-dependency-review.yml
Inputs: fail-on-severity (default moderate),
comment-summary-in-pr (default on-failure).
reusable-pr-title-lint.yml
Input: require-scope (default false). Types list baked in to
match CONTRIBUTING.md - when that list changes, change it
here and in the starter template too.
reusable-stale.yml
Fully parameterised: days-before-*, exempt-*-labels,
operations-per-run. Defaults match what stale.yml currently
uses. Caller provides the schedule (cron) since workflow_call
can't carry schedule triggers through to the reusable.
Notable decisions
-----------------
- Every reusable declares its own permissions: block at the
workflow or job level. Callers must grant AT LEAST those
permissions on the calling job; if they grant less, GitHub
silently downgrades and steps fail in hard-to-debug ways. Each
reusable's header comment spells out what the caller needs.
- No SHA-pinning of internal `uses:` inside the reusables. Tag
pins (@v4, @v5) are acceptable here because Dependabot now
tracks this repo's own Actions dependencies (see commit 44dfac9).
Consuming repos that want SHA-pinning should reference this
repo's reusables by SHA, not tag.
- Reusables do NOT declare `on:` triggers other than
workflow_call. The caller owns the trigger surface. This is
non-negotiable: reusables that try to declare pull_request
will simply never run.
- I kept Rust's dtolnay/rust-toolchain switched from `@stable` to
`@master` with a `toolchain:` input, because the former is an
alias that only points at stable and can't be parameterised.
Functionally identical when the input is left at its default.
What this deliberately does NOT do
----------------------------------
- Does not delete, rewrite, or otherwise touch workflow-templates/.
The starter templates remain valid standalone workflows. Repos
that already adopted them keep working.
- Does not ship .properties.json for the reusables. Reusable
workflows don't show up in the "New workflow" picker - they're
called, not scaffolded - so properties metadata is irrelevant.
- Does not enforce which path a repo picks. Platform team can
recommend; repos choose per workflow.
- Does not set up a release versioning scheme (e.g. v1/v2 major-
version tags pointing at the tip of each major). Callers use
@main today. A future commit can add moving major tags once
we've actually broken something.
Cross-references
----------------
- README.md: the Reusable workflow templates section is now split
into Path A (starter templates) and Path B (reusable workflows)
with a short paragraph explaining when to pick each. Both tables
are populated with every shipped file.
- ORG_SETTINGS.md § Actions permissions: the org Actions allow-list
must include `nyuchitech/*` (workflows from the same org are
allowed by default, but if the allow-list is tightened, the
reusables' paths must remain reachable).
- AGENTS.md § Security: "Pin third-party GitHub Actions by SHA"
applies to the reusables too - consuming repos that care about
this should reference the reusables by SHA, not @main.
Why this exists --------------- Every other file in this repo - workflow templates, CODEOWNERS example, AGENTS.md, ORG_SETTINGS.md, the issue/PR templates - is written with the assumption that downstream repos may copy it. That assumption only works if we're explicit about what copying entails. Up to now, the repo has shipped without any LICENSE file. Our own CONTRIBUTING.md says "contributions inherit the repo's LICENSE." Without a LICENSE, that sentence referenced nothing. What's in this commit --------------------- LICENSE at the repo root: the standard MIT text, copyright Nyuchi Web Services 2026. Why MIT ------- The org's public repos use a mix of licenses - MIT is the majority (14 of 20 repos per the April 2026 scan), Apache 2.0 covers the infrastructure tier (ntl, siafudb), and GPL covers auto-seo-manager. MIT was chosen for this defaults repo because: 1. Consumers will copy these files into repos under ALL of those licenses. MIT is one-way-compatible with Apache 2.0 (MIT code can be included in an Apache 2.0 project under Apache's terms) and GPL (MIT code can be included in a GPL project under GPL's terms). The reverse is not true for any other reasonable choice. 2. It matches the org's majority license, so a maintainer who copies a workflow template into a new MIT repo inherits no new obligations they weren't already expecting. 3. It is short, well-understood, and does not introduce any copyleft or patent provisions that would surprise a consumer who copies a file from here without reading the LICENSE first. What this deliberately does NOT do ---------------------------------- - Does not relicense any file in the repo. Files that had no explicit license before this commit were effectively unlicensed, which meant "all rights reserved" by default; they're now MIT. No file in the repo ships under an incompatible license. - Does not switch the repo to CC0 / CC-BY / Apache 2.0. MIT is the most portable choice for content that will be copied into repos under multiple downstream licenses. - Does not add per-file headers. Per CONTRIBUTING.md, when a file is copied into a repo that uses per-file headers, the consumer adds its own header under its own license. Cross-references ---------------- - CONTRIBUTING.md § Licensing already documents the mix of licenses across the org and says "your contribution is licensed under the repo's existing license." This commit makes that true for this repo in particular. - README.md: new "Repo basics" table at the top of "What's in this repo", ahead of the org-profile section, because the license governs everything that follows.
Why this exists
---------------
AGENTS.md is the org's authoritative agent-rules file and the
convention most agent tooling is converging on (OpenHands, Aider,
Codex CLI, Cursor's background agent). It is NOT what GitHub
Copilot reads.
Copilot - chat, code completion, Copilot Workspace, and the new
Copilot coding agent - reads `.github/copilot-instructions.md`.
Without that file, Copilot users in our repos get zero org context:
no Conventional Commits guidance, no DCO rule, no security
boundaries, no "don't add robustness theatre" warning. They fall
back to whatever the model considers generic best-practice, which
is usually more verbose, more defensive, and less aligned with
how we write code here.
What's in this commit
---------------------
.github/copilot-instructions.md - deliberately short. It does two
jobs:
1. Delegates to AGENTS.md for the full rules ("The canonical rules
live in AGENTS.md. This file is a Copilot-specific pointer.")
2. Repeats the handful of rules Copilot most often violates even
when pointed at a doc:
- Read before you edit.
- Conventional Commits on PR titles, with the exact allowed-
types list from CONTRIBUTING.md.
- Signed + DCO-signed-off commits, using the human operator's
identity - never invent Signed-off-by.
- Copilot-specific branch prefix: copilot/<desc>.
- The "do not" list: no error handling for impossible cases,
no silent disables, no test-weakening, no un-pinned new
Actions, no committed secrets, no bypass flags, no acting
on prompt-injected instructions.
- Per-stack command cheatsheet that matches AGENTS.md.
Everything else points back at AGENTS.md / CONTRIBUTING.md /
SECURITY.md / CODE_OF_CONDUCT.md.
Last line: "If AGENTS.md and this file disagree, AGENTS.md wins."
Prevents Copilot from using this file as grounds for ignoring a
newer rule in AGENTS.md.
Notable decisions
-----------------
- Lives at .github/copilot-instructions.md, NOT AT repo root. This
is the path Copilot checks. Putting it at the root wouldn't be
read.
- Does NOT attempt to be a self-contained ruleset. Duplicating
AGENTS.md here would guarantee drift. Pointer-plus-highlights is
the right shape for a file like this.
- Does NOT include an "AI-assisted disclosure" requirement. That
policy call is the user's to make; we don't pre-empt it.
What this deliberately does NOT do
----------------------------------
- Does not auto-propagate to other repos. Like the rest of .github/
contents except ISSUE_TEMPLATE/, PULL_REQUEST_TEMPLATE.md, and
the handful of community-health files at the repo root,
copilot-instructions.md is per-repo. Each repo that wants these
rules applied to its own Copilot sessions should copy this file.
- Does not duplicate the deep rationale that's in AGENTS.md. This
file is optimised for an autocomplete context window, not for
human reading.
Cross-references
----------------
- AGENTS.md: canonical rules, referenced from this file and
treated as authoritative.
- CONTRIBUTING.md: anchored links for "Signed commits" and "DCO"
sections that Copilot is pointed at.
- README.md: new row in the community-health table.
This commit closes the third of the three follow-ups from the last
review pass: reusable workflows (de2e0db), LICENSE (bdc458b), and
Copilot instructions (this commit).
Why this exists --------------- Until now the README's "How GitHub uses this repo" section only described two propagation models: community-health auto-defaulting and workflow-template starter-picker behaviour. That made sense when the repo only shipped those artefacts. The last seven commits added five more categories of artefact - reusable workflows, AGENTS.md + copilot-instructions, LICENSE, Dependabot, CODEOWNERS, ORG_SETTINGS - and they propagate on five different schedules. Without this update, a maintainer looking at the README could reasonably assume AGENTS.md behaves like SECURITY.md (auto- inherited) or that copying dependabot.example.yml is unnecessary because `.github/dependabot.yml` already exists here. Both assumptions would be wrong and would produce silent failures. What changed ------------ Rewrote the "How GitHub uses this repo" section from a flat list into five labelled categories matching the actual behaviours: 1. Auto-propagated by GitHub (no action needed): CODE_OF_CONDUCT, CONTRIBUTING, SECURITY, SUPPORT, ISSUE_TEMPLATE, PULL_REQUEST_TEMPLATE, workflow-templates. 2. Referenced at runtime via `uses:`: .github/workflows/reusable-*.yml. 3. Read by AI agents in this repo only (do NOT auto-propagate): AGENTS.md, .github/copilot-instructions.md. Repos that want the rules applied must copy the files. 4. Per-repo configuration (starter templates here, each repo copies): .github/dependabot.example.yml, CODEOWNERS.example. Explicit call-out that this repo's own .github/dependabot.yml and .github/CODEOWNERS do NOT get inherited - GitHub's default- propagation mechanism doesn't cover these paths. 5. Operational (applied manually in GitHub's UI): ORG_SETTINGS.md. Source-of-truth doc; not enforced by any file. What this deliberately does NOT do ---------------------------------- - Does not add any new artefacts. Pure documentation sync. - Does not move anything into a different category by changing propagation semantics - the five categories describe the actual, existing behaviour. - Does not promise org-level tooling to auto-copy the "per-repo configuration" files across the 20 repos. That's adoption work, not a defaults-repo concern. Standing rule ------------- Going forward, any commit that adds a new artefact to this repo must also place that artefact in one of the five categories above (or add a new category if none fits) in the same commit. Docs ship with the feature.
Why this exists
---------------
Up to now this repo had zero CI on itself. We were validating
YAML and JSON ad-hoc with PyYAML / json.load before each commit,
which catches syntax errors but does NOT catch:
- Invalid action references (typos in `uses:` lines).
- Wrong event types or unsupported `with:` keys.
- Reusable workflow interface mismatches between caller and
callee (the bug class most likely to bite us, given commit
de2e0db just shipped 8 reusable workflows).
- Missing required `permissions:` blocks for declared actions.
- GitHub Actions expression syntax errors.
- Markdown reference-link breakage and structural issues.
For an enterprise-grade defaults repo whose contents propagate to
20+ other repos, "we ran PyYAML on it" is not adequate quality
evidence. Lint CI is the lowest-cost intervention that closes the
gap.
What's in this commit
---------------------
.github/workflows/lint.yml ships four parallel jobs:
1. actionlint
- Pinned to actionlint 1.7.4, downloaded via the official
install script at the same pinned tag (no curl|bash from a
moving target).
- Runs against BOTH .github/workflows/*.yml AND
workflow-templates/*.yml. The starter templates are designed
to be valid standalone workflows once copied into a repo, so
we lint them in place to catch breakage before consumers do.
2. yamllint
- Pinned to yamllint 1.35.1.
- Configured via .yamllint.yaml at the repo root. Notable
deviations from the default config:
* `truthy.check-keys: false` so `on:` (a YAML 1.1 boolean)
doesn't fail every Actions workflow.
* `line-length` set to warn at 120, not fail. Workflow
files have legitimately long shell heredocs.
* `document-start` disabled (we don't prefix YAML files
with `---`).
3. markdownlint
- Uses DavidAnson/markdownlint-cli2-action@v18.
- Configured via .markdownlint.jsonc at the repo root. Notable
deviations from the default config:
* MD013 (line-length) disabled — we wrap for readability.
* MD033 (inline HTML) disabled — profile/README.md uses
<div align="center"> for the org landing page layout.
* MD041 (first-line H1) disabled — profile/README.md and
several docs open with HTML comments or <div>.
* MD040 (fenced-code-language) disabled — acceptable for
output blocks and plain-text fences.
* MD024 (no-duplicate-headings) set to siblings_only.
4. JSON validity
- Walks every *.json file in the repo and validates with
json.load. Catches the case where a workflow-template's
.properties.json or any other JSON config has been
hand-edited into something invalid.
All four jobs run on every PR and every push to main. They are
expected to be required status checks on main once branch
protection is applied (see ORG_SETTINGS.md and the rulesets JSON
provided alongside this commit).
Notable decisions
-----------------
- actionlint, yamllint, and markdownlint are all pinned to
specific versions or majors. We do NOT pin to commit SHAs at
this point because the friction outweighs the benefit for an
internal lint workflow with no secrets and read-only contents.
Dependabot (commit 44dfac9) will track these dependencies and
open update PRs.
- The lint workflow declares `permissions: contents: read` at
the workflow level — minimum required, no token elevation.
- concurrency: cancel-in-progress on PRs but not on main, so
superseded PR runs are killed but main pushes always complete.
- Lint configs (.yamllint.yaml, .markdownlint.jsonc) are
documented inline with rationale for every rule deviation, so
a future maintainer can tell why each rule is relaxed.
What this deliberately does NOT do
----------------------------------
- Does not add cspell. Spell-check on docs is high-noise on
technical content (Mukoko, Nyuchi, Ubuntu, Shamwari, ntl,
siafudb, shadcn, Turborepo, ...) and would need a custom
dictionary maintained alongside. Defer until we have a docs-
authoring workflow that benefits from it.
- Does not add the lint workflow as a reusable workflow. It is
specific to this repo's structure (workflow-templates/ +
.github/workflows/) and isn't intended for adoption elsewhere.
- Does not enforce MD025 (single H1) or MD025 strictness. Our
files comply with the defaults; explicit configuration would
be noise.
Cross-references
----------------
- README.md: three new rows in the "Repo basics" table for
lint.yml, .yamllint.yaml, and .markdownlint.jsonc.
- ORG_SETTINGS.md § Branch protection — required status checks:
the four job names from this workflow (actionlint, yamllint,
markdownlint, JSON validity) should be added to the required-
status-checks list for this repo's main branch in addition to
"Conventional Commits" from pr-title-lint.
This commit closes the actionlint gap I flagged in the
"merge-readiness" review.
What broke ---------- The lint workflow added in fad7db9 ran for the first time on PR #1 and the markdownlint job reported 34 errors across 4 files. Three were genuine accessibility / structure issues; one was a brittle cosmetic rule. Real content fixes ------------------ SECURITY.md - Line 19: `**[email protected]**` was a bare email. Wrapped in <...> so it renders as a mailto link (`**<[email protected]>**`). Bold preserved. - Line 34: "GitHub's documentation for this flow is [here]" used non-descriptive link text. Rewritten so the link text itself describes the destination ("[private vulnerability reporting flow]"), which is what screen readers and link indexers actually surface. - Lines 65 and 75: `**In scope**` and `**Out of scope**` were bold paragraphs styled as headings. Promoted to real `### ` headings so they appear in the table of contents and get proper landmark navigation. SUPPORT.md - Lines 37 and 45: same pattern - `**We can help with**` and `**We generally cannot help with**` promoted from bold-as- heading to `### ` headings. These are not "make the linter happy" patches - they fix real accessibility issues. Bold text used as a heading is a known a11y antipattern (reported by MD036 for exactly this reason): screen readers don't announce it as a heading, link indexers can't navigate to it, and it doesn't appear in tables of contents. Config change ------------- .markdownlint.jsonc: disable MD060 (table-column-style) with inline rationale. MD060 fires the moment any cell length changes in a table - re-padding sibling rows by hand on every edit is high-friction for zero rendered-output difference. The rule exists because formatters like prettier and mdformat auto-fix it; without one wired up to CI, manually maintaining column alignment is busywork. The 28 MD060 failures across AGENTS.md and ORG_SETTINGS.md were all of this form. If we add a markdown formatter to the lint pipeline later (prettier --write, mdformat, dprint), re-enable MD060. Verification ------------ Re-ran `npx markdownlint-cli2 "**/*.md" "!**/node_modules/**"` locally with the updated config: 0 errors across 10 files. Adheres to the standing rule from AGENTS.md ------------------------------------------- "Don't disable lints, type checks, or tests silently. If you must silence something, leave a same-line comment with the reason." MD060 is now disabled with a multi-line rationale explaining why and what would let us re-enable it. Not silent.
Why this exists --------------- The previous commit (80ec6b3) disabled markdownlint's MD060 (table-column-style) rule with a stated condition for re-enabling it: "If we add a markdown formatter to the lint pipeline later, re-enable this rule." This commit satisfies that condition. The 28 MD060 violations across AGENTS.md and ORG_SETTINGS.md were all the same shape: a single cell length changing on a single row broke the whole table's alignment. That's exactly the failure mode auto-formatters exist to eliminate. Prettier's markdown printer reformats every table to a consistent aligned style, so MD060 can be enforced as a check against drift rather than as a manual maintenance burden. What's in this commit --------------------- 1. `.prettierrc` at the repo root pinning the formatter behaviour that matters: - printWidth: 80 (fits standard terminal/diff views). - proseWrap: preserve - critical. Without this, Prettier would reflow every paragraph on every save and produce huge cross-cutting diffs that destroy git blame. Our prose is wrapped intentionally for readability. - tabWidth: 2, useTabs: false, endOfLine: lf, trailingComma: all - house style that matches the rest of our ecosystem. - embeddedLanguageFormatting: off for *.md / *.mdx so that fenced code blocks (especially YAML and bash inside our workflow examples) aren't reformatted by Prettier and put into conflict with the original source. 2. `.prettierignore` at the repo root that excludes: - YAML files. yamllint and actionlint enforce stricter semantics on those (the `on:` truthy quirk in particular) that Prettier doesn't understand. Letting both touch the same files would produce conflicting expectations. - LICENSE (verbatim text, must not be reformatted). - CODEOWNERS files (have their own structural meaning that Prettier doesn't model). - Lockfiles and node_modules. 3. New `prettier --check` job in .github/workflows/lint.yml. Pinned to prettier 3.3.3, installed globally on Node 20, runs against `**/*.{md,mdx,json,jsonc}`. Should be a required status check on main alongside the other four lint jobs. 4. Re-enabled `MD060: true` in `.markdownlint.jsonc`. Verified locally that Prettier's table output satisfies markdownlint's "aligned" style - 0 errors after `prettier --write` followed by `markdownlint-cli2`. 5. The 11 files Prettier reformatted on this first run: - AGENTS.md, CONTRIBUTING.md, ORG_SETTINGS.md, README.md, SECURITY.md, SUPPORT.md, profile/README.md - tables re-aligned, italics normalised from `*x*` to `_x_`. - .markdownlint.jsonc - trailing comma per JSONC convention. - workflow-templates/ci-docs-mdx.properties.json, codeql.properties.json, dependency-review.properties.json - arrays expanded to multi-line where they exceeded printWidth. None of the changes alter rendered output. Notable decisions ----------------- - `--check` not `--write` in CI. Auto-fixing in CI would require pushing a commit, which is its own surface (token scope, signed commits, DCO trailer). `--check` fails fast and tells the contributor to run `prettier --write` locally. - Prettier scope is markdown + JSON only. NOT YAML. yamllint's `truthy: { check-keys: false }` config and Prettier's YAML printer disagree about how to render the GitHub Actions `on:` key, and resolving that means picking one tool. yamllint wins because actionlint depends on the same indentation conventions. - Prettier 3.3.3 pinned. Dependabot (44dfac9) will track it via GitHub Actions ecosystem updates triggered by changes to the workflow file. - Italics use `_underscores_` after this commit. That's Prettier's default and not worth fighting. GitHub renders both identically. What this deliberately does NOT do ---------------------------------- - Does NOT add a pre-commit hook to auto-format on commit. That's a per-developer-machine concern; an org-wide default repo isn't the place to mandate it. Anyone who wants one can add husky or lefthook to their own repo. - Does NOT format YAML. See above. - Does NOT add Prettier as a reusable workflow. The same pattern (a few lines of CI calling `npm install -g [email protected] && prettier --check`) is shorter than the reusable-workflow invocation would be. Cross-references ---------------- - README.md "Repo basics": new rows for `.prettierrc` and `.prettierignore`. - ORG_SETTINGS.md § required status checks should add `prettier --check` to the list for this repo's main branch alongside the existing four lint job names. Closes the MD060 / "no markdown formatter" gap from the merge-readiness review.
Why this exists
---------------
The previous lint workflow gated merges on prettier --check,
markdownlint, and yamllint. That treats style preferences the
same way as real bugs: a one-character italics-quoting difference
fails CI just as hard as a broken `uses:` reference.
The user's call: style is a developer choice, not an enforcement
target. The CI job's job is to *surface a suggested fix*, not to
demand the developer accept it. Real bugs (workflow syntax,
unparseable JSON) still block. Cosmetic issues (formatting,
italics style, table alignment, line-length warnings) become
inline PR review suggestions.
Two tiers
---------
BLOCKING - failure prevents merge. These jobs catch real bugs
that would ship to every consuming repo of our reusable
workflows:
- actionlint: workflow syntax, expressions, action references,
reusable-workflow interface mismatches.
- JSON validity: anything that can't be parsed.
These two are the ones that belong in the required-status-checks
list of the main-branch ruleset.
ADVISORY - the job runs the linter in --fix / --write mode, then
reviewdog/action-suggester picks up the resulting diff and posts
it as inline PR review suggestions. The developer can click
"commit suggestion" or dismiss. Job exits 0 either way.
- prettier (advisory): markdown + JSON formatting suggestions.
- markdownlint (advisory): markdown structure suggestions.
- yamllint (advisory): YAML style and warnings, surfaced as
GitHub Actions workflow annotations via `yamllint -f github`
plus continue-on-error: true.
These three should NOT be in the required-status-checks list.
They will report a green check or "neutral" status; the value is
the inline suggestions, not the pass/fail signal.
Italics
-------
With prettier moved to advisory, contributors can use `*x*` or
`_x_` for italics. CI will suggest converting `*x*` to `_x_`
(Prettier's preference) but won't fail. Existing files stay as
they are; new contributions can use either style. This matches
the user's "leave it as be" call - we stop enforcing one style.
Permissions
-----------
The three advisory jobs need `pull-requests: write` so reviewdog
can post the suggestions. The two blocking jobs keep the
workflow-default `contents: read`. Token scope is granted at the
job level, not workflow level - principle of least privilege.
What this deliberately does NOT do
----------------------------------
- Does NOT remove the configs (.prettierrc, .markdownlint.jsonc,
.yamllint.yaml). They still drive what reviewdog suggests; the
policy change is purely about whether suggestions are
enforced.
- Does NOT add a pre-commit hook to auto-format on commit. Hooks
remain optional per-developer setup, not an org requirement.
- Does NOT revert the italic changes Prettier made in commit
3344f79. Those files are now formatted; future drift is
acceptable; CI suggests but doesn't insist.
Cross-references
----------------
- README.md "Repo basics": `.github/workflows/lint.yml` row
rewritten to call out the blocking vs advisory split and which
job names go in required-status-checks.
- ORG_SETTINGS.md § required status checks: list should now
contain only `actionlint` and `JSON validity` for this repo,
not the three advisory ones.
- The rulesets JSON I provided previously needs updating too -
remove `markdownlint`, `yamllint`, `prettier --check` from
required_status_checks. Keep only `actionlint` and `JSON
validity`.
Comment on lines
+14
to
+16
| | Path | Purpose | Status | | ||
| | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----: | | ||
| | `LICENSE` | MIT. Declares the terms every other file in this repo ships under, so downstream consumers copying a workflow template or CODEOWNERS example know their obligations. | ✅ | |
There was a problem hiding this comment.
[prettier] reported by reviewdog 🐶
Suggested change
| | Path | Purpose | Status | | |
| | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----: | | |
| | `LICENSE` | MIT. Declares the terms every other file in this repo ships under, so downstream consumers copying a workflow template or CODEOWNERS example know their obligations. | ✅ | | |
| | Path | Purpose | Status | | |
| | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----: | | |
| | `LICENSE` | MIT. Declares the terms every other file in this repo ships under, so downstream consumers copying a workflow template or CODEOWNERS example know their obligations. | ✅ | |
Why this exists --------------- The previous commit (803b732) made prettier, markdownlint, and yamllint advisory - they posted PR review suggestions via reviewdog and never blocked the merge. The reviewdog comment that appeared on PR #1 demonstrated the model working as designed: prettier offered a suggested table-padding diff, the developer could click "commit suggestion" or ignore it. The user's call: that's the wrong shape. CI should not be writing fixes. CI should fail, and the developer should fix it locally and push the corrected commit. The reasoning: - Posting a fix as a PR comment normalises CI editing the developer's PR, which blurs the line between "the developer chose to land this change" and "a bot landed something on behalf of the developer." - Auto-fix-as-suggestion makes lint feel optional. Some PRs get them applied, others don't, and the resulting `main` is inconsistently formatted. - Style consistency only works if it's enforced. A linter that can be ignored stops being a linter. What changed ------------ .github/workflows/lint.yml: rewrote so all five jobs are strict blocking. Removed every `--write`, `--fix`, `continue-on-error`, and `reviewdog/action-suggester`. Job names dropped the "(advisory)" suffix. - actionlint: blocking (unchanged). - JSON validity: blocking (unchanged). - prettier: now `prettier --check`, blocking. Fails the build if any file isn't Prettier-formatted. - markdownlint: now blocking, no `fix: true`. - yamllint: now `yamllint -s -f github .` (strict mode treats warnings as errors). Removed continue-on-error. Required status checks for THIS repo's main branch must list all five names. .github/workflows/reusable-lint.yml (new): same five-job policy exposed as a reusable workflow. Every other repo in the org should call it from a thin caller workflow: on: pull_request: push: branches: [main] jobs: lint: uses: nyuchitech/.github/.github/workflows/reusable-lint.yml@main Inputs let callers tune file globs (`prettier-glob`, `markdownlint-globs`, `yamllint-target`, `actionlint-paths`) and tool versions (`prettier-version`, `yamllint-version`). Defaults match this repo's local lint workflow. Each consuming repo ships its own .prettierrc, .markdownlint.jsonc, and .yamllint.yaml (copy from this repo as starting point) - the tools auto-discover config from the repo root. .yamllint.yaml: two policy adjustments to make strict mode viable: - line-length: bumped 120 -> 140, level: warning -> error. Workflow files have legitimately long shell URLs (the actionlint download script is 95 chars by itself, plus `bash <(curl ` and `) 1.7.4`) and issue-template `about:` strings render as a single line in the picker, so wrapping them changes user-visible behaviour. 140 still flags truly excessive lines. - comments-indentation: disabled. Real configs use comments to delimit sections (long `# ----` banners) and to mark commented-out blocks at the level of the code they replace. Neither fits the rule cleanly. Disabled with rationale inline (not silent). README.md: lint.yml row rewritten to call out the strict-blocking five-job model. Added a row for `reusable-lint.yml` in the reusable-workflows table. ORG_SETTINGS.md § required status checks: split into a "Lint - required on every repo, no exceptions" group (the five lint names) and a "Per-repo CI" group (Conventional Commits, Review dependencies, CodeQL Analyze, primary CI). The lint group is the new bottom-line requirement for every repo. What this commit deliberately does NOT do ----------------------------------------- - Does NOT delete the existing reviewdog comment on PR #1 (it's historical record; will become stale). - Does NOT revert the italic style changes Prettier made in commit 3344f79. The files are formatted; future contributors who use `*x*` will get a strict `prettier --check` failure pointing at exactly what to run (`prettier --write`); they fix locally and push. No hidden state. - Does NOT reformat any file beyond what was needed to make the new strict checks pass. README.md was the only file with prettier drift; one auto-format brought it back in line. - Does NOT add a pre-commit hook. Hooks remain optional per- developer setup. Strict CI is the enforcement point. Cross-references ---------------- - Rulesets JSON I provided previously needs updating: required status checks for main are now exactly: actionlint, JSON validity, prettier, markdownlint, yamllint Same five names for every repo that calls reusable-lint.
Why this exists
---------------
We were shipping every CI / policy workflow twice: once as a copy-
on-init starter template under workflow-templates/, and again as
a `uses:`-callable reusable workflow under .github/workflows/. The
user's call: pick one. They want reusable workflows only.
Reusable wins because:
- Single source of truth. A bug fix in the org's CI logic
propagates to every adopter on the next CI run, instead of
being stuck in N copy-pasted instances.
- Adopter cost is low. A consuming repo writes a 5-line caller
workflow that `uses:` the reusable. That's smaller than the
starter template would have been.
- Matches the user's stated philosophy: examples should be
"alive" - actually executing in production - not static
files repos copy and then forget to upgrade.
What's gone
-----------
Deleted 16 files under workflow-templates/ (8 *.yml + 8 matching
*.properties.json):
- ci-nextjs-monorepo
- ci-rust-monorepo
- ci-python-monorepo
- ci-docs-mdx
- codeql
- dependency-review
- pr-title-lint
- stale
The directory is removed entirely. Each of these has a parallel
reusable workflow in .github/workflows/reusable-*.yml that
adopters call instead.
What's new / changed
--------------------
.github/workflows/lint.yml: rewritten as a thin caller of
.github/workflows/reusable-lint.yml from the same repo. This
proves the reusable works by using it on its own repo - if the
reusable breaks, this repo's CI breaks first, before any
consumer is affected.
jobs:
lint:
uses: ./.github/workflows/reusable-lint.yml
The five jobs (actionlint, JSON validity, prettier, markdownlint,
yamllint) now run via the reusable. Status check names take the
form `lint / actionlint`, `lint / prettier`, etc. - the caller
job name prefixes the reusable job name.
.github/CODEOWNERS: removed `/workflow-templates/` rule; added
`/.github/workflows/` to the platform team's ownership instead
(reusable workflows are even more sensitive than starter
templates were because changes propagate live).
README.md:
- "Reusable workflow templates" section rewritten. The Path A /
Path B split is gone. There is one path: write a thin caller
that `uses:` the reusable.
- Added a 10-line copy-paste adopter pattern showing the
canonical `uses:` invocation.
- Note about SHA-pinning for repos with stricter supply-chain
requirements.
- "How GitHub uses this repo" propagation section: the
`Workflow templates` bullet under "Auto-propagated by GitHub"
is removed (those don't exist anymore).
ORG_SETTINGS.md required-status-checks list: rewritten to use
the `<caller-job> / <reusable-job>` format that reusable
workflows produce. Convention: callers name their jobs `lint:`,
`pr-title:`, `dep-review:`, `codeql:`, etc., so check names
read sensibly.
What this deliberately does NOT do
----------------------------------
- Does NOT change the reusable workflows themselves (other than
lint.yml's caller). The eight reusables under
.github/workflows/reusable-*.yml are unchanged in this commit.
- Does NOT version the reusables (no v1 tag, no major-version
alias). Adopters use @main today; we add tags after a real
release lifecycle proves itself.
- Does NOT delete the dependabot.example.yml or CODEOWNERS.example.
Those are configuration files, not workflows; Dependabot config
and CODEOWNERS are inherently per-repo and can't be made
reusable in the same way. Each repo copies the example once and
customises.
Cross-references
----------------
- Rulesets JSON I provided previously needs updating again: the
required status check contexts now include the `lint /` prefix.
For this repo's main branch:
"lint / actionlint", "lint / JSON validity",
"lint / prettier", "lint / markdownlint", "lint / yamllint"
Why this exists
---------------
Previous Dependabot config split each ecosystem's PRs across
multiple sub-groups (production-minor-and-patch separately from
dev-minor-and-patch, separate PRs for major-version updates, etc.)
and staggered ecosystems across Monday-Friday. The result: a repo
using npm + cargo + pip + github-actions could see 10-15
Dependabot PRs per week, scattered across days.
User's call: that's annoying. Run once a week, produce one PR per
ecosystem with every update bundled in. No per-package PR sprawl,
no per-update-type splits. Major version bumps land in the same PR
as patch bumps; the developer reviews the bundle and decides.
What changed
------------
`.github/dependabot.yml` (this repo, github-actions only):
- open-pull-requests-limit: 5 -> 1. We only want one PR. The
previous limit allowed Dependabot to create up to 5 in flight
if it felt like splitting things; with the single `all` group
that won't happen, but the cap makes the policy explicit.
- groups: collapsed `actions:` -> `all:` with explicit
`update-types: ["major", "minor", "patch"]` so nothing
escapes the bundle.
- Header comment rewritten to document the org policy that this
file embodies.
`.github/dependabot.example.yml` (template for other repos):
- All ecosystems (github-actions, npm, cargo, pip, plus
commented docker and gitsubmodule) now use the same
`groups: { all: { patterns: ["*"], update-types: [...] } }`
pattern. Single PR per ecosystem.
- All ecosystems run weekly Monday 06:00 Africa/Harare. Removed
the Mon-Fri stagger - the staggering only made sense when each
day produced separate PRs. Now Mondays produce at most one PR
per ecosystem (so 5 PRs at most for a repo with all ecosystems).
- open-pull-requests-limit: 10 -> 1 across the board.
- npm ignore list for next/react/react-dom major-version updates
is preserved with a clarifying comment that minor and patch
updates still flow through the `all` group.
- Removed per-ecosystem grouping rules
(`runtime-minor-and-patch`, `dev-minor-and-patch`,
`rust-minor-and-patch`, `python-minor-and-patch`) - all
replaced with the single `all` group.
Concrete result
---------------
A repo using github-actions + npm + cargo + pip + docker now
receives at most 5 Dependabot PRs per week, all on Monday morning,
each containing every available update for one ecosystem.
Trade-off
---------
Bundling major version bumps with patches in one PR means the
developer can't merge "the easy stuff" while reviewing the breaking
change separately. The user has decided this trade-off is worth it
- volume reduction matters more than per-update merge granularity.
A repo that needs the inverse can split its `all` group locally.
Cross-references
----------------
- README.md "Automation config" rows for both Dependabot files
updated to call out the weekly-Monday + one-grouped-PR policy.
- CONTRIBUTING.md § Dependencies still applies: new RUNTIME deps
in human-authored PRs need justification. Dependabot bumps are
exempt from that since they're version-tracking, not new deps.
Mechanical search-and-replace of `nyuchitech` -> `nyuchi` across all 99 occurrences in 29 tracked files: URLs, repo paths, @team handles in CODEOWNERS, `uses:` references in reusable workflow header comments, and the org-search query string in SUPPORT.md. Prettier re-alignment: the new name is 4 chars shorter, so pipe- aligned tables in README.md and SUPPORT.md needed re-padding. `prettier --write` fixed that; no content change beyond whitespace. Verified locally before commit: - prettier --check: clean - markdownlint: 0 errors - yamllint -s: clean - git grep nyuchitech: no remaining occurrences Pushed via MCP (mcp__github__push_files) because the sandbox's git remote returned HTTP 503 on all 7 direct-push attempts. MCP still uses the old `nyuchitech` slug because the session's allowlist is immutable; GitHub's org-rename redirect translates to `nyuchi/.github` on the backend. Out-of-band follow-ups for the user: - `git remote set-url origin https://github.com/nyuchi/.github.git` on any clone that still tracks the old URL. - Verify the teams exist at @nyuchi/maintainers, /security, /platform, /docs, /marketing. - Rulesets JSON should be POSTed to /repos/nyuchi/.github/rulesets.
Follow-up to 820770e which only landed .prettierignore. This commit pushes the rest of the 29-file rename: the rest of the .github/ contents (CODEOWNERS, ISSUE_TEMPLATE, PR template, copilot- instructions, dependabot, workflows) and every root doc. After this commit, the branch state matches the intended single logical rename: 99 occurrences of `nyuchitech` across 29 files replaced with `nyuchi`, plus Prettier re-alignment of two tables whose column widths no longer lined up with the 4-char-shorter name. Same verification as 820770e: prettier --check clean, markdownlint 0 errors, yamllint -s clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Bootstraps
nyuchitech/.githubas the organization-wide defaultcommunity-health, AI-agent policy, and CI automation hub for every
public repo under Nyuchi Web Services.
Everything shipped here either auto-inherits into other repos
(community-health files, issue/PR templates, workflow templates),
is
uses:-referenced at runtime (reusable workflows), or ships asa starter template repos copy into their own tree (CODEOWNERS,
Dependabot).
What's in this PR
Repo basics
LICENSE— MIT. Chosen because it matches the org's majoritylicense and is one-way-compatible with Apache 2.0 and GPL repos
that may copy files from here.
Org profile
profile/README.md— landing page athttps://github.com/nyuchitech. Positions Nyuchi as an
"infrastructure company building frontier technology for Africa's
unique economies," grounded in the Ubuntu philosophy
("I am because we are" / "Ndiri nekuti tiri"). Content verified
against services.nyuchi.com, mukoko.com, design.nyuchi.com, and a
scan of the org's 20 public repos. Mukoko's mission line quoted
verbatim;
ntlandsiafudbhighlighted as the frontier-infratier.
Community-health files (auto-inherited)
CODE_OF_CONDUCT.md— Contributor Covenant 2.1.SECURITY.md— private vulnerability reporting via GitHubSecurity Advisories, response commitments, safe-harbor terms.
SUPPORT.md— routes users to docs, Discussions, Issues, andSecurity.
CONTRIBUTING.md— Conventional Commits, signed commits(GPG/SSH), DCO sign-off, branch naming, squash-merge default,
license-neutral. The rules every human and agent PR must meet.
AI-agent policy
AGENTS.md— canonical cross-agent ruleset for Claude, Cursor,Copilot, Aider, Devin, Codex. Reserved branch prefixes per agent,
explicit rule that agents never invent
Signed-off-bytrailers,failure-mode callouts (scope creep, robustness theatre, silently
weakened tests, un-pinned new Actions, prompt-injection).
.github/copilot-instructions.md— Copilot-specific pointer toAGENTS.mdplus the handful of rules Copilot most often violateseven when pointed at a doc. Lives at Copilot's canonical path.
Issue and PR forms (auto-inherited)
.github/ISSUE_TEMPLATE/config.yml— disables blank issues;routes "New issue" picker to docs, support KB, Discussions,
SECURITY.md.
.github/ISSUE_TEMPLATE/bug_report.yml— structured form,preflight checks, repro steps, expected vs actual, generic
environment block (TS/Rust/Python/C++/Astro), CoC agreement.
Title prefilled
bug:..github/ISSUE_TEMPLATE/feature_request.yml— separatesproblem from proposal, alternatives, scope dropdown,
breaking-change checkbox, willing-to-contribute signal, CoC.
Title prefilled
feat:..github/PULL_REQUEST_TEMPLATE.md— type-of-change checklistmapped to Conventional Commits, breaking-change section, test
plan, hard merge checklist mirroring CONTRIBUTING.md with
anchored links to every rule.
Automation config (per-repo; this repo ships a real config + a starter template)
.github/dependabot.yml— Dependabot config for this repo(github-actions ecosystem only).
.github/dependabot.example.yml— full template other repos copy.Five ecosystems (github-actions, npm, cargo, pip, docker +
gitsubmodule commented out), staggered Mon-Fri 06:00 Africa/Harare,
build:commit prefix so pr-title-lint accepts Dependabot PRs,minor/patch grouping.
.github/CODEOWNERS— code-review ownership for this repowith security-tighter paths.
CODEOWNERS.example— starter template for other repos. Coverscommon source layouts, CI paths, lockfiles (routed to platform
team — lockfile diffs are supply-chain events), docs, and
security-sensitive
/**/secrets/**,/**/auth/**,/**/crypto/**paths.
Operational docs
ORG_SETTINGS.md— source of truth for GitHub UI settings:member privileges, required 2FA, Actions allow-list, workflow
default permissions, domain verification, full org-wide security
features (dependency graph, Dependabot alerts, secret scanning +
push protection, private vulnerability reporting, CodeQL),
repo-level defaults, branch protection for
main(requiredsigned commits, linear history, CODEOWNERS review, required
status checks named by workflow, applied to admins too), tag
protection for
v*, secrets policy (no long-lived cloud creds —OIDC only), Ruleset/Terraform migration path, quarterly audit.
Starter workflow templates (Actions → New workflow)
CI:
workflow-templates/ci-nextjs-monorepo.yml— Turborepo + pnpm,affected-only via
turbo --filter=...[base].workflow-templates/ci-rust-monorepo.yml— Cargo workspace gatedon Rust changes; fmt, clippy, nextest, build, doc with
Swatinem/rust-cache.
workflow-templates/ci-python-monorepo.yml— uv workspace withtwo-layer change detection: workspace-wide ruff/mypy plus
per-package pytest matrix driven by diff against the base SHA.
workflow-templates/ci-docs-mdx.yml— cspell incremental on PRs,lychee link check (cached), pnpm-based site build for MDX docs.
Security and housekeeping:
workflow-templates/codeql.yml— security-extended +security-and-quality over JS/TS, Python, C/C++. PR, push to main,
weekly cron. Rust excluded (no stable CodeQL support).
workflow-templates/dependency-review.yml— blocks PRs withmoderate-or-higher vulnerabilities. License enforcement left to
per-repo overrides.
workflow-templates/pr-title-lint.yml— amannn/action-semantic-pull-request@v5enforcing CONTRIBUTING's Conventional Commits types.
workflow-templates/stale.yml— 60/14 on issues, 30/14 on PRs,daily 01:23 UTC.
Each ships with a matching
*.properties.jsonfor GitHub'sworkflow picker.
Reusable workflows (runtime-referenced via
uses:)New: eight reusable workflows under
.github/workflows/reusable-*.yml,one per starter template. Solves the starter-template drift problem:
fixes propagate to every adopter automatically.
reusable-ci-nextjs-monorepo.yml— inputs:tasks,node-version-file; secrets:TURBO_TOKEN,TURBO_TEAM.reusable-ci-rust-monorepo.yml— input:toolchain.reusable-ci-python-monorepo.yml— convention-based, no inputs.reusable-ci-docs-mdx.yml— inputs:build-command,node-version-file,files-glob.reusable-codeql.yml— required input:languages(JSON array).reusable-dependency-review.yml— inputs:fail-on-severity,comment-summary-in-pr.reusable-pr-title-lint.yml— input:require-scope; types listtracks CONTRIBUTING.md.
reusable-stale.yml— fully parameterised, defaults matchstale.yml.Adoption model: Path A = copy starter template, own it forever.
Path B = reference the reusable, get automatic upgrades. Repos
choose per workflow.
Repo-index README
README.md— documents the layout, propagation semantics(auto-inherited vs runtime-referenced vs per-repo-copy vs
operational), and tracks status. Every row reads ✅.
How GitHub uses these artefacts
Different artefacts propagate in different ways — "org-wide default"
means different things for different files:
CODE_OF_CONDUCT.md,CONTRIBUTING.md,SECURITY.md,SUPPORT.md,.github/ISSUE_TEMPLATE/*,.github/PULL_REQUEST_TEMPLATE.md,workflow-templates/*uses:.github/workflows/reusable-*.ymlAGENTS.md,.github/copilot-instructions.md.github/dependabot.example.yml,CODEOWNERS.exampleORG_SETTINGS.mdREADME.md § "How GitHub uses this repo" covers this in full.
Test plan
workflows, 3 issue templates, 2 Dependabot configs).
.properties.jsonfiles parse withjson.load.profile/README.mdrenders correctly at the org landing page.(verified against services.nyuchi.com, mukoko.com,
design.nyuchi.com, and the public repo scan).
CONTRIBUTING references
AGENTS.md; it exists.ORG_SETTINGS.mdreferences required status checks by name;those workflow names exist.
pr-title-lintis adopted on this repo, it will lint itself).ORG_SETTINGS.mdin GitHub's UI(branch protection, secret scanning, Actions allow-list,
domain verification, org secrets).
CODEOWNERSreferences(
@nyuchitech/maintainers,/security,/platform,/docs,/marketing) or rename the handles to match existing teams.templates in every public org repo.
profile/README.md.least one repo (likely
ntlorsiafudb) end-to-end.Caveats to flag for review
@nyuchitech/maintainers,/security,/platform,/docs,/marketing) are theintended names. If the real team handles differ, rename before
enabling "Require review from Code Owners" in branch protection,
or GitHub silently can't resolve reviewers.
Each repo that wants these rules applied to its own agent sessions
must copy the files.
Applying it is manual against the GitHub UI, or a follow-up PR
to a terraform/github or probot/settings configuration.
@mainin the examples. Consumingrepos that want supply-chain-safe pinning should reference the
reusables by commit SHA.
Commit order
The 17 commits on this branch tell an ordered story — read them
in order if you want the full rationale:
7cd1caabootstrap: profile + 3 monorepo CI templates4c4a5ecCODE_OF_CONDUCT + drop invented URL3a39f87SECURITY044d08eSUPPORT + README refresh9c74465reposition profile around infrastructure + Ubuntu41b8d78enrich profile with verified extras from brand sitesbc0db2bCONTRIBUTING958e6aaissue forms + PR templatec82f8aeCodeQL + dep-review + stale + PR-title-lint + docs/MDX CI266859dAGENTS.md44dfac9Dependabot config + examplee4fe364CODEOWNERS + examplebcbf40eORG_SETTINGS.mdde2e0db8 reusable workflowsbdc458bLICENSE6327c8acopilot-instructions.md155c0f0document propagation semantics for every artefactReferences