# Reducing Maven stdout noise by 99% while keeping failure traces

> Source: <https://pvrlabs.xyz/articles/introverted-maven.html>
> Published: 2026-08-11 14:59:01+00:00

Maven is excellent at building Java projects.

It is also extremely talkative.

That verbosity is usually harmless when a developer is reading a terminal. For a coding agent, however, several thousand bytes of routine build output can displace source code, instructions, and the actual failure from the model’s working context.

So I built [ mvn-lite](https://github.com/ejboy/agent-scripts): a small, deterministic Bash wrapper that keeps successful Maven runs to one line and extracts only bounded, actionable evidence from failures.

I call it **Introverted Maven**.

Vanilla Maven████████████████████████████████6,753 bytes

mvn-lite▏16 bytes

In a real four-module Java application, successful output fell from **6,753 bytes to 16 bytes**,
and an invalid lifecycle failure fell from **2,730 bytes to 228 bytes**. Maven’s exit status
was preserved, complete failure logs were retained, and Maven arguments were not echoed in compact failure
output.

Full measurements and methodology are available in the [compatibility experiment report](https://github.com/ejboy/agent-scripts/blob/main/experiments/test/PVR-LABS-FINANCIAL-ENGINE-APP.md).

The goal is not to replace Maven’s diagnostics. It is to keep them from consuming an agent’s context unless they are needed.

## The problem with ordinary Maven output

A routine Maven test run can produce dozens or hundreds of lines covering routine details such as:

- reactor ordering
- plugin execution
- compiler phases
- test totals

Most of this is useful when investigating a build, but almost none is useful when it succeeds.

A coding agent usually needs to know only:

```
PASS · 4.240 s
```

If the build fails, the agent needs a compact answer to a different question:

What is the next actionable thing I should inspect or fix?

A compact test failure might look like:

```
FAIL
Failure summary:
  Test: com.example.AppTest.shouldRejectInvalidInput
  Exception: java.lang.AssertionError: expected 400

Full Maven log:
  /project/.agent-logs/maven/maven-20260730-100000-12345.log

Re-run with --full for complete live output.
```

The raw Maven output still matters. It simply does not need to occupy the main conversation by default.

Maven’s `-q`

option is too blunt for this use case: it suppresses output before the wrapper can inspect it. `mvn-lite`

captures normal Maven output privately, so it can still surface failing tests, compiler errors, unresolved dependencies, failed goals, and immediate causes when a build fails.

## What `mvn-lite`

does

In compact mode, `mvn-lite`

prefers the project’s executable `./mvnw`

, then falls back to `mvn`

from `PATH`

. It adds batch mode and disables transfer progress and color when equivalent options were not already supplied, then:

- captures complete output locally;
- preserves Maven’s exit status;
- prints a one-line success or bounded failure summary;
- retains failed logs and points to the complete log;
- falls back to a bounded tail for unknown failures.

Successful logs are deleted by default but can be preserved with `--keep-log`

. Ordinary Maven
output remains available through `--full`

.

`mvn-lite`

targets common local workflows: build, test, package, install, and verify. It recognizes high-value categories including compiler, test, dependency-resolution, failed-goal, and common Maven command errors.

Unknown output receives a bounded log tail rather than an invented interpretation, and the raw log remains authoritative.

## Testing it in a real application

I tested `mvn-lite`

in a real four-module Maven application. The selected module ran 102 unit tests using Maven 3.9.16 and the system-provided Bash 3.2 on macOS. The comparison covered vanilla Maven, the application’s former project-specific low-noise helper, and `mvn-lite`

.

Bytes from stdout and stderr were the primary metric because they are exact and tokenizer-independent.
Estimated tokens use `ceil(output bytes / 4)`

for relative comparison, not as an exact
model-token or billing measurement.

## Successful build results

The successful run selected one module and ran its tests:

```
./mvnw -B -ntp -Dstyle.color=never -pl common test
```

| Runner | Bytes | Lines | Estimated tokens |
|---|---|---|---|
| Vanilla Maven | 6,753 | 90 | ~1,689 |
| Former project-specific helper | 18 | 1 | ~5 |
`mvn-lite` | 16 | 1 | ~4 |

`mvn-lite`

produced `PASS · 3.944 s`

, reducing successful agent-visible output by more
than **99.7%**. It effectively matched the former helper; the two-byte difference is
operationally meaningless.

As an additional real-world check, I also ran `mvn-lite`

against the [Scriptella](https://scriptella.org/) reactor under JDK 17. The normal Maven run produced hundreds of lines, while `mvn-lite`

completed with a single `PASS · 11.137 s`

line. See the [Scriptella smoke test](https://github.com/ejboy/agent-scripts/blob/main/experiments/test/SCRIPTELLA-MVN-LITE-SMOKE-TEST.md).

## Failure results

The initial failure experiment invoked an invalid Maven lifecycle phase:

```
./mvnw definitely-not-a-maven-phase
```

The first `mvn-lite`

version was smaller than vanilla Maven and the former helper, but it repeated
Maven’s complete list of valid lifecycle phases. The actionable information was only:

```
Maven: Unknown lifecycle phase "definitely-not-a-maven-phase"
```

I updated the parser to shorten recognized Maven command errors while preserving the complete original message in the raw log.

### Actionable failure output after parser improvement

Agent-visible UTF-8 bytes, stdout + stderr

**2,730**

**1,393**

`mvn-lite`

**873**

```
Updated mvn-lite228
```

73.9% less output than the original `mvn-lite`

The updated wrapper reduced output by **91.6%** relative to vanilla Maven, **83.6%** relative to the former project-specific helper, and **73.9%** relative to the first `mvn-lite`

implementation. It still preserved Maven’s exit status, the invalid phase, the complete-log location, and rerun guidance.

## Extracting the immediate cause

Some Maven plugin failures include the useful cause on the same line as the failed goal:

```
Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.2.5:test
(default-test) on project app:
Execution default-test failed:
Unable to create temporary directory
```

A useful compact summary needs both:

```
Goal: org.apache.maven.plugins:maven-surefire-plugin:3.2.5:test (default-test)
Cause: Unable to create temporary directory
```

`mvn-lite`

extracts at most one immediate cause from bounded forms such as:

```
Execution ... failed: <cause>
```

It does not reconstruct arbitrary exception chains or stack traces.

During the final application integration, a restricted workspace prevented Surefire from creating its normal `target/surefire`

directory. The installed wrapper preserved exit status 1, retained the complete log, identified the Surefire goal, surfaced `Unable to create temporary directory`

, and remained bounded.

The same build passed after normal filesystem permissions were restored.

## Try `mvn-lite`

in your project

To try the latest version:

```
curl -fsSL \
  https://raw.githubusercontent.com/ejboy/agent-scripts/main/scripts/mvn-lite \
  -o mvn-lite
chmod +x mvn-lite
```

Review the script before running it in a sensitive repository. For a reproducible vendored copy, replace
`main`

with a release tag such as `v0.1.0`

.

Arguments pass through to Maven. Use `--full`

when ordinary Maven output is needed:

```
./mvn-lite test
./mvn-lite -pl common test
./mvn-lite --full test
./mvn-lite --help-mvn-lite
```

## Why not summarize the logs with an LLM?

`mvn-lite`

extracts the useful signal locally instead of spending agent tokens on an LLM summary
or sending repository logs outside the environment. It requires no API key and falls back to a bounded log
tail instead of making a probabilistic guess.

## Limitations

This was a focused compatibility and adoption experiment: one real multi-module application, one successful module build, one synthetic lifecycle error, one real Surefire environmental failure, warm incremental state, and single measured runs.

This article summarizes the main findings. The [complete experiment report](https://github.com/ejboy/agent-scripts/blob/main/experiments/test/PVR-LABS-FINANCIAL-ENGINE-APP.md) contains the full environment, controls, intermediate results, checksums, and supporting evidence.

The results do not establish a fixed savings percentage, diagnostic parity for every Maven plugin or failure category, or that Maven itself runs faster.

The claim is narrower:

For the measured local-development scenarios,

`mvn-lite`

dramatically reduced agent-visible output while preserving the exit status, actionable failure evidence, and access to the complete raw log.

## The broader lesson

Coding agents change the economics of command-line output.

Traditional build tools assume that console output is cheap. A developer can scroll, search, collapse, or ignore it.

For an agent, output competes directly with source code, requirements, conversation history, repository context, and the actual diagnostic. A tool designed for agent workflows should:

- Make success extremely small.
- Preserve exit status and actionable failure evidence.
- Retain complete diagnostics outside the main context.
- Bound unknown-output fallbacks and avoid echoing sensitive arguments.
- Preserve an escape hatch to ordinary output.

That is what `mvn-lite`

is trying to do.

Maven still knows everything it knew before.

It has simply learned when not to say it.

Try `mvn-lite`

in an existing Maven project. Real-world failure cases and feedback are
especially useful.
