cctally 1.118.0 → 1.118.2

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.
@@ -6871,9 +6871,14 @@ def build_cached_claude_range_aggregates_from_capture(
6871
6871
  try:
6872
6872
  if "projects" in failures:
6873
6873
  raise failures["projects"]
6874
+ # #1059: observing memos, so a spelling first resolved on an
6875
+ # incremental tick is registered for the identity sweep too. A flip
6876
+ # bumps the generation in `base`, which forces the cold branch below
6877
+ # with a fresh memo in the same build.
6878
+ _sc = _cctally()._load_sibling("_lib_snapshot_cache")
6874
6879
  if incremental:
6875
6880
  project_mut = copy.deepcopy(prior["project_mut"])
6876
- resolver_cache = dict(prior["resolver_cache"])
6881
+ resolver_cache = _sc.observing_project_memo(prior["resolver_cache"])
6877
6882
  for row, priced_entry in zip(rows, prepared):
6878
6883
  _fold_projects_entry(
6879
6884
  project_mut,
@@ -6883,7 +6888,8 @@ def build_cached_claude_range_aggregates_from_capture(
6883
6888
  priced_entry=priced_entry,
6884
6889
  )
6885
6890
  else:
6886
- resolver_cache = {}
6891
+ _sc.note_project_work("rangeFullRefolds")
6892
+ resolver_cache = _sc.observing_project_memo()
6887
6893
  project_mut = fold_projects_over_range(
6888
6894
  rows,
6889
6895
  resolver_cache=resolver_cache,
@@ -7080,6 +7086,7 @@ def _assemble_projects_via_cache(
7080
7086
  cw_end: "dt.datetime",
7081
7087
  cur_max_id: int,
7082
7088
  cur_max_seq: int,
7089
+ resolver_cache: "dict | None" = None,
7083
7090
  ) -> "tuple[dict, dict, dict]":
7084
7091
  """Flag-ON assembly (#269 §14 Win 2): recompute only the CURRENT week each
7085
7092
  warm tick; serve CLOSED weeks from the per-(project, week) cache on a hit and
@@ -7097,10 +7104,17 @@ def _assemble_projects_via_cache(
7097
7104
  tick's ``MAX(mutation_seq)`` (the #270 §8 delta watermark, so an id-stable
7098
7105
  in-place finalization is caught). The accumulator is single-writer here (this
7099
7106
  runs only on the sync-thread ``use_projects_env_cache=True`` path).
7107
+
7108
+ #1059: the memo is an OBSERVING memo (``resolver_cache`` when the caller
7109
+ supplies one), because every resolution here can be retained — by the
7110
+ current-week accumulator, a cold seed or a closed-week miss — and a
7111
+ retained resolution the identity sweep cannot see would keep a stale
7112
+ grouping until a restart.
7100
7113
  """
7101
7114
  c = _cctally()
7102
7115
  sc = c._load_sibling("_lib_snapshot_cache")
7103
- resolver_cache: dict = {}
7116
+ if resolver_cache is None:
7117
+ resolver_cache = sc.observing_project_memo()
7104
7118
  buckets: dict = {}
7105
7119
  total_cost_by_week: dict = {}
7106
7120
  key_by_bucket: dict = {}
@@ -7196,6 +7210,9 @@ def _assemble_projects_via_cache(
7196
7210
  if hit is not None:
7197
7211
  week_buckets, week_total = hit
7198
7212
  else:
7213
+ # #1059 SD3: counted when attempted, so a raising compute is
7214
+ # still visible as work.
7215
+ sc.note_project_work("closedWeekComputes")
7199
7216
  week_buckets, week_total = _aggregate_projects_week(
7200
7217
  conn, week_start=w, week_end=w_end,
7201
7218
  resolver_cache=resolver_cache,
@@ -7264,6 +7281,8 @@ def _build_projects_envelope(
7264
7281
  weeks_back: int = 12,
7265
7282
  use_projects_env_cache: bool = False,
7266
7283
  content_revision_leg: "str | None" = None,
7284
+ project_resolver_cache: "dict | None" = None,
7285
+ use_envelope_memo: bool = True,
7267
7286
  ) -> dict:
7268
7287
  """Build the ``projects.{current_week, trend}`` envelope block.
7269
7288
 
@@ -7300,6 +7319,13 @@ def _build_projects_envelope(
7300
7319
  lookup; the memo key carries it, so an in-place ``weekly_percent``
7301
7320
  correction (which moves no ``MAX(id)``) rebuilds the envelope. ``None``
7302
7321
  reads it here, on ``conn``.
7322
+
7323
+ #1059: ``project_resolver_cache`` is the resolver memo of the full-window
7324
+ walk, passed through to the cached assembly; ``None`` keeps a fresh plain
7325
+ dict (the walk) or an observing memo (the assembly). An owner build that
7326
+ retains this result passes an observing memo so the identity sweep sees
7327
+ every spelling. ``use_envelope_memo=False`` neither reads nor writes
7328
+ ``_PROJECTS_ENV_MEMO``, for request-thread callers.
7303
7329
  """
7304
7330
  c = _cctally()
7305
7331
 
@@ -7354,7 +7380,7 @@ def _build_projects_envelope(
7354
7380
  cw_key = getattr(current_week, "week_start_at", None)
7355
7381
  memo_key = (max_id, max_wus_id, entry_mutation_seq, cw_key, weeks_back,
7356
7382
  content_revision_leg)
7357
- cached = _PROJECTS_ENV_MEMO.get("value")
7383
+ cached = _PROJECTS_ENV_MEMO.get("value") if use_envelope_memo else None
7358
7384
  if cached is not None and _PROJECTS_ENV_MEMO.get("key") == memo_key:
7359
7385
  return cached
7360
7386
 
@@ -7420,13 +7446,17 @@ def _build_projects_envelope(
7420
7446
  buckets, total_cost_by_week, key_by_bucket = _assemble_projects_via_cache(
7421
7447
  conn, week_bounds=week_bounds, cw_start=cw_start, cw_end=cw_end,
7422
7448
  cur_max_id=max_id, cur_max_seq=entry_mutation_seq,
7449
+ resolver_cache=project_resolver_cache,
7423
7450
  )
7424
7451
  else:
7425
7452
  # `_resolve_project_key` is the production resolver; we use git-root
7426
7453
  # mode (default for `cmd_project --group` absent) — matches the
7427
7454
  # CLI's default.
7428
7455
  _resolve_project_key = c._resolve_project_key
7429
- resolver_cache: dict = {}
7456
+ resolver_cache: dict = (
7457
+ {} if project_resolver_cache is None else project_resolver_cache)
7458
+ c._load_sibling("_lib_snapshot_cache").note_project_work(
7459
+ "fullWindowProjectWalks")
7430
7460
 
7431
7461
  buckets = {}
7432
7462
  total_cost_by_week = {}
@@ -7787,8 +7817,9 @@ def _build_projects_envelope(
7787
7817
  "current_week": cw_block,
7788
7818
  "trend": trend_block,
7789
7819
  }
7790
- _PROJECTS_ENV_MEMO["key"] = memo_key
7791
- _PROJECTS_ENV_MEMO["value"] = result
7820
+ if use_envelope_memo:
7821
+ _PROJECTS_ENV_MEMO["key"] = memo_key
7822
+ _PROJECTS_ENV_MEMO["value"] = result
7792
7823
  return result
7793
7824
 
7794
7825
 
@@ -7849,11 +7880,15 @@ def _project_detail_for_window(
7849
7880
  if projects_envelope is not None:
7850
7881
  env = projects_envelope
7851
7882
  else:
7883
+ # #1059: this fallback runs on a request thread (the `/api/project`
7884
+ # handler passes `None` after a failed envelope build), so it must
7885
+ # neither read nor write the owner's process-wide envelope memo.
7852
7886
  env = _build_projects_envelope(
7853
7887
  conn,
7854
7888
  now_utc=now_utc,
7855
7889
  current_week=current_week,
7856
7890
  weeks_back=weeks_back,
7891
+ use_envelope_memo=False,
7857
7892
  )
7858
7893
 
7859
7894
  # Resolve project_key → bucket_path via the envelope's identity map.
@@ -8724,6 +8759,30 @@ def _debug_memory_owner(stats, *, prefix: str = "") -> dict:
8724
8759
  }
8725
8760
 
8726
8761
 
8762
+ def _debug_project_work() -> dict:
8763
+ """#1059 SD3: the project work counters, or ``{}`` when unreadable."""
8764
+ cctally = sys.modules.get("cctally")
8765
+ loader = getattr(cctally, "_load_sibling", None)
8766
+ if not callable(loader):
8767
+ return {}
8768
+ try:
8769
+ return dict(loader("_lib_snapshot_cache").project_work_stats())
8770
+ except Exception: # noqa: BLE001 -- diagnostics fail closed.
8771
+ return {}
8772
+
8773
+
8774
+ def _debug_project_identity() -> dict:
8775
+ """#1059: the project-identity sweep block, or ``{}`` when unreadable."""
8776
+ cctally = sys.modules.get("cctally")
8777
+ loader = getattr(cctally, "_load_sibling", None)
8778
+ if not callable(loader):
8779
+ return {}
8780
+ try:
8781
+ return dict(loader("_lib_snapshot_cache").project_identity_stats())
8782
+ except Exception: # noqa: BLE001 -- diagnostics fail closed.
8783
+ return {}
8784
+
8785
+
8727
8786
  def _debug_retained_memory(hub, *, main_frontier_stats=None) -> dict:
8728
8787
  """Numeric retained-owner ceilings; never values, keys or source paths."""
8729
8788
  owners: dict[str, dict] = {}
@@ -9610,6 +9669,13 @@ class DashboardHTTPHandler(BaseHTTPRequestHandler):
9610
9669
  # #901 §5.5: kernel-accounted process writes, the trailing
9611
9670
  # five-minute statistic and its budget verdict. Additive camelCase.
9612
9671
  "writeIo": _debug_write_io(),
9672
+ # #1059 SD3: cumulative counts at the project work sites, so a
9673
+ # warm-tick measurement can prove no closed week was rebuilt.
9674
+ # Additive and opaque, like every field of this diagnostic.
9675
+ "projectWork": _debug_project_work(),
9676
+ # #1059: the identity registry and sweep diagnostics, reported
9677
+ # separately from the accelerator memory budget.
9678
+ "projectIdentity": _debug_project_identity(),
9613
9679
  }
9614
9680
  if body["phases"] is None:
9615
9681
  body["note"] = "tracing_disabled"
@@ -7641,6 +7641,9 @@ def _capture_codex_accounting(
7641
7641
  active_roots,
7642
7642
  _cache_database_identity(context.cache_conn),
7643
7643
  ),
7644
+ # #1059: one observing memo per load, so every spelling the
7645
+ # qualified resolution visits is registered for the regroup
7646
+ # sweep.
7644
7647
  load_all=lambda: load_qualified_codex_entries(
7645
7648
  accounting_start,
7646
7649
  accounting_end,
@@ -7648,6 +7651,8 @@ def _capture_codex_accounting(
7648
7651
  sync=False,
7649
7652
  cache_conn=context.cache_conn,
7650
7653
  inventory=inventory,
7654
+ project_resolver_cache=(
7655
+ _lib_snapshot_cache.observing_project_memo()),
7651
7656
  ),
7652
7657
  load_paths=lambda identities: load_qualified_codex_entries(
7653
7658
  accounting_start,
@@ -7657,6 +7662,8 @@ def _capture_codex_accounting(
7657
7662
  cache_conn=context.cache_conn,
7658
7663
  source_identities=identities,
7659
7664
  inventory=inventory,
7665
+ project_resolver_cache=(
7666
+ _lib_snapshot_cache.observing_project_memo()),
7660
7667
  ),
7661
7668
  path_of=lambda entry: (
7662
7669
  str(entry.source_root_key), str(entry.source_path),
@@ -4908,6 +4908,269 @@ def _apply_codex_quota_latest_summary(
4908
4908
  conn.execute("RELEASE codex_quota_latest_apply")
4909
4909
 
4910
4910
 
4911
+ # ── #1033 §5.1: the exact reset-anchor evidence summary ─────────────────────
4912
+ #
4913
+ # `CodexResetAnchorResolver._components_for` seeded every anchor group by
4914
+ # reading the group's whole quota history (328,727 rows for the measured weekly
4915
+ # group) on every appended Codex hook, twice (walk and journal applier). The
4916
+ # components depend only on each distinct raw reset's minimum order key and the
4917
+ # row count, so this table holds exactly that per in-scope
4918
+ # (root, slot, limit key, raw reset) bucket: `row_count`, the lexicographic
4919
+ # minimum `(source_path, line_offset, id)` (never independently minimized
4920
+ # columns) and `stale`. A stale row's stored minimum is the deleted minimum, a
4921
+ # strict lower bound of every survivor; the seed never reads a stale group and
4922
+ # the drain's repair unit re-derives it.
4923
+ #
4924
+ # Membership is `source = 'codex' AND observed_slot IS NOT NULL`; the raw TEXT
4925
+ # spelling is the key and the seed folds spellings and skips unparseable raws.
4926
+ #
4927
+ # The triggers use built-in functions only, so a head-52 binary opening this
4928
+ # store keeps them firing, and they never upsert (an outer INSERT OR IGNORE
4929
+ # propagates its conflict policy into trigger bodies): an UPDATE, then an
4930
+ # INSERT ... WHERE NOT EXISTS. Insert and delete maintenance runs only while
4931
+ # the state is `ready`; every statement is a primary-key operation, and the
4932
+ # delete trigger never searches quota_window_snapshots for a replacement
4933
+ # minimum (#971). An insert whose (source_path, line_offset) is at or below a
4934
+ # stale minimum's is the exact minimum (that pair is unique within a bucket),
4935
+ # so a heal's same-line reinsert clears the staleness its delete caused. An
4936
+ # UPDATE of any key or order column flips the state to `bootstrap` and
4937
+ # advances the generation, which the drain's publication token reads (the
4938
+ # change ledger excludes `id`). A REPLACE against the snapshots table would
4939
+ # delete without firing the delete trigger; test_no_writer_replaces_quota_rows
4940
+ # refuses one.
4941
+
4942
+ CODEX_RESET_ANCHOR_EVIDENCE_STATE_KEY = "codex_reset_anchor_evidence"
4943
+ CODEX_RESET_ANCHOR_EVIDENCE_GENERATION_KEY = (
4944
+ "codex_reset_anchor_evidence_generation")
4945
+ CODEX_RESET_ANCHOR_EVIDENCE_READY = "ready"
4946
+ CODEX_RESET_ANCHOR_EVIDENCE_BOOTSTRAP = "bootstrap"
4947
+ CODEX_RESET_ANCHOR_EVIDENCE_TABLE = "codex_reset_anchor_evidence"
4948
+
4949
+ #: The five objects whose joint presence (with a valid state) means the summary
4950
+ #: has been maintained since it was last published.
4951
+ CODEX_RESET_ANCHOR_EVIDENCE_OBJECTS = (
4952
+ ("table", "codex_reset_anchor_evidence"),
4953
+ ("index", "idx_codex_reset_anchor_evidence_stale"),
4954
+ ("trigger", "trg_qws_anchor_evidence_ins"),
4955
+ ("trigger", "trg_qws_anchor_evidence_del"),
4956
+ ("trigger", "trg_qws_anchor_evidence_upd"),
4957
+ )
4958
+
4959
+ #: Every key or order column; an UPDATE of any of them invalidates readiness.
4960
+ _CODEX_RESET_ANCHOR_EVIDENCE_UPDATE_OF = (
4961
+ "source", "source_root_key", "observed_slot", "logical_limit_key",
4962
+ "resets_at_utc", "source_path", "line_offset", "id",
4963
+ )
4964
+
4965
+
4966
+ def _codex_reset_anchor_evidence_ddl() -> "tuple[str, ...]":
4967
+ """DDL for the summary, its stale index and its three triggers.
4968
+
4969
+ Triggers are dropped and re-created on every apply, like the latest
4970
+ summary's, so a definition change never leaves an older body behind; the
4971
+ table keeps IF NOT EXISTS and its rows survive.
4972
+ """
4973
+ state = CODEX_RESET_ANCHOR_EVIDENCE_STATE_KEY
4974
+ generation = CODEX_RESET_ANCHOR_EVIDENCE_GENERATION_KEY
4975
+ ready = (f"(SELECT value FROM cache_meta WHERE key = '{state}')"
4976
+ f" = '{CODEX_RESET_ANCHOR_EVIDENCE_READY}'")
4977
+
4978
+ def bucket(image: str) -> str:
4979
+ return (f"source_root_key = {image}.source_root_key"
4980
+ f" AND observed_slot = {image}.observed_slot"
4981
+ f" AND logical_limit_key = {image}.logical_limit_key"
4982
+ f" AND resets_at_utc = {image}.resets_at_utc")
4983
+
4984
+ def in_scope(image: str) -> str:
4985
+ return (f"{image}.source = 'codex'"
4986
+ f" AND {image}.observed_slot IS NOT NULL")
4987
+
4988
+ takes = (
4989
+ "((stale = 0 AND (NEW.source_path, NEW.line_offset, NEW.id)"
4990
+ " < (min_source_path, min_line_offset, min_id))"
4991
+ " OR (stale = 1 AND (NEW.source_path, NEW.line_offset)"
4992
+ " <= (min_source_path, min_line_offset)))")
4993
+ differs = " OR ".join(
4994
+ f"OLD.{column} IS NOT NEW.{column}"
4995
+ for column in _CODEX_RESET_ANCHOR_EVIDENCE_UPDATE_OF)
4996
+ return (
4997
+ "CREATE TABLE IF NOT EXISTS codex_reset_anchor_evidence (\n"
4998
+ " source_root_key TEXT NOT NULL,\n"
4999
+ " observed_slot TEXT NOT NULL,\n"
5000
+ " logical_limit_key TEXT NOT NULL,\n"
5001
+ " resets_at_utc TEXT NOT NULL,\n"
5002
+ " row_count INTEGER NOT NULL CHECK (row_count > 0),\n"
5003
+ " min_source_path TEXT NOT NULL,\n"
5004
+ " min_line_offset INTEGER NOT NULL,\n"
5005
+ " min_id INTEGER NOT NULL,\n"
5006
+ " stale INTEGER NOT NULL DEFAULT 0"
5007
+ " CHECK (stale IN (0, 1)),\n"
5008
+ " PRIMARY KEY (source_root_key, observed_slot,"
5009
+ " logical_limit_key, resets_at_utc)\n"
5010
+ " ) WITHOUT ROWID",
5011
+ "CREATE INDEX IF NOT EXISTS idx_codex_reset_anchor_evidence_stale"
5012
+ " ON codex_reset_anchor_evidence"
5013
+ " (source_root_key, observed_slot, logical_limit_key)"
5014
+ " WHERE stale = 1",
5015
+ "DROP TRIGGER IF EXISTS trg_qws_anchor_evidence_ins",
5016
+ "CREATE TRIGGER trg_qws_anchor_evidence_ins\n"
5017
+ " AFTER INSERT ON quota_window_snapshots\n"
5018
+ f" WHEN {in_scope('NEW')} AND {ready}\n"
5019
+ " BEGIN\n"
5020
+ " UPDATE codex_reset_anchor_evidence"
5021
+ " SET row_count = row_count + 1,"
5022
+ f" min_source_path = CASE WHEN {takes} THEN NEW.source_path"
5023
+ " ELSE min_source_path END,"
5024
+ f" min_line_offset = CASE WHEN {takes} THEN NEW.line_offset"
5025
+ " ELSE min_line_offset END,"
5026
+ f" min_id = CASE WHEN {takes} THEN NEW.id ELSE min_id END,"
5027
+ f" stale = CASE WHEN {takes} THEN 0 ELSE stale END"
5028
+ f" WHERE {bucket('NEW')};\n"
5029
+ " INSERT INTO codex_reset_anchor_evidence"
5030
+ " (source_root_key, observed_slot, logical_limit_key, resets_at_utc,"
5031
+ " row_count, min_source_path, min_line_offset, min_id, stale)"
5032
+ " SELECT NEW.source_root_key, NEW.observed_slot,"
5033
+ " NEW.logical_limit_key, NEW.resets_at_utc, 1, NEW.source_path,"
5034
+ " NEW.line_offset, NEW.id, 0"
5035
+ " WHERE NOT EXISTS (SELECT 1 FROM codex_reset_anchor_evidence"
5036
+ f" WHERE {bucket('NEW')});\n"
5037
+ " END",
5038
+ "DROP TRIGGER IF EXISTS trg_qws_anchor_evidence_del",
5039
+ "CREATE TRIGGER trg_qws_anchor_evidence_del\n"
5040
+ " AFTER DELETE ON quota_window_snapshots\n"
5041
+ f" WHEN {in_scope('OLD')} AND {ready}\n"
5042
+ " BEGIN\n"
5043
+ " UPDATE cache_meta"
5044
+ f" SET value = '{CODEX_RESET_ANCHOR_EVIDENCE_BOOTSTRAP}'"
5045
+ f" WHERE key = '{state}'"
5046
+ " AND NOT EXISTS (SELECT 1 FROM codex_reset_anchor_evidence"
5047
+ f" WHERE {bucket('OLD')});\n"
5048
+ " DELETE FROM codex_reset_anchor_evidence"
5049
+ f" WHERE {bucket('OLD')} AND row_count = 1;\n"
5050
+ " UPDATE codex_reset_anchor_evidence"
5051
+ " SET row_count = row_count - 1,"
5052
+ " stale = CASE WHEN (min_source_path, min_line_offset, min_id)"
5053
+ " = (OLD.source_path, OLD.line_offset, OLD.id) THEN 1 ELSE stale END"
5054
+ f" WHERE {bucket('OLD')};\n"
5055
+ " END",
5056
+ "DROP TRIGGER IF EXISTS trg_qws_anchor_evidence_upd",
5057
+ "CREATE TRIGGER trg_qws_anchor_evidence_upd\n"
5058
+ " AFTER UPDATE OF "
5059
+ f"{', '.join(_CODEX_RESET_ANCHOR_EVIDENCE_UPDATE_OF)}"
5060
+ " ON quota_window_snapshots\n"
5061
+ f" WHEN (({in_scope('OLD')}) OR ({in_scope('NEW')}))"
5062
+ f" AND ({differs})\n"
5063
+ " BEGIN\n"
5064
+ " UPDATE cache_meta"
5065
+ " SET value = CAST(CAST(value AS INTEGER) + 1 AS TEXT)"
5066
+ f" WHERE key = '{generation}';\n"
5067
+ " INSERT INTO cache_meta (key, value)"
5068
+ f" SELECT '{generation}', '1' WHERE NOT EXISTS"
5069
+ f" (SELECT 1 FROM cache_meta WHERE key = '{generation}');\n"
5070
+ " UPDATE cache_meta"
5071
+ f" SET value = '{CODEX_RESET_ANCHOR_EVIDENCE_BOOTSTRAP}'"
5072
+ f" WHERE key = '{state}'"
5073
+ f" AND value = '{CODEX_RESET_ANCHOR_EVIDENCE_READY}';\n"
5074
+ " END",
5075
+ )
5076
+
5077
+
5078
+ def _advance_codex_reset_anchor_evidence_generation(
5079
+ conn: sqlite3.Connection,
5080
+ ) -> None:
5081
+ """Advance (or initialize) the invalidation generation in the caller's
5082
+ transaction. A malformed stored value restarts from it parsed as 0."""
5083
+ row = conn.execute(
5084
+ "SELECT value FROM cache_meta WHERE key = ?",
5085
+ (CODEX_RESET_ANCHOR_EVIDENCE_GENERATION_KEY,)).fetchone()
5086
+ try:
5087
+ current = int(row[0]) if row is not None else 0
5088
+ except (TypeError, ValueError):
5089
+ current = 0
5090
+ conn.execute(
5091
+ "INSERT INTO cache_meta(key, value) VALUES (?, ?) "
5092
+ "ON CONFLICT(key) DO UPDATE SET value=excluded.value",
5093
+ (CODEX_RESET_ANCHOR_EVIDENCE_GENERATION_KEY, str(current + 1)))
5094
+
5095
+
5096
+ def _set_codex_reset_anchor_evidence_state(
5097
+ conn: sqlite3.Connection, state: str,
5098
+ ) -> None:
5099
+ conn.execute(
5100
+ "INSERT INTO cache_meta(key, value) VALUES (?, ?) "
5101
+ "ON CONFLICT(key) DO UPDATE SET value=excluded.value",
5102
+ (CODEX_RESET_ANCHOR_EVIDENCE_STATE_KEY, state))
5103
+
5104
+
5105
+ def _apply_codex_reset_anchor_evidence(conn: sqlite3.Connection) -> None:
5106
+ """Install the reset-anchor evidence summary on every delivery path.
5107
+
5108
+ Shared by ``_apply_cache_schema`` and migration 053 so fresh and upgraded
5109
+ stores converge. Atomic like ``_apply_codex_retained_identity_inventory``:
5110
+ the table, its stale index, the three triggers and the state commit
5111
+ together, as a savepoint inside the caller's transaction or as an
5112
+ IMMEDIATE transaction of their own.
5113
+
5114
+ The state is decided only when any of the five objects was missing before
5115
+ this call, or the state key is missing or malformed: every summary row is
5116
+ deleted, the generation advances and the state becomes ``ready`` when no
5117
+ in-scope Codex quota row exists, else ``bootstrap`` for the drain's
5118
+ off-hook bootstrap unit. A store carrying all five objects and a valid
5119
+ state has been maintained since they were created, so its recorded state
5120
+ stands (a markerless retry changes nothing). A history scan never runs
5121
+ here: the population check is one LIMIT 1 probe. Guarded on the columns
5122
+ the triggers read, which a legacy-shape cache whose schema apply returned
5123
+ early can lack, and on ``cache_meta``.
5124
+ """
5125
+ columns = {
5126
+ str(row[0]) for row in conn.execute(
5127
+ "SELECT name FROM pragma_table_info('quota_window_snapshots')")
5128
+ }
5129
+ if not ({"id", *_CODEX_RESET_ANCHOR_EVIDENCE_UPDATE_OF} <= columns
5130
+ and _table_exists(conn, "cache_meta")):
5131
+ return
5132
+ own_transaction = not conn.in_transaction
5133
+ conn.execute(
5134
+ "BEGIN IMMEDIATE" if own_transaction
5135
+ else "SAVEPOINT codex_reset_anchor_evidence_apply")
5136
+ try:
5137
+ present = {
5138
+ (str(row[0]), str(row[1])) for row in conn.execute(
5139
+ "SELECT type, name FROM sqlite_master "
5140
+ "WHERE type IN ('table', 'index', 'trigger')")
5141
+ }
5142
+ complete = all(
5143
+ item in present for item in CODEX_RESET_ANCHOR_EVIDENCE_OBJECTS)
5144
+ state_row = conn.execute(
5145
+ "SELECT value FROM cache_meta WHERE key = ?",
5146
+ (CODEX_RESET_ANCHOR_EVIDENCE_STATE_KEY,)).fetchone()
5147
+ valid_state = state_row is not None and state_row[0] in (
5148
+ CODEX_RESET_ANCHOR_EVIDENCE_READY,
5149
+ CODEX_RESET_ANCHOR_EVIDENCE_BOOTSTRAP)
5150
+ for statement in _codex_reset_anchor_evidence_ddl():
5151
+ conn.execute(statement)
5152
+ if not (complete and valid_state):
5153
+ conn.execute("DELETE FROM codex_reset_anchor_evidence")
5154
+ _advance_codex_reset_anchor_evidence_generation(conn)
5155
+ populated = conn.execute(
5156
+ "SELECT 1 FROM quota_window_snapshots WHERE source = 'codex' "
5157
+ "AND observed_slot IS NOT NULL LIMIT 1").fetchone()
5158
+ _set_codex_reset_anchor_evidence_state(
5159
+ conn, CODEX_RESET_ANCHOR_EVIDENCE_BOOTSTRAP if populated
5160
+ else CODEX_RESET_ANCHOR_EVIDENCE_READY)
5161
+ except BaseException:
5162
+ if own_transaction:
5163
+ conn.rollback()
5164
+ else:
5165
+ conn.execute("ROLLBACK TO codex_reset_anchor_evidence_apply")
5166
+ conn.execute("RELEASE codex_reset_anchor_evidence_apply")
5167
+ raise
5168
+ if own_transaction:
5169
+ conn.commit()
5170
+ else:
5171
+ conn.execute("RELEASE codex_reset_anchor_evidence_apply")
5172
+
5173
+
4911
5174
  def _apply_codex_quota_load_order_index(conn: sqlite3.Connection) -> None:
4912
5175
  """The general loader's ORDER BY, served by an index (#901 §5.1, T1/T3).
4913
5176
 
@@ -5527,6 +5790,11 @@ CACHE_REDERIVABLE_OBJECTS: "tuple[SchemaDeliveryObject, ...]" = (
5527
5790
  "index", "idx_codex_files_root_thread",
5528
5791
  "_apply_codex_accounting_change_ledger",
5529
5792
  "052_codex_delete_trigger_plans"),
5793
+ # #1033 spec §5.5: migration 053's handler calls the shared helper.
5794
+ *(SchemaDeliveryObject(
5795
+ kind, name, "_apply_codex_reset_anchor_evidence",
5796
+ "053_codex_reset_anchor_evidence")
5797
+ for kind, name in CODEX_RESET_ANCHOR_EVIDENCE_OBJECTS),
5530
5798
  )
5531
5799
 
5532
5800
 
@@ -6370,6 +6638,8 @@ def _apply_cache_schema(conn: sqlite3.Connection) -> None:
6370
6638
  # window-attribution read order (Amendment 1 item 1). BEFORE the
6371
6639
  # legacy-FTS early-return, so an old-shape cache.db gains them.
6372
6640
  _apply_codex_quota_latest_summary(conn)
6641
+ # #1033 §5.5: the reset-anchor evidence summary and its readiness state.
6642
+ _apply_codex_reset_anchor_evidence(conn)
6373
6643
  _apply_codex_quota_load_order_index(conn)
6374
6644
  _apply_codex_metadata_lookup_indexes(conn)
6375
6645
  _apply_pricing_model_indexes(conn)
@@ -6571,6 +6841,10 @@ def _apply_conversations_schema(conn: sqlite3.Connection) -> None:
6571
6841
  -- #927: the retained-identity inventory is cache.db prune state. Its
6572
6842
  -- triggers went with the four family tables dropped above.
6573
6843
  DROP TABLE IF EXISTS codex_retained_identities;
6844
+ -- #1033: the reset-anchor evidence summary is a cache.db read model
6845
+ -- over quota_window_snapshots, whose drop above took its triggers;
6846
+ -- dropping the table takes its stale index.
6847
+ DROP TABLE IF EXISTS codex_reset_anchor_evidence;
6574
6848
 
6575
6849
  CREATE TABLE IF NOT EXISTS conversation_source_files (
6576
6850
  path TEXT PRIMARY KEY,
@@ -10848,6 +11122,36 @@ def _052_codex_delete_trigger_plans(conn: sqlite3.Connection) -> None:
10848
11122
  conn.commit()
10849
11123
 
10850
11124
 
11125
+ @cache_migration("053_codex_reset_anchor_evidence")
11126
+ def _053_codex_reset_anchor_evidence(conn: sqlite3.Connection) -> None:
11127
+ """#1033 spec §5.5: deliver the reset-anchor evidence summary.
11128
+
11129
+ The schema apply that declares the table, its stale index and its three
11130
+ triggers is version-gated, so only a registered migration reaches an
11131
+ already-current store. The shared helper records ``ready`` when no
11132
+ in-scope Codex quota row exists, else ``bootstrap``: the drain's
11133
+ ``reset_anchor_evidence_bootstrap`` unit (or ``cache-sync`` inline)
11134
+ aggregates an existing history off the hook, never here, so an ordinary
11135
+ open pays no history scan. Until then every seed falls back to the full
11136
+ read, which is today's behaviour.
11137
+
11138
+ Downgrade: a head-52 binary opening a head-53 store never reads the
11139
+ summary, and the dispatcher's version-ahead recovery trims this marker.
11140
+ The built-in-only triggers keep maintaining a ``ready`` summary under its
11141
+ writes, including its row-by-row ``_clear_codex_derived_rows``, which
11142
+ empties the summary exactly; a ``bootstrap`` summary stays inert. A
11143
+ re-upgrade re-runs this handler, finds every object and a valid state, and
11144
+ changes nothing.
11145
+
11146
+ Takes no provider flock: the helper's IMMEDIATE transaction (or savepoint)
11147
+ makes the trigger creation and the state decision atomic against every
11148
+ writer. Re-running is a no-op. NO self-stamp — the dispatcher
11149
+ central-stamps on a clean return (#140).
11150
+ """
11151
+ _apply_codex_reset_anchor_evidence(conn)
11152
+ conn.commit()
11153
+
11154
+
10851
11155
  # === Region 7d: Stats migration 008_recompute_weekly_cost_snapshots_dedup_fix ===
10852
11156
 
10853
11157
  @stats_migration("008_recompute_weekly_cost_snapshots_dedup_fix")
@@ -1160,9 +1160,16 @@ class _CodexProjectResolution:
1160
1160
 
1161
1161
  __slots__ = ("_group", "_resolver_cache", "_by_cwd", "_by_git_json")
1162
1162
 
1163
- def __init__(self, group: str = "git-root") -> None:
1163
+ def __init__(
1164
+ self, group: str = "git-root",
1165
+ resolver_cache: "dict[object, object] | None" = None,
1166
+ ) -> None:
1164
1167
  self._group = group
1165
- self._resolver_cache: dict[object, object] = {}
1168
+ # #1059: the owner's dashboard loads pass an observing memo so every
1169
+ # spelling this resolution visits is registered for the regroup
1170
+ # sweep; every other caller keeps a private plain dict.
1171
+ self._resolver_cache: dict[object, object] = (
1172
+ {} if resolver_cache is None else resolver_cache)
1166
1173
  self._by_cwd: dict[str, object] = {}
1167
1174
  self._by_git_json: dict[str, str | None] = {}
1168
1175
 
@@ -1248,6 +1255,7 @@ def load_qualified_codex_entries(
1248
1255
  source_identities: Iterable[tuple[str, str]] | None = None,
1249
1256
  inventory: "CodexThreadMetadataInventory | None" = None,
1250
1257
  _on_sync: "Callable[[object], object] | None" = None,
1258
+ project_resolver_cache: "dict[object, object] | None" = None,
1251
1259
  ) -> tuple[QualifiedCodexEntry, ...]:
1252
1260
  """Load exactly one bounded, root-qualified Codex accounting read.
1253
1261
 
@@ -1316,7 +1324,10 @@ def load_qualified_codex_entries(
1316
1324
  conn.row_factory = previous_row_factory
1317
1325
 
1318
1326
  return _qualify_codex_rows(
1319
- rows, inherited_metadata, speed=speed, group=group)
1327
+ rows, inherited_metadata, speed=speed, group=group,
1328
+ projects=_CodexProjectResolution(
1329
+ group, resolver_cache=project_resolver_cache),
1330
+ )
1320
1331
 
1321
1332
 
1322
1333
  def _qualify_codex_rows(