@tractiontactics/tt-fidelity
v0.2.3
Published
Section-first visual fidelity: hybrid SSIM+pixel gate + computed-style explainer for TT Platform prototype→site builds.
Maintainers
Readme
@tractiontactics/tt-fidelity
Section-first visual fidelity for prototype → TT WP Theme builds.
Pixel / perceptual section screenshots are the acceptance gate. Computed styles explain what to change. Exit 0 means every compared section is under the clean threshold — not that an agent believed the page matched.
npm i -D playwright && npx playwright install chromium
npx @tractiontactics/tt-fidelity \
--proto "https://prototype.example.com/page" \
--draft "https://site.example.com/?p=12&preview=true" \
--viewports 1280,768,390 \
--out ./fidelity \
--mode full \
--scope contentDefault --scope is content (page body; excludes TT header/footer rows). Use --scope chrome for header/footer, --scope full only when you need both.
Monorepo:
node tools/fidelity-diff/fidelity-diff.mjs --proto … --draft … --out ./fidelity
npm run fidelity:test # self-testsPublishing to npm
Do not use a 2FA-bypass granular access token (deprecated / blocked for publish).
First publish (creates the package). npm requires account 2FA or a granular token that can publish — a plain login session is not enough.
- Enable 2FA on your npm account (if not already):
https://www.npmjs.com/settings/~/account/security → Authenticator app
(ornpm profile enable-2fa auth-and-writesand follow prompts) - Create a Granular Access Token (not classic):
https://www.npmjs.com/settings/~/tokens → Generate new token → Granular- Permissions: Read and write
- Packages / scopes:
@tractiontactics(or “All packages” for the org) - Bypass 2FA: leave off if your account 2FA works with
--otp; turn on only for a one-shot first publish if interactive OTP still fails (token is being deprecated for publish ~2027 — replace with Trusted Publishing after)
- Point npm at that token and publish with a real 6-digit authenticator code (not the placeholder
XXXXXX):
cd tools/fidelity-diff
npm config set //registry.npmjs.org/:_authToken=npm_YOUR_TOKEN_HERE
npm whoami
npm publish --access public --otp=123456 # real code from your authenticatorLater releases: prefer GitHub Actions trusted publishing (OIDC).
- On npmjs.com → package → Settings → Trusted Publisher
- GitHub repo:
tractiontactics/tt-wp-platform - Workflow filename:
publish-tt-fidelity.yml
- GitHub repo:
- Bump
versionin thispackage.json, commit, tag, push:
Workflow:git tag tt-fidelity-v0.2.2 git push origin tt-fidelity-v0.2.2.github/workflows/publish-tt-fidelity.yml— noNPM_TOKEN.
Until the package is published, agents should use the monorepo path above.
Exit codes
| Code | Meaning |
| --- | --- |
| 0 | Pixel gate clean (or styles clean in --mode styles only) |
| 1 | Differences remain — continue the loop |
| 2 | Could not run / low landmark coverage |
| 3 | --max-rounds reached with diffs still present |
Modes
| Mode | Exit 0 from | Styles |
| --- | --- | --- |
| full (default) | Section pixel scores | Explainer + work plan enrichment |
| pixel | Section pixel scores | Skipped |
| styles | Computed-style diffs (triage only) | Authoritative for this mode only |
Do not treat --mode styles exit 0 as visual parity. Gradients, borders via background-image, and inline SVG are invisible to a property census.
Severity bands (pixel score = differing pixels / area)
| Band | Score | Meaning | | --- | --- | --- | | clean | < 0.10 | Pass | | fix | 0.10–0.40 | Fix settings / tokens | | rebuild | ≥ 0.40 | Rebuild section |
Override fail cut with --threshold (default 0.10).
CLI
| Flag | Purpose |
| --- | --- |
| --proto / --draft | Single URL pair |
| --pages <file> | JSON [{id,proto,draft}] or proto\|draft lines |
| --out <dir> | fidelity.json, QUEUE.md, shots/sections/… |
| --mode full\|pixel\|styles | See above |
| --viewports | Default 1280,768,390 |
| --scope | Default content. chrome = header/footer. full = both (may skew TT section counts). |
| --cache-bust / --no-cache-bust | Default on (tt_nocache=); logs cache headers |
| --concurrency <n> | Parallel pages |
| --only-failing | Skip sections clean in prior fidelity.json — finish with a full pass before exit 0 |
| --roles | Landmark map when defaults miss |
| --round / --max-rounds | Agent loop |
| --json | Extra copy of the document |
| Auth / cookies | --auth, --draft-auth, --cookie |
Programmatic API
import { runFidelity } from '@tractiontactics/tt-fidelity';
const result = await runFidelity({
proto: 'https://…',
draft: 'https://…',
viewports: [1280, 390],
mode: 'full',
outDir: './fidelity'
});
// result.exitCode, result.summary, result.pages, result.clusters, result.document, result.textSchema: fidelity.schema.json (schema_version: 2).
How measurement works
- Load both URLs at the same viewport (cache-busted by default).
- Prepare for capture (0.2.3+): full-page scroll (scroll-reveal / IntersectionObserver), eager lazy images, unlock common
opacity:0reveal classes, hide sticky tip/chat overlays. - Discover sections: draft prefers
.tt-pb-row/data-tt-row; prototype prefersmain > section(orbody > section). - Align by document order; count mismatch → structural task.
- Screenshot each pair (scroll section into view again), normalize canvas,
pixelmatch→ score + band. - Optionally capture landmark computed styles (explainer).
- Cluster failing headings across
--pages→QUEUE.mdleverage list. - Emit TT ids (
data-tt-row/data-tt-block) when present forPUT /pages/{id}/layout.
If prototype shots look like blank cream with only a sticky widget, you are on a pre-0.2.3 build — upgrade. Broken/404 media on the prototype itself will still appear empty.
Agent contract
See AGENTS.md. Short version:
- Run the checker; paste
QUEUE.md/ JSON — do not self-author “100% parity.” - Exit 0 = pixel gate. Styles alone ≠ done.
- One WORK PLAN task → re-measure → increment
--round. - Never apply a finding site-wide until
instances_per_pagejustifies it. - Trust
--cache-bust(default on).
Platform docs: fidelity-measurement.md, prototype-to-site.md.
Theme hooks (0.15.3+)
Builders emit additive data-tt-row / data-tt-block with layout JSON ids (stable even when advanced.custom_id overrides HTML id). Paint unchanged.
