quirework 0.1.0__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.
- quirework/__init__.py +3 -0
- quirework/__main__.py +3 -0
- quirework/cli.py +431 -0
- quirework/core/__init__.py +1 -0
- quirework/core/artifact_paths.py +86 -0
- quirework/core/checks.py +235 -0
- quirework/core/frontmatter.py +83 -0
- quirework/core/globs.py +45 -0
- quirework/core/pinned.py +169 -0
- quirework/core/usage.py +604 -0
- quirework/delivery/__init__.py +1 -0
- quirework/delivery/artifacts.py +210 -0
- quirework/delivery/text.py +166 -0
- quirework/manuscript/__init__.py +1 -0
- quirework/manuscript/backfill.py +1972 -0
- quirework/manuscript/editions.py +111 -0
- quirework/manuscript/footprint.py +95 -0
- quirework/manuscript/formats.py +208 -0
- quirework/manuscript/lifecycle.py +191 -0
- quirework/manuscript/notes.py +2301 -0
- quirework/manuscript/pages.py +284 -0
- quirework/manuscript/quarto.py +416 -0
- quirework/manuscript/quotes.py +512 -0
- quirework/manuscript/rename.py +309 -0
- quirework/manuscript/sources.py +286 -0
- quirework/manuscript/trail.py +1875 -0
- quirework/manuscript/units.py +416 -0
- quirework/project.py +645 -0
- quirework/provenance/__init__.py +1 -0
- quirework/provenance/agyfilter.py +436 -0
- quirework/provenance/backup/__init__.py +469 -0
- quirework/provenance/backup/base.py +290 -0
- quirework/provenance/backup/command.py +107 -0
- quirework/provenance/backup/directory.py +142 -0
- quirework/provenance/backup/git.py +150 -0
- quirework/provenance/backup/rclone.py +153 -0
- quirework/provenance/capture/__init__.py +1 -0
- quirework/provenance/capture/commands.py +28 -0
- quirework/provenance/capture/extract_artifacts.py +110 -0
- quirework/provenance/capture/extract_files.py +118 -0
- quirework/provenance/capture/fetch_conversation.py +127 -0
- quirework/provenance/capture/fetch_gemini.py +149 -0
- quirework/provenance/capture/render_transcript.py +126 -0
- quirework/provenance/capture/transform.py +22 -0
- quirework/provenance/catalog.py +733 -0
- quirework/provenance/ccfilter.py +1126 -0
- quirework/provenance/codexfilter.py +883 -0
- quirework/provenance/coldread.py +1731 -0
- quirework/provenance/curate.py +67 -0
- quirework/provenance/humaninput.py +811 -0
- quirework/provenance/ingest.py +31 -0
- quirework/provenance/ingestion/__init__.py +1 -0
- quirework/provenance/ingestion/agy.py +192 -0
- quirework/provenance/ingestion/claude.py +493 -0
- quirework/provenance/ingestion/codex.py +296 -0
- quirework/provenance/lineage.py +148 -0
- quirework/provenance/metrics.py +1247 -0
- quirework/provenance/rulings.py +478 -0
- quirework/provenance/scope.py +73 -0
- quirework/provenance/search.py +455 -0
- quirework/provenance/semantic.py +475 -0
- quirework/provenance/show.py +1213 -0
- quirework/provenance/store.py +1271 -0
- quirework/provenance/trees.py +133 -0
- quirework/provenance/worktrees.py +199 -0
- quirework/providers/__init__.py +1 -0
- quirework/providers/blind.py +2192 -0
- quirework/providers/canary.py +685 -0
- quirework/providers/catalog.py +44 -0
- quirework/providers/claude_login.py +92 -0
- quirework/providers/exits.py +196 -0
- quirework/providers/harness.py +830 -0
- quirework/providers/images.py +92 -0
- quirework/providers/models.py +1284 -0
- quirework/providers/quota.py +588 -0
- quirework/providers/reads.py +474 -0
- quirework/providers/replies.py +175 -0
- quirework/publish/__init__.py +1 -0
- quirework/publish/backends.py +112 -0
- quirework/publish/bookstage.py +1086 -0
- quirework/publish/build.py +60 -0
- quirework/publish/editions.py +48 -0
- quirework/publish/epubcheck.py +287 -0
- quirework/publish/experiment.py +1097 -0
- quirework/publish/figfonts.py +234 -0
- quirework/publish/fignumbers.py +293 -0
- quirework/publish/figures.py +729 -0
- quirework/publish/folios.py +62 -0
- quirework/publish/fonts.py +307 -0
- quirework/publish/graybuild.py +73 -0
- quirework/publish/hugo.py +117 -0
- quirework/publish/kindle.js +411 -0
- quirework/publish/kindle.py +1393 -0
- quirework/publish/panelkit.py +918 -0
- quirework/publish/plans.py +651 -0
- quirework/publish/proof.py +792 -0
- quirework/publish/redpen.py +1654 -0
- quirework/publish/render.py +942 -0
- quirework/publish/targets.py +216 -0
- quirework/publish/variant.py +918 -0
- quirework/runs/__init__.py +1 -0
- quirework/runs/__main__.py +15 -0
- quirework/runs/commands.py +278 -0
- quirework/runs/coverage.py +86 -0
- quirework/runs/engine.py +2143 -0
- quirework/runs/feed.py +853 -0
- quirework/runs/observe.py +379 -0
- quirework/runs/storage.py +155 -0
- quirework/settings.py +492 -0
- quirework/sops/__init__.py +1 -0
- quirework/sops/catalog.py +386 -0
- quirework/sops/commands.py +567 -0
- quirework/sops/composition.py +382 -0
- quirework/sops/instruments.py +184 -0
- quirework/sops/personas.py +296 -0
- quirework/sops/review.py +2568 -0
- quirework/sops/room.py +2791 -0
- quirework/sops/roundtable.py +2578 -0
- quirework/sops/sections.py +169 -0
- quirework/sops/workshop.py +1629 -0
- quirework/templates/backup.toml +39 -0
- quirework/templates/book.toml +88 -0
- quirework/templates/run.toml +32 -0
- quirework/templates/site.toml +90 -0
- quirework/templates/tells.toml +64 -0
- quirework/templates/terminology.toml +20 -0
- quirework/templates/tmpfiles.conf +42 -0
- quirework/testing.py +345 -0
- quirework/text/__init__.py +1 -0
- quirework/text/jargon.py +545 -0
- quirework/text/mdwrap.py +1001 -0
- quirework/text/tells.py +547 -0
- quirework-0.1.0.dist-info/METADATA +444 -0
- quirework-0.1.0.dist-info/RECORD +138 -0
- quirework-0.1.0.dist-info/WHEEL +5 -0
- quirework-0.1.0.dist-info/entry_points.txt +2 -0
- quirework-0.1.0.dist-info/licenses/LICENSE +21 -0
- quirework-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Verify image identities in native session ancestry."""
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import hashlib
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
# Claude Code's records of the requests it sent, not turns of the dialogue.
|
|
9
|
+
# A blob stores a request's messages by hash, so when the client retries a
|
|
10
|
+
# stream that was cut off, the retried turn's images appear in a blob as
|
|
11
|
+
# well as in the user row that delivered them. Counting both reports a
|
|
12
|
+
# figure the reader was shown once as shown twice.
|
|
13
|
+
REQUEST_RECORDS = frozenset({"api-request", "api-request-shape", "api-request-blob"})
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _agy_media_hashes(transcript, rows):
|
|
17
|
+
"""Hash the images an agy session was shown, from the copies agy keeps.
|
|
18
|
+
|
|
19
|
+
agy records an image it showed the model as a step's `media`, naming a
|
|
20
|
+
file it copied into the session's own directory, beside the transcript's
|
|
21
|
+
`.system_generated/logs/`. The name is the path inside the jail, so only
|
|
22
|
+
the part from `brain/` on is kept and resolved against this session's
|
|
23
|
+
directory on the host. A copy that is missing is not counted: the check
|
|
24
|
+
it feeds then reports a shortfall rather than proof.
|
|
25
|
+
"""
|
|
26
|
+
if "step_index" not in (rows[0] if rows else {}):
|
|
27
|
+
return []
|
|
28
|
+
brain = transcript.parents[3]
|
|
29
|
+
hashes = []
|
|
30
|
+
for row in rows:
|
|
31
|
+
if row.get("status") != "DONE":
|
|
32
|
+
continue
|
|
33
|
+
for item in row.get("media") or []:
|
|
34
|
+
uri = str(item.get("uri", "")) if isinstance(item, dict) else ""
|
|
35
|
+
parts = Path(uri).parts
|
|
36
|
+
if "brain" not in parts:
|
|
37
|
+
continue
|
|
38
|
+
copy = brain.joinpath(*parts[parts.index("brain") + 1 :]).resolve()
|
|
39
|
+
if copy.is_file() and copy.is_relative_to(brain.resolve()):
|
|
40
|
+
hashes.append(hashlib.sha256(copy.read_bytes()).hexdigest())
|
|
41
|
+
return hashes
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def history_image_hashes(rollout, home, seen=None):
|
|
45
|
+
"""Audit persisted image bytes, following Codex's native parent references.
|
|
46
|
+
|
|
47
|
+
Recent Codex forks store new turns plus a parent reference, whereas Claude
|
|
48
|
+
copies its earlier messages into the child transcript. Neither shape may
|
|
49
|
+
be mistaken for evidence that the reader lost its earlier pages. agy
|
|
50
|
+
stores no image in its transcript at all, only a copy beside it.
|
|
51
|
+
"""
|
|
52
|
+
path = Path(rollout)
|
|
53
|
+
seen = set() if seen is None else seen
|
|
54
|
+
if path in seen:
|
|
55
|
+
raise RuntimeError("cycle in native fork history")
|
|
56
|
+
seen.add(path)
|
|
57
|
+
rows = [json.loads(line) for line in path.read_text().splitlines() if line.strip()]
|
|
58
|
+
hashes = []
|
|
59
|
+
|
|
60
|
+
def visit(value):
|
|
61
|
+
if isinstance(value, dict):
|
|
62
|
+
if value.get("type") == "input_image":
|
|
63
|
+
url = value.get("image_url", "")
|
|
64
|
+
if not url.startswith("data:image/") or ";base64," not in url:
|
|
65
|
+
raise RuntimeError("persisted image is not self-contained")
|
|
66
|
+
hashes.append(
|
|
67
|
+
hashlib.sha256(base64.b64decode(url.split(",", 1)[1])).hexdigest()
|
|
68
|
+
)
|
|
69
|
+
elif value.get("type") == "image" and isinstance(value.get("source"), dict):
|
|
70
|
+
source = value["source"]
|
|
71
|
+
if source.get("type") != "base64":
|
|
72
|
+
raise RuntimeError("persisted Claude image is not self-contained")
|
|
73
|
+
hashes.append(
|
|
74
|
+
hashlib.sha256(base64.b64decode(source["data"])).hexdigest()
|
|
75
|
+
)
|
|
76
|
+
for child in value.values():
|
|
77
|
+
visit(child)
|
|
78
|
+
elif isinstance(value, list):
|
|
79
|
+
for child in value:
|
|
80
|
+
visit(child)
|
|
81
|
+
|
|
82
|
+
visit([row for row in rows if row.get("type") not in REQUEST_RECORDS])
|
|
83
|
+
hashes.extend(_agy_media_hashes(path, rows))
|
|
84
|
+
for row in rows:
|
|
85
|
+
if row.get("type") == "session_meta":
|
|
86
|
+
parent = row["payload"].get("forked_from_id")
|
|
87
|
+
if parent:
|
|
88
|
+
parents = list((Path(home) / "sessions").glob(f"**/*{parent}.jsonl"))
|
|
89
|
+
if len(parents) != 1:
|
|
90
|
+
raise RuntimeError("fork history has a missing or ambiguous parent")
|
|
91
|
+
hashes.extend(history_image_hashes(parents[0], home, seen))
|
|
92
|
+
return hashes
|