Retrospective: M4 (GitHub repository and project infrastructure)#
Status: complete (2026-09-27) on the branch m4-infra, which ran in parallel with M1b on
main and is merged by the orchestrator. Steps 1-2 each appended their section; Step 3
(finish) hardened the workflows, made the workflow rules permanent checks, ran the final
verification and added the milestone summary: what was built, the acceptance record, the
lessons and the list of author actions. An independent review then found 29 problems; the
last section, “Review fixes”, records how each was fixed or why it was deferred.
Step 1: community files, templates and repository settings (2026-09-27)#
Done#
Community files (PLAN Section 10.1):
CODE_OF_CONDUCT.mdre-checked against the official Contributor Covenant 3.0 text (downloaded from contributor-covenant.org; only the two placeholders differ: the reporting contact sm.shovan@gmail.com and the removed enforcement note);SECURITY.mdwith supported versions, private vulnerability reporting, what to include, and the disclosure process (acknowledgement within 7 days, a fix in a private advisory fork, coordinated disclosure within 90 days, credit);SUPPORT.md;MAINTAINERS.md(new); a completeCONTRIBUTING.md(where to start, setup, presets, test labels and the local gate, parity, performance, style and headers, Doxygen, commit conventions, DCO, the pull-request process with the review rules of Section 8.9, adding or porting an algorithm).GitHub metadata: eight issue forms (
bug,feature,new_algorithm,port_research_code,parity_regression,api_change,performance_regression,docs) andconfig.yml(no blank issues; links to Discussions, private vulnerability reporting, the docs and the Code of Conduct);PULL_REQUEST_TEMPLATE.mdwith the checklist of Section 8.9;CODEOWNERS(@SMShovanowns everything; core block and a per-algorithm block);.github/dco.yml;.github/labels.yml(46 labels) and thelabelsworkflow that applies it;docs/developer/labels.md.Validation: the issue forms and
config.ymlvalidate against the SchemaStore schemas (github-issue-forms.json,github-issue-config.json, checked once withjsonschemain a scratch virtual environment; negative mutations are rejected).ci/github_meta_check.py(a new pre-commit hook, run with--self-test) checks what the schema does not: unique ids and labels per form, dropdown options (no reserved “None”, no unquoted yes/no), every label a form applies exists inlabels.yml, the label file itself, CODEOWNERS syntax and paths, and the PR template’s checklist.actionlint1.7.12 passes on every workflow; the two actions oflabels.ymlare pinned by SHA and the SHAs were checked withgit ls-remote.Read the Docs:
.readthedocs.yaml(not connected; checkpoint A4). It validates against the Read the Docs v2 schema, and itspre_buildcommands (configure-onlycpu-onlypreset, thenci/docs.shfor the Doxygen XML) were run in a fresh clone.Repository settings guide:
docs/developer/repository_settings.md, fourteen account-level steps with click paths (organization 2FA, About, features, squash merges, Actions permissions, security, the DCO app, labels, themainruleset with the exact required check names, a release-tag ruleset, environment protections, Discussions, and later Read the Docs and Zenodo).
Deviations from the plan (pragmatic choices, same intent)#
Plan |
What was done |
Why |
|---|---|---|
Section 8.9: “DCO sign-off on every commit” |
sign-off required from external contributors; members of |
the M4 brief asks for DCO sign-off for external contributors; the maintainers’ commits already carry their identity and a |
Section 10.2 label |
|
GitHub lists issues with exactly these two names on the repository’s |
Section 10.1 issue forms |
also |
the M4 acceptance criteria ask for them |
no label workflow in Section 8.8 |
|
otherwise the author would have to create 46 labels by hand; the file stays the single source of truth |
Section 4.2 lists “three scripts” of tooling |
one more checker, |
GitHub validates issue forms, CODEOWNERS and labels only after a push; a broken form silently falls back to a blank issue |
Section 8.9 rule 6: the CODEOWNERS algorithm block is generated from the manifests |
written by hand, marked as the block |
|
Section 9.2 (Read the Docs installs the docs requirements) |
|
the M4 brief asks for the same pinned tool versions as |
Notes for the next steps#
Docs site (Step 2):
.readthedocs.yamlexpectsdocs/conf.pyand the Doxygen XML inbuild/doxygen/xml(whatci/docs.shwrites, aftercmake --preset cpu-only -DDYNG_BUILD_TESTS=OFF ...); keepdocs.ymland.readthedocs.yamlin step. Add the documented site-build command toCONTRIBUTING.md(“Documentation and Doxygen”). The new pagesdocs/developer/labels.mdanddocs/developer/repository_settings.mdbelong in the Developer section of the toctree; they use no in-page anchor links (MyST would needmyst_heading_anchors).Required checks: when
docs.ymlexists, put its job names into the table ofdocs/developer/repository_settings.md(step 9), which now says “every job of the workflow”. Any action from a new owner must be added to the Actions allow list (step 5).Roadmap:
CONTRIBUTING.mdand thenew_algorithmform linkdocs/roadmap.md, which Step 3 writes.ci/github_meta_check.pyhasREQUIRED_FORMSandREQUIRED_LABELS; update them when a form or a plan label is added or renamed.
Step 2: documentation site (2026-09-27)#
Done#
Sphinx site (PLAN Sections 9.1-9.6, D14):
docs/conf.pywith MyST Markdown, the pydata-sphinx-theme, Breathe over the Doxygen XML, sphinx-copybutton, sphinx-design and sphinxcontrib-bibtex;nitpickyreferences and warnings as errors. Diataxis sections: getting started (install and build, a first update in C++), tutorials (dynamic shortest paths on MOSP files, quotingexamples/cpp/sssp_update.cppby text anchors), how-to guides (choose a backend, profile an update, read and write files, run parity, build the docs), explanation (the update model: the thesis Chapter 3 template, G1-G10, the template card, the two families, the four challenges, a workedssspexample; backends and resources; results and versions), reference (the algorithm catalogue, the curated C++ API with onedoxygengrouppage per Doxygen group, the file formats), developer (the plan, contributing, writing documentation, the parity guide included fromparity/README.md, labels, repository settings, the name reservation, the retrospectives), the ADRs (docs/adr/index.mdincludes the README’s table; the ADRs are globbed), roadmap, how to cite (the bibliography rendered fromdocs/references.bib, the BibTeX andCITATION.cff), changelog (included fromCHANGELOG.md) and a glossary. A landing page with the tagline, the algorithm table (ssspnow, the others marked planned with their release), install and build, a first update, and “How to cite”. Every existing page (algorithms/sssp.md,api/file_formats.md, the ADRs, the retrospectives,labels.md,repository_settings.md,pypi_name_reservation.md) is part of the site.docs/_ext/dyng_breathe.py, a small local extension for three Breathe limitations found while making the reference build warning-free: (1) on group pages Breathe declares free functions, enumerations and aliases in the global scope (to_vector, notdyng::to_vector), so 134 references did not resolve; the extension qualifies them with Doxygen’s<qualifiedname>; (2) Doxygen links@ref ssspand header names to group and file pages that a group-based reference does not have; they are resolved to the page that renders the group (a header: its@ingroupgroup), and a Doxygen group without a page is a warning, so a new group cannot go missing; (3) Doxygen 1.18 emitsconstexprtwice for constexpr constructors, which Sphinx cannot parse.ci/docs.shnow builds everything: Doxygen with warnings as errors andci/doxygen_coverage.py(unchanged),sphinx-build -W --keep-going -n -Eintobuild/docs/html, and thelinkcheckbuilder for internal links (documents, anchors, local files; external links only withDYNG_LINKCHECK_EXTERNAL=1).--doxygen-onlykeeps the old behaviour forlint.yml, the CMake targetdocs-doxygenand the Read the Docs pre-build;--no-linkcheckskips step 3. It removes stale Doxygen XML before each run..github/workflows/docs.yml: jobsiteon pushes tomain, pull requests and on demand (micromamba installsenvironment.yml, so the versions are those of the local environment and of.readthedocs.yaml; configure-onlycpu-onlypreset;ci/docs.sh; the site uploaded as the artifactdocs-html); jobexternal-linksweekly and on demand (DYNG_LINKCHECK_EXTERNAL=1), not a required check.permissions: contents: read,persist-credentials: false, actions pinned by SHA.environment.ymlpins Sphinx 8.2.3, myst-parser 5.1.0, linkify-it-py 2.2.0, pydata-sphinx-theme 0.22.0, Breathe 5.0.0, sphinx-copybutton 0.5.2, sphinx-design 0.7.0 and sphinxcontrib-bibtex 2.7.0; thedyng-devenvironment was updated and the conda cache cleaned.docs/developer/plan.md(the short roadmap, from PLAN Sections 0, 1, 11, 12 and Appendix E, with the M1a re-estimate) anddocs/roadmap.md(the public roadmap, PLAN Section 10.5).Follow-ups of Step 1: the site-build command in
CONTRIBUTING.md; thedocs.ymlrequired check (site) and the micromamba action in the Actions allow list ofdocs/developer/repository_settings.md;labels.mdandrepository_settings.mdin the Developer toctree.
Deviations from the plan (pragmatic choices, same intent)#
Plan |
What was done |
Why |
|---|---|---|
the newest Sphinx |
Sphinx 8.2.3 (not 9.1) |
Breathe 5.0.0, the newest, requires Sphinx < 9 (the conda solver refused 9.1) |
Section 9.2 lists |
not installed yet |
there are no notebooks before M5/M6; it is added with the first notebook |
Section 9.2 lists no bibliography or linkify extension |
|
“How to cite” renders |
Breathe renders the Doxygen XML as is |
|
without it the curated group pages (Section 9.2) cannot build with |
every reference resolves |
|
no external inventory is loaded (the build must not need the network); |
|
|
Doxygen’s |
|
|
M1b was building and timing with the same environment at the same time; |
|
runs |
Read the Docs builds the Sphinx site itself; the link check stays in |
docs.yml: “pinned pip packages + a pinned Doxygen binary” or micromamba |
micromamba with |
one source of truth for every version; the environment is cached between runs |
existing files untouched |
two comment edits: |
the comments now say the same in words that parse |
|
grouped three |
actionlint with shellcheck (SC2129) flagged it; the M4 criterion asks for every workflow to pass actionlint |
Validation#
Check |
Result |
|---|---|
|
Doxygen + coverage OK (96 compounds); Sphinx |
negative tests (a page edited, then restored) |
a link to a missing page, an unknown |
the link check found a real defect |
Doxygen had turned |
fresh environment from |
created in 28 s; Sphinx 8.2.3, Breathe 5.0.0, myst-parser 5.1.0, pydata-sphinx-theme 0.22.0, Doxygen 1.18.0; configure-only preset + |
|
all checks passed in 74 s: clang-format, |
code on the pages |
the “first update in C++” program compiled against the fresh build prints |
actionlint 1.7.12 with shellcheck (temporary venv) |
all five workflows pass (after the |
action pins |
every |
|
pass |
machine-specific paths |
none in the diff |
Notes for the next steps#
Merging M1b: every new page under
docs/must be in a toctree (ADRs, retrospectives and algorithm pages are globbed); a new Doxygen group needs adocs/api/cpp/<group>.mdpage (the build warns); new ADRs still need a row indocs/adr/README.md. After the merge, runci/docs.shonce: M1b’s new public headers (CUDA streams, memory resources) are the first real test of the Breathe workaround beyond the M1a headers.docs/getting_started/*,how_to/choose_a_backend.mdand the landing page say that CUDA arrives in M1b; update them when it has.The algorithm table exists three times (README, landing page,
algorithms/index.md) untilscripts/regen.pygenerates it from the manifests (M3).M5 adds the Python API (sphinx-autoapi over the committed stubs; pin it in
environment.yml),myst-nbwith the first notebook, the CLI page and the history pages.
Open items for the author#
Add
mamba-org/setup-micromambato the Actions allow list andsiteto the required checks (both indocs/developer/repository_settings.md, steps 5 and 9) oncedocs.ymlhas run on a pull request.Read the Docs remains untested end to end (checkpoint A4): whether its conda build of
environment.ymlfits the time limit, and whether it adds its own Sphinx next to the pins.
Step 3: finish (2026-09-27)#
Done#
actionlint 1.7.12 with shellcheck 0.11.0 (
actionlint-pyandshellcheck-pyin a temporary virtual environment) passes on all five workflows. zizmor 1.30.1, a security auditor for workflows, was run as a second opinion and found what actionlint does not check:six
actions/checkoutsteps (cpu.yml, the four jobs oflint.yml,release.yml) left the job token in.git/config(auditartipacked); all now setpersist-credentials: false, asdocs.ymlandlabels.ymlalready did;release.ymlexpanded two step outputs (project_dir,version) inside shell scripts (template-injection; the values are computed from the tag, so this was hardening, not an exploitable hole); they now reach the scripts throughenv:, and the smoke-test virtual environment moved from/tmptoRUNNER_TEMP;dependabot.ymlproposed a new action release as soon as it appeared (dependabot-cooldown); it now waits 7 days.
The two per-job permission grants of
labels.ymlgained comments saying why they are needed. With the default persona zizmor reports nothing; the remaining pedantic notes are listed under deviations.Least privilege, checked: every workflow has a top-level
permissions: contents: read; the only write permissions areissues: writein thesyncjob oflabels.ymlandid-token: write(Trusted Publishing) in the two publish jobs ofrelease.yml, which get nocontentspermission at all. No workflow usespull_request_targetor secrets.Pins verified: all eight actions are pinned to a full SHA with a
# vX.Y.Zcomment, and each SHA equals the commit of its tag bygit ls-remote(annotated tags peeled). Each pin is also the newest release tag of its action today:Action
Tag
SHA
actions/cachev6.1.0
55cc8345863c7cc4c66a329aec7e433d2d1c52a9actions/checkoutv7.0.1
3d3c42e5aac5ba805825da76410c181273ba90b1actions/download-artifactv8.0.1
3e5f45b2cfb9172054b4087a40e8e0b5a5461e7cactions/setup-pythonv7.0.0
5fda3b95a4ea91299a34e894583c3862153e4b97actions/upload-artifactv7.0.1
043fb46d1a93c77aae656e7c1c64a875d1fc6a0acrazy-max/ghaction-github-labelerv6.0.0
548a7c3603594ec17c819e1239f281a3b801ab4dmamba-org/setup-micromambav3.2.1
f457c30a868e4760d3a6fcea5f25dc655b8edf39pypa/gh-action-pypi-publishv1.14.2
dc37677b2e1c63e2034f94d8a5b11f265b73ba33The checks are permanent now, not a one-off manual run:
ci/github_meta_check.pychecks every workflow: a top-levelpermissionsblock grantingreadat most, nopull_request_target, every remote action pinned to a full SHA with a version comment,persist-credentials: falseon every checkout, and no${{ }}expression in arun:script exceptmatrix.*,env.*andrunner.*. Its self-test gained nine workflow mutations, each of which must be rejected; run on the pre-fix workflows it reports exactly the eight findings above.--verify-pinsrepeats thegit ls-remotecomparison (network, so not part of pre-commit).pre-commit runs
actionlint(with shellcheck) andzizmor --offlineon the workflows, soci/check.shand thepre-commitjob oflint.ymlenforce them. A deliberately broken workflow (an unquoted variable) was rejected by the hook with shellcheck’s SC2086, which shows that shellcheck is found inside the hook environment.CONTRIBUTING.md(Style) states the workflow rules and the--verify-pinscommand.
Final verification (all under
flock -s ... nice -n 10, GPU 1 visible only):a fresh clone of
m4-infraatb060762passesci/check.shin 73 s: clang-format,cpu-only196/196,dev202/202, clang-tidy, REUSE, provenance (63 files), harness, docs (Doxygen coverage of 96 compounds, the Sphinx site with-W -n, the internal link check) and pre-commit with the new hooks;reuse lint: 1067/1067 files compliant (REUSE 3.3);trial merge: in a scratch clone,
main(M1b at5eb1b61) merged intom4-infrawithout conflicts (CHANGELOG.mdauto-merged), and the merged tree passes the wholeci/check.sh(221 CPU tests in thedevpreset; the docs build picks up ADR 0015 and the M1b retrospective through the globs). This is only a forecast: M1b is still committing;the final retrospective commit was checked again with
ci/docs.shand pre-commit.
CHANGELOG: one additive
Unreleasedbullet (workflow hardening).
Deviations from the plan (pragmatic choices, same intent)#
Plan |
What was done |
Why |
|---|---|---|
Section 8.7/8.8 hooks: pre-commit, REUSE, regen check |
two more pre-commit hooks, |
the M4 criterion asks that every workflow passes actionlint; a hook keeps it true after M4. zizmor found six credential-persisting checkouts that actionlint does not look for |
Section 8.8: “never trigger on |
|
no workflow needs it today; if |
Section 10.1: |
GitHub Actions only, plus a 7-day cooldown |
there is no pip manifest with dependencies yet (the name-reservation package has none); add the |
zizmor pedantic: |
not changed |
a job’s name is its required-check name ( |
zizmor pedantic: |
allowed (the checker allows |
matrix values are fixed in the workflow file, not attacker-controlled; keeping |
Lessons#
Turn each manual validation into a check. Steps 1 and 2 validated the issue forms, the pins and actionlint by hand; the pins and actionlint are now hooks or a script option, so they stay true after the next workflow edit. The self-test’s rule “every mutation must be rejected” also guards the test itself: a mutation that no longer applies (the fixture text moved) leaves the input unchanged, is accepted, and fails the self-test.
Use two linters for workflows. actionlint checks syntax, types and shell; zizmor checks security properties (credentials, injection, permissions, supply chain). Each found things the other does not.
Required checks couple to job names. Renaming a job, or giving it a
name:, silently turns a required check into one that never reports, which blocks every pull request. The settings guide lists the exact names; keep it in the same pull request as any rename.Trial-merge a parallel branch early. Merging
maininto a scratch clone and running the full gate costs two minutes and turns “the merge may break the docs build” into evidence.
Milestone summary#
What M4 built#
Community files:
CODE_OF_CONDUCT.md(Contributor Covenant 3.0, contact sm.shovan@gmail.com),SECURITY.md,SUPPORT.md,MAINTAINERS.md, a completeCONTRIBUTING.md(now with the workflow rules).GitHub metadata: eight issue forms and the chooser, the pull request template with the Section 8.9 checklist,
CODEOWNERS,dco.yml,labels.yml(46 labels, including the Section 10.2 categories) with thelabelsworkflow anddocs/developer/labels.md,dependabot.ymlwith a cooldown.The documentation site (Sphinx, MyST, pydata-sphinx-theme, Breathe; Diataxis sections; the short plan and the public roadmap),
ci/docs.sh,docs.yml,.readthedocs.yaml(not connected), and the repository settings guide.Checks:
ci/github_meta_check.py(issue forms, labels, CODEOWNERS, PR template, workflows;--verify-pins), theactionlintandzizmorhooks, and the hardened workflows.
Acceptance record#
# |
Criterion |
Evidence |
Status |
|---|---|---|---|
1 |
a fresh clone passes |
Step 3 final verification (fresh clone at |
met |
2 |
community files, issue forms (bug, feature, new algorithm, performance regression, docs, + config.yml), PR template, CODEOWNERS ( |
Step 1; SchemaStore validation of the forms; |
met (rendering on GitHub not yet seen) |
3 |
|
Step 2 ( |
met locally; not yet run on GitHub |
4 |
|
Steps 1-2 ( |
met (Read the Docs not connected: checkpoint A4) |
5 |
|
Step 1, updated in Step 2 ( |
met |
6 |
|
|
met for the M4 diff; defaults documented |
Open items#
Nothing of M4 has run on GitHub yet; all workflow, form, label and CODEOWNERS checks are local (schemas, actionlint, zizmor,
ci/github_meta_check.py).Read the Docs remains untested end to end (checkpoint A4): the time limit for the conda build of
environment.yml, and whether it installs its own Sphinx next to the pins.After merging M1b (a trial merge with
mainat5eb1b61passedci/check.sh): any workflow M1b adds (for examplecuda-build.yml) must satisfy the new checks, that is a read-only top-levelpermissions, SHA pins with version comments,persist-credentials: false, and values passed to scripts throughenv:;pre-commit run --all-filesreports the exact lines. Its jobs also go into the required checks of the settings guide (step 9). The pages that say CUDA arrives in M1b need updating (Step 2 notes).dependabot.ymlgets apipentry with the rootpyproject.toml(M5).The algorithm table exists three times until
scripts/regen.py(M3); CODEOWNERS’ algorithm block is hand-written until then.
Review fixes (2026-09-27)#
Independent reviewers (newcomer, docs quality, governance and legal, workflows) confirmed 29
problems at 7e70b7b; two pairs were duplicates. All are fixed, except the parts listed under
“Deferred”, each with its reason.
Fixed#
Finding |
Fix |
Commit |
|---|---|---|
clang-tidy naming step ran in no workflow, although CONTRIBUTING and |
|
|
|
a step of the |
|
only |
every job has one (cpu 60, lint 15-30, labels 10, release 30, docs 30, welcome 5); |
|
the weekly external-link run and pushes to |
the group includes the event name; only pull-request runs cancel superseded runs (cpu, lint, docs) |
|
the micromamba environment cache was keyed on |
|
|
Doxygen pinned twice by hand (the lint |
the lint job is removed (docs.yml |
|
PLAN 10.2’s first-interaction welcome bot missing and unrecorded (two findings) |
|
|
parity commands failed without the author’s |
|
|
|
renumbered; the pattern accepts backtick and colon fences |
|
step 3 of |
|
|
the weekly external-link job could never pass (admin-only and login-only URLs return 404 to anonymous clients; about 19 minutes under GitHub rate limits) |
|
|
“Edit this page” on the include-only pages opened the stub (two findings) |
an |
|
no gate compiled the C++ on the docs pages; the landing snippet did not compile |
|
|
tutorial: “both CPU backends” but one command; CTest compared only the tree file |
the commands loop over both backends; new test |
|
no figure for the worked |
a committed SVG (no new dependency) of the tree before and after the batch, with the invalidated subtree |
|
“API review checklist” required in four places but did not exist |
|
|
|
written (commit identities and counts per pinned original, placeholder and AI-assisted commits, the basis for publishing, the third-party rule and table; no e-mail addresses or host names); the M0 row corrected |
|
CoC: the only reporting channel was the single moderator (two findings); CoC pointed to a missing GOVERNANCE contact |
an escalation paragraph (reports about the lead maintainer go to GitHub’s own reporting until a second moderator exists; attribution note updated; |
|
GOVERNANCE said every change goes through a PR, but |
“Reviews and merges” states the interim practice and that the route is an open decision; “Open decisions” lists it |
|
O3 (NOTICE institution line and years) never confirmed |
listed as an open decision in GOVERNANCE and the short plan; author action below |
|
DCO fix contradicted “no force-push during review”; remediation commits undocumented; no upstream remote |
CONTRIBUTING: |
|
web-editor commits fail the required DCO check |
settings guide step 7: Require contributors to sign off on web-based commits |
|
DCO summary left out clause (d) |
all four statements, (d) spelled out (name and e-mail kept publicly and indefinitely) |
|
commit-subject style versus PR-title style unexplained |
“Commits” explains that branch commits use |
|
porting guidance would strip MIT/BSD notices; no path for unlicensed code with permission |
CONTRIBUTING (Style, Adding an algorithm) and the port form: outside code keeps its notices ( |
|
script defaults assume the author’s layout, unexplained |
CONTRIBUTING has a table of the locations |
|
newcomer files cite “PLAN Section N” of an unpublished document |
removed from CONTRIBUTING, the PR template, the issue forms, MAINTAINERS and the non-retrospective docs pages (links to CONTRIBUTING or site pages instead); |
|
labels: “exactly one kind”, but the parity form sets two |
“at least one kind”; |
|
roadmap: HIP both “not planned” and “open to contributors” |
one wording in both lists and in the short plan: the maintainers will not build it; a contributor-led backend is welcome through the feature form |
|
Deviations from the plan (pragmatic choices, same intent)#
Plan |
What was done |
Why |
|---|---|---|
Section 8.8: clang-tidy on host TUs in |
a separate |
it needs the pinned clang-tidy of |
Section 8.8: no |
allowed only for a workflow without checkout and |
the greeting must comment on fork pull requests, whose |
Section 9.6: the README quickstarts are extracted and run by |
not yet: the docs quote |
the README’s build and quickstart sections are being changed by M1b at the same time; extraction (and the Python quickstart) lands with M5, when both quickstarts exist |
the linkcheck builder as the internal link check (Step 2) |
|
the builder never checks in-page anchors and ignores URLs; MyST resolution in the |
Deferred (with reasons)#
Neutral defaults for the scratch and toolkit locations.
DYNG_SCRATCHdefaults to~/Projects/dyng-workinscripts/dev_env.sh,ci/check.sh,parity/*.py,parity/*.shandtools/compat/CMakeLists.txt(goldens);parity/references.tomlandparity/export_patches/MOSP-CUDA/build.shname/usr/local/cuda-13.1/bin/nvcc;dev_env.shprefers/usr/local/cuda-13.1;parity/perf_ab.pysetsOMP_NUM_THREADS=28. These are the lead maintainer’s layout (Appendix E records it as the author’s location; the plan’s generic default was~/scratch/dyng-parity). Changing them now would touch the parity files M1b is editing and move the author’s goldens; they are documented in CONTRIBUTING instead. Follow-up for the parity owner after the M1b merge:${XDG_CACHE_HOME:-~/.cache}/dyng(or keep the author’s default) and${CUDACXX:-nvcc}fromPATH/CUDA_HOME.README quickstart extraction (Section 9.6): M5, see the deviation above.
The external-link job end to end. Not run locally again (about 20 minutes against GitHub’s rate limits, and the
blob/mainlinks of files that reachmainonly with this merge would fail until then); first check it with Run workflow after the merge.
Validation#
Check |
Result |
|---|---|
fresh clone of |
all checks passed in 73 s: clang-format; |
hosted |
passes; with a |
|
all nine pins equal their tags (new: |
zizmor on |
reports |
negative tests |
a typo’d |
parity harness from a fresh https clone |
with |
edit links of the include-only pages |
|
trial merge of |
two textual conflicts that already existed before these fixes ( |
Lessons#
A check that cannot fail is not a check. The linkcheck step printed “OK” while checking nothing; reading the builder’s
output.jsoncounts (alluncheckedorignored) would have shown it. Every new gate step now comes with a negative test.“CI runs the same steps” has to be checked step by step. A table of ci/check.sh steps against workflow jobs (now in the header of
ci/check.sh) found the missing tidy job.Grep what the newcomer reads, not only the diff. The machine-specific defaults were in files M4 did not change but sent contributors to.
Acceptance fixes (2026-09-27)#
The independent acceptance check (at 402a467) passed criteria 1-3, 5 and 6 and failed
criterion 4. The second check (at f902800) passed criteria 1 and 3-6 and failed criterion 2
on one sentence of CONTRIBUTING.
Fixed#
Finding |
Fix |
Commit |
|---|---|---|
|
the estimate table carries the retrospective’s rows: 0.1.x 1.5-2 weeks focused / 3-4 calendar, 0.2.0 and 0.3.0 3-4 / 6-8 each, up to 0.3.0 about 12-16 / 22-30 (plan: 32-47) |
|
informational: the weekly external-link job hit GitHub’s rate limit (HTTP 429) and a 404 for a file not yet on |
the external linkcheck ignores |
|
the external check reported a permanent redirect for GitHub’s private-vulnerability-reporting docs page |
the settings guide links the new URL |
|
second acceptance check (at |
the rule states what |
|
cosmetic: the |
rewrapped to 100 columns |
|
Lessons#
A summary must be checked against its source number by number. The short plan reproduced the retrospective’s table only up to the row the plan text was written from; a diff of the two tables would have caught it.
When a rule gets an exception, grep every place that states the rule. The checker and the retrospective were updated for
welcome.yml, the contributor guide was not.