Add backpressure stack for agentic coding quality gates
PostToolUse hooks run after every file edit: - Backend: ./mvnw compile (Checkstyle Google Style + javac) - Frontend: vue-tsc --noEmit + oxlint + ESLint Stop hook runs test suites when source files changed, blocks the agent on failure and re-engages it to fix the issue. Output is filtered to [ERROR] lines only for context efficiency. Static analysis: Checkstyle (validate phase), SpotBugs (verify phase), ArchUnit (9 hexagonal architecture rules as JUnit tests). Fail-fast: Surefire skipAfterFailureCount=1, Vitest bail=1. Test log noise suppressed via logback-test.xml (WARN level), redirectTestOutputToFile, and trimStackTrace. Existing Java sources reformatted to Google Style (2-space indent, import order, Javadoc on public types). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
487
docs/agents/plan/2026-03-04-backpressure-agentic-coding.md
Normal file
487
docs/agents/plan/2026-03-04-backpressure-agentic-coding.md
Normal file
@@ -0,0 +1,487 @@
|
||||
---
|
||||
date: 2026-03-04T01:40:21+01:00
|
||||
git_commit: a55174b32333d0f46a55d94a50604344d1ba33f6
|
||||
branch: master
|
||||
topic: "Backpressure for Agentic Coding"
|
||||
tags: [plan, backpressure, hooks, checkstyle, spotbugs, archunit, quality]
|
||||
status: complete
|
||||
---
|
||||
|
||||
# Backpressure for Agentic Coding — Implementation Plan
|
||||
|
||||
## Overview
|
||||
|
||||
Implement automated feedback mechanisms (backpressure) that force the AI agent to self-correct before a human reviews the output. The approach follows the 90/10 rule: 90% deterministic constraints (types, linting, architecture tests), 10% agentic review.
|
||||
|
||||
## Current State vs. Desired State
|
||||
|
||||
| Layer | Backend (now) | Backend (after) | Frontend (now) | Frontend (after) |
|
||||
|-------|---------------|-----------------|----------------|------------------|
|
||||
| Type System | Java 25 (strong) | *unchanged* | TS strict + `noUncheckedIndexedAccess` | *unchanged* |
|
||||
| Static Analysis | **None** | Checkstyle (Google Style) + SpotBugs | ESLint + oxlint + Prettier | *unchanged* |
|
||||
| Architecture Tests | **None** | ArchUnit (hexagonal enforcement) | N/A | N/A |
|
||||
| Unit Tests | JUnit 5 | JUnit 5 + fail-fast (`skipAfterFailureCount: 1`) | Vitest | Vitest + fail-fast (`bail: 1`) |
|
||||
| PostToolUse Hook | **None** | `./mvnw compile -q` (incl. Checkstyle) | **None** | `vue-tsc --noEmit` |
|
||||
| Stop Hook | **None** | `./mvnw test` | **None** | `npm run test:unit -- --run` |
|
||||
|
||||
## What We're NOT Doing
|
||||
|
||||
- **Error Prone** — overlaps with SpotBugs, Java 25 compatibility uncertain, more invasive setup
|
||||
- **Custom ESLint rules** — add later when recurring agent mistakes are observed
|
||||
- **MCP LSP Server** — experimental, high setup cost, unclear benefit vs. hooks
|
||||
- **Pre-commit git hooks** — orthogonal concern, not part of this plan
|
||||
- **CI/CD pipeline** — out of scope, this is about local agent feedback
|
||||
|
||||
## Design Decisions
|
||||
|
||||
- **Hook matchers** are regex on **tool names** (not file paths). File-path filtering must happen inside the hook script via `tool_input.file_path` from stdin JSON.
|
||||
- **Context-efficient output**: ✓ on success, full error on failure. Don't waste the agent's context window with passing output.
|
||||
- **Fail-fast**: one failure at a time. Prevents context-switching between multiple bugs.
|
||||
- **Stop hook** checks `git status --porcelain` to determine if code files changed. Skips test runs on conversational responses.
|
||||
- **Checkstyle** bound to Maven `validate` phase — automatically triggered by `./mvnw compile`, which means the PostToolUse hook gets Checkstyle for free.
|
||||
- **SpotBugs** bound to Maven `verify` phase — NOT hooked, run manually via `./mvnw verify`.
|
||||
- **ArchUnit**: use `archunit-junit5` 1.4.1 only. Do NOT use `archunit-hexagonal` addon (dormant since Jul 2023, pulls Kotlin, pinned to ArchUnit 1.0.1, expects different package naming).
|
||||
|
||||
---
|
||||
|
||||
## Task List
|
||||
|
||||
Tasks are ordered by priority. Execute one task per iteration, in order. Each task includes the exact changes and a verification command.
|
||||
|
||||
### T-BP-01: Create backend compile-check hook script `[x]`
|
||||
|
||||
**File**: `.claude/hooks/backend-compile-check.sh` (new, create `.claude/hooks/` directory if needed)
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Read hook input from stdin (JSON with tool_input.file_path)
|
||||
INPUT=$(cat)
|
||||
FILE_PATH=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_input',{}).get('file_path',''))" 2>/dev/null || echo "")
|
||||
|
||||
# Only run for Java files under backend/
|
||||
case "$FILE_PATH" in
|
||||
*/backend/src/*.java) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
|
||||
cd "$CLAUDE_PROJECT_DIR/backend"
|
||||
|
||||
# Run compile (includes validate phase → Checkstyle if configured)
|
||||
# Context-efficient: suppress output on success, show full output on failure
|
||||
if OUTPUT=$(./mvnw compile -q 2>&1); then
|
||||
echo '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"✓ Backend compile passed."}}'
|
||||
else
|
||||
ESCAPED=$(echo "$OUTPUT" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))")
|
||||
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":$ESCAPED}}"
|
||||
fi
|
||||
```
|
||||
|
||||
Make executable: `chmod +x .claude/hooks/backend-compile-check.sh`
|
||||
|
||||
**Verify**: `echo '{"tool_input":{"file_path":"backend/src/main/java/de/fete/FeteApplication.java"}}' | CLAUDE_PROJECT_DIR=. .claude/hooks/backend-compile-check.sh` → should output JSON with "✓ Backend compile passed."
|
||||
|
||||
**Verify skip**: `echo '{"tool_input":{"file_path":"README.md"}}' | CLAUDE_PROJECT_DIR=. .claude/hooks/backend-compile-check.sh` → should exit 0 silently.
|
||||
|
||||
---
|
||||
|
||||
### T-BP-02: Create frontend type-check hook script `[x]`
|
||||
|
||||
**File**: `.claude/hooks/frontend-type-check.sh` (new)
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Read hook input from stdin (JSON with tool_input.file_path)
|
||||
INPUT=$(cat)
|
||||
FILE_PATH=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_input',{}).get('file_path',''))" 2>/dev/null || echo "")
|
||||
|
||||
# Only run for TS/Vue files under frontend/
|
||||
case "$FILE_PATH" in
|
||||
*/frontend/src/*.ts|*/frontend/src/*.vue) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
|
||||
cd "$CLAUDE_PROJECT_DIR/frontend"
|
||||
|
||||
# Run type-check
|
||||
# Context-efficient: suppress output on success, show full output on failure
|
||||
if OUTPUT=$(npx vue-tsc --noEmit 2>&1); then
|
||||
echo '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"✓ Frontend type-check passed."}}'
|
||||
else
|
||||
ESCAPED=$(echo "$OUTPUT" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))")
|
||||
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":$ESCAPED}}"
|
||||
fi
|
||||
```
|
||||
|
||||
Make executable: `chmod +x .claude/hooks/frontend-type-check.sh`
|
||||
|
||||
**Verify**: `echo '{"tool_input":{"file_path":"frontend/src/App.vue"}}' | CLAUDE_PROJECT_DIR=. .claude/hooks/frontend-type-check.sh` → should output JSON with "✓ Frontend type-check passed."
|
||||
|
||||
---
|
||||
|
||||
### T-BP-03: Create stop hook script (test gate) `[x]`
|
||||
|
||||
**File**: `.claude/hooks/run-tests.sh` (new)
|
||||
|
||||
Runs after the agent finishes its response. Checks `git status` for changed source files and runs the relevant test suites. Skips on conversational responses (no code changes).
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd "$CLAUDE_PROJECT_DIR"
|
||||
|
||||
# Check for uncommitted changes in backend/frontend source
|
||||
HAS_BACKEND=$(git status --porcelain backend/src/ 2>/dev/null | head -1)
|
||||
HAS_FRONTEND=$(git status --porcelain frontend/src/ 2>/dev/null | head -1)
|
||||
|
||||
# Nothing changed — skip
|
||||
if [[ -z "$HAS_BACKEND" && -z "$HAS_FRONTEND" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ERRORS=""
|
||||
PASSED=""
|
||||
|
||||
# Run backend tests if Java sources changed
|
||||
if [[ -n "$HAS_BACKEND" ]]; then
|
||||
if OUTPUT=$(cd backend && ./mvnw test -q 2>&1); then
|
||||
PASSED+="✓ Backend tests passed. "
|
||||
else
|
||||
ERRORS+="Backend tests failed:\n$OUTPUT\n\n"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Run frontend tests if TS/Vue sources changed
|
||||
if [[ -n "$HAS_FRONTEND" ]]; then
|
||||
if OUTPUT=$(cd frontend && npm run test:unit -- --run 2>&1); then
|
||||
PASSED+="✓ Frontend tests passed. "
|
||||
else
|
||||
ERRORS+="Frontend tests failed:\n$OUTPUT\n\n"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "$ERRORS" ]]; then
|
||||
ESCAPED=$(printf '%s' "$ERRORS" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))")
|
||||
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"Stop\",\"additionalContext\":$ESCAPED}}"
|
||||
else
|
||||
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"Stop\",\"additionalContext\":\"$PASSED\"}}"
|
||||
fi
|
||||
```
|
||||
|
||||
Make executable: `chmod +x .claude/hooks/run-tests.sh`
|
||||
|
||||
**Verify**: `CLAUDE_PROJECT_DIR=. .claude/hooks/run-tests.sh` → if no uncommitted changes in source dirs, should exit 0 silently.
|
||||
|
||||
---
|
||||
|
||||
### T-BP-04: Create `.claude/settings.json` with hook configuration `[x]`
|
||||
|
||||
**File**: `.claude/settings.json` (new — do NOT modify `.claude/settings.local.json`, that has permissions)
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Edit|Write",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/backend-compile-check.sh\"",
|
||||
"timeout": 120
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/frontend-type-check.sh\"",
|
||||
"timeout": 60
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"Stop": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/run-tests.sh\"",
|
||||
"timeout": 300
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Verify**: File exists and is valid JSON: `python3 -c "import json; json.load(open('.claude/settings.json'))"`
|
||||
|
||||
---
|
||||
|
||||
### T-BP-05: Configure Vitest fail-fast `[x]`
|
||||
|
||||
**File**: `frontend/vitest.config.ts` (modify)
|
||||
|
||||
Add `bail: 1` to the test configuration object. The result should look like:
|
||||
|
||||
```typescript
|
||||
export default mergeConfig(
|
||||
viteConfig,
|
||||
defineConfig({
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
exclude: [...configDefaults.exclude, 'e2e/**'],
|
||||
root: fileURLToPath(new URL('./', import.meta.url)),
|
||||
bail: 1,
|
||||
},
|
||||
}),
|
||||
)
|
||||
```
|
||||
|
||||
**Verify**: `cd frontend && npm run test:unit -- --run` passes.
|
||||
|
||||
---
|
||||
|
||||
### T-BP-06: Configure Maven Surefire fail-fast `[x]`
|
||||
|
||||
**File**: `backend/pom.xml` (modify)
|
||||
|
||||
Add `maven-surefire-plugin` configuration within `<build><plugins>`:
|
||||
|
||||
```xml
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<!-- Fail-fast: stop on first test failure -->
|
||||
<skipAfterFailureCount>1</skipAfterFailureCount>
|
||||
</configuration>
|
||||
</plugin>
|
||||
```
|
||||
|
||||
**Verify**: `cd backend && ./mvnw test` passes.
|
||||
|
||||
---
|
||||
|
||||
### T-BP-07: Add Checkstyle plugin + fix violations `[x]`
|
||||
|
||||
**File**: `backend/pom.xml` (modify)
|
||||
|
||||
Add `maven-checkstyle-plugin` within `<build><plugins>`:
|
||||
|
||||
```xml
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>13.3.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<configuration>
|
||||
<configLocation>google_checks.xml</configLocation>
|
||||
<consoleOutput>true</consoleOutput>
|
||||
<failOnViolation>true</failOnViolation>
|
||||
<violationSeverity>warning</violationSeverity>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>checkstyle-validate</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
```
|
||||
|
||||
Then run `cd backend && ./mvnw checkstyle:check` to find violations. Fix all violations in existing source files (`FeteApplication.java`, `HealthController.java`, `FeteApplicationTest.java`, all `package-info.java` files). Google Style requires: 2-space indentation, specific import order, Javadoc on public types, max line length 100.
|
||||
|
||||
**Verify**: `cd backend && ./mvnw checkstyle:check` passes with zero violations AND `cd backend && ./mvnw compile` passes (Checkstyle now runs during validate phase).
|
||||
|
||||
---
|
||||
|
||||
### T-BP-08: Add SpotBugs plugin + verify `[x]`
|
||||
|
||||
**File**: `backend/pom.xml` (modify)
|
||||
|
||||
Add `spotbugs-maven-plugin` within `<build><plugins>`:
|
||||
|
||||
```xml
|
||||
<plugin>
|
||||
<groupId>com.github.spotbugs</groupId>
|
||||
<artifactId>spotbugs-maven-plugin</artifactId>
|
||||
<version>4.9.8.2</version>
|
||||
<configuration>
|
||||
<effort>Max</effort>
|
||||
<threshold>Low</threshold>
|
||||
<xmlOutput>true</xmlOutput>
|
||||
<failOnError>true</failOnError>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
```
|
||||
|
||||
Run `cd backend && ./mvnw verify` — if SpotBugs finds issues, fix them.
|
||||
|
||||
**Verify**: `cd backend && ./mvnw verify` passes (includes compile + test + SpotBugs).
|
||||
|
||||
---
|
||||
|
||||
### T-BP-09: Add ArchUnit dependency + write architecture tests `[x]`
|
||||
|
||||
**File 1**: `backend/pom.xml` (modify) — add within `<dependencies>`:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.tngtech.archunit</groupId>
|
||||
<artifactId>archunit-junit5</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
Do NOT use the `archunit-hexagonal` addon.
|
||||
|
||||
**File 2**: `backend/src/test/java/de/fete/HexagonalArchitectureTest.java` (new)
|
||||
|
||||
```java
|
||||
package de.fete;
|
||||
|
||||
import com.tngtech.archunit.core.importer.ImportOption;
|
||||
import com.tngtech.archunit.junit.AnalyzeClasses;
|
||||
import com.tngtech.archunit.junit.ArchTest;
|
||||
import com.tngtech.archunit.lang.ArchRule;
|
||||
|
||||
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
|
||||
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
|
||||
import static com.tngtech.archunit.library.Architectures.onionArchitecture;
|
||||
|
||||
@AnalyzeClasses(packages = "de.fete", importOptions = ImportOption.DoNotIncludeTests.class)
|
||||
class HexagonalArchitectureTest {
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule onion_architecture_is_respected = onionArchitecture()
|
||||
.domainModels("de.fete.domain.model..")
|
||||
.domainServices("de.fete.domain.port.in..", "de.fete.domain.port.out..")
|
||||
.applicationServices("de.fete.application.service..")
|
||||
.adapter("web", "de.fete.adapter.in.web..")
|
||||
.adapter("persistence", "de.fete.adapter.out.persistence..")
|
||||
.adapter("config", "de.fete.config..");
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule domain_must_not_depend_on_adapters = noClasses()
|
||||
.that().resideInAPackage("de.fete.domain..")
|
||||
.should().dependOnClassesThat().resideInAPackage("de.fete.adapter..");
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule domain_must_not_depend_on_application = noClasses()
|
||||
.that().resideInAPackage("de.fete.domain..")
|
||||
.should().dependOnClassesThat().resideInAPackage("de.fete.application..");
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule domain_must_not_depend_on_config = noClasses()
|
||||
.that().resideInAPackage("de.fete.domain..")
|
||||
.should().dependOnClassesThat().resideInAPackage("de.fete.config..");
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule inbound_ports_must_be_interfaces = classes()
|
||||
.that().resideInAPackage("de.fete.domain.port.in..")
|
||||
.should().beInterfaces();
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule outbound_ports_must_be_interfaces = classes()
|
||||
.that().resideInAPackage("de.fete.domain.port.out..")
|
||||
.should().beInterfaces();
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule domain_must_not_use_spring = noClasses()
|
||||
.that().resideInAPackage("de.fete.domain..")
|
||||
.should().dependOnClassesThat().resideInAPackage("org.springframework..");
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule web_must_not_depend_on_persistence = noClasses()
|
||||
.that().resideInAPackage("de.fete.adapter.in.web..")
|
||||
.should().dependOnClassesThat().resideInAPackage("de.fete.adapter.out.persistence..");
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule persistence_must_not_depend_on_web = noClasses()
|
||||
.that().resideInAPackage("de.fete.adapter.out.persistence..")
|
||||
.should().dependOnClassesThat().resideInAPackage("de.fete.adapter.in.web..");
|
||||
}
|
||||
```
|
||||
|
||||
**Verify**: `cd backend && ./mvnw test` passes and output shows `HexagonalArchitectureTest` with 9 tests.
|
||||
|
||||
---
|
||||
|
||||
### T-BP-10: Update CLAUDE.md `[x]`
|
||||
|
||||
**File**: `CLAUDE.md` (modify)
|
||||
|
||||
Add two rows to the Build Commands table:
|
||||
|
||||
| What | Command |
|
||||
|------|---------|
|
||||
| Backend checkstyle | `cd backend && ./mvnw checkstyle:check` |
|
||||
| Backend full verify | `cd backend && ./mvnw verify` |
|
||||
|
||||
---
|
||||
|
||||
### T-BP-11: Final verification `[x]`
|
||||
|
||||
Run all verification commands to confirm the complete backpressure stack works:
|
||||
|
||||
1. `test -x .claude/hooks/backend-compile-check.sh`
|
||||
2. `test -x .claude/hooks/frontend-type-check.sh`
|
||||
3. `test -x .claude/hooks/run-tests.sh`
|
||||
4. `python3 -c "import json; json.load(open('.claude/settings.json'))"`
|
||||
5. `cd backend && ./mvnw verify` (triggers: Checkstyle → compile → test w/ ArchUnit → SpotBugs)
|
||||
6. `cd frontend && npm run test:unit -- --run`
|
||||
7. `echo '{"tool_input":{"file_path":"backend/src/main/java/de/fete/FeteApplication.java"}}' | CLAUDE_PROJECT_DIR=. .claude/hooks/backend-compile-check.sh`
|
||||
8. `echo '{"tool_input":{"file_path":"frontend/src/App.vue"}}' | CLAUDE_PROJECT_DIR=. .claude/hooks/frontend-type-check.sh`
|
||||
9. `echo '{"tool_input":{"file_path":"README.md"}}' | CLAUDE_PROJECT_DIR=. .claude/hooks/backend-compile-check.sh` (should be silent)
|
||||
|
||||
All commands must exit 0. If any fail, go back and fix the issue before marking complete.
|
||||
|
||||
---
|
||||
|
||||
## Addendum: Implementation Deviations (2026-03-04)
|
||||
|
||||
Changes made during implementation that deviate from or extend the original plan:
|
||||
|
||||
1. **Stop hook JSON schema**: The plan used `hookSpecificOutput` with `hookEventName: "Stop"` for the stop hook output. This is invalid — `hookSpecificOutput` is only supported for `PreToolUse`, `PostToolUse`, and `UserPromptSubmit` events. Fixed to use top-level `"decision": "approve"/"block"` with `"reason"` field.
|
||||
|
||||
2. **Stop hook loop prevention**: Added `stop_hook_active` check from stdin JSON to prevent infinite re-engagement loops. Not in original plan.
|
||||
|
||||
3. **Context-efficient test output**: Added `logback-test.xml` (root level WARN), `redirectTestOutputToFile=true`, and `trimStackTrace=true` to Surefire config. The stop hook script filters output to `[ERROR]` lines only, stripping Maven boilerplate. Not in original plan — added after observing that raw test failure output consumed excessive context.
|
||||
|
||||
4. **Hook path matching**: Case patterns in hook scripts extended to match both absolute and relative file paths (`*/backend/src/*.java|backend/src/*.java`). Original plan only had `*/backend/src/*.java` which doesn't match relative paths.
|
||||
|
||||
5. **Checkstyle `includeTestSourceDirectory`**: Set to `true` so test sources also follow Google Style. Not in original plan. `FeteApplicationTest.java` was reformatted to 2-space indentation with correct import order (static imports first).
|
||||
|
||||
6. **ArchUnit field naming**: Changed from `snake_case` (`onion_architecture_is_respected`) to `camelCase` (`onionArchitectureIsRespected`) to comply with Google Checkstyle rules.
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- Research document: `docs/agents/research/2026-03-04-backpressure-agentic-coding.md`
|
||||
- Geoffrey Huntley: [Don't waste your back pressure](https://ghuntley.com/pressure/)
|
||||
- JW: [If you don't engineer backpressure, you'll get slopped](https://jw.hn/engineering-backpressure)
|
||||
- HumanLayer: [Context-Efficient Backpressure for Coding Agents](https://www.hlyr.dev/blog/context-efficient-backpressure)
|
||||
- Claude Code: [Hooks Reference](https://code.claude.com/docs/en/hooks)
|
||||
- ArchUnit: [User Guide](https://www.archunit.org/userguide/html/000_Index.html)
|
||||
Reference in New Issue
Block a user