Skip to content

Adding QUALITY.md to an Existing Repo: What the CLI Really Does

QUALITY.md is a new open spec for declaring what quality means in a repo. I ran the 0.35.2 CLI: what init changes, what lint catches, what CI costs.

· · 8 min read
Hands measuring a machined metal part with a digital caliper on a workbench

Quick Take

QUALITY.md is an MIT-licensed file format, agent skill and CLI that puts a project's quality model in YAML frontmatter next to AGENTS.md. I installed qualitymd 0.35.2, pointed it at a small package that already had agent instruction files, and recorded what each command changed.

QUALITY.md is an open file format for declaring a project's quality model: which qualities matter (security, testability, whatever you choose), what each one requires, and how an evaluator should check it. It lives in the repo root as YAML frontmatter plus a Markdown body, sits next to AGENTS.md, and comes with a /quality agent skill and a qualitymd CLI. The current release is qualitymd 0.35.2, implementing specification 0.12 (Draft), MIT licensed. I installed it, ran it against a small TypeScript package that already had AGENTS.md and CLAUDE.md, and wrote down what changed.

Quick take: npm i -D quality.md@0.35.2, then npx qualitymd init --no-agent-instructions if you don't want your agent files edited. qualitymd lint exits 1 on errors, so it works as a CI gate in one line. It validates structure only. The untouched template, placeholders included, passes.

One honest limit before anything else. The loop the project is built around (/quality evaluate, /quality improve) drives a coding agent, and I didn't run it. Everything measured below is the deterministic CLI.

What Is QUALITY.md, and How Is It Different From AGENTS.md?

QUALITY.md describes what "good" means for a project and how to check it, while AGENTS.md tells a coding agent how to work: which commands to run, which folders to leave alone. The first is a typed model a tool can validate. The second is free-form prose that nothing checks.

AGENTS.mdQUALITY.md
Contentfree-form instructionstyped model in YAML + context in Markdown
Validated bynothingqualitymd lint, JSON Schema via qualitymd schema
Main unita rule or commanda requirement with an assessment
Statuswidely read by coding agentsspec 0.12 Draft, "early alpha" per its README

The format's author launched it as a Show HN on July 2, 2026, where it collected 29 points and 29 comments. The thread was split. One commenter argued the same content already fits in AGENTS.md or a skill; another asked for even one benchmark showing it improves agent output. Neither got a number back, and the README still doesn't offer one. Is that a dealbreaker? Not for the file itself, which works as plain documentation even if no agent ever opens it.

Pencil resting on a dimensioned technical drawing next to an architect scale ruler
Photo by Sven Mieke on Unsplash

What Does a QUALITY.md Schema Look Like?

A valid model needs three things: a title, a ratingScale ordered from best to worst with a criterion on every level, and at least one of factors, requirements or areas. Here's the smallest honest file I could write for a two-function TypeScript package:

---
title: demo-api
ratingScale:
  - level: target
    title: Target
    description: Good enough to ship.
    criterion: "Satisfies the requirement."
  - level: unacceptable
    title: Unacceptable
    description: Blocks a release.
    criterion: "Does not meet the requirement."
factors:
  type-safety:
    title: Type safety
    description: Public functions are typed so callers and agents get compiler feedback.
    requirements:
      no-implicit-any:
        title: no implicit any in src
        assessment: Run tsc with strict enabled and confirm zero errors under src/.
---

The vocabulary is small. A factor is a quality dimension such as type safety or reliability. A requirement is one expectation you can assess, and an assessment is the instruction for checking it, written inline or as a pointer to an existing runbook or test plan. An area is a narrower part of the project, say one service folder, that deserves its own factors. Names must match ^[A-Za-z0-9](?:[A-Za-z0-9_-]*[A-Za-z0-9])?$, so dots and slashes are out, and root is reserved.

Every element also gets a stable ID: qualitymd model tree printed factor:root::type-safety and requirement:root::no-implicit-any for the file above. How big does a real model get? The project's own QUALITY.md runs to 710 lines, and model tree counts 10 areas (root included), 38 factors and 30 requirements in it. It lints clean.

How Do You Add QUALITY.md to an Existing Repo?

Install the CLI as a pinned dev dependency, run init, and read the diff before committing, because init edits more than one file by default. The pin matters: the README calls the format early alpha, and npm lists 60 releases between June 12 and July 15, 2026.

  1. npm i -D quality.md@0.35.2
  2. npx qualitymd init, adding --no-agent-instructions if your agent files are off limits
  3. npx qualitymd lint QUALITY.md
  4. Replace every placeholder by hand, then commit

On the test package, init wrote a 7.8 KB QUALITY.md and printed Agent instructions: AGENTS.md, CLAUDE.md. Both files got the same two lines appended:

 - Use strict TypeScript.
+
+<!-- Added by qualitymd init. -->
+See [QUALITY.md](QUALITY.md) for this project's quality model.

Harmless, but it touches files your team probably reviews line by line. The --no-agent-instructions flag skips that, and --minimal drops the long guidance comments from the template. A second init refused with QUALITY.md already exists; pass --force to overwrite and exit code 70, so you can't clobber a finished model by accident.

Why Does the Template Pass Lint Before You Write a Word?

I ran qualitymd lint on the freshly generated file, still full of <the system, component, or artifact this model is about> placeholders, and got QUALITY.md is valid. with exit 0.

Correct by the spec, which defines structure and not content. Still a trap. The monorepo behind this site hit the same pattern elsewhere: a bare tsc --noEmit at a root with project references reported success while checking nothing.

A passing QUALITY.md lint proves the YAML has the right shape, not that anyone on the team has agreed what quality means.

Share this Post on X Bluesky

What Does qualitymd lint Catch, and What Won't --fix Repair?

The linter rejects missing required fields and references that don't resolve, and it exits 1 when it finds any error. To see real findings I broke a copy on purpose: dropped one criterion, one assessment and one factor description, then pointed a requirement at a factor that doesn't exist. Output from 0.35.2, verbatim:

warning missing-factor-description: The factor `type-safety` declares no `description`; a description is recommended for each factor. (factors.type-safety.description)
error invalid-assessment: The requirement `no-implicit-any` has no `assessment`; a requirement must declare exactly one non-empty scalar assessment. (factors.type-safety.requirements.no-implicit-any.assessment)
error missing-criterion: A rating level declares no `criterion`; each rating level requires a non-empty criterion. (ratingScale[0].criterion)
error unknown-factor: The requirement `readme-current` references unknown factor `docs`; factor references must resolve within the declaring area. (requirements.readme-current.factors.0)

3 error(s), 1 warning(s).

Three errors, one warning, exit code 1. Every message names the rule ID and the exact model path. Running qualitymd lint --fix on the same file changed zero bytes and still exited 1. The JSON report says why: every finding came back with "fixable": false. Don't expect --fix to behave like eslint --fix; it applies deterministic repairs only, and none of these four qualified. Would an automatic fix even make sense here? Not really. A missing assessment is a decision nobody has made yet.

How Do You Run QUALITY.md Lint in CI?

Add one step that runs qualitymd lint QUALITY.md --json on every pull request, and the non-zero exit fails the job. The project's own CI runs the same lint QUALITY.md --json command against its own model with QUALITYMD_NO_UPDATE_CHECK=1 and NO_COLOR=1 set, so the workflow below copies both variables:

name: quality-model
on: [pull_request]
jobs:
  lint-quality-md:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npx qualitymd lint QUALITY.md --json
        env:
          QUALITYMD_NO_UPDATE_CHECK: "1"
          NO_COLOR: "1"

I ran those commands locally, not inside GitHub Actions, so treat the file as a template rather than a tested workflow. Two things I could measure. Lint itself is quick: a median of 104 ms for the direct binary and 124 ms through the npm launcher, over 30 runs on an Apple M1, and the 710-line upstream model took 104 ms and 131 ms. Process startup dominates, so file size barely registers. The install is the heavy part. The npm package is a tiny launcher, and the real binary arrives as a platform-specific optional dependency: for linux-x64, the 0.35.2 tarball is 36.8 MB and unpacks to 95.2 MB.

Cache ~/.npm (the cache: npm line does it) and the added time per pull request should stay small. Should a CLI you'll mostly use as a linter be a 37 MB download, though? For a project at this stage, it's a fair question.

Octocat figurine standing in front of a laptop showing a blurred GitHub profile page
Photo by Roman Synkevych on Unsplash

Should You Adopt QUALITY.md Yet?

Adopt the file format, and hold off on building process around the tooling. Writing each requirement with an explicit assessment is useful discipline whether or not an agent ever reads it, and it pairs well with the quality gates for AI-generated code you may already run. If your team keeps a quality framework for vibe-coded TypeScript, QUALITY.md gives it a machine-readable home.

The project itself is young. On September 29, 2026 the GitHub repo showed 33 stars and zero forks, and the last commit on main was the 0.35.2 release on July 15. A quiet repo isn't a dead one, but two and a half months without a commit is a reason to pin versions and keep your model small enough to rewrite by hand. Nothing else in your agent setup has to change, whether that's a Claude Code workflow for React or a plain AGENTS.md.

Every CLI behavior described here (exit codes, rule IDs, --fix, the init diff) is asserted in a quality-md-lint code example kept in this site's repository, pinned to quality.md@0.35.2, so a release that changes it fails that check instead of quietly contradicting this page.

Frequently Asked Questions

What is QUALITY.md?
QUALITY.md is an open file format for writing down what quality means for a project. The file has YAML frontmatter holding a quality model (a rating scale, factors such as security or testability, and requirements with an assessment for each) and a Markdown body that explains scope, needs and risks. It ships with a /quality agent skill and a qualitymd CLI. The specification is version 0.12, marked Draft, and the project is MIT licensed.
Does qualitymd init change my existing AGENTS.md?
Yes, by default. In my test with qualitymd 0.35.2, running init in a folder that already had AGENTS.md and CLAUDE.md appended a two-line block to both files: an HTML comment saying the lines were added by qualitymd init, and a sentence pointing agents to QUALITY.md. Pass --no-agent-instructions to skip that. Running init a second time refused to overwrite the existing QUALITY.md and exited with code 70 unless you pass --force.
Can qualitymd lint fail a CI build?
Yes. qualitymd lint exits 0 when the file is valid and 1 when it finds an error, and --json prints a machine-readable report with a ruleId, severity and model path for each finding. Warnings alone do not fail it. The project's own CI runs lint with --json and the QUALITYMD_NO_UPDATE_CHECK and NO_COLOR variables set. Lint only checks structure, so a template full of placeholders still passes.