Skip to content

docs: add ADR for local-first reads - #702

Open
nogringo wants to merge 1 commit into
masterfrom
docs/adr-local-first-reads
Open

docs: add ADR for local-first reads#702
nogringo wants to merge 1 commit into
masterfrom
docs/adr-local-first-reads

Conversation

@nogringo

@nogringo nogringo commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Defines NdkDataResponse and NdkValue as the return format for reads that render from cache first and refine when relays answer, with the private relay list (NIP-37 kind 10013) as first use.

Summary by CodeRabbit

  • Documentation
    • Added an architecture decision record describing local-first read responses.
    • Documented how cached results are delivered before relay-confirmed updates.
    • Defined result metadata distinguishing cached values, relay values, loading states, and confirmed empty results.
    • Documented future-based final results and stream-based error reporting.
    • Included private relay-list reads as the initial application, including the distinction between an empty list and no list.

Defines NdkDataResponse<T> and NdkValue<T> as the return format for reads that render from cache first and refine when relays answer, with the private relay list (NIP-37 kind 10013) as first use.
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The ADR defines local-first reads that emit cached values, update from relays, expose the final relay value through a future, and preserve distinctions between unknown, unreadable, and confirmed absence. It applies the model to private relay lists.

Changes

Local-first read design

Layer / File(s) Summary
Read contract and private relay-list application
doc/library-development/ADRs/local-first-reads.md
The ADR defines NdkDataResponse<T>, NdkValue<T>, and DataOrigin. It specifies cache and relay emissions, stream errors, nullable-value semantics, and the getPrivateUserRelays API for NIP-37 private relay lists.

Estimated code review effort: 1 (Trivial) | ~5 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the added ADR for local-first reads, which is the main change in the pull request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/adr-local-first-reads

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (1)
doc/library-development/ADRs/local-first-reads.md (1)

31-71: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add executable tests for the local-first contract.

The existing packages/ndk/example/user_relay_list_test.dart does not cover the behavior defined here. Add tests for (null, cache), (null, relays), empty-list confirmation, relay-confirmed future, unreadable values, unreachable relays, and timeout handling.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@doc/library-development/ADRs/local-first-reads.md` around lines 31 - 71, Add
executable tests covering the NdkDataResponse local-first contract, including
`(null, cache)`, `(null, relays)`, empty-list confirmation, relay-confirmed
`future`, unreadable values, unreachable relays, and timeout behavior. Extend
the existing user relay list test coverage using the relevant read API and
assert stream ordering, origins, future completion, and stream errors according
to the documented outcomes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@doc/library-development/ADRs/local-first-reads.md`:
- Line 9: Update the “Updated on” date in local-first-reads.md to an unambiguous
ISO 8601 format (YYYY-MM-DD), preserving the intended calendar date.
- Around line 42-45: Update the local-first read contract around NdkValue<T> so
cache misses are representable for every declared T: either constrain
cache-miss-capable reads to nullable T or introduce an explicit miss state via a
state field or sealed value type. Apply and document this rule for all
local-first reads, including getPrivateUserRelays, rather than relying on a
non-nullable NdkValue<T>.value.
- Around line 38-40: Update the ADR section defining the Future<T> future
contract to specify how cache decryption and no-reach failures complete it,
whether those cache failures are terminal and prevent relay refinement, and
whether a failed relay is tolerated when another relay returns a value; keep the
stream behavior consistent with these documented rules.
- Line 77: Update the getPrivateUserRelays signature so its optional timeout
parameter is null-safe: make it Duration? and document the default timeout
behavior used when it is omitted.
- Around line 33-39: Update the future contract and its implementation to select
the NIP-01 replacement-order winner by createdAt rather than using the last
arriving relay response. Align ordering with getDmRelays() and
loadMissingRelayListsFromNip65OrNip02(), complete future from that selected
relay-confirmed value, and add coverage for reverse-order relay arrivals.

---

Nitpick comments:
In `@doc/library-development/ADRs/local-first-reads.md`:
- Around line 31-71: Add executable tests covering the NdkDataResponse
local-first contract, including `(null, cache)`, `(null, relays)`, empty-list
confirmation, relay-confirmed `future`, unreadable values, unreachable relays,
and timeout behavior. Extend the existing user relay list test coverage using
the relevant read API and assert stream ordering, origins, future completion,
and stream errors according to the documented outcomes.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1487de4f-7aef-4ce5-9ed8-c57adba64760

📥 Commits

Reviewing files that changed from the base of the PR and between 4e28d2e and d0fe6dc.

📒 Files selected for processing (1)
  • doc/library-development/ADRs/local-first-reads.md


proposed

Updated on 05-08-2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use an unambiguous update date.

At Line 9, 05-08-2026 can mean May 8, 2026 or August 5, 2026. Use ISO 8601 format, such as 2026-05-08 or 2026-08-05, based on the intended date.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@doc/library-development/ADRs/local-first-reads.md` at line 9, Update the
“Updated on” date in local-first-reads.md to an unambiguous ISO 8601 format
(YYYY-MM-DD), preserving the intended calendar date.

Comment on lines +33 to +39
/// Emits a `cache` value first, then every newer `relays` value as it
/// arrives, even when it holds the same value as the cache.
/// Closes after EOSE or timeout.
final Stream<NdkValue<T>> stream;

/// The relay-confirmed value, so the last emitted one.
Future<T> get future;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== repo files matching ADR/main candidates =="
git ls-files | rg '(^|/)(local-first-reads|user_relay_lists|.*ndk.*\.dart)$' | sed -n '1,200p'

echo
echo "== ADR target =="
if [ -f doc/library-development/ADRs/local-first-reads.md ]; then
  nl -ba doc/library-development/ADRs/local-first-reads.md | sed -n '1,120p'
else
  echo "ADR file not found"
fi

echo
echo "== user_relay_lists relevant lines =="
if [ -f packages/ndk/lib/domain_layer/usecases/user_relay_lists/user_relay_lists.dart ]; then
  wc -l packages/ndk/lib/domain_layer/usecases/user_relay_lists/user_relay_lists.dart
  nl -ba packages/ndk/lib/domain_layer/usecases/user_relay_lists/user_relay_lists.dart | sed -n '1,220p'
else
  echo "user_relay_lists.dart not found"
fi

echo
echo "== Search for NdkValue future/stream definitions and replace/by version =="
rg -n "NdkValue|final Stream<NdkValue|Future<T> get future|createdAt|kind: NdkKind\.kind10013|kind10013|replace|latest|relays|relays values|newer|last emitted" -S .

Repository: relaystr/ndk

Length of output: 12652


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== ADR target with cat -A line numbers =="
if [ -f doc/library-development/ADRs/local-first-reads.md ]; then
  cat -n doc/library-development/ADRs/local-first-reads.md | sed -n '1,120p'
else
  echo "doc/library-development/ADRs/local-first-reads.md not found"
fi

echo
echo "== user_relay_lists.dart relevant lines =="
if [ -f packages/ndk/lib/domain_layer/usecases/user_relay_lists/user_relay_lists.dart ]; then
  wc -l packages/ndk/lib/domain_layer/usecases/user_relay_lists/user_relay_lists.dart
  cat -n packages/ndk/lib/domain_layer/usecases/user_relay_lists/user_relay_lists.dart | sed -n '1,240p'
else
  echo "packages/ndk/lib/domain_layer/usecases/user_relay_lists/user_relay_lists.dart not found"
fi

echo
echo "== Search relevant identifiers =="
rg -n "NdkValue|final Stream<NdkValue|Future<T> get future|createdAt|kind10013|kind1221|kind30078|latest|latest_by|sortedBy|sortBy|last emitted|newer|last relay|last event|Replace|replace" -S . | sed -n '1,240p'

Repository: relaystr/ndk

Length of output: 39897


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Locate replacement/selection helpers =="
rg -n "replaceable|latestStateOnly|dedupe|deduplicate|sort\\(.*createdAt|shouldReplace|isReplaceableKind|isParameterizedReplaceable" packages/ndk/lib packages/ndk/test -S | sed -n '1,240p'

echo
echo "== Candidate implementation files likely containing read response/event selection =="
git ls-files packages/ndk/lib | rg 'requests|cache_read|read|local|nip01|event_utils|relay|cache_manager|user_relay_list' | sed -n '1,200p'

echo
echo "== Read implementation slices around selected candidates =="
for f in $(git ls-files packages/ndk/lib | rg 'requests|cache_read|read|local|event_utils|nip01_utils|user_relay_lists' | sed -n '1,50p'); do
  echo "--- $f ($(wc -l < "$f") lines) ---"
  cat -n "$f" | sed -n '1,260p'
done

Repository: relaystr/ndk

Length of output: 50368


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Replaceable winner selection implementations =="
for f in \
  packages/ndk/lib/shared/nips/nip01/nip01_event.dart \
  packages/ndk/lib/data_layer/entities/event_cache_records.dart \
  packages/ndk/lib/data_layer/cache_manager/ndk_extensions.dart \
  packages/ndk/lib/data_layer/cache_manager/mem_cache_manager.dart \
  packages/ndk/lib/data_layer/cache_manager/sembast_cache_manager.dart \
  packages/ndk/lib/domain_layer/usecases/users/users.dart \
  packages/ndk/lib/domain_layer/usecases/metadata/metadata.dart \
  packages/ndk/lib/domain_layer/usecases/lists/lists.dart
do
  if [ -f "$f" ]; then
    echo "--- $f ($(wc -l < "$f") lines) ---"
    rg -n "created_at|createdAt|id|replaceableWinners|latestStateOnly|_shouldReplace|isReplaceableKind|_isParameterized|visible.winners|sort|first" "$f" -A 6 -B 4 | sed -n '1,220p'
  fi
done

echo
echo "== Events model selection helpers =="
if [ -f packages/ndk/lib/shared/nips/nip01/nip01_event.dart ]; then
  cat -n packages/ndk/lib/shared/nips/nip01/nip01_event.dart | sed -n '200,380p'
fi
if [ -f packages/ndk/lib/data_layer/cache_manager/ndk_extensions.dart ]; then
  cat -n packages/ndk/lib/data_layer/cache_manager/ndk_extensions.dart | sed -n '1,220p'
fi

echo
echo "== Local first replaceable tests relevant slices =="
cat -n packages/ndk/test/usecases/local_first/local_first_test.dart | sed -n '220,370p'
cat -n packages/ndk/test/usecases/local_first/local_first_test.dart | sed -n '620,670p'

Repository: relaystr/ndk

Length of output: 15642


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Event entity implementation slices =="
for f in \
  packages/ndk/lib/shared/nips/nip01/nip01_event.dart \
  packages/ndk/lib/data_layer/entities/nip_01_event_model.dart \
  packages/ndk/lib/data_layer/entities/event_cache_records.dart
do
  if [ -f "$f" ]; then
    echo "--- $f ($(wc -l < "$f") lines) ---"
    rg -n "class Nip01Event|getTags|getDtag|createdAt|id|operator <:|compareTo|sort|shouldReplace|replaceable|visibleWinners|_buildReplaceableConflictKey|_replaceableConflictKey" "$f" -A 12 -B 4 | sed -n '1,260p'
  fi
done

echo
echo "== Replaceable comparison helper tests =="
cat -n packages/ndk/test/usecases/local_first/local_first_subscription_test.dart | sed -n '150,290p'

echo
echo "== Static verifier: selection in ADR vs implementation patterns =="
python3 - <<'PY'
from pathlib import Path
import re

adr = Path('doc/library-development/ADRs/local-first-reads.md').read_text()
checks = {
    "future defined as last emitted": re.search(r"The relay-confirmed value, so the last emitted one\.", adr) is not None,
    "newer values mentioned": re.search(r"every newer `relays` value", adr) is not None,
}
for name, ok in checks.items():
    print(f"{name}: {ok}")

files = {
    "nip01_event.dart": Path('packages/ndk/lib/shared/nips/nip01/nip01_event.dart'),
    "event_cache_records.dart": Path('packages/ndk/lib/data_layer/entities/event_cache_records.dart'),
    "local_first_subscription_test.dart": Path('packages/ndk/test/usecases/local_first/local_first_subscription_test.dart'),
}
for name, path in files.items():
    text = path.read_text()
    print(f"{name} has operator '<': {bool(re.search(r'operator\s+<\s*\(', text, re.S))}")
    print(f"{name} has createdAt/id comparison: {bool(re.search(r'returned\s+event\.createdAt|return.*event\.createdAt|return.*event\.id|createdAt.*id|lower id|lower id|tie|id <=|created_at|_compare|compare', text, re.S))}")
PY

Repository: relaystr/ndk

Length of output: 6753


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Files existence and sizes =="
wc -l packages/ndk/lib/shared/nips/nip01/nip01_event.dart packages/ndk/lib/data_layer/entities/event_cache_records.dart packages/ndk/lib/domain_layer/usecases/local_first/local_first.dart 2>/dev/null || true

echo
echo "== Nip01 event implementation slices =="
cat -n packages/ndk/lib/shared/nips/nip01/nip01_event.dart | sed -n '1,280p'
cat -n packages/ndk/lib/shared/nips/nip01/nip01_event.dart | sed -n '280,500p'

echo
echo "== Event cache records implementation slices =="
cat -n packages/ndk/lib/data_layer/entities/event_cache_records.dart | sed -n '450,580p'
cat -n packages/ndk/lib/data_layer/entities/event_cache_records.dart | sed -n '580,710p'

echo
echo "== local-first implementation slices =="
cat -n packages/ndk/lib/domain_layer/usecases/local_first/local_first.dart | sed -n '1,260p'
cat -n packages/ndk/lib/domain_layer/usecases/local_first/local_first.dart | sed -n '260,520p'

Repository: relaystr/ndk

Length of output: 315


Define relay-version ordering before completing future.

future is documented as the last relay-confirmed emission, but for replaceable events the last arriving relay response may be stale. Use NIP-01 replacement ordering, and complete future from the selected winner rather than arrival order. Align this rule with the createdAt ordering used by getDmRelays() and loadMissingRelayListsFromNip65OrNip02(). Add coverage for relay versions arriving in reverse order.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@doc/library-development/ADRs/local-first-reads.md` around lines 33 - 39,
Update the future contract and its implementation to select the NIP-01
replacement-order winner by createdAt rather than using the last arriving relay
response. Align ordering with getDmRelays() and
loadMissingRelayListsFromNip65OrNip02(), complete future from that selected
relay-confirmed value, and add coverage for reverse-order relay arrivals.

Comment on lines +38 to +40
/// The relay-confirmed value, so the last emitted one.
Future<T> get future;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 8 \
  '\b(NdkDataResponse|future|addError|onError|timeout|EOSE|eose|decrypt|10013)\b' \
  packages/ndk doc \
  --glob '*.dart' \
  --glob '*.md'

Repository: relaystr/ndk

Length of output: 50369


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== target ADR =="
cat -n doc/library-development/ADRs/local-first-reads.md | sed -n '1,130p'

echo "== focused symbol search =="
rg -n -C 6 \
  '\b(NdkDataResponse|future\s*:|Stream<|addError\(|onError:|timeout\(|EOSE|eose|decrypt|10013)\b' \
  packages/ndk --glob '*.dart'

Repository: relaystr/ndk

Length of output: 50369


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== local-first-reads.md =="
cat -n doc/library-development/ADRs/local-first-reads.md | sed -n '1,130p'

echo "== NdkDataResponse candidates =="
rg -n -C 10 'class NdkDataResponse|abstract class NdkDataResponse|interface NdkDataResponse|NdkDataResponse' packages/ndk --glob '*.dart'

echo "== focused lines/regions around addError/onError/timeout/eose in packages/ndk/lib =="
for f in $(rg -l 'addError\(|onError:|timeout\(|eose\(|EOSE|class .*DataResponse|abstract class .*Response|Future<T> get future' packages/ndk/lib --glob '*.dart' | head -80); do
  echo "--- $f"
  rg -n -C 4 'addError\(|onError:|timeout\(|eose\(|EOSE|Future<T> get future|class .*Response|abstract class .*Response' "$f" | sed -n '1,220p'
done

Repository: relaystr/ndk

Length of output: 3518


Define failure completion for future, cache errors, and partial relay failures.

The ADR reports cache decryption failures and no-reach cases as stream errors, but leaves future completion unspecified. Define whether future completes with the same error or a terminal failure. Also state whether cache failures are terminal so relay data cannot refine the read, and whether one failed relay is tolerated when another relay provides a value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@doc/library-development/ADRs/local-first-reads.md` around lines 38 - 40,
Update the ADR section defining the Future<T> future contract to specify how
cache decryption and no-reach failures complete it, whether those cache failures
are terminal and prevent relay refinement, and whether a failed relay is
tolerated when another relay returns a value; keep the stream behavior
consistent with these documented rules.

Comment on lines +42 to +45
class NdkValue<T> {
final T value;
final DataOrigin origin;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Make the cache-miss state representable for every declared T.

At Lines 42-45 and 50-58, the contract requires a cache emission when no cache exists, but NdkValue<T>.value cannot be null when T is non-nullable.

Constrain reads that can miss the cache to nullable T, or model the cache-miss state explicitly with a separate state field or sealed value type. Document this as a contract rule for all local-first reads, not only getPrivateUserRelays.

Also applies to: 50-58

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@doc/library-development/ADRs/local-first-reads.md` around lines 42 - 45,
Update the local-first read contract around NdkValue<T> so cache misses are
representable for every declared T: either constrain cache-miss-capable reads to
nullable T or introduce an explicit miss state via a state field or sealed value
type. Apply and document this rule for all local-first reads, including
getPrivateUserRelays, rather than relying on a non-nullable NdkValue<T>.value.


```dart
// on Lists, requires a logged in account, no pubkey parameter
NdkDataResponse<List<String>?> getPrivateUserRelays({Duration timeout});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

if ! command -v dart >/dev/null 2>&1; then
  echo "dart is required for this check" >&2
  exit 2
fi

tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT

cat >"$tmp_dir/bad.dart" <<'DART'
class NdkDataResponse<T> {}

abstract class Lists {
  NdkDataResponse<List<String>?> getPrivateUserRelays({Duration timeout});
}
DART

if dart analyze "$tmp_dir/bad.dart"; then
  echo "The invalid signature was accepted unexpectedly" >&2
  exit 1
fi

cat >"$tmp_dir/good.dart" <<'DART'
class NdkDataResponse<T> {}

abstract class Lists {
  NdkDataResponse<List<String>?> getPrivateUserRelays({Duration? timeout});
}
DART

dart analyze "$tmp_dir/good.dart"

Repository: relaystr/ndk

Length of output: 182


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Dart availability:"
command -v dart || true
dart --version 2>/tmp/dart-version 2>/tmp/dart-version-err || true
cat /tmp/dart-version 2>/dev/null || true
cat /tmp/dart-version-err 2>/dev/null || true

echo
echo "Target file location/context:"
rg -n "getPrivateUserRelays|LocalFirstReads|NdkDataResponse" -g '!*.lock' .

Repository: relaystr/ndk

Length of output: 448


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "File excerpt:"
sed -n '1,100p' doc/library-development/ADRs/local-first-reads.md | cat -n

echo
echo "Top-level Dart-related files:"
fd -i 'analysis_options|pubspec\.yaml|README|CONTRIBUTING' . -t f | sed 's#^\./##' | head -80

echo
echo "Optional named parameter references in ADR/documentations:"
rg -n '\{[^;]*Duration [^?]+\}|\(.*Duration .*=\)|{[^{}]*required\s+\w+\s+\w+}' .

Repository: relaystr/ndk

Length of output: 5521


🌐 Web query:

Dart optional named parameters sound null safety non-nullable type nullable default required

💡 Result:

In Dart, null safety enforces that variables cannot hold null unless explicitly marked as nullable with a question mark (?) [1][2]. This impacts how optional parameters are defined [1]. Named parameters are optional by default unless they are marked with the 'required' keyword [3][2]. Because optional parameters have an implicit default value of null, they must either be nullable or have a non-null default value provided [3][1][4]. If you define a named parameter with a non-nullable type and do not provide a default value, you must mark it as 'required' [1][4][2]. This tells the compiler that the caller must always provide the argument, making it non-optional [1]. Conversely, if you want a parameter to be truly optional without a default value, you must use a nullable type [1][4]. Summary of rules for optional/named parameters: - To make a parameter optional with an implicit null default: Use a nullable type (e.g., {int? x}) [3][4]. - To provide a non-null default value: Assign a compile-time constant (e.g., {int x = 1}) [3][5]. - To make a named parameter mandatory: Use the 'required' keyword (e.g., {required int x}) [1][2]. - A required named parameter cannot have a default value [6].

Citations:


Fix the null-safe Dart signature for timeout.

At doc/library-development/ADRs/local-first-reads.md:77, Duration timeout is an optional named parameter without required or a default value. In null-safe Dart, optional named parameters must be nullable, required, or given a compile-time default.

Use Duration? timeout if omission should select a default contract, or use required Duration timeout if the caller must provide it. Document the chosen default timeout.

Proposed nullable signature
-NdkDataResponse<List<String>?> getPrivateUserRelays({Duration timeout});
+NdkDataResponse<List<String>?> getPrivateUserRelays({Duration? timeout});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
NdkDataResponse<List<String>?> getPrivateUserRelays({Duration timeout});
NdkDataResponse<List<String>?> getPrivateUserRelays({Duration? timeout});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@doc/library-development/ADRs/local-first-reads.md` at line 77, Update the
getPrivateUserRelays signature so its optional timeout parameter is null-safe:
make it Duration? and document the default timeout behavior used when it is
omitted.

Source: MCP tools

@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 71.32%. Comparing base (4e28d2e) to head (d0fe6dc).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #702      +/-   ##
==========================================
- Coverage   71.32%   71.32%   -0.01%     
==========================================
  Files         225      225              
  Lines       13201    13201              
==========================================
- Hits         9416     9415       -1     
- Misses       3785     3786       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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