Skip to content

drop inline graph writer - #1130

Merged
kans merged 3 commits into
mainfrom
kans/CXE-1376-drop-inline-graph-writer
Sep 9, 2026
Merged

drop inline graph writer#1130
kans merged 3 commits into
mainfrom
kans/CXE-1376-drop-inline-graph-writer

Conversation

@kans

@kans kans commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Deletes WithEntitlementGraphInCheckpoints and the inline-graph checkpoint
writer behind it. state.Marshal now drops the entitlement graph and blanks the
expansion page token unconditionally, instead of doing so only when the option
was unset.

Why this is safe

The option had no callers. A sweep of 65 ConductorOne repos under ~/work plus
the c1 monorepo found zero call sites in first-party code, in the current trees
or in git log -S history — every text hit is vendored SDK source. It was also
never reachable except by a direct Go call: nothing in pkg/cli, pkg/config,
pkg/field, pkg/connectorrunner, or pkg/tasks ever plumbed it to a flag or
config field, so no connector could have enabled it through config. Inside the
SDK the only invocation was TestWithEntitlementGraphInCheckpointsReachesState,
which this change removes.

It is a public API in a v0.x module, released in v0.23.0 through v0.29.0
(2026-08-11 to 2026-09-04), so a third-party caller cannot be ruled out. It is
deleted rather than kept as a deprecated no-op deliberately: the behavior is
intentionally unavailable rather than relocated, so there is no replacement call
to migrate to, and a no-op would leave a caller believing they still have an OOM
remedy. A compile error is the honest signal and the fix is deleting one line.

Reader compatibility is unchanged

Only the writer is gone. Tokens written by pre-omission SDKs still decode with
the graph restored and pagination preserved. The reader tests that used to build
those bytes by enabling the option now hand-craft them through a
marshalLegacyInlineGraphToken helper, so TestSyncerTokenLegacyInlineGraphStillDecodes,
TestGraphFromToken, and the graph compatibility matrix keep their coverage. The
golden token corpus added in #1129 pins the wire format on bytes across this
change.

Accepted risk

The option was introduced as a break-glass knob for a tenant whose grant
expansion cannot finish within one worker or activity lifetime, on the theory
that checkpointing the graph would let expansion resume mid-run. On Pebble it
never did. RunSingleStep routes to RunTopologicalMergeProjection whenever the
store yields principal-sorted grants, which is the Pebble adapter;
driveTopologicalLayer never consults edge.IsExpanded, and
MarkExpansionComplete flips every edge at once only after the full projection
returns clean. A Pebble worker that dies at 90% of the projection comes back with
every edge unexpanded whether or not the graph was in the token. That has been
true since #965 landed the projection on 2026-06-16, six weeks before the option
was written.

Mid-expansion resume only ever existed on the source-batched expander that
SQLite and the in-memory doubles use, where RunSingleStep retires one queued
graph.Actions entry per call and marks that action's edges expanded as it goes.
So the exposure is writable syncs over not-yet-converted v1 files, which
selectStoreDriver deliberately keeps on SQLite when opened without an explicit
engine. That exposure already exists on main: #1019 made graph omission the
default on 2026-08-10 and nothing has set the option since, so no sync has been
getting inline-graph checkpoints for a month. This change deletes an unused
remedy rather than changing behavior.

The intended path for such a tenant is conversion to Pebble via an explicit
WithEngine(EnginePebble), which makes expansion fast enough that resume stops
mattering. Persisting the whole graph into every checkpoint is what caused the
OOM that #1019 fixed.

Ride-alongs

  • Rewrote two comments in pkg/sync/syncer.go that claimed
    preserveEntitlementGraph falls back to inline token storage when the store
    lacks the sidecar capabilities. There was never such a fallback in code — the
    claim was only reachable through the option this change deletes.
  • Corrected two comments in pkg/synccompactor for the same reason: a preserved
    graph without a sidecar has nowhere to go rather than bloating the final token,
    and state.Marshal drops the graph from every token rather than by default.
  • Removed stateOpt and newState's variadic parameter, which had no remaining
    implementations.

@linear-code

linear-code Bot commented Sep 9, 2026

Copy link
Copy Markdown

CXE-1376

No code path can now produce a sync token containing "entitlement_graph",
and every code path can still consume one. The reader is untouched: tokens
written before #1019 carry an inline graph and must still resume.

Removed WithEntitlementGraphInCheckpoints, cfg.checkpointEntitlementGraph,
the withCheckpointEntitlementGraph stateOpt, and
state.checkpointEntitlementGraph. Marshal's graph drop and expansion
page-token blanking are now unconditional; the normalization itself is
unchanged, including that only the serialized copy is normalized while the
live state keeps its page token. withCheckpointEntitlementGraph was the
only stateOpt, so newState takes no arguments and the option type goes with
it rather than staying as a constructor mechanism with zero options.

The option had no callers: none in this repo, none in the c1 monorepo, none
across the baton-* connector repos. It has never been enabled by anything.
Its tradeoff was also asymmetric. #1019 stopped serializing the graph
because doing so OOM-killed sync workers twice in production, with
state.Marshal at 6.4GB cumulative of a 13.5GB heap against a live graph of
~0.9GB. The option was then added for a tenant whose expansion "could fail
to converge", which was never observed; loadEntitlementGraph rebuilds the
graph from PendingExpansionPage with no connector calls, so what it
preserved cost seconds of rework even on very large c1zs.

Tests: TestSyncerTokenIncludesEntitlementGraphWhenEnabled and
TestWithEntitlementGraphInCheckpointsReachesState are deleted with the
option they cover. The reader assertions in the former's tail (graph
restored with Depth and Nodes intact, expansion page token preserved) are
already made by TestSyncerTokenLegacyInlineGraphStillDecodes, so no
coverage moves. Three reader tests built their legacy token by turning the
writer on — the compatibility matrix, TestGraphFromToken, and
TestPrepareExpansionReplayToken_ClearsPreservedGraph — and now encode
serializedTokenV1 directly through marshalLegacyInlineGraphToken.
goldenTokenCases sheds the v1_inline_graph.json self round-trip, which was
the writer; the v1_inline_graph.json -> v1_inline_graph.dropped.json case
and the dropped self case stay, so no fixture is orphaned.

Validated against three planted mutants. Restoring the graph into the
serialized struct and dropping the page-token blanking each fail
TestGoldenTokenRoundTrip on the v1_inline_graph.json -> .dropped.json case,
the second on the action map. Making Unmarshal drop the inline graph fails
all four reader tests plus TestGoldenTokenHelpers, so the rewritten reader
tests are not tautologies through the deleted writer.

Ride-along: two comments claimed preserveEntitlementGraph keeps the graph
in the final token when the store lacks the sidecar capability. That branch
cannot run. Its only caller gates on c1zstore.EnginePebble
(synccompactor/compactor.go:1327) and pebbleStore is the only implementer
of either EntitlementGraphStore or c1zstore.GrantGenerationDigestReader, so
the capabilities are always present. Both comments now describe the sidecar
as the only path; no fallback added.

No big-O change: Marshal loses one branch. pkg/sync/checkpoint_cost_bench_test.go
needs no new ratchet.

Fixture README: v1_inline_graph.json moves back to hand-authored input,
because this commit deletes the writer that produced it, and the two table
rows that named the "opt-in" and "default" writers no longer describe
anything — there is one writer now. The companion commit classified it as
recorded output, which was correct only while the option existed.

CXE-1376

Co-authored-by: Cursor <cursoragent@cursor.com>
@kans
kans force-pushed the kans/CXE-1376-drop-inline-graph-writer branch from 29576f5 to d124cfa Compare September 9, 2026 20:06
Comment thread pkg/sync/syncer.go
Comment thread pkg/sync/syncer.go
Comment thread pkg/sync/state.go
Comment thread pkg/sync/state_test.go
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

General PR Review: drop inline graph writer

Blocking Issues: 0 | Suggestions: 0 | Threads Resolved: 0
Criteria: Criteria status: loaded .claude/skills/ci-review.md from trusted base 3659f92ec9f2.
Review mode: incremental since f9512053
View review run

Review Summary

The new commit c4a5eb15 is documentation-only and closes the one open suggestion from the last pass: the stale "a graph preserved by WithPreserveEntitlementGraph travels in the token" claim is now corrected in both places the earlier sweep missed — pkg/sync/state.go:91-97 and the pkg/sync/graph_from_token_test.go doc comment, whose test is renamed to TestPrepareExpansionReplayToken_DropsInlineGraph and now says outright that Marshal's unconditional drop is what enforces U2 and that no assertion below pins the redundant ClearEntitlementGraph. I re-swept pkg/ and found no remaining comment attributing an inline token graph to that option. The full PR diff was scanned for security and correctness: no security issues; no go.mod/go.sum/vendor changes; the removal is symbol-complete (zero references to WithEntitlementGraphInCheckpoints, checkpointEntitlementGraph, withCheckpointEntitlementGraph, or stateOpt remain) and legacy-reader coverage survives through marshalLegacyInlineGraphToken plus the v1_inline_graph.json.dropped.json golden pair. The earlier exported-API-removal, capability-miss-warn, and version-stamp-literal findings were answered by the author and are not re-flagged.

Risk triage (per docs/BUG_CATCHING.md §2): Silence — no; the removal surfaces as a compile error and this commit changes no executable code. Durability — writer-only, and this commit touches no writer. Uncontrolled dimensions — none introduced; graph-less tokens have been the default since #1019. Consumer distance — downstream connectors, via one exported SyncOpt already discussed on this PR. Consequence — remediation rung 1. Verdict: MEDIUM, unchanged from the prior pass; the instruments the criteria ask for (golden token corpus on bytes, the three legacy-reader tests, TestSyncerTokenVersionStampSurvivesGraphOmission) are present in the diff.

Security Issues

None found.

Correctness Issues

None found.

Suggestions

None.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Blocking issues found — see review comments.

Both described a fallback the inline-graph writer used to provide. With
that writer gone, a preserved graph without a sidecar has nowhere to go
rather than landing in the final sync token as bloat, and state.Marshal
drops the graph from every token it writes rather than by default.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread pkg/sync/graph_from_token_test.go

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No blocking issues found.

Both said an in-token graph came from WithPreserveEntitlementGraph. That
option writes to the c1z sidecar, and with the inline writer gone only a
pre-omission SDK puts a graph in a token.

Also record what actually enforces the replay contract. Marshal drops the
graph from every token it writes, so ClearEntitlementGraph in
PrepareExpansionReplayToken cannot change the emitted bytes: removing that
call leaves the whole pkg/sync suite green. The call stays to keep the
contract stated where the token is rewritten, and the test comment now says
no assertion pins it.

Renames the test from ClearsPreservedGraph to DropsInlineGraph: the graph is
not preserved, and the test does not pin the clear.

Co-authored-by: Cursor <cursoragent@cursor.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No blocking issues found.

@kans
kans merged commit 4b4f675 into main Sep 9, 2026
12 checks passed
@kans
kans deleted the kans/CXE-1376-drop-inline-graph-writer branch September 9, 2026 21:25
@kans kans mentioned this pull request Sep 10, 2026
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.

2 participants