sourcebound 1.2.1__py3-none-any.whl

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 (71) hide show
  1. clean_docs/__init__.py +26 -0
  2. clean_docs/__main__.py +3 -0
  3. clean_docs/accessibility.py +182 -0
  4. clean_docs/adapters/__init__.py +1 -0
  5. clean_docs/adapters/event_capture.py +56 -0
  6. clean_docs/adapters/mdx_dependencies.json +714 -0
  7. clean_docs/adapters/mdx_parser.mjs +29992 -0
  8. clean_docs/applicability.py +330 -0
  9. clean_docs/audit.py +1120 -0
  10. clean_docs/bootstrap.py +507 -0
  11. clean_docs/capabilities.py +200 -0
  12. clean_docs/changed.py +452 -0
  13. clean_docs/claims.py +840 -0
  14. clean_docs/cli.py +1612 -0
  15. clean_docs/context.py +307 -0
  16. clean_docs/corpus.py +377 -0
  17. clean_docs/demo.py +369 -0
  18. clean_docs/doctor.py +184 -0
  19. clean_docs/emit/__init__.py +4 -0
  20. clean_docs/emit/llms_txt.py +102 -0
  21. clean_docs/emit/stepwise.py +168 -0
  22. clean_docs/engine.py +324 -0
  23. clean_docs/errors.py +20 -0
  24. clean_docs/evaluation.py +867 -0
  25. clean_docs/execution.py +138 -0
  26. clean_docs/explain.py +123 -0
  27. clean_docs/extractors/__init__.py +19 -0
  28. clean_docs/extractors/command.py +51 -0
  29. clean_docs/extractors/inventory.py +176 -0
  30. clean_docs/extractors/json_pointer.py +84 -0
  31. clean_docs/extractors/python_literal.py +104 -0
  32. clean_docs/extractors/static.py +111 -0
  33. clean_docs/feedback.py +1390 -0
  34. clean_docs/impact.py +1624 -0
  35. clean_docs/improvements.py +1178 -0
  36. clean_docs/inventory.py +474 -0
  37. clean_docs/isolation.py +157 -0
  38. clean_docs/manifest.py +898 -0
  39. clean_docs/mdx.py +272 -0
  40. clean_docs/migration.py +121 -0
  41. clean_docs/models.py +194 -0
  42. clean_docs/outcomes.py +296 -0
  43. clean_docs/performance.py +123 -0
  44. clean_docs/phrasing.py +448 -0
  45. clean_docs/plugins.py +249 -0
  46. clean_docs/policy.py +536 -0
  47. clean_docs/projections.py +255 -0
  48. clean_docs/regions.py +75 -0
  49. clean_docs/release.py +232 -0
  50. clean_docs/renderers.py +57 -0
  51. clean_docs/residue.py +311 -0
  52. clean_docs/review_contracts.py +862 -0
  53. clean_docs/review_ledger.py +297 -0
  54. clean_docs/review_limits.py +9 -0
  55. clean_docs/sensitivity.py +602 -0
  56. clean_docs/snapshot.py +212 -0
  57. clean_docs/standard.py +281 -0
  58. clean_docs/standards/default.json +309 -0
  59. clean_docs/standards/exemplars.md +87 -0
  60. clean_docs/standards/v0-migrations.json +26 -0
  61. clean_docs/symbols.py +47 -0
  62. clean_docs/templates.py +96 -0
  63. clean_docs/verdict.py +1397 -0
  64. clean_docs/visuals.py +346 -0
  65. clean_docs/write_gate.py +170 -0
  66. sourcebound-1.2.1.dist-info/LICENSE +21 -0
  67. sourcebound-1.2.1.dist-info/METADATA +109 -0
  68. sourcebound-1.2.1.dist-info/RECORD +71 -0
  69. sourcebound-1.2.1.dist-info/WHEEL +5 -0
  70. sourcebound-1.2.1.dist-info/entry_points.txt +2 -0
  71. sourcebound-1.2.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,330 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+ from typing import Literal, cast
7
+
8
+ from clean_docs.policy import REGISTER_PROFILE
9
+
10
+
11
+ DocumentRole = Literal[
12
+ "agent-procedure",
13
+ "architecture",
14
+ "component-overview",
15
+ "evidence",
16
+ "overview",
17
+ "plan",
18
+ "reference",
19
+ "task",
20
+ "template",
21
+ "troubleshooting",
22
+ "tutorial",
23
+ ]
24
+
25
+ ROLE_RULES: dict[DocumentRole, frozenset[str]] = {
26
+ "overview": frozenset({
27
+ "code-block-language",
28
+ "diagram-text-equivalent",
29
+ "image-alternative",
30
+ "purpose-contract",
31
+ "purpose-template",
32
+ "preamble-contract",
33
+ "prohibited-booster",
34
+ "sentence-variance",
35
+ "nominalization-density",
36
+ "significance-narration",
37
+ "qualifier-density",
38
+ "doc-length",
39
+ "section-length",
40
+ "readme-routing",
41
+ "readme-reference-depth",
42
+ "elaboration-depth",
43
+ }),
44
+ "component-overview": frozenset({
45
+ "code-block-language",
46
+ "diagram-text-equivalent",
47
+ "image-alternative",
48
+ "prohibited-booster",
49
+ "sentence-variance",
50
+ "nominalization-density",
51
+ "significance-narration",
52
+ }),
53
+ "tutorial": frozenset({
54
+ "code-block-language",
55
+ "diagram-text-equivalent",
56
+ "image-alternative",
57
+ "purpose-contract",
58
+ "purpose-template",
59
+ "prohibited-booster",
60
+ "sentence-variance",
61
+ "nominalization-density",
62
+ "significance-narration",
63
+ "doc-length",
64
+ "section-length",
65
+ "elaboration-depth",
66
+ }),
67
+ "task": frozenset({
68
+ "code-block-language",
69
+ "diagram-text-equivalent",
70
+ "image-alternative",
71
+ "purpose-contract",
72
+ "purpose-template",
73
+ "prohibited-booster",
74
+ "sentence-variance",
75
+ "nominalization-density",
76
+ "significance-narration",
77
+ "doc-length",
78
+ "section-length",
79
+ }),
80
+ "troubleshooting": frozenset({
81
+ "code-block-language",
82
+ "diagram-text-equivalent",
83
+ "image-alternative",
84
+ "purpose-contract",
85
+ "purpose-template",
86
+ "prohibited-booster",
87
+ "sentence-variance",
88
+ "nominalization-density",
89
+ "significance-narration",
90
+ }),
91
+ "architecture": frozenset({
92
+ "code-block-language",
93
+ "diagram-text-equivalent",
94
+ "image-alternative",
95
+ "prohibited-booster",
96
+ "sentence-variance",
97
+ "nominalization-density",
98
+ "significance-narration",
99
+ }),
100
+ "reference": frozenset({
101
+ "code-block-language",
102
+ "diagram-text-equivalent",
103
+ "image-alternative",
104
+ "prohibited-booster",
105
+ "nominalization-density",
106
+ "significance-narration",
107
+ }),
108
+ # These files are inputs, procedures, plans, or records. Rewriting them into a
109
+ # generic reader page can change runtime behavior or erase evidence.
110
+ "agent-procedure": frozenset(),
111
+ "template": frozenset(),
112
+ "evidence": frozenset(),
113
+ "plan": frozenset(),
114
+ }
115
+ ROLE_DESCRIPTIONS: dict[DocumentRole, str] = {
116
+ "overview": "repository entry point for orientation and routing",
117
+ "component-overview": "component-local entry point whose parent supplies context",
118
+ "tutorial": "ordered learning path to a verified result",
119
+ "task": "procedure or explanation for one reader job",
120
+ "troubleshooting": "symptom-to-diagnosis-to-recovery procedure",
121
+ "reference": "lookup surface for exact current facts",
122
+ "architecture": "design boundary, decision, or time-horizon record",
123
+ "plan": "time-scoped work whose live or historical status needs judgment",
124
+ "evidence": "review, result, or longitudinal observation record",
125
+ "agent-procedure": "executable instructions for an agent reader",
126
+ "template": "runtime prompt or generated-content input",
127
+ }
128
+ ROLE_OVERRIDE = re.compile(
129
+ r"(?:<!--|\{/\*)\s*sourcebound:role\s+([a-z][a-z-]+)\s*(?:-->|\*/\})"
130
+ )
131
+ ROLE_MARKER = re.compile(
132
+ r"(?:<!--|\{/\*)\s*sourcebound:role\b.*?(?:-->|\*/\})"
133
+ )
134
+ REGISTER_MARKER = re.compile(
135
+ r"(?:<!--|\{/\*)\s*sourcebound:policy\s+register-v2\s*(?:-->|\*/\})"
136
+ )
137
+
138
+ _TEMPLATE_PARTS = frozenset({"prompts", "prompt", "templates", "template"})
139
+ _EVIDENCE_NAME = re.compile(
140
+ r"(?:^|[-_])(?:review|report|journal|findings|receipt|retro|postmortem|"
141
+ r"evaluation|eval-results?|status|progress|handoff|dispatch|workorder|blocked|"
142
+ r"changelog|changes?|news|release-notes?|history)(?:[-_.]|$)",
143
+ re.IGNORECASE,
144
+ )
145
+ _PLAN_NAME = re.compile(r"(?:^|[-_])plan(?:[-_.]|$)", re.IGNORECASE)
146
+ _REFERENCE_NAME = re.compile(
147
+ r"(reference|standard|spec|schema|surface|commands?|cli|api|configuration|"
148
+ r"policy|contract)",
149
+ re.IGNORECASE,
150
+ )
151
+ _TUTORIAL_NAME = re.compile(
152
+ r"(tutorial|quickstart|quick-start|getting-started|walkthrough)",
153
+ re.IGNORECASE,
154
+ )
155
+ _TROUBLESHOOTING_NAME = re.compile(
156
+ r"(troubleshoot|debug|diagnos|fixing|recovery|runbook|incident)",
157
+ re.IGNORECASE,
158
+ )
159
+ _SUPPORT_NAMES = frozenset({"operations.md", "support.md"})
160
+ _ARCHITECTURE_NAME = re.compile(
161
+ r"(architecture|compromise|design|decision|adr|proposal|rfc)",
162
+ re.IGNORECASE,
163
+ )
164
+
165
+
166
+ @dataclass(frozen=True)
167
+ class DocumentProfile:
168
+ path: str
169
+ role: DocumentRole
170
+ reason: str
171
+ registered: bool
172
+
173
+ def applies(self, rule: str) -> bool:
174
+ return rule in ROLE_RULES[self.role]
175
+
176
+
177
+ def role_override_error(text: str) -> str | None:
178
+ markers = ROLE_MARKER.findall(text)
179
+ overrides = ROLE_OVERRIDE.findall(text)
180
+ if not markers:
181
+ return None
182
+ if len(markers) != 1 or len(overrides) != 1:
183
+ return "add exactly one complete sourcebound role marker"
184
+ if overrides[0] not in ROLE_RULES:
185
+ return f"unsupported sourcebound role: {overrides[0]}"
186
+ return None
187
+
188
+
189
+ def frontmatter_error(text: str) -> str | None:
190
+ lines = text.splitlines()
191
+ if not lines or lines[0] != "---":
192
+ return None
193
+ if "---" not in lines[1:]:
194
+ return "frontmatter opens at the document start but has no closing delimiter"
195
+ return None
196
+
197
+
198
+ def _has_frontmatter(text: str) -> bool:
199
+ lines = text.splitlines()
200
+ return bool(lines and lines[0] == "---" and "---" in lines[1:])
201
+
202
+
203
+ def classify_document(relative: Path, text: str) -> DocumentProfile:
204
+ """Classify a Markdown file by the job its current form performs."""
205
+ normalized = relative.as_posix()
206
+ parts = tuple(part.lower() for part in relative.parts)
207
+ name = relative.name.lower()
208
+ title = next(
209
+ (
210
+ line.lstrip("#").strip().lower()
211
+ for line in text.splitlines()
212
+ if line.startswith("#")
213
+ ),
214
+ "",
215
+ )
216
+ registered = REGISTER_PROFILE in text or REGISTER_MARKER.search(text) is not None
217
+ if match := ROLE_OVERRIDE.search(text):
218
+ requested = match.group(1)
219
+ if requested in ROLE_RULES:
220
+ role = cast(DocumentRole, requested)
221
+ return DocumentProfile(
222
+ normalized,
223
+ role,
224
+ "explicit sourcebound role marker",
225
+ registered,
226
+ )
227
+
228
+ if (
229
+ name in {"agents.md", "skill.md"}
230
+ or parts[:1] == (".agents",)
231
+ or "skills" in parts
232
+ and _has_frontmatter(text)
233
+ ):
234
+ return DocumentProfile(
235
+ normalized,
236
+ "agent-procedure",
237
+ "path or frontmatter identifies an executable agent procedure",
238
+ registered,
239
+ )
240
+ if any(part in _TEMPLATE_PARTS for part in parts[:-1]):
241
+ return DocumentProfile(
242
+ normalized,
243
+ "template",
244
+ "path identifies prompt or generated-content input",
245
+ registered,
246
+ )
247
+ if _EVIDENCE_NAME.search(relative.name) or any(
248
+ part in {"reviews", "reports", "journals", "receipts", "evaluations"}
249
+ for part in parts[:-1]
250
+ ):
251
+ return DocumentProfile(
252
+ normalized,
253
+ "evidence",
254
+ "path identifies a review, result, or longitudinal record",
255
+ registered,
256
+ )
257
+ if _PLAN_NAME.search(relative.name):
258
+ return DocumentProfile(
259
+ normalized,
260
+ "plan",
261
+ "filename identifies a time-scoped plan whose status needs judgment",
262
+ registered,
263
+ )
264
+ if (
265
+ name in _SUPPORT_NAMES
266
+ or _TROUBLESHOOTING_NAME.search(normalized)
267
+ or _TROUBLESHOOTING_NAME.search(title)
268
+ ):
269
+ return DocumentProfile(
270
+ normalized,
271
+ "troubleshooting",
272
+ "path or title identifies diagnose-fix-verify work",
273
+ registered,
274
+ )
275
+ if _TUTORIAL_NAME.search(normalized) or _TUTORIAL_NAME.search(title):
276
+ return DocumentProfile(
277
+ normalized,
278
+ "tutorial",
279
+ "path or title identifies an ordered learning path",
280
+ registered,
281
+ )
282
+ if (
283
+ "adr" in parts
284
+ or _ARCHITECTURE_NAME.search(relative.name)
285
+ or _ARCHITECTURE_NAME.search(title)
286
+ ):
287
+ return DocumentProfile(
288
+ normalized,
289
+ "architecture",
290
+ "path or title identifies a design or decision record",
291
+ registered,
292
+ )
293
+ if (
294
+ "references" in parts
295
+ or _REFERENCE_NAME.search(relative.name)
296
+ or _REFERENCE_NAME.search(title)
297
+ ):
298
+ return DocumentProfile(
299
+ normalized,
300
+ "reference",
301
+ "path or title identifies a lookup surface",
302
+ registered,
303
+ )
304
+ if name in {"contributing.md", "contributor-guide.md"}:
305
+ return DocumentProfile(
306
+ normalized,
307
+ "component-overview",
308
+ "filename identifies a contributor entry point with several reader routes",
309
+ registered,
310
+ )
311
+ if name == "readme.md":
312
+ if len(relative.parts) > 1:
313
+ return DocumentProfile(
314
+ normalized,
315
+ "component-overview",
316
+ "nested README identifies a component-local entry point",
317
+ registered,
318
+ )
319
+ return DocumentProfile(
320
+ normalized,
321
+ "overview",
322
+ "README identifies a repository or component entry point",
323
+ registered,
324
+ )
325
+ return DocumentProfile(
326
+ normalized,
327
+ "task",
328
+ "default role for an authored how-to or explanatory page",
329
+ registered,
330
+ )