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.
- clean_docs/__init__.py +26 -0
- clean_docs/__main__.py +3 -0
- clean_docs/accessibility.py +182 -0
- clean_docs/adapters/__init__.py +1 -0
- clean_docs/adapters/event_capture.py +56 -0
- clean_docs/adapters/mdx_dependencies.json +714 -0
- clean_docs/adapters/mdx_parser.mjs +29992 -0
- clean_docs/applicability.py +330 -0
- clean_docs/audit.py +1120 -0
- clean_docs/bootstrap.py +507 -0
- clean_docs/capabilities.py +200 -0
- clean_docs/changed.py +452 -0
- clean_docs/claims.py +840 -0
- clean_docs/cli.py +1612 -0
- clean_docs/context.py +307 -0
- clean_docs/corpus.py +377 -0
- clean_docs/demo.py +369 -0
- clean_docs/doctor.py +184 -0
- clean_docs/emit/__init__.py +4 -0
- clean_docs/emit/llms_txt.py +102 -0
- clean_docs/emit/stepwise.py +168 -0
- clean_docs/engine.py +324 -0
- clean_docs/errors.py +20 -0
- clean_docs/evaluation.py +867 -0
- clean_docs/execution.py +138 -0
- clean_docs/explain.py +123 -0
- clean_docs/extractors/__init__.py +19 -0
- clean_docs/extractors/command.py +51 -0
- clean_docs/extractors/inventory.py +176 -0
- clean_docs/extractors/json_pointer.py +84 -0
- clean_docs/extractors/python_literal.py +104 -0
- clean_docs/extractors/static.py +111 -0
- clean_docs/feedback.py +1390 -0
- clean_docs/impact.py +1624 -0
- clean_docs/improvements.py +1178 -0
- clean_docs/inventory.py +474 -0
- clean_docs/isolation.py +157 -0
- clean_docs/manifest.py +898 -0
- clean_docs/mdx.py +272 -0
- clean_docs/migration.py +121 -0
- clean_docs/models.py +194 -0
- clean_docs/outcomes.py +296 -0
- clean_docs/performance.py +123 -0
- clean_docs/phrasing.py +448 -0
- clean_docs/plugins.py +249 -0
- clean_docs/policy.py +536 -0
- clean_docs/projections.py +255 -0
- clean_docs/regions.py +75 -0
- clean_docs/release.py +232 -0
- clean_docs/renderers.py +57 -0
- clean_docs/residue.py +311 -0
- clean_docs/review_contracts.py +862 -0
- clean_docs/review_ledger.py +297 -0
- clean_docs/review_limits.py +9 -0
- clean_docs/sensitivity.py +602 -0
- clean_docs/snapshot.py +212 -0
- clean_docs/standard.py +281 -0
- clean_docs/standards/default.json +309 -0
- clean_docs/standards/exemplars.md +87 -0
- clean_docs/standards/v0-migrations.json +26 -0
- clean_docs/symbols.py +47 -0
- clean_docs/templates.py +96 -0
- clean_docs/verdict.py +1397 -0
- clean_docs/visuals.py +346 -0
- clean_docs/write_gate.py +170 -0
- sourcebound-1.2.1.dist-info/LICENSE +21 -0
- sourcebound-1.2.1.dist-info/METADATA +109 -0
- sourcebound-1.2.1.dist-info/RECORD +71 -0
- sourcebound-1.2.1.dist-info/WHEEL +5 -0
- sourcebound-1.2.1.dist-info/entry_points.txt +2 -0
- sourcebound-1.2.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"""Validate the append-only denominator for review observations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import re
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from clean_docs.errors import ConfigurationError, PolicyError
|
|
13
|
+
from clean_docs.improvements import ImprovementCandidateSet
|
|
14
|
+
from clean_docs.regions import atomic_write
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
REVIEW_EVENT_LEDGER_SCHEMA = "sourcebound.review-event-ledger.v2"
|
|
18
|
+
_LEGACY_REVIEW_EVENT_LEDGER_SCHEMA = "sourcebound.review-event-ledger.v1"
|
|
19
|
+
_SHA256 = re.compile(r"[0-9a-f]{64}")
|
|
20
|
+
_IDENTIFIER = re.compile(r"[a-z0-9]+(?:-[a-z0-9]+)*")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class ReviewEvent:
|
|
25
|
+
id: str
|
|
26
|
+
observation_id: str
|
|
27
|
+
disposition: str
|
|
28
|
+
candidate_id: str | None
|
|
29
|
+
successor: str | None
|
|
30
|
+
previous_digest: str | None
|
|
31
|
+
digest: str
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _object(value: Any, where: str) -> dict[str, Any]:
|
|
35
|
+
if not isinstance(value, dict):
|
|
36
|
+
raise ConfigurationError(f"{where} must be an object")
|
|
37
|
+
return value
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _string(value: Any, where: str) -> str:
|
|
41
|
+
if not isinstance(value, str) or not value.strip():
|
|
42
|
+
raise ConfigurationError(f"{where} must be a non-empty string")
|
|
43
|
+
return value
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _identifier(value: Any, where: str) -> str:
|
|
47
|
+
output = _string(value, where)
|
|
48
|
+
if not _IDENTIFIER.fullmatch(output):
|
|
49
|
+
raise ConfigurationError(f"{where} must be kebab-case")
|
|
50
|
+
return output
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _digest(payload: dict[str, Any]) -> str:
|
|
54
|
+
return hashlib.sha256(
|
|
55
|
+
json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
|
56
|
+
).hexdigest()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _event_digest(event: ReviewEvent) -> str:
|
|
60
|
+
return _digest(
|
|
61
|
+
{
|
|
62
|
+
"id": event.id,
|
|
63
|
+
"observation_id": event.observation_id,
|
|
64
|
+
"disposition": event.disposition,
|
|
65
|
+
"candidate_id": event.candidate_id,
|
|
66
|
+
"successor": event.successor,
|
|
67
|
+
"previous_digest": event.previous_digest,
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _event(value: Any, index: int) -> ReviewEvent:
|
|
73
|
+
where = f"review event ledger.events[{index}]"
|
|
74
|
+
raw = _object(value, where)
|
|
75
|
+
expected = {
|
|
76
|
+
"id",
|
|
77
|
+
"observation_id",
|
|
78
|
+
"disposition",
|
|
79
|
+
"candidate_id",
|
|
80
|
+
"successor",
|
|
81
|
+
"previous_digest",
|
|
82
|
+
"digest",
|
|
83
|
+
}
|
|
84
|
+
if set(raw) != expected:
|
|
85
|
+
raise ConfigurationError(f"{where} must contain exactly: {', '.join(sorted(expected))}")
|
|
86
|
+
disposition = _string(raw["disposition"], f"{where}.disposition")
|
|
87
|
+
if disposition not in {"candidate", "superseded", "merged"}:
|
|
88
|
+
raise ConfigurationError(f"{where}.disposition is invalid")
|
|
89
|
+
candidate_id = raw["candidate_id"]
|
|
90
|
+
successor = raw["successor"]
|
|
91
|
+
if disposition == "candidate":
|
|
92
|
+
candidate_id = _string(candidate_id, f"{where}.candidate_id")
|
|
93
|
+
if not _SHA256.fullmatch(candidate_id):
|
|
94
|
+
raise ConfigurationError(f"{where}.candidate_id must be a SHA-256")
|
|
95
|
+
if successor is not None:
|
|
96
|
+
raise ConfigurationError(f"{where}.successor must be null for a candidate")
|
|
97
|
+
else:
|
|
98
|
+
if candidate_id is not None:
|
|
99
|
+
raise ConfigurationError(f"{where}.candidate_id must be null for {disposition}")
|
|
100
|
+
successor = _identifier(successor, f"{where}.successor")
|
|
101
|
+
previous_digest = raw["previous_digest"]
|
|
102
|
+
if previous_digest is not None:
|
|
103
|
+
previous_digest = _string(previous_digest, f"{where}.previous_digest")
|
|
104
|
+
if not _SHA256.fullmatch(previous_digest):
|
|
105
|
+
raise ConfigurationError(f"{where}.previous_digest must be a SHA-256 or null")
|
|
106
|
+
event = ReviewEvent(
|
|
107
|
+
id=_identifier(raw["id"], f"{where}.id"),
|
|
108
|
+
observation_id=_identifier(raw["observation_id"], f"{where}.observation_id"),
|
|
109
|
+
disposition=disposition,
|
|
110
|
+
candidate_id=candidate_id,
|
|
111
|
+
successor=successor,
|
|
112
|
+
previous_digest=previous_digest,
|
|
113
|
+
digest=_string(raw["digest"], f"{where}.digest"),
|
|
114
|
+
)
|
|
115
|
+
if event.digest != _event_digest(event):
|
|
116
|
+
raise ConfigurationError(f"{where}.digest does not match its content")
|
|
117
|
+
return event
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@dataclass(frozen=True)
|
|
121
|
+
class ReviewEventLedger:
|
|
122
|
+
schema: str
|
|
123
|
+
review_id: str
|
|
124
|
+
events: tuple[ReviewEvent, ...]
|
|
125
|
+
head_digest: str
|
|
126
|
+
migration_base_head_digest: str | None
|
|
127
|
+
|
|
128
|
+
def as_dict(self) -> dict[str, object]:
|
|
129
|
+
return {
|
|
130
|
+
"schema": self.schema,
|
|
131
|
+
"review_id": self.review_id,
|
|
132
|
+
"events": [
|
|
133
|
+
{
|
|
134
|
+
"id": event.id,
|
|
135
|
+
"observation_id": event.observation_id,
|
|
136
|
+
"disposition": event.disposition,
|
|
137
|
+
"candidate_id": event.candidate_id,
|
|
138
|
+
"successor": event.successor,
|
|
139
|
+
"previous_digest": event.previous_digest,
|
|
140
|
+
"digest": event.digest,
|
|
141
|
+
}
|
|
142
|
+
for event in self.events
|
|
143
|
+
],
|
|
144
|
+
"head_digest": self.head_digest,
|
|
145
|
+
"migration_base_head_digest": self.migration_base_head_digest,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _load_review_event_ledger(path: Path) -> ReviewEventLedger:
|
|
150
|
+
"""Load one internally consistent ledger without assigning candidate authority."""
|
|
151
|
+
try:
|
|
152
|
+
raw = json.loads(path.read_text(encoding="utf-8"))
|
|
153
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
154
|
+
raise ConfigurationError(f"cannot read review event ledger {path}: {exc}") from exc
|
|
155
|
+
root = _object(raw, "review event ledger")
|
|
156
|
+
schema = _string(root.get("schema"), "review event ledger.schema")
|
|
157
|
+
if schema == REVIEW_EVENT_LEDGER_SCHEMA:
|
|
158
|
+
expected_keys = {
|
|
159
|
+
"schema",
|
|
160
|
+
"review_id",
|
|
161
|
+
"events",
|
|
162
|
+
"head_digest",
|
|
163
|
+
"migration_base_head_digest",
|
|
164
|
+
}
|
|
165
|
+
elif schema == _LEGACY_REVIEW_EVENT_LEDGER_SCHEMA:
|
|
166
|
+
expected_keys = {"schema", "review_id", "events", "head_digest"}
|
|
167
|
+
else:
|
|
168
|
+
raise ConfigurationError(
|
|
169
|
+
"review event ledger schema must be "
|
|
170
|
+
f"{REVIEW_EVENT_LEDGER_SCHEMA} or {_LEGACY_REVIEW_EVENT_LEDGER_SCHEMA}"
|
|
171
|
+
)
|
|
172
|
+
if set(root) != expected_keys:
|
|
173
|
+
raise ConfigurationError("review event ledger has an invalid shape")
|
|
174
|
+
review_id = _identifier(root["review_id"], "review event ledger.review_id")
|
|
175
|
+
if not isinstance(root["events"], list) or not root["events"]:
|
|
176
|
+
raise ConfigurationError("review event ledger.events must be a non-empty list")
|
|
177
|
+
events = tuple(_event(item, index) for index, item in enumerate(root["events"]))
|
|
178
|
+
seen_ids: set[str] = set()
|
|
179
|
+
by_observation: dict[str, ReviewEvent] = {}
|
|
180
|
+
previous: str | None = None
|
|
181
|
+
for event in events:
|
|
182
|
+
if event.id in seen_ids:
|
|
183
|
+
raise ConfigurationError(f"duplicate review event id: {event.id}")
|
|
184
|
+
seen_ids.add(event.id)
|
|
185
|
+
if event.previous_digest != previous:
|
|
186
|
+
raise ConfigurationError("review event ledger chain is not append-only")
|
|
187
|
+
previous = event.digest
|
|
188
|
+
if event.observation_id in by_observation:
|
|
189
|
+
raise ConfigurationError(f"duplicate review event observation id: {event.observation_id}")
|
|
190
|
+
by_observation[event.observation_id] = event
|
|
191
|
+
if root["head_digest"] != previous:
|
|
192
|
+
raise ConfigurationError("review event ledger.head_digest does not match the event chain")
|
|
193
|
+
assert previous is not None
|
|
194
|
+
migration_base_head_digest = root.get("migration_base_head_digest")
|
|
195
|
+
if migration_base_head_digest is not None:
|
|
196
|
+
migration_base_head_digest = _string(
|
|
197
|
+
migration_base_head_digest,
|
|
198
|
+
"review event ledger.migration_base_head_digest",
|
|
199
|
+
)
|
|
200
|
+
if not _SHA256.fullmatch(migration_base_head_digest):
|
|
201
|
+
raise ConfigurationError(
|
|
202
|
+
"review event ledger.migration_base_head_digest must be a SHA-256"
|
|
203
|
+
)
|
|
204
|
+
return ReviewEventLedger(
|
|
205
|
+
schema=schema,
|
|
206
|
+
review_id=review_id,
|
|
207
|
+
events=events,
|
|
208
|
+
head_digest=previous,
|
|
209
|
+
migration_base_head_digest=migration_base_head_digest,
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def validate_review_event_ledger(
|
|
214
|
+
path: Path,
|
|
215
|
+
candidates: ImprovementCandidateSet,
|
|
216
|
+
*,
|
|
217
|
+
prior_path: Path | None = None,
|
|
218
|
+
) -> str:
|
|
219
|
+
"""Require one current disposition and preserve the base ledger as a prefix."""
|
|
220
|
+
ledger = _load_review_event_ledger(path)
|
|
221
|
+
if ledger.review_id != candidates.review_id:
|
|
222
|
+
raise PolicyError("review event ledger belongs to another review")
|
|
223
|
+
if prior_path is not None:
|
|
224
|
+
prior = _load_review_event_ledger(prior_path)
|
|
225
|
+
if prior.review_id != ledger.review_id:
|
|
226
|
+
raise PolicyError("prior review event ledger belongs to another review")
|
|
227
|
+
if prior.schema == REVIEW_EVENT_LEDGER_SCHEMA and ledger.schema != prior.schema:
|
|
228
|
+
raise PolicyError("review event ledger cannot downgrade from the anchored schema")
|
|
229
|
+
if ledger.schema == REVIEW_EVENT_LEDGER_SCHEMA and prior.schema == _LEGACY_REVIEW_EVENT_LEDGER_SCHEMA:
|
|
230
|
+
if ledger.migration_base_head_digest != prior.head_digest:
|
|
231
|
+
raise PolicyError("review event ledger migration does not bind the legacy head")
|
|
232
|
+
else:
|
|
233
|
+
if ledger.events[: len(prior.events)] != prior.events:
|
|
234
|
+
raise PolicyError("review event ledger rewrites the immutable base history")
|
|
235
|
+
if ledger.migration_base_head_digest != prior.migration_base_head_digest:
|
|
236
|
+
raise PolicyError("review event ledger rewrites its migration anchor")
|
|
237
|
+
events = ledger.events
|
|
238
|
+
by_observation = {event.observation_id: event for event in events}
|
|
239
|
+
candidate_ids = {candidate.observation_id: candidate.id for candidate in candidates.candidates}
|
|
240
|
+
for observation_id, candidate_id in candidate_ids.items():
|
|
241
|
+
ledger_event = by_observation.get(observation_id)
|
|
242
|
+
if ledger_event is None:
|
|
243
|
+
raise PolicyError(f"review event ledger is missing observation: {observation_id}")
|
|
244
|
+
if ledger_event.disposition != "candidate" or ledger_event.candidate_id != candidate_id:
|
|
245
|
+
raise PolicyError(f"review event ledger retargets observation: {observation_id}")
|
|
246
|
+
for event in events:
|
|
247
|
+
if event.disposition == "candidate" and event.observation_id not in candidate_ids:
|
|
248
|
+
raise PolicyError(f"review candidate is silently removed from the review: {event.observation_id}")
|
|
249
|
+
if event.disposition != "candidate":
|
|
250
|
+
successor = by_observation.get(event.successor or "")
|
|
251
|
+
if successor is None or successor.disposition != "candidate":
|
|
252
|
+
raise PolicyError(f"review event {event.id} has no candidate successor")
|
|
253
|
+
return events[-1].digest
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def initialize_review_event_ledger(
|
|
257
|
+
candidates: ImprovementCandidateSet,
|
|
258
|
+
) -> ReviewEventLedger:
|
|
259
|
+
"""Start a version 2 ledger from one compiled candidate denominator."""
|
|
260
|
+
events: list[ReviewEvent] = []
|
|
261
|
+
previous_digest: str | None = None
|
|
262
|
+
for candidate in candidates.candidates:
|
|
263
|
+
event = ReviewEvent(
|
|
264
|
+
id=f"event-{candidate.observation_id}",
|
|
265
|
+
observation_id=candidate.observation_id,
|
|
266
|
+
disposition="candidate",
|
|
267
|
+
candidate_id=candidate.id,
|
|
268
|
+
successor=None,
|
|
269
|
+
previous_digest=previous_digest,
|
|
270
|
+
digest="",
|
|
271
|
+
)
|
|
272
|
+
event = ReviewEvent(
|
|
273
|
+
id=event.id,
|
|
274
|
+
observation_id=event.observation_id,
|
|
275
|
+
disposition=event.disposition,
|
|
276
|
+
candidate_id=event.candidate_id,
|
|
277
|
+
successor=event.successor,
|
|
278
|
+
previous_digest=event.previous_digest,
|
|
279
|
+
digest=_event_digest(event),
|
|
280
|
+
)
|
|
281
|
+
events.append(event)
|
|
282
|
+
previous_digest = event.digest
|
|
283
|
+
if not events:
|
|
284
|
+
raise ConfigurationError("review event ledger requires at least one candidate")
|
|
285
|
+
return ReviewEventLedger(
|
|
286
|
+
schema=REVIEW_EVENT_LEDGER_SCHEMA,
|
|
287
|
+
review_id=candidates.review_id,
|
|
288
|
+
events=tuple(events),
|
|
289
|
+
head_digest=events[-1].digest,
|
|
290
|
+
migration_base_head_digest=None,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def write_review_event_ledger(ledger: ReviewEventLedger, output: Path) -> Path:
|
|
295
|
+
"""Write one initial review ledger atomically."""
|
|
296
|
+
atomic_write(output, json.dumps(ledger.as_dict(), indent=2, ensure_ascii=False) + "\n")
|
|
297
|
+
return output
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Fixed work limits for observe-only review contracts."""
|
|
2
|
+
|
|
3
|
+
MAX_REVIEW_CONTRACTS = 64
|
|
4
|
+
MAX_REVIEW_LOCATORS_PER_CONTRACT = 32
|
|
5
|
+
MAX_REVIEW_LOCATORS = 256
|
|
6
|
+
MAX_REVIEW_UNIQUE_PATHS = 128
|
|
7
|
+
MAX_REVIEW_FILE_BYTES = 1_000_000
|
|
8
|
+
MAX_REVIEW_TOTAL_BYTES = 16_000_000
|
|
9
|
+
MAX_REVIEW_STRUCTURED_NODES = 50_000
|