Skip to content

jasonmorganson/harness-engineering-rules

Repository files navigation

Harness engineering rules

This package turns the mechanical parts of OpenAI's "Harness engineering" write-up into ast-grep lint rules.

Domain layer order: Types → Config → Repo → Service → Runtime → UI

Enforcing architecture and taste

Documentation alone doesn't keep a fully agent-generated codebase coherent. By enforcing invariants, not micromanaging implementations, we let agents ship fast without undermining the foundation. For example, we require Codex to parse data shapes at the boundary, but are not prescriptive on how that happens. The model seems to like Zod, but we didn't specify that specific library.

Agents are most effective in environments with strict boundaries and predictable structure, so we built the application around a rigid architectural model. Each business domain is divided into a fixed set of layers, with strictly validated dependency directions and a limited set of permissible edges. These constraints are enforced mechanically via custom linters (Codex-generated, of course!) and structural tests.

The diagram below shows the rule: within each business domain (e.g. App Settings), code can only depend “forward” through a fixed set of layers (Types → Config → Repo → Service → Runtime → UI). Cross-cutting concerns (auth, connectors, telemetry, feature flags) enter through a single explicit interface: Providers. Anything else is disallowed and enforced mechanically.

Layered domain architecture with explicit cross-cutting boundaries

Layered domain architecture with explicit cross-cutting boundaries

Rules

The rules focus on invariants that are realistic for ast-grep to enforce:

Principle Rules
Strict domain layers harness-no-forward-layer-import-from-*
Cross-cutting services enter through Providers harness-cross-cutting-through-providers-*
Parse data shapes at boundaries harness-no-raw-boundary-json-*, harness-no-direct-request-body-field-*
Structured logging harness-no-console-logging-*
Schema/type naming conventions harness-zod-schema-name-suffix-*
Platform reliability through explicit config harness-no-direct-process-env-*
Garbage collection of ad-hoc helpers harness-no-local-concurrency-primitive-*, harness-no-p-limit-import-*

The rules are written for TypeScript and JavaScript packages. JavaScript path-sensitive rules cover .js, .jsx, .mjs, and .cjs.

Run

npm install
npm run scan

To scan only the included examples:

npm run scan:fixtures

The fixture scan is expected to fail because tests/fixtures/packages/domains/billing/** contains intentionally bad examples. Test fixtures live under tests/fixtures so the package root mirrors the repository layout being enforced.

Adaptation Points

The architecture rules assume a Turborepo-style package workspace with domain code under packages/domains:

apps/<app>/**
packages/domains/<domain>/types/**
packages/domains/<domain>/config/**
packages/domains/<domain>/repo/**
packages/domains/<domain>/service/**
packages/domains/<domain>/runtime/**
packages/domains/<domain>/ui/**
packages/domains/<domain>/providers/**
packages/shared*/**
packages/tooling*/**

Layer imports are allowed to point toward more foundational layers only: Types → Config → Repo → Service → Runtime → UI.

The rules intentionally target packages/**; app-specific apps/** checks can be added separately if an app needs its own architecture policy. If your repository uses different names, update the files globs and the source-path regexes in rules/harness/architecture-layers.yml.

Direct process.env reads are allowed in package config modules. TypeScript also keeps common *.config.ts(x) and env.ts(x) carve-outs. JavaScript does not ignore *.config.js or env.js; use a package config module when JS code needs direct environment access.

The data-boundary rules are heuristics. They intentionally flag raw JSON and request body access, then tell the agent how to remediate: parse the external shape at the boundary with a schema or typed SDK and pass typed values inward.

Limits

The article also mentions file size limits, doc freshness, quality grades, and recurring cleanup agents. ast-grep is the right tool for structural code patterns, but those checks need companion scripts because they depend on line counts, timestamps, links, ownership metadata, or generated reports.

About

ast-grep rules for Harness engineering domain architecture and codebase invariants

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors