Skip to content

feat: close the Graphle data-layer parity gaps - #38

Merged
Mearman merged 10 commits into
mainfrom
feat/github-parity
Oct 11, 2026
Merged

Mearman merged 10 commits into
mainfrom
feat/github-parity

Conversation

@Mearman

@Mearman Mearman commented Oct 11, 2026 •

Copy link
Copy Markdown
Member

Closes the data-layer gaps between Graphle and process-graph in the source contract, the GitHub adapter and the store.

The source contract's v1 gains a typed sync filter (states, labels, order field and direction), carried in every cursor, and scopes.roots and scopes.children, which list the scopes a picker offers as typed entries with paging, resumption, gaps and aborts. Both are additive: a pager without scopes still compiles and its listing answers NOT_IMPLEMENTED. The fake implements both and the conformance kit covers them; graphSourceListingConformance runs the listing part on its own client.

The GitHub adapter reads blocking beside blockedBy (one transition per dependency whichever end it is read from), maps the filter to GitHub's issue listing arguments, syncs a Projects v2 board as github:owner/projects/number through the same paging, rate-limit and gap handling, and lists the viewer's account and organisations and an owner's repositories. The token port takes an undefined owner for the viewer's own listing, and storedTokenPort ports Graphle's per-owner choice among stored tokens for a browser host. The fixture is re-recorded from public repositories and a public board in GitHub's own organisation; the viewer's listing is tested over written responses and live, never recorded.

The store contract keeps the child-of, assigned-to and part-of relations a sync already returns: items.upsert takes an optional relations list, checked against the model, and new v1 procedures relations.list, query.children, query.descendants and query.ancestors read them back and walk the hierarchy. The SQLite store persists them in a new relations table (committed drizzle-kit migration) and answers the walks with WITH RECURSIVE; the fake and conformance kit cover both.

Graphle never loaded labels as data on an issue (only as a listing filter) and never linked pull requests to issues, so neither is added here.

Closes #40. The board scope is github:owner/projects/number; turning a pasted board URL into it is the one-line readScopeInput change in graph-web.

A sync takes an optional typed filter (states, labels, order field and
direction) that narrows and orders what each scope lists on the source's
side, and a cursor is valid only under the filter it was issued for.

scopes.roots and scopes.children list the scopes a picker offers as typed
entries (scope, label, description, URL, archived flag), paged, resumable
and abortable like sync, with an unknown parent reported as a gap. A
pager supplies them through an optional scopes member; one without it
answers NOT_IMPLEMENTED, so existing pagers keep compiling.

The fake honours both, and the conformance kit gains filter cases and
optional scope listing cases.
An adapter that replays recorded responses has to record the filtered requests the
kit makes, so the filter is exported as conformanceFilter.
…s and scope listings

Each issue's blocking connection is read beside blockedBy, so work blocked in a
repository outside the requested scopes is found from its blocker; both ends
give the same transition id, so a dependency read twice is one transition.

A sync's filter becomes GitHub's issue listing arguments (states, labels and
IssueOrder) and is carried in the cursor. github:owner/projects/number syncs
the issues on an organisation's or user's board through the same paging,
rate-limit and gap handling, passing over pull requests and draft issues; a
filter with a board scope is invalid-scope, since a board cannot be filtered.

The pager lists scopes: the viewer's own account and organisations, and an
owner's repositories with description, URL and archived flag. The token port
takes an undefined owner for the viewer's own listing.

The fixture is re-recorded from public repositories and a public board in
GitHub's own organisation; the viewer's listing is tested over written
responses and live, never recorded.
graphSourceListingConformance takes its own client, so an adapter can exercise the
listing at a page size that makes the parent span two pages while syncing at its
default.
… page size

The demonstration organisation's repositories fit one page at the default size, so
the live listing runs on a client at the fixture page size, as the recording does.
storedTokenPort is a token port over tokens a host keeps, each scoped to some
owners or to any, ported from Graphle's resolution: a token naming the owner
wins, the most specific first and then the most recently used; otherwise the
most recently used token for any owner, which is also the only kind a
request about the viewer can use. Logins match without regard to case, and
no fitting token is unauthorised without naming a token. A browser host gets
the same per-owner choice the environment helper gives Node.
The test spread the shared options with an undefined page size, which
exactOptionalPropertyTypes refuses; it now builds its own listing and asserts the
whole page.
…d-of hierarchy

items.upsert takes an optional relations list, the child-of, assigned-to and
part-of relations a source already returns, which the store used to drop.
Each distinct relation is stored once in first-stored order; an end that
names nothing the merged model declares in the space its kind requires is an
unknown-reference issue and the write is refused. Saving a model keeps only
the relations of what it still declares, and deleting it removes them.

relations.list reads them back, and query.children, query.descendants and
query.ancestors walk child-of relations, nearest first in model order,
ending on a cycle. All are new v1 procedures, so existing clients are
unchanged; the fake implements them through the exported reference
functions and the conformance kit covers them.
…s in SQL

A relations table keyed by model, kind and both ends holds what items.upsert
sends, with a position for first-stored order and an index on the parent end
for the downward walk; the primary key serves the upward one. The drizzle-kit
migration that adds it is committed beside the initial one.

Writes rewrite a model's relations with its other rows in the same
transaction after the contract's relationIssues check. query.descendants and
query.ancestors are WITH RECURSIVE walks over child-of rows, capped at the
model's element count like the dependency walks, and query.children a plain
query; all answer as the contract's reference functions do, which the
conformance kit checks.
…ilter on the fake

The message-port run exercises graphSourceListingConformance on its own and a fake
test syncs under conformanceFilter, so both exports are used where they are defined.
@Mearman
Mearman marked this pull request as ready for review October 11, 2026 03:25
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
🔒 Security Review ✅ Completed 2026-10-11T03:29:42.250735Z 526097d Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@Mearman

Mearman commented Oct 11, 2026

Copy link
Copy Markdown
Member Author

@codex security review

1 similar comment
@Mearman

Mearman commented Oct 11, 2026

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 526097dd7f

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@Mearman
Mearman merged commit 8c05555 into main Oct 11, 2026
14 checks passed
@Mearman
Mearman deleted the feat/github-parity branch October 11, 2026 03:29
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.1.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Read GitHub Projects v2 boards as a source scope

1 participant