Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
94b7b1e
fix(live): repair cursor authority across active archives
Sinity Aug 11, 2026
995eb86
fix(live): follow active index authority in every route
Sinity Aug 11, 2026
5795e3c
fix(live): preserve terminal evidence for new source paths
Sinity Aug 11, 2026
5743f4c
fix(live): redrain newly added hook spool shards
Sinity Aug 11, 2026
ec1756b
fix(live): retry hook shard publication races
Sinity Aug 11, 2026
843ca58
fix(live): bound hook shard retries
Sinity Aug 11, 2026
fae5087
fix: harden live ingest authority repairs
Sinity Aug 12, 2026
22f4ab2
test(live): align synthetic archive authority fixtures
Sinity Aug 12, 2026
46891e7
fix(live): retain unknown weak-path JSON
Sinity Aug 12, 2026
2c7ab72
fix(live): repair ingest authority lifecycles
Sinity Aug 12, 2026
bb041aa
fix(live): prioritize strong sidecars before streaming
Sinity Aug 12, 2026
e0cda08
test(live): isolate writer exit deadline
Sinity Aug 13, 2026
354cc34
fix(live): close ingest authority review gaps
Sinity Aug 13, 2026
584aed1
docs(storage): define scoped retention authority
Sinity Aug 13, 2026
041a9ca
fix(retention): revoke stale terminal parse authority
Sinity Aug 13, 2026
f649f4c
fix(raw): clear stale validation after reparse
Sinity Aug 13, 2026
cd2bf4a
fix(raw): retain current validation after reparse
Sinity Aug 13, 2026
8eaa421
fix(raw): replay raws after successful reparse
Sinity Aug 13, 2026
9e63e8f
fix(storage): close live archive authority gaps
Sinity Aug 13, 2026
8f57ade
fix(watcher): surface non-transient spool failures
Sinity Aug 13, 2026
7c59321
fix(ingest): preserve live and generation authority
Sinity Aug 13, 2026
1242cc6
fix(ingest): bind source writes to live authority
Sinity Aug 13, 2026
c6f3e69
fix(watcher): keep degraded acquisition source-only
Sinity Aug 13, 2026
8ef9e72
fix(storage): preserve live generation authority
Sinity Aug 13, 2026
39181b8
fix(storage): order retained evidence by observation
Sinity Aug 13, 2026
bedb186
fix(storage): bind raw authority to active generation
Sinity Aug 13, 2026
6dc78df
fix(storage): pin whale planning to active index
Sinity Aug 13, 2026
5f0acd4
fix(live): honor source and generation authority
Sinity Aug 13, 2026
f7cb9e3
fix(ingest): avoid decode in source-only acquisition
Sinity Aug 13, 2026
4cebae5
fix(daemon): pin generation through FTS closure
Sinity Aug 13, 2026
35abd21
fix(ingest): preserve source authority across outages
Sinity Aug 13, 2026
c1cd22b
fix(ingest): replay retained source authority
Sinity Aug 13, 2026
21b25ec
fix(repair): preserve raw authority contracts
Sinity Aug 13, 2026
08ba6a7
fix(replay): bound retained source recovery
Sinity Aug 13, 2026
4bbc47d
test(replay): type eager-read sentinel
Sinity Aug 13, 2026
b43242c
fix: preserve retained raw replay authority (#3968)
Sinity Aug 13, 2026
79a5b2d
fix: retain source-only append authority
Sinity Aug 13, 2026
ade85d2
fix: bind source-only append before parsing
Sinity Aug 13, 2026
b90b6f6
fix: preserve source-only append authority
Sinity Aug 13, 2026
da30080
fix: scan retained JSONL before terminal classification
Sinity Aug 13, 2026
95e9b79
fix: preserve stream exclusions during retained inspection
Sinity Aug 13, 2026
e843008
fix: bound retained stream recovery records
Sinity Aug 13, 2026
c3b3303
fix: retain retry authority for bounded acquisition
Sinity Aug 13, 2026
355cfd7
test: patch ZIP acquisition through canonical export
Sinity Aug 13, 2026
d0add31
fix: stabilize exact-head verification startup
Sinity Aug 13, 2026
dd5ec5b
fix: retain oversized stream parse candidates
Sinity Aug 13, 2026
b9567a6
fix: isolate exact-head ref authority
Sinity Aug 13, 2026
c9a3f64
fix: close exact-head ingest authority gaps
Sinity Aug 13, 2026
f80f07d
fix: preserve unknown JSONL document fallback
Sinity Aug 13, 2026
5ca621d
fix: bound oversized unknown replay detection
Sinity Aug 13, 2026
fbeb3f2
fix: close retained authority review gaps
Sinity Aug 13, 2026
6125915
fix: harden retained provider authority routes
Sinity Aug 13, 2026
6bef347
fix: close retained authority review residuals
Sinity Aug 13, 2026
553a05d
fix: close retained replay review gaps
Sinity Aug 13, 2026
b7057eb
fix: preserve provider and ZIP acquisition authority
Sinity Aug 14, 2026
118005d
fix: harden retained replay review paths
Sinity Aug 14, 2026
ec151aa
fix: hold generation authority across raw recovery
Sinity Aug 14, 2026
7653393
fix: prevent raw writes before generation lease
Sinity Aug 14, 2026
c0cd770
fix: hold rebuild exclusion for daemon lifetime
Sinity Aug 14, 2026
4984774
refactor: route daemon lease through product boundary
Sinity Aug 14, 2026
7e58dd9
fix: retain rebuild exclusion through writer drain
Sinity Aug 14, 2026
8dbb635
fix: retain rebuild exclusion on drain cancellation
Sinity Aug 14, 2026
ed804c8
fix: retain rebuild exclusion across cleanup failures
Sinity Aug 14, 2026
58a687b
fix: scan oversized retained JSONL records structurally
Sinity Aug 14, 2026
7412c09
fix: drain standalone watcher after stop failure
Sinity Aug 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions devtools/verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -3861,24 +3861,32 @@ def _main(argv: list[str] | None = None) -> int:
final_checkout_fingerprint = worktree_fingerprint(ROOT)
mutation_observation = finish_checkout_mutation_monitor(mutation_monitor)
checkout_stable = True
if (
changed_path_authority_failed
or head is None
checkout_fingerprint_unavailable = (
head is None
or final_head is None
or "unavailable" in {checkout_fingerprint, final_checkout_fingerprint}
or mutation_observation.unavailable
):
or "unavailable"
in {
checkout_fingerprint,
final_checkout_fingerprint,
}
)
if changed_path_authority_failed or checkout_fingerprint_unavailable or mutation_observation.unavailable:
checkout_stable = False
diagnosis = (
"testmon_changed_path_authority_unavailable"
if changed_path_authority_failed
else (
"checkout_fingerprint_unavailable"
if checkout_fingerprint_unavailable
else "checkout_mutation_monitor_unavailable"
)
)
step_results.append(
{
"name": "checkout stability",
"duration_s": 0.0,
"exit": 125,
"diagnosis": (
"testmon_changed_path_authority_unavailable"
if changed_path_authority_failed
else "checkout_fingerprint_unavailable"
),
"diagnosis": diagnosis,
"initial_git_head": head,
"final_git_head": final_head,
"initial_worktree_fingerprint": checkout_fingerprint,
Expand All @@ -3887,7 +3895,7 @@ def _main(argv: list[str] | None = None) -> int:
)
if exit_code == 0:
exit_code = 125
sys.stderr.write("verify: checkout fingerprint unavailable; evidence is not exact-head.\n")
sys.stderr.write(f"verify: {diagnosis.replace('_', ' ')}; evidence is not exact-head.\n")
elif final_head != head or mutation_observation.changed or final_checkout_fingerprint != checkout_fingerprint:
checkout_stable = False
step_results.append(
Expand Down
85 changes: 64 additions & 21 deletions devtools/verify_runs.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,12 +277,15 @@ def __init__(self, root: Path) -> None:
self._unavailable = False
self._stop = threading.Event()
self._ready = threading.Event()
self._initialized = threading.Event()
self._thread: threading.Thread | None = None
self._state_lock = threading.Lock()
self._tracked_paths: frozenset[Path] = frozenset()
self._tracked_directories: frozenset[Path] = frozenset()
self._ignored_roots: frozenset[Path] = frozenset()
self._git_index_path: Path | None = None
self._git_current_ref_path: Path | None = None
self._git_current_ref_was_loose: bool | None = None
self._git_authority_paths: dict[Path, str] = {}
self._directory_topology_fingerprint: frozenset[str] | None = None

Expand All @@ -292,14 +295,35 @@ def start(self) -> None:
with self._state_lock:
self._unavailable = True
self._ready.set()
self._initialized.set()
return
self._thread = threading.Thread(target=self._watch, name="checkout-mutation-monitor", daemon=True)
# Repository enumeration and Git authority discovery are synchronous
# preflight, not native watcher startup. Keeping them outside the
# backend deadline prevents a slow CI checkout from consuming the
# entire readiness budget before watchfiles can initialize.
watched_directories = self._watched_directories()
if self._unavailable:
self._ready.set()
self._initialized.set()
return
self._thread = threading.Thread(
target=self._watch,
args=(watched_directories,),
name="checkout-mutation-monitor",
daemon=True,
)
self._thread.start()
if not self._ready.wait(timeout=self._WATCH_START_TIMEOUT_S):
with self._state_lock:
self._unavailable = True
self._stop.set()
self._thread.join(timeout=self._WATCH_START_TIMEOUT_S)
return
# The one-second deadline proves only native backend startup. The
# protected topology recheck is ordinary repository discovery and may
# legitimately take longer on a cold CI checkout; complete it before
# the verification command can mutate the tree.
self._initialized.wait()

def finish(self) -> CheckoutMutationObservation:
"""Stop monitoring only after the caller took its final fingerprint."""
Expand All @@ -320,11 +344,8 @@ def finish(self) -> CheckoutMutationObservation:
observed_path=self._observed_path,
)

def _watch(self) -> None:
def _watch(self, watched_directories: Sequence[Path]) -> None:
try:
watched_directories = self._watched_directories()
if self._unavailable:
return
for changes in watchfiles.watch(
*watched_directories,
watch_filter=None,
Expand All @@ -338,12 +359,17 @@ def _watch(self) -> None:
recursive=False,
):
# An empty timeout batch proves the backend initialized before
# a verification command starts, closing the startup race.
if not self._ready.is_set() and not self._directory_topology_is_stable(watched_directories):
with self._state_lock:
self._unavailable = True
return
self._ready.set()
# a verification command starts, closing the startup race. The
# active watcher protects the following topology recheck. The
# native-ready event has its own bounded startup deadline;
# ``start`` waits separately for repository discovery.
if not self._ready.is_set():
self._ready.set()
if not self._directory_topology_is_stable(watched_directories):
with self._state_lock:
self._unavailable = True
return
self._initialized.set()
for _change, raw_path in changes:
self._record_change(Path(raw_path))
if self._changed or self._unavailable:
Expand All @@ -356,6 +382,7 @@ def _watch(self) -> None:
self._unavailable = True
finally:
self._ready.set()
self._initialized.set()

@classmethod
def _polling_backend_requested(cls) -> bool:
Expand Down Expand Up @@ -459,6 +486,12 @@ def _resolve_git_head_paths(self) -> dict[Path, str]:
self._unavailable = True
return paths
paths[Path(raw_head_path)] = ".git/HEAD"
if symbolic_result.returncode == 1:
# A detached checkout's complete revision authority is the
# worktree-specific HEAD file. packed-refs is shared by every
# linked worktree, so unrelated fetch/pack maintenance cannot
# mutate this checkout and must not invalidate its verification.
return paths
packed_result = self._git_command(["rev-parse", "--path-format=absolute", "--git-path", "packed-refs"])
if packed_result is None:
return paths
Expand All @@ -468,16 +501,18 @@ def _resolve_git_head_paths(self) -> dict[Path, str]:
self._unavailable = True
return paths
paths[Path(raw_packed_path)] = ".git/packed-refs"
if symbolic_result.returncode == 0:
ref_result = self._git_command(["rev-parse", "--path-format=absolute", "--git-path", symbolic_ref])
if ref_result is None:
return paths
raw_ref_path = os.fsdecode(ref_result.stdout).strip()
if not raw_ref_path:
with self._state_lock:
self._unavailable = True
return paths
paths[Path(raw_ref_path)] = f".git/{symbolic_ref}"
ref_result = self._git_command(["rev-parse", "--path-format=absolute", "--git-path", symbolic_ref])
if ref_result is None:
return paths
raw_ref_path = os.fsdecode(ref_result.stdout).strip()
if not raw_ref_path:
with self._state_lock:
self._unavailable = True
return paths
self._git_current_ref_path = Path(raw_ref_path)
if self._git_current_ref_was_loose is None:
self._git_current_ref_was_loose = self._git_current_ref_path.exists()
paths[self._git_current_ref_path] = f".git/{symbolic_ref}"
return paths

def _git_command(
Expand Down Expand Up @@ -527,6 +562,14 @@ def _record_change(self, candidate: Path) -> None:
if not candidate.is_absolute():
candidate = self.root / candidate
for authority_path, label in self._git_authority_paths.items():
if label == ".git/packed-refs" and self._git_current_ref_was_loose is True:
Comment thread
Sinity marked this conversation as resolved.
# packed-refs is shared by linked worktrees. When this
# worktree's current branch has a loose ref, unrelated fetch
# maintenance cannot change its HEAD through the packed file.
# A real pack transition remains visible when the loose ref
# is removed or replaced. Preserve the startup state so a
# packed-to-loose transition cannot hide its own first event.
continue
if candidate != authority_path and authority_path.is_relative_to(candidate):
with self._state_lock:
self._changed = True
Expand Down
6 changes: 3 additions & 3 deletions docs/plans/classifier-fingerprints.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
}
},
"polylogue/archive/artifact_taxonomy/runtime.py:classify_artifact_path": {
"fingerprint": "10f54d1827fdca585985cf3180e54c27098de26121530076e51a26cef7b2de24",
"fingerprint": "32b6f26516b4cc9ed0342c262e492fca469aaca9fb47a66e4ca1c76b7f58987a",
"covered_by": {
"kind": "acknowledged_safe",
"reason": "Baseline snapshot at classifier-fingerprint gate introduction (polylogue-gucv).",
"ref": "polylogue-gucv"
"reason": "Strong-only admission helper preserves this classifier output; no archived payload classification changes.",
"ref": "#3952"
}
},
"polylogue/archive/artifact_taxonomy/support.py:looks_like_beads_interaction": {
Expand Down
2 changes: 1 addition & 1 deletion docs/plans/layering.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ writer_modules:
interruption: atomic
entrypoints:
[apply_source_raw_state_update, bind_source_raw_revision, record_capture_mode_observation,
record_excised_blob_hash, write_history_sidecar,
record_excised_blob_hash, record_raw_container_coordinate, write_history_sidecar,
delete_source_hook_event, write_source_blob_refs, write_source_hook_event, write_source_raw_session,
write_source_raw_session_blob_ref, upsert_raw_artifact]
- path: polylogue/storage/sqlite/archive_tiers/write.py
Expand Down
7 changes: 6 additions & 1 deletion polylogue/archive/artifact_taxonomy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@
from __future__ import annotations

from polylogue.archive.artifact_taxonomy.models import ArtifactClassification, ArtifactKind
from polylogue.archive.artifact_taxonomy.runtime import classify_artifact, classify_artifact_path
from polylogue.archive.artifact_taxonomy.runtime import (
classify_artifact,
classify_artifact_path,
strong_path_classification,
)

__all__ = [
"ArtifactClassification",
"ArtifactKind",
"classify_artifact",
"classify_artifact_path",
"strong_path_classification",
]
15 changes: 15 additions & 0 deletions polylogue/archive/artifact_taxonomy/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,21 @@ def classify_artifact_path(
"""
if weak := _self_generated_artifact_dir_classification(source_path, provider=provider):
return weak
return strong_path_classification(source_path, provider=provider)


def strong_path_classification(
source_path: str | Path | None,
*,
provider: str | Provider,
) -> ArtifactClassification | None:
"""Classify only definitive path rules.

Live admission uses this before deciding whether a payload may enter a
bounded streaming route. The weak ``analysis/`` location heuristic is
deliberately excluded there because it must yield to bounded payload
evidence or the streaming policy.
"""
return _classify_artifact_path_strong(source_path, provider=provider)


Expand Down
Loading