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
clean_docs/outcomes.py ADDED
@@ -0,0 +1,296 @@
1
+ """Build one local outcome receipt from deterministic repository checks."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+
8
+ from clean_docs import __version__
9
+ from clean_docs.audit import AuditReport, audit
10
+ from clean_docs.changed import ChangedReport, check_changed
11
+ from clean_docs.claims import SourceClaimReport, scan_source_claims
12
+ from clean_docs.engine import evaluate
13
+ from clean_docs.errors import ConfigurationError
14
+ from clean_docs.execution import ExecutionPolicy
15
+ from clean_docs.manifest import load_manifest
16
+ from clean_docs.inventory import InventoryReport, scan_inventory
17
+ from clean_docs.models import BindingResult, Manifest
18
+ from clean_docs.projections import evaluate_projections
19
+ from clean_docs.snapshot import RepositorySnapshot
20
+
21
+
22
+ @dataclass(frozen=True)
23
+ class RepositoryEvidence:
24
+ manifest: Manifest
25
+ audit: AuditReport
26
+ inventory: InventoryReport
27
+ bindings: tuple[BindingResult, ...]
28
+ projections: tuple[BindingResult, ...]
29
+ source_claims: SourceClaimReport | None
30
+ changed: ChangedReport | None
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class OutcomeReceipt:
35
+ ref: str
36
+ documents: int
37
+ archived_documents: int
38
+ hygiene_findings: int
39
+ baselined_hygiene_findings: int
40
+ bindings: int
41
+ current_bindings: int
42
+ drifted_bindings: int
43
+ skipped_bindings: int
44
+ regions: int
45
+ command_pins: int
46
+ command_pins_with_prose: int
47
+ symbols: int
48
+ projections: int
49
+ current_projections: int
50
+ stale_projections: int
51
+ inventory_items: int
52
+ covered_inventory_items: int
53
+ cataloged_inventory_items: int
54
+ ignored_inventory_items: int
55
+ standard_gaps: int
56
+ source_claims: int
57
+ current_source_claims: int
58
+ drifted_source_claims: int
59
+ missing_source_claims: int
60
+ execution_mode: str = ExecutionPolicy.TRUSTED.value
61
+ manifest_deprecations: tuple[str, ...] = ()
62
+ changed: ChangedReport | None = None
63
+
64
+ @property
65
+ def ok(self) -> bool:
66
+ return (
67
+ self.hygiene_findings == 0
68
+ and self.drifted_bindings == 0
69
+ and self.skipped_bindings == 0
70
+ and self.stale_projections == 0
71
+ and self.drifted_source_claims == 0
72
+ and self.missing_source_claims == 0
73
+ and (self.changed is None or self.changed.ok)
74
+ )
75
+
76
+ def as_dict(self) -> dict[str, object]:
77
+ changed = None
78
+ if self.changed is not None:
79
+ changed = {
80
+ "base": self.changed.base,
81
+ "head": self.changed.head,
82
+ "required": len(self.changed.required),
83
+ "coverage_gaps": len(self.changed.gaps),
84
+ "reasoned_ignores": len(self.changed.ignored),
85
+ "ok": self.changed.ok,
86
+ }
87
+ return {
88
+ "schema": "sourcebound.outcome.v2",
89
+ "version": __version__,
90
+ "ref": self.ref,
91
+ "ok": self.ok,
92
+ "assurance": {
93
+ "scope": "configured-contract",
94
+ "region_bytes_checked": self.regions > 0,
95
+ "command_pin_output_checked": self.command_pins > 0,
96
+ "command_pin_prose_checked": (
97
+ self.command_pins > 0
98
+ and self.command_pins == self.command_pins_with_prose
99
+ ),
100
+ "symbol_existence_checked": self.symbols > 0,
101
+ "accepted_source_claim_prose_checked": self.source_claims > 0,
102
+ "cataloged_surfaces_check_prose": False,
103
+ "judgment_prose_certified": False,
104
+ },
105
+ "outcomes": {
106
+ "protected_baseline_current": (
107
+ self.ok and self.changed is None and self.standard_gaps == 0
108
+ ),
109
+ "coverage_complete": self.standard_gaps == 0,
110
+ "direct_coverage_complete": (
111
+ self.standard_gaps == 0 and self.cataloged_inventory_items == 0
112
+ ),
113
+ "drift_caught_before_merge": (
114
+ 0
115
+ if self.changed is None
116
+ else len(self.changed.required) + len(self.changed.gaps)
117
+ ),
118
+ },
119
+ "documentation": {
120
+ "active": self.documents,
121
+ "archived": self.archived_documents,
122
+ "hygiene_findings": self.hygiene_findings,
123
+ "baselined_hygiene_findings": self.baselined_hygiene_findings,
124
+ },
125
+ "coverage": {
126
+ "total": self.inventory_items,
127
+ "bound": self.covered_inventory_items,
128
+ "cataloged": self.cataloged_inventory_items,
129
+ "ignored": self.ignored_inventory_items,
130
+ "standard_gaps": self.standard_gaps,
131
+ },
132
+ "bindings": {
133
+ "total": self.bindings,
134
+ "current": self.current_bindings,
135
+ "drifted": self.drifted_bindings,
136
+ "skipped": self.skipped_bindings,
137
+ "regions": self.regions,
138
+ "command_pins": self.command_pins,
139
+ "symbols": self.symbols,
140
+ },
141
+ "projections": {
142
+ "total": self.projections,
143
+ "current": self.current_projections,
144
+ "stale": self.stale_projections,
145
+ },
146
+ "source_claims": {
147
+ "total": self.source_claims,
148
+ "current": self.current_source_claims,
149
+ "drifted": self.drifted_source_claims,
150
+ "missing": self.missing_source_claims,
151
+ },
152
+ "changed": changed,
153
+ "execution": {
154
+ "mode": self.execution_mode,
155
+ "declared_processes": (
156
+ "skipped"
157
+ if self.execution_mode == ExecutionPolicy.STATIC_ONLY.value
158
+ else "permitted-by-manifest"
159
+ ),
160
+ "network_isolation": "not-provided",
161
+ "network_observation": "not-instrumented",
162
+ },
163
+ "deprecations": list(self.manifest_deprecations),
164
+ }
165
+
166
+
167
+ def build_outcome_receipt(
168
+ root: Path,
169
+ manifest_path: Path,
170
+ *,
171
+ base: str | None = None,
172
+ head: str | None = None,
173
+ project: Path = Path("."),
174
+ execution_policy: ExecutionPolicy = ExecutionPolicy.TRUSTED,
175
+ use_cache: bool = True,
176
+ ) -> OutcomeReceipt:
177
+ evidence = collect_repository_evidence(
178
+ root,
179
+ manifest_path,
180
+ base=base,
181
+ head=head,
182
+ project=project,
183
+ execution_policy=execution_policy,
184
+ use_cache=use_cache,
185
+ )
186
+ root = root.resolve()
187
+ inventory = evidence.inventory
188
+ audit_report = evidence.audit
189
+ bindings = evidence.bindings
190
+ projections = evidence.projections
191
+ source_claim_report = evidence.source_claims
192
+ changed = evidence.changed
193
+ manifest = evidence.manifest
194
+ covered_items = sum(item.coverage == "bound" for item in inventory.items)
195
+ cataloged_items = sum(item.coverage == "cataloged" for item in inventory.items)
196
+ ignored_items = sum(item.coverage == "ignored" for item in inventory.items)
197
+ standard_gaps = sum(item.coverage == "standard-gap" for item in inventory.items)
198
+ return OutcomeReceipt(
199
+ RepositorySnapshot(root).label,
200
+ len(audit_report.documents),
201
+ len(audit_report.ignored_documents),
202
+ len(audit_report.findings) + len(audit_report.stale_baseline),
203
+ len(audit_report.baselined_findings),
204
+ len(bindings),
205
+ sum(not item.changed for item in bindings),
206
+ sum(
207
+ item.changed and item.state != "skipped-untrusted-execution"
208
+ for item in bindings
209
+ ),
210
+ sum(item.state == "skipped-untrusted-execution" for item in bindings),
211
+ sum(item.binding_type == "region" for item in bindings),
212
+ sum(
213
+ item.binding_type == "command-pin"
214
+ and item.state != "skipped-untrusted-execution"
215
+ for item in bindings
216
+ ),
217
+ sum(
218
+ item.binding_type == "command-pin"
219
+ and item.state != "skipped-untrusted-execution"
220
+ and item.prose_checked
221
+ for item in bindings
222
+ ),
223
+ sum(item.binding_type == "symbol" for item in bindings),
224
+ len(projections),
225
+ sum(not item.changed for item in projections),
226
+ sum(item.changed for item in projections),
227
+ len(inventory.items),
228
+ covered_items,
229
+ cataloged_items,
230
+ ignored_items,
231
+ standard_gaps,
232
+ 0 if source_claim_report is None else len(source_claim_report.results),
233
+ 0
234
+ if source_claim_report is None
235
+ else sum(item.status == "current" for item in source_claim_report.results),
236
+ 0
237
+ if source_claim_report is None
238
+ else sum(item.status == "drift" for item in source_claim_report.results),
239
+ 0 if source_claim_report is None else len(source_claim_report.missing),
240
+ execution_policy.value,
241
+ manifest.deprecations,
242
+ changed,
243
+ )
244
+
245
+
246
+ def collect_repository_evidence(
247
+ root: Path,
248
+ manifest_path: Path,
249
+ *,
250
+ base: str | None = None,
251
+ head: str | None = None,
252
+ project: Path = Path("."),
253
+ execution_policy: ExecutionPolicy = ExecutionPolicy.TRUSTED,
254
+ use_cache: bool = True,
255
+ ) -> RepositoryEvidence:
256
+ if (base is None) != (head is None):
257
+ raise ConfigurationError("verify requires both --base and --head")
258
+ root = root.resolve()
259
+ manifest = load_manifest(manifest_path)
260
+ audit_report = audit(root)
261
+ inventory = scan_inventory(root)
262
+ bindings = tuple(evaluate(root, manifest_path, execution_policy=execution_policy))
263
+ projections = (
264
+ tuple(evaluate_projections(root, manifest))
265
+ if manifest.projections is not None
266
+ else ()
267
+ )
268
+ source_claim_report = (
269
+ scan_source_claims(
270
+ root,
271
+ manifest.source_claim_checks,
272
+ discover=False,
273
+ )
274
+ if manifest.source_claim_checks
275
+ else None
276
+ )
277
+ changed = None
278
+ if base is not None and head is not None:
279
+ changed = check_changed(
280
+ root,
281
+ manifest_path,
282
+ base=base,
283
+ head=head,
284
+ project=project,
285
+ execution_policy=execution_policy,
286
+ use_cache=use_cache,
287
+ )
288
+ return RepositoryEvidence(
289
+ manifest,
290
+ audit_report,
291
+ inventory,
292
+ bindings,
293
+ projections,
294
+ source_claim_report,
295
+ changed,
296
+ )
@@ -0,0 +1,123 @@
1
+ """Measure changed-check latency and peak process memory against published budgets."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ import math
8
+ import resource
9
+ import sys
10
+ import time
11
+ from dataclasses import dataclass
12
+ from pathlib import Path
13
+
14
+ from clean_docs.changed import CHANGED_CHECK_BUDGET_SECONDS, ChangedReport, check_changed
15
+ from clean_docs.errors import ConfigurationError
16
+
17
+
18
+ CHANGED_CHECK_MEMORY_BUDGET_MB = 256.0
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class PerformanceReceipt:
23
+ base: str
24
+ head: str
25
+ project: str
26
+ iterations: int
27
+ p95_seconds: float
28
+ peak_memory_mb: float
29
+ time_budget_seconds: float
30
+ memory_budget_mb: float
31
+ result_sha256: str
32
+ required: int
33
+ gaps: int
34
+
35
+ @property
36
+ def ok(self) -> bool:
37
+ return (
38
+ self.p95_seconds <= self.time_budget_seconds
39
+ and self.peak_memory_mb <= self.memory_budget_mb
40
+ )
41
+
42
+ def as_dict(self) -> dict[str, object]:
43
+ return {
44
+ "schema": "sourcebound.performance.v2",
45
+ "ok": self.ok,
46
+ "base": self.base,
47
+ "head": self.head,
48
+ "project": self.project,
49
+ "iterations": self.iterations,
50
+ "p95_seconds": self.p95_seconds,
51
+ "peak_memory_mb": self.peak_memory_mb,
52
+ "budgets": {
53
+ "p95_seconds": self.time_budget_seconds,
54
+ "peak_memory_mb": self.memory_budget_mb,
55
+ },
56
+ "normalized_result_sha256": self.result_sha256,
57
+ "required": self.required,
58
+ "coverage_gaps": self.gaps,
59
+ "execution": {
60
+ "declared_processes": "may-run-when-required-by-changed-scope",
61
+ "network_isolation": "not-provided",
62
+ "network_observation": "not-instrumented",
63
+ },
64
+ }
65
+
66
+
67
+ def _digest(report: ChangedReport) -> str:
68
+ payload = json.dumps(report.as_dict(), sort_keys=True, separators=(",", ":"))
69
+ return hashlib.sha256(payload.encode()).hexdigest()
70
+
71
+
72
+ def benchmark_changed_check(
73
+ root: Path,
74
+ manifest_path: Path,
75
+ *,
76
+ base: str,
77
+ head: str,
78
+ project: Path = Path("."),
79
+ iterations: int = 7,
80
+ time_budget_seconds: float = CHANGED_CHECK_BUDGET_SECONDS,
81
+ memory_budget_mb: float = CHANGED_CHECK_MEMORY_BUDGET_MB,
82
+ ) -> PerformanceReceipt:
83
+ if not 3 <= iterations <= 50:
84
+ raise ConfigurationError("benchmark iterations must be 3..50")
85
+ if time_budget_seconds <= 0 or memory_budget_mb <= 0:
86
+ raise ConfigurationError("benchmark budgets must be positive")
87
+ durations: list[float] = []
88
+ reports: list[ChangedReport] = []
89
+ for _index in range(iterations):
90
+ started = time.perf_counter()
91
+ report = check_changed(
92
+ root,
93
+ manifest_path,
94
+ base=base,
95
+ head=head,
96
+ use_cache=False,
97
+ project=project,
98
+ )
99
+ durations.append(time.perf_counter() - started)
100
+ reports.append(report)
101
+ digests = {_digest(report) for report in reports}
102
+ if len(digests) != 1:
103
+ raise ConfigurationError("benchmark changed-check results varied across iterations")
104
+ ordered = sorted(durations)
105
+ p95 = ordered[max(0, math.ceil(len(ordered) * 0.95) - 1)]
106
+ peak_rss = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
107
+ peak_memory_mb = (
108
+ peak_rss / (1024 * 1024) if sys.platform == "darwin" else peak_rss / 1024
109
+ )
110
+ last = reports[-1]
111
+ return PerformanceReceipt(
112
+ last.base,
113
+ last.head,
114
+ project.as_posix(),
115
+ iterations,
116
+ round(p95, 6),
117
+ round(peak_memory_mb, 3),
118
+ time_budget_seconds,
119
+ memory_budget_mb,
120
+ next(iter(digests)),
121
+ len(last.required),
122
+ len(last.gaps),
123
+ )