Code is MIT; by opening a PR you license your contribution under the repo's terms (GitHub ToS §D.6,
inbound = outbound). No CLA, no sign-off line. Agents: also read AGENTS.md.
python3.11 -m venv .venv && source .venv/bin/activate && pip install -e ".[dev,local,s3,notebooks,grid,azure,aml,mpc,titiler,serving]"
.venv/bin/python -m pytest -q && .venv/bin/ruff check src/ tests/main is protected: no direct pushes. Work on a branch and open a PR from the template, as a draft
while you are still working. When gates 1–3 hold, mark it ready ("Ready for review" on GitHub): that tells
the maintainer it is waiting to be merged. If the review or the real run waits on the maintainer, mark it
ready once the other gates hold, and say what waits in the PR description.
- CI green: ruff, the fast pytest suite, the guard tests,
scripts/docs_kwarg_sweep.py. A PR that changes only Markdown files (orLICENSE/NOTICE) runs ruff, the docs guards and the sweep, not the rest. - Linked issue. Changing a convention, an on-disk format or the public API also needs a signed-off
short spec (
specs/TEMPLATE.md; its number is its tracking issue's number). Bug fixes, refactors, docs and a new collection that followsdocs/adding-a-source.mdneed only the issue. - Reviewed by someone other than the author, from an account with write access to the repo, as a PR comment. A review by anyone else, including your own reviewer agent when you have no write access, is a welcome pre-review: mark the PR ready and say that gate 3 waits on the maintainer. Every finding is fixed in the PR or filed as an issue.
- Real-run evidence when you touch real data, the cloud or pixels: paste the output or a screenshot
(QGIS). "The cloud" includes code that dispatches, runs as or reports from a cloud job: the AML runner,
rise_eo.workflows.shard/infer_shard, therise_eo.workflows.runnersthey call, image definitions. That holds even when tests use fakes. A run with the real tools and no fakes (e.g. real Snakemake on a laptop) counts, unless the change depends on Azure itself. No archive or Azure access? Say so; the reviewer runs it. A comments-only diff needs no run when all four hold on a clean checkout of the PR head, with<base>=git merge-base origin/main HEAD; paste the four outputs instead (spec 102 A9.1):scripts/comment_astcheck.py <base>exits 0 with noNEWline;git diff --diff-filter=ADR --name-only <base> -- src/prints nothing;git diff --name-only <base> -- src/ ':(exclude)*.py'prints nothing;grep -rn "__doc__" src/rise_eoprints nothing.
The PR title becomes the release-note line. The maintainer labels and merges (merge commit).
- Specs and ADRs: copy
specs/TEMPLATE.md/docs/adr/TEMPLATE.md. ADR numbers are sequential; if two PRs pick the same number, the one that merges second renumbers (CI fails on duplicates). - Keep the living docs true in the same PR (
README.md,ARCHITECTURE.md,CONTEXT.md,LIMITATIONS.md,ROADMAP.md,docs/). Specs, ADRs and run-books are point-in-time. TODO #NNin old text = GitHub issue #NN. File deferred work as an issue.- Docs removed in spec 102 (the progress log and its archive, the changelog, recipes, the dropped,
bug and TODO lists, finished run-books) are readable at tag
docs-archive-2026. - Call a MGRS tile (the ~110 km source granule) and a grid cell (the ~5 km ROI subdivision) by those
names; never a bare "tile". See
CONTEXT.md.
Each line points at the incident that taught it.
- Real run, not just green tests. Synthetic fixtures encode today's assumptions (spec 50: a real run
found stale
input.csvrows adopted and a ~3600-call serial blob sweep after two review rounds). - Verify the primitive a spec cites. A docstring or our own issue is not evidence (#74: "no
.parthere" missed thatfs.transferwas already atomic). - Test the serialization boundary. Shard CSVs and
input.csvretype values ("05.00"becomes5.0); test the round trip, not the in-memory rows. - A new verb kwarg is forwarded on every runner. A default hides a dropped kwarg (
19b5ad8: the AML download droppedcollection=, so an S1 request discovered S2 granules); add a forwarding test per hop. - Address per unit path. Never hash a set; watch control files written once per run (spec 58 D13).
- Doc call sites rot silently. pytest never runs a notebook cell;
scripts/docs_kwarg_sweep.pydoes (four deadscl_mask_classes=call sites after spec 58 P1). - Prior art. If the mechanism is homemade, does the PR say what standard practice it replaced?
- Spec vs. code. Was the spec checked against rise-eo's own code, not only against its sources?
- A lesson becomes a check. If review caught a class of bug, did the PR add a test or guard for the class?
-
Merge with a merge commit once the four gates hold; apply one label (
breaking,feature,fix,docs,internal). After merging, fast-forward localmainand delete the branch and worktree. -
Branch protection (Settings → Branches →
main): require a pull request, require theteststatus check, no required approvals (authors cannot approve their own PRs), merge commits only, no direct pushes. Once the repo is in thenasaharvestorg and two or more maintainers are active, add.github/CODEOWNERSwith one line naming the maintainers team (* @<org>/<maintainers-team>), require 1 approval and turn on "Require review from Code Owners". Gate 3 is then that approval (spec 102 A10.3). -
Release. Tag
v0.y.zwhen there is something worth shipping;breakingbumpsy, everything else bumpsz. Only the maintainer edits thepyproject.tomlversion, the one place it is written (rise_eo.__version__reads it from the installed package). First merge a PR that sets the new version. Then, on an up-to-datemain, create the release with the tag read frompyproject.toml. The check stops a missed bump:gh release createwould otherwise publish a release on an old tag that has none (v0.1.0is one).v="v$(python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')" if git ls-remote --exit-code --tags origin "refs/tags/$v" >/dev/null; then echo "$v exists: bump pyproject.toml first" else gh release create "$v" --target main --generate-notes; fi
.github/release.ymlgroups the notes by label. CI runs on the new tag and fails if tag and version differ. -
Weekly CI run (Mondays, on
main): a failure opens an issue. Fix it by pinning a range inpyproject.tomlwith a comment saying why. GitHub disables scheduled runs after 60 days without repository activity. -
Shared cloud aliases (
current,champion,demo-*) move only frommain, and only the maintainer moves them. Teammates with Azure access use a personal namespace (dev-<user>). -
Sweep for private identifiers before pushing prose about a real run (it has leaked four times). The CI guard catches the usual shapes; this catches your own concrete values. Keep them one per line in a file outside the repo, then scan only tracked files:
while read -r v; do git ls-files -z | xargs -0 grep -lF "$v"; done < <your-values-file>. Replace a hit with a placeholder (st<proj>) and describe the value, never spell it, even when writing up the leak. A pushed leak stays in history unless it is rewritten. -
Order of work: one pinned issue (#125) holds the order; each piece of work keeps its own state in its PR description or issue.
- Transfer the repo to the
nasaharvestorg (needs repo-create permission there; old links redirect, and the redirect is lost if a repo is ever created at the old name). - Branch protection and CI move with the repo; check the weekly failure still opens an issue.
- With two or more maintainers, add
.github/CODEOWNERSand turn on the code-owner approval ("Maintainer tasks", branch protection). - The successor gets their own
riseaccess through the platform admin; shared aliases and blob roots are indocs/reference/AZURE_INFRA.md. - Hand private values (resource group, workspace names) over privately; never commit them.
- Dry run: a fresh agent session or the successor takes a
good first issueto a green PR using only these docs. Every question it has to ask becomes a doc fix.