staleguard 0.1.0__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.
Files changed (46) hide show
  1. staleguard-0.1.0/LICENSE +21 -0
  2. staleguard-0.1.0/PKG-INFO +361 -0
  3. staleguard-0.1.0/README.md +309 -0
  4. staleguard-0.1.0/pyproject.toml +65 -0
  5. staleguard-0.1.0/setup.cfg +4 -0
  6. staleguard-0.1.0/src/__init__.py +64 -0
  7. staleguard-0.1.0/src/adapters/__init__.py +11 -0
  8. staleguard-0.1.0/src/adapters/chroma.py +50 -0
  9. staleguard-0.1.0/src/adapters/json_adapter.py +67 -0
  10. staleguard-0.1.0/src/adapters/langchain.py +34 -0
  11. staleguard-0.1.0/src/alternatives/__init__.py +3 -0
  12. staleguard-0.1.0/src/alternatives/finder.py +35 -0
  13. staleguard-0.1.0/src/api.py +135 -0
  14. staleguard-0.1.0/src/audit_types.py +11 -0
  15. staleguard-0.1.0/src/auditor.py +444 -0
  16. staleguard-0.1.0/src/chroma_demo.py +143 -0
  17. staleguard-0.1.0/src/cli.py +60 -0
  18. staleguard-0.1.0/src/config.py +31 -0
  19. staleguard-0.1.0/src/conflicts/__init__.py +3 -0
  20. staleguard-0.1.0/src/conflicts/nli.py +68 -0
  21. staleguard-0.1.0/src/conflicts/rules.py +213 -0
  22. staleguard-0.1.0/src/engineering_demo.py +104 -0
  23. staleguard-0.1.0/src/eval.py +202 -0
  24. staleguard-0.1.0/src/main.py +31 -0
  25. staleguard-0.1.0/src/matching.py +179 -0
  26. staleguard-0.1.0/src/providers.py +166 -0
  27. staleguard-0.1.0/src/schema.py +124 -0
  28. staleguard-0.1.0/src/scorers/__init__.py +17 -0
  29. staleguard-0.1.0/src/scorers/freshness.py +195 -0
  30. staleguard-0.1.0/staleguard.egg-info/PKG-INFO +361 -0
  31. staleguard-0.1.0/staleguard.egg-info/SOURCES.txt +44 -0
  32. staleguard-0.1.0/staleguard.egg-info/dependency_links.txt +1 -0
  33. staleguard-0.1.0/staleguard.egg-info/entry_points.txt +2 -0
  34. staleguard-0.1.0/staleguard.egg-info/requires.txt +11 -0
  35. staleguard-0.1.0/staleguard.egg-info/top_level.txt +1 -0
  36. staleguard-0.1.0/tests/test_alternatives.py +62 -0
  37. staleguard-0.1.0/tests/test_api.py +177 -0
  38. staleguard-0.1.0/tests/test_auditor.py +267 -0
  39. staleguard-0.1.0/tests/test_chroma_adapter.py +76 -0
  40. staleguard-0.1.0/tests/test_conflicts.py +51 -0
  41. staleguard-0.1.0/tests/test_eval.py +128 -0
  42. staleguard-0.1.0/tests/test_freshness.py +54 -0
  43. staleguard-0.1.0/tests/test_json_adapter.py +83 -0
  44. staleguard-0.1.0/tests/test_langchain_adapter.py +105 -0
  45. staleguard-0.1.0/tests/test_provenance.py +97 -0
  46. staleguard-0.1.0/tests/test_schema.py +35 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
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.
@@ -0,0 +1,361 @@
1
+ Metadata-Version: 2.4
2
+ Name: staleguard
3
+ Version: 0.1.0
4
+ Summary: Post-retrieval audit layer for RAG pipelines.
5
+ Author: Ishan
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/ish4n10/staleguard
29
+ Project-URL: Repository, https://github.com/ish4n10/staleguard
30
+ Project-URL: Issues, https://github.com/ish4n10/staleguard/issues
31
+ Keywords: rag,retrieval,llm,audit,staleness,nli
32
+ Classifier: Development Status :: 3 - Alpha
33
+ Classifier: Intended Audience :: Developers
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Classifier: Programming Language :: Python :: 3
36
+ Classifier: Programming Language :: Python :: 3.11
37
+ Classifier: Programming Language :: Python :: 3.12
38
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
39
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
40
+ Requires-Python: >=3.11
41
+ Description-Content-Type: text/markdown
42
+ License-File: LICENSE
43
+ Requires-Dist: numpy>=1.26
44
+ Provides-Extra: local
45
+ Requires-Dist: sentence-transformers>=3.0; extra == "local"
46
+ Provides-Extra: demo
47
+ Requires-Dist: chromadb>=0.5; extra == "demo"
48
+ Provides-Extra: dev
49
+ Requires-Dist: build>=1.2; extra == "dev"
50
+ Requires-Dist: twine>=5.1; extra == "dev"
51
+ Dynamic: license-file
52
+
53
+ # StaleGuard
54
+
55
+ StaleGuard is a post-retrieval audit layer for RAG pipelines.
56
+
57
+ It sits between retrieval and generation:
58
+
59
+ ```text
60
+ Retriever / Vector DB
61
+ ->
62
+ Retrieved chunks
63
+ ->
64
+ StaleGuard audit
65
+ ->
66
+ Decision:
67
+ FRESH
68
+ STALE
69
+ MIXED
70
+ CONFLICTED
71
+ UNKNOWN
72
+ ->
73
+ LLM
74
+ ```
75
+
76
+ StaleGuard does not replace retrieval. It checks whether retrieved chunks are trustworthy enough to send to the model.
77
+
78
+ ## Install
79
+
80
+ ```bash
81
+ pip install staleguard
82
+ ```
83
+
84
+ For local embedding and NLI models:
85
+
86
+ ```bash
87
+ pip install "staleguard[local]"
88
+ ```
89
+
90
+ For the repo demos:
91
+
92
+ ```bash
93
+ pip install "staleguard[demo]"
94
+ ```
95
+
96
+ ## What It Does
97
+
98
+ - score chunk freshness from metadata, version, and date signals
99
+ - find fresher alternatives from a corpus
100
+ - detect contradictions with rules and optional local NLI
101
+ - surface schema issues when retrieved metadata is incomplete
102
+ - return a provenance object you can use in middleware or UI
103
+
104
+ Current trust order:
105
+
106
+ ```text
107
+ metadata > rules = nli
108
+ ```
109
+
110
+ That means:
111
+ - explicit metadata wins when it is available
112
+ - rules and NLI are secondary evidence sources
113
+ - if rules and NLI agree, confidence increases
114
+
115
+ ## Core API
116
+
117
+ The shortest path is the package-level `audit(...)` function.
118
+
119
+ ```python
120
+ from staleguard import audit
121
+
122
+ result = audit(
123
+ query="How do I configure Redis Cluster in Redis 8?",
124
+ retrieved=retriever_output,
125
+ corpus=corpus,
126
+ )
127
+
128
+ print(result.verdict)
129
+ print(result.conflicts)
130
+ print(result.provenance)
131
+ ```
132
+
133
+ If you want to reuse configuration across calls, use `StaleGuard`.
134
+
135
+ ```python
136
+ from staleguard import StaleGuard
137
+
138
+ guard = StaleGuard(use_nli=True, block_on_conflict=False)
139
+ result = guard.audit(
140
+ query="How do I configure Redis Cluster in Redis 8?",
141
+ retrieved=retriever_output,
142
+ corpus=corpus,
143
+ )
144
+ ```
145
+
146
+ For advanced integrations, the same API also accepts:
147
+
148
+ - already-normalized chunk dicts
149
+ - raw Chroma query results
150
+ - LangChain-style documents
151
+ - custom embedding / conflict providers
152
+
153
+ ### Normalized chunks
154
+
155
+ ```python
156
+ from staleguard import StaleGuard
157
+
158
+ guard = StaleGuard(use_nli=True)
159
+
160
+ result = guard.audit_chunks(
161
+ query="How do I configure Redis Cluster in Redis 8?",
162
+ retrieved_chunks=[
163
+ {
164
+ "id": "redis_6_cluster_001",
165
+ "text": "Redis 6 uses requirepass configuration for cluster authentication.",
166
+ "product": "redis",
167
+ "topic": "cluster_configuration",
168
+ "version": "6.2",
169
+ "date_ts": 1640995200,
170
+ "source": "redis-6.2-cluster.md",
171
+ "metadata": {"status": "superseded", "superseded_by": "8.0"},
172
+ }
173
+ ],
174
+ corpus=[...],
175
+ )
176
+ ```
177
+
178
+ `audit(...)` and `guard.audit(...)` auto-detect:
179
+
180
+ - raw Chroma query results
181
+ - LangChain-style documents with `page_content` and `metadata`
182
+ - already-normalized chunk dicts
183
+
184
+ The lower-level helpers still exist for direct use:
185
+
186
+ - `audit_retrieved(...)`
187
+ - `audit_chroma_result(...)`
188
+ - `audit_langchain_docs(...)`
189
+
190
+ ## Verdicts
191
+
192
+ - `FRESH`: retrieved context looks current
193
+ - `STALE`: outdated chunks were found
194
+ - `MIXED`: stale and conflicting evidence were found
195
+ - `CONFLICTED`: conflicting evidence was found
196
+ - `UNKNOWN`: metadata is too incomplete to make a strong judgment
197
+
198
+ If you want any conflict to block generation, set:
199
+
200
+ ```python
201
+ block_on_conflict=True
202
+ ```
203
+
204
+ That collapses `MIXED` into `CONFLICTED`.
205
+
206
+ ## Middleware Pattern
207
+
208
+ This is the intended integration shape:
209
+
210
+ ```python
211
+ from staleguard import StaleGuard
212
+
213
+ guard = StaleGuard(use_nli=True, block_on_conflict=False)
214
+
215
+ def audited_retrieve(query: str, retriever_output, corpus: list[dict]):
216
+ audit_result = guard.audit(
217
+ query=query,
218
+ retrieved=retriever_output,
219
+ corpus=corpus,
220
+ )
221
+
222
+ if audit_result.verdict == "CONFLICTED":
223
+ return {"block": True, "audit": audit_result}
224
+
225
+ return {"block": False, "audit": audit_result}
226
+ ```
227
+
228
+ The application can then:
229
+
230
+ - send `FRESH` chunks to the LLM
231
+ - replace or warn on `STALE`
232
+ - block or escalate on `CONFLICTED`
233
+ - show provenance on `MIXED`
234
+
235
+ ## Supported Retrieval Inputs
236
+
237
+ ### Chroma
238
+
239
+ ```python
240
+ from staleguard import StaleGuard
241
+
242
+ guard = StaleGuard(use_nli=True)
243
+
244
+ result = guard.audit(
245
+ query=query,
246
+ retrieved=chroma_result,
247
+ corpus=corpus,
248
+ )
249
+ ```
250
+
251
+ ### LangChain-style documents
252
+
253
+ ```python
254
+ from staleguard import StaleGuard
255
+
256
+ guard = StaleGuard(use_nli=True)
257
+
258
+ result = guard.audit(
259
+ query=query,
260
+ retrieved=docs,
261
+ corpus=corpus,
262
+ )
263
+ ```
264
+
265
+ The repo also exposes adapter helpers:
266
+
267
+ - `normalize_chroma_result(...)`
268
+ - `normalize_langchain_docs(...)`
269
+ - `normalize_chunks(...)`
270
+
271
+ ## CLI
272
+
273
+ ```bash
274
+ staleguard audit --query "How do I configure Redis Cluster in Redis 8?" --retrieved retrieved.json --corpus corpus.json
275
+ staleguard eval --corpus eval_cases/kubernetes/corpus.json --cases eval_cases/kubernetes/cases.json --use-nli
276
+ ```
277
+
278
+ ## Publish
279
+
280
+ ```bash
281
+ python -m pip install ".[dev]"
282
+ python -m build
283
+ python -m twine upload dist/*
284
+ ```
285
+
286
+ ## Chunk Schema
287
+
288
+ Best case input:
289
+
290
+ ```python
291
+ {
292
+ "id": "redis_8_cluster_001",
293
+ "text": "...",
294
+ "product": "redis",
295
+ "topic": "cluster_configuration",
296
+ "version": "8.0",
297
+ "date_ts": 1735689600,
298
+ "source": "redis-8.0-cluster.md",
299
+ "metadata": {
300
+ "status": "current"
301
+ }
302
+ }
303
+ ```
304
+
305
+ Audit-critical fields:
306
+
307
+ - `text`
308
+ - `product`
309
+ - `topic`
310
+ - `version`
311
+ - `date_ts`
312
+
313
+ If some metadata is missing, StaleGuard will:
314
+
315
+ - infer a few safe fields from `source`
316
+ - record `schema_issues`
317
+ - lower confidence or return `UNKNOWN` when needed
318
+
319
+ ## Demos
320
+
321
+ ### Redis demo
322
+
323
+ ```bash
324
+ python -m staleguard.chroma_demo
325
+ ```
326
+
327
+ This builds a local Chroma collection and shows:
328
+
329
+ - raw Chroma retrieval
330
+ - normalized chunks
331
+ - prepared chunks
332
+ - audit result
333
+
334
+ ### Large engineering demo
335
+
336
+ ```bash
337
+ python -m staleguard.engineering_demo
338
+ ```
339
+
340
+ This uses a larger corpus around:
341
+
342
+ - loop engineering
343
+ - context engineering
344
+ - tool loop policy
345
+ - memory policy
346
+ - planning
347
+ - retrieval policy
348
+
349
+ The example intentionally includes:
350
+
351
+ - stale 2024/2025 chunks
352
+ - fresher 2026 replacements
353
+ - conflicting guidance that triggers NLI
354
+
355
+ ## Current Status
356
+
357
+ The repo is at the local/offline MVP stage.
358
+
359
+ ## License
360
+
361
+ [MIT](LICENSE)
@@ -0,0 +1,309 @@
1
+ # StaleGuard
2
+
3
+ StaleGuard is a post-retrieval audit layer for RAG pipelines.
4
+
5
+ It sits between retrieval and generation:
6
+
7
+ ```text
8
+ Retriever / Vector DB
9
+ ->
10
+ Retrieved chunks
11
+ ->
12
+ StaleGuard audit
13
+ ->
14
+ Decision:
15
+ FRESH
16
+ STALE
17
+ MIXED
18
+ CONFLICTED
19
+ UNKNOWN
20
+ ->
21
+ LLM
22
+ ```
23
+
24
+ StaleGuard does not replace retrieval. It checks whether retrieved chunks are trustworthy enough to send to the model.
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ pip install staleguard
30
+ ```
31
+
32
+ For local embedding and NLI models:
33
+
34
+ ```bash
35
+ pip install "staleguard[local]"
36
+ ```
37
+
38
+ For the repo demos:
39
+
40
+ ```bash
41
+ pip install "staleguard[demo]"
42
+ ```
43
+
44
+ ## What It Does
45
+
46
+ - score chunk freshness from metadata, version, and date signals
47
+ - find fresher alternatives from a corpus
48
+ - detect contradictions with rules and optional local NLI
49
+ - surface schema issues when retrieved metadata is incomplete
50
+ - return a provenance object you can use in middleware or UI
51
+
52
+ Current trust order:
53
+
54
+ ```text
55
+ metadata > rules = nli
56
+ ```
57
+
58
+ That means:
59
+ - explicit metadata wins when it is available
60
+ - rules and NLI are secondary evidence sources
61
+ - if rules and NLI agree, confidence increases
62
+
63
+ ## Core API
64
+
65
+ The shortest path is the package-level `audit(...)` function.
66
+
67
+ ```python
68
+ from staleguard import audit
69
+
70
+ result = audit(
71
+ query="How do I configure Redis Cluster in Redis 8?",
72
+ retrieved=retriever_output,
73
+ corpus=corpus,
74
+ )
75
+
76
+ print(result.verdict)
77
+ print(result.conflicts)
78
+ print(result.provenance)
79
+ ```
80
+
81
+ If you want to reuse configuration across calls, use `StaleGuard`.
82
+
83
+ ```python
84
+ from staleguard import StaleGuard
85
+
86
+ guard = StaleGuard(use_nli=True, block_on_conflict=False)
87
+ result = guard.audit(
88
+ query="How do I configure Redis Cluster in Redis 8?",
89
+ retrieved=retriever_output,
90
+ corpus=corpus,
91
+ )
92
+ ```
93
+
94
+ For advanced integrations, the same API also accepts:
95
+
96
+ - already-normalized chunk dicts
97
+ - raw Chroma query results
98
+ - LangChain-style documents
99
+ - custom embedding / conflict providers
100
+
101
+ ### Normalized chunks
102
+
103
+ ```python
104
+ from staleguard import StaleGuard
105
+
106
+ guard = StaleGuard(use_nli=True)
107
+
108
+ result = guard.audit_chunks(
109
+ query="How do I configure Redis Cluster in Redis 8?",
110
+ retrieved_chunks=[
111
+ {
112
+ "id": "redis_6_cluster_001",
113
+ "text": "Redis 6 uses requirepass configuration for cluster authentication.",
114
+ "product": "redis",
115
+ "topic": "cluster_configuration",
116
+ "version": "6.2",
117
+ "date_ts": 1640995200,
118
+ "source": "redis-6.2-cluster.md",
119
+ "metadata": {"status": "superseded", "superseded_by": "8.0"},
120
+ }
121
+ ],
122
+ corpus=[...],
123
+ )
124
+ ```
125
+
126
+ `audit(...)` and `guard.audit(...)` auto-detect:
127
+
128
+ - raw Chroma query results
129
+ - LangChain-style documents with `page_content` and `metadata`
130
+ - already-normalized chunk dicts
131
+
132
+ The lower-level helpers still exist for direct use:
133
+
134
+ - `audit_retrieved(...)`
135
+ - `audit_chroma_result(...)`
136
+ - `audit_langchain_docs(...)`
137
+
138
+ ## Verdicts
139
+
140
+ - `FRESH`: retrieved context looks current
141
+ - `STALE`: outdated chunks were found
142
+ - `MIXED`: stale and conflicting evidence were found
143
+ - `CONFLICTED`: conflicting evidence was found
144
+ - `UNKNOWN`: metadata is too incomplete to make a strong judgment
145
+
146
+ If you want any conflict to block generation, set:
147
+
148
+ ```python
149
+ block_on_conflict=True
150
+ ```
151
+
152
+ That collapses `MIXED` into `CONFLICTED`.
153
+
154
+ ## Middleware Pattern
155
+
156
+ This is the intended integration shape:
157
+
158
+ ```python
159
+ from staleguard import StaleGuard
160
+
161
+ guard = StaleGuard(use_nli=True, block_on_conflict=False)
162
+
163
+ def audited_retrieve(query: str, retriever_output, corpus: list[dict]):
164
+ audit_result = guard.audit(
165
+ query=query,
166
+ retrieved=retriever_output,
167
+ corpus=corpus,
168
+ )
169
+
170
+ if audit_result.verdict == "CONFLICTED":
171
+ return {"block": True, "audit": audit_result}
172
+
173
+ return {"block": False, "audit": audit_result}
174
+ ```
175
+
176
+ The application can then:
177
+
178
+ - send `FRESH` chunks to the LLM
179
+ - replace or warn on `STALE`
180
+ - block or escalate on `CONFLICTED`
181
+ - show provenance on `MIXED`
182
+
183
+ ## Supported Retrieval Inputs
184
+
185
+ ### Chroma
186
+
187
+ ```python
188
+ from staleguard import StaleGuard
189
+
190
+ guard = StaleGuard(use_nli=True)
191
+
192
+ result = guard.audit(
193
+ query=query,
194
+ retrieved=chroma_result,
195
+ corpus=corpus,
196
+ )
197
+ ```
198
+
199
+ ### LangChain-style documents
200
+
201
+ ```python
202
+ from staleguard import StaleGuard
203
+
204
+ guard = StaleGuard(use_nli=True)
205
+
206
+ result = guard.audit(
207
+ query=query,
208
+ retrieved=docs,
209
+ corpus=corpus,
210
+ )
211
+ ```
212
+
213
+ The repo also exposes adapter helpers:
214
+
215
+ - `normalize_chroma_result(...)`
216
+ - `normalize_langchain_docs(...)`
217
+ - `normalize_chunks(...)`
218
+
219
+ ## CLI
220
+
221
+ ```bash
222
+ staleguard audit --query "How do I configure Redis Cluster in Redis 8?" --retrieved retrieved.json --corpus corpus.json
223
+ staleguard eval --corpus eval_cases/kubernetes/corpus.json --cases eval_cases/kubernetes/cases.json --use-nli
224
+ ```
225
+
226
+ ## Publish
227
+
228
+ ```bash
229
+ python -m pip install ".[dev]"
230
+ python -m build
231
+ python -m twine upload dist/*
232
+ ```
233
+
234
+ ## Chunk Schema
235
+
236
+ Best case input:
237
+
238
+ ```python
239
+ {
240
+ "id": "redis_8_cluster_001",
241
+ "text": "...",
242
+ "product": "redis",
243
+ "topic": "cluster_configuration",
244
+ "version": "8.0",
245
+ "date_ts": 1735689600,
246
+ "source": "redis-8.0-cluster.md",
247
+ "metadata": {
248
+ "status": "current"
249
+ }
250
+ }
251
+ ```
252
+
253
+ Audit-critical fields:
254
+
255
+ - `text`
256
+ - `product`
257
+ - `topic`
258
+ - `version`
259
+ - `date_ts`
260
+
261
+ If some metadata is missing, StaleGuard will:
262
+
263
+ - infer a few safe fields from `source`
264
+ - record `schema_issues`
265
+ - lower confidence or return `UNKNOWN` when needed
266
+
267
+ ## Demos
268
+
269
+ ### Redis demo
270
+
271
+ ```bash
272
+ python -m staleguard.chroma_demo
273
+ ```
274
+
275
+ This builds a local Chroma collection and shows:
276
+
277
+ - raw Chroma retrieval
278
+ - normalized chunks
279
+ - prepared chunks
280
+ - audit result
281
+
282
+ ### Large engineering demo
283
+
284
+ ```bash
285
+ python -m staleguard.engineering_demo
286
+ ```
287
+
288
+ This uses a larger corpus around:
289
+
290
+ - loop engineering
291
+ - context engineering
292
+ - tool loop policy
293
+ - memory policy
294
+ - planning
295
+ - retrieval policy
296
+
297
+ The example intentionally includes:
298
+
299
+ - stale 2024/2025 chunks
300
+ - fresher 2026 replacements
301
+ - conflicting guidance that triggers NLI
302
+
303
+ ## Current Status
304
+
305
+ The repo is at the local/offline MVP stage.
306
+
307
+ ## License
308
+
309
+ [MIT](LICENSE)