Skip to content

chore: remove dead petname code from Account model - #42

Merged
DocNR merged 1 commit into
mainfrom
chore/remove-dead-petname
May 9, 2026
Merged

chore: remove dead petname code from Account model#42
DocNR merged 1 commit into
mainfrom
chore/remove-dead-petname

Conversation

@DocNR

@DocNRDocNR commented May 9, 2026

Copy link
Copy Markdown
Owner

Summary

Petname was deprecated in build 46 — Account.displayLabel was updated to ignore it, with a comment noting "petname field on Account remains for on-disk backward compat but UI ignores it as of build 46." Two builds later, petname has zero production usage:

  • displayLabel resolves to kind:0 displayName → kind:0 name → 8-char pubkey prefix (never consults petname)
  • All production callers of addAccount / generateAccount pass petname: nil
  • renamePetname has zero production callers (only tests exercised it)
  • The @petname mentions in 3 view doc comments were stale — describing aspirational behavior that never shipped past build 46

The only live use was MultiAccountDiagnosticsView (dev-menu-gated) — its "petname" row + timestamp-petname Generate button. Both removed.

Numbers

10 files changed, −108 net LOC (38 insertions, 146 deletions).

What was removed

Production code:

  • petname: String? field from Account struct + init
  • petname: parameter from addAccount + generateAccount + 9 call sites
  • renamePetname(for:to:) method + doc (~14 LOC)
  • sanitizePetname(_:) private helper (~7 LOC)
  • Account() constructor petname args in AppState+ProfileFetcher.swift
  • petname row + timestamp-petname Generate button in MultiAccountDiagnosticsView
  • Stale @petname doc comments in SlimIdentityBar, SigningAsHeader, ConnectAccountContextBar
  • Stale renamePetname doc references in 3 source files + 2 test files

Tests:

  • 3 renamePetname tests deleted (the methods they tested are gone)
  • 2 displayLabel-ignores-petname tests deleted (the assertion is meaningless without petname to ignore)
  • 7 incidental tests edited to drop petname: argument; testAddAccount_duplicateNsec_switchesToExisting now uses addedAt as the disambiguator (was using petname before)
  • testAccount_equatable_isContentBased now uses addedAt as the differentiator

Backward compatibility

Existing on-disk Account JSON has "petname":null (or rare non-null values from the dev menu). Swift's JSONDecoder ignores unknown keys by default, so build 69+ decoding existing rows is fine — the petname key is silently dropped on next encode. No migration needed.

Test plan

  • Pre-baseline on main (d535054): 236 passed / 0 failed
  • Post-removal on this branch: 231 passed / 0 failed (236 − 5) ✅
  • ** TEST SUCCEEDED ** in both runs
  • grep -rnE "petname|sanitizePetname|renamePetname" --include="*.swift" → zero matches

Why now

Lands as a focused dead-code removal PR before Stage 4a (AccountManager extraction). Keeps the structural refactor PR clean — Stage 4a can be a pure file move without bundling product decisions.

🤖 Generated with Claude Code

Petname was deprecated in build 46 (Account.displayLabel was updated
to ignore it; doc comment at the time said "petname field on Account
remains for on-disk backward compat but UI ignores it"). Two builds
later, petname has zero production usage:
- displayLabel resolves to kind:0 displayName -> kind:0 name -> 8-char
pubkey prefix (never consults petname)
- All production callers of addAccount/generateAccount pass petname=nil
- renamePetname has zero production callers (only tests exercised it)
- The "@petname" mentions in 3 view doc comments were stale —
describing aspirational behavior that never shipped past build 46
Only live use was MultiAccountDiagnosticsView (dev-menu-gated) — its
"petname" row + timestamp-petname Generate button. Both removed.
Removed:
- petname: String? field from Account struct
- petname: parameter from Account init + 9 call sites
- petname: parameter from addAccount + generateAccount
- renamePetname(for:to:) method (~14 LOC + doc)
- sanitizePetname(_:) private helper (~7 LOC)
- petname display + petname-on-generate in MultiAccountDiagnosticsView
- 3 renamePetname* tests (testRenamePetname_persistsAndUpdatesCurrent,
testRenamePetname_sanitizesInput_audit_A3,
testRenamePetname_emptyAfterSanitization_setsNilNotEmptyString)
- 2 displayLabel-ignores-petname tests (
testAccount_displayLabel_prefersDisplayNameOverPetname,
testAccount_displayLabel_ignoresPetname_usesPubkeyPrefixWhenNoProfile)
- Stale "@petname" doc comments in SlimIdentityBar, SigningAsHeader,
ConnectAccountContextBar
- Stale "renamePetname" doc references in AppState class doc comment +
SharedModels.swift Account struct doc + AppStateMultiAccountTests
class doc + AccountModelTests internal comments
Updated 7 incidental tests to drop petname argument + use addedAt as
the disambiguator in testAccount_equatable_isContentBased and
testAddAccount_duplicateNsec_switchesToExisting (where petname had been
used to verify "didn't replace existing").
Backward compat: existing on-disk Account JSON includes "petname":null
(or rare non-null from dev menu). Swift's JSONDecoder ignores unknown
keys by default, so build-69+ decoding existing rows is fine —
the petname key is silently dropped on next encode.
Verification:
- xcodebuild test -skip-testing:ClaveUITests on iPhone 17 / iOS 26.4:
- Pre-baseline (main @ d535054): 236 passed / 0 failed
- Post-deletion (this branch): 231 passed / 0 failed (236 - 5)
- ** TEST SUCCEEDED ** in both runs
Net change: -108 LOC across 10 files (38 insertions, 146 deletions).
Lands as a focused dead-code removal PR before Stage 4a (AccountManager
extraction) so the structural refactor stays clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@DocNR
DocNR merged commit f51bc18 into mainMay 9, 2026
@DocNR
DocNR deleted the chore/remove-dead-petname branch May 9, 2026 02:48
DocNR added a commit that referenced this pull request May 9, 2026
)
Stage 4a — penultimate sub-extraction in the AppState god-object split.
Moves all account lifecycle + bunker-URI + persistence logic out of
AppState.swift into a dedicated extension file. Cleanup follow-up to
the petname-removal PR (#42) that simplified the surface first.
Moves out of Clave/AppState.swift (16 methods + 1 MARK header):
- var bunkerURI (computed) + bunkerURI(for:) (per-account)
- loadAccounts (was private, now internal — called by loadState)
- recoverAccountsFromKeychainIfNeeded (Stage 2 deferred — moves now)
- cleanupOrphanLegacyKeychainEntry (Stage 2 deferred — moves now)
- persistAccountsList (private, stays private — only inter-extension)
- persistAccounts
- persistCurrentAccountPubkey (private, stays private)
- switchToAccount(pubkey:)
- addAccount(nsec:)
- generateAccount()
- deleteAccount(pubkey:)
- rotateBunkerSecret()
- importKey / generateKey / deleteKey (legacy wrappers preserved)
- refreshBunkerSecret() (no-op kept for source-compat)
- "// MARK: - Multi-account methods (Task 5)" header
Into the new file:
- Clave/AppState+AccountManager.swift (312 LOC) — extension AppState
What stays in AppState.swift:
- @observable stored properties (accounts, currentAccount,
bunkerSecretsTick, etc.) — Swift forbids stored properties in
extensions. Pointer comment added at the multi-account state block.
- init() with NotificationCenter observers
- loadState() coordinator (calls into the extension via cross-file
extension dispatch — works because all are extension AppState on
the same final class in the same module)
One AppState.swift access change required:
- bunkerSecretsTick was `private(set) var` (file-scope setter).
rotateBunkerSecret in the extension needs to mutate it across
files, so dropped `private(set)` to internal-set. Same constraint
pattern as Stage 3b's persistAccounts widen.
Cross-extension calls all resolve correctly:
- addAccount calls registerWithProxy (Stage 3c) + fetchProfileIfNeeded
(Stage 3b) — works
- deleteAccount calls unregisterWithProxy + unpairClientWithProxy
(Stage 3c) + cachedImageURL (Stage 3b) — works
- switchToAccount calls loadCachedProfileImage + fetchProfileIfNeeded
(Stage 3b) — works
Zero behavior change:
- Function bodies preserved byte-for-byte
- All `private` modifiers preserved on persistAccountsList +
persistCurrentAccountPubkey (only called within extension file —
same-file private access)
- Modifiers loosened only where strictly required by cross-file
calls (loadAccounts, recoverAccountsFromKeychainIfNeeded,
cleanupOrphanLegacyKeychainEntry: private -> internal because
loadState in AppState.swift calls them)
Test impact: zero changes.
- AppStateMultiAccountTests calls appState.<method> via extension
dispatch — works unchanged.
External callers unchanged:
- ClaveApp + 7 view files call appState.<method> — extension dispatch.
No pbxproj edits — Clave/ directory is auto-synced.
Verification:
- xcodebuild test -skip-testing:ClaveUITests on iPhone 17 / iOS 26.4:
- Pre-baseline (main @ f51bc18): 231 passed / 0 failed
- Post-extraction (this branch): 231 passed / 0 failed
- ** TEST SUCCEEDED ** in both runs
AppState.swift: 893 -> 611 LOC (-282, -31.6%).
Combined Stages 1+2+3+petname+4a: 2,338 -> 611 LOC (-73.9% from sprint
start).
Stage 4b (PendingApprovalCoordinator, ~290 LOC) is the final
extraction. After it lands, AppState should reach the BACKLOG
sketched ~300 LOC slim target.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DocNR added a commit that referenced this pull request May 9, 2026
Internal-only build bundling three merges since build 69 was tagged:
- Sprint 5a (#41): bunker pair-cap bypass fix
- Petname removal (#42): -108 LOC dead code
- Stage 4a (#43): AccountManager extension, -282 AppState LOC
Bumps CURRENT_PROJECT_VERSION 69 -> 70 across 4 targets x 2 configs.
Cumulative AppState refactor reduction: 2,338 -> 611 LOC (-73.9%).
Stage 4b (PendingApprovalCoordinator) is the final refactor PR.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for freeto 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

@DocNR