fix(install): dogfood Prettier on the shipped surface — GH #531 - #540
Merged
Conversation
…ant — GH #531 Prettier (adopted for the shipped surface in the companion #531 commit) re-aligns markdown table columns — padding each cell to its column width. Principle 19's two table assertions matched exact single-space padding ('| ALIAS | DISPATCH ...' and '| DIRECT |'), so a prettier-formatted pipeline/SKILL.md broke them. Collapse intra-line whitespace runs before matching: column padding is cosmetic and NOT part of the invariant. The principle still verifies the SAME thing — the header columns + all 6 ALIAS rows are present. This changes HOW it matches (whitespace- tolerant), not WHAT it validates; intent preserved. Separate atomic commit per the Artifact Ownership Contract (packages/core/principles/ is meta-tests-CI-owned; cross-owner edits land as their own commit with rationale).
Right after install, a consumer's first `npm run validate` failed on `format:check`
(`prettier --check .`) on ~50 files — every one installer-shipped, none consumer-
original. The framework had no `format` script and never ran its own authored skill
docs / agent prompts / ESLint-rule sources / config templates through Prettier, so it
shipped unformatted artefacts. "One button -> green validate" was red out-of-box.
Fix (scope: the SHIPPED surface only — NOT the framework's own README/CLAUDE/
.claude/rules/docs, which are authority-owned and out of the consumer surface):
- Run prettier --write over the shipped sources (skills, agents, ESLint-rule
sources, config templates) — formatted with the framework's existing style.
- Ship .prettierrc.json ({ singleQuote: true }) so the consumer's prettier --check
uses the same style the artefacts are written in (default double-quote would re-flag
every .ts/.mjs/.cjs). copy_safe never clobbers a consumer's own config.
- Exclude GENERATED install artefacts (.claude/settings.json, the eslint-rules-local
index.ts barrel) in the shipped .prettierignore — they have no stable authored
source, same class as the already-ignored rendered RULES.md. Authored docs are
formatted, not hidden.
- Fix the stryker packageManager patch to swap the VALUE in place instead of a full
JSON.stringify re-serialize (which re-expanded prettier-collapsed arrays and re-broke
the consumer).
- hooks/package.json template -> multi-line (Prettier formats package.json with the
json-stringify parser, which forces multi-line).
- Add a language to 4 pre-existing bare code fences in the ARCHITECTURE templates
(MD040, surfaced by the pre-commit markdownlint gate when re-committing them).
Dogfood gate so it can't regress: scripts/format-shipped.sh (--check/--write) over the
shipped surface, wired as npm run format / format:check and a shipped-prettier
audit-self CI job. Deterministic (Prettier, not an LLM) — complies with no-paid-llm-in-ci.
Verified: fresh ts-server AND react-next consumers are prettier --check . clean (0
issues); framework format:check green; install-sh suite 32/32; principles 188/188.
Regression: tests/install-sh/gh-531-shipped-prettier.test.sh.
Prior-art: skipped — bug fix + dogfood gate, no new capability (no new dependency; the gate is bash + npx prettier, no code module >=80 LOC under packages/).
artyhoo
added a commit
that referenced
this pull request
Jun 15, 2026
…merge — GH #531 (reopen) (#543) * fix(install): pin prettier@3.8.3 exact on both shipped + dogfood sides — GH #531 (reopen) RC#1: prettier floated on both surfaces — install.sh CORE_DEVDEPS listed bare `prettier`, format-shipped.sh used `npx --yes prettier` (latest). Prettier ships formatting changes in minor/patch, so a consumer's format:check was non-deterministic across re-installs. Pin BOTH to prettier@3.8.3 exact (the version the shipped surface is already clean under, post-#540). The format-shipped npx-with-pinned-version is the deterministic path for the shipped-prettier CI job (no npm install there). Prior-art: skipped — bug fix to existing #531 prettier surface, no new capability * fix(install): non-destructive .prettierignore merge + drift-guard + CI wiring — GH #531 (reopen) RC#2: install.sh used copy_safe (skip-if-exists) for .prettierignore, so a BROWNFIELD consumer with a pre-existing .prettierignore never got the AIF exclusions → generated .ai-factory/RULES.md (+ RULES.react-next.md, .claude/settings.json, eslint-rules-local/ index.ts) stayed un-ignored → `prettier --check .` re-broke on the non-format-stable RULES.md table. Fix: merge_prettierignore appends a marker-delimited block of missing AIF entries (dedup, idempotent on re-install); greenfield path stays byte-identical; --force overwrites wholesale as before. Plain bash, no yq, no new dependency. Also: drift-guard asserting the two pin sites agree on the exact version (project invariants 2+4), paired-negative tests that genuinely flip, and wires gh-531 into audit-self.yml (it was never in CI — closes the latent gap that #540's test never ran). Prior-art: skipped — bug fix to existing #531 prettier surface, no new capability
5 tasks
artyhoo
added a commit
that referenced
this pull request
Jun 16, 2026
…te — GH #531 audit follow-up (#545) The #531 audit found 4 install-sh test files that exist but were never invoked by CI (armed-but-not-fired): gh-534-arch-boundaries, gh-535-rule-enforced, c1-wiring, ship-orchestration-skills. A test wired at NO channel is a false-green — it fails at none, contradicting the project's "fail at the earliest reachable channel" thesis. Same class as #540 shipping gh-531's test unwired (fixed in #543). - audit-self.yml: wire the 4 tests as deterministic bash steps (all pass locally; no paid-LLM — compliant with no-paid-llm-in-ci.md). - tests/install-sh/meta-all-wired.test.sh (new): a meta-gate asserting EVERY tests/install-sh/*.test.sh is wired in audit-self.yml, so this omission cannot reland silently. Non-vacuous NEG (removing a wiring from a workflow copy flips the gate). Wired itself (33/33 test files now invoked in CI). Prior-art: skipped — CI wiring + a deterministic meta-gate test, no new capability.
artyhoo
added a commit
that referenced
this pull request
Aug 10, 2026
… cannot drift again (#1362) `.github/workflows/audit-self.yml` defines 31 top-level jobs; `ci-success` needed 28. Excluding itself, exactly two were missing — `shipped-prettier` (:99) and `framework-fresh-install-validate-multistack` (:1094). Both trigger on `pull_request` and neither carries an `if:` guard, so both run on every PR. Branch protection on staging requires only `ci-success`, `fidelity-verdict-in-pr-body` and `stale-revert-in-pr-diff`, so neither was separately required: either could go RED while `ci-success` was green and the PR merged. `git log -S` on each needs-entry returns empty — neither was EVER wired, from the day the jobs shipped (#540, #815). Both were green on the latest staging run (31371037396), re-confirmed before pushing, so wiring them in does not deadlock merges. The two lines are not the deliverable. `scripts/ci-success-gate.sh:6-7` asserted in its own header that audit-self.yml «`needs:` every audit-self PR job» — false, and detected by nothing: the script only judges the RESULTS of jobs already listed (it consumes `join(needs.*.result, ' ')`). The gaps are the predicted output of a check resting on someone reading the workflow, which attention-is-not-a-mechanism.md §1 rejects as a detection layer. `launch-preannounce-track/kickoff.md:46` had already ordered this exact fix once — «immediately, one line, before anything else» — and it never happened, then drifted further. A fix without a mechanism regressed once already. So the mechanism ships with it: `packages/core/principles/36-ci-needs-completeness.test.ts` asserts every job defined in audit-self.yml appears in `ci-success.needs`, with an explicit allowlist carrying a per-entry justification. The allowlist has exactly one member — `ci-success` itself, which cannot depend on itself. No job is push-only: the sole `if:`-guarded job (`pr-commit-trailers`) is wired in, and `skipped` already counts as passing at ci-success-gate.sh:30, so an `if:` guard is not grounds for exclusion. Channel (rule-enforcement-channel-selection.md §3): mechanically detectable → gate, not reminder. A principle test is the earliest channel that actually fires — the principles suite runs at pre-push (pre-push.ts:1267) AND in CI (audit-self.yml:210), so CI stays the backstop rather than the only line. Deliberately NOT placed inside ci-success-gate.sh: that script is the body of the very job under test, would run in CI only, and would conflate judging job results with asserting the job graph. Paired-negative, both directions, exercised against the real file — not a synthetic fixture (destination-environment-verification.md §4 `#contract-that-cannot-fail`): arm (c) deletes a real `needs:` entry from the real text and expects RED; arm (d) seeds a new job definition and expects RED; arm (f) renames the aggregate and proves the parser returns null instead of passing vacuously on an empty list. Confirmed live by seeding each mutation on disk and running the gate: RED naming the exact job both times, GREEN after restore. Suite 7/7; full principles suite 38 files / 360 tests green. The ci-success-gate.sh header now states its real scope — it judges results, never the wiring — and names principle 36 as what keeps the aggregate claim true. §1.7: Forward-check — deterministic node/vitest, zero API-billed calls (no-paid-llm-in-ci.md §1); BFR consult recorded as SSOT #244 with a BUILD verdict; capability commit carries this trailer plus `Prior-art:` below; touches no maintainer-owned artefact (.claude/rules/** and .husky/** unedited per the CLAUDE.md Artifact Ownership Contract). Backward-check — delegated cold to agents/backward-sweep-auditor.md on the change's class only (registry completeness assumed rather than asserted), never the diff; two sibling surfaces verified by hand and judged SEPARATE, with evidence, in the PR body: packages/core/hooks/pre-push.ts has zero consumer-matrix sections against the claim at .claude/orchestrator-prompts/launch-preannounce-track/kickoff.md:46, and tests/consumer-matrix/python-unfamiliar-stack-cell.sh:39 claims it runs under `make consumer-matrix` while Makefile:34-38 invokes only pnpm-monorepo-cell.sh and npm-tarball-cell.sh. Prior-art: prior-art-evaluations.md#244 (actionlint RuleJobNeeds / zizmor / marketplace aggregate actions, verdict BUILD — upstream validates the `needs:` edges that ARE written, or aggregates results at runtime; none asserts statically that the list mirrors the job population. DeepWiki on rhysd/actionlint: «no built-in rule to enforce that every job defined in the workflow must be depended on by another job»). Co-authored-by: Test <test@example.com>
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.
Fixes #531
Problem
Right after
install.sh, a consumer's firstnpm run validatefails onformat:check(prettier --check .) on ~50 files — every one installer-shipped, none consumer-original. The framework had noformatscript and never ran its own authored skill docs / agent prompts / ESLint-rule sources / config templates through Prettier, so it delivered unformatted artefacts. The headline "one button → green validate" was red out-of-box through no fault of the consumer's code.Fix — dogfood Prettier on the shipped surface
Scope: the shipped artefacts only — NOT the framework's own README / CLAUDE /
.claude/rules//docs/meta-factory(authority-owned, out of the consumer surface; reformatting them would be a huge unrelated diff touching frozen docs).prettier --writeover the shipped sources (skills, agents, ESLint-rule sources, config templates), in the framework's existing style..prettierrc.json({ singleQuote: true }) so the consumer'sprettier --checkuses the same style the artefacts are written in — prettier's double-quote default would otherwise re-flag every shipped.ts/.mjs/.cjs.copy_safenever clobbers a consumer's own config..claude/settings.json, theeslint-rules-local/index.tsbarrel) in the shipped.prettierignore— they have no stable authored source (same class as the already-ignored renderedRULES.md). Authored docs are formatted, not hidden (a regression test asserts skill docs are not blanket-ignored).packageManagerpatch to swap the value in place instead of a fullJSON.stringifyre-serialize (which re-expanded prettier-collapsed arrays and re-broke the consumer).hooks/package.jsontemplate → multi-line (Prettier formatspackage.jsonwith the json-stringify parser, which forces multi-line).Regression guards (dogfood — so it can't return)
scripts/format-shipped.sh(--check/--write) over the shipped surface, wired asnpm run format/format:check+ ashipped-prettieraudit-selfCI job. Deterministic (Prettier, not an LLM) — complies with no-paid-llm-in-ci.tests/install-sh/gh-531-shipped-prettier.test.sh(deterministic core + an end-to-end arm).Verification
Fresh ts-server and react-next consumers are
prettier --check .clean (0 issues); frameworkformat:checkgreen; install-sh suite 32/32; principles 188/188.Note on the second commit
packages/core/principles/19had two §2.5-table assertions that matched exact column padding, which Prettier re-aligns. Made them whitespace-tolerant (same content checked) — a separate atomic commit per the Artifact Ownership Contract (principles are meta-tests-CI-owned).§1.7 Forward-check applied
Complies with active disciplines, verified against source: no-paid-llm-in-ci — the gate is deterministic Prettier via
npx, no API-billed LLM (scripts/format-shipped.sh:58; CI step at.github/workflows/audit-self.yml:116); build-first-reuse-default — REUSE of Prettier (already a shipped consumer dev-dep), zero new framework dependency, the only new code is a bash glob-list helper underscripts/(notpackages/), so not a capability commit (escape-hatch trailer); doc-authority — new.sh/.jsonare code/config (no prose header); dual-implementation — single deterministic gate, no dual-channel. The consumer config is shipped non-destructively atinstall.sh:525(copy_safe, skip-if-exists).§1.7 Backward-check applied
Swept all surfaces under the new gate's scope (the shipped artefact set): formatted every shipped skill/agent/template/rule-source, verified both consumer stacks green end-to-end, and confirmed the GENERATED-vs-AUTHORED split is principled — only generated artefacts are excluded (
packages/core/templates/shared/.prettierignore:12), authored docs are formatted (paired-negative in the test asserts skill docs are not blanket-ignored). The stryker mutation surface that also writes a shipped config was fixed to preserve formatting (install.sh:274). The one meta-test that asserted exact table padding was made format-tolerant (packages/core/principles/19-meta-orchestrator-alias-routing-consistency.test.ts:84, separate commit). Verified no existing test regressed (install-sh 32/32, principles 188/188) and the framework's authority docs were deliberately left out of scope (no README/CLAUDE/rules churn).