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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 3 additions & 3 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: CodeQL

# Layer 4 of the analysis stack (docs/testing.md § Static analysis) — the only one that sees
# Layer 4 of the analysis stack (docs/reference/testing.md § Static analysis) — the only one that sees
# whole-program taint. It earns its place on one question: we parse six network packet formats
# (ArtNet, DDP, E1.31, WLED audio sync, MQTT, WLED) plus HTTP, doing ~22 memcpy operations on
# data arriving from the LAN, on a device with no MMU and no process isolation. Nothing else in
Expand All @@ -22,13 +22,13 @@ name: CodeQL

on:
# NOT A GATE: no `pull_request` trigger, so it cannot block a merge. The reasoning is
# docs/testing.md § Static analysis, which every analyser here follows. The Security tab
# docs/reference/testing.md § Static analysis, which every analyser here follows. The Security tab
# keeps the open/fixed alert lifecycle, which is the baselining we would otherwise build.
#
# Both branches are listed on purpose. A workflow whose trigger names a branch nobody is
# pushing to runs never, and reports nothing while looking healthy — this job sat idle for
# two commits when the working branch was called `next` instead of `next-iteration`
# (docs/history/lessons.md). `main` keeps it firing once this branch is merged and retired.
# (docs/work/past/lessons.md). `main` keeps it firing once this branch is merged and retired.
push:
branches:
- main
Expand Down
46 changes: 46 additions & 0 deletions .github/workflows/prose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Prose

# Vale over the lines a PR ADDS or changes, with the rules in .vale/styles/projectMM/. Diff-scoped
# on purpose: the tree still holds pre-existing violations, and a contributor is answerable for
# what they wrote, not for inherited prose. An `error` (em-dash, British spelling, "e.g.") fails
# the check; warnings and suggestions land as inline annotations only.
#
# Two things are scheduled to change once the tree is swept clean (`vale docs/` exits 0): this
# job drops `filter_mode` and checks whole files, and moondeck/check/{check_prose,hook_prose}.py
# are deleted, since their only job was the added-lines scope this job then no longer needs.

on:
pull_request:
paths:
- '**.md'
- '.vale.ini'
- '.vale/**'

jobs:
vale:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# Full history: reviewdog needs the base commit to work out which lines a PR added.
# A shallow clone leaves it no base, so every line of every file reads as added.
fetch-depth: 0
# Pinned to the v3.0.0 commit. v2 derived reviewdog's -level from fail_on_error and ignored
# the level input, so annotations could not be limited to errors.
- uses: vale-cli/vale-action@518a9136acc6e6668ce7c00d367051e0941e87ff
with:
# Annotate only the lines this PR touches. This scopes what reviewdog REPORTS, not what
# Vale scans: Vale lints whole files either way.
filter_mode: added
# Never fail. Vale sees the findings carried by prose that predates the standards, so a
# blocking check here would fail every PR whatever the PR changed. check_prose.py is the
# gate that blocks, on added lines alone. Raise this once the sweep clears the tree.
fail_level: none
# Errors only. GitHub caps annotations per run and Vale finds more than that, so the cap
# decides what is shown: spend it on errors rather than suggestions.
level: error
# Writes annotations through the runner log rather than creating a check run. The
# check-run reporters publish their own status, which stays red however the job exits.
reporter: github-pr-annotations
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ jobs:
# tagged pre-release the local pin uses — fixed and signed (not the
# rolling `release-v6.1` branch tag), so CI and local build the exact
# same tree. This tracks the v6.1 line toward GA; re-pin to the `v6.1`
# tag once it ships (a deliberate re-test pass, see docs/building.md).
# tag once it ships (a deliberate re-test pass, see docs/how-to/building.md).
esp_idf_version: v6.1-rc1
# The IDF target follows the firmware-key prefix: esp32s31* → esp32s31
# (checked BEFORE esp32s3 — esp32s31 also startsWith 'esp32s3'),
Expand Down Expand Up @@ -855,7 +855,7 @@ jobs:
# installer staged above under pages/install/ survives (a plain
# --site-dir pages would wipe it — mkdocs cleans its output dir).
# history/ and backlog/ are excluded in mkdocs.yml (internal docs).
uv run moondeck/docs/build_docs.py --site-dir "$RUNNER_TEMP/docs-site"
uv run moondeck/docs/build_docs.py --strict --site-dir "$RUNNER_TEMP/docs-site"
cp -r "$RUNNER_TEMP/docs-site/." pages/
ls -la pages/

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ jobs:
# third-party static init; keep the signal on what we're actually looking for.
#
# RTSan does NOT halt: the render path's known blocking calls are frozen in
# docs/metrics/hotpath-baseline.txt and backlogged as architecture work, so halting would
# docs/reference/metrics/hotpath-baseline.txt and backlogged as architecture work, so halting would
# fail this lane on every run. It reports; the log is the signal.
ASAN_OPTIONS=detect_leaks=0 TSAN_OPTIONS=halt_on_error=1 \
RTSAN_OPTIONS=halt_on_error=0 ./build/san/test/mm_tests
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ __pycache__/

# Generated test inventory pages — built into the site by moondeck/docs/mkdocs_hooks.py
# (the CLI generate_test_docs.py can still write them locally, but they are not committed).
/docs/tests/*.md
/docs/reference/tests/*.md

# Generated per-module technical pages — Doxygen+moxygen from each `.h`, built into
# the site by moondeck/docs/mkdocs_hooks.py (via gen_api.py). Regenerated every build.
Expand Down
56 changes: 56 additions & 0 deletions .vale.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Prose rules, enforced. The rules themselves are stated in docs/documentation-standards.md;
# this is their mechanical half. Run: `vale docs/ CLAUDE.md README.md` or via check_prose.py.
StylesPath = .vale/styles
MinAlertLevel = suggestion

# Defaults first: in Vale a LATER [glob] overrides an earlier one, so the general rule leads and
# the specific pages below it win.
[*.md]
BasedOnStyles = projectMM

# Exempt for the reasons check_prose.py gives: quoted material, generated pages, vendor code.
# All of docs/work/ is exempt: past and future are quoted or inherited material, and a plan in
# present is a working document that ends at its PR rather than a page anyone reads twice.
[docs/work/past/*.md]
BasedOnStyles =
[docs/work/present/*.md]
BasedOnStyles =
[docs/work/future/*.md]
BasedOnStyles =
[docs/friend-repos/*.md]
BasedOnStyles =
[docs/metrics/*.md]
BasedOnStyles =
[docs/tests/*.md]
BasedOnStyles =
[docs/moonmodules/**/moxygen/*.md]
BasedOnStyles =

# CLOSING THE NET. A page the sweep has finished is held to every rule on the whole file: a
# suggestion becomes an error there, so a regression fails the gate instead of annotating a PR.
# Add a page here when `vale <page>` reports nothing; the sweep plan in docs/work/present/ is the
# list. When every page is here, the two prose scripts go and this section becomes the default.
[docs/tutorials/installing-on-linux.md]
BasedOnStyles = projectMM
projectMM.SentenceLength = error
projectMM.Weasel = error
projectMM.NegatedHeading = error
projectMM.SelfReference = error

[docs/coding-standards.md]
BasedOnStyles = projectMM
projectMM.SentenceLength = error
projectMM.Weasel = error
projectMM.NegatedHeading = error
projectMM.SelfReference = error

# The rules page must QUOTE the things it bans (an em-dash, "analyse", "this page", "really"), so
# the four rules whose examples it carries stay off here. The rest are strict.
[docs/documentation-standards.md]
BasedOnStyles = projectMM
projectMM.EmDash = NO
projectMM.Spelling = NO
projectMM.SelfReference = NO
projectMM.Weasel = NO
projectMM.SentenceLength = error
projectMM.NegatedHeading = error
8 changes: 8 additions & 0 deletions .vale/styles/projectMM/EmDash.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# documentation-standards.md: "No em-dashes in prose." A comma, colon, parentheses or a full stop
# instead, whichever the clause actually calls for.
extends: existence
message: "Em-dash: use a comma, colon, parentheses, or a full stop."
level: error
nonword: true
tokens:
- '—'
13 changes: 13 additions & 0 deletions .vale/styles/projectMM/NegatedHeading.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# documentation-standards.md: "Positive form only." A heading that names an absence gives the
# reader no shape at all. Headings only: inside prose a real constraint may legitimately negate.
extends: existence
message: "A heading in negative form: name what the section IS."
level: warning
scope: heading
ignorecase: true
tokens:
- '\bnot\b'
- '\bnever\b'
- '\bno\b'
- '\bwithout\b'
- "\\bdon't\\b"
20 changes: 20 additions & 0 deletions .vale/styles/projectMM/SelfReference.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# documentation-standards.md: "The text never refers to itself." A page that says "this page" is
# the author stepping in front of the content. A reviewer named it as the tell of machine-written
# text: "self referential stuff like mentioning 'this recipe'."
extends: existence
message: "'%s' is the text referring to itself: say the thing instead."
level: warning
ignorecase: true
tokens:
- this page
- this recipe
- this guide
- this document
- this section
- this tutorial
- this article
- the following section
- as described above
- as mentioned above
- as we saw
- in this post
8 changes: 8 additions & 0 deletions .vale/styles/projectMM/SentenceLength.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# documentation-standards.md: "Say it, then stop: about 40 words." Past that a reader skims, and a
# skimmed statement is not followed. A suggestion, since the rule calls itself "about".
extends: occurrence
message: "Sentence runs past 40 words; a skimmed statement is not followed."
level: suggestion
scope: sentence
max: 40
token: '\b\w+\b'
23 changes: 23 additions & 0 deletions .vale/styles/projectMM/Spelling.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# documentation-standards.md: "American English spelling, everywhere." Stems, so one entry covers
# its inflections. Ported from check_prose.py, which this replaces.
extends: substitution
message: "American spelling: use '%s'."
level: error
ignorecase: true
swap:
behaviour: behavior
colour: color
initialis: initializ
optimis: optimiz
recognis: recogniz
analyse: analyze
analysing: analyzing
materialis: materializ
normalis: normaliz
serialis: serializ
cancelled: canceled
modelling: modeling
labelled: labeled
centre: center
licence: license
defence: defense
9 changes: 9 additions & 0 deletions .vale/styles/projectMM/SuchAs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# documentation-standards.md: write "such as", not "e.g." A /// brief ends at the first period, so
# "e.g." truncates the generated summary mid-sentence.
extends: substitution
message: "Write 'such as' rather than '%s': a period ends a /// brief."
level: error
ignorecase: true
swap:
'e\.g\.': such as
'i\.e\.': that is
16 changes: 16 additions & 0 deletions .vale/styles/projectMM/Weasel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# documentation-standards.md: "factual, no nonsense." These words assert emphasis without carrying
# information; a sentence reads the same without them, which is the test.
extends: existence
message: "'%s' adds emphasis, not information."
level: suggestion
ignorecase: true
tokens:
- very
- really
- simply
- just
- basically
- actually
- obviously
- clearly
- of course
Loading
Loading