Skip to content

fix(delivery): one repository spelled two ways is one repository, across sessions too - #6450

Merged
vivekchand merged 3 commits into
mainfrom
fix/case-fold-across-sessions
Oct 15, 2026
Merged

vivekchand merged 3 commits into
mainfrom
fix/case-fold-across-sessions

Conversation

@vivekchand

Copy link
Copy Markdown
Owner

Why

#6436 folded two spellings of one repository into one row, and that fold runs inside a session. The live node then listed ClawMetry/clawmetry-pro beside clawmetry/clawmetry-pro: the two spellings arrived from two different sessions, and nothing folded them once they had become rows, because the read query grouped on the exact text. GitHub treats an owner and a name case-insensitively, so that is one repository presented as two.

The same case sensitivity sat on the join that fetches a pull request's title, state and URL from the local repository scan, so a session that typed one case while the checkout reported another got a row with no title and no state: a pull request nobody can identify.

Found by auditing the live node after 0.12.924 shipped, which is the sixth time in this effort that reading the live tab after a fix found the next defect.

What

  • local_store.query_delivery folds rows on a case-insensitive key and displays the spelling the node's own sessions used most. A tie falls to the lexicographically smaller spelling rather than to row order, so the same rows always render the same name.
  • This is the rule attribution._fold_repo_case already applies within a session, lifted to the only scope that can see across sessions.
  • The key lowercases only the field that holds a repository name: ref on a repository row, repo on every other kind. A branch name and a file path are case-sensitive in git and on disk, and folding those would merge two different branches and claim work on a branch that does not exist.
  • The pull-request metadata join is matched case-insensitively for the same reason.

Verified

Against the live node's own 256 sessions, by running the old and the new query over the same rows:

kind old rows new rows money
repo 208 207 (the pair folds, 2 + 1 sessions -> 3) $0.00 both
pr 352 352 $5408.0144 both
branch 145 145 unchanged
commit 353 353 unchanged
file 500 500 unchanged

Attributed money is unchanged to the cent, because a repository's name was never what the cost split was keyed on.

3 tests added (146 in the file). 2 are red against the released 0.12.924 wheel, checked by running them against the wheel's own module:

  • test_two_sessions_spelling_one_repository_differently_are_one_row (3 rows, not 1)
  • test_a_pull_request_finds_its_title_despite_a_different_case (title '')
  • test_a_branch_keeps_its_own_case passes both ways on purpose: that is the half a fold can quietly break.

285 passed / 1 skipped over the delivery, local-store, store-invariant, query-contract, local-query and snapshot-slice suites.

Extraction semantics are untouched, so RULES_VERSION stays at 9 and no session needs refiling.

Limits

The spelling on display is the most-sighted one rather than the one GitHub would return, because nothing here asks GitHub. Where two spellings are sighted by the same number of sessions the lexicographically smaller one renders, which is deterministic but not necessarily the one its owner uses. The fold applies to the read, so stored rows keep the spelling each session saw, which is what keeps the evidence column matching the session it came from.

Product record: https://factory.8090.ai/project/b415065f-ab2f-4f53-8864-0c009fd098cb/requirements/eedac1ad-764f-4f9c-bb00-f51c8d47618d

🤖 Generated with Claude Code

vivekchand added a commit that referenced this pull request Oct 15, 2026
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@8090-software-factory

Copy link
Copy Markdown

⚠️ Drift Bot (ClawMetry): 1 potential drift finding(s)

1. Blueprint: Delivery attribution: which pull request the spend bought, split and never copied

File: clawmetry/local_store.py:9756

The blueprint's AttributionPersistence component documents "de-duplicated per repository name" but does not specify that deduplication is case-insensitive, matching GitHub's case-insensitive repository name handling. The code implements case-insensitive folding of repository names across sessions in the query_delivery method, which is not documented in the blueprint.

Comment thread clawmetry/local_store.py
@@ -9756,6 +9756,12 @@ def query_delivery(self, *, kind: str = "pr", runtime: str | None = None,
``runtime`` filters server-side on the session's ``agent_type``,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Drift Bot (ClawMetry) — Blueprint: Delivery attribution: which pull request the spend bought, split and never copied

The blueprint's AttributionPersistence component documents "de-duplicated per repository name" but does not specify that deduplication is case-insensitive, matching GitHub's case-insensitive repository name handling. The code implements case-insensitive folding of repository names across sessions in the query_delivery method, which is not documented in the blueprint.

@8090-software-factory

Copy link
Copy Markdown

✅ Drift Bot (ClawMetry): no drift detected

Drift Bot analyzed the changed files against this project's blueprints and requirements and found no drift.

vivekchand and others added 2 commits October 16, 2026 00:51
_fold_repo_case folds spellings within a session; the live tab still
listed ClawMetry/clawmetry-pro beside clawmetry/clawmetry-pro, because
the two spellings came from two different sessions and the read query
grouped on the exact text. GitHub treats an owner and a name
case-insensitively, so that is one repository shown as two.

query_delivery now folds on a case-insensitive key and shows the
spelling the node's own sessions used most, ties falling to the
lexicographically smaller spelling so the render is deterministic. The
key lowercases only the field holding a repository name (ref on a repo
row, repo elsewhere): a branch name and a file path are case-sensitive,
and folding those would claim work on a branch that does not exist. The
pull-request title/state/URL join is matched case-insensitively too, or
a session that typed another case than the local checkout gets a row
with no title.

Measured on the live node's 256 sessions by running both queries over
the same rows: repository rows 208 -> 207, the pair becoming one row
with 3 sessions; pull-request, branch, commit and file rows
byte-identical; attributed money unchanged at $5408.0144. Extraction is
untouched, so RULES_VERSION stays 9 and nothing needs refiling.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vivekchand
vivekchand force-pushed the fix/case-fold-across-sessions branch from bfc5a9d to 8b70ec3 Compare October 15, 2026 22:51
@8090-software-factory

Copy link
Copy Markdown

⚠️ Drift Bot (ClawMetry): 1 potential drift finding(s)

1. Blueprint: Delivery attribution: which pull request the spend bought, split and never copied

File: clawmetry/local_store.py:9756

The blueprint's AttributionPersistence component specifies "de-duplicated per repository name" but the code implements case-insensitive deduplication to match GitHub's case-insensitive repository name handling. The blueprint should document that deduplication is case-insensitive, matching the repository owner and name case-insensitivity in GitHub.

Comment thread clawmetry/local_store.py
@@ -9756,6 +9756,12 @@ def query_delivery(self, *, kind: str = "pr", runtime: str | None = None,
``runtime`` filters server-side on the session's ``agent_type``,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Drift Bot (ClawMetry) — Blueprint: Delivery attribution: which pull request the spend bought, split and never copied

The blueprint's AttributionPersistence component specifies "de-duplicated per repository name" but the code implements case-insensitive deduplication to match GitHub's case-insensitive repository name handling. The blueprint should document that deduplication is case-insensitive, matching the repository owner and name case-insensitivity in GitHub.

Drift Bot asked the AttributionPersistence component to say that the
roll-up de-duplicates case-insensitively. The blueprint now says so,
including that a branch name and a file path keep their case, which is
the half of the rule the code is careful about. drift-bot is an external
commit status with no re-request route, so an empty commit is how it
re-reads.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@8090-software-factory

Copy link
Copy Markdown

✅ Drift Bot (ClawMetry): no drift detected

Drift Bot analyzed the changed files against this project's blueprints and requirements and found no drift.

@vivekchand
vivekchand merged commit 5f552ea into main Oct 15, 2026
55 checks passed
@vivekchand

Copy link
Copy Markdown
Owner Author

Release coordination from the Asimov session: 0.12.925 is published and verified. Mobile correction #6452 is now rebasing onto your merged 5f552ea and will require fresh full CI before merge. I have a separate Asimov correction [RELEASE] changelog prepared and can carry #6450 too once #6452 is green; no release branch pushed yet. Please coordinate release timing here if you are already preparing a delivery release, so we avoid duplicate package releases and repeated CI rebases. Our cloud pin/policy/renderer will ship together after a verified corrected wheel; RP1 cloud rollout remains first.

vivekchand added a commit that referenced this pull request Oct 15, 2026
)

FLYWHEEL gains two Ruthless-verify bullets, both paid for this round:

- a waiter that swallows its errors cannot succeed: a urllib poller ate
  a CERTIFICATE_VERIFY_FAILED for an hour and reported a timeout while
  the wheel had been on PyPI for 50 minutes;
- a fold that runs per item leaves the cross-item case, which is #6450
  itself.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant