Introduces a structured meta-workflow for AI agents that ensures
safe incremental changes, persistent state across context resets,
and a clear decision trail for multi-agent coordination.
Key additions:
- Work scoping (goal, success criteria, non-goals)
- External notes files (CONTEXT.md, TODO.md, DECISIONS.md, TEST_LOG.md)
- Locate → Implement → Verify → Update notes work loop
- Context recovery procedure for new agent sessions
- Quality bar principles
- Cross-references from CLAUDE.md to the new section
https://claude.ai/code/session_0159eSJrqKvKpgDiwYZ2iGxK
The NIP-07 adapter uses type "nip07" but all permission pre-granting
checks only matched type "extension", causing NIP-07 browser extension
users to fail decryption for block/mute lists, subscriptions, DMs, and
watch history. Hashtag preferences appeared to work because their read
path had a separate fix, masking the systemic issue.
Changes:
- shouldRequestExtensionPermissions() now matches both "extension" and
"nip07" signer types (central fix affecting DM signals, comments,
publish helpers, and watch history fetch)
- buildDmDecryptContext() pre-grants extension permissions for NIP-07
- userBlocks.js: remove capabilities-gated decryptor check that failed
with lazy NIP-07 module injection (same fix already in subscriptions.js);
always resolve signer regardless of allowPermissionPrompt; increase
fast-path relay timeout from 6s to 10s
- watchHistoryService: recognize NIP-07 type and always resolve signer
- All publish paths (blocks, subscriptions, hashtags, relays, uploads,
sharing, moderation) now pre-grant permissions for NIP-07 signers
https://claude.ai/code/session_01PipsdaTQmw7wBTJ1LmJjkk
Section 14 in AGENTS.md covers the full test harness API, mock relay
HTTP endpoints, Playwright fixture usage, and data-testid selector
reference. CLAUDE.md testing section updated with quick-start example.
https://claude.ai/code/session_01XnrszNmdqrAGd8g8qhAn4G
Three CI-enforced lint scripts to prevent common codebase growth problems:
- lint:file-size — enforces 1000-line max for new JS files, grandfathers
existing oversized files and prevents them from growing further
- lint:innerhtml — tracks innerHTML usage per-file with a baseline count,
blocks new innerHTML additions (guides agents toward safe DOM APIs)
- Relay constants consolidated from duplicate definitions in client.js and
ConnectionManager.js into shared js/nostr/relayConstants.js
- AGENTS.md Section 13: documents all three rules with examples
- docs/scheduled-agent-prompts.md: copy-paste prompts for recurring
file decomposition, innerHTML migration, and constant dedup tasks
https://claude.ai/code/session_01LzErpH9FNw4hAPsjoYb1v9
- AGENTS.md Section 12: subsystem boundary table, PR discipline rules,
in-flight work reservation section, and signaling conventions for
parallel AI agent workflows (Claude Code, Codex, Jules)
- CLAUDE.md: document the unstable→beta→main promotion pipeline,
add multi-agent workflow section with coordination rules for both
agents and human maintainers, fix Node.js version reference (20→22)
https://claude.ai/code/session_01LzErpH9FNw4hAPsjoYb1v9
- Add docs/capacitor-native-app-plan.md to hex-lint ignore list (code examples
naturally reference hex colors in Capacitor config)
- Replace direct console.error/console.log calls with logger utility in
historyView.js, profileCache.js, EventDetailsModal.js, ZapInterface.js,
and notificationController.js
- Update CI workflow from Node 20 to Node 22 to match package.json engines
https://claude.ai/code/session_01LzErpH9FNw4hAPsjoYb1v9
Comprehensive plan for wrapping bitvid as native apps using Capacitor
for Android, iOS, and desktop (Windows/macOS/Linux via Electron).
Covers architecture, phased implementation, CI/CD workflows, testing
strategy, platform risks, and app store submission guidance.
https://claude.ai/code/session_01RBdcFkjzXaawaLY68MMhxc
- Export resolveSignerCapabilities, hydrateExtensionSignerCapabilities,
and attachNipMethodAliases from SignerManager so they can be used by
the module-level setActiveSigner in client.js
- Fix ensureExtensionPermissions: pass the extension object to
requestEnablePermissions (was passing methods array as extension),
check response.ok instead of response.enabled
- Hydrate extensionPermissionCache from localStorage on construction
so fresh NostrClient instances recognize previously granted permissions
- Cache individual permission method names (not just composite keys)
so tests and code checking specific methods work correctly
- Guard extension.signEvent.bind() against missing signEvent
- Add extensionPermissionCache getter to NostrClient
- Add derivePrivateKeyFromSecret and registerPrivateKeySigner methods
to SignerManager and NostrClient (needed by nsec auth provider)
- Add installNip46Client method for NIP-46 test infrastructure
- Apply signer hydration in module-level setActiveSigner
- Proxy ensureExtensionPermissions through NostrClient in
loginWithExtension to support test monkey-patching
- Fix nostr-signer-race test to use Map.set() instead of Set.add()
Fixes 5 previously-failing CI tests:
- tests/nostr/signers.test.mjs
- tests/nostr/dm-direct-message-flow.test.mjs
- tests/nostr/dmSignalEvents.test.mjs
- tests/nostr-login-permissions.test.mjs
- tests/nostr-signer-race.test.mjs
https://claude.ai/code/session_01KuHrohbGvitA41A2fPD5p5
The NostrClient.fetchListIncrementally() method delegates to
this.relayBatchFetcher, but RelayBatchFetcher was never imported or
instantiated in client.js, causing "Cannot read properties of undefined
(reading 'fetchListIncrementally')" on every page load when admin lists
try to load.
https://claude.ai/code/session_01KuHrohbGvitA41A2fPD5p5
- Remove redundant double signer readiness gate evaluation that added 5-10s
to the login path (authSessionCoordinator)
- Reduce decryption timeouts (10s→6s per scheme, 3s→1.5s retry delay,
8s→5s NIP-07 no-prompt, 45s→25s overall ensureLoaded) for faster scheme
fallback during list sync
- Fix storage tab unlock: remove overly strict capabilities check that
blocked lazy-injecting NIP-07 extensions, and add auto-unlock attempt
when profile modal opens with an active signer
- Fix subscription/hashtag decryption: always resolve signer for background
refreshes (was previously gated behind allowPermissionPrompt), and always
check window.nostr as fallback decryptor regardless of prompt mode
- Fix capabilities check in subscription decryption: use method existence
instead of dynamic capabilities getter that returns false during lazy
extension injection
- Wrap Promise.any decryptor calls in Promise.resolve().then() to catch
synchronous throws from NIP-07 adapter methods when extension modules
haven't loaded yet
- Make watch-history feed registration race-safe with pre-check and
try/catch for concurrent registration attempts
- Improve NIP-07 extension detection with DOMContentLoaded listener for
faster detection of extensions that inject during page load
https://claude.ai/code/session_0174XTB5ADJ9cMBSucqL9Wer
The auth:logout event listener in applicationBootstrap.js fires
synchronously during authService.logout(), setting __handled = true and
starting handleAuthLogout as fire-and-forget. When requestLogout() then
checked detail.__handled, it saw true and returned early — before
applyLoggedOutUiState() could update the UI.
Two changes fix this:
1. Remove the __handled early-return guard from requestLogout() so it
always awaits handleAuthLogout.
2. Add a promise-based dedup guard to handleAuthLogout so the event
listener's fire-and-forget call and requestLogout's awaited call
share a single execution instead of running the logout cleanup twice.
https://claude.ai/code/session_01ArpiYU9bNdzme5xBHWnKuS
Four issues caused login to be extremely slow and lists to fail syncing:
1. Subscription list decryption excluded NIP-04 when NIP-44 was available,
preventing decryption of NIP-04-encrypted lists entirely. Removed the
allowNip04 gate so NIP-04 is always a fallback (matching userBlocks.js
and hashtagPreferencesService.js which already worked correctly).
2. When the NIP-07 extension permission pre-grant failed (common on page
refresh when the extension injects slowly), all list services were called
with allowPermissionPrompt:false and could never recover. Now list
services are allowed to prompt for permissions themselves when the
pre-grant fails, avoiding 6-10 second retry delays.
3. The profile modal storage tab was never refreshed on login or logout,
so it stayed locked even after authentication succeeded. Added
populateStoragePane() calls to handleAuthLogin and handleAuthLogout.
4. The watch-history feed engine.run() call was missing await, so rejected
promises from unregistered feeds escaped the try/catch and propagated
as unhandled errors instead of falling through to the service fallback.
https://claude.ai/code/session_013puYzbf1cC7a59HLcGQUdJ
The NIP-07 login flow was extremely slow because list decryption (blocks,
subscriptions, hashtag preferences) started before the signer was ready.
On page refresh, all three services failed with "permission-required" and
scheduled 3-second retry delays, adding 6-10 seconds to every login.
Key fixes:
- List loading now waits for the permission pre-grant to complete before
attempting decryption, ensuring the signer is available on first try
- Extension wait timeout increased from 3s to 5s for slower extensions
- Decrypt timeouts reduced from 20s to 10s (signer is guaranteed ready)
- Auto-login now forwards authType/providerId from saved profiles
- Storage tab unlock falls back to ensureActiveSignerForPubkey when the
global signer is not yet registered
- Watch history feed registration adds defensive re-registration and
try/catch fallback to prevent "Feed not registered" errors
- Decryption scheme cache TTL extended from 10 minutes to 2 hours
- ensureExtensionPermissions waits for extension injection when there
are outstanding permissions to request (was returning immediately)
https://claude.ai/code/session_01T4wwPu7m5qp1Nxx3H2v57j
Three performance bottlenecks were causing slow list loading after NIP-07 login:
1. Each list service (blocks, subscriptions, hashtag preferences) maintained
its own isolated decryptionSuccessCache. When blocks decrypted with nip44_v2,
subscriptions and hashtag preferences had no way to know — they each had to
independently probe all schemes through the NIP-07 extension queue, tripling
the number of extension calls during the critical login path.
2. List loading was gated on both relaysReadyPromise AND permissionPromise in
the coordinator. Since the NIP-07 extension already grants decrypt permissions
during loginWithExtension(), the redundant pre-grant was adding 2-5 seconds
of unnecessary latency before any list fetching could begin.
3. The DECRYPT_RETRY_DELAY_MS was 10 seconds across all services. When a decrypt
attempt failed transiently (extension still initializing), the retry waited
far too long before trying again.
Changes:
- Create shared decryptionSchemeCache module used by all three list services
- Add fast-path: when shared cache knows the correct scheme, try ONLY that
scheme before falling back to parallel Promise.any() probe
- Remove permissionPromise from the list-loading gate in authSessionCoordinator
- Reduce DECRYPT_RETRY_DELAY_MS from 10s to 3s in all three services
- Clear shared cache on logout
https://claude.ai/code/session_013QrGMnxtMTsGTPqaP35jMj
When hashtag preferences decryption fails with "permission-required" during
background loading (allowPermissionPrompt=false), the service silently returned
without setting loaded state, scheduling a retry, or emitting a change event.
This left the UI stuck showing empty hashtag lists. The subscriptions module
already handled this case correctly.
Also fix error scheme mapping in Promise.any() catch blocks for both hashtag
preferences and subscriptions — the index into the original order array was
wrong after filtering out deduplicated scheme families.
https://claude.ai/code/session_016xYxLQ1U2XVVY5rFuCRKfc
Login with NIP-07 extensions was extremely slow because list loading
(blocks, subscriptions, hashtag preferences) happened sequentially and
each list's decryption tried encryption schemes one at a time.
Changes:
- Load blocks, subscriptions, and hashtags in parallel instead of
sequentially (blocks no longer blocks other lists from starting)
- Use Promise.any() to try all decryption schemes simultaneously
instead of trying nip44_v2 → nip44 → nip04 one-by-one
- Deduplicate same-family schemes (nip44/nip44_v2) to avoid redundant
extension calls
- Increase NIP-07 request queue concurrency from 3 to 5 to support
parallel decrypt operations during login
- Raise no-prompt decrypt timeout from 3s/5s to 8s since extensions
that have already granted permission still need time to decrypt
- Increase feed sync timeout from 8s to 12s to give lists more time
to settle before rendering unfiltered content
https://claude.ai/code/session_01V6m1gTecsppPrBHQnUyPnF
- Remove undefined hashtagPreferencesPromise reference in handleAuthLogin
that caused the feed sync Promise.allSettled to silently fail, preventing
proper list/profile synchronization before grid refresh
- Add missing getModerationSettings and getActiveProfilePubkey dependency
injection to authSessionCoordinator, fixing crashes during logout
moderation reset and profile switch pubkey resolution
- Parallelize NIP-07 permission request with relay readiness instead of
gating list loading behind it; parallelize subscriptions and hashtag
preference loading (independent of each other, only blocks must sequence)
- Add native run() alias to feedEngine so callers don't rely solely on
the applicationBootstrap compatibility shim
- Remove redundant refreshVisibleModerationUi call from renderVideoList
that re-decorated every video immediately after decoration
- Avoid re-normalizing moderation settings on every getActiveModerationThresholds
call when settings are already an object
https://claude.ai/code/session_01FCosieUXjEn95KtUeyGemY
Move cohesive method groups out of the monolithic Application class
(js/app.js, ~10K lines) into five coordinator modules under js/app/:
- feedCoordinator: feed registration, runtime building, refresh,
video loading, URL-health helpers, and feed telemetry
- playbackCoordinator: URL-first + magnet fallback pipeline, probing,
torrent status intervals, and video timestamp resolution
- authSessionCoordinator: signer lifecycle, profile switching,
relay/block/wallet management, and cleanup
- modalCoordinator: modal open/close, view-count subscriptions,
watch-history metadata, and playback logging
- moderationCoordinator: moderation settings, video decoration,
override/block/hide action routing
Each coordinator factory receives injected dependencies instead of
importing globals. Application retains thin delegator methods so all
public call-sites remain unchanged. Coordinator initialisation is
idempotent (via _initCoordinators) and supports both constructor-based
and Object.create()-based instantiation used by test harnesses.
All existing unit tests, lint checks, and syntax validation pass.
https://claude.ai/code/session_01C3PKJTCdavsGbk5jjC85EB
The user's block/mute list (NIP-51) was not being checked when
processing direct messages, allowing blocked users' messages to appear
in conversations, trigger notifications, and increment unseen counts.
This adds block filtering at four layers:
- nostrService.applyDirectMessage: drops incoming DMs from blocked users
before they enter the in-memory message cache
- nostrService.persistDirectMessageRecord: prevents blocked DM senders
from being persisted to IndexedDB or triggering notifications
- profileModalController.buildDmConversationData: filters blocked users
from the conversation list UI (catches pre-block persisted messages)
- applicationBootstrap: excludes blocked-user conversations from the
unread DM badge count
The block checker is injected via setDmBlockChecker() to keep
nostrService decoupled from userBlocks, wired up in app.js init().
https://claude.ai/code/session_01EAVLtAM14GN99Eik8otLg2
The subscription list was failing to load correctly during login because:
1. Permission-required decryption errors silently returned without setting
loaded=true or scheduling a retry, leaving subscriptions permanently
stuck in a loading state.
2. Generic decryption failures (all schemes exhausted) had no retry path,
unlike timeout errors which already scheduled retries.
3. The ensureLoaded timeout (30s) was too tight for the worst-case
pipeline of relay fetch (12s) + decryption (20s) = 32s, causing
premature timeouts that left loaded=false.
4. The loadingPromise was cleared in a finally block even when the loader
was still in-flight after a timeout, allowing duplicate concurrent
loads that could race against each other.
Changes:
- Handle permission-required errors like timeout errors: set loaded=true,
schedule a retry (with allowPermissionPrompt: true), emit change event
- Schedule decrypt retries for all decrypt failure paths
- Increase ensureLoaded timeout from 30s to 45s
- Mark loaded=true on ensureLoaded timeout so the UI can proceed
- Clean up loadingPromise via loader.then() instead of finally block,
preventing duplicate loads when the first is still in-flight
https://claude.ai/code/session_015MhPiSWNa7dm4UaRuq5x4U
The login-to-feed pipeline had several issues causing lists (blocks,
subscriptions, hashtag preferences) to load too slowly or not reflect
in the feed:
1. All list loading was chained after profileStatePromise, adding
2.5-6s of unnecessary delay. Lists only need relay URLs, not profile
data. Now they chain off relaysReadyPromise instead, loading in
parallel with profile fetching.
2. The video grid refreshed immediately without waiting for any lists,
so the feed briefly displayed unfiltered content. Now the grid refresh
waits (up to 8s) for critical feed-filtering lists to settle.
3. Hashtag preferences weren't cached into the snapshot before the feed
rendered, so interests/disinterests were empty on first paint.
4. Subscription ensureLoaded had a 6s timeout that was too aggressive
for relay fetch (12s) + decryption (3-12s), causing silent failures.
Increased to 30s.
5. Subscription relay fetches for user and session actor ran sequentially.
Now they run in parallel via Promise.all.
https://claude.ai/code/session_01UKmTWb83AnQPRVQo3bKZqm