Skip to content

The QA Loop

Syntaxis treats QA as a track of its own, not an afterthought bolted onto the build loop. Four commands cover the cycle:

/test-plan → /test-cases → /automate → /test-run
(strategy) (the cases) (the code) (the cycle)

Syntaxis QA workflow: decide scope once with test-plan, then repeat test-cases, automate, and test-run each cycle, with failures opening findings in the shared ledger

Like the build loop, this diagram splits into a setup step that runs once and a ring that repeats — but here the ring only closes back on itself once you decide to run another cycle, so nothing forces a fixed cadence the way feature-by-feature work does.

01 · /test-plan. Writes syntaxis/qa/test-plan.md before any case gets designed: what’s in scope and explicitly out of it, which test levels apply, which environments and devices matter, the risk-based priority order, and the entry and exit criteria that decide when a cycle counts as done. This is a decision record, not a checklist to fill in — a solo project with one environment says so plainly rather than inventing a staging tier it doesn’t have. Its arrow feeds into 02 on the ring.

02 · /test-cases. Designs cases into syntaxis/qa/cases/<area>.md, each with a durable TC-NN identifier and a Covers field naming the exact requirement it traces to — a case that covers nothing is either missing its requirement or isn’t a case. Cases come from what the spec claims, never from reading the implementation, because that would only prove the code agrees with itself.

03 · /automate. Takes the cases that are stable, deterministic, and repeatable, and turns them into real specs in whatever harness the project already has — web or mobile — one test per case, titled with the case’s ID so a failure points straight back at its specification. It then proves each new test can actually fail, because a test that only ever passes is worse than no test: it turns an unverified path into a false guarantee. A case that needs visual judgement, a real payment, or a permission dialog the harness can’t reach stays manual on purpose, and the report says so rather than pretending.

04 · /test-run. Drawn in rust, matching /check’s color in the build loop, because this is the other diagram’s proof step: it executes the cases — automated and manual alike — against the real running application and writes syntaxis/qa/runs/<date>-<cycle>.md with the exact commit, environment, platform versions, and every case’s result as pass, fail, blocked, or skipped.

The shared ledger — where a failure actually goes

Section titled “The shared ledger — where a failure actually goes”

The panel below the ring, connected to 04 by a dashed line, is not a fifth step; it’s what happens inside /test-run every time a case fails. Every fail opens a finding in syntaxis/context/findings.md — the exact same file /audit writes to from the build loop. That one design choice is why the QA track needs no gate of its own: a P0 or P1 finding from a failed test blocks /complete through the findings gate that already exists, so a code review and a test cycle can never quietly disagree about whether work is ready to ship.

There’s no arrow drawn from /test-run back to /test-cases on the diagram — running another cycle is a decision you make, not a step that fires automatically. In practice that decision is easy: run /test-cases again when new behavior needs cases, /automate again once more of them stabilize, and /test-run for every regression pass, smoke test, or release candidate. /test-plan itself only needs revisiting when the scope changes, which is why its setup box says “run once, or when scope changes.”

/test-plan — decide what testing this project needs

Section titled “/test-plan — decide what testing this project needs”

Writes syntaxis/qa/test-plan.md: scope and out-of-scope, test levels, environments, target devices and browsers, risk-based priority, entry and exit criteria, defect management, tooling, and roles.

It is a decision record, not a template. A solo project with one environment says so plainly rather than inventing a staging tier and a sign-off committee.

The out of scope section carries real weight: it is the record of an accepted risk, not an oversight.

Writes cases to syntaxis/qa/cases/<area>.md with durable TC-NN identifiers.

### TC-014 [P1] manual - Checkout rejects an expired card
**Covers:** feature 7 / done-when 3
**Preconditions:** A signed-in account with one saved expired card.
**Steps:**
1. Open the cart with one item and choose Checkout.
2. Select the saved card ending 4242.
3. Submit the order.
**Expected:** The order is refused, the message names the expiry as the
reason, the cart keeps its item, and no charge is created.
**Platforms:** web, iOS

Two rules do most of the work:

  • Cases derive from requirements, not from the implementation. Writing cases by reading the code only proves the code does what it does.
  • Every case names what it covers. A case that covers nothing is either missing its requirement or is not a case.

See Test Design.

Converts approved cases into specs in the harness the project already has, one test per case, titled with the case ID.

It automates a case only when the behaviour is stable, deterministic, repeated, and observable by the harness. Leaving a case manual is a legitimate outcome, not a failure — visual judgement, real payments, biometrics, and browser permission dialogs stay manual and get said so.

Then it proves each new test can fail. A test that passes against broken behaviour is worse than no test, because it turns an untested path into a false guarantee.

See Web Automation and Mobile Automation.

/test-run — execute a cycle and record it

Section titled “/test-run — execute a cycle and record it”

Runs the cases against a real running application and writes syntaxis/qa/runs/<date>-<cycle>.md with the branch, the exact commit, the environment, the platform versions, and every case’s result.

Result Meaning
pass The expected result was observed in full, including its negative half
fail The expected result was not observed
blocked The case could not be run, and the record names what blocked it
skipped Deliberately not run this cycle, with a reason

Every fail opens a finding. See Defects and Evidence.

/automate writes tests but never installs a runner. Setup is separate and opt-in:

Command Sets up
/tests A stack-native unit test runner
/tests browser A browser and end-to-end harness
/tests mobile A device or simulator harness

See Testing Setup.

They answer different questions and neither replaces the other.

/check /test-run
Scope One work item’s done-when criteria A set of designed cases across areas
Output Evidence for the current spec A cycle record with per-case results
When Inside the build loop, every feature Regression, smoke, release candidate

syntaxis/context/qa-standards.md holds the conventions all four commands read: the identifier scheme, severity and priority definitions, evidence rules, and automation policy. Edit it once to match how your project tests.