Repository navigation
Releases
v0.5.0
Fixed
- User sync no longer fails a search page because one pull request sits in a
SAML-protected or forbidden repository. The node is skipped, counted in
skipped, and named in a note with GitHub's message; the rest of the page is
stored, and the cursor moves past it. Any other GraphQL error still fails the
page.
Changed
- Library API:
fetch::user_search::parse_user_search_pagetakes the response's
errorsas a second argument, andUserSearchPagehas a newinaccessible
field.
v0.4.0
Breaking
- Library API:
query::validate_select_only,
storage::sync_state_repository::acquire_lockandrelease_lockare
removed, andfetch::GhCommandLineis built withGhCommandLine::new().
CLI flags and MCP tool inputs are unchanged. prs_mergednow counts by merge date, so metric values change.
Changed
prs_merged(user, repo and group metrics, and thetop_mergers
leaderboard) now counts pull requests merged in the window, by
merged_date_key. It used to count pull requests opened in the window that
are merged now, so period-over-period deltas ran low. Expect different
numbers after upgrading.query::validate_select_onlyis removed;raw_querynow asks SQLite whether
the prepared statement is read-only.
Fixed
-
query --group <name>for a group with no members returned every pull
request; it now returns none, matchingmetrics.QueryBuilder::authors
andreposdo the same for an empty list. -
raw_queryrefused nothing but a non-SELECT/WITHfirst word, so
WITH x AS (SELECT 1) DELETE FROM ...ran. It also rejected a;inside a
string literal. -
gh api graphqlexits 1 when a response carries partialerrorsbut
still prints the data. That data is now kept, so a search with one
SAML-protected or missing node stores the rest instead of failing.
Rate-limited responses still go through retry and backoff. -
A hung
ghis killed after 120 seconds with a clear error
(GITHUBDW_GH_DEADLINE_SECSchanges the limit).ghnow runs with
GH_PROMPT_DISABLED=1and withoutGH_DEBUG/DEBUG, so request headers
never reach stored error text. -
A permanent HTTP 403 (SAML, permissions, blocked repository) fails on the
first attempt instead of seven. Only rate-limit 403s and specific
transient network errors are retried. -
GraphQL string variables are passed with
-f, so a login or repository
named123,trueornullreaches GitHub as a string and@xis never
read as a file. Only variables the query declaresIntorBooleanuse
-F. -
sync: run-level errors (cancellation, a spent rate limit or quota reserve,
an unusablegh, a failing database) now stop the repository and user walks
at once instead of being recorded as a failure for every remaining item.
Constraint errors on one item's rows are still recorded per item. Page size
halves only for 502/504, timeout and resource-limit failures; any other
error returns immediately. -
sync: the per-entity sync lock is now an owned lease. Recording progress
renews it, so a walk that runs past 30 minutes (search pacing, rate-limit
sleeps) no longer lets a second process start the same sync. A run releases
only the lock it holds, so a stale holder finishing cannot delete its
successor's lock. A lock with an unparsablestarted_atis reclaimable
instead of blocking forever, and a failure while recording the job outcome
releases the lock and no longer replaces the sync's own error. -
sync watch: the reported start time stays the job's real start instead of
following lock renewals. -
mcp:trigger_syncno longer blocks the server. It runs on a blocking
thread with its own database connection, so other tool calls, pings and
cancellation are answered while it works. A call processes at most 500
pull requests and returnspartial: true(statusPartial) when it stops
at that budget; calling again resumes. A rate-limit wait over 30 seconds
is not slept: the call returns statusRateLimitedwith
rate_limited_until. The result also carriesnotes. Input is unchanged.
Removed
- The unused
live-testsCargo feature. storage::sync_state_repository::acquire_lockandrelease_lock; use
SyncLease(oracquire_owned_lock).
v0.3.5
Added
retention: a shared collection policy file
($XDG_CONFIG_HOME/adlio-collection/policy.json) re-read at every write.
A valid file caps diff retention (none,transient,retain); an
invalid file stores no new diff text; no file keeps the old behaviour.
While a file exists, every sync path preserves stored payloads: an empty
provider value never replaces a stored title, body, comment or patch, and
no stored row is deleted.collection: counts-first collection by warehouse day (collect_day)
with per-day coverage, a review frontier for reviews on pull requests
updated later,day_countsthat returnsNonefor days not collected,
on-demand detail (fetch_pull_request_detail), in-memory diffs
(fetch_pull_request_diff_live) and revision fingerprints.GhClientcancellation (set_cancellation) and deferred rate-limit
waits (set_defer_waits_over), reported asError::Cancelledand
Error::RateLimited.
Compatibility
- The three new tables are created with
CREATE TABLE IF NOT EXISTSon
open, not by a numbered migration.user_versionstays 8, so a 0.3.4
binary still opens a warehouse 0.3.5 has used.
v0.3.4
Fixed
- Sync no longer slows down as the warehouse grows. The full-text triggers
located a row's index entry withDELETE ... WHERE <key> = ...on an
UNINDEXED FTS5 column, which scans the whole FTS table, so every upsert of a
PR, issue or comment cost a full scan. At 259k issue comments onesync all
ran past launchd's two-hour limit. Migration 8 rebuilds the four FTS tables
keyed by the source rowid, makes the triggers delete by rowid, and skips the
update trigger when no indexed column changed. On an 8.5 GB warehouse a
comment upsert now takes about 8 ms, and the one-time rebuild took 6m49s.
The rebuild runs when the first command opens the warehouse after the
upgrade, so expect that firstsyncorsearchto take minutes on a large
warehouse.
v0.3.3
Performance
- Migration 7 adds
fact_pull_requests(created_at DESC), so newest-first
pull-request listings stop sorting the whole table: on a 16 GB test
warehouse with nothing cached, the first page went from about 30 s to
24 ms. The index is descending on purpose. Pull requests that share a
created_atkeep the order the old scan-and-sort returned. - It also adds
fact_pull_requests(author_key, created_date_key, repo_key, title), so per-author work lists over a date window (repository and title)
read only the index.
Upgrade notes
- Migration 7 adds about 6 MB and builds in one transaction at first open.
Binaries older than 0.3.3 refuse an upgraded warehouse.
v0.3.2
Performance
- Migration 6 adds covering indexes for per-author activity over a date
window: pull requests opened (with additions, deletions and changed files),
reviews submitted, and review comments written. Callers that count these
per author (user metrics, leaderboards, or their own SQL over the
warehouse) no longer read the full rows. On a 16 GB test warehouse, counts
for a dozen authors went from seconds to milliseconds on a cold page cache.
Upgrade notes
- Like migration 5, migration 6 builds its indexes in one transaction the
first time a warehouse is opened (tens of seconds on a large warehouse)
and adds about 16 MB there. Binaries older than 0.3.2 refuse an upgraded
warehouse.
v0.3.1
Performance
- Migration 5 adds covering indexes for the repository metrics and
leaderboards: pull requests by repository and creation date, check-run
outcomes, review and issue comments, and reviews per pull request; and
per-author merged pull requests and pull-request conversation comments. On a
warehouse whose pages are not cached,repo_metricsandrepo_aggregations
previously read every pull-request, comment and check-run row they touched,
including the wide title and body columns; they now read index pages only. On a 16 GB test warehouse the
slowest queries went from tens of seconds to under two seconds on a cold
page cache, and the comment counts from about 1 s to under 0.2 s warm. - Repository comment counts and the top-commenters leaderboard restrict each
side of the review/issue comment union to the repository's pull requests
before combining them, instead of reading every comment in the warehouse.
Results are unchanged.
Upgrade notes
- Migration 5 builds the indexes the first time a warehouse is opened, which
takes minutes on a large warehouse, followed by the one-time foreign key
check every schema change triggers. The migration runs in one transaction:
if interrupted, the warehouse stays at version 4 and the next open retries. - The indexes add about 1.6% to the database size, most of it check runs. As
with every migration, a binary built before this release refuses to open a
warehouse that has been upgraded.
v0.3.0
Breaking
ErrorgainsGitRecoveryandQuotaReservevariants; exhaustive matches
onErrorneed a new arm.- New public fields break struct literals:
fetch::issues::IssueData
(pending_comments,pending_labels,pending_assignees),
fetch::pull_requests::PullRequestData(node_id,pending,
changed_files_reported,check_runs_truncated, ...), and
sync::SyncSummary(partial).
Added
- Nested-connection continuation, so heavy pull requests and issues land with
every comment, review, thread and file instead of the first slice. - Git-backed recovery of pull-request file sets GitHub caps at 3,000 files.
sync targets,sync repair-plan,sync check-historyand
sync check-history-plan.GithubDW::open_read_only.query::PrFiltersandPrState::parse: the filter set the CLI and the MCP
query tool share.MetricsEngine::group_metrics,storage::config_repository, and
sync_state_repository::{active_syncs, recent_jobs}.
Changed
query --statewith an unknown value now lists the accepted values.- Internal layout:
sync, the CLI binary,check_historyand
fetch::git_filesare split into submodules. Public paths are unchanged.
v0.2.4
Added
sync user <login>and user coverage insync all.monitor add-userhas
registered user sources since 0.1.0 andmonitor listhas shown them, but
sync only ever served repositories — so a monitored user'slast_sync_at
never moved and no command could answer "what did this person do". This
release makes sync serve them: for each enabled user it searches the pull
requests they authored (author:) and the ones they reviewed
(reviewed-by:), and upserts every result through the same entity, fact, and
time-dimension pipelinesync repouses. A PR reachable from both paths lands
identically either way.- Coverage, locking, and job records for user sources under the
user:<login>
entity key, sosync watch, stale-lock recovery, and the recorded date-range
merge all behave as they do for repositories. The two roles keep separate
updated_atwatermarks (useranduser_reviewsinsync_metadata) because
they walk disjoint result sets — one shared cursor would let the busier side
advance past items the other has not fetched. As on the repository path, a
watermark only ever moves past an item that actually persisted, so a failed
upsert is retried on the next run instead of becoming a permanent hole. - Honest handling of the two limits search imposes. GitHub serves at most 1,000
results per query however many it reports matching, so a backfill is
partitioned into calendar-monthcreated:windows and any window still
reporting more than the cap is bisected until each part fits — the over-cap
query is abandoned before its results are spent, not truncated silently. When
a window is already a single day and still over the cap, the run reports the
shortfall rather than recording coverage it did not fetch. An incremental run
is bounded by its watermark instead of by the window, so there the cap is only
a problem if pagination runs out before the watermark is reached: that case is
reported with the--dayscommand that closes the gap, while a walk that
reaches its watermark stays quiet no matter how many lifetime matches the
query reports. Search's request budget is 30/minute, an order of magnitude
tighter than the 5,000 points/hour the repository path spends, so search calls
are paced on their own clock independent of the observedrateLimitblock.
Changed
sync allnow iterates every supported source type — repositories first, then
users — and prints a line for each enabled source it will not serve. A source
type this build does not implement, and a row that is present but disabled,
are both named on stdout. Previously the source list was filtered to
repositories and everything else disappeared without a word, which read as
"there was nothing to do".sync usercovers pull requests and reviews.--issues-onlyhas no
user-scoped counterpart in this release; passing it reports that instead of
returning zero, which would read as "this user has no issues".- Repositories discovered through a user sync are stored as dimensions but are
not added tomonitored_repos. Monitoring a person is a statement about
that person, not an instruction to start tracking every repository they
contributed to.
v0.2.3
Fixed
- Every command that names an author printed the warehouse's internal entity
key (user:octocat,bot:dependabot) instead of the login GitHub uses.
queryin all four modes (table, json, csv, count-by-author),search --json, themetrics userheader, and thequery_pull_requests,search
andget_metricsMCP tools were all affected, so the only githubdw output
that showed a usable login was the metrics leaderboards. A value copied off
any other surface could not be pasted into a GitHub URL or API call without
knowing to strip a namespace first, and the field carrying it was already
namedauthororlogin— the bare spelling the schema itself uses for
dim_entities.login. Identity fields now hold the bare login everywhere, and
the type that the namespace used to convey travels in its own field:
author_typeon PR rows and search hits,entity_typeon a user report. The
key remains available where a caller genuinely needs to join back to the star
schema, under a name that says so:author_keyon PR rows andentity_key
on a user report. - A single display column could not tell a bot from the person who shares its
login, since dropping the namespace drops that distinction. Bots are now
marked visibly instead —dependabot [bot]in table output, in
count-by-authorgroup labels, and in themetrics userheader — so the two
identities stay distinguishable and remain separate groups in a breakdown. - A leaderboard row for an entity with no display name fell back to printing
its raw key. It now falls back to the bare login, with the same bot marker.
Added
- Schema v4 constrains
dim_entitiesto the identity invariants the ingest
code already upholds:loginis non-empty and never namespaced,
entity_keyis exactlyentity_type+:+login, andentity_typeis
one of the namespaces ingestion mints. These were enforced by code alone, so
a writer that bypassed the normalizer could store a row that no bare-login
lookup would ever match while the write reported success. The upgrade is a
no-op on a warehouse whose rows already conform, and refuses to complete on
one that carries a violating row rather than migrating the problem forward.
Changed
- The
csvoutput ofquerygained anauthor_typecolumn, so the bare
author and its type are both present without the namespaced key. Callers
parsing CSV by column position need to account for the new column.