Harness Intelligence Wiki
Research

Ultracite Agent Lint Feedback Research

Ultracite Agent Lint Feedback Research

Question

How can Harness Intelligence connect consuming agents to the scaffolded Ultracite/Oxlint stack so edited code is fixed while the agent is working, and what would have prevented the Collective Intelligence repository from accumulating more than 50,000 lint diagnostics?

Four readonly lanes covered Ultracite's first-party AI integration, the current Harness hook/projection path, issue 181 plus the Collective Intelligence repair commit, and the highest compatible Effect/Oxlint/Ultracite version tuple. The official Codex hook contract was then checked directly. This report records facts, recommendations, and open decisions. It does not authorize an implementation or a lint-policy change.

Executive conclusion

Ultracite provides four different agent surfaces. Its rules and reusable skill guide generation. Its post-edit hooks run ordinary autofix. Since version 7.10.0, its explicit ultracite fix --codex and --claude mode implements the stronger repair loop that Harness needs: autofix, collect the remaining structured diagnostics, give one file to an agent, re-lint, retry up to three times, and fail if findings remain.

Harness should reuse that protocol, not copy Ultracite's prompts, skill, or hook files wholesale. The current Harness hook already has safer edited-path selection, managed-file exclusions, and multi-provider projection. Its missing link is actionable feedback: JavaScript and TypeScript files receive Oxfmt and a read-only oxlint <path> check, but Oxlint output is discarded and the agent sees only a generic "Auto-lint failed" message.

The 50,000-diagnostic case also needs two distinct controls:

  1. A post-edit loop prevents new debt in files an agent changes.
  2. A deliberate adoption/convergence path inventories and reduces existing repository debt. A hook cannot make an already-red repository clean.

What each lane covered

LaneScopeResult
Ultracite AI surfaceOfficial docs and upstream CLI sourceIdentified rules, skill, hooks, and the version 7.10 agent-assisted fixer as separate capabilities.
Harness hook auditCatalog, hook runner, provider adapters, projection, and docsFound path-scoped formatting and lint detection, but no safe JS autofix or diagnostic handoff.
Collective Intelligence caseIssue 181, commit 1a9927873, and current repository evidenceSeparated Harness routing defects from strict-policy and existing-code debt; the repair reduced diagnostics by 3,264, not 50,000.
Version compatibilityIsolated frozen package install, Effect patch, and live lintProved the highest compatible stable tuple and rejected Oxlint 1.81 with Effect's runtime version guard.

Trusted facts

The 50,000 findings were not one Ultracite failure

Issue 181 records a measured change from 54,451 to 51,187 diagnostics after the lint hierarchy was repaired. The 3,264-diagnostic reduction proves the generated routing defects were material. The remaining 51,187 findings prove the case was not resolved by configuration repair and cannot be attributed to one cause from the available evidence.

The repaired Collective Intelligence commit 1a9927873 did four relevant things:

  • Root and workspace lint commands restored Oxlint nested-config discovery with oxlint . instead of flattening discovery through explicit config routing.
  • Formatting became a separate command instead of being coupled to workspace lint scripts.
  • Frontend typeAware settings became active only when Oxlint runs from the owning workspace directory.
  • The Studio package stopped registering unsupported effecttsgo; the only retained global rule disables were func-names, func-style, and sort-keys.

No retained primary evidence decomposes the remaining findings by Ultracite core, framework preset, Anti-Slop, Effect rules, or existing code defect. That decomposition remains unknown.

Ultracite's Oxlint configuration is deliberately opt-out: it enables broad rule categories at error level and then disables selected rules. Optional JS-plugin and Anti-Slop presets add slower or more opinionated checks and are off by default upstream. Sources: Oxlint provider guide and ultracite@7.10.7 provider source.

Ultracite's four AI surfaces do different jobs

SurfaceWhat it doesImportant limit for Harness
Agent rulesultracite init --agents ... appends repo-local guidance such as AGENTS.md or .claude/CLAUDE.md.Guidance does not execute or verify lint. Copying it would duplicate and enlarge Harness context.
Reusable skill--install-skill or npx skills add haydenbleasel/ultracite teaches detection plus check, fix, and doctor.It is a broad optional baseline, not a diagnostic transport. Repo lint config remains authoritative.
Post-edit hooks--hooks installs provider JSON that runs the package fix script after edits.Upstream does not generate a Codex hook. The generated command has no edited-file argument, so it defaults to project-wide fix; remaining diagnostics are not handed back to the editing agent.
Agent-assisted fixerultracite fix --codex or --claude autofixes, collects remaining diagnostics, delegates one file, re-lints, and retries.It launches a separate authenticated CLI agent. It is an explicit repair harness, not a current-session Codex App hook.

Rules intentionally omit formatter details and leave them to repository config. Sources: Agent Rules and rules.ts. The reusable skill contains SKILL.md plus one detailed standards reference. Sources: Agent Skills and skills/ultracite.

Upstream hooks support Cursor, Windsurf, CodeBuddy, Claude Code, and GitHub Copilot. They invoke the generated package fix script; source inspection shows that the command does not interpolate the edited filename. Sources: Agent Hooks, hooks.ts, agents.ts, and editors.ts.

The agent-assisted fixer is the strongest reusable design

Ultracite 7.10.0 added fix --codex and fix --claude. The flow:

  1. Runs Oxfmt and Oxlint safe fixes for the requested targets.
  2. Parses the remaining Oxlint JSON into file, rule, line, column, message, help, documentation URL, and source-span data.
  3. Groups findings by file and processes files sequentially.
  4. Writes full diagnostics to a temporary JSON file and gives the agent a short one-file prompt.
  5. Prohibits edits outside that file, suppressions, lint-config changes, and unrelated formatting.
  6. Re-lints after the attempt and retries surviving findings up to three times with fresh diagnostics.
  7. Reports newly introduced findings and exits nonzero if anything remains.

Codex runs as codex exec --full-auto in its workspace-write sandbox; Claude uses acceptEdits with read/edit tools and no shell. Each attempt has a five-minute limit. Sources: AI-powered fixing, agent-fix/index.ts, prompt.ts, linters.ts, and agents.ts.

Version 7.10.0 introduced this capability. Harness currently pins Ultracite 7.8.3 and Oxlint 1.78.0 at the repository root, so the pinned CLI does not provide it. Ultracite 7.10.7 peers on Oxlint ^1.79.0. Source: CHANGELOG.md and the published package metadata for 7.8.3, 7.10.0, and 7.10.7.

The highest proven compatible stable tuple stops at Oxlint 1.80

An isolated install and runtime probe proved this tuple:

PackageVersion
@effect/tsgo0.39.0
oxlint1.80.0
oxlint-tsgolint7.0.2001
typescript7.0.2
ultracite7.10.7
oxfmt0.66.0

effect-tsgo patch --oxlint --typescript-package typescript succeeded. A config extending both ultracite/oxlint/core and @effect/tsgo/oxlint-presets/correctness loaded and emitted a structured effecttsgo(floating-effect) diagnostic. Raw Oxlint JSON included file, severity, rule, message, span, and canonical documentation URL.

Oxlint 1.81.0 is newer but unsupported by @effect/tsgo@0.39.0. The patcher rejected its platform binding and listed only 1.79.0 and 1.80.0 as supported. The tuple must therefore pin Oxlint exactly. ultracite init changed the probe dependency to latest, which silently upgraded it to the incompatible 1.81.0 on the next install. Machine-readable hook feedback should call Oxlint directly: ultracite check remained human-readable even when JSON format arguments were passed through.

Harness currently detects failure but hides the evidence

The canonical runner is apps/cli/src/data/hooks/format-edited-file.mjs; provider copies are generated projections. Current JavaScript and TypeScript behavior is:

  • Oxfmt receives the edited content on stdin.
  • Only files under apps/ or packages/ are linted.
  • Oxlint runs as oxlint <edited-path> from the repository root, without --fix or JSON output.
  • Oxlint stdout and stderr are discarded.
  • Codex receives only { "systemMessage": "Auto-lint failed ..." }.
  • Claude receives generic additionalContext; Cursor receives a generic user_message; OpenCode writes a warning log.

Evidence: apps/cli/src/data/hooks/format-edited-file.mjs:23-42, :152-175, :283-322, :656-737, :752-809, :932-1017, and :1087-1137.

Codex projection configures SessionStart fingerprint capture and PostToolUse for Bash, patch, edit, and write-like tools. The adapter describes lifecycle support as degraded. Snapshot mode assumes specific Codex payload fields, but the repository has no integration fixture captured from the Codex App. Bash matches fall back to all pending dirty files because there is no exact edited path. Evidence: apps/cli/src/data/scripts/harness-projection/adapters/codex.mjs:15-49 and :112-135, plus format-edited-file.mjs:915-1007.

The user reports that Codex App hook execution currently requires manual confirmation and is unreliable beyond the session-start check. Current official Codex documentation explains the confirmation: every non-managed hook must be reviewed and trusted against the exact definition hash. New or changed hook definitions are skipped until trusted. This is a product trust boundary; HI can document it but should not present the hook as approval-free.

The same official contract closes the output-shape question. A synchronous PostToolUse hook receives cwd, turn_id, tool_name, tool_use_id, tool_input, and tool_response. It can return:

{
  "decision": "block",
  "reason": "Resolve the remaining scoped lint diagnostics.",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "file, rule, location, message, and repair constraints"
  }
}

Codex then replaces the completed tool result with this feedback and continues the current model from the hook message. This is the native current-session repair seam; HI does not need to start codex exec after every edit. Source: official Codex hooks documentation. The repository still needs captured runtime fixtures and a real Codex App verification because current tests cover projection intent, not actual hook delivery.

Synthesized recommendation

Use one preventive post-edit loop

The desired loop is:

agent edit -> exact changed path -> owning workspace cwd -> format -> safe lint autofix -> JSON lint verification -> structured feedback to the current agent -> agent repair -> re-lint

The owning workspace cwd is part of correctness. Issue 181's repaired frontend configs intentionally enable typeAware only from that cwd. Running every edited path from the repository root cannot exercise those typed settings.

The feedback should be concise in context and complete on disk:

  • Include file, rule, line, column, and message in the provider hook response.
  • Put full help, URL, and spans in one temporary JSON artifact when the payload would be large.
  • Tell the active agent to edit only the affected file, avoid suppressions or config changes, and preserve runtime behavior.
  • Re-run the same scoped lint command before declaring the edit clean.
  • Keep a bounded retry count and surface unresolved or introduced findings.

For Codex, emit that feedback through PostToolUse additionalContext and use decision: "block" only when diagnostics remain. A clean result exits without extra context. The hook's retry budget prevents an unchanged diagnostic from restarting the same continuation indefinitely.

This applies the useful Ultracite protocol without injecting its full rule prose or starting a nested CLI agent after every Codex App edit.

Keep explicit debt convergence separate

ultracite fix --codex <bounded-files> is useful as an explicit migration or repair command once the version tuple is resolved. It is a poor automatic hook for a 50,000-diagnostic repository: files are processed sequentially, each attempt may consume up to five minutes, and it launches a new agent process.

Existing repositories need an adoption decision before a strict preset becomes the default authority. At minimum, scaffold should inventory the candidate policy before mutation and distinguish:

  • findings in newly edited files;
  • findings introduced by the scaffold/config change;
  • pre-existing repository debt;
  • config-load, unsupported-plugin, or wrong-cwd failures.

Port the Claude behavior into Codex's native contract

The upstream hook generator has no Codex target, runs a whole-project package script without an edited path, and does not send remaining diagnostics to the editing agent. Harness's managed-file exclusions, path confinement, provider projection, and exact changed-file tracking are stronger foundations. Preserve Claude's post-edit behavior, but render it as Codex TOML and return Codex's documented PostToolUse payload. The config bytes cannot be copied because Claude uses .claude/settings.json while Codex discovers .codex/config.toml or .codex/hooks.json. Copy the repair contract and JSON normalization ideas, not the generated Claude JSON bytes.

Keep rules and skills optional

The actual Oxlint config and returned diagnostics are executable authority. Ultracite's rules and skill can improve first-pass generation, but they duplicate Harness's context packs and do not prove conformance. Do not add them wholesale to default context. If retained at all, expose a small, conditional lint-repair capability that activates when diagnostics exist.

Conflicts and uncertainty

  • Ultracite documentation describes hooks as fixing code after an edited file, but current source passes no edited path to the generated command. The source-backed interpretation is a project-wide fix after each matched edit.
  • Ultracite describes its Oxlint policy as high-signal and bug-focused. Issue 181 still shows that applying a strict opt-out policy to a mature repository can expose tens of thousands of existing findings. Both claims can be true; signal quality does not remove migration cost.
  • The 54,451 and 51,187 counts are issue evidence from a dirty Collective Intelligence checkout, not a fresh clean-tree benchmark.
  • Codex's documented payload and continuation contract is clear, but HI still needs a captured runtime fixture and live App verification. Project-local hook trust remains a manual review boundary by product design.
  • The highest compatible stable version tuple is proven. Whether issue 181 should ship that dependency migration together with the hook and scaffold policy work remains a product-scope decision.

Open product decisions

  1. Should the post-edit hook apply Oxlint safe fixes automatically, or only format and return diagnostics?
  2. Should issue 181 deliver Codex first using its native blocking PostToolUse continuation, or require equivalent automatic continuation for Claude, Cursor, and OpenCode in the same delivery?
  3. Should type-aware lint run on every edit from the owning workspace, or in a later focused validation gate?
  4. Should the proven version tuple ship in the same delivery, or remain a separately reviewed dependency migration while the hook calls Oxlint directly?
  5. What adoption policy applies when a candidate scaffold config creates a large pre-existing baseline: block, warn and baseline, or stage a migration?
  6. Which bounded command owns explicit repository-wide debt convergence, and what file/rule limits prevent an unreviewable agent run?

Next local action

Route the six decisions through requirements work for issue 181. The resulting spec should cover the hook protocol, real Codex App payload fixtures, owning-cwd resolution, safe-fix and JSON behavior, version compatibility, and a separate existing-debt adoption path. Do not begin by importing Ultracite's rules, skill, or hook files.

On this page