For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Flag German invoices that omit the explicit VAT rate (§14 Abs. 4 Satz 1 Nr. 8 UStG) as a warning, escalated through the Uncertain Validations Resolver (UVR) for a final vision-LLM decision.
Architecture: Extraction emits a new optional boolean :tax-rate-stated?. A new deterministic check-tax-rate-stated (validation.clj) never terminally warns — it only escalates to "uncertain". UVR owns it as a 4th sub-path: deterministic check → vision-LLM resolver (final say) → pass/not-applicable/warning. The warning surfaces through the existing findings path, which requires touching five duplicated check-ordering lists.
Tech Stack: Clojure, Malli (schema), HoneySQL/next-jdbc, Integrant, clojure.test, the IProcessor v2 / UVR resolver pattern.
Spec: docs/superpowers/specs/2026-05-18-vat-rate-statement-check-design.html
| File | Responsibility | Change |
|---|---|---|
src/com/getorcha/schema/invoice/structured_data.clj |
InvoiceData schema |
Add optional nilable boolean |
src/com/getorcha/workers/ap/ingestion/extraction.clj |
Extraction prompt | Add field + instruction |
src/com/getorcha/workers/ap/ingestion/validation.clj |
Deterministic checks | New check-tax-rate-stated |
src/com/getorcha/workers/ap/processors/uncertain_validations.clj |
UVR | 4th sub-path + resolver |
src/com/getorcha/diagnostics/findings.clj |
Shared findings | Label + ordering |
src/com/getorcha/app/http/documents/view/invoice.clj |
Issue-count badge | Hardcoded list entry |
src/com/getorcha/app/ui/components.clj |
UI ordering/text | 3 hardcoded maps |
Tests mirror source: test/com/getorcha/....
Conventions for every task:
orcha-clojure-style skill before editing any .clj.clj -X:test:silent :nses '[the.ns-test]' 2>&1 | grep -E "FAIL in|ERROR in|Ran .* tests|failures"clj-kondo --lint <files> — must report errors: 0, warnings: 0.git commit, single-line -m, no Co-Authored-By. Run git from the cwd (/home/volrath/code/orcha/orcha) with cwd-relative paths; do not use git -C. No ;/&& chaining in Bash calls.:tax-rate-stated? to InvoiceData schemaFiles:
Modify: src/com/getorcha/schema/invoice/structured_data.clj:163
Test: test/com/getorcha/schema/invoice/structured_data_test.clj
Step 1: Write the failing test
Add this deftest after test-valid-invoice-baseline (after line 72):
(deftest test-tax-rate-stated-field
(testing "key absent — still valid (optional)"
(is (m/validate invoice.schema/InvoiceData valid-invoice)))
(testing "explicit true / false / nil all valid"
(is (m/validate invoice.schema/InvoiceData (assoc valid-invoice :tax-rate-stated? true)))
(is (m/validate invoice.schema/InvoiceData (assoc valid-invoice :tax-rate-stated? false)))
(is (m/validate invoice.schema/InvoiceData (assoc valid-invoice :tax-rate-stated? nil))))
(testing "wrong type rejected (key is declared, not just an open-map passthrough)"
(is (not (m/validate invoice.schema/InvoiceData
(assoc valid-invoice :tax-rate-stated? "yes"))))))
Run: clj -X:test:silent :nses '[com.getorcha.schema.invoice.structured-data-test]' 2>&1 | grep -E "FAIL in|Ran .* tests|failures"
Expected: FAIL on the "wrong type rejected" assertion (open map currently ignores the unknown key, so "yes" validates and (not ...) is false).
In src/com/getorcha/schema/invoice/structured_data.clj, add the line immediately after :tax-rate-breakdowns (line 163):
[:tax-rate-breakdowns [:maybe [:vector TaxRateBreakdown]]] ;; Per-rate breakdown if printed on invoice
[:tax-rate-stated? {:optional true} [:maybe :boolean]] ;; true iff the VAT rate is printed verbatim; absent/nil ⇒ unknown (legacy)
Run: clj -X:test:silent :nses '[com.getorcha.schema.invoice.structured-data-test]' 2>&1 | grep -E "FAIL in|Ran .* tests|failures"
Expected: PASS, 0 failures, 0 errors.
Run: clj-kondo --lint src/com/getorcha/schema/invoice/structured_data.clj test/com/getorcha/schema/invoice/structured_data_test.clj
Expected: errors: 0, warnings: 0.
git add src/com/getorcha/schema/invoice/structured_data.clj test/com/getorcha/schema/invoice/structured_data_test.clj
git commit -m "feat(schema): add optional :tax-rate-stated? to InvoiceData"
tax-rate-stated?Files:
src/com/getorcha/workers/ap/ingestion/extraction.clj:103 (prose instruction) and :463 (JSON schema block)No unit test — prompt wording is verified by the regression run in Task 6. This is a deliberate, documented exception to TDD (LLM prompt text has no deterministic unit assertion); the behavioral check is the ingestion-regression-test in Task 6.
In extraction.clj, the tax-amount bullet ends at line 103. Insert a new bullet immediately after it (before the - tax-rate-breakdowns bullet at line 104):
- tax-amount = the SINGLE TOTAL tax/VAT amount, ONLY if explicitly shown as one number on the invoice.
Set to null if the invoice shows multiple separate tax lines without a combined total.
Do NOT calculate this - extract only if a single total tax figure is printed.
- tax-rate-stated? = true ONLY if the applicable VAT rate is printed verbatim on the invoice
(e.g. "19 %", "MwSt 19%", "USt 19 %", "zzgl. 19% USt") OR an explicit exemption /
reverse-charge note is present. false if the rate is absent and only derivable from the
amounts. Do NOT infer it from tax-amount / subtotal. Set null only if you genuinely cannot tell.
- tax-rate-breakdowns = Extract EVERY tax breakdown shown on the invoice, even if they share the
In extraction.clj, after the tax-rate-breakdowns array closes (line 463, the ] | null, line), add:
] | null,
"tax-rate-stated?": boolean | null, // true iff VAT rate printed verbatim; do NOT infer
Run: clj -M -e "(require 'com.getorcha.workers.ap.ingestion.extraction :reload) (println \"ok\")"
Expected: prints ok (no reader/compile error from the edited string).
Run: clj-kondo --lint src/com/getorcha/workers/ap/ingestion/extraction.clj
Expected: errors: 0, warnings: 0.
git add src/com/getorcha/workers/ap/ingestion/extraction.clj
git commit -m "feat(extraction): prompt for tax-rate-stated? (§14 Abs. 4 Nr. 8)"
check-tax-rate-statedFiles:
src/com/getorcha/workers/ap/ingestion/validation.clj (add public fn near check-required-fields, after line 556)test/com/getorcha/workers/ap/ingestion/validation_test.cljinvoice-tier and resolve-issuer-country are private fns already in validation.clj; the new fn lives in the same ns and uses them directly.
Add after test-line-item-issue-message-format in validation_test.clj:
(deftest test-check-tax-rate-stated
(let [eu-base {:document-type "invoice"
:total 1000.0 :tax-amount 190.0 :tax-rate 19
:issuer {:name "Acme GmbH" :country "DE"
:tax-id-type "vat" :tax-id "DE136695976"}}]
(testing "rate printed (stated? true) → pass"
(is (= "pass" (:status (validation/check-tax-rate-stated
(assoc eu-base :tax-rate-stated? true))))))
(testing "legacy: key absent → pass"
(is (= "pass" (:status (validation/check-tax-rate-stated eu-base)))))
(testing "key explicitly nil → pass"
(is (= "pass" (:status (validation/check-tax-rate-stated
(assoc eu-base :tax-rate-stated? nil))))))
(testing "non-EU issuer → pass even when not stated"
(is (= "pass" (:status (validation/check-tax-rate-stated
(-> eu-base
(assoc :tax-rate-stated? false)
(assoc-in [:issuer :country] "US")
(assoc-in [:issuer :tax-id-type] nil)
(assoc-in [:issuer :tax-id] nil)))))))
(testing "no positive VAT (zero tax, no breakdowns) → pass"
(is (= "pass" (:status (validation/check-tax-rate-stated
(assoc eu-base :tax-rate-stated? false
:tax-amount 0.0 :tax-rate 0))))))
(testing "reverse-charge with compliance statement → not-applicable"
(is (= "not-applicable"
(:status (validation/check-tax-rate-stated
(assoc eu-base :tax-rate-stated? false
:tax-amount 0.0 :tax-rate 0
:compliance-statements
[{:type "reverse-charge"
:text "Steuerschuldnerschaft des Leistungsempfängers"
:legal-basis "§13b UStG"}]))))))
(testing "exempt via legal-basis §13b only (type not in set) → not-applicable"
(is (= "not-applicable"
(:status (validation/check-tax-rate-stated
(assoc eu-base :tax-rate-stated? false
:tax-amount 0.0 :tax-rate 0
:compliance-statements
[{:type "other"
:text "Steuerschuldnerschaft des Leistungsempfängers"
:legal-basis "§13b UStG"}]))))))
(testing "EU, positive VAT, rate not stated → uncertain (escalate)"
(let [r (validation/check-tax-rate-stated
(assoc eu-base :tax-rate-stated? false))]
(is (= "uncertain" (:status r)))
(is (re-find #"§14" (:message r)))))
(testing "positive VAT via breakdowns only (tax-amount nil) still escalates"
(is (= "uncertain"
(:status (validation/check-tax-rate-stated
(assoc eu-base :tax-rate-stated? false
:tax-amount nil
:tax-rate-breakdowns
[{:rate 19 :subtotal 1000.0 :tax-amount 190.0}]))))))))
Run: clj -X:test:silent :nses '[com.getorcha.workers.ap.ingestion.validation-test]' 2>&1 | grep -E "FAIL in|ERROR in|Ran .* tests|failures"
Expected: ERROR — check-tax-rate-stated is unresolved.
check-tax-rate-statedIn validation.clj, after check-required-fields (it ends at line 556 with {:status "pass" :details {:tier tier}})))), add:
(defn check-tax-rate-stated
"§14 Abs. 4 S. 1 Nr. 8 UStG: an invoice must print the applicable VAT rate,
not only the tax amount. Ordered cond — first match wins. Never terminally
warns; only escalates to \"uncertain\" for UVR to decide against the PDF.
`:tax-rate-stated?` is set by extraction (true only if the rate is printed
verbatim). Absent/nil ⇒ unknown (legacy doc) ⇒ pass, so legacy docs stay
silent until re-ingested. \"Positive VAT\" is decided from PRINTED fields
(`tax-amount` / `tax-rate-breakdowns`), never the LLM-inferred `tax-rate`."
[{:keys [tax-rate-stated? total tax-amount tax-rate tax-rate-breakdowns
compliance-statements issuer]}]
(let [tier (invoice-tier total (resolve-issuer-country issuer))
positive-vat? (or (and tax-amount (pos? (double tax-amount)))
(some #(and (:rate %) (pos? (double (:rate %))))
tax-rate-breakdowns))
exempt? (and (or (nil? tax-rate) (zero? tax-rate))
(some (fn [{:keys [type legal-basis]}]
(or (#{"reverse-charge" "vat-exemption"} type)
(and legal-basis (re-find #"(?i)13b" legal-basis))))
compliance-statements))]
(cond
(true? tax-rate-stated?) {:status "pass"}
(nil? tax-rate-stated?) {:status "pass"}
(= :non-eu tier) {:status "pass"}
exempt? {:status "not-applicable"}
(not positive-vat?) {:status "pass"}
:else
{:status "uncertain"
:message (str "Invoice does not state the applicable VAT rate "
"(§14 Abs. 4 Nr. 8 UStG); only the tax amount is shown.")
:details {:tier tier}})))
Run: clj -X:test:silent :nses '[com.getorcha.workers.ap.ingestion.validation-test]' 2>&1 | grep -E "FAIL in|ERROR in|Ran .* tests|failures"
Expected: PASS, 0 failures, 0 errors.
Run: clj-kondo --lint src/com/getorcha/workers/ap/ingestion/validation.clj test/com/getorcha/workers/ap/ingestion/validation_test.clj
Expected: errors: 0, warnings: 0.
git add src/com/getorcha/workers/ap/ingestion/validation.clj test/com/getorcha/workers/ap/ingestion/validation_test.clj
git commit -m "feat(validation): add check-tax-rate-stated (§14 Abs. 4 Nr. 8)"
tax-rate-stated into the UVRFiles:
src/com/getorcha/workers/ap/processors/uncertain_validations.clj (docstring lines 4-8; add instruction + resolve-tax-rate-stated; -reads, -diagnostic, -compute)test/com/getorcha/workers/ap/processors/uncertain_validations_test.cljThe resolver mirrors resolve-dates (value→status, no corrections — a presentation defect has nothing to fix). UVR only calls a resolver when the deterministic status is "uncertain"; (or (:det-result resolved) det-check) means the resolver overwrites the escalation.
Add to uncertain_validations_test.clj. Verified ns aliases (lines 3-7): [com.getorcha.ai.llm :as llm], [com.getorcha.ai.prompts :as ai.prompts], [com.getorcha.workers.ap.processors.uncertain-validations :as uncertain-validations] — so use the uncertain-validations alias for the resolver (not uv). The only missing alias is cheshire.core.
(deftest test-resolve-tax-rate-stated-mapping
(let [ctx {:db-pool nil
:llm-config {:post-processing :pp :vision :vis}}
state {:structured-data {:document-type "invoice" :tax-amount 190.0}
:document {:document/tenant-id (random-uuid)}
:file nil}
det {:status "uncertain"
:message "Invoice does not state the applicable VAT rate (§14 Abs. 4 Nr. 8 UStG); only the tax amount is shown."}
run (fn [llm-value]
(with-redefs [ai.prompts/tenant-prompt (fn [& _] "PROMPT")
llm/generate (fn [& _]
{:text (json/generate-string
{:tax-rate-stated
{:value llm-value :confidence 0.9
:reasoning "r"}})})]
(-> (#'uncertain-validations/resolve-tax-rate-stated ctx state det)
:det-result :status)))]
(is (= "pass" (run "pass")))
(is (= "not-applicable" (run "not-applicable")))
(is (= "warning" (run "warning")))
(is (= "warning" (run "unresolved")))))
Add only [cheshire.core :as json] to the test ns :require (llm, ai.prompts, and the uncertain-validations processor alias are already present).
Run: clj -X:test:silent :nses '[com.getorcha.workers.ap.processors.uncertain-validations-test]' 2>&1 | grep -E "FAIL in|ERROR in|Ran .* tests|failures"
Expected: ERROR — resolve-tax-rate-stated unresolved.
In uncertain_validations.clj, after the recipient-identity-instruction def block (the private instruction strings section), add:
(def ^:private tax-rate-stated-instruction
"**VAT rate statement (§14 Abs. 4 S. 1 Nr. 8 UStG)**:
Our extractor reports the applicable VAT rate is NOT printed on the invoice
(only the tax amount / net / gross are shown). German law requires the rate
itself to be stated. Look at the actual invoice and decide:
- \"pass\": the applicable rate (e.g. \"19 %\", \"MwSt 19%\", \"USt-Satz 19 %\")
IS printed somewhere — the extractor missed it.
- \"not-applicable\": this is a reverse-charge / VAT-exempt / 0% invoice that
legitimately carries a compliance statement instead of a rate.
- \"warning\": a positive VAT applies but the rate is genuinely not printed.
Return: {\"tax-rate-stated\": {\"value\": \"pass\"|\"not-applicable\"|\"warning\",
\"confidence\": 0.9, \"reasoning\": \"...\"}}")
resolve-tax-rate-statedAdd after resolve-dates (it ends at line 358):
(defn ^:private resolve-tax-rate-stated
"Calls the vision-LLM resolver for the tax-rate-stated uncertain check.
No corrections — a missing printed rate is a defect in the paper, not our
data. Mirrors resolve-required-fields' PDF handling."
[ctx state deterministic]
(ai-events/with-ai-phase :post-process.uncertain-validations
(let [{:keys [db-pool]} ctx
{:keys [structured-data document]} state
tenant-id (:document/tenant-id document)
invoice-data (select-keys structured-data
[:document-type :tax-rate :tax-amount
:tax-rate-breakdowns :compliance-statements
:subtotal :total])
questions (format "1. tax-rate-stated: %s" (:message deterministic))
prompt (ai.prompts/tenant-prompt db-pool tenant-id
:resolve-uncertain-validations
{:invoice-data (json/generate-string invoice-data {:pretty true})
:questions questions
:instructions tax-rate-stated-instruction})
pdf-bytes (get-in state [:file :contents])
relevant-pdf (when pdf-bytes
(try
(common/extract-relevant-pdf-pages pdf-bytes structured-data)
(catch Exception e
(log/warn "Could not extract PDF pages for tax-rate-stated resolver"
{:error (ex-message e)}))))
input (if relevant-pdf
[{:type :text :content prompt}
{:type :document :mime-type "application/pdf" :data relevant-pdf}]
prompt)
llm-config (if relevant-pdf
(get-in ctx [:llm-config :vision])
(get-in ctx [:llm-config :post-processing]))
{:keys [text] :as llm-stats} (llm/generate llm-config input)
parsed (llm/parse-json-response text)
{:keys [value confidence reasoning]} (:tax-rate-stated parsed)]
{:det-result {:status (case value
"pass" "pass"
"not-applicable" "not-applicable"
"warning")
:resolved-by :uncertain-validations-resolver
:confidence confidence
:reasoning reasoning
:message (:message deterministic)}
:stats llm-stats})))
Change the docstring sub-path list (lines 4-8). Replace:
Owns three diagnostic sub-paths under the `:validations` slice for
invoice documents:
- `:validations.required-fields`
- `:validations.date-reasonableness`
- `:validations.recipient-identity`
with:
Owns four diagnostic sub-paths under the `:validations` slice for
invoice documents:
- `:validations.required-fields`
- `:validations.date-reasonableness`
- `:validations.recipient-identity`
- `:validations.tax-rate-stated`
-reads, -diagnostic, -computeIn -reads (after line 507's (reads/read-leaf :line-items :* :amount) / before the date-reasonableness comment) add:
;; Tax-rate-stated check
(reads/read-leaf :tax-rate-stated?)
(reads/read-leaf :tax-rate-breakdowns :* :rate)
(reads/read-leaf :compliance-statements :* :type)
In -diagnostic, change the :sub-paths vector to add the 4th:
:sub-paths [[:required-fields]
[:date-reasonableness]
[:recipient-identity]
[:tax-rate-stated]]}))
In -compute, add the deterministic call, the conditional resolve, and the result key. Replace the let bindings + result map:
(-compute [_ ctx state]
(let [sd (:structured-data state)
le (:tenant state)
det-required (validation/check-required-fields sd)
det-dates (validation/check-date-reasonableness sd)
det-identity (validation/check-recipient-identity sd le)
det-tax-rate (validation/check-tax-rate-stated sd)
req-resolved (when (= "uncertain" (:status det-required))
(resolve-required-fields ctx state det-required))
dates-resolved (when (= "uncertain" (:status det-dates))
(resolve-dates ctx state det-dates))
ident-resolved (when (#{"uncertain" "warning"} (:status det-identity))
(resolve-identity ctx state det-identity))
tax-resolved (when (= "uncertain" (:status det-tax-rate))
(resolve-tax-rate-stated ctx state det-tax-rate))
result {:required-fields (or (:det-result req-resolved) det-required)
:date-reasonableness (or (:det-result dates-resolved) det-dates)
:recipient-identity (or (:det-result ident-resolved) det-identity)
:tax-rate-stated (or (:det-result tax-resolved) det-tax-rate)}]
{:result result
:stats (merge-llm-stats (:stats req-resolved)
(:stats dates-resolved)
(:stats ident-resolved)
(:stats tax-resolved))}))
merge-llm-stats is variadic — (defn ^:private merge-llm-stats [& stats-maps] ...) at uncertain_validations.clj:467. The 4-arg call works directly; no nesting needed. Also update its docstring (line 468), which says "up to three resolver calls", to "four".
Run: clj -X:test:silent :nses '[com.getorcha.workers.ap.processors.uncertain-validations-test]' 2>&1 | grep -E "FAIL in|ERROR in|Ran .* tests|failures"
Expected: PASS, 0 failures, 0 errors.
Run: clj-kondo --lint src/com/getorcha/workers/ap/processors/uncertain_validations.clj test/com/getorcha/workers/ap/processors/uncertain_validations_test.clj
Expected: errors: 0, warnings: 0.
git add src/com/getorcha/workers/ap/processors/uncertain_validations.clj test/com/getorcha/workers/ap/processors/uncertain_validations_test.clj
git commit -m "feat(uvr): resolve tax-rate-stated as 4th sub-path"
Files:
Modify: src/com/getorcha/diagnostics/findings.clj (lines 13-19 labels; 37-38 ordering)
Modify: src/com/getorcha/app/http/documents/view/invoice.clj:201
Modify: src/com/getorcha/app/ui/components.clj (1939-1949, 1958-1967, 1971-1978)
Test: test/com/getorcha/diagnostics/findings_test.clj
Step 1: Write the failing test
Add to findings_test.clj (use the file's existing findings/alias convention):
(deftest test-tax-rate-stated-finding
(testing "warning status yields a formal finding"
(let [fs (findings/extract-findings
{:validations {:tax-rate-stated
{:status "warning"
:message "Invoice does not state the applicable VAT rate (§14 Abs. 4 Nr. 8 UStG); only the tax amount is shown."}}
:fraud-flags []}
nil)
f (first (filter #(= :tax-rate-stated (:check %)) fs))]
(is (some? f))
(is (= "VAT Rate Statement" (:label f)))
(is (= :warning (:severity f)))))
(testing "pass / not-applicable produce no finding"
(doseq [st ["pass" "not-applicable"]]
(is (empty? (filter #(= :tax-rate-stated (:check %))
(findings/extract-findings
{:validations {:tax-rate-stated {:status st}}
:fraud-flags []}
nil)))))))
Run: clj -X:test:silent :nses '[com.getorcha.diagnostics.findings-test]' 2>&1 | grep -E "FAIL in|ERROR in|Ran .* tests|failures"
Expected: FAIL — no finding produced / label nil (key not in formal-requirement-checks).
Add to validation-check-labels (after :recipient-country line 18):
:recipient-country "Recipient Country"
:tax-rate-stated "VAT Rate Statement"
:tax "Tax"})
Add to formal-requirement-checks (line 37-38) — place before :tax:
[:large-document-summary-only :required-fields :financial-math :date-reasonableness
:issuer-country :recipient-country :tax-rate-stated :tax])
view/invoice.clj:201-202. Add :tax-rate-stated to the literal:
formal-issue-count (count (filter (fn [k] (#{"error" "warning" "uncertain"}
(:status (get validation-results' k))))
[:required-fields :financial-math :date-validity
:issuer-country :recipient-country :tax-rate-stated :tax]))
validation-check-order (def at 1939, body 1943-1949) — place after :tax:
[:large-document-summary-only
:required-fields
:financial-math
:tax
:tax-rate-stated
:date-reasonableness
:issuer-country
:recipient-country])
validation-check-descriptions (1958-1967) — add entry:
:recipient-country "The recipient's country could not be determined from the address or tax ID."
:tax-rate-stated "The invoice may not print the applicable VAT rate (§14 Abs. 4 Nr. 8 UStG requires the rate, not only the tax amount)."
:tax "The tax ID format may be incorrect or there may be tax compliance issues."})
formal-status-texts (1971-1978) — add entry:
:recipient-country {"pass" "Available" "uncertain" "No Recipient" "error" "Not Available"}
:tax-rate-stated {"pass" "Stated" "warning" "Rate Not Stated" "not-applicable" "Not Applicable" "uncertain" "Checking"}
:tax {"pass" "Correct" "not-applicable" "No Tax ID" "warning" "Issue Found" "error" "Issue Found"}})
Run: clj -X:test:silent :nses '[com.getorcha.diagnostics.findings-test]' 2>&1 | grep -E "FAIL in|ERROR in|Ran .* tests|failures"
Expected: PASS, 0 failures, 0 errors.
Run: clj -M -e "(doseq [n '[com.getorcha.diagnostics.findings com.getorcha.app.http.documents.view.invoice com.getorcha.app.ui.components]] (require n :reload)) (println \"ok\")"
Expected: prints ok.
Run: clj-kondo --lint src/com/getorcha/diagnostics/findings.clj src/com/getorcha/app/http/documents/view/invoice.clj src/com/getorcha/app/ui/components.clj test/com/getorcha/diagnostics/findings_test.clj
Expected: errors: 0, warnings: 0.
git add src/com/getorcha/diagnostics/findings.clj src/com/getorcha/app/http/documents/view/invoice.clj src/com/getorcha/app/ui/components.clj test/com/getorcha/diagnostics/findings_test.clj
git commit -m "feat(findings): surface tax-rate-stated warning across ordering lists"
Files: none (verification only).
Run: clj-kondo --lint src test
Expected: errors: 0, warnings: 0.
Run: clj -X:test:silent 2>&1 | grep -A 5 -E "(FAIL in|ERROR in|Execution error|Ran .* tests)"
Expected: Ran N tests, 0 failures, 0 errors. If anything else fails, it is in scope to fix (likely another hardcoded check list or a fixture lacking the new key) before proceeding.
Re-ingest the bikosigma document locally (system must be running; see clojure-eval skill for the nREPL port). In the REPL:
(require '[com.getorcha.app.ingestion :as app.ingestion] :reload)
(let [sys integrant.repl.state/system
bytes (with-open [in (java.io.FileInputStream.
"/tmp/debug-fetch/documents/019e3a85-4776-7067-81b6-49a6dfde031a.pdf")]
(.readAllBytes in))]
(app.ingestion/queue-for-ingestion!
(:com.getorcha.db/pool sys) (:com.getorcha.aws/state sys)
{:content bytes :content-type "application/pdf"
:tenant-id #uuid "00000000-0000-0000-0000-000000000001"
:uploaded-by #uuid "00000000-0000-0000-0000-000000000001"
:file-original-name "bikosigma-vat-rate-check.pdf" :source-metadata {}}))
Wait for ingestion to complete, then assert the diagnostics carry the new check:
(get-in (:document/diagnostics
(com.getorcha.db.sql/execute-one!
(:com.getorcha.db/pool integrant.repl.state/system)
{:select [:diagnostics] :from [:document]
:where [:= :id <new-doc-id>]}))
[:validations :tax-rate-stated])
Expected: a map with :status of "warning" (rate genuinely absent on this invoice; UVR confirmed) or "pass" (if UVR finds a rate we missed). Either is a correct outcome — confirm it is not absent and not "uncertain".
Invoke the ingestion-regression-test skill comparing structured data before/after for a representative sample including doc 019e3a85-4776-7067-81b6-49a6dfde031a. Expected: no regressions in unrelated fields; :tax-rate-stated? populated on the sample.
git add -A
git commit -m "fix(vat-rate-check): regression follow-ups"
Spec coverage:
positive-vat? from tax-amount/breakdowns; true?/nil? short-circuit before tier ⇒ I3/I4 covered). ✔ Cond-order correction (applied during execution): exempt? must precede (not positive-vat?) — a reverse-charge/exempt invoice has zero VAT, so the original order would silently return "pass" and never reach the not-applicable exempt branch. The spec's "Reverse-charge precedence" prose mandates not-applicable; the spec trigger-matrix row numbering (row 4 before row 5) was buggy for the zero-VAT-exempt edge case and is superseded by this ordering. Independently confirmed spec-faithful by review.-apply-ops [] → Task 4 (no -apply-ops change; resolver returns :det-result consumed via (or ...)). ✔Placeholder scan: No TBD/TODO; every code step shows complete code; commands have expected output. Task 2's "no unit test" is explicitly justified (prompt text) with the behavioral check located in Task 6. ✔
Type consistency: check-tax-rate-stated (Task 3) is the exact symbol called in Task 4 -compute; resolve-tax-rate-stated (Task 4) is the exact private symbol exercised by the Task 4 test via #'uncertain-validations/resolve-tax-rate-stated. Diagnostics key :tax-rate-stated is consistent across Task 4 (result map / -diagnostic sub-path) and Task 5 (findings ordering, labels, the three components maps, invoice.clj literal) and Task 5/6 tests. Status vocabulary pass/not-applicable/uncertain/warning consistent with findings/formal-finding ("error"→:error, else :warning; pass/not-applicable silent). ✔
No open implementation risks. The previously-flagged merge-llm-stats arity is resolved: it is variadic ([& stats-maps], uncertain_validations.clj:467); the 4-arg call works directly, and Task 4 Step 6 also updates its stale "three"→"four" docstring.