"ClauseWatch: an agent that refuses to give you one number" A developer built ClauseWatch, an open-source agent that surfaces conflicts between legal instruments rather than returning a single answer, such as the EU AI Act's six-month log retention floor colliding with the GDPR's unnamed ceiling. The tool pairs a structured Sanity dataset of provisions, requirements, and claims with an LLM agent and a deterministic no-model mode, marking unresolved conflicts as "unmade" with empty decidedBy and decidedAt fields. It runs without credentials against a public query API, with the knowledge base and agent requiring a Context token. This is a submission for the Sanity Challenge, Path One: Ship an Agent That Queries Real Content https://dev.to/challenges/sanity-2026-09-16 Ask any model how long you must keep AI system logs under the EU AI Act. It will say six months. That is true, and it is not the answer. One instrument sets a floor. Another sets a ceiling. Neither says where the other one sits. The honest answer is a period you can justify against both — plus a record of who decided it. ClauseWatch gives that answer. It reads a structured model of obligations and the prose of the instruments behind them, and when two sources pull in opposite directions it shows both with their citations and reports that the decision is unmade : Both clauses stand until someone signs. The dataset keeps a decidedBy and a decidedAt field for exactly that, and they are empty. This is the part that needed structured content. A keyword search over the same texts returns Art. 19 and stops. It cannot know that a clause in a different regulation constrains the same artifact from the opposite direction, because that fact is not inside either document. It is in the relationship between them — which is to say, in the schema. git clone https://github.com/oleg-vdv/clausewatch && cd clausewatch/agent npm install && npm run web http://localhost:4173 No credentials. No login, no token, no API key. The dataset is public, so a fresh clone reads it over the public query API and renders the full report. The knowledge base and the LLM agent do need a Context token, which you cannot have — so --check and the page footer say which of the two sources the answer came from, rather than crediting one they never touched. It also runs with no model at all: npm run ask -- --profile biometric-access --no-llm provider, high-risk, KZ → EU npm run ask -- --profile support-agent --no-llm deployer, risk class unset Two elements of the viewer do the arguing. The pressure bar — a solid edge on the left where Art. 19 states six months, and a hatched, edgeless right where the GDPR caps the period without naming one. Drawing a tidy range there would be a lie about the law. The signature block — every conflict prints a decided by and a date rule. Resolved, they carry a name and a date. Open, they are two empty lines in a compliance report. The thesis as a form field rather than a paragraph. The Studio is live at https://clausewatch.sanity.studio/ https://clausewatch.sanity.studio/ , with open conflicts on the first screen, because that is the one thing an editor of this dataset actually does. https://github.com/oleg-vdv/clausewatch https://github.com/oleg-vdv/clausewatch studio/ schema, desk structure, seed dataset agent/ MCP client, domain layer, LLM agent, deterministic report, viewer demo/ saved runs with their tool calls attached source an instrument: binding status, official URL, version label provision one citable unit: "Art. 19", verbatim text, effective date, roles bound requirement a normalised obligation: "retain automatically generated logs" └ claim what each instrument asserts — direction floor/ceiling/none , period if stated, condition, and a reference to its provision conflict two clauses that cannot both be satisfied, plus resolution, rationale, decidedBy, decidedAt systemProfile roles, jurisdictions, risk class, agentic or not Three decisions carry the weight. Claims sit apart from requirements. One obligation, many instruments, and disagreement between them is content — not a data-quality problem to be cleaned up. direction on a claim. An earlier version bucketed claims by does it state a number , which filed GDPR Art. 5 1 e under silence. It is not silent: it caps the period without naming one. Floor, ceiling and duty-only are three different answers. A conflict holds a decision, not a resolution rule. An agent that silently picks the stricter number is guessing on your behalf. One that shows both sides and cites a named, dated decision produces something an auditor can accept. Two website sources, both crawled to exact paths rather than wildcards: | Source | Paths | Documents | |---|---|---| | artificialintelligenceact.eu | Arts. 3, 6, 9, 11–14, 16–21, 26, 27, 72, 73, 99 and Annexes I, III, IV | 21 | | gdpr-info.eu | Arts. 5, 17, 25, 30, 32 | 5 | 26 documents, indexed into 10 entries . Curated on purpose — and also capped, because the Context beta allows 150 indexed documents on this plan and I hit that twice before narrowing properly. The indexing surprised me. Entries are topic nodes rather than chunks: each carries an article range, a topic list, cross-references excludes: … see