Files
clean-architecture-backend-…/docs/superpowers/specs/2026-07-20-harness-policy-engine-design.md
T

9.1 KiB
Raw Blame History

Harness Policy Engine Refactoring Design

  • Date: 2026-07-20
  • Status: Approved by user request
  • Scope: repository-local development harness (.harness, .agents, .claude, .codex, root/module guidance, Gradle module registry integration)
  • Source: user-provided “개발 하네스 분석·리뷰” plus repository evidence gathered on 2026-07-20

1. Problem Statement

The repository now has 19 nested Gradle leaf modules, but the write-time import gate, agent prompts, runner allowlist, and root guidance still contain parts of the previous flat module topology. Platform variants are copied manually, so commit policy and orchestration already differ between Claude, Codex, and Antigravity. Verdict validation checks a text summary but does not consistently require enum fields, non-negative counts, or arithmetic balance.

The harness must move from duplicated platform prompts to a small policy engine with one project manifest, deterministic renderers, strict validators, and platform adapters.

2. Goals

  1. Make the actual nested Gradle topology a single machine-readable source of truth.
  2. Resolve a touched file to its nearest owning leaf module without assuming src/<module>.
  3. Generate write-time import policy and focused Gradle task validation from that registry.
  4. Validate machine verdicts with required fields, non-negative integers, arithmetic rules, upstream evidence, revision identity, and TDD red evidence for behavior changes.
  5. Materialize validated evidence as JSON artifacts that platform hooks can share.
  6. Render Claude, Codex, and Antigravity agent variants from one canonical source and fail parity checks when generated files drift.
  7. Use one human-only commit policy on every platform.
  8. Replace file-count and exhaustive-report rules with risk and review profiles.
  9. Add mutation and cross-platform static parity tests.

3. Non-Goals

  • This change does not run authenticated end-to-end golden tasks inside all three external products. It supplies the deterministic fixtures and validators those runs will consume.
  • It does not add application features or alter production Java behavior.
  • It does not require PyYAML, jsonschema, Pydantic, or another runtime dependency. Harness data files use JSON syntax, which is valid YAML, and validators use Python stdlib only.
  • It does not make natural-language agent self-reports authoritative. Hooks convert accepted reports into evidence artifacts; validators remain authoritative.

4. Architecture

.harness/project/modules.yaml ──┬── Gradle settings/includes
                                ├── Gradle dependency verification
                                ├── owning-module resolver
                                ├── import gate
                                └── Gradle command validator

.harness/agents/*.md + platforms.yaml
                                └── render_agents.py
                                    ├── .claude/agents/*.md
                                    ├── .codex/agents/*.toml
                                    └── .agents/agents/*/agent.json

Claude hook ───────────────┐
Antigravity hook adapter ──┼── verdict validator ── evidence JSON
Codex validation command ──┘

4.1 Project registry

modules.yaml contains, per leaf module:

  • stable module id
  • repository-relative source path
  • Gradle path
  • role
  • Java package roots (informational and import-policy lookup only)
  • allowed project dependencies
  • focused test command
  • profiles/capabilities
  • owning CLAUDE.md when present
  • an intentional mutation import used by gate tests

src/settings.gradle reads the registry to declare projects. The verifyCleanArchitectureDependencies task reads the same registry instead of maintaining a second dependency map.

4.2 Owning-module resolution

Owner selection uses the longest filesystem-boundary match among registered leaf source paths. Package prefixes never decide ownership because support owns a broad dev.caskeleton.adapter.outbound package and sample code mirrors production packages.

Instruction discovery walks upward from the touched file and returns the nearest CLAUDE.md; if a leaf has none, root CLAUDE.md and AGENTS.md are the explicit fallback.

4.3 Import gate

The import gate first resolves the registered leaf module, then applies:

  • dependency-derived sibling module isolation
  • role-specific framework rules for domain, application, inbound, outbound, persistence, identifier, shared-contract, bootstrap, and sample roles
  • global unsafe-pattern checks

All registered production modules receive a mutation test using their real nested source path. Sample-only exemptions are explicit registry data, not accidental regex misses.

4.4 Verdict and evidence

The canonical verdict schema requires agent, verdict, task_id, revision, and agent specific evidence. Non-blocked verdicts require every declared field. Counts are non-negative. Required equations include:

  • spec totals balance
  • Gradle run = passed + failed + skipped
  • ready Gradle results include at least one command and no failed command
  • behavior-changing implementation requires at least one observed red test
  • quality-ready references validated architecture and spec artifacts

Claude's fenced ca-verdict remains a compatibility input, but its accepted form is converted to the same JSON evidence model. Missing or malformed payloads for detected CA agents fail closed. Evidence records include a source-message hash and current revision/diff identity.

4.5 Platform rendering and hook adapters

Canonical agent Markdown lives under .harness/agents/; platform metadata lives in .harness/project/platforms.yaml. Generated files carry generated_from, source_hash, generator_version, and do_not_edit metadata.

Antigravity gains a plugin hooks.json and a platform adapter using the documented camelCase stdin/stdout contract. Claude keeps its native hook entry points but calls the common library. Codex variants instruct the runner/reviewer to invoke the common validation command because the repository has no equivalent local lifecycle-hook registration surface.

4.6 Risk and review profiles

Risk is determined by change surface, not file count:

  • high: security, migration/schema, public contract, module dependency, architecture rule, transaction/concurrency, CI/deployment
  • medium: behavior, multiple modules, external integration
  • low: docs/comments, test fixture, local refactor protected by characterization tests

Review profiles:

  • review-lite: direct diff references; no saved report by default
  • review-standard: verify blocking citations; one report only when risk or findings justify it
  • audit-deep: verify all quotes and persist detailed findings
  • regulated: immutable evidence and full traceability

Option analysis uses a dependency DAG and at most 35 materially distinct alternatives. Counterarguments are required for judgment-dependent findings, not deterministic failures.

5. Commit Policy

All platforms use human-only. Implementers never stage or commit. Reviewers may inspect a working-tree diff before commit or an explicit immutable range after the human commits.

6. Verification Strategy

  1. Stdlib unit tests for registry loading and owner resolution.
  2. Mutation tests for every registered production module path.
  3. Strict verdict negative tests: missing fields, negatives, arithmetic imbalance, missing upstream evidence, revision mismatch, and behavior change without red evidence.
  4. Golden renderer tests and --check parity validation.
  5. JSON validation of generated Antigravity hook and agent files.
  6. Gradle projects, architecture dependency verification, focused ArchUnit test, and full check after harness tests pass.

7. Migration and Compatibility

  • Existing fenced verdicts remain parseable only when they satisfy the new required fields.
  • Generated platform files are overwritten only by the renderer and documented as generated.
  • Root and module guidance is updated to the registered nested topology.
  • Actual external cross-platform golden executions remain a follow-up; static parity and seeded mutation coverage become mandatory in this change.

8. Acceptance Criteria

  • A seeded forbidden import under every nested production module is rejected.
  • No legacy flat adapter path remains in gate tests or agent task allowlists.
  • settings.gradle, dependency verification, import gate, and Gradle runner resolve the same 19 leaf modules from modules.yaml.
  • Missing/negative/inconsistent ready verdicts fail validation.
  • Claude and Antigravity adapters invoke the shared validator; accepted verdicts produce JSON evidence.
  • Rendering followed by --check reports no platform drift.
  • Agent variants uniformly state human-only commit policy and risk-based orchestration.
  • N! enumeration, all-quote routine grep, file-count report splitting, and unconditional counterargument requirements are absent from active rules.
  • Harness tests and Gradle checks pass, or every unrun/failing command is reported with risk.