Skip to content

feat(review): add sin-code review --complexity subcommand with ponytail's 5-tag format (delete/stdlib/native/yagni/shrink) #179

Description

@Delqhi

Summary

Erstelle sin-code review --complexity als neues Subcommand und einen neuen Reviewer-Subagent im Orchestrator (Issue #174), basierend auf https://github.com/DietrichGebert/ponytail/blob/main/skills/ponytail-review/SKILL.md:1-50.

Output-Format (1:1 von ponytail-review):

L<line>: <tag> <what>. <replacement>.

Tags:

  • delete: – dead code, unused flexibility, speculative feature
  • stdlib: – hand-rolled thing the standard library ships
  • native: – dependency or code doing what the platform already does
  • yagni: – abstraction with one implementation
  • shrink: – same logic, fewer lines

Endet mit: net: -<N> lines possible.

Motivation

ponytail's ponytail-review Skill (https://github.com/DietrichGebert/ponytail/blob/main/skills/ponytail-review/SKILL.md:1-50) ist das Beispiel für effektive Code-Reviews:

Review diffs for unnecessary complexity. One line per finding: location, what to cut, what replaces it. The diff's best outcome is getting shorter.

Format-Beispiele (aus ponytail):

L12-38: stdlib: 27-line validator class. "@" in email, 1 line, real validation is the confirmation mail.
L4: native: moment.js imported for one format call. Intl.DateTimeFormat, 0 deps.
repo.py:L88: yagni: AbstractRepository with one implementation. Inline it until a second one exists.
L52-71: delete: retry wrapper around an idempotent local call. Nothing replaces it.
L30-44: shrink: manual loop builds dict. dict(zip(keys, values)), 1 line.

Scoring:

End with the only metric that matters: net: -<N> lines possible.
If there is nothing to cut, say Lean already. Ship. and stop.

Boundaries (kritisch!):

Complexity only, correctness bugs, security holes, and performance go to a normal review pass, not this one. A single smoke test or assert-based self-check is the ponytail minimum, not bloat, never flag it for deletion. Does not apply the fixes, only lists them.

SIN-Code hat aktuell:

  • Critic (cmd/sin-code/internal/orchestrator/critic.go:1-101) – bounded verify→diagnose→retry. Output: Diagnosis (free-form).
  • Adversary (adversary.go:1-171) – attack-finding, output: CounterexampleBrief (free-form).
  • Keine dedizierte Complexity-Reviewsin-code review existiert nicht.

Current State in SIN-Code

Bereich Datei Was da ist
Critic cmd/sin-code/internal/orchestrator/critic.go:1-101 CriticResult.Attempts[].Diagnose (free-form)
Adversary cmd/sin-code/internal/orchestrator/adversary.go:1-171 CounterexampleBrief() (free-form)
Subagent Contracts (geplant) Issue #174 Generic path:line format
Symbol Search cmd/sin-code/internal/scout/ scout.go:1+
Code Understanding cmd/sin-code/internal/grasp/ grasp.go:1+
Intent-Based Diffing cmd/sin-code/internal/ibd/ ibd.go:1+
CLI cmd/sin-code/review_cmd.go existiert NICHT (nur cmd/sin-code/gh_cmd.go für GitHub-PR-Reviews)

Was fehlt: Ein dedizierter Complexity-Review mit dem ponytail-Output-Format.

Was ponytail genau macht

Aus https://github.com/DietrichGebert/ponytail/blob/main/skills/ponytail-review/SKILL.md:

Format:

L<line>: <tag> <what>. <replacement>.

Tags (5 Stück):

  • delete: – dead code, unused flexibility, speculative feature. Replacement: nothing.
  • stdlib: – hand-rolled thing the standard library ships. Name the function.
  • native: – dependency or code doing what the platform already does. Name the feature.
  • yagni: – abstraction with one implementation, config nobody sets, layer with one caller.
  • shrink: – same logic, fewer lines. Show the shorter form.

Scoring: Endet mit net: -<N> lines possible. oder Lean already. Ship.

Boundaries: Complexity only, correctness/security/perf gehen in den normalen Review-Pass. ponytail:-Marker werden respektiert (nicht gelöscht).

Detailed Implementation Plan

Phase 1 — Reviewer-Subagent (Issue #174 Ergänzung)

  1. Erstelle cmd/sin-code/internal/orchestrator/reviewer.go (neue Datei, analog zu critic.go):

    type ComplexityReviewer struct {
        Scout  Scout  // symbol search
        Grasp  Grasp  // deep understanding
        IBD    IBD    // intent-based diffing
        LLM    LLM    // for the LLM-judge
    }
    type ComplexityFinding struct {
        Line       int
        Path       string
        Tag        string  // "delete" | "stdlib" | "native" | "yagni" | "shrink"
        Problem    string
        Replacement string
    }
    type ComplexityResult struct {
        Findings   []ComplexityFinding
        NetLines   int
        Status     string  // "lean" | "cuts-available" | "needs-review"
    }
    func (r *ComplexityReviewer) Review(ctx, diff) (*ComplexityResult, error)
  2. Parser cmd/sin-code/internal/orchestrator/complexity_parser.go:

    var complexityRE = regexp.MustCompile(
        `^(?P<file>[^:]+(?:\.go|\.py|\.js)):?L?(?P<line>\d+)(?:-\d+)?: ` +
        `(?P<tag>delete|stdlib|native|yagni|shrink): ` +
        `(?P<problem>[^.]+)\. (?P<replacement>.+)\.$`)
    func ParseComplexityOutput(s string) ([]ComplexityFinding, error)
    • Mechanisch validierbar (regex-basiert)
    • Failure-Path: free-form Fallback + Warning
  3. LLM-Prompt (in reviewer.go):

    You are a complexity reviewer. For every finding, output exactly one line:
    L<line>: <tag> <what>. <replacement>.
    Tags: delete | stdlib | native | yagni | shrink
    
    If nothing to cut, output exactly: "Lean already. Ship."
    End with: "net: -<N> lines possible."
    

Phase 2 — CLI

  1. Erstelle cmd/sin-code/review_cmd.go (neue Datei):

    • sin-code review --complexity <file> – lokale Datei
    • sin-code review --complexity <PR-number> – GitHub-PR
    • sin-code review --complexity --diff <diff> – inline diff
    • sin-code review --complexity --since <git-ref> – seit Commit
    • sin-code review --complexity --format json – JSON-Output
    • Output-Format: ponytail-kompatibel (Text) oder JSON
  2. Integration mit cmd/sin-code/gh_cmd.go (Issue feat(skill): add skill-code-lazy bundled skill (SIN-Code variant of ponytail, respects M3 verify-first) #178 verbindet diese):

    • sin-code gh review --complexity <PR> ist ein Shortcut für GitHub-PRs

Phase 3 — Cross-Cutting Integration

  1. Integration mit Adversary (Issue feat(orchestrator): caveman-style output contracts for Critic/Adversary/Governor/Cartographer (path:line format, one-liner findings) #174):

    • Adversary findet Bugs: bug:-Tag im Output
    • Reviewer findet Complexity: delete:, yagni:, shrink:-Tags
    • Aggregator: cmd/sin-code/internal/orchestrator/aggregator.go kombiniert beide zu einem Bericht
  2. // sin-debt:-Marker-Integration (Issue feat(code): adopt ponytail's ponytail: marker convention as // sin-debt: <ceiling>, upgrade: <trigger> #177):

    • Wenn der Reviewer einen delete:-Tag hat, aber der Code einen sin-debt:-Marker mit Trigger → als "approved" markieren
    • Wenn der Reviewer einen delete:-Tag hat, aber kein Marker → als "neues Issue" markieren
  3. Token-Tracking (Issue feat(observability): persist LLM token usage, aggregate per session/lifetime, expose sin-code tokens + TUI badge #168):

    • llm_usage Ledger-Entry mit subagent: "reviewer-complexity", contract: "complexity"

Phase 4 — Tests

  1. Unit-Test complexity_parser_test.go:

    • 5 reale ponytail-Beispiele parsen
    • Lean already. Ship. → empty findings
    • Malformed Lines → Error
  2. Unit-Test reviewer_test.go:

  • Stub LLM mit bekannter Output → erwartete Findings
  • Net-Lines-Counter
  1. Integration-Test review_cmd_test.go:
  • sin-code review --complexity testdata/sample.diff → expected JSON
  • Round-Trip: text → parse → render
  1. Race-Test: go test -race -count=1 ./cmd/sin-code/internal/orchestrator/...

Phase 5 — Docs

  1. Erstelle docs/complexity-review.md: ponytail-Format, Tags, Boundaries.
  2. Update AGENTS.md §4 (Architecture): füge Reviewer-Subagent hinzu.
  3. Update BACKLOG.md: done.
  4. Update CHANGELOG.md Unreleased-Section.

Acceptance Criteria

  • cmd/sin-code/internal/orchestrator/reviewer.go existiert mit ComplexityReviewer
  • cmd/sin-code/internal/orchestrator/complexity_parser.go parst ponytail-Output mechanisch
  • cmd/sin-code/review_cmd.go existiert mit 5 Subcommands
  • Tags delete | stdlib | native | yagni | shrink werden korrekt erkannt
  • net: -<N> lines possible. wird korrekt aggregiert
  • // sin-debt:-Marker werden respektiert (nicht als delete: geflagt)
  • Lean already. Ship. führt zu leerer Findings-Liste
  • Integration mit Adversary funktioniert (zwei Subagents parallel)
  • docs/complexity-review.md existiert
  • Alle Tests passen mit -race
  • golangci-lint, govulncheck, gosec (SARIF) grün

Risk and Rollback

  • Risk: LLM produziert ungültiges Format (z. B. Markdown-Bullet statt Plain-Text). Mitigation: Parser loggt Warning, Review-Output enthält Fallback-Section "Free-form findings".
  • Risk: Adversary + Reviewer doppeln sich (z. B. beide finden dead code). Mitigation: Tags unterscheiden sich (bug: vs delete:). Aggregator sortiert nach Tag.
  • Risk: delete:-Findings werden blind umgesetzt. Mitigation: // sin-debt:-Marker werden respektiert. Andere delete:-Findings erfordern manuelles Sign-off im sin-code review --apply Flow (separater Issue).
  • Rollback: Revert PR. Kein Datenverlust.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions