Claude Skill

code-review

Use to judge a concrete diff, branch, or GitHub PR on its own merits with no rsc-SDD spec/plan chain to key off — the spec-less giving pass behind /code-review: only findings you can defend, one verdict, read-only unless --comment or --fix. NOT the SDD gate keyed to 02-DOCS/wiki/

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies

#code-review

Virus-scanned Reviewed automatically before listing.

Full trust report

Download ericrisco-rsc-harness-skills_code-review-953fef5.zip · 8 KB
Part of ericrisco/rsc-harness — 46 skills

Install

skills CLI npx skills add https://github.com/ericrisco/rsc-harness/tree/main/skills/code-review
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install ericrisco-rsc-harness@llmmart
Git git clone https://github.com/ericrisco/rsc-harness.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole ericrisco/rsc-harness collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Code review — standalone, spec-less diff judgment

You are reviewing a concrete change — a git diff, a branch, a GitHub PR, a pasted patch — on its own merits. No rsc-SDD spec/plan/constitution chain is required and you should not pretend one exists. This is the doctrine behind the executable /code-review slash command: same evidence bar, written as a discipline you run by hand. If the user is mid-SDD and wants to process incoming comments against 02-DOCS/wiki/sdd/, that is ../review/SKILL.md; a naked diff or an inbound third-party PR is this skill.

The north star is signal-to-noise. Report only findings you would stake your name on. A clean diff is APPROVE, not a manufactured nit. High-false-positive review gets tuned out by humans in about two weeks; the bar to aim for is the logic-error review where under 1% of findings come back marked wrong. Padding does not make you look thorough — it trains the reader to ignore you.

Get the change and its intent first

Three inputs, in this order: the diff, its stated purpose, and the touched surface (the files around the hunks, not just the hunks).

# A GitHub PR
gh pr diff 1432
gh pr view 1432 --json title,body,files,additions,deletions

# A local branch against the trunk
git diff main...HEAD
git diff --stat main...HEAD   # see blast radius before reading

# A pasted patch — read it as given

A review with no notion of intent is a review of vibes. If no purpose is stated, infer it from the diff and say what you assumed ("Assuming this is meant to add idempotency to the webhook handler…") so the reader can correct a wrong premise — a silent wrong premise produces a confidently wrong review. Then read the whole changed file, not just the green/red lines: the structural failure of standalone review is judging a hunk without its context and shipping generic pattern-matched suggestions.

The pass order

Run these in order. Passes 1–5 are correctness/safety and are blocking-eligible; pass 6 is cleanup and is usually [should-fix] or [nit]. A clean pass is a reportable result ("contracts: nothing changed shape, no finding"), not a pass you silently skip.

# Pass The question Typical defects
1 Intent fidelity Does it do what it claims? Wrong behaviour, missing case from the stated goal, scope creep
2 Correctness & boundaries Right on the edges? Off-by-one, null/empty/unicode, overflow, timezone, concurrency, swallowed errors
3 Contracts & data Do callers/data still hold? Broken API shape, migration without backfill, nullable made non-null, enum drift
4 Security boundary Untrusted input → dangerous sink? Unsanitized input to query/shell/template, authz gap, secret in code/log
5 Tests as evidence Do the tests prove the change? Tests assert nothing, test the mock, miss the new branch, were deleted to go green
6 Reuse / simplification / efficiency Could existing code do this? Reimplemented helper, copy-paste divergence, N+1, needless allocation in a hot loop

Pass 4 is a boundary pass — trace untrusted input to its sink and flag the reachable ones. For a real STRIDE/OWASP threat model with exploitability ranking and vulnerable→fixed diffs, hand off to ../secure-coding/SKILL.md.

Adjacent jobs, delegated by name: running the lint/type/test gates until they are green is ../verify/SKILL.md (this skill judges whether green is correct); root-causing one confirmed failure is ../debug/SKILL.md; cross-checking spec/plan/tasks before code exists is ../analyze/SKILL.md.

Confidence floor and the false-positive skip-list

The 80% rule: if you are not at least ~80% sure a finding is real, you have two moves — trace the code until you are sure, or downgrade it to [question]. Never ship a guess dressed as a defect.

Skip these common false positives outright (or demote to [question]/[nit]):

  • Guarded upstream — the "missing" check happens in the caller you can see; trace before you flag.
  • Framework-enforced — the framework already does it (e.g. an ORM that parameterizes, a router that validates).
  • Behind an off-everywhere flag — real but unreachable in any deployed config → [nit], not blocking.
  • Test-only / generated code held to the prod bar — don't demand prod-grade error handling in a fixture or a generated client.
  • Style the linter owns — quotes, import order, line length. If a tool enforces it, don't spend a finding on it.

No severity inflation. Rank by blast radius × reachability, not by how clever the catch was. A typo in a log string is a nit even if it took effort to spot.

Severity and finding format

  • [blocking] — wrong/unsafe; merging causes a real defect. Must be fixed.
  • [should-fix] — a real problem with bounded blast radius; fix it or consciously accept it.
  • [nit] — minor; the reader may ignore it without consequence.
  • [question] — you suspect an issue but cannot prove reachability; asking, not asserting.

Every finding carries where / why / repro / fix:

[should-fix] api/orders.py:88 — duplicated total logic
  where:  `subtotal = sum(i.price * i.qty for i in items)` re-implements
          `cart.compute_subtotal()` (cart/totals.py:14), which also applies
          per-item discounts this copy silently drops.
  why:    discounted items now bill at full price on this path only;
          the two implementations will drift on the next discount change.
  repro:  order containing any item with `discount_pct > 0` → charged the
          undiscounted amount; covered by no test.
  fix:    call `cart.compute_subtotal(items)` instead of inlining the sum.

Rule: no repro or stated mechanism → it is a [question], not a blocker. "This could overflow" with no path is a question; "n*1000 with n up to 3M exceeds int32 at orders.py:51" is a finding.

Verify before you flag

Read the surrounding code, trace the value, confirm the path is reachable.

  • Bad: "Looks like SQL injection." (pattern-match)
  • Good: "search() interpolates req.query.q straight into db.execute(\… WHERE name='$'`)at search.ts:22;q` is unvalidated user input → injection." (traced)

If you cannot trace it to a concrete value and a reachable sink, you do not yet have a finding.

The verdict

End every review with exactly one, plainly — no mushy middle:

  • APPROVE — no blockers, no should-fix. Point the user to ../ship/SKILL.md to merge.
  • APPROVE WITH NITS — mergeable; nits listed but none gate the merge.
  • CHANGES REQUESTED — at least one [blocking]. List precisely what unblocks it, so the author knows when they are done.

Effort dial

Mirror the slash command's effort level: low/medium → fewer, high-confidence findings (raise the confidence floor, focus on passes 1–4). high/max → broader coverage; uncertain findings are allowed but must be labelled [question], never inflated into blockers. This is coverage vs precision, not the harness accompaniment dial — it changes what you look at, not how much you narrate.

Emitting comments and applying fixes

Read-only by default. You produce findings + a verdict and stop there. Two opt-in modes:

  • --comment → post the findings as an inline-anchored review on the PR.
  • --fix → apply the agreed findings to the working tree.
# Summary review (the verdict)
gh pr review 1432 --request-changes -b "CHANGES REQUESTED — see inline. Blocker: orders.py:88 …"
gh pr review 1432 --approve -b "APPROVE — correctness and contracts clean."

Inline line-anchored comments go through the GitHub REST API — see references/pr-workflow.md for the JSON shape, fork-PR handling, and large-diff strategy. If --fix puts you on the default branch, branch first; commit or push only when the user asks; git authorship is Eric (no Claude co-author or generated footer).

Anti-patterns

Failure mode Reality
"It compiles and the tests pass, so it's correct." Tests prove green, not correct. Pass 5 asks whether the tests actually exercise the new branch — green for the wrong reason is a finding.
Listing everything you would have done differently. That is noise. Report defects and reuse wins you can defend; preference is not a finding.
"It's just a dependency bump, skim it." Bumps carry supply-chain and transitive risk and behaviour changes. Check the changelog/lockfile diff, not just the version string.
Applying every nit "to be safe" under --fix. Each unrequested edit is scope creep and a regression surface. Apply the agreed findings only.
Files (rsc-harness)
  • evals
    • cases.yaml 3.6 KB
      skill: code-review
      
      should_trigger:
        - prompt: "review this PR before I merge it"
          why: Core diff/PR review on its own merits with no SDD spec/plan chain — the spec-less giving pass.
        - prompt: "revisa este diff, ¿está bien?"
          why: Spanish phrasing for a standalone diff judgment with no project artifact chain.
        - prompt: "què està malament en aquest canvi?"
          why: Catalan phrasing — judge a concrete change for defects, no spec required.
        - prompt: "is this dependency bump safe to merge?"
          why: Non-obvious — a diff with almost no code still needs supply-chain/transitive review; this is the giving pass on a bump.
        - prompt: "a contractor pushed a branch, tear it apart"
          why: Inbound external change where you have only the diff + stated intent, no rsc-SDD artifacts to key off.
        - prompt: "/code-review --comment 1432"
          why: Running the executable discipline by hand, in comment-posting mode.
        - prompt: "give me a high-signal review of this change, no nitpicks"
          why: Names the signal-to-noise north star directly — exactly this skill's defining trait.
      
      should_not_trigger:
        - prompt: "the reviewer left comments — do I have to make these changes?"
          route_to: review
          why: That is the receiving loop keyed to the SDD review gate, which review owns; code-review is giving-only.
        - prompt: "run the lint, type-check and test gate and make it green"
          route_to: verify
          why: Running the gates to prove green is the verify phase; code-review judges whether green is correct.
        - prompt: "threat-model this upload endpoint for OWASP issues with fixed diffs"
          route_to: secure-coding
          why: A deep STRIDE/OWASP exploitability pass with vulnerable→fixed diffs is secure-coding; code-review does only a boundary pass.
        - prompt: "this test is flaky, find the root cause"
          route_to: debug
          why: Root-causing one confirmed failure is debug; code-review surveys a change for latent defects.
        - prompt: "open the PR and merge it now that it's approved"
          route_to: ship
          why: PR/merge/close after approval is ship; code-review only produces the verdict ship consumes.
        - prompt: "cross-check the spec, plan and tasks before we write any code"
          route_to: analyze
          why: Pre-code consistency across SDD artifacts is analyze; code-review reads the diff after code exists.
      
      capability:
        - scenario: |
            Review a PR diff for a checkout service that contains TWO things:
            (a) a real correctness bug — the new code re-implements an existing
            `cart.compute_subtotal()` helper inline as `sum(i.price * i.qty ...)`,
            silently dropping the per-item discount the helper applies; and
            (b) a planted non-bug — a call that interpolates a value into a SQL
            string and *looks* like injection, but the value is a server-derived
            enum already validated upstream in the same function (guarded upstream).
            The user asked for a review and did NOT pass --comment or --fix.
          must_include:
            - Flags the duplicated/discount-dropping logic as [blocking] or [should-fix] with where/why/repro/fix.
            - Does NOT flag the guarded SQL call as a blocker — either omits it or labels it [question] only after stating it traced the value to an upstream validation (signal-to-noise discipline).
            - Emits exactly one verdict — APPROVE, APPROVE WITH NITS, or CHANGES REQUESTED.
            - Ranks findings by blast radius × reachability with no severity inflation.
            - Stays read-only — posts no PR comment and applies no fix, since neither --comment nor --fix was requested.
            - States the assumed intent when reasoning about the change.
      
    • README.md 952 B
      # code-review evals — how to run
      
      These cases are routing + capability checks for the `code-review` skill; there is no automated runner. Run them manually or via an orchestrator. For routing, read each `should_trigger` prompt and confirm the skill's description would fire on it (especially the spec-less diff/PR cases and the non-obvious dependency-bump one), then read each `should_not_trigger` prompt and confirm the description routes it to the named sibling — the load-bearing boundary is `review` vs `code-review` (review needs `02-DOCS/wiki/sdd/` and owns the receiving loop; code-review needs only the diff). For capability, hand an agent the scenario diff and score the response against the rubric: the planted-non-bug line is the real test — a review that flags the guarded-upstream SQL call as a blocker fails on signal-to-noise even if it caught the real bug. Also confirm it stayed read-only since no `--comment`/`--fix` was passed.
      
  • references
    • pr-workflow.md 3.5 KB
      # PR review workflow — `gh` mechanics
      
      Tooling depth for fetching a diff and posting review feedback end to end. Nothing here is posted or applied unless the user passed `--comment` or `--fix`. The body's pass order and confidence floor still govern *what* you report; this file is only *how*.
      
      ## Fetch the diff
      
      ```bash
      # By PR number in the current repo
      gh pr diff 1432
      gh pr view 1432 --json title,body,files,additions,deletions,baseRefName,headRefName
      
      # A PR in another repo (e.g. an OSS contribution you're reviewing)
      gh pr diff 1432 --repo owner/name
      
      # Fork PRs: the head branch lives on the contributor's fork. `gh pr diff`
      # resolves it for you — no manual remote add needed. To check out locally:
      gh pr checkout 1432
      
      # A local branch with no PR yet
      git diff main...HEAD          # everything since the branch diverged
      git diff --stat main...HEAD   # blast radius first
      ```
      
      Read the whole touched file, not just the hunk. `gh pr diff` shows only changed lines; open the file when a hunk's correctness depends on surrounding code.
      
      ## Post the verdict (summary review)
      
      ```bash
      gh pr review 1432 --request-changes -b "CHANGES REQUESTED — 1 blocker, 2 should-fix. See inline."
      gh pr review 1432 --approve        -b "APPROVE — correctness, contracts and security boundary clean."
      gh pr review 1432 --comment        -b "APPROVE WITH NITS — non-blocking; see inline nits."
      ```
      
      Pick exactly one of `--approve` / `--request-changes` / `--comment` to match the body's verdict. Note: you cannot `--approve` your own PR; on your own branch use `--comment`.
      
      ## Post inline line-anchored comments
      
      Inline (line-anchored) comments beat PR-level prose for actionable feedback — they land on the exact line. The GitHub REST API takes one comment per call:
      
      ```bash
      gh api repos/{owner}/{repo}/pulls/1432/comments \
        -f body='[should-fix] re-implements `cart.compute_subtotal()` and drops per-item discounts. Call the helper instead.' \
        -f commit_id="$(gh pr view 1432 --json headRefOid -q .headRefOid)" \
        -f path='api/orders.py' \
        -F line=88 \
        -f side='RIGHT'
      ```
      
      - `path` is the file path as it appears in the diff.
      - `line` is the line number in the file at `commit_id`.
      - `side`: `RIGHT` for the new version (additions/context), `LEFT` for the old.
      - Multi-line span: add `-F start_line=85 -f start_side='RIGHT'`.
      
      To batch several inline comments into one pending review with a single verdict, use `POST /pulls/{n}/reviews` with a `comments[]` array and an `event` of `REQUEST_CHANGES` / `APPROVE` / `COMMENT` — see the GitHub "Create a review for a pull request" API. Otherwise individual `pulls/{n}/comments` calls post immediately and unbatched.
      
      ## Large-diff strategy
      
      When the diff is too big to hold in one pass:
      
      1. `git diff --stat` (or the `files` array from `gh pr view`) to rank files by churn.
      2. Review by file or by commit (`gh pr diff` per commit SHA), hot paths first — auth, money, data migrations, anything user-input-facing.
      3. Generated/vendored files (lockfiles, snapshots, `dist/`) get a structural skim for surprises, not a line-by-line read.
      4. Carry findings across files: a contract change in one file is only a finding if a caller in another file breaks — confirm the caller.
      
      ## Read-only default
      
      The review itself touches nothing. Posting (`--comment`) and editing (`--fix`) are explicit opt-ins. With `--fix`, branch first if you are on the default branch, apply only the agreed findings, and commit/push only when the user asks. Git authorship is Eric — no Claude co-author or generated footer on any commit or PR body.
      
  • SKILL.md 9.1 KB
    ---
    name: code-review
    description: "Use to judge a concrete diff, branch, or GitHub PR on its own merits with no rsc-SDD spec/plan chain to key off — the spec-less giving pass behind /code-review: only findings you can defend, one verdict, read-only unless --comment or --fix. NOT the SDD gate keyed to 02-DOCS/wiki/sdd/ that also processes incoming review comments (that is `review`)."
    tags: [code-review, pr-review, quality, correctness]
    recommends: [review, secure-coding, verify]
    origin: risco
    ---
    
    # Code review — standalone, spec-less diff judgment
    
    You are reviewing a concrete change — a `git diff`, a branch, a GitHub PR, a pasted patch — on its own merits. No rsc-SDD spec/plan/constitution chain is required and you should not pretend one exists. This is the doctrine behind the executable `/code-review` slash command: same evidence bar, written as a discipline you run by hand. If the user is mid-SDD and wants to process *incoming* comments against `02-DOCS/wiki/sdd/`, that is `../review/SKILL.md`; a naked diff or an inbound third-party PR is this skill.
    
    **The north star is signal-to-noise.** Report only findings you would stake your name on. A clean diff is `APPROVE`, not a manufactured nit. High-false-positive review gets tuned out by humans in about two weeks; the bar to aim for is the logic-error review where under 1% of findings come back marked wrong. Padding does not make you look thorough — it trains the reader to ignore you.
    
    ## Get the change and its intent first
    
    Three inputs, in this order: **the diff**, **its stated purpose**, and **the touched surface** (the files around the hunks, not just the hunks).
    
    ```bash
    # A GitHub PR
    gh pr diff 1432
    gh pr view 1432 --json title,body,files,additions,deletions
    
    # A local branch against the trunk
    git diff main...HEAD
    git diff --stat main...HEAD   # see blast radius before reading
    
    # A pasted patch — read it as given
    ```
    
    A review with no notion of intent is a review of vibes. If no purpose is stated, **infer it from the diff and say what you assumed** ("Assuming this is meant to add idempotency to the webhook handler…") so the reader can correct a wrong premise — a silent wrong premise produces a confidently wrong review. Then read the *whole* changed file, not just the green/red lines: the structural failure of standalone review is judging a hunk without its context and shipping generic pattern-matched suggestions.
    
    ## The pass order
    
    Run these in order. Passes 1–5 are correctness/safety and are blocking-eligible; pass 6 is cleanup and is usually `[should-fix]` or `[nit]`. **A clean pass is a reportable result** ("contracts: nothing changed shape, no finding"), not a pass you silently skip.
    
    | # | Pass | The question | Typical defects |
    |---|------|--------------|-----------------|
    | 1 | Intent fidelity | Does it do what it claims? | Wrong behaviour, missing case from the stated goal, scope creep |
    | 2 | Correctness & boundaries | Right on the edges? | Off-by-one, null/empty/unicode, overflow, timezone, concurrency, swallowed errors |
    | 3 | Contracts & data | Do callers/data still hold? | Broken API shape, migration without backfill, nullable made non-null, enum drift |
    | 4 | Security boundary | Untrusted input → dangerous sink? | Unsanitized input to query/shell/template, authz gap, secret in code/log |
    | 5 | Tests as evidence | Do the tests prove the change? | Tests assert nothing, test the mock, miss the new branch, were deleted to go green |
    | 6 | Reuse / simplification / efficiency | Could existing code do this? | Reimplemented helper, copy-paste divergence, N+1, needless allocation in a hot loop |
    
    Pass 4 is a **boundary** pass — trace untrusted input to its sink and flag the reachable ones. For a real STRIDE/OWASP threat model with exploitability ranking and vulnerable→fixed diffs, hand off to `../secure-coding/SKILL.md`.
    
    Adjacent jobs, delegated by name: running the lint/type/test gates until they are green is `../verify/SKILL.md` (this skill judges whether green is *correct*); root-causing one confirmed failure is `../debug/SKILL.md`; cross-checking spec/plan/tasks *before* code exists is `../analyze/SKILL.md`.
    
    ## Confidence floor and the false-positive skip-list
    
    **The 80% rule:** if you are not at least ~80% sure a finding is real, you have two moves — trace the code until you *are* sure, or downgrade it to `[question]`. Never ship a guess dressed as a defect.
    
    Skip these common false positives outright (or demote to `[question]`/`[nit]`):
    
    - **Guarded upstream** — the "missing" check happens in the caller you can see; trace before you flag.
    - **Framework-enforced** — the framework already does it (e.g. an ORM that parameterizes, a router that validates).
    - **Behind an off-everywhere flag** — real but unreachable in any deployed config → `[nit]`, not blocking.
    - **Test-only / generated code held to the prod bar** — don't demand prod-grade error handling in a fixture or a generated client.
    - **Style the linter owns** — quotes, import order, line length. If a tool enforces it, don't spend a finding on it.
    
    **No severity inflation.** Rank by `blast radius × reachability`, not by how clever the catch was. A typo in a log string is a nit even if it took effort to spot.
    
    ## Severity and finding format
    
    - `[blocking]` — wrong/unsafe; merging causes a real defect. Must be fixed.
    - `[should-fix]` — a real problem with bounded blast radius; fix it or consciously accept it.
    - `[nit]` — minor; the reader may ignore it without consequence.
    - `[question]` — you suspect an issue but cannot prove reachability; asking, not asserting.
    
    Every finding carries **where / why / repro / fix**:
    
    ```text
    [should-fix] api/orders.py:88 — duplicated total logic
      where:  `subtotal = sum(i.price * i.qty for i in items)` re-implements
              `cart.compute_subtotal()` (cart/totals.py:14), which also applies
              per-item discounts this copy silently drops.
      why:    discounted items now bill at full price on this path only;
              the two implementations will drift on the next discount change.
      repro:  order containing any item with `discount_pct > 0` → charged the
              undiscounted amount; covered by no test.
      fix:    call `cart.compute_subtotal(items)` instead of inlining the sum.
    ```
    
    **Rule: no repro or stated mechanism → it is a `[question]`, not a blocker.** "This could overflow" with no path is a question; "n*1000 with n up to 3M exceeds int32 at orders.py:51" is a finding.
    
    ## Verify before you flag
    
    Read the surrounding code, trace the *value*, confirm the path is reachable.
    
    - **Bad:** "Looks like SQL injection." (pattern-match)
    - **Good:** "`search()` interpolates `req.query.q` straight into `db.execute(\`… WHERE name='${q}'\`)` at search.ts:22; `q` is unvalidated user input → injection." (traced)
    
    If you cannot trace it to a concrete value and a reachable sink, you do not yet have a finding.
    
    ## The verdict
    
    End every review with exactly one, plainly — no mushy middle:
    
    - **APPROVE** — no blockers, no should-fix. Point the user to `../ship/SKILL.md` to merge.
    - **APPROVE WITH NITS** — mergeable; nits listed but none gate the merge.
    - **CHANGES REQUESTED** — at least one `[blocking]`. List precisely what unblocks it, so the author knows when they are done.
    
    ## Effort dial
    
    Mirror the slash command's effort level: **low/medium** → fewer, high-confidence findings (raise the confidence floor, focus on passes 1–4). **high/max** → broader coverage; uncertain findings are allowed but must be labelled `[question]`, never inflated into blockers. This is *coverage vs precision*, not the harness accompaniment dial — it changes what you look at, not how much you narrate.
    
    ## Emitting comments and applying fixes
    
    **Read-only by default.** You produce findings + a verdict and stop there. Two opt-in modes:
    
    - `--comment` → post the findings as an inline-anchored review on the PR.
    - `--fix` → apply the agreed findings to the working tree.
    
    ```bash
    # Summary review (the verdict)
    gh pr review 1432 --request-changes -b "CHANGES REQUESTED — see inline. Blocker: orders.py:88 …"
    gh pr review 1432 --approve -b "APPROVE — correctness and contracts clean."
    ```
    
    Inline line-anchored comments go through the GitHub REST API — see `references/pr-workflow.md` for the JSON shape, fork-PR handling, and large-diff strategy. If `--fix` puts you on the default branch, **branch first**; commit or push **only when the user asks**; git authorship is **Eric** (no Claude co-author or generated footer).
    
    ## Anti-patterns
    
    | Failure mode | Reality |
    |---|---|
    | "It compiles and the tests pass, so it's correct." | Tests prove green, not correct. Pass 5 asks whether the tests actually exercise the new branch — green for the wrong reason is a finding. |
    | Listing everything you would have done differently. | That is noise. Report defects and reuse wins you can defend; preference is not a finding. |
    | "It's just a dependency bump, skim it." | Bumps carry supply-chain and transitive risk and behaviour changes. Check the changelog/lockfile diff, not just the version string. |
    | Applying every nit "to be safe" under `--fix`. | Each unrequested edit is scope creep and a regression surface. Apply the agreed findings only. |
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related