sooth 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: Bug report
3
+ about: Something is broken or a verdict is wrong
4
+ labels: bug
5
+ ---
6
+
7
+ **What happened**
8
+
9
+ **What you expected**
10
+
11
+ **Repro**
12
+
13
+ ```bash
14
+ sooth --source ... --text ...
15
+ ```
16
+
17
+ (Attach the draft/source snippets if shareable — remove secrets first.)
18
+
19
+ **Environment**
20
+
21
+ - sooth version:
22
+ - Python version:
23
+ - OS:
@@ -0,0 +1,11 @@
1
+ ---
2
+ name: Feature request
3
+ about: Propose an improvement
4
+ labels: enhancement
5
+ ---
6
+
7
+ **Problem it solves**
8
+
9
+ **Proposed behavior**
10
+
11
+ **Alternatives you considered**
@@ -0,0 +1,9 @@
1
+ ## What
2
+
3
+ ## Why
4
+
5
+ ## Checklist
6
+
7
+ - [ ] `python3 tests/test_core.py` passes
8
+ - [ ] New logic covered by an assert in `tests/test_core.py`
9
+ - [ ] Docs updated if behavior changed (`docs/PRD.md`, `docs/DESIGN.md`, README)
@@ -0,0 +1,21 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.11", "3.12"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-python@v5
17
+ with:
18
+ python-version: ${{ matrix.python-version }}
19
+ - run: pip install -e ".[dev]"
20
+ - run: ruff check src tests
21
+ - run: pytest
sooth-0.2.1/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ __pycache__/
2
+ .venv/
3
+ build/
4
+ dist/
5
+ *.egg-info/
6
+ .pytest_cache/
7
+ .ruff_cache/
8
+ .env
9
+ decisions.jsonl
10
+ .DS_Store
sooth-0.2.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Naufal Hilmiaji
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
sooth-0.2.1/PKG-INFO ADDED
@@ -0,0 +1,110 @@
1
+ Metadata-Version: 2.5
2
+ Name: sooth
3
+ Version: 0.2.1
4
+ Summary: Verify AI-generated text against source material. Claim-by-claim trust reports powered by Jev (TypeSafe System One).
5
+ Project-URL: Homepage, https://github.com/naufalhilmiaji/sooth
6
+ Project-URL: Documentation, https://github.com/naufalhilmiaji/sooth/tree/main/docs
7
+ Author-email: Naufal Hilmiaji <nhilmiaji@gmail.com>
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: ai,jev,llm,trust,typesafe,verification
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Topic :: Software Development :: Quality Assurance
17
+ Requires-Python: >=3.11
18
+ Requires-Dist: typesafe-sdk
19
+ Provides-Extra: dev
20
+ Requires-Dist: pytest; extra == 'dev'
21
+ Requires-Dist: ruff; extra == 'dev'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # Sooth
25
+
26
+ Verify AI-generated text against source material. Claim-by-claim **PASS / FAIL / REVIEW**, with calibrated probabilities you can act on in CI.
27
+
28
+ Built on [Jev](https://docs.typesafe.ai) (TypeSafe System One) — typed judgments and probabilities instead of generated prose. Every verdict shows its probability distribution, and the combination rules are plain code you can read. No black box.
29
+
30
+ ```
31
+ sooth --source policy.md --text draft-reply.md
32
+ ```
33
+
34
+ Real output (planted errors in the draft vs a news source):
35
+
36
+ ```
37
+ # Sooth
38
+
39
+ **PASS 2 · FAIL 3 · REVIEW 1 · UNCHECKABLE 1** — threshold 0.70
40
+
41
+ | # | Claim | Verdict | P | Why (P distribution) | Evidence |
42
+ |---|-------|---------|---|----------------------|----------|
43
+ | 3 | BNBR baru menuntaskan rights issue bernilai besar di harga Rp 33. | ❌ FAIL | ████████ 1.00 | contradicts 1.00 / not_found 0.00 / supports 0.00 · details 0.01 · missing #s: 33 | `examples/news-1.md:25` "Saham ini juga baru menyelesaikan rights issue dalam juml…" |
44
+ | 5 | BUMI hanya perlu turun sekitar 10% untuk menyentuh level Rp 50. | ❌ FAIL | ████████ 1.00 | contradicts 1.00 / not_found 0.00 / supports 0.00 · details 0.01 · missing #s: 10 | `examples/news-1.md:23` "Adapun PT Bumi Resources Tbk (BUMI) di sekitar Rp192 haru…" |
45
+ | 6 | BEI juga menetapkan batas atas harga saham Rp 5.000 per saham. | ⚠️ REVIEW | ████████ 1.00 | contradicts 0.00 / not_found 1.00 / supports 0.00 · details 0.02 · missing #s: 5.000 | |
46
+ | 7 | Para investor ritel sangat senang dengan aturan baru ini. | ➖ UNCHECKABLE | ██░░░░░░ 0.24 | P(checkable)=0.24 | |
47
+ ```
48
+
49
+ ## Why
50
+
51
+ AI writes fast, nobody checks. Claims ship wrong. Sooth checks each claim against the evidence you give it — and when it is unsure, it says `REVIEW` instead of guessing.
52
+
53
+ ## Install
54
+
55
+ ```bash
56
+ pip install sooth # or: pip install -e ".[dev]" from source
57
+ export TYPESAFE_API_KEY=... # get one at console.typesafe.ai
58
+ ```
59
+
60
+ ## Usage
61
+
62
+ ```bash
63
+ sooth --source docs/policy.md --source tickets/t123.md --text draft-reply.md
64
+
65
+ # CI-friendly exit codes
66
+ sooth --source policy.md --text draft.md --format plain
67
+ # 0 = clean · 1 = any FAIL · 2 = any REVIEW · 3 = usage/config error
68
+
69
+ # Options
70
+ # --confidence T REVIEW below this confidence (default 0.7)
71
+ # --format md|plain
72
+ # -o FILE write report to file
73
+ # --log FILE append full judgment trace (one JSONL line per run)
74
+ ```
75
+
76
+ ## How it works
77
+
78
+ 1. Draft is split into claims (one sentence each).
79
+ 2. Each claim gets three questions to Jev, all fanned out in parallel batches: *is this checkable?*, *does the source support it?* (`supports` / `contradicts` / `not_found`), and *do all details match exactly?*
80
+ 3. Verdicts are mapped in code: uncheckable → `UNCHECKABLE`; low confidence → `REVIEW`; then `PASS` / `FAIL`. Safeguards demote `PASS` to `REVIEW` when details drift or claim numbers are absent from the source (checked in plain code).
81
+ 4. The report shows the full probability distribution per claim — not just a label.
82
+
83
+ The decision logic lives in [`src/sooth/verify.py`](src/sooth/verify.py) in a dozen readable lines. Change thresholds and rules there, not in prompts.
84
+
85
+ ## Known limits (alpha)
86
+
87
+ - Safeguards catch most drift and smuggling: claim numbers absent from the source demote `PASS` to `REVIEW`, and a detail-match gate flags altered hedges ("about 74%" → "over 74%"). Not perfect — read the `Why` column before trusting a verdict.
88
+ - Derived numbers (totals, values computed outside the source) look "missing" and land in `REVIEW`.
89
+ - Source + questions must fit ~64k tokens — split long documents yourself.
90
+
91
+ ## Development
92
+
93
+ ```bash
94
+ pip install -e ".[dev]"
95
+ python3 tests/test_core.py # pure checks, no network
96
+ pytest # same suite
97
+ bash tests/smoke.sh # live smoke (needs TYPESAFE_API_KEY)
98
+ PYTHONPATH=src python3 tests/calibrate.py # live calibration, 30 labeled claims
99
+ ```
100
+
101
+ Docs: [PRD](docs/PRD.md) · [Design](docs/DESIGN.md) · [Testing](docs/TESTING.md)
102
+
103
+ ## Roadmap
104
+
105
+ - v0.2 shipped: source-span evidence (the exact source line behind each verdict, shown in the report)
106
+ - later: hosted web app — paste UI, history, team review queues
107
+
108
+ ## License
109
+
110
+ MIT © Naufal Hilmiaji. Powered by [TypeSafe](https://typesafe.ai) / Jev.
sooth-0.2.1/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # Sooth
2
+
3
+ Verify AI-generated text against source material. Claim-by-claim **PASS / FAIL / REVIEW**, with calibrated probabilities you can act on in CI.
4
+
5
+ Built on [Jev](https://docs.typesafe.ai) (TypeSafe System One) — typed judgments and probabilities instead of generated prose. Every verdict shows its probability distribution, and the combination rules are plain code you can read. No black box.
6
+
7
+ ```
8
+ sooth --source policy.md --text draft-reply.md
9
+ ```
10
+
11
+ Real output (planted errors in the draft vs a news source):
12
+
13
+ ```
14
+ # Sooth
15
+
16
+ **PASS 2 · FAIL 3 · REVIEW 1 · UNCHECKABLE 1** — threshold 0.70
17
+
18
+ | # | Claim | Verdict | P | Why (P distribution) | Evidence |
19
+ |---|-------|---------|---|----------------------|----------|
20
+ | 3 | BNBR baru menuntaskan rights issue bernilai besar di harga Rp 33. | ❌ FAIL | ████████ 1.00 | contradicts 1.00 / not_found 0.00 / supports 0.00 · details 0.01 · missing #s: 33 | `examples/news-1.md:25` "Saham ini juga baru menyelesaikan rights issue dalam juml…" |
21
+ | 5 | BUMI hanya perlu turun sekitar 10% untuk menyentuh level Rp 50. | ❌ FAIL | ████████ 1.00 | contradicts 1.00 / not_found 0.00 / supports 0.00 · details 0.01 · missing #s: 10 | `examples/news-1.md:23` "Adapun PT Bumi Resources Tbk (BUMI) di sekitar Rp192 haru…" |
22
+ | 6 | BEI juga menetapkan batas atas harga saham Rp 5.000 per saham. | ⚠️ REVIEW | ████████ 1.00 | contradicts 0.00 / not_found 1.00 / supports 0.00 · details 0.02 · missing #s: 5.000 | |
23
+ | 7 | Para investor ritel sangat senang dengan aturan baru ini. | ➖ UNCHECKABLE | ██░░░░░░ 0.24 | P(checkable)=0.24 | |
24
+ ```
25
+
26
+ ## Why
27
+
28
+ AI writes fast, nobody checks. Claims ship wrong. Sooth checks each claim against the evidence you give it — and when it is unsure, it says `REVIEW` instead of guessing.
29
+
30
+ ## Install
31
+
32
+ ```bash
33
+ pip install sooth # or: pip install -e ".[dev]" from source
34
+ export TYPESAFE_API_KEY=... # get one at console.typesafe.ai
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ ```bash
40
+ sooth --source docs/policy.md --source tickets/t123.md --text draft-reply.md
41
+
42
+ # CI-friendly exit codes
43
+ sooth --source policy.md --text draft.md --format plain
44
+ # 0 = clean · 1 = any FAIL · 2 = any REVIEW · 3 = usage/config error
45
+
46
+ # Options
47
+ # --confidence T REVIEW below this confidence (default 0.7)
48
+ # --format md|plain
49
+ # -o FILE write report to file
50
+ # --log FILE append full judgment trace (one JSONL line per run)
51
+ ```
52
+
53
+ ## How it works
54
+
55
+ 1. Draft is split into claims (one sentence each).
56
+ 2. Each claim gets three questions to Jev, all fanned out in parallel batches: *is this checkable?*, *does the source support it?* (`supports` / `contradicts` / `not_found`), and *do all details match exactly?*
57
+ 3. Verdicts are mapped in code: uncheckable → `UNCHECKABLE`; low confidence → `REVIEW`; then `PASS` / `FAIL`. Safeguards demote `PASS` to `REVIEW` when details drift or claim numbers are absent from the source (checked in plain code).
58
+ 4. The report shows the full probability distribution per claim — not just a label.
59
+
60
+ The decision logic lives in [`src/sooth/verify.py`](src/sooth/verify.py) in a dozen readable lines. Change thresholds and rules there, not in prompts.
61
+
62
+ ## Known limits (alpha)
63
+
64
+ - Safeguards catch most drift and smuggling: claim numbers absent from the source demote `PASS` to `REVIEW`, and a detail-match gate flags altered hedges ("about 74%" → "over 74%"). Not perfect — read the `Why` column before trusting a verdict.
65
+ - Derived numbers (totals, values computed outside the source) look "missing" and land in `REVIEW`.
66
+ - Source + questions must fit ~64k tokens — split long documents yourself.
67
+
68
+ ## Development
69
+
70
+ ```bash
71
+ pip install -e ".[dev]"
72
+ python3 tests/test_core.py # pure checks, no network
73
+ pytest # same suite
74
+ bash tests/smoke.sh # live smoke (needs TYPESAFE_API_KEY)
75
+ PYTHONPATH=src python3 tests/calibrate.py # live calibration, 30 labeled claims
76
+ ```
77
+
78
+ Docs: [PRD](docs/PRD.md) · [Design](docs/DESIGN.md) · [Testing](docs/TESTING.md)
79
+
80
+ ## Roadmap
81
+
82
+ - v0.2 shipped: source-span evidence (the exact source line behind each verdict, shown in the report)
83
+ - later: hosted web app — paste UI, history, team review queues
84
+
85
+ ## License
86
+
87
+ MIT © Naufal Hilmiaji. Powered by [TypeSafe](https://typesafe.ai) / Jev.
@@ -0,0 +1,159 @@
1
+ # Technical Design — Sooth v0.1
2
+
3
+ Companion to `PRD.md`. Stack and data flow only. No speculative layers.
4
+
5
+ ## Stack
6
+
7
+ - Python 3.11+, official `typesafe-sdk` package (`TypeSafeClient.system_one`), stdlib otherwise (argparse, re, json, pathlib).
8
+ - No web framework, no DB, no plugin system. Four modules + CLI entry is enough.
9
+
10
+ ## Layout (src layout, standard packaging)
11
+
12
+ ```
13
+ src/sooth/
14
+ __init__.py # package API
15
+ claims.py # split text → claims (pure)
16
+ verify.py # build Jev questions, call API, map answers → verdicts (pure map + one I/O call)
17
+ report.py # verdicts → markdown / plain text + JSONL log record (pure)
18
+ cli.py # argparse, file I/O, exit codes, --log writer
19
+ tests/
20
+ conftest.py
21
+ test_core.py # asserts for pure functions + verdict mapping
22
+ smoke.sh # optional live smoke (real key)
23
+ examples/ # fixture: source.md, draft.md
24
+ docs/ # PRD, DESIGN, TESTING
25
+ ```
26
+
27
+ ## Flow
28
+
29
+ ```
30
+ sources + draft
31
+ → claims.split(draft) → list[Claim]
32
+ → verify.batch(claims, sources) → list[Verdict] # 1..n Jev calls
33
+ → report.render(verdicts) → markdown string
34
+ → stdout / -o file
35
+ → optional JSONL log append
36
+ ```
37
+
38
+ Pure functions everywhere except `verify.call_jev()` and file I/O. Testable without network.
39
+
40
+ ## Claim splitting (`claims.py`)
41
+
42
+ v0.1: sentence splitter on `.`/`!`/`?` + newline, keeping numbers intact (`3.5 days`). Drop empties and pure questions? No — questions are claims too ("Does it support X?" is checkable as written intent… actually drop interrogatives and headings, they are not claims). Rules:
43
+
44
+ - Keep: declarative sentences, ≥ 4 words.
45
+ - Skip: headings (`# …`), list bullets' leading markers kept as text, sentences < 4 words, lines that end with `?`.
46
+ - Each claim keeps `text` + `source_span` = `{file, line}` of origin (for "claim #3 came from draft line 12").
47
+
48
+ ```python
49
+ # ponytail: regex splitter, mis-splits quotes/abbreviations; upgrade to clause-level when real drafts demand
50
+ ```
51
+
52
+ ## Jev request (`verify.py`)
53
+
54
+ One request per batch of ≤ 30 claims. State is shared; questions reference claims by path.
55
+
56
+ ```jsonc
57
+ // state
58
+ {
59
+ "sources": [
60
+ { "name": "policy.md", "text": "…" }, // concatenated if many; name kept for evidence line
61
+ { "name": "tickets/t123.md", "text": "…" }
62
+ ],
63
+ "claims": [
64
+ { "id": "c1", "text": "Refunds are processed in 3 days." },
65
+ { "id": "c2", "text": "We support Bitcoin." }
66
+ ]
67
+ }
68
+ ```
69
+
70
+ Per claim, **four parallel questions** (fan-out pattern; statement text is embedded in the instructions):
71
+
72
+ ```jsonc
73
+ "c1_checkable": {
74
+ "type": "noul",
75
+ "instructions": "Statement: <claim text>\n\nIs the statement a concrete factual claim that the evidence in `sources` could support or contradict? ...",
76
+ // noul answer = P(checkable)
77
+ },
78
+ "c1_verdict": {
79
+ "type": "choice",
80
+ "instructions": "Statement: <claim text>\n\nDoes the evidence in `sources` support the statement?",
81
+ "criteria": {
82
+ "supports": "...",
83
+ "contradicts": "...",
84
+ "not_found": "..."
85
+ }
86
+ // choice + probabilities + confidence
87
+ },
88
+ "c1_details": {
89
+ "type": "noul",
90
+ "instructions": "Statement: <claim text>\n\nDoes EVERY specific detail — names, numbers, dates, comparisons such as 'more than' or 'about' — exactly match the evidence in `sources`? ...",
91
+ // noul answer = P(all details match)
92
+ },
93
+ "c1_evidence": {
94
+ "type": "choice",
95
+ "instructions": "Statement: <claim text>\n\nWhich candidate segment best supports or contradicts the statement? Full text of each id is in `segments`. Choose 'none' if no segment is relevant.",
96
+ "criteria": { "s12": "<first 80 chars of candidate>", "...": "…", "none": "No segment is relevant to the statement." }
97
+ // code pre-filters ~6 candidate ids per claim (word overlap + number hits)
98
+ }
99
+ ```
100
+
101
+ - 30 claims → 120 questions, one call. Over batch cap or 422 → split and retry half (SDK retries 429/529 already).
102
+ - Pin model `jev-1.13.0` (thresholds tuned against it). Constant in `verify.py`.
103
+ - API key: env `TYPESAFE_API_KEY`. Missing → exit 3 with one-line hint.
104
+
105
+ ## Verdict mapping (pure)
106
+
107
+ ```
108
+ if p_checkable < 0.5: → UNCHECKABLE
109
+ elif confidence < threshold: → REVIEW
110
+ elif choice == "supports": → PASS
111
+ elif choice == "contradicts": → FAIL
112
+ else: → REVIEW # not_found
113
+ ```
114
+
115
+ Then safeguards (`apply_safeguards`, pure) — demote `PASS` → `REVIEW` when:
116
+
117
+ - `details_p < 0.5` (detail-gate Noul says some detail drifted), or
118
+ - `missing_numbers(claim, sources)` non-empty — claim numbers absent from every source (regex extraction, separator-normalized; no model involved).
119
+
120
+ FAIL/REVIEW/UNCHECKABLE pass through untouched.
121
+
122
+ `Verdict = {claim_id, claim_text, draft_line, kind, p_checkable?, choice?, probabilities, confidence, details_p?, missing_numbers}`
123
+
124
+ Evidence (v0.2, pre-parsed selection pattern): sources are split into sentence `segments`; code ranks ~6 candidates per claim (word overlap, numbers weighted ×3); a per-claim Choice selects the best span (`none` allowed). Report shows `source:line` + snippet. Best-effort — `none` is valid when no span matches.
125
+
126
+ (If PRD table showed source snippets — that is v0.2. v0.1 report substitutes distribution; PRD example is target UX.)
127
+
128
+ ## Report (`report.py`)
129
+
130
+ Markdown: summary line (`PASS n · FAIL n · REVIEW n · UNCHECKABLE n`), then one table as in PRD, ASCII probability bar in P column (`██████░░ 0.91` is enough; no unicode-only requirement). FAIL rows first? No — keep draft order, print summary counts first. `REVIEW` and `UNCHECKABLE` share a section below the table if any.
131
+
132
+ ## Decision log (`--log`)
133
+
134
+ Append-only JSONL, one object per run (not per claim — one line, pretty fields):
135
+
136
+ ```json
137
+ {"ts": "…", "model": "jev-1.13.0", "threshold": 0.7, "sources": ["policy.md"], "draft": "draft-reply.md", "usage": {"input_tokens": 0, "output_tokens": 0}, "results": [ …verdicts with full probabilities… ]}
138
+ ```
139
+
140
+ This is the decision-ledger seed (audit trail). No viewer in v0.1.
141
+
142
+ ## Exit codes
143
+
144
+ | Code | Meaning |
145
+ |------|---------|
146
+ | 0 | all PASS or UNCHECKABLE only |
147
+ | 1 | any FAIL |
148
+ | 2 | any REVIEW (no FAIL) |
149
+ | 3 | usage / missing key / unreadable file |
150
+
151
+ ## Errors
152
+
153
+ - SDK raises typed errors; catch at CLI boundary, print one line to stderr, exit 3 (or 2 on API failure mid-run? → exit 3, "could not verify", never print a partial report marked complete).
154
+ - Empty draft → usage error. Empty sources → usage error (nothing to check against).
155
+
156
+ ## Security
157
+
158
+ - Key via env only, never logged, never in `--log`.
159
+ - Files read as UTF-8 text. No shell-out, no eval. `--log` path user-controlled write (documented).
@@ -0,0 +1,18 @@
1
+ # Dogfood log — week of 2026-09-25
2
+
3
+ Run Sooth on your own real AI output before building anything new. One row per session. Findings here drive Phase 4/5 decisions.
4
+
5
+ ## Loop (5 min per draft)
6
+
7
+ 1. Take an AI draft you were about to trust (reply, summary, PR description, doc).
8
+ 2. `sooth --source <the ground truth> --text <the draft> --log dogfood.jsonl`
9
+ 3. Read the report. Mark each verdict right/wrong by your own eyes.
10
+ 4. Log one row below.
11
+
12
+ | Date | Draft type | Verdicts right/wrong | Surprise (if any) | Want next |
13
+ |------|------------|----------------------|-------------------|-----------|
14
+ | | | | | |
15
+
16
+ ## Question this answers
17
+
18
+ Does anyone actually paste ground truth + draft and act on the report? If the tool dies on real drafts (formatting, length, language), Phase 4 chunking/URL input jumps the queue. If the report is ignored even when right, the web app idea is wrong.
@@ -0,0 +1,90 @@
1
+ # PRD — Sooth (v0.1)
2
+
3
+ ## Problem
4
+
5
+ AI writes text fast. Nobody checks it. Wrong claims ship. In 2026 most drafts (support replies, docs, reports, PR descriptions) are AI-written and reviewed by skimming humans.
6
+
7
+ Sooth is a cheap, automatic checker: does each claim in the AI draft actually follow from the source material?
8
+
9
+ It targets the AI-era verification gap: AI writes fast, nobody checks before shipping. Opaque AI decisions are addressed by design: every verdict shows its probability distribution, and `--log` records the full judgment trace.
10
+
11
+ ## What it is
12
+
13
+ CLI tool. Input: source text(s) + AI-generated draft. Output: a **trust report** — every claim marked `PASS` / `FAIL` / `REVIEW`, with probability, reason, and source snippet.
14
+
15
+ Engine by [Jev](https://docs.typesafe.ai) (TypeSafe System One): typed judgments + calibrated probabilities, not generated prose.
16
+
17
+ ## Who it is for (v0.1)
18
+
19
+ Developers and technical teams who already work in a terminal and pipe AI output into things: CI jobs, agent pipelines, release notes, support macros.
20
+
21
+ ## User story
22
+
23
+ ```
24
+ sooth --source docs/policy.md --source tickets/t123.md --text draft-reply.md
25
+ ```
26
+
27
+ 1. User gives sources (ground truth) and draft (AI text to verify).
28
+ 2. Tool splits draft into claims (sentence per claim in v0.1).
29
+ 3. Each claim judged against sources: supported / contradicted / not found — plus "is this even checkable?".
30
+ 4. Report printed as markdown. Low-confidence claims land in `REVIEW` instead of fake-precise verdicts.
31
+ 5. Optional `--log decisions.jsonl` appends full judgment trace (audit trail seed).
32
+
33
+ ## Report shape (contract)
34
+
35
+ | # | Claim | Verdict | P | Why (v0.1) |
36
+ |---|-------|---------|---|------------|
37
+ | 1 | Refunds are processed in 3 days. | ✅ PASS | 0.91 | supports .91 / contradicts .02 / not_found .07 |
38
+ | 2 | We support Bitcoin. | ❌ FAIL | 0.87 | supports .03 / contradicts .87 / not_found .10 |
39
+ | 3 | Customers love the change. | ⚠️ REVIEW | 0.54 | not found in sources |
40
+
41
+ "Why" = probability distribution. Evidence = the source span the model selected (`source:line` + snippet).
42
+
43
+ Verdict rules (fixed, readable in code):
44
+
45
+ - claim not checkable (opinion, vague) → `UNCHECKABLE` (listed under REVIEW section)
46
+ - `supports` + confidence ≥ threshold → `PASS`
47
+ - `contradicts` + confidence ≥ threshold → `FAIL`
48
+ - `not in source` **or** confidence < threshold → `REVIEW`
49
+
50
+ Default threshold 0.7, flag `--confidence`.
51
+
52
+ ## MVP scope
53
+
54
+ In:
55
+
56
+ - CLI: `sooth --source … [--source …] --text … [--confidence T] [--log FILE] [--format md|plain]`
57
+ - Plain text / markdown input (UTF-8). Multiple sources.
58
+ - Sentence-per-claim splitting.
59
+ - Jev fan-out verification (batched).
60
+ - Markdown report to stdout (or `-o FILE`).
61
+ - JSONL decision log (`--log`).
62
+ - Exit codes for CI: `0` all PASS/CLEAN, `1` any FAIL, `2` any REVIEW remaining, `3` usage/config error.
63
+
64
+ Out (v0.1 — later or never):
65
+
66
+ - Web UI (paid phase), PDF/HTML parsing, images
67
+ - Smarter claim splitting (clauses, bullet merging)
68
+ - Team features, history, webhooks
69
+ - Non-English tuning (Jev is English-strongest)
70
+ - Custom question editing UI
71
+
72
+ ## Success criteria (v0.1 done when)
73
+
74
+ 1. `sooth` runs end-to-end against a sample fixture and prints the report above.
75
+ 2. On a hand-labeled set of 30 claims (10 supported / 10 contradicted / 10 absent-or-fluff): ≥ 24 correct verdicts after threshold tuning, no confident (≥0.7) wrong `PASS` on a contradicted claim in the set.
76
+ 3. CI-usable: exit codes behave; works without network when `--log`-replaying a fixture later (replay is stretch).
77
+ 4. One new user understands the output with no explanation beyond `--help`.
78
+
79
+ ## Monetization (later — not built now)
80
+
81
+ Free OSS: CLI + library. Paid web app later: paste UI, history, team folders, "verify before publish" API. Same engine. No paywall logic in v0.1.
82
+
83
+ ## Open decisions (defaults chosen, reversible)
84
+
85
+ | Decision | Default | Note |
86
+ |----------|---------|------|
87
+ | Language | Python 3.11+ | official Jev Python SDK |
88
+ | Claim split | sentence regex | upgrade when it mis-splits real drafts |
89
+ | Batch size | 30 claims/request | stay well under 64k token cap |
90
+ | REVIEW exit code | 2 | CI can choose to ignore |
@@ -0,0 +1,51 @@
1
+ # Tests & Verification Plan — Sooth v0.1
2
+
3
+ Three layers: fast pure checks (always), live API smoke (optional), accuracy calibration (human, before release). Matches `DESIGN.md` module split.
4
+
5
+ ## 1. Unit / pure checks — `tests/test_core.py`
6
+
7
+ One file, plain asserts, run with `python -m tests.test_core` or pytest if already installed. No network. Covers the logic that would silently corrupt verdicts:
8
+
9
+ | Area | Cases (assert each) |
10
+ |------|---------------------|
11
+ | `claims.split` | sentence boundaries; `3.5 days` stays one claim; headings skipped; `?` lines skipped; `< 4` words skipped; `draft_line` correct |
12
+ | verdict mapping | checkable<0.5 → UNCHECKABLE; low confidence → REVIEW; supports/contradicts/not_found → PASS/FAIL/REVIEW at high confidence; threshold boundary inclusive |
13
+ | `report.render` | counts line correct; every claim appears once; bar length ∝ probability; empty verdicts list → still prints summary |
14
+ | log writer | one JSON line per run; round-trips `json.loads`; no `api_key` field anywhere |
15
+
16
+ Entry bar: `python -m tests.test_core` exits 0 on a clean tree. This is the one runnable check left behind with the code.
17
+
18
+ ## 2. Live smoke (optional, real key)
19
+
20
+ - Fixture `examples/`: `source.md` (short policy: refunds in 3 days, card-and-bank only), `draft.md` (3 claims: one supported, one contradicted, one fluff).
21
+ - `sooth --source examples/source.md --text examples/draft.md`
22
+ - Pass when: process exits with code matching expected verdicts (1 FAIL present → exit 1), report table has 3 rows, `--log /tmp/t.jsonl` has exactly 1 line with `model` = pinned version.
23
+ - Script it as `tests/smoke.sh` (5 lines). Not run in unit CI. Skip entirely when `TYPESAFE_API_KEY` unset.
24
+
25
+ ## 3. Accuracy calibration (live, labeled set)
26
+
27
+ Goal from PRD: ≥ 24/30 correct on a labeled set, zero confident-wrong `PASS` on contradicted claims.
28
+
29
+ - Labeled set: `examples/calibration.json` — 30 claims across `news-1.md`, `policy.md`, `pricing.md` (10 supported / 10 contradicted / 10 absent-or-opinion, each with an `expected` verdict and a note).
30
+ - Runner: `PYTHONPATH=src python3 tests/calibrate.py [--confidence T]` — batches live Jev calls, prints the confusion matrix, exits 0 only when the bar is met.
31
+ - Frozen matrix + notes: `examples/calibration.md` (threshold 0.7, `jev-1.13.0`, 30/30).
32
+ - Never move the threshold to hide a confident-wrong `PASS` — fix question wording instead (`instructions`/`criteria` in `verify.py`). Bump model version → re-run this layer.
33
+
34
+ ## 4. Manual UX check (10 min)
35
+
36
+ - [ ] `sooth --help` alone explains enough to run.
37
+ - [ ] Report readable by someone who never saw the tool (show one person).
38
+ - [ ] Missing key → one-line hint mentioning `TYPESAFE_API_KEY`, exit 3, no stack trace.
39
+ - [ ] Exit codes: all-PASS fixture → 0; with-REVIEW fixture → 2; with-FAIL → 1.
40
+
41
+ ## 5. Definition of done (v0.1)
42
+
43
+ 1. `tests/test_core.py` green.
44
+ 2. Smoke fixture behaves (§2) with a real key at least once.
45
+ 3. Calibration table recorded (§3) and default threshold justified by it.
46
+ 4. Manual checklist (§4) done.
47
+ 5. Docs match reality: report column that shows probability distribution (not source snippets) — snippets deferred to v0.2 per `DESIGN.md`.
48
+
49
+ ## Out of scope for testing now
50
+
51
+ Load/perf (CLI is one-shot), fuzzing, multi-language accuracy, regression CI on live API (costs money; add when thresholds ship to users).