docs(branding): make branding/design-lab the lab's single home (#3310) - #3437
Merged
Merged
Conversation
The design lab was tracked twice. `branding/design-lab/` (12 files, #1827 + #1935) is the real one: `branding/` is the visual spec, `pages.yml` builds and publishes it, and `quality.yml` runs `lab.test.mjs` out of it in both the self-hosted and the GitHub-hosted lane. `docs/design-lab/` (10 files) was the #1763 snapshot re-added incidentally by the #3182 a11y PR (526eaf5) and had drifted, so anyone editing it was editing a dead fork. It was not a pure duplicate, which is why this is a fold and not just a delete. The `docs/` copy had been maintained after the fact: the 2026-09-27 markdown reconciliation (b2b230c) gave it the redaction notice and the design-notes staleness banner, it carried the redaction of one captured attacker IP (85.14.245.122 -> RFC 5737 203.0.113.122) that `branding/` never received, and it held a colour-research section the canonical README had never had. All of it moves to `branding/`, which is the tree that gets published, so the redaction now lives where it is read. Two files keep the canonical version on purpose. `playground/compare.html`: both copies added a `safePath` guard, the canonical one whitelists the path character by character and keeps the `|| '/'` fallback at every call site, the other resolves against an origin and drops it, so the duplicate had nothing the canonical tree lacked. `README.md`: the canonical one documents `lab.mjs`, `lab.test.mjs` and the read-only gate; the duplicate's last section said it "does not carry the harness itself", which is no longer true of either tree. Ten files removed, none dropped: every one of the duplicate's filenames exists in `branding/design-lab/`, and the ten byte-identical ones plus the two redacted files are asserted in the new gate. The one change of substance is that the redaction moved rather than disappeared, so `branding/` no longer carries the address the `docs/` copy had already replaced. Gate: tests/docs/test_3310_design_lab_dedupe.py, picked up by the existing `pytest tests/docs/` row. Three of its six tests name no path -- they key off basenames that exist nowhere but the lab, so a copy reappearing at `docs/design-lab/`, `branding/docs/` or a new `lab/` fails, not just the one path this issue does. References repointed: the CodeQL `paths-ignore` entry moves to the tree that exists (its reasoning is a property of the lab, not the directory, so dropping it would have broken the CodeQL job and keeping it would have exempted nothing while blessing a re-added fork), the `docs/` record-tree exemption goes with the tree it named, and docs/README.md links the canonical README. No workflow, no action pin and no openapi.json change.
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
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
branding/design-lab/is now the design lab's only home. Thedocs/design-lab/fork is removed, its unique content folded into the canonical tree, and a gate makes the duplication structurally impossible to reintroduce.Canonical tree:
branding/design-lab/— not merely because it has more files, but because three independent things depend on it:quality.yml:177,194runnode --test branding/design-lab/lab.test.mjsin both the self-hosted and the GitHub-hosted lane (design-lab-readonly,design-lab-readonly-cloud)pages.ymltriggers onbranding/**and builds it withbranding/scripts/build_pages_site.pylab.mjs+lab.test.mjs(#1828/#1935), which the fork never hadThe fork was the dead one: the #1763 snapshot, re-added incidentally by the #3182 a11y PR (
526eaf5c), frozen since 2026-08-24 while the canonical tree moved to 2026-08-25.The issue's characterisation was wrong in two places — verified, not assumed
1.
playground/compare.htmlis not "slightly newer" in the fork. That is its commit date (2026-09-14 vs 2026-08-24), not its content. Content-wise the canonical file is the later, stronger fix. The #1763 snapshot (f6614521) has nosafePathat all — both copies added one:branding/: character-by-character whitelist, explicitly drops@,\, quote and angle characters, and keeps|| '/'at all four call sites. The comment names CodeQL as the reason.docs/: resolves againstnew URL(p, ORIGIN + '/')and keeps only a matching origin, with no|| '/'fallback at the call sites.So the fork had nothing the canonical tree lacked. Dropped, not moved.
2. The fork was not purely stale — it was edited, and it carried the safer copy. The 2026-09-27 markdown reconciliation (
b2b230cd, this repo's HEAD) gave thedocs/copy contentbranding/never received:README.mddesign-notes.md"Public, redacted copy" banner, including the note that the Go dashboard these findings cite was deleted in Port Foundation: production cutover checklist #162885.14.245.122→ RFC 5737203.0.113.122(27 occurrences inplayground/elements.html, one indesign-notes.md)A blind
git rm -r docs/design-labwould have deleted all four and left the unredacted captured address in the treepages.ymlpublishes. That is why this is a fold.Proof the fold is lossless for the two pure-redaction files — after the fold both are byte-identical to the fork:
and the
elements.htmldiff is purely the address swap — every one of its 25 changed lines contains85.14.245.or203.0.113.and nothing else.Nothing lost — full enumeration
10 files deleted. Every one exists in
branding/design-lab/:docs/design-lab/README.mdlab.mjs/lab.test.mjs/the read-only gate, which this one lacked) and gained the redaction notice + Colour research. Its closing line ("does not carry the harness itself; run it frombranding/design-lab/") was dropped as false of either tree.docs/design-lab/contrast_scan.jsdocs/design-lab/design-notes.mddocs/design-lab/gen_palettes.pydocs/design-lab/palettes.cssdocs/design-lab/playground/compare.htmldocs/design-lab/playground/elements.htmldocs/design-lab/playground/index.htmldocs/design-lab/playground/layouts.htmldocs/design-lab/v5-picks-override.css8 of the 12 canonical files were already byte-identical; after the fold 10 of 12 are, and the two that differ (
lab.mjs,lab.test.mjs) never existed in the fork.The gate
tests/docs/test_3310_design_lab_dedupe.py— six tests, no new CI wiring (the existingpytest tests/docs/row atquality.yml:1633picks it up). Stdlib + pytest only; the CodeQL assertion reads YAML as text, the same tradetest_3331_fix.pymakes.Three of the six name no path. They key off five basenames (
design-notes.md,gen_palettes.py,contrast_scan.js,palettes.css,v5-picks-override.css) that exist nowhere in the repository except the lab, so a copy reappearing atdocs/design-lab/,branding/docs/, a newlab/, or a merge under a new name fails too. A gate that only asserteddocs/design-labis absent would go green the moment the fork came back one directory over.RED, before the fix — 5 failed, 1 passed, each for the right reason:
GREEN, after — 6 passed.
Zero remaining references
The gate is strict enough that my own first draft of the explanatory prose failed it — the CodeQL comment, the
check-docs-reachable.pydocstring and two READMEs all named the path in order to explain the history. Rather than add a prose allowlist, those four now carry provenance by issue number (#3310), which is this repo's existing convention for exactly this (everypaths-ignoreentry carries a#NNNNcomment). That keeps the gate maximally strict: it would also catch the path reappearing in a link.References repointed
.github/codeql/codeql-config.ymlpaths-ignoreentrydocs/design-lab→branding/design-labscripts/check-docs-reachable.pydocs/design-lab/record-tree exemption (a dead exemption exempts nothing, and is one more place a re-added fork could hide)docs/README.mddesign-lab/removed from the subdirectory list; links the canonical README insteadOn the CodeQL exclusion — this is a repoint of an existing entry, not a new allowlist, and it is not a zizmor finding. The exclusion's stated reasoning is a property of the lab, not of the directory:
playground/compare.htmlis a local-only harness, not shipped and not served by any route. Those files are still there. Dropping the entry would have broken the CodeQL job; keeping the old path would have exempted nothing while silently blessing a re-added duplicate. No new GitHub action was added, so no new pin was needed.Issues
Refs #3310. Not merging.
Security impact
.envfiles were added. — Net effect is the reverse: the unredacted captured attacker address (85.14.245.122, 27 occurrences) is removed frombranding/design-lab/playground/elements.htmland one fromdesign-notes.md, replacing it with RFC 5737203.0.113.122, because that is the copypages.ymlpublishes.branding/design-lab/lab.mjsis untouched, so the read-only harness (BACKEND_URLgate,BACKEND_MOUNTED_URLstub) is unchanged;node --test branding/design-lab/lab.test.mjsstill passes 8/8. No container, mount or route is touched.playground/compare.htmlkeeps the canonical strictersafePath; no listener, no route, no compose file is modified.The redaction is deliberately left as partial as it was:
123.188.73.228,213.176.26.114,45.156.87.34,172.98.32.65and172.110.223.159are still literal inplayground/elements.html, andlab.mjs/playground/index.htmlstill name thehomeserverand10.8.0.2. The original redaction covered one captured address; widening it is a separate privacy decision, not a dedupe, so it is flagged rather than done.Validation
No existing test was weakened, skipped or deleted. The two
design-labmentions inscripts/tests/test_ci_lane_summary.pyand the one intests/docs/test_3331_fix.pyare CI job names (design-lab-readonly), not paths, and are correctly untouched.Not validated:
scripts/tests/test_compose_drift_watch_sweep.pyhas 2 failures — pre-existing, confirmed by running them on a cleanHEADworktree with none of these changes. They are out of scope here and untouched. CodeQL and the hosted CI lanes themselves were not executed (no runner available locally); the CodeQL config change is a one-line path repoint with the reasoning preserved in place.Rollout
None. Documentation tree only; nothing is deployed by merging, and no service reads either path.
What I did not do
docs/design-lab/. A directory holding one file is still a directory, and one more thing to keep in sync. Git history plus the#3310references are the breadcrumb.lab.mjs/lab.test.mjs/quality.yml/pages.yml— the canonical harness and its CI wiring are unchanged, which is the point.openapi.json— untouched, and nothing here affects the OpenAPI contract.design-notes.mdnow readshomeserver xore@<homeserver>, which is clumsy. It is a preserved record whose own banner says "Nothing else was edited", so silently rewriting a redaction placeholder inside it is the wrong kind of edit. Flagging it here instead — say the word and it is a one-line follow-up.