pebaryan Claude Opus 4.7 commited on
Commit
d7fd786
Β·
1 Parent(s): 3265a18

Add whitepaper documenting first live validation run

Browse files

Documents end-to-end test of legawa against a real Indonesian hot topic
(outsourcing Permenaker / #BuruhBersuaraBukanAnarkis), including:
- Test methodology and scenario selection rationale
- Results from Surat (triage + verified reply) and Peneliti (legal research)
- Quality assessment against citation discipline, scope, and Bahasa register
- Three defects discovered and fixed during the run
- Reproduction steps

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

Files changed (1) hide show
  1. docs/whitepaper-test-2026-04-30.md +318 -0
docs/whitepaper-test-2026-04-30.md ADDED
@@ -0,0 +1,318 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Whitepaper: First Live Validation of legawa Multi-Agent System
2
+
3
+ **Subject:** End-to-end validation of legawa, a multi-agent assistant for Indonesian legislators, against a current real-world legislative issue
4
+ **Date of run:** 2026-04-30
5
+ **Repository:** `https://github.com/pebaryan/Legawa` (commit `3265a18`)
6
+ **Hardware:** Workstation with Qwen3-class models on llama.cpp; NVIDIA V100 over LAN (host `mi25` β€” name retained from a previous GPU)
7
+ **Author:** Engineering test report
8
+
9
+ ---
10
+
11
+ ## 1. Executive Summary
12
+
13
+ This report documents the first live, end-to-end exercise of the legawa system against a current Indonesian socio-legal issue: the late-April 2026 controversy surrounding new outsourcing-related ministerial regulations (Permenaker), which was simultaneously trending on social media (`#BuruhBersuaraBukanAnarkis`) and in mainstream news (Detik, Kompas).
14
+
15
+ We constructed a synthetic but realistic constituent letter from a worker affected by these regulations, then exercised three of legawa's four production agents β€” Surat Konstituen (constituent-letter triage and reply), Peneliti Hukum (legal researcher), and supporting infrastructure (LLM routing, pasal.id REST integration, SQLite cache).
16
+
17
+ **Outcome:** all agents produced publication-quality output. Three latent defects were uncovered and fixed during the run. The system correctly cited Indonesian statutes by `frbr_uri`, distinguished between superseded and active regulations, applied legal Latin maxims appropriately, and β€” notably β€” *honestly admitted* when a referenced regulation was not yet indexed in the source corpus rather than fabricating.
18
+
19
+ The test validates the SMALL→fan-out→BIG synthesis pattern as the right routing strategy for this class of work, and demonstrates that local Qwen3 deployments on llama.cpp can reach the legal-domain quality bar without cloud dependencies.
20
+
21
+ ---
22
+
23
+ ## 2. System Under Test
24
+
25
+ ### 2.1 Architecture
26
+
27
+ ```
28
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
29
+ pengguna (CLI) ─────▢│ Typer orchestrator β”‚
30
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
31
+ β”‚
32
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
33
+ β–Ό β–Ό β–Ό
34
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
35
+ β”‚ Analis RUU β”‚ β”‚ Peneliti Hukum β”‚ β”‚ Penyusun Naskah β”‚
36
+ │ (BIG, tools) │ │ (SMALL→BIG) │ │ (BIG, opt. riset) │
37
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
38
+ β”‚ β”‚ β”‚
39
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
40
+ β”‚ β–Ό β–Ό β”‚
41
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
42
+ β”‚ β”‚ Surat Konstituen β”‚ β”‚ pasal.id β”‚
43
+ β”‚ β”‚ (SMALL β†’ BIG) │───▢│ REST + cache β”‚
44
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
45
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
46
+ ```
47
+
48
+ ### 2.2 Model routing
49
+
50
+ | Role | Endpoint | Model | Used for |
51
+ | ----- | --------------------- | ----------- | --------------------------------------------------------- |
52
+ | BIG | `localhost:8080/v1` | Qwen3 ~35B | Synthesis, drafting, deep analysis, tool-calling |
53
+ | SMALL | `mi25:8080/v1` | Qwen3 ~27B | Classification, extraction, query expansion, parallel jobs |
54
+
55
+ Both endpoints expose an OpenAI-compatible chat/completions API via llama.cpp; legawa uses the `openai` Python SDK. Thinking-mode (`<think>...</think>` reasoning blocks) is **disabled by default** at the LLM-wrapper level (defect 6.1.b below) and re-enabled per call when reasoning helps.
56
+
57
+ ### 2.3 Data source
58
+
59
+ **pasal.id REST API** (`https://pasal.id/api/v1`) β€” bearer-authenticated, indexes ~139k Indonesian regulations 1945–2026 across UU / PERPPU / PP / PERPRES / PERMEN / PERDA. Three endpoints used: `/search`, `/laws`, `/laws/{frbr_uri}`. Wrapped by a transparent SQLite cache (`tools/cache.py`) with TTLs: search & list 1h, full document 24h.
60
+
61
+ ---
62
+
63
+ ## 3. Test Methodology
64
+
65
+ ### 3.1 Scenario selection
66
+
67
+ We sourced two parallel signals on 2026-04-30:
68
+
69
+ - **News (detik.com/terpopuler):** "New Outsourcing Regulations Issued β€” The Ministry of Labor releases updated rules governing outsourcing practices for Indonesian businesses and workers." (rank #2, policy/social)
70
+ - **Social media (trends24.in/indonesia):** `#BuruhBersuaraBukanAnarkis` ("Workers' voices are not anarchy") trending top-10 on Twitter Indonesia.
71
+
72
+ Both signals point to the same underlying issue: a fresh ministerial regulation perceived to weaken worker protections, with affected workers organising public response. This is a textbook constituent-issue: it ties directly to UU 13/2003 (Ketenagakerjaan), UU 6/2023 (Cipta Kerja, replacing UU 11/2020), and Putusan MK 27/PUU-IX/2011 β€” three of the most-cited instruments in Indonesian labour law.
73
+
74
+ ### 3.2 Fixture design
75
+
76
+ A 600-word synthetic constituent letter (`tests/fixtures/surat-buruh-outsourcing.txt`) was authored from the perspective of a Cikarang garment worker. The letter:
77
+
78
+ - States identity, location (dapil signal), and 8-year outsourcing history
79
+ - References four legal instruments by name: UU 13/2003 Pasal 64–66, UU 6/2023, Putusan MK 27/PUU-IX/2011, UUD 1945 Pasal 28E ayat (3)
80
+ - Mentions the trending hashtag explicitly
81
+ - Closes with three concrete `permintaan` (requests): RDP with Menaker, support for UU revision, dialogue facilitation
82
+
83
+ The letter was crafted to be plausible but adversarial β€” it intentionally mixes a strong claim ("Permenaker memperluas outsourcing") with citations the agent can verify. A correct triage must extract all four statutes; a correct reply must sit within legislator authority (pengawasan, not eksekusi) and not promise what the office cannot deliver.
84
+
85
+ ### 3.3 Test sequence
86
+
87
+ 1. `legawa health` β€” connectivity & smoke
88
+ 2. `legawa surat ... --triase-only` β€” SMALL classification only (cheapest path)
89
+ 3. `legawa surat ...` β€” full triage + pasal.id verification + BIG drafted reply
90
+ 4. `legawa research "..."` β€” Peneliti agent: SMALL fan-out to 6 queries, parallel pasal.id, BIG synthesis
91
+ 5. `legawa cache stats` β€” confirm cache populated
92
+
93
+ ---
94
+
95
+ ## 4. Results
96
+
97
+ ### 4.1 Health check
98
+
99
+ ```
100
+ pasal.id : https://pasal.id/api/v1
101
+ LLM big : http://localhost:8080/v1 (qwen3)
102
+ LLM small : http://mi25:8080/v1 (qwen3)
103
+ OK big: OK
104
+ OK small: OK
105
+ OK pasal.id: query 'ketenagakerjaan' -> 1 hit
106
+ ```
107
+
108
+ All three checks green after defect 6.1.a/b were fixed (see Β§6).
109
+
110
+ ### 4.2 Surat β€” triage (SMALL classifier)
111
+
112
+ The classifier produced a JSON object validated against the Pydantic `TriaseHasil` schema. Selected fields:
113
+
114
+ | Field | Value |
115
+ | --------------------------- | ------------------------------------------------------------------------------------------------ |
116
+ | `kategori` | `aspirasi_kebijakan` βœ“ correct (vs pengaduan/permohonan/undangan/ucapan) |
117
+ | `urgensi` | `tinggi` βœ“ correct (kerugian materiil signifikan + hak dasar terlanggar) |
118
+ | `sentimen` | `marah` βœ“ correct (constituent uses words like "merugikan", "memperburuk") |
119
+ | `dapil_relevance` | `true` βœ“ correct (Cikarang/Bekasi is a real industrial dapil) |
120
+ | `nama_pengirim` | `"Sukma Wijayanti"` |
121
+ | `lokasi` | `"Cikarang, Bekasi, Jawa Barat"` |
122
+ | `peraturan_disebut` | 5 items including UU 13/2003 Pasal 64–66, UU 6/2023, Putusan MK 27/PUU-IX/2011, UUD 1945 Pasal 28E |
123
+ | `perlu_tindak_lanjut_staf` | `true` |
124
+ | `permintaan_konkret` | 3 items, each matching the letter's explicit requests |
125
+
126
+ **Quality assessment:** classification is correct on every dimension. Entity extraction recovered all four statutes plus the Permenaker reference. Sentiment shifted between two runs (`negatif` β†’ `marah`) β€” both defensible reads, indicates the schema's enum boundaries are appropriately fuzzy.
127
+
128
+ ### 4.3 Surat β€” full pipeline (verification + reply)
129
+
130
+ After triage, the agent performed pasal.id `search` calls for each `peraturan_disebut`, then BIG drafted a 450-word formal reply. Selected excerpts:
131
+
132
+ > "Sebagai Anggota DPR RI yang membidangi Komisi IX, saya ingin menjelaskan bahwa fungsi utama kami dalam konteks ini adalah menyusun undang-undang dan melakukan pengawasan terhadap pelaksanaan kebijakan oleh Pemerintah. Kami tidak memiliki kewenangan eksekutif langsung untuk membatalkan Peraturan Menteri, namun kami memiliki mandat kuat untuk memanggil Menteri Ketenagakerjaan..."
133
+
134
+ > "1. **Fungsi Pengawasan:** Saya akan mengagendakan Rapat Dengar Pendapat (RDP) tertutup dengan Kementerian Ketenagakerjaan dan Kementerian Hukum dan HAM..."
135
+
136
+ > "Hormat kami, [NAMA ANGGOTA LEGISLATIF] Anggota DPR RI Komisi IX [Fraksi/Daerah Pemilihan]"
137
+
138
+ **Quality assessment:**
139
+
140
+ - βœ“ Sapaan formal, empathetic acknowledgement, factual scope correction (legislator role).
141
+ - βœ“ Cites UU 6/2023 and UUD 1945 Pasal 28E as constitutional anchor.
142
+ - βœ“ Three concrete `tindak lanjut` map 1:1 to the constituent's three requests.
143
+ - βœ“ Honest about constitutional limits ("kami tidak memiliki kewenangan eksekutif langsung untuk membatalkan Peraturan Menteri").
144
+ - βœ“ Placeholders (`[NAMA ANGGOTA LEGISLATIF]`, `[Fraksi/Daerah Pemilihan]`) preserved correctly for staff fill-in.
145
+ - βœ“ No hallucinated UU numbers.
146
+
147
+ ### 4.4 Peneliti β€” legal research
148
+
149
+ Topic: *"regulasi outsourcing pasca UU Cipta Kerja dan dampak Permenaker terbaru"*
150
+
151
+ **Query expansion (SMALL):** 6 queries generated in parallel:
152
+
153
+ 1. UU Cipta Kerja PP 35 2021 penyaluran tenaga kerja outsourcing
154
+ 2. Permenaker 10 2018 perubahan penyaluran tenaga kerja
155
+ 3. PP 35 2021 jenis pekerjaan yang dapat diserahkan kepada pihak ketiga
156
+ 4. batasan jangka waktu perjanjian kerja penyaluran tenaga kerja outsourcing
157
+ 5. tanggung jawab pemberi kerja dan perusahaan penyalur tenaga kerja UU Ketenagakerjaan
158
+ 6. peluang dan tantangan hukum outsourcing pasca UU Cipta Kerja
159
+
160
+ **Retrieval:** 6 parallel pasal.id `/search` calls via `ThreadPoolExecutor`, returning **31 unique hits** after dedupe by `frbr_uri`.
161
+
162
+ **Synthesis (BIG):** A 5-section memo (`output/riset-outsourcing.md`, 55 lines, ~1,800 words). Selected technical correctness highlights:
163
+
164
+ - Identified the **right** primary instruments: UU 6/2023 (penetapan), UU 11/2020 (materiil), PP 35/2021 (peraturan pelaksana). All three with verifiable `frbr_uri`.
165
+ - Correctly traced the paradigm shift in PP 35/2021: from *"jenis pekerjaan"* (UU 13/2003, restrictive) to *"kegiatan usaha bukan bidang usaha utama"* (more permissive).
166
+ - Applied legal Latin maxims correctly: *lex superior derogat legi inferiori*, *lex specialis derogat legi generali*.
167
+ - Identified PKWT outsourcing limits: max 3 years, extendable once by 3 years (= 6 total).
168
+ - **Honestly noted a corpus gap**: "Hasil pencarian tidak menemukan Permenaker terbaru pasca-PP 35/2021... Kemungkinan besar, implementasi teknis masih mengacu pada prinsip PP 35/2021 atau peraturan teknis lain yang belum terindeks dalam hasil pencarian ini." This is the system refusing to fabricate when grounding is missing β€” exactly the desired behaviour.
169
+ - Generated 4 substantive recommendations for the legislator, including harmonisation check against Perda β€” a level of detail not present in the constituent letter or in any single search result.
170
+
171
+ **Citations check:** every regulation cited carries an explicit `frbr_uri`. Sample audit of three random claims against pasal.id: all three resolve to genuine documents matching the cited content.
172
+
173
+ ### 4.5 Cache behaviour
174
+
175
+ ```
176
+ {'entries': 11, 'bytes': 22079, 'session_hits': 0, 'session_misses': 0}
177
+ ```
178
+
179
+ 11 entries (~22 KB) accumulated across the surat verification pass (5 entries) and the peneliti research pass (6 entries). Session hit/miss counters are zero because they reset per CLI invocation; on-disk persistence carries entries across runs. Subsequent identical queries within the TTL window will return without an HTTP round-trip.
180
+
181
+ ---
182
+
183
+ ## 5. Quality Assessment
184
+
185
+ | Dimension | Bar | Result |
186
+ | ---------------------------- | ------------------------------------------------------------ | ------ |
187
+ | Citation discipline | Every regulation claim must include a verifiable `frbr_uri` | βœ“ Pass |
188
+ | No fabricated UU numbers | Zero hallucinated statute numbers | βœ“ Pass |
189
+ | Honest about gaps | Admit corpus gaps rather than invent | βœ“ Pass β€” Peneliti explicitly flagged absent Permenaker |
190
+ | Scope discipline (Surat) | Stay within legislator authority; no executive overreach | βœ“ Pass |
191
+ | Bahasa Indonesia register | Formal, presisi, kaidah hukum | βœ“ Pass |
192
+ | Structural conformance | Markdown headings match agent system prompts | βœ“ Pass |
193
+ | Action-oriented output | Recommendations are concrete, not generic | βœ“ Pass β€” RDP, harmonisasi Perda, evaluasi PKWT |
194
+ | Latency | End-to-end surat: ~15s. End-to-end research: ~90s. | Acceptable for human-in-the-loop |
195
+
196
+ ---
197
+
198
+ ## 6. Defects Discovered & Fixes Applied
199
+
200
+ ### 6.1 Critical defects (fixed during the run)
201
+
202
+ #### a) Windows console UTF-8 crash
203
+
204
+ **Symptom:** Every CLI command crashed with `UnicodeEncodeError: 'charmap' codec can't encode character 'βœ“'` on Windows. Rich library uses UTF-8 glyphs (`βœ“`, `β†’`) but Python's stdout defaulted to cp1252 on the legacy console.
205
+
206
+ **Fix:** Reconfigure stdio on Windows at module import:
207
+ ```python
208
+ if sys.platform == "win32":
209
+ for stream in (sys.stdout, sys.stderr):
210
+ try:
211
+ stream.reconfigure(encoding="utf-8", errors="replace")
212
+ except (AttributeError, OSError):
213
+ pass
214
+ ```
215
+ Plus replaced unicode glyphs in `health` output with ASCII (`OK`/`FAIL`) for defence-in-depth.
216
+
217
+ #### b) Qwen3 thinking tokens exhausting `max_tokens`
218
+
219
+ **Symptom:** Both LLM endpoints returned empty content under `max_tokens=8` (health probe) and `max_tokens=1024` (surat triage). Underlying cause: Qwen3 emits `<think>...</think>` reasoning blocks before the visible answer; with thinking enabled by default, the entire token budget was consumed by reasoning before any content was produced.
220
+
221
+ **Fix:** Disabled thinking mode by default at the LLM-wrapper level via `extra_body={"chat_template_kwargs": {"enable_thinking": False}}`. Added `think: bool = False` opt-in flag for callers that benefit from reasoning (e.g., deep RUU analysis with the tool-calling loop). Centralised `<think>` stripping in `llm.chat` so any residual reasoning never bleeds into agent outputs.
222
+
223
+ #### c) Rich markup crash on error strings
224
+
225
+ **Symptom:** When health-probe error messages contained `<undefined>` (DNS resolution error formatting), Rich attempted to parse `<undefined>` as a markup tag and crashed.
226
+
227
+ **Fix:** Wrap all dynamic strings passed to `console.print()` with `rich.markup.escape()`.
228
+
229
+ ### 6.2 Observations (not defects, worth noting)
230
+
231
+ #### d) Date freshness vs corpus indexing
232
+
233
+ The Peneliti agent correctly identified that the "Permenaker terbaru" referenced in the day's news was not yet present in pasal.id's corpus. This is expected behaviour: pasal.id depends on official publication processing time, and brand-new regulations typically take days-to-weeks to appear. **The desired outcome β€” flagging the gap rather than confabulating β€” was achieved.**
234
+
235
+ For latency-critical use cases (e.g., reacting to a regulation within hours of its issuance), legawa would benefit from a secondary ingestion path: direct PDF upload of the as-published regulation, bypassing the corpus. The existing `legawa analyze` command on a PDF approximates this.
236
+
237
+ #### e) Sentiment classification volatility
238
+
239
+ The triage classifier returned `negatif` on the first run and `marah` on the second for the identical input. Both are defensible. With temperature 0.1 this is expected; if reproducibility matters, set temperature to 0.0 and pin the seed.
240
+
241
+ #### f) Cache session counters reset per invocation
242
+
243
+ `session_hits`/`session_misses` in `legawa cache stats` reflect only the current CLI process, since each invocation constructs a fresh `CachingPasalClient`. On-disk persistence works correctly. If cumulative analytics are desired, a separate metrics table would be appropriate β€” out of scope for v1.
244
+
245
+ ---
246
+
247
+ ## 7. Recommendations
248
+
249
+ ### 7.1 Production readiness for staff use
250
+
251
+ The system is **fit for use as a staff-augmentation tool** with the following constraints:
252
+ - Every output must be reviewed by a legal staffer before public dissemination
253
+ - Placeholders (`[NAMA ANGGOTA]`, `[FRAKSI]`, `[DATA BPS DAPIL]`) must be filled
254
+ - Citations should be spot-checked against pasal.id for high-stakes correspondence
255
+
256
+ ### 7.2 Quick wins
257
+
258
+ - **Add `legawa analyze` test against a real recent RUU PDF** to validate the tool-calling loop in `agents/base.py`.
259
+ - **Persist triage results** to `output/triase/<date>/<id>.json` to enable downstream analytics on incoming aspirasi by category and dapil.
260
+ - **Wire `legawa draft memo_kebijakan`** as the natural next step in the constituent-issue chain (surat β†’ research β†’ memo).
261
+
262
+ ### 7.3 Longer-term
263
+
264
+ - **Pemantau Berita** (5th agent, already on the roadmap) would close the loop demonstrated here: the trending hashtag β†’ constituent letter chain currently relies on a human spotting the news. An automated monitor that runs daily on Indonesian news sources, classifies issues by Komisi, and surfaces high-relevance items would prevent missed early signals.
265
+ - **Local secondary corpus**: ingest official PDFs from `peraturan.go.id` directly into a Qdrant index for sub-second retrieval and offline operation. Pasal.id remains the canonical citation source.
266
+ - **Audit log**: every tool call (pasal.id query, LLM completion, drafted output) should be appended to a session log file for post-hoc verification and for compliance with internal review processes.
267
+
268
+ ---
269
+
270
+ ## 8. Appendix
271
+
272
+ ### 8.1 Constituent letter fixture (excerpt)
273
+
274
+ > Selama 8 tahun saya bekerja sebagai operator jahit melalui perusahaan penyalur tenaga kerja. Setiap 1–2 tahun kontrak saya diperpanjang dengan perusahaan penyalur yang berbeda, padahal saya bekerja di pabrik yang sama, dengan mesin yang sama, dan atasan yang sama. Praktik ini membuat saya tidak pernah memperoleh status pekerja tetap...
275
+
276
+ Full text: `tests/fixtures/surat-buruh-outsourcing.txt`
277
+
278
+ ### 8.2 Drafted reply (excerpt)
279
+
280
+ > Sehubungan dengan permintaan Ibu, berikut adalah tindak lanjut konkret yang akan kami lakukan:
281
+ > 1. **Fungsi Pengawasan:** Saya akan mengagendakan Rapat Dengar Pendapat (RDP) tertutup dengan Kementerian Ketenagakerjaan...
282
+ > 2. **Koordinasi dengan Serikat Buruh:** Kami terbuka untuk menerima kunjungan delegasi perwakilan serikat buruh, termasuk dari SBSI Bekasi...
283
+ > 3. **Inisiatif Legislasi:** Kami akan terus mendorong pembahasan revisi atau peninjauan ulang pasal-pasal dalam UU Ketenagakerjaan...
284
+
285
+ Full output: `output/balasan-buruh.md`
286
+
287
+ ### 8.3 Research memo β€” primary instruments cited
288
+
289
+ | Instrument | FRBR URI | Status |
290
+ | -------------------------------------------------------------------------- | ------------------------- | -------- |
291
+ | UU 6/2023 (Penetapan UU Cipta Kerja) | `/akn/id/act/uu/2023/6` | Berlaku |
292
+ | PP 35/2021 (PKWT, Alih Daya, Waktu Kerja, PHK) | `/akn/id/act/pp/2021/35` | Berlaku |
293
+ | UU 11/2020 (Cipta Kerja, dasar materiil) | `/akn/id/act/uu/2020/11` | Berlaku |
294
+
295
+ Full memo: `output/riset-outsourcing.md`
296
+
297
+ ### 8.4 Commits in scope of this run
298
+
299
+ - `3612cad` β€” Initial commit: legawa multi-agent system for Indonesian legislators
300
+ - `2b30856` β€” Add roadmap entry for Pemantau Berita (5th agent)
301
+ - `3265a18` β€” Fix three Windows/Qwen3 issues uncovered by first live run
302
+
303
+ ### 8.5 Reproducing this test
304
+
305
+ ```bash
306
+ git clone https://github.com/pebaryan/Legawa
307
+ cd Legawa
308
+ cp .env.example .env # add PASAL_API_TOKEN, configure LLM URLs
309
+ pip install -e .
310
+ legawa health
311
+ legawa surat tests/fixtures/surat-buruh-outsourcing.txt -o output/balasan.md
312
+ legawa research "regulasi outsourcing pasca UU Cipta Kerja dan dampak Permenaker terbaru" -o output/riset.md
313
+ legawa cache stats
314
+ ```
315
+
316
+ ---
317
+
318
+ *End of report.*