<feed xmlns='http://www.w3.org/2005/Atom'>
<title>npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/src/sync/self_subscriber.rs, branch master</title>
<subtitle>Unnamed repository; edit this file 'description' to name the repository.
</subtitle>
<id>https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/atom?h=master</id>
<link rel='self' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/'/>
<updated>2026-02-23T15:20:59+00:00</updated>
<entry>
<title>Merge master into 3ca0-announcements-purgatory</title>
<updated>2026-02-23T15:20:59+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2026-02-23T15:20:59+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=113928aa84894ea8f65c247d9987527e792b32a9'/>
<id>urn:sha1:113928aa84894ea8f65c247d9987527e792b32a9</id>
<content type='text'>
</content>
</entry>
<entry>
<title>refactor: replace inline purgatory sync registration with timer-only approach</title>
<updated>2026-02-23T12:48:26+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2026-02-23T12:48:26+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=f62ef12fb84e2210f9a0a67a5e1e574a8ee66c16'/>
<id>urn:sha1:f62ef12fb84e2210f9a0a67a5e1e574a8ee66c16</id>
<content type='text'>
Remove the redundant inline kind-30617 registration block from the sync
event loop and the three is_generic/recompute_new_sync_filters_for_relay
calls from confirm_batch error paths. The purgatory announcement sync
timer (run_purgatory_announcement_sync) is now the sole registration path.

Consolidate NGIT_SYNC_BATCH_WINDOW_MS and NGIT_PURGATORY_SYNC_INTERVAL_MS
into a single NGIT_TEST=1 flag that sets both timers to 200ms, replacing
two ad-hoc env vars with one reusable test-mode flag.
</content>
</entry>
<entry>
<title>fix: simplify purgatory sync - fix SelfSubscriber sync_level upgrade and negentropy fallback</title>
<updated>2026-02-18T19:41:29+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2026-02-18T19:41:29+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=e22021f0b248ebcf3bd09210d59b2cdb4701032f'/>
<id>urn:sha1:e22021f0b248ebcf3bd09210d59b2cdb4701032f</id>
<content type='text'>
Three targeted fixes for purgatory announcement sync:

1. SelfSubscriber sync_level upgrade: After or_insert_with in process_batch,
   always set entry.sync_level = SyncLevel::Full so that when a promoted
   announcement is broadcast via notify_event and SelfSubscriber receives it,
   an existing StateOnly entry gets upgraded to Full and PR event subscriptions
   are triggered immediately (not delayed up to 24h).

2. Negentropy fallback filter split: In handle_eose, when falling back from
   negentropy to REQ+EOSE, split batch_repos by SyncLevel and call
   build_sync_level_aware_filters instead of build_layer2_and_layer3_filters.
   Prevents StateOnly (purgatory) repos from getting Layer 2 #a/#A/#q filters
   prematurely, which caused nostr-sdk client deduplication to permanently
   drop PR events after orphan rejection.

3. Recompute sync filters after announcement batch EOSE: Add
   recompute_new_sync_filters_for_relay calls at all three batch-completion
   paths in handle_eose for generic filter (announcement) batches. This
   triggers state-only subscriptions for any purgatory repos registered during
   that batch, fixing the 24h delay before state event sync starts.

4. User-submitted purgatory announcements: Add repo_sync_index field to
   PolicyContext with setter/getter, wire in main.rs after SyncManager
   creation, and register in AcceptPurgatory handler so user-submitted
   announcements get StateOnly sync started immediately.

5. Update archive tests: test_archive_without_state_events_does_not_sync_git
   updated to reflect that StateOnly subscription now proactively fetches
   state events from source relays. test_archive_read_only_creates_bare_repo
   un-ignored as it now works end-to-end.
</content>
</entry>
<entry>
<title>feat: add SyncLevel to sync system for purgatory announcement state-only sync</title>
<updated>2026-02-13T19:59:36+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2026-02-13T19:59:36+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=e922e14e3ec4b898c111b2100cd63dddbe2fcdb1'/>
<id>urn:sha1:e922e14e3ec4b898c111b2100cd63dddbe2fcdb1</id>
<content type='text'>
Purgatory announcements need state events (kind 30618) synced from
external relays, but not full L2/L3 events (patches, issues, PRs)
which would be rejected anyway. This implements the SyncLevel concept
from the design doc (decision #6):

- Add SyncLevel enum (Full vs StateOnly) to RepoSyncNeeds
- When announcement enters purgatory during sync, register in
  RepoSyncIndex with SyncLevel::StateOnly
- Add build_sync_level_aware_filters() that partitions repos by level:
  StateOnly repos only get state event filters (kind 30618)
- Update derive_relay_targets to track state_only_repos separately
- Update compute_actions to handle both repo sets
- SelfSubscriber always uses SyncLevel::Full (promoted repos)
</content>
</entry>
<entry>
<title>fix(sync): Remove .since() filter from database queries in load_existing_events()</title>
<updated>2026-01-27T20:38:18+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2026-01-27T09:16:41+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=6e5b7eb84b3ca8a902ac4bcbab9c2a9f9ecdee51'/>
<id>urn:sha1:6e5b7eb84b3ca8a902ac4bcbab9c2a9f9ecdee51</id>
<content type='text'>
Root cause: `last_connected` was set to Timestamp::now() BEFORE
load_existing_events() was called (line 425), causing the database
query to filter out all existing events with .since(current_time).

The query became: SELECT * FROM events WHERE created_at &gt;= &lt;now&gt;
Result: 0 events returned (nothing has created_at in the future)

Solution: Remove .since() filter from database queries entirely.
The `last_connected` field is now only used for WebSocket subscription
filters to avoid re-fetching events from remote relays on reconnect.

Rationale for this approach over reordering operations:
- Database queries are fast (indexed by kind and created_at)
- Loading all events on startup ensures consistency
- Eliminates subtle ordering dependency that could break in refactoring
- Cleaner mental model: database = full load, WebSocket = incremental

This fixes the issue where ~190 state events weren't being fetched
after deploying the database query fix (commit 4162c90).

Evidence: Production logs showed "Loaded announcements from database
count=0" when there should have been hundreds of announcements.
</content>
</entry>
<entry>
<title>fix: load existing events from database on startup with two-pass queries</title>
<updated>2026-01-27T20:38:16+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2026-01-26T17:20:11+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=905ebd838a9ff8cc777cf3b3b6306066e8c177fc'/>
<id>urn:sha1:905ebd838a9ff8cc777cf3b3b6306066e8c177fc</id>
<content type='text'>
Previously, SelfSubscriber only saw events returned by the WebSocket
subscription to the local relay, which has limits on the number of
events returned. This caused repos with announcements in the database
to never get Layer 2/3 filters created, resulting in missing state events.

Now, on startup, we query the database directly with two separate queries:
1. Query announcements (30617) to populate repo_sync_index
2. Query root events (1617/1618/1621) to create Layer 3 filters

Both queries use .since(last_connected) if available for incremental
loading on reconnect.

Filters are created inline and made mutable to support the .since()
clause, rather than using a shared create_event_filter() method.

Fixes the issue where state events were missing for repos like cashbird
and creative-space that had announcements in the database but weren't
returned by the WebSocket subscription.
</content>
</entry>
<entry>
<title>fix: Enable sync relay discovery in archive_all mode</title>
<updated>2026-01-13T12:49:30+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2026-01-13T12:45:55+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=7dba18eb9ae64d429fef1a1f5437981efefb86b6'/>
<id>urn:sha1:7dba18eb9ae64d429fef1a1f5437981efefb86b6</id>
<content type='text'>
The bug: SelfSubscriber filtered announcements with lists_our_relay() check,
preventing archive_all mode from discovering relays in announcements that
don't list our relay domain.

The insight: SelfSubscriber only receives events that ALREADY passed
write policy validation (archive_all, archive_whitelist, blacklist, etc.)
via admit_event() before being saved to the database. The event flow:

  External relay → process_event_static() → write_policy.admit_event()
  → (validation happens here) → save to DB → notify_event()
  → SelfSubscriber receives via WebSocket

So the lists_our_relay() check was redundant double-validation that
broke archive_all mode by filtering events that had already been
accepted by the write policy.

The fix: Simply remove the lists_our_relay() filtering. Events reaching
SelfSubscriber are pre-validated and should all be processed for relay
discovery according to the configured archive policy.

Changes:
- Removed lists_our_relay() check from process_notification() (4 lines)
- Removed unused lists_our_relay() helper function (9 lines)
- Added comment explaining events are pre-validated (3 lines)
- Total: 13 lines removed, 3 lines added

Fixes #194d
</content>
</entry>
<entry>
<title>refactor: replace hardcoded Kind constants with rust-nostr variants</title>
<updated>2026-01-08T11:23:28+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2026-01-08T11:20:35+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=5d02ad6b893f9059044914c115d77cf9d8e589c3'/>
<id>urn:sha1:5d02ad6b893f9059044914c115d77cf9d8e589c3</id>
<content type='text'>
- Replace KIND_REPOSITORY_ANNOUNCEMENT with Kind::GitRepoAnnouncement
- Replace KIND_REPOSITORY_STATE with Kind::RepoState
- Replace KIND_PR with Kind::GitPullRequest
- Replace KIND_PR_UPDATE with Kind::GitPullRequestUpdate
- Replace KIND_USER_GRASP_LIST with Kind::GitUserGraspList
- Replace KIND_PATCH with Kind::GitPatch
- Replace KIND_ISSUE with Kind::GitIssue
- Replace KIND_COMMENT with Kind::Comment
- Replace all Kind::Custom(30617|30618|1617|1618|1619|1621|1111|10317) patterns
- Remove all hardcoded KIND_* constants from events.rs
- Update all match statements to use Kind enum directly
- Update all filter builders to use Kind variants
- Update all test helpers and assertions

Benefits:
- Type safety: compiler prevents wrong kind numbers
- Readability: Kind::GitRepoAnnouncement is self-documenting
- Maintainability: single source of truth (rust-nostr)
- IDE support: full autocompletion and refactoring
- Standards: aligns with rust-nostr best practices

Files modified: 21
Constants removed: 9
Patterns replaced: 100+
Tests passing: 222/222
</content>
</entry>
<entry>
<title>accept all UserGraspList for better discovery</title>
<updated>2025-12-22T20:21:26+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2025-12-22T17:22:25+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=5d3d96c7f0397a079384b7291ff21df76029b126'/>
<id>urn:sha1:5d3d96c7f0397a079384b7291ff21df76029b126</id>
<content type='text'>
</content>
</entry>
<entry>
<title>proactive sync prep - some helper functions written but not enabled</title>
<updated>2025-12-16T15:26:55+00:00</updated>
<author>
<name>DanConwayDev</name>
<email>DanConwayDev@protonmail.com</email>
</author>
<published>2025-12-16T15:26:55+00:00</published>
<link rel='alternate' type='text/html' href='https://upleb.uk/npub1tkq8unhsd5jqx6ueex5lcpsgknrpquxuk44ftpjlpm3ulaake7xs76txrw/ngit-grasp-mirror/commit/?id=7821b107190cc116a30a4c339f935bc16a1d5197'/>
<id>urn:sha1:7821b107190cc116a30a4c339f935bc16a1d5197</id>
<content type='text'>
</content>
</entry>
</feed>
