Skip to content

fix(storage): stop a credential deletion from bumping a retired connection - #3415

Merged
Astro-Han merged 1 commit into
apache:mainfrom
Joob1n:fix/retired-connection-credential-delete
Aug 21, 2026
Merged

fix(storage): stop a credential deletion from bumping a retired connection#3415
Astro-Han merged 1 commit into
apache:mainfrom
Joob1n:fix/retired-connection-credential-delete

Conversation

@Joob1n

@Joob1n Joob1n commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Summary

A retained retired connection is a tombstone: readable, queryable, deletable, and byte-stable until it is deleted. #3183 established that against direct writes and against the global verification sweep, and left one path open.

Deleting the credential is the write a tombstone must still accept — it is how a user removes the token that retirement deliberately kept. The deletion itself was never the problem. On its way out it invalidated the connection's verification through clearConnectionLastTest, which rewrote the row and advanced its revision.

Reproduced: revision 1 becomes 2 for removing a credential.

That bump is not cosmetic. A revision is what a concurrent deletion of the row itself compares against, so removing the credential could make deleting the connection fail as stale — and those two are the same user action, one after the other.

The fix

clearAllConnectionLastTests already skips retired rows. This is the same invariant reached one connection at a time rather than all of them at once, so it gets the same predicate rather than a second rule:

if (previous.lastTest === undefined || isRetiredProvider(previous.providerType)) return false;

One line of behaviour. The asymmetry it removes is the whole bug: the global sweep was taught the rule and the single-connection path was not.

Verification

The test asserts the tombstone is untouched — revision and lastTest both — and carries a live connection as a control: the same deletion against it must still clear the verification and bump the revision. Without that half, the test would pass just as well if the guard stopped every invalidation instead of only the retired one.

Verified in both directions rather than only the passing one:

  • with the guard reverted, the test fails on exactly the revision it was written for (actual: 2, expected: 1)
  • with the guard in place, it passes, and so does the control

Gates: build, lint, format:check, typecheck, check:stale, check:release (32), check:asf-source (9), windows:inventory, the CI planner tests (53, including the pre-npm ci case), knip on both workspaces, and the Astryx inventory and theme checks. Suites: storage 849 (848 + this one), core 576, runtime-host 1045, desktop 1023 — all green.

No wire contract changes, so no RUNTIME_HOST_COMPATIBILITY_EPOCH bump.

Review focus

Whether the tombstone invariant is the right frame for this path at all. The alternative reading is that deleting a credential should invalidate verification unconditionally, and that a retired row's revision moving is acceptable because nothing will test it again. I do not think so — deletability is part of what the invariant promises, and a revision bump is exactly what breaks it — but that is the judgement worth a second opinion.

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: Claude Code — reproduced the defect, wrote the test and its control, made the one-line fix, and ran the verification above. Reviewed and submitted by the contributor of record.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

A retained retired connection is a tombstone: readable, queryable, deletable,
and byte-stable until it is deleted. apache#3183 established that against direct
writes and against the global verification sweep, and left one path open.

Deleting the credential is the write a tombstone must still accept — it is how
a user removes the token retirement deliberately kept. The deletion itself was
never the problem. On its way out it invalidated the connection's verification
through `clearConnectionLastTest`, which rewrote the row and advanced its
revision. Reproduced: revision 1 becomes 2 for removing a credential.

That bump is not cosmetic. A revision is what a concurrent deletion of the row
itself compares against, so removing the credential could make deleting the
connection fail as stale — and the two are the same user action, one after the
other.

`clearAllConnectionLastTests` already skips retired rows. This is the same
invariant reached one connection at a time rather than all of them at once, so
it gets the same predicate rather than a second rule.

The test asserts the tombstone is untouched — revision and `lastTest` both —
and carries a live connection as a control: the same deletion against it must
still clear the verification and bump the revision. Without that half the test
would pass just as well if the guard stopped every invalidation instead of
only the retired one. Verified in both directions: with the guard reverted the
test fails on the revision it was written for, and passes with it in place.

No wire contract changes, so no compatibility epoch bump.

Claude-Session: https://claude.ai/code/session_014ajaRxC4jydavY9nYUFj5J

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for fixing this at the catalog-document boundary. I reviewed exact head f9f0702328a7f34f6d7f075a7c1045be2e0d1605. Treating a retained retired connection as a byte-stable tombstone is consistent with the existing bulk invalidation path, and the one-line guard avoids an unnecessary revision change without weakening credential CAS. The live-connection control in the regression also confirms that ordinary verification invalidation still works. I found no actionable issues.

Hosted CI has not run successfully for this head yet, so merge should still wait for the required checks.

AI-assisted review disclosure: Codex performed the exact-head analysis, and an independent reviewer agent conducted an adversarial storage, revision, and concurrency pass. I verified the source paths, live GitHub state, and final review decision.

@Astro-Han
Astro-Han merged commit 3b9a2e2 into apache:main Aug 21, 2026
1 check passed
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.

2 participants