Development
Repo layout
Section titled “Repo layout”This repo holds two Go modules, joined by a go.work file so they build
and test together but stay independently deployable. See Server →
Control plane for why they’re split.
main.go CLI: check, baseline, serve, migrate, hook, hash, license, login, logout, versioninternal/ check/ the deterministic check engine (colors, fonts, terms), plus per-format extraction for .pptx/.docx ruleset/ YAML ruleset schema, loader, hot-reloading registry verdict/ shared Verdict/Violation types, the core contract baseline/ accepted-debt baselines (content-identity matching) config/ operational config resolution (flags > env > file) checker/ the port between adapters and the core: Local | Remote githook/ git pre-commit adapter (staged blobs) claudehook/ Claude Code PostToolUse adapter codexhook/ Codex PostToolUse adapter pptx/ .pptx (zip-of-XML) parsing: slide text, theme, media docx/ .docx (zip-of-XML) parsing: body text, theme, media assetmeta/ in-content/document-property metadata markers imagehash/ perceptual + exact image hashing for outdated logos tone/ LLM tone-judge Provider implementations (Anthropic) oidcverify/ shared ID-token verification (dashboard SSO, /v1/check) clilogin/ stylist login's OIDC device-code flow credentials/ persisted personal login credential (~/.stylist/…) server/ core API HTTP server + auth middleware store/ check store: portable SQL, SQLite/Postgres, migrations license/ ed25519 license keys dashboard/ server-rendered dashboard UI (go:embed templates)controlplane/ separate module: admin UI for org creation, API token issuance/revocation, and ruleset publishing, see server.mdtestdata/ fixture ruleset + good/bad HTML used across suitesdocs/ this documentationDependency direction: adapters and surfaces depend on checker,
verdict, config; the core (check, ruleset) depends on nothing
above it. All harness-specific code stays in its adapter package.
controlplane depends on the shared Postgres schema only, never on
internal/ packages as a Go import. The two modules are coupled by
schema, not by shared library code.
Conventions
Section titled “Conventions”- TDD. Every feature lands as failing specs first, then the
implementation. Tests live next to the code (
*_test.go, same package, unexported access); file fixtures go intestdata/. - Fail-open is a product invariant for hooks: violations block,
infrastructure failures warn and allow (unless
fail_open: false). - Secrets are env-only. Never add a config-file key for a
credential; there is a test asserting
api_token:in YAML is ignored. - Single binary. New surfaces are subcommands; assets are
go:embeded; dependencies that break CGO-free static builds are off the table.
Common tasks
Section titled “Common tasks”make test # go test ./...make e2e # agent-seam scenarios: real claude/codex CLIs vs scripted fake modelsmake vet # go vet ./...make build # dist/stylist with version stampedmake release # cross-compiled static binariesmake docker # distroless server imageRun the store suite against a real Postgres:
docker run -d --name pg -e POSTGRES_PASSWORD=test -p 5433:5432 postgres:16-alpineSTYLIST_TEST_POSTGRES_DSN='postgres://postgres:test@127.0.0.1:5433/postgres?sslmode=disable' \ go test ./internal/store/The e2e agent scenarios (make e2e) need the claude and/or codex
CLIs on PATH; each scenario skips cleanly when its CLI is missing, and
STYLIST_E2E_AGENTS=1 promotes the skip to a failure for CI runners
that promise to have them. Design notes and the per-CLI findings live
in documents/e2e-agent-harness.md (last green: claude 2.1.206,
codex 0.144.1).
Adding a deterministic check
Section titled “Adding a deterministic check”- Spec it in
internal/check/check_test.go. - Implement the
Checkinterface (Name,Enabled,Run) in a new file ininternal/check/; register it indeterministicChecks. - Extend the ruleset schema in
internal/ruleset/ruleset.goif the check needs configuration. - Give violations a stable
Snippet: it is the identity baselines match on.
Adding an adapter
Section titled “Adding an adapter”Adapters are deliberately thin: gather (asset name, content) pairs,
call checker.FromConfig(...), report verdicts, honor fail-open. Use
internal/githook (≈100 lines) as the template. Rule knowledge never
belongs in an adapter.