Fix Developer Reviews Skill (apply human review findings to an EARS file)
You are closing the gap between what a developer found by reviewing an EARS file against the
codebase and the EARS specification itself. After /springboot-to-ears produced the spec, a
developer read it next to the source and recorded findings the extraction missed — directly in the
EARS file, inside two dedicated sections:
## Review by Developer (code) — backticked source-code pointers to things that exist in the
code but are missing from the spec (file, file#method, or file:line-range), each with an
optional note and an optional → Module: placement override. A code entry asserts either that a
rule is missing (→ a new EARS statement) or that a whole entity / enum-state is missing
(→ a new ### <Name> block in Domain Entities / Domain Concepts, plus any rule-bearing fields as
statements).
## Review by Developer (business requirements) — business decisions/requirements the
developer wants captured that need a human answer (no code expected); optional | context:
pointer and optional → Module: override.
This skill reads those two sections, investigates each unchecked entry, proposes resolutions, and —
only after the user approves — applies them and marks each entry done. The EARS statements
stay code-free (springboot-to-ears Rule 2): no inline citations, no class/method names in statement
prose. File paths do, however, legitimately appear in the body as the per-heading
> **Source files:** blockquote annotation that every ## Module: and content ### Subsection
carries (springboot-to-ears Rule 14) — this skill preserves those annotations, consults them as
a supplemental locate hint, and keeps them accurate when it inserts new content. The developer's
source pointers themselves live only in the review sections (developer input) and in this skill's
report file.
This skill does two tasks in two gated phases:
- Find — parse both review sections, investigate each unchecked entry against the code (driven
by its pointer), and write a
DEV-REVIEW REPORT proposing a resolution per entry. Then STOP
and wait for the user.
- Fix — only on the user's approval, apply the approved entries to the EARS file (confident rule
findings as new code-free EARS statements; confident structural findings as a new
### <Name> block — full property/state table — in Domain Entities / Domain Concepts, plus any
rule-bearing fields as statements; ambiguous findings and all business-requirement entries as
[NEEDS REVIEW] Open Questions), check each applied entry off (- [ ] → - [x]) with a
→ resolved: annotation that names the landing location and the EARS line number, then
refresh the Extraction Summary.
Scope is developer-seeded only. This skill acts on the entries the developer wrote in the two
review sections — nothing else. It does not re-scan the whole controller to discover new gaps
(that is ears-gap-fix), and it does not report orphan EARS statements or value mismatches.
Companion skills (all in this project's .claude/skills/):
springboot-to-ears — produced the EARS file and emits the two empty review-section placeholders.
This skill reuses its output rules by reference; read
.claude/skills/springboot-to-ears/SKILL.md for the cited Rules/Steps/Procedures.
ears-gap-fix — the agent-discovered gap closer. This skill reuses its Step 1 gates and its
Step 2a large-file extraction protocol by reference; read
.claude/skills/ears-gap-fix/SKILL.md.
resolve-open-questions — the breadcrumb resolver. Run it after this skill's Phase 2 to chase any
new [NEEDS REVIEW] Open Questions the business-requirement entries produced. This skill reuses
its Extraction-Summary refresh mechanics (its Step 6) by reference.
Never read or rely on skills outside this repository.
Step 1 — Invocation Contract & Validation Gates
Invocation:
/fix-dev-reviews <path-to-EARS-file.md> [<Controller.java> ...]
The first (and only required) argument is the EARS file. Any trailing arguments are controller
source files passed as an optional override for the controller set (used only to scope smart
auto-locate, Step 3); when supplied, they replace the header-parsed set entirely.
Resolve the controller set exactly as ears-gap-fix Step 1 does: if controller args were
passed, that list is the set; otherwise read the > **Source Controller(s):** line in the EARS
header preamble and extract every backtick-wrapped project-relative path. Never scan the repo to
guess a controller.
Validate before doing any work and exit early with the matching message:
| Condition |
Exit message |
| No arguments at all |
Usage: /fix-dev-reviews <path-to-EARS-file.md> [<Controller.java> ...] |
| EARS path missing or not a file |
EARS file not found: <path> |
EARS path is not a .md file |
Expected a Markdown EARS file; got <path> |
EARS file has no ## Module: section AND no ## Extraction Summary |
<path> does not look like a /springboot-to-ears output (no Module / Extraction Summary section) — nothing to do. |
Neither ## Review by Developer (code) nor ## Review by Developer (business requirements) section exists |
<path> has no Review-by-Developer sections — run /springboot-to-ears to add the placeholders, or add the sections manually. |
Both review sections exist but contain zero entries, or every entry in both is already - [x] |
No unprocessed Review-by-Developer entries in <path> — nothing to do. |
| A resolved controller path (header or args) does not exist on disk — only checked if a note-only / smart-locate entry needs it |
Controller file not found: <path> |
When the gates pass, read the EARS file end-to-end once so you know which ## Module: / ###
subsections exist (including the ## Domain Entities and Properties and ## Domain Concepts and States reference sections and the ### <Name> blocks already under them) and where the two review
sections sit. Note that each ## Module: heading and every content ### Subsection carries a
> **Source files:** blockquote line (springboot-to-ears Rule 14); record those lists — they
are a supplemental locate hint (Step 3), a placement cross-check (Step 3), and the annotation-upkeep
target (Step 5). Derive <Domain> from the EARS title (e.g. # Loan Proposal — OTC … (EARS) →
Loan Proposal — OTC).
Step 2 — Phase 1: Parse Both Review Sections
Within each review section, every list item matching ^- \[[ x]\] is one entry. Skip every entry
already marked - [x] (done on a prior run — do not re-investigate). For each unchecked
(- [ ]) entry, build a record:
| Field |
Source |
section |
code or business |
pointer |
the backticked code pointer (code section) or the ` |
note |
the free-text after the pointer / the decision sentence |
override |
the module/subsection named after a trailing → Module: <Module> › <Subsection>, if present |
kind |
(code entries only) rule or structural — see the classification below; structural is further sub-typed entity or concept |
Multi-line entries are allowed (the developer may wrap a note); the entry runs until the next
- [ ] / - [x] line or the next ## heading. Record entries in document order. Number them
DR-001, DR-002, … for the report.
A code entry asserts one of two things; classify each by note wording with a class-shape
fallback:
- rule (default) — "a real rule lives here and is missing from the spec." Produces a new EARS
statement.
- structural — "a whole entity or enum/state is missing from the spec." Treat a code entry as
structural when either the note signals a missing entity / model / DTO or enum / state /
concept (words like "entity", "model", "DTO", "enum", "status", "states", "add … with its
fields/properties"), or the pointer resolves to a class whose shape is structural — a JPA
@Entity, a *Dto/record/POJO that is just fields + accessors, or an enum / state type — and
no ### <Name> block for it already exists under ## Domain Entities and Properties /
## Domain Concepts and States. Sub-type it entity (data class / @Entity / *Dto) or
concept (enum / state type). If a matching ### <Name> block already exists, it is not
structural — handle it as a rule entry (a missing field/rule on an existing block), never a
duplicate block.
A business entry asserts "this decision/requirement must be captured", is never structural
(structure is code-discoverable), and is always destined for a [NEEDS REVIEW] Open Question.
Step 3 — Phase 1: Investigate Each Entry
Drive the investigation off the entry's pointer. Reuse ears-gap-fix Step 2a (the large-file
structural-index + rule-site-ledger protocol) and the springboot-to-ears "named-not-read"
prohibition (Step 2i) — do not invent a parallel method.
| Pointer form |
How to investigate |
File.java#method |
Resolve the file path (find . -name File.java / graphify if bare). Open it, jump to that method, read its full body and every private helper / injected sub-validator it delegates to (recursively, Procedure A-3/A-4). |
File.java:start-end |
Read that line span plus the enclosing method, then any helpers it calls. |
File.java (whole file) |
Run the ears-gap-fix Step 2a protocol on the whole file: wc -l, build the rule-site map, read by method range, keep a ledger. |
| none (smart auto-locate) |
Use graphify query "<note nouns>" / grep keyed on the note's distinctive nouns and any quoted message, with the resolved controller set's call chain as the primary scope. If the entry has a → Module: override (or its placement is otherwise obvious), additionally consult that section's > **Source files:** list as a supplemental hint for which files to read first. Read the matched method fully. |
For each entry capture, where applicable: the exact file:line(-range), the precise code condition
/ trigger, the verbatim i18n message or string literal it produces (Rule 5), and the exact
reference entity/field that supplies the comparison value (Rule 4 — "loan product details" ≠
"loan product policy"). These are the anchors for the proposed statement and the report's proof.
For a structural entry, investigate the whole class instead of a single rule-site. Resolve and
read the pointed class in full (reuse ears-gap-fix Step 2a large-file protocol if it is large;
obey the springboot-to-ears "named-not-read" prohibition — never describe a field you did not
read). Capture every persisted/serialised field (entity) or every value/state and transition
(enum/state). Map each to business terms per springboot-to-ears Rule 4 (code-free naming):
property names like "Proposed loan amount" — never proposedLoanAmount; Types in business terms
(Amount / Text / Number / Boolean / Date / Reference / Enum / List), never Java types. Exclude the
shared audit, soft-delete, and optimistic-locking fields — they are described once under
Cross-Cutting → Audit and Record Lifecycle and are deliberately not repeated per entity (the Domain
Entities section preamble states this). While reading, also note any rule-bearing field — a
default, a required/optional constraint, a computed/derived value, an enum-state default — for the
derived-statements step.
Classify each entry:
- confident (
rule code entries) — the rule and its values/messages are unambiguous from the
source. You can write a finished, code-free EARS statement now. Choose the EARS pattern by
springboot-to-ears Step 4 priority (Event-Driven > State-Driven > Unwanted Behaviour > Optional
Ubiquitous > Complex); most validator rejections are Unwanted Behaviour
(If …, the <Domain> system shall reject …). Quote any i18n message verbatim. No IDs, no bullets,
no code references (Rule 2).
- confident structural (
structural code entries) — the class is located and its shape is
unambiguous. You can build a finished ### <Name> block now (full property/state table) plus any
rule-bearing fields as code-free statements. See the structural build/place rules below.
- ambiguous — a
rule code entry whose value/intent cannot be pinned from code alone
(config/profile-driven, seeded in Flyway/DB, depends on an external constant not locatable after
applying the Rule 9 gates), a structural entry whose class cannot be located or whose meaning
cannot be pinned, or any business entry. These become a [NEEDS REVIEW] Open Question with
both Rule 8 footers. For a business entry the *Where agent looked:* footer records whatever
context the | context: pointer yielded; if there was no pointer, it records that the entry is a
developer-supplied business decision with no code source.
Build the structural block (confident structural entries). Follow the reference-file shapes
exactly; each block is a content ### subsection, so it must carry its own > **Source files:**
annotation (Rule 14):
- entity → target
## Domain Entities and Properties:
### <Entity> → > **Source files:** <backticked path(s)> → a one-line description →
| Property | Type | Meaning | Constraints | table covering all properties (audit/soft-delete/
version fields excluded).
- concept → target
## Domain Concepts and States:
### <Concept> → > **Source files:** <backticked path(s)> → an optional one-line description →
**<Label>:** + | <Name> | Business Meaning | table, plus a | From State | To State | Triggered by | transitions table where the type encodes transitions.
Derive rule-bearing fields into statements. For every rule-bearing field noted during
investigation, additionally write a code-free EARS statement (Rule 2 prose, valid pattern, i18n quoted
verbatim) and place it in the correct module by Rule 7. Pure data-shape fields contribute only their
table row — no statement.
Resolve each entry's target location.
rule / confident → if the entry has an override (→ Module:), use it — unless that
module/subsection does not exist in the file, in which case fall back to the closest existing
subsection and note the substitution in the report. Otherwise pick the target by
springboot-to-ears Rule 7 placement (Validation Rules module for validator branches,
Sub-Validators for sub-validator rules, Async for side effects, etc.); use a subsection's
> **Source files:** annotation as a confirmation signal — the subsection whose listed source
files include the entry's pointer file is its natural home.
structural → the block targets ## Domain Entities and Properties (entity) or
## Domain Concepts and States (concept); its derived statements are placed by Rule 7 like any
rule entry. Both sections already exist in springboot-to-ears output, so adding a ### <Name>
subsection is not a new top-level section. If the relevant section is absent, downgrade the
entry to ambiguous (Open Question) rather than invent it.
Never invent a new top-level section. Ambiguous/business entries target ## Open Questions.
Step 4 — Phase 1: Write the DEV-REVIEW REPORT, then STOP at the approval gate
Write the report to docs/ears/<Domain>-FIX-DEV-REVIEWS-REPORT.md (overwrite if present). Use this
format:
# <Domain> — EARS Developer-Review Report
Source EARS: <ears-path>
Controllers in scope: <controller path(s), comma-separated>
Generated: <Day, DD Month YYYY>
## Summary
- Entries found (unchecked): <N> (code-rule: <Nr> · code-structural: <Ns> · business: <Nb>)
- Confident rule (→ EARS statement): <Gc>
- Confident structural (→ entity/concept block): <Gs>
- Ambiguous / business (→ Open Question): <Ga>
---
## DR-001 [code · rule · confident]
Entry: `BufferLoanProposalValidatorImpl.java#checkTenure` — tenure exceeds product details max not captured
Target: Module: <…> › ### <…> (developer override / agent-chosen)
Source: src/main/java/.../BufferLoanProposalValidatorImpl.java:1820-1834
Code condition: if (dto.getTenure() > details.getMaxTenure()) throwError("<verbatim message>")
Proposed EARS statement:
> If the requested loan tenure exceeds the maximum tenure configured on the loan product details,
> the <Domain> system shall reject the request with the message "<verbatim message>".
## DR-002 [code · entity · confident]
Entry: `GuarantorDto.java` — Guarantor entity missing from the spec
Target: ## Domain Entities and Properties › ### Guarantor (new block)
Source: lib/shared-dto/src/main/java/smartmf/shared/dto/GuarantorDto.java
Proposed entity block:
> ### Guarantor
> > **Source files:** `lib/shared-dto/src/main/java/smartmf/shared/dto/GuarantorDto.java`
> A guarantor attached to the loan proposal from the member's record.
> | Property | Type | Meaning | Constraints |
> |----------|------|---------|-------------|
> | <… every property, audit/version fields excluded …> | | | |
Proposed derived statement(s) (rule-bearing fields only):
> When a new loan proposal is submitted, the <Domain> system shall … → Module: <…> › ### <…>
## DR-003 [business · open question]
Entry: Should auto-disbursement be blocked for migration loans during month-end freeze?
Target: Open Questions
Why open: developer-supplied business decision; no single code source determines the intended policy.
Proposed Open Question:
> N. **[NEEDS REVIEW]** <concrete question a domain expert can answer in 1–2 sentences>
> *Where agent looked:* `<context-file>:<line>` — <what was read> (or: developer-supplied business decision, no code source).
> *Hint for reviewer:* Likely answer in `<probable/file>` (<reasoning>). Try `<grep/find/graphify command>`.
…
Then print a stdout summary (≤ 12 lines): the entry counts (code-rule vs code-structural vs
business), and the confident-rule / confident-structural / ambiguous split. Do not edit the EARS
file in this phase. End the turn by asking the user to
review docs/ears/<Domain>-FIX-DEV-REVIEWS-REPORT.md and reply with exactly one of:
apply all — apply every entry in the report;
apply DR-003 DR-007 … — apply only the listed entries;
abort — make no changes.
Phase 2 runs only after the user gives one of these in the conversation.
Step 5 — Phase 2: Apply the Approved Entries
Run only when the user has approved (all or a subset). Apply only approved entries. Mirror the
surgical editing discipline of resolve-open-questions (Step 5–6):
- confident rule entry → insert the proposed statement as a standalone prose paragraph (blank-line
separated) into its target
### subsection, in springboot-to-ears Rule 2 style: complete
sentence, subject the <Domain> system, no ID, no bullet, no code reference, i18n quoted
verbatim. Place it logically (e.g. after related rejection paragraphs in the same subsection).
Then, annotation upkeep (Rule 14): if the finding's source file is not already listed in that
subsection's > **Source files:** blockquote, append it (comma-separated, backticked,
project-relative, preserving existing order). Touch only the target subsection's annotation;
never reorder or rewrite existing entries; never add a > **Source files:** line to a section that
lacks one.
- confident structural entry → insert the proposed
### <Name> block into its target section
(## Domain Entities and Properties for entity, ## Domain Concepts and States for concept),
placed logically (after related blocks) or appended at the section end. The block carries its own
> **Source files:** annotation as built in Step 3. Then insert each derived statement into
its module exactly like a confident rule entry above, applying the same per-subsection annotation
upkeep for each.
- ambiguous / business entry → append a new numbered item to
## Open Questions, continuing the
existing numbering, with **[NEEDS REVIEW]** and both mandatory footers (*Where agent looked:* and *Hint for reviewer:*) exactly as springboot-to-ears Rule 8 requires, so
/resolve-open-questions can act on it next. (Open-Question entries touch no > **Source files:**
annotation.)
- Check off and annotate the entry (the audit feature). After the statement/block/Open Question
is in place, edit the originating review-section line: flip
- [ ] → - [x] and append, on a
continuation line indented under it, the resolution annotation:
- confident rule →
→ resolved: Module: <Module> › <Subsection> (EARS line <N>)
- confident structural →
→ resolved: <Domain Entities|Domain Concepts> › <Name> (EARS line <N>) and, on further continuation lines, one → also: Module: <Module> › <Subsection> (EARS line <N>) per derived statement
- ambiguous/business →
→ resolved: Open Question <N> (EARS line <M>)
Compute the EARS line number after the edit lands (so it points at the inserted paragraph,
block, or new Open Question). When applying several entries, apply edits one at a time and recompute
line numbers so earlier insertions don't make later annotations stale. The (EARS line N) is a
convenience snapshot — it may drift if the file is later hand-edited.
- Refresh
## Extraction Summary (identical mechanics to resolve-open-questions Step 6):
recount EARS statements by leading word — If →Unwanted, When /After →Event-Driven,
While →State-Driven, Where →Optional, The <Domain> system shall →Ubiquitous,
While … when →Complex — and recount the marker rows ([NEEDS REVIEW], [DISABLED],
[UNRESOLVED]). Overwrite only the right-hand count cells; the Total EARS statements row must
equal the sum of the pattern rows. Update the **Extraction completed:** line to today's date in
<Day, DD Month YYYY> format. If the file has no ## Extraction Summary, skip this. A new
entity/concept block adds no EARS statement, so it does not move the pattern counts; its
derived statements do, and are counted by their leading word like any other statement.
Print a stdout summary (≤ 12 lines): statements added (per target module), entity/concept blocks
added, Open Questions added, entries checked off, entries skipped because the user did not approve
them.
Step 6 — Idempotency & Edge Cases
| Case |
Behaviour |
| Re-run after applying |
A - [x] entry is skipped in Step 2. Phase 1 is a no-op for already-applied entries; only - [ ] entries are processed. |
Pointer's file/method can't be found (after find/graphify) |
Treat the entry as ambiguous: propose a [NEEDS REVIEW] Open Question whose *Where agent looked:* footer states the failed search verbatim. Never crash, never silently drop. |
→ Module: override names a module/subsection that doesn't exist |
Place in the closest existing subsection and note the substitution in the report; never add a new top-level ## Module: section. |
| Note-only (smart) entry can't be pinned confidently |
Becomes an ambiguous [NEEDS REVIEW] Open Question — never a guessed statement. |
| Malformed entry (no recognisable pointer, note, or decision) |
Report it under its own DR id as [unparseable], leave it unchecked, and apply nothing for it. |
| Same rule referenced by two entries |
Investigate once; in Phase 2 insert the statement once and annotate both entries pointing at the same EARS line. |
| Structural entry: class can't be located, or its shape/meaning can't be pinned |
Treat as ambiguous — propose a [NEEDS REVIEW] Open Question; never emit a guessed/partial block. |
Structural entry: target section (## Domain Entities and Properties / ## Domain Concepts and States) is absent |
Downgrade to ambiguous (Open Question); never invent the top-level section. |
Structural entry: a ### <Name> block already exists for it |
Not structural — re-handle as a rule entry (a missing field/rule on the existing block); never insert a duplicate block. |
| User approves a subset |
Apply exactly those DR ids; leave the rest reported but unchecked. |
| Unsafe shell during investigation (paths outside repo, destructive flags) |
Never run it. Find another lead, or mark the entry ambiguous. |
Step 7 — Self-Review Checklist (before ending each phase)
Phase 1 (report):
Phase 2 (fix):
1---2name: fix-dev-reviews3description: Fourth-pass closer for an EARS specification file produced by /springboot-to-ears. Consumes the two developer-review sections a human filled in directly in the spec — `## Review by Developer (code)` (source-code pointers to rules — or whole entities/enums — missing from the spec) and `## Review by Developer (business requirements)` (business decisions needing a human answer). For each unchecked entry it investigates the referenced code, writes a reviewable report, and — only after the user approves — folds confident findings in as code-free EARS statements, adds any missing entity/enum-state as a new `### <Name>` block (with all properties) in Domain Entities / Domain Concepts, and turns ambiguous findings and all business-requirement entries into [NEEDS REVIEW] Open Questions, then checks the entry off and annotates where it landed (module/subsection, entity/concept block, or Open Question) plus the EARS line number. Each `## Module:` and content `### Subsection` in the input carries a `> **Source files:**` 4---56# Fix Developer Reviews Skill (apply human review findings to an EARS file)78You are closing the gap between what a **developer found by reviewing an EARS file against the9codebase** and the EARS specification itself. After `/springboot-to-ears` produced the spec, a10developer read it next to the source and recorded findings the extraction missed — directly in the11EARS file, inside two dedicated sections:1213- `## Review by Developer (code)` — backticked **source-code pointers** to things that exist in the14 code but are missing from the spec (file, `file#method`, or `file:line-range`), each with an15 optional note and an optional `→ Module:` placement override. A code entry asserts either that a16 **rule** is missing (→ a new EARS statement) or that a whole **entity / enum-state** is missing17 (→ a new `### <Name>` block in Domain Entities / Domain Concepts, plus any rule-bearing fields as18 statements).19- `## Review by Developer (business requirements)` — **business decisions/requirements** the20 developer wants captured that need a human answer (no code expected); optional `| context:`21 pointer and optional `→ Module:` override.2223This skill reads those two sections, investigates each unchecked entry, proposes resolutions, and —24**only after the user approves** — applies them and marks each entry done. The EARS **statements**25stay code-free (springboot-to-ears Rule 2): no inline citations, no class/method names in statement26prose. File paths do, however, legitimately appear in the body as the per-heading27`> **Source files:**` blockquote annotation that every `## Module:` and content `### Subsection`28carries (springboot-to-ears Rule 14) — this skill **preserves** those annotations, consults them as29a supplemental locate hint, and keeps them **accurate** when it inserts new content. The developer's30source pointers themselves live only in the review sections (developer input) and in this skill's31report file.3233This skill does **two tasks in two gated phases**:34351. **Find** — parse both review sections, investigate each unchecked entry against the code (driven36 by its pointer), and write a `DEV-REVIEW REPORT` proposing a resolution per entry. Then **STOP**37 and wait for the user.382. **Fix** — only on the user's approval, apply the approved entries to the EARS file (confident rule39 findings as new code-free EARS statements; confident **structural** findings as a new40 `### <Name>` block — full property/state table — in Domain Entities / Domain Concepts, plus any41 rule-bearing fields as statements; ambiguous findings and all business-requirement entries as42 `[NEEDS REVIEW]` Open Questions), check each applied entry off (`- [ ]` → `- [x]`) with a43 `→ resolved:` annotation that names the landing location **and the EARS line number**, then44 refresh the Extraction Summary.4546**Scope is developer-seeded only.** This skill acts on the entries the developer wrote in the two47review sections — nothing else. It does **not** re-scan the whole controller to discover new gaps48(that is `ears-gap-fix`), and it does **not** report orphan EARS statements or value mismatches.4950**Companion skills (all in this project's `.claude/skills/`):**51- `springboot-to-ears` — produced the EARS file and emits the two empty review-section placeholders.52 This skill reuses its output rules by reference; read53 `.claude/skills/springboot-to-ears/SKILL.md` for the cited Rules/Steps/Procedures.54- `ears-gap-fix` — the agent-discovered gap closer. This skill reuses its **Step 1 gates** and its55 **Step 2a large-file extraction protocol** by reference; read56 `.claude/skills/ears-gap-fix/SKILL.md`.57- `resolve-open-questions` — the breadcrumb resolver. Run it after this skill's Phase 2 to chase any58 new `[NEEDS REVIEW]` Open Questions the business-requirement entries produced. This skill reuses59 its Extraction-Summary refresh mechanics (its Step 6) by reference.6061Never read or rely on skills outside this repository.6263---6465## Step 1 — Invocation Contract & Validation Gates6667**Invocation:**6869```70/fix-dev-reviews <path-to-EARS-file.md> [<Controller.java> ...]71```7273The first (and only required) argument is the EARS file. Any trailing arguments are controller74source files passed as an **optional override** for the controller set (used only to scope smart75auto-locate, Step 3); when supplied, they replace the header-parsed set entirely.7677**Resolve the controller set** exactly as `ears-gap-fix` Step 1 does: if controller args were78passed, that list is the set; otherwise read the `> **Source Controller(s):**` line in the EARS79header preamble and extract every backtick-wrapped project-relative path. Never scan the repo to80guess a controller.8182Validate before doing any work and exit early with the matching message:8384| Condition | Exit message |85|-----------|--------------|86| No arguments at all | `Usage: /fix-dev-reviews <path-to-EARS-file.md> [<Controller.java> ...]` |87| EARS path missing or not a file | `EARS file not found: <path>` |88| EARS path is not a `.md` file | `Expected a Markdown EARS file; got <path>` |89| EARS file has no `## Module:` section AND no `## Extraction Summary` | `<path> does not look like a /springboot-to-ears output (no Module / Extraction Summary section) — nothing to do.` |90| Neither `## Review by Developer (code)` nor `## Review by Developer (business requirements)` section exists | `<path> has no Review-by-Developer sections — run /springboot-to-ears to add the placeholders, or add the sections manually.` |91| Both review sections exist but contain zero entries, or every entry in both is already `- [x]` | `No unprocessed Review-by-Developer entries in <path> — nothing to do.` |92| A resolved controller path (header or args) does not exist on disk — *only checked if a note-only / smart-locate entry needs it* | `Controller file not found: <path>` |9394When the gates pass, read the EARS file end-to-end once so you know which `## Module:` / `###`95subsections exist (including the `## Domain Entities and Properties` and `## Domain Concepts and96States` reference sections and the `### <Name>` blocks already under them) and where the two review97sections sit. Note that each `## Module:` heading and every content `### Subsection` carries a98`> **Source files:**` blockquote line (springboot-to-ears Rule 14); **record those lists** — they99are a supplemental locate hint (Step 3), a placement cross-check (Step 3), and the annotation-upkeep100target (Step 5). Derive `<Domain>` from the EARS title (e.g. `# Loan Proposal — OTC … (EARS)` →101`Loan Proposal — OTC`).102103---104105## Step 2 — Phase 1: Parse Both Review Sections106107Within each review section, every list item matching `^- \[[ x]\] ` is one entry. **Skip every entry108already marked `- [x]`** (done on a prior run — do not re-investigate). For each **unchecked**109(`- [ ]`) entry, build a record:110111| Field | Source |112|-------|--------|113| `section` | `code` or `business` |114| `pointer` | the backticked code pointer (code section) or the `| context:` pointer (business section), if any. One of: `File.java`, `File.java#method`, `File.java:start-end`, or **none** |115| `note` | the free-text after the pointer / the decision sentence |116| `override` | the module/subsection named after a trailing `→ Module: <Module> › <Subsection>`, if present |117| `kind` | (code entries only) `rule` or `structural` — see the classification below; `structural` is further sub-typed `entity` or `concept` |118119Multi-line entries are allowed (the developer may wrap a note); the entry runs until the next120`- [ ]` / `- [x]` line or the next `## ` heading. Record entries in document order. Number them121`DR-001`, `DR-002`, … for the report.122123A **code** entry asserts one of two things; classify each by **note wording with a class-shape124fallback**:125126- **rule** (default) — "a real rule lives here and is missing from the spec." Produces a new EARS127 statement.128- **structural** — "a whole entity or enum/state is missing from the spec." Treat a code entry as129 `structural` when **either** the note signals a missing entity / model / DTO or enum / state /130 concept (words like "entity", "model", "DTO", "enum", "status", "states", "add … with its131 fields/properties"), **or** the `pointer` resolves to a class whose shape is structural — a JPA132 `@Entity`, a `*Dto`/record/POJO that is just fields + accessors, or an `enum` / state type — **and**133 no `### <Name>` block for it already exists under `## Domain Entities and Properties` /134 `## Domain Concepts and States`. Sub-type it `entity` (data class / `@Entity` / `*Dto`) or135 `concept` (enum / state type). If a matching `### <Name>` block already exists, it is **not**136 structural — handle it as a `rule` entry (a missing field/rule on an existing block), never a137 duplicate block.138139A **business** entry asserts "this decision/requirement must be captured", is never structural140(structure is code-discoverable), and is always destined for a `[NEEDS REVIEW]` Open Question.141142---143144## Step 3 — Phase 1: Investigate Each Entry145146Drive the investigation off the entry's `pointer`. **Reuse `ears-gap-fix` Step 2a (the large-file147structural-index + rule-site-ledger protocol) and the `springboot-to-ears` "named-not-read"148prohibition (Step 2i) — do not invent a parallel method.**149150| Pointer form | How to investigate |151|--------------|--------------------|152| `File.java#method` | Resolve the file path (`find . -name File.java` / graphify if bare). Open it, jump to that method, read its full body and every private helper / injected sub-validator it delegates to (recursively, Procedure A-3/A-4). |153| `File.java:start-end` | Read that line span plus the enclosing method, then any helpers it calls. |154| `File.java` (whole file) | Run the `ears-gap-fix` Step 2a protocol on the whole file: `wc -l`, build the rule-site map, read by method range, keep a ledger. |155| none (**smart auto-locate**) | Use `graphify query "<note nouns>"` / `grep` keyed on the note's distinctive nouns and any quoted message, with the resolved controller set's call chain as the **primary** scope. If the entry has a `→ Module:` override (or its placement is otherwise obvious), additionally consult that section's `> **Source files:**` list as a **supplemental hint** for which files to read first. Read the matched method fully. |156157For each entry capture, where applicable: the exact `file:line(-range)`, the precise code condition158/ trigger, the **verbatim** i18n message or string literal it produces (Rule 5), and the **exact159reference entity/field** that supplies the comparison value (Rule 4 — "loan product details" ≠160"loan product policy"). These are the anchors for the proposed statement and the report's proof.161162**For a `structural` entry, investigate the whole class instead of a single rule-site.** Resolve and163read the pointed class **in full** (reuse `ears-gap-fix` Step 2a large-file protocol if it is large;164obey the `springboot-to-ears` "named-not-read" prohibition — never describe a field you did not165read). Capture **every** persisted/serialised field (entity) or **every** value/state and transition166(enum/state). Map each to **business terms** per `springboot-to-ears` Rule 4 (code-free naming):167property names like "Proposed loan amount" — never `proposedLoanAmount`; Types in business terms168(Amount / Text / Number / Boolean / Date / Reference / Enum / List), never Java types. **Exclude** the169shared audit, soft-delete, and optimistic-locking fields — they are described once under170Cross-Cutting → Audit and Record Lifecycle and are deliberately not repeated per entity (the Domain171Entities section preamble states this). While reading, also note any **rule-bearing** field — a172default, a required/optional constraint, a computed/derived value, an enum-state default — for the173derived-statements step.174175**Classify each entry:**176177- **confident** (`rule` code entries) — the rule and its values/messages are unambiguous from the178 source. You can write a finished, code-free EARS statement now. Choose the EARS pattern by179 `springboot-to-ears` Step 4 priority (Event-Driven > State-Driven > Unwanted Behaviour > Optional180 > Ubiquitous > Complex); most validator rejections are Unwanted Behaviour181 (`If …, the <Domain> system shall reject …`). Quote any i18n message verbatim. No IDs, no bullets,182 no code references (Rule 2).183- **confident structural** (`structural` code entries) — the class is located and its shape is184 unambiguous. You can build a finished `### <Name>` block now (full property/state table) plus any185 rule-bearing fields as code-free statements. See the structural build/place rules below.186- **ambiguous** — a `rule` code entry whose value/intent cannot be pinned from code alone187 (config/profile-driven, seeded in Flyway/DB, depends on an external constant not locatable after188 applying the Rule 9 gates), a `structural` entry whose class cannot be located or whose meaning189 cannot be pinned, **or** any **business** entry. These become a `[NEEDS REVIEW]` Open Question with190 **both** Rule 8 footers. For a business entry the `*Where agent looked:*` footer records whatever191 context the `| context:` pointer yielded; if there was no pointer, it records that the entry is a192 developer-supplied business decision with no code source.193194**Build the structural block (confident `structural` entries).** Follow the reference-file shapes195exactly; each block is a content `### subsection`, so it **must** carry its own `> **Source files:**`196annotation (Rule 14):197198- **entity** → target `## Domain Entities and Properties`:199 `### <Entity>` → `> **Source files:** <backticked path(s)>` → a one-line description →200 `| Property | Type | Meaning | Constraints |` table covering **all** properties (audit/soft-delete/201 version fields excluded).202- **concept** → target `## Domain Concepts and States`:203 `### <Concept>` → `> **Source files:** <backticked path(s)>` → an optional one-line description →204 `**<Label>:**` + `| <Name> | Business Meaning |` table, plus a `| From State | To State | Triggered205 by |` transitions table where the type encodes transitions.206207**Derive rule-bearing fields into statements.** For every rule-bearing field noted during208investigation, additionally write a code-free EARS statement (Rule 2 prose, valid pattern, i18n quoted209verbatim) and place it in the correct module by Rule 7. Pure data-shape fields contribute only their210table row — no statement.211212**Resolve each entry's target location.**213- `rule` / `confident` → if the entry has an `override` (`→ Module:`), use it — unless that214 module/subsection does not exist in the file, in which case fall back to the closest existing215 subsection and note the substitution in the report. Otherwise pick the target by216 `springboot-to-ears` Rule 7 placement (Validation Rules module for validator branches,217 Sub-Validators for sub-validator rules, Async for side effects, etc.); use a subsection's218 `> **Source files:**` annotation as a confirmation signal — the subsection whose listed source219 files include the entry's pointer file is its natural home.220- `structural` → the block targets `## Domain Entities and Properties` (entity) or221 `## Domain Concepts and States` (concept); its derived statements are placed by Rule 7 like any222 rule entry. Both sections already exist in `springboot-to-ears` output, so adding a `### <Name>`223 subsection is **not** a new top-level section. **If the relevant section is absent, downgrade the224 entry to ambiguous** (Open Question) rather than invent it.225226**Never invent a new top-level section.** Ambiguous/business entries target `## Open Questions`.227228---229230## Step 4 — Phase 1: Write the DEV-REVIEW REPORT, then STOP at the approval gate231232Write the report to `docs/ears/<Domain>-FIX-DEV-REVIEWS-REPORT.md` (overwrite if present). Use this233format:234235```236# <Domain> — EARS Developer-Review Report237238Source EARS: <ears-path>239Controllers in scope: <controller path(s), comma-separated>240Generated: <Day, DD Month YYYY>241242## Summary243- Entries found (unchecked): <N> (code-rule: <Nr> · code-structural: <Ns> · business: <Nb>)244- Confident rule (→ EARS statement): <Gc>245- Confident structural (→ entity/concept block): <Gs>246- Ambiguous / business (→ Open Question): <Ga>247248---249250## DR-001 [code · rule · confident]251Entry: `BufferLoanProposalValidatorImpl.java#checkTenure` — tenure exceeds product details max not captured252Target: Module: <…> › ### <…> (developer override / agent-chosen)253Source: src/main/java/.../BufferLoanProposalValidatorImpl.java:1820-1834254Code condition: if (dto.getTenure() > details.getMaxTenure()) throwError("<verbatim message>")255Proposed EARS statement:256> If the requested loan tenure exceeds the maximum tenure configured on the loan product details,257> the <Domain> system shall reject the request with the message "<verbatim message>".258259## DR-002 [code · entity · confident]260Entry: `GuarantorDto.java` — Guarantor entity missing from the spec261Target: ## Domain Entities and Properties › ### Guarantor (new block)262Source: lib/shared-dto/src/main/java/smartmf/shared/dto/GuarantorDto.java263Proposed entity block:264> ### Guarantor265> > **Source files:** `lib/shared-dto/src/main/java/smartmf/shared/dto/GuarantorDto.java`266> A guarantor attached to the loan proposal from the member's record.267> | Property | Type | Meaning | Constraints |268> |----------|------|---------|-------------|269> | <… every property, audit/version fields excluded …> | | | |270Proposed derived statement(s) (rule-bearing fields only):271> When a new loan proposal is submitted, the <Domain> system shall … → Module: <…> › ### <…>272273## DR-003 [business · open question]274Entry: Should auto-disbursement be blocked for migration loans during month-end freeze?275Target: Open Questions276Why open: developer-supplied business decision; no single code source determines the intended policy.277Proposed Open Question:278> N. **[NEEDS REVIEW]** <concrete question a domain expert can answer in 1–2 sentences>279> *Where agent looked:* `<context-file>:<line>` — <what was read> (or: developer-supplied business decision, no code source).280> *Hint for reviewer:* Likely answer in `<probable/file>` (<reasoning>). Try `<grep/find/graphify command>`.281282…283```284285Then print a stdout summary (≤ 12 lines): the entry counts (code-rule vs code-structural vs286business), and the confident-rule / confident-structural / ambiguous split. **Do not edit the EARS287file in this phase.** End the turn by asking the user to288review `docs/ears/<Domain>-FIX-DEV-REVIEWS-REPORT.md` and reply with exactly one of:289290- `apply all` — apply every entry in the report;291- `apply DR-003 DR-007 …` — apply only the listed entries;292- `abort` — make no changes.293294Phase 2 runs only after the user gives one of these in the conversation.295296---297298## Step 5 — Phase 2: Apply the Approved Entries299300Run only when the user has approved (all or a subset). Apply **only** approved entries. Mirror the301surgical editing discipline of `resolve-open-questions` (Step 5–6):302303- **confident rule entry** → insert the proposed statement as a standalone prose paragraph (blank-line304 separated) into its target `###` subsection, in `springboot-to-ears` Rule 2 style: complete305 sentence, subject `the <Domain> system`, no ID, no bullet, no code reference, i18n quoted306 verbatim. Place it logically (e.g. after related rejection paragraphs in the same subsection).307 Then, **annotation upkeep (Rule 14):** if the finding's source file is not already listed in that308 subsection's `> **Source files:**` blockquote, append it (comma-separated, backticked,309 project-relative, preserving existing order). Touch **only** the target subsection's annotation;310 never reorder or rewrite existing entries; never add a `> **Source files:**` line to a section that311 lacks one.312- **confident structural entry** → insert the proposed `### <Name>` block into its target section313 (`## Domain Entities and Properties` for entity, `## Domain Concepts and States` for concept),314 placed logically (after related blocks) or appended at the section end. The block carries its own315 `> **Source files:**` annotation as built in Step 3. Then insert each **derived statement** into316 its module exactly like a confident rule entry above, applying the same per-subsection annotation317 upkeep for each.318- **ambiguous / business entry** → append a new numbered item to `## Open Questions`, continuing the319 existing numbering, with `**[NEEDS REVIEW]**` and **both** mandatory footers (`*Where agent320 looked:*` and `*Hint for reviewer:*`) exactly as `springboot-to-ears` Rule 8 requires, so321 `/resolve-open-questions` can act on it next. (Open-Question entries touch no `> **Source files:**`322 annotation.)323- **Check off and annotate the entry (the audit feature).** After the statement/block/Open Question324 is in place, edit the originating review-section line: flip `- [ ]` → `- [x]` and append, on a325 continuation line indented under it, the resolution annotation:326 - confident rule → ` → resolved: Module: <Module> › <Subsection> (EARS line <N>)`327 - confident structural → ` → resolved: <Domain Entities|Domain Concepts> › <Name> (EARS line <N>)` and, on further continuation lines, one ` → also: Module: <Module> › <Subsection> (EARS line <N>)` per derived statement328 - ambiguous/business → ` → resolved: Open Question <N> (EARS line <M>)`329 Compute the EARS line number **after** the edit lands (so it points at the inserted paragraph,330 block, or new Open Question). When applying several entries, apply edits one at a time and recompute331 line numbers so earlier insertions don't make later annotations stale. The `(EARS line N)` is a332 convenience snapshot — it may drift if the file is later hand-edited.333- **Refresh `## Extraction Summary`** (identical mechanics to `resolve-open-questions` Step 6):334 recount EARS statements by leading word — `If `→Unwanted, `When `/`After `→Event-Driven,335 `While `→State-Driven, `Where `→Optional, `The <Domain> system shall `→Ubiquitous,336 `While … when `→Complex — and recount the marker rows (`[NEEDS REVIEW]`, `[DISABLED]`,337 `[UNRESOLVED]`). Overwrite only the right-hand count cells; the `Total EARS statements` row must338 equal the sum of the pattern rows. Update the `**Extraction completed:**` line to today's date in339 `<Day, DD Month YYYY>` format. If the file has no `## Extraction Summary`, skip this. A new340 entity/concept **block** adds no EARS statement, so it does not move the pattern counts; its341 **derived statements** do, and are counted by their leading word like any other statement.342343Print a stdout summary (≤ 12 lines): statements added (per target module), entity/concept blocks344added, Open Questions added, entries checked off, entries skipped because the user did not approve345them.346347---348349## Step 6 — Idempotency & Edge Cases350351| Case | Behaviour |352|------|-----------|353| Re-run after applying | A `- [x]` entry is skipped in Step 2. Phase 1 is a no-op for already-applied entries; only `- [ ]` entries are processed. |354| Pointer's file/method can't be found (after `find`/graphify) | Treat the entry as **ambiguous**: propose a `[NEEDS REVIEW]` Open Question whose `*Where agent looked:*` footer states the failed search verbatim. Never crash, never silently drop. |355| `→ Module:` override names a module/subsection that doesn't exist | Place in the closest existing subsection and note the substitution in the report; never add a new top-level `## Module:` section. |356| Note-only (smart) entry can't be pinned confidently | Becomes an **ambiguous** `[NEEDS REVIEW]` Open Question — never a guessed statement. |357| Malformed entry (no recognisable pointer, note, or decision) | Report it under its own DR id as `[unparseable]`, leave it **unchecked**, and apply nothing for it. |358| Same rule referenced by two entries | Investigate once; in Phase 2 insert the statement once and annotate **both** entries pointing at the same EARS line. |359| Structural entry: class can't be located, or its shape/meaning can't be pinned | Treat as **ambiguous** — propose a `[NEEDS REVIEW]` Open Question; never emit a guessed/partial block. |360| Structural entry: target section (`## Domain Entities and Properties` / `## Domain Concepts and States`) is absent | Downgrade to **ambiguous** (Open Question); never invent the top-level section. |361| Structural entry: a `### <Name>` block already exists for it | Not structural — re-handle as a `rule` entry (a missing field/rule on the existing block); never insert a duplicate block. |362| User approves a subset | Apply exactly those DR ids; leave the rest reported but unchecked. |363| Unsafe shell during investigation (paths outside repo, destructive flags) | Never run it. Find another lead, or mark the entry ambiguous. |364365---366367## Step 7 — Self-Review Checklist (before ending each phase)368369**Phase 1 (report):**370- [ ] Every unchecked entry in **both** sections has a DR item; every `- [x]` entry was skipped.371- [ ] Every confident item has a `file:line` source proof and a resolved target location; placements372 were cross-checked against target subsections' `> **Source files:**` lists where available.373- [ ] Every confident rule item's proposed statement is code-free, uses a valid EARS pattern, and374 quotes any i18n message verbatim (Rules 2, 4, 5).375- [ ] Every confident **structural** item's proposed block covers **all** properties/states (audit/376 soft-delete/version fields excluded), carries a `> **Source files:**` annotation, names a valid377 target section, and lists any rule-bearing fields as derived statements.378- [ ] Every ambiguous/business item's proposed Open Question carries **both** Rule 8 footers.379- [ ] No new top-level section was proposed; the EARS file was **not** modified; the report was380 written; the approval prompt was shown.381382**Phase 2 (fix):**383- [ ] Only user-approved entries were applied.384- [ ] New statements are plain paragraphs in existing subsections (no IDs/bullets/code refs); new385 entity/concept blocks match the reference shape and carry their `> **Source files:**` line; new386 Open Questions are well-formed with both footers.387- [ ] Every applied statement's source file is present in its target subsection's `> **Source files:**`388 line (appended if it was missing); **no other** `> **Source files:**` annotation was modified,389 removed, or reordered.390- [ ] Each structural block covers all properties/states with audit/soft-delete/version fields391 excluded; its derived statements are code-free, rule-bearing only, and correctly placed.392- [ ] Every applied entry was flipped to `- [x]` and annotated with `→ resolved: …` **and** an393 accurate `(EARS line N)` computed after the edit (structural entries also name each derived394 statement's location).395- [ ] No new top-level sections were invented; the two review sections themselves were not396 restructured (only entries checked off + annotated).397- [ ] `## Extraction Summary` counts sum correctly and `**Extraction completed:**` is today's date.398- [ ] The diff is additive and surgical — only new statements/blocks/Open Questions, the399 source-file annotation append, the entry check-off + annotation, and the Summary refresh400 changed.