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:
- A post-edit loop prevents new debt in files an agent changes.
- A deliberate adoption/convergence path inventories and reduces existing repository debt. A hook cannot make an already-red repository clean.
What each lane covered
| Lane | Scope | Result |
|---|---|---|
| Ultracite AI surface | Official docs and upstream CLI source | Identified rules, skill, hooks, and the version 7.10 agent-assisted fixer as separate capabilities. |
| Harness hook audit | Catalog, hook runner, provider adapters, projection, and docs | Found path-scoped formatting and lint detection, but no safe JS autofix or diagnostic handoff. |
| Collective Intelligence case | Issue 181, commit 1a9927873, and current repository evidence | Separated Harness routing defects from strict-policy and existing-code debt; the repair reduced diagnostics by 3,264, not 50,000. |
| Version compatibility | Isolated frozen package install, Effect patch, and live lint | Proved 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
typeAwaresettings became active only when Oxlint runs from the owning workspace directory. - The Studio package stopped registering unsupported
effecttsgo; the only retained global rule disables werefunc-names,func-style, andsort-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
| Surface | What it does | Important limit for Harness |
|---|---|---|
| Agent rules | ultracite 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 fixer | ultracite 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:
- Runs Oxfmt and Oxlint safe fixes for the requested targets.
- Parses the remaining Oxlint JSON into file, rule, line, column, message, help, documentation URL, and source-span data.
- Groups findings by file and processes files sequentially.
- Writes full diagnostics to a temporary JSON file and gives the agent a short one-file prompt.
- Prohibits edits outside that file, suppressions, lint-config changes, and unrelated formatting.
- Re-lints after the attempt and retries surviving findings up to three times with fresh diagnostics.
- 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:
| Package | Version |
|---|---|
@effect/tsgo | 0.39.0 |
oxlint | 1.80.0 |
oxlint-tsgolint | 7.0.2001 |
typescript | 7.0.2 |
ultracite | 7.10.7 |
oxfmt | 0.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/orpackages/are linted. - Oxlint runs as
oxlint <edited-path>from the repository root, without--fixor JSON output. - Oxlint stdout and stderr are discarded.
- Codex receives only
{ "systemMessage": "Auto-lint failed ..." }. - Claude receives generic
additionalContext; Cursor receives a genericuser_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
- Should the post-edit hook apply Oxlint safe fixes automatically, or only format and return diagnostics?
- Should issue 181 deliver Codex first using its native blocking
PostToolUsecontinuation, or require equivalent automatic continuation for Claude, Cursor, and OpenCode in the same delivery? - Should type-aware lint run on every edit from the owning workspace, or in a later focused validation gate?
- Should the proven version tuple ship in the same delivery, or remain a separately reviewed dependency migration while the hook calls Oxlint directly?
- What adoption policy applies when a candidate scaffold config creates a large pre-existing baseline: block, warn and baseline, or stage a migration?
- 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.