Skip to content

[DRAFT] Phase 5: add type-scoped grants and entitlements#1029

Open
kans wants to merge 3 commits into
mainfrom
kans/type-scoped
Open

[DRAFT] Phase 5: add type-scoped grants and entitlements#1029
kans wants to merge 3 commits into
mainfrom
kans/type-scoped

Conversation

@kans

@kans kans commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements Phase 5 of the source-cache replay stack: type-scoped grant and entitlement collection.

Resource types can now opt out of per-resource fan-out using TypeScopedGrants or TypeScopedEntitlements. The syncer instead issues one type-scoped planner action, and connectors can enqueue checkpointed sibling cursors with EnqueuePageTokens.

Changes

  • Add shared type-scoped collection plumbing.
  • Route type-scoped requests through connectorbuilder optional interfaces.
  • Preserve those interfaces through the V1-to-V2 adapter.
  • Validate annotation/interface coherence during connector construction.
  • Persist spawned cursor state across checkpoints.
  • Bound cursor count and token sizes.
  • Avoid per-resource collection for type-scoped types, including targeted syncs.
  • Add count-only progress reporting for type-scoped phases.
  • Register type-scoped annotations with ingestion invariants I7 and I8.
  • Add focused routing, validation, fan-out, and checkpoint tests.

Scope

This PR contains collection and routing behavior only. Source-cache replay, continuation lookup, and warm/cold equivalence tests remain in Phase 6.

Testing

  • go test ./pkg/sync ./pkg/sync/progresslog ./pkg/connectorbuilder

@kans
kans requested a review from a team July 22, 2026 21:51
Comment thread pkg/sync/syncer.go
Comment on lines 1269 to 1300
@@ -1278,11 +1286,17 @@ func (s *syncer) SyncTargetedResource(ctx context.Context, action *Action) error
}

if !shouldSkipEnts {
s.state.PushAction(ctx, Action{
Op: SyncEntitlementsOp,
ResourceTypeID: resourceTypeID,
ResourceID: resourceID,
})
typeScopedEnts, err := s.resourceTypeHasTypeScopedEntitlements(ctx, resourceTypeID)
if err != nil {
return err
}
if !typeScopedEnts {
s.state.PushAction(ctx, Action{
Op: SyncEntitlementsOp,
ResourceTypeID: resourceTypeID,
ResourceID: resourceID,
})
}
}

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.

🟡 Suggestion: For a targeted sync, SyncTargetedResource skips the per-resource grant/entitlement actions when the type is type-scoped, but the targeted-sync seeding in parallel_syncer.go (InitOp path, ~L199-217) only pushes SyncTargetedResourceOp + SyncResourceTypesOp and never enqueues a top-level SyncGrantsOp/SyncEntitlementsOp. Net effect: a targeted sync of a type-scoped resource type collects zero grants/entitlements. If that's intended for a later phase, consider a comment noting it; otherwise the targeted path needs to enqueue the type-scoped action. (medium confidence)

p.entitlementsCountOnly[resourceType] = true
}

func (p *ProgressLog) EntitlementsProgress(resourceType string) int {

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.

🟡 Suggestion: EntitlementsProgress (and GrantsProgress at L342) are new exported methods but have no callers in this PR. Exported surface on a shared SDK is a long-term commitment — if these are only needed by Phase 6 replay/equivalence tests, consider adding them alongside their first use rather than landing dead exported API now. (low confidence)

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

General PR Review: [DRAFT] Phase 5: add type-scoped grants and entitlements

Blocking Issues: 0 | Suggestions: 0 | Threads Resolved: 0
Criteria: Criteria status: loaded .claude/skills/ci-review.md from trusted base dd39a54a9c95.
Review mode: full
View review run

Review Summary

Scanned the full PR diff for security and correctness. This change adds opt-in type-scoped grant/entitlement collection (new optional TypeScopedGrantsSyncer/TypeScopedEntitlementsSyncer interfaces, TypeScoped* request annotations, and a Spawned/TypeScoped/TypeScopedPlanned extension to the checkpointed Action), reworks the parallel scheduler into a dynamically extensible queue with cycle-detected and size-bounded spawned cursors, and converts C1File.dbUpdated from bool to atomic.Bool. The SDK-compatibility surface is additive: the new interfaces are optional (the newResourceSyncerV1toV2 adapter only claims them when the wrapped syncer implements them), the new Action fields are omitempty and default to pre-type-scoped behavior on legacy checkpoints, and per-worker pagination continuation is preserved by syncOneAction's re-read loop. The atomic.Bool conversion is complete (all reads/writes use Load/Store) and safe (C1File already holds sync.Mutex fields, so it was already non-copyable). No new blocking issues found. Two low-confidence items from prior reviews still stand and are not re-posted as new: handleOperationError (renamed from handleParallelBatchError) still drops batchErr when runCtx is deadline-exceeded, and the exported EntitlementsProgress/GrantsProgress getters remain unused repo-wide.

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.

No blocking issues found.

@kans
kans force-pushed the kans/type-scoped branch from 94a9c44 to 29c006a Compare July 22, 2026 22:13

@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.

Add type-scoped collection for grants and entitlements, including
checkpointed EnqueuePageTokens fan-out, connectorbuilder routing and
registration validation, targeted-sync guards, progress accounting,
and I7/I8 invariant coverage.
@kans
kans force-pushed the kans/type-scoped branch from 29c006a to 48adc0b Compare July 22, 2026 22:38
Comment on lines 271 to +344
@@ -313,6 +333,18 @@ func (p *ProgressLog) LogGrantsProgress(ctx context.Context, resourceType string
}
}

func (p *ProgressLog) SetGrantsCountOnly(resourceType string) {
p.mu.Lock()
defer p.mu.Unlock()
p.grantsCountOnly[resourceType] = true
}

func (p *ProgressLog) GrantsProgress(resourceType string) int {
p.mu.RLock()
defer p.mu.RUnlock()

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.

🟡 Suggestion: EntitlementsProgress and GrantsProgress are newly exported but have no callers anywhere in the repo (only the Set*CountOnly and Add*Progress helpers are used by the type-scoped path). Per the SDK scope-control guidance, unused exported helpers become long-term compatibility commitments — consider dropping them until a caller needs them. (confidence: high)

@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.

@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.

Comment on lines +431 to +435
if !errors.Is(context.Cause(runCtx), context.DeadlineExceeded) {
return warnings, batchErr
}
checkpointErr := s.Checkpoint(ctx, true)
return warnings, errors.Join(checkpointErr, ErrSyncNotComplete)

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.

🟡 Suggestion (low confidence): when runCtx is deadline-exceeded, a genuine action error carried in batchErr is silently discarded in favor of ErrSyncNotComplete. This only bites in the race where the run duration expires at the same moment a worker hits a real (non-cancellation) error, and resume-then-retry limits the blast radius, but the original error is neither logged nor surfaced. Consider logging batchErr here so a connector bug isn't masked by an expiry that happened to land concurrently.

@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 force-pushed the kans/type-scoped branch from 1ef6a09 to c9a27cf Compare July 23, 2026 00:39

@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.

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