Newest entry on top.
Tablet tier — split-screen layouts with compact chrome (feature/responsive-foundation, 2.41.0)
Owner reported tablets (820/853/912/1024) fell into desktop mode and looked whacky — header controls ran off-screen, Generate button clipped. Confirmed via 820/1024 screenshots. Decided (via AskUserQuestion) on a tablet tier of 769–1024px with the header collapsing behind the ⋯ menu.
The key idea: split the responsive system into two breakpoints instead of one.
- Compact chrome →
<=1024px: wideneduseCompact()(768→1024, so Insert-menu / inline-Links / Size-cog switch on for tablets) and moved the header-overflow (topbar-responsive.css), composer field-bar wrap (composer-field.css), Prompt/Negative height match (compose-toolbar.css), and submenu bottom-sheets (mobile-sheets.css) to<=1024px. - Layout-collapse stays
<=768px(CSS-only, phones): the Generate palette drawer and Manage master/detail are untouched, so tablets keep the two-pane split. Split the topbar block so the wordmark-hide + tab-tighten stay phone-only (tablets keep them).
Verified desktop resumes at >=1025 (overflow toggle hidden at 1025 + 1280; desktop header inline unchanged). Added 3 tablet E2E specs. Gate green (check:docs, lint, smoke, 260 unit + 313 SPA + 21 E2E). MINOR bump — it's a new responsive tier / milestone.
Mobile links fold into the overflow menu + app version on show (feature/responsive-foundation, 2.40.23)
Two user-requested header/footer items during the live UI review:
- Links inline on mobile. The phone overflow ("⋯") menu's "Links" row opened another separate
bottom sheet — a nested, differently-shaped surface the owner (rightly) called dumb.
LinksMenunow has a compact branch (via a new shareduseCompact()hook, extracted fromDplInsertBar) that folds the project links → legal pages → language picker directly into the overflow menu as inline rows — one panel, no nested trigger/sheet. Desktop keeps the icon-button dropdown byte-for-byte. Dropped the dead mobile.links-pop/.links-scrimsheet rules. - App version shown. Baked the canonical version in at build time (
gui/vite.config.jsdefine: __APP_VERSION__from repo-rootpackage.json;gui/src/lib/version.jsguards it to "dev" under Vitest) and displayed it asv<VERSION>— faint at the right of the desktop footer, and at the foot of the mobile inline links menu (footer is hidden on phones).
Verified with desktop + Pixel-7 screenshots (self-review). Fixed two tests the changes surfaced: the phone
top-bar E2E (assert inline links, not the old .links-trigger) and a stale PromptResult clear-button test
(match the icon button's descriptive aria-label). Gate green: lint + 260 unit + 313 SPA + 18 E2E. Also
committed the earlier Manage editor polish (2.40.22 — compact name/Save, chevron Back button).
Repo-hygiene guardrails so the drift/litter/uncommitted-file problems can't recur (feature/repo-hygiene)
After the flatten + the multi-round cleanup, added mechanical enforcement so the same problems don't silently accumulate again:
- Doc-drift guard —
scripts/check-links.mjs(npm run check:docs) fails on any broken relative Markdown link, so a doc that still points at a renamed/removed file fails the build. Wired intonpm testand the CIcheckjob (new step). Catches exactly the kind ofweb-app.md/expansion.js/listFiles.jsbroken links this session had to hand-fix. - Uncommitted-file guard —
scripts/check-tidy.mjs(npm run check:tidy) fails on untracked non-ignored files. This is the guard that would have caught the 4 fairyfox reports prior sessions left uncommitted. Run before finishing (not in CI — a fresh checkout has none). - Branch-litter fix — found the root cause: the GitHub
delete_branch_on_mergerepo setting wasfalse, so merged PR branches never auto-deleted. Set it totrue. - Codified the rules — new
notes/reference/repo-hygiene.mdrunbook + standing instructions inCLAUDE.md(commit everything incl. fairyfox reports; sweep docs in the same change on any rename/remove; delete spent branches; the new checks in the verify loop) + indexed it in the notes.
Tooling/CI/docs only — no version bump. Verified the checks work (check:docs = 314 links OK;
check:tidy correctly flagged the two new script files as untracked before they were committed).
Follow-up: enabling delete_branch_on_merge surfaced a subtlety — GitHub deletes a merged PR's head
branch, and a dev → main PR's head is dev itself (it got auto-deleted once, recreated by the
back-merge). Fixed it with a deletion-only branch protection on dev (blocks deletion + force-push;
no PR/review/status requirement, so direct pushes are unchanged). Documented the interaction in
repo-hygiene.md. Also wrote a comprehensive fairyfox roundup report
(fairyfox-reports/2026-07-02-shareable-systems-roundup.md) proposing the repo-hygiene guardrails, the
monorepo-flatten methodology, the docs current-vs-historical discipline, and the honest friction/lessons as
candidate hub standards.
Flattened the repo: removed engine-v1-2, engine-v3 → repo root, consolidated stages (2.40.0)
Undid the engine-vN split and cleaned up the tree. Done on feature/flatten-monorepo, in verified
checkpoints:
- Removed
engine-v1-2/— the frozen 2022–2023 CommonJS snapshot (335 files, ~257k lines). It's recoverable from git history and the reference clone atassets/references/og-pre-revival-2023-04-07-241a148/. This also clears its 10 known dependency vulns (the last thing keeping Scorecard's Vulnerabilities check from staying clean). Dropped its entries from the CodeQL + CodeRabbit configs. - Flattened
engine-v3/to the repo root —git mv'dsrc/,data/,gui/,scripts/,tests/and the tooling configs up one level (908 renames, history preserved). Rewrote every hardcodedengine-v3/path: all 6 CI workflows (droppedworking-directory: engine-v3, fixed cache/coverage/artifact paths),netlify.toml,jsdoc.config.json,sonar-project.properties,codecov.yml,.coderabbit.yaml,.github/dependabot.yml,.gitignore(de-duped),build-docs.mjs(root === repoRootnow), andmanageFs.js'sRAW_BASE(mainnow carries the flat layout). - Consolidated live pipeline stages —
cleanup.js+prompt-salt.jswere active stages sitting in a folder labeled "legacy" (src/prompt-modules/); moved them intosrc/core/stages/with the other stages and removed the empty folder. Dropped the deadpromptModuleFilessetting (0 usages, pointed at the removed folder). - Docs — rewrote CLAUDE.md's framing, README's project-layout section,
status.md, thesystems/*andcontext/*banners,reference/deployment.md, and the plans that referencedengine-v3/…; kept the dated session/changelog history intact and annotatedplans/engine-split.mdas reversed. Legal pages unaffected (no data-practice change).
Verified green at each checkpoint: npm run smoke, npm --prefix gui run build (Vite glob over
data/blocks resolves), npm run lint, format:check, the Node suite (260) and the SPA suite
(313).
Follow-up cleanup (same day, feature/post-flatten-cleanup): committed the 4 pending fairyfox
reports (their own commit); rewrote CLAUDE.md's stale src/chdir.js / src/common.js "Critical" bullet
(those files + process.chdir don't exist — that was the removed classic system) to the real path model
(module-relative loaders via import.meta.url; cwd-relative list/preset settings that rely on npm
running from the repo root); and swept the stale web-app/ folder name (renamed to gui/ at 2.7.1) out
of the current-state docs, including three broken web-app.md → gui.md links. Investigated the three
data/process-*.js data-prep scripts — not broken: they double-chdir (import.meta.dirname then
..) to pin cwd to the repo root, so ./data/sources/*.csv resolves correctly. Left in place (they're
build tooling co-located with the data/sources/ CSVs they consume; moving destructive-if-run,
untestable scripts wasn't worth the risk).
Scorecard follow-up — verified the real breakdown, fixed Signed-Releases + Branch-Protection badge
After 2.39.0 shipped, the badge moved 4.2 → 6.2. The public Scorecard JSON API lagged (still served the old 4.2 scan), so instead of guessing I downloaded the scorecard binary (v5.5.0) and ran it against the live repo with an admin token for the authoritative per-check breakdown. Findings:
- Token-Permissions, Pinned-Dependencies, Security-Policy → 10, and CI-Tests → 10 (unlocked by the PR flow). These drove 4.2 → 6.2.
- Vulnerabilities still 0 — engine-v3 (0) and gui (0) are clean; the frozen engine-v1-2/ has 10 vulns and Scorecard scans the whole repo. Owner is removing engine-v1-2 soon, so left as-is.
- Signed-Releases still 0 — I had wrongly said it was satisfied.
attest-build-provenancewrites to the attestations API, which Scorecard's check does not read; it scans release assets for signature files. Fix: added keyless cosignsign-blobtorelease.yml, attaching.sig+.pemas release assets (kept the attestation too). Activates on the next release. - Branch-Protection is really 4/10, not 0 (deletion/force-push blocked, admins enforced, PR + status
checks required; the missing points all need review approvals = solo wall). The CI badge reads 0
because the default token can't read protection. Fix:
scorecard.ymlnow passesrepo_token: ${{ secrets.SCORECARD_TOKEN || github.token }}(safe fallback). Owner must create a repo-admin-read PAT and save it asSCORECARD_TOKENto unlock the 4 on the badge.
Both fixes are CI-only (no version bump). release.yml + scorecard.yml re-validated (YAML parses);
deployment.md corrected. Signing activates on the next release; the branch-protection badge improves
once the PAT secret exists and the next Scorecard run uses it.
Hardened the supply chain — raised the OpenSSF Scorecard from 4.2 (2.39.0)
Owner flagged the README Scorecard badge (4.2) as feeling low. Pulled the live scan from the OpenSSF API and worked the actual failing checks rather than touching the badge. Score was dragged down by, in weight order: Code-Review, Token-Permissions, Pinned-Dependencies, Branch-Protection, Vulnerabilities, Signed-Releases (all high-weight, 0/10), plus Security-Policy, Fuzzing, CII, Contributors.
Executed (owner approved all tiers, incl. moving main to a PR-based flow):
- Token-Permissions — top-level
permissions: contents: readadded toci.yml,codeql.yml,sonar.yml,visual-baselines.yml;release.yml's write dropped to thereleasejob scope (+id-token/attestations: writethere for signing). - Pinned-Dependencies — pinned all 33 Action refs to full commit SHAs (
# vXcomments) via a PowerShell literal-replace pass across the 9 workflow files; verified 0 refs left on a moving tag. Dependabot already covers thegithub-actionsecosystem, so the SHAs stay fresh. - Signed-Releases — added
actions/attest-build-provenance@<sha>torelease.yml(keyless Sigstore SLSA provenance for the tarball + docs zip). Verify:gh attestation verify <file> --repo …. - Branch-Protection — enabled on
mainviagh api PUT …/branches/main/protection: require PR (0 approvals), strict status checks (the two core CI jobs), enforce-admins, block force-push/deletion, require conversation resolution, linear history OFF (so release merge commits are allowed). Read back and confirmed.devleft unprotected. - Security-Policy — added root
SECURITY.md(private reporting →fairy@fairyfox.io). - Vulnerabilities —
npm audit --omit=devwas already 0; the 18 OSV hits were all dev-only from@lhci/cli(tmp/inquirer/external-editor/uuid). Addedoverridestmp@^0.2.7+uuid@^11.1.1→npm install→npm auditnow 0. Confirmed@lhci/cli(0.15.1),uuid.v4, andtmpstill load under the forced majors. - Code-Review — left at 0 on purpose: it needs an approved PR review and GitHub blocks self-approval, so it's unreachable solo. This caps a one-person repo at ~8.
Docs / standards updated in the same change
- CLAUDE.md release step (5) rewritten to the PR-based flow (
gh pr create→gh pr checks --watch→gh pr merge --merge→ ffdevup tomain). notes/reference/git-workflow.md— new "mainis branch-protected" section; PATCH / MINOR-MAJOR / hotfix release commands rewritten to PR merges; branch-model + merging notes updated.notes/reference/deployment.md— new "Supply-chain hardening" subsection; Scorecard, release, and provenance notes updated.- Version bumped 2.38.1 → 2.39.0 (MINOR, feature set); changelog entry added.
Landmine hit
gh api --method PUT … --input - (stdin pipe) failed with "Problems parsing JSON" — PowerShell pipes
strings to a native process's stdin as UTF-16, which gh can't parse. Fix: write the JSON to a UTF-8
(no BOM) temp file and pass --input <file>. Logged in reference/fix-patterns.md.
Follow-ups
- Wrote a hub proposal (
notes/fairyfox-reports/2026-07-02-propose-scorecard-hardening.md) to standardize this hardening across all fairyfox projects — for the owner to carry to the hub (anti-recursion: this session doesn't touch the hub repo). - The badge won't move until
scorecard.ymlre-runs (weekly Monday cron, or the nextmainpush). - Signed-Releases only goes green after the next actual release runs the workflow.