Skip to content

fix(cli): stabilize delegated credentials#294

Merged
khaliqgant merged 4 commits into
mainfrom
pear/workforce-e3c9d114
Jun 17, 2026
Merged

fix(cli): stabilize delegated credentials#294
khaliqgant merged 4 commits into
mainfrom
pear/workforce-e3c9d114

Conversation

@khaliqgant

@khaliqgant khaliqgant commented Jun 17, 2026

Copy link
Copy Markdown
Member

Summary

  • quarantine legacy relayfile cloud credential helpers behind explicit legacy names
  • store delegated relayfile bundles by workspace and normalized scope set, with legacy global-file fallback
  • serialize delegated refresh across processes and fall back to Agent Relay cloud re-mint when token rotation cannot run
  • preserve workspace catalog default scopes when command-specific delegated refreshes run

Fixes #291

Validation

  • npm ci
  • go test ./cmd/relayfile-cli ./internal/mountsync ./internal/delegatedauth
  • go test ./cmd/relayfile-cli
  • GOOS=windows GOARCH=amd64 go test -c ./cmd/relayfile-cli -o /private/tmp/relayfile-cli-windows.test.exe
  • go test ./...

Notes

  • No OpenAPI contract changes; HTTP server surface was not changed.
  • .agentworkforce trajectory output remains untracked locally.

Review in cubic

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces several new subcommands to the CLI, including writeback push for uploading local files, writeback skip-stuck to bypass unreadable events, and workspace status to report workspace health. It also refactors delegated credentials storage to be workspace- and scope-specific, and adds a cloud re-minting fallback for expired tokens. Two critical issues were identified in the review: first, the direct use of syscall.Flock will break compilation on Windows, requiring a cross-platform file locking solution; second, a race condition exists in SkipStuck where releasing the mutex before calling s.sync could allow concurrent operations to run with skipStuckMode enabled, potentially polluting the syncer's state.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread cmd/relayfile-cli/main.go Outdated
Comment on lines +6506 to +6509
if err := syscall.Flock(int(file.Fd()), syscall.LOCK_EX); err != nil {
return err
}
defer syscall.Flock(int(file.Fd()), syscall.LOCK_UN)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Using syscall.Flock directly will cause compilation to fail on Windows because syscall.Flock, syscall.LOCK_EX, and syscall.LOCK_UN are not defined on Windows. Since this CLI is intended to support Windows (as evidenced by other platform-specific path handling and comments), you should use a cross-platform file locking library such as github.com/gofrs/flock, or split the locking implementation into platform-specific files using build tags (e.g., lock_unix.go and lock_windows.go).

Comment on lines +1554 to +1571
func (s *Syncer) SkipStuck(ctx context.Context, max int) (int, error) {
s.mu.Lock()
s.skipStuckMode = true
s.skipStuckMax = max
s.skipStuckCount = 0
s.mu.Unlock()
defer func() {
s.mu.Lock()
s.skipStuckMode = false
s.skipStuckMax = 0
s.mu.Unlock()
}()
err := s.sync(ctx, true)
s.mu.Lock()
count := s.skipStuckCount
s.mu.Unlock()
return count, err
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

There is a race condition and state pollution risk in SkipStuck. The mutex s.mu is unlocked before calling s.sync(ctx, true) to avoid a deadlock (since s.sync locks s.mu internally). However, during this unlocked window, another concurrent goroutine could call Reconcile or another method that triggers s.sync, which would then run with s.skipStuckMode = true and pollute the state.\n\nTo resolve this, consider refactoring s.sync into an unexported s.syncLocked method that assumes s.mu is already held. Then, s.sync can simply lock s.mu and call s.syncLocked. This allows SkipStuck to keep s.mu locked for the entire duration of the operation, ensuring atomicity and preventing concurrent state pollution.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f054d2b15b

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread cmd/relayfile-cli/main.go
Comment on lines +6412 to +6413
if strings.TrimSpace(workspaceValue) != "" || len(scopes) > 0 {
return delegatedCredentialsPathForRequest(workspaceValue, scopes), false

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Use the resolved default workspace for scoped credential paths

When callers omit --workspace but pass any scope set (for example mount passes defaultJoinScopes), this branch hashes the literal empty/active workspace key. Login/setup/join save delegated bundles with delegatedCredentialsPathForRequest(record.ID, ...), so a normal no-arg relayfile mount after login looks in a different path and fails with missing delegated credentials instead of using the active/default workspace. Resolve the empty workspace value to the active/default workspace ID before deriving the scoped credential path.

Useful? React with 👍 / 👎.

Comment thread cmd/relayfile-cli/main.go
CorrelationID: commandID,
ContentIdentity: identity,
}
if err := writeWritebackPushReceipt(resolved.MountRoot, pendingReceipt); err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve the same dedupe identity for pending push receipts

This writes a pending outbox receipt before the direct /fs/bulk call finishes. A running mount daemon can read that pending file while the command is waiting for the writeback op, but internal/mountsync/outbox.go drops the receipt's contentIdentity and re-sends it as a mount-command keyed by commandId, while the direct push uses mount-writeback-create-draft keyed by workspace/path/hash. If the daemon flushes during the wait, the server sees two different dedupe keys and can create duplicate provider drafts/tickets; either keep the pending record private until retry is needed or have mountsync preserve this same content identity.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in e16f30f. The durable-outbox flush (outboxRecordsAsBulkFiles) now derives the mount-writeback-create-draft content identity (workspace:path:hash) for draft paths via outboxRecordContentIdentity, matching both the in-flight bulkWriteFilesForPending path and the CLI direct push — so a daemon flush of a pending push receipt collides with the direct push instead of minting a second idempotency key. Non-draft mount commands keep the stable commandId identity. Also fixes a latent inconsistency where a daemon-originated draft used the draft identity on first attempt but commandId on persisted-outbox retry. Updated TestBulkWrite_FlushSendsContentIdentity (it had encoded the buggy mount-command assertion for a draft path) and added TestOutboxRecordContentIdentityDraftMatchesDirectPush.

@khaliqgant khaliqgant force-pushed the pear/workforce-e3c9d114 branch from f054d2b to 6f0fb19 Compare June 17, 2026 09:59
@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@khaliqgant, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 35 minutes and 40 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: da5b792d-b7ef-4f22-8e57-e1af9b8dc2c6

📥 Commits

Reviewing files that changed from the base of the PR and between 6f0fb19 and e16f30f.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • cmd/relayfile-cli/filelock_other.go
  • cmd/relayfile-cli/filelock_unix.go
  • cmd/relayfile-cli/filelock_windows.go
  • cmd/relayfile-cli/main.go
  • cmd/relayfile-cli/main_test.go
  • internal/mountsync/syncer.go
  • internal/mountsync/syncer_test.go
📝 Walkthrough

Walkthrough

The PR adds three new CLI subcommands (writeback push, writeback skip-stuck, workspace status), reworks delegated-credential storage to request-scoped paths with scope validation and cloud remint fallback, and extends the mountsync.Syncer with stuck-event drain controls (SkipStuck, stale alias auto-drain, StaleAliasSkips/BacklogDraining observability methods) plus expanded WriteResult receipt fields.

Changes

Syncer stuck-event infrastructure, scoped delegated credentials, writeback push, and workspace status

Layer / File(s) Summary
Syncer: WriteResult expansion, stuck-event fields, public methods, 404 drain, tests
internal/mountsync/syncer.go, internal/mountsync/syncer_test.go
WriteResult gains OpID, Status, and nested Writeback fields. Syncer adds staleAliasSkips, skip-stuck mode state, and three new public methods (StaleAliasSkips, BacklogDraining, SkipStuck). isMountWritebackCreateDraftPath predicate added. Incremental pull 404 handling extended: in skipStuckMode events are dropped immediately up to an optional cap; outside that mode, stale provider-layout alias paths are drained in the same cycle via isProviderLayoutAliasRemotePath. Two new integration tests cover stale-alias draining and SkipStuck event dropping.
Delegated credentials: scoped paths, loading, scope matching, refresh/remint, call sites
cmd/relayfile-cli/main.go, cmd/relayfile-cli/main_test.go
delegatedCredentialsPathForRequest derives per-(workspace, scope-set) credential file paths. loadDelegatedCredentialsForRequest loads from the scoped path, with legacy fallback only when scopes are compatible (JWT claim parsing + wildcard/path matching). bootstrapDelegatedCredentialsFromAgentRelay now returns and persists the scoped path. refreshDelegatedCredentials adds locking and a cloud remint fallback. All call sites (setup, join, integration connect, mount, login) updated to use scoped paths. loadLegacyCloudCredentials rename applied. Tests cover scoped load, bootstrap, remint fallback, scope preservation, and refresh rotation.
Writeback push + skip-stuck: types, receipt lifecycle, implementation, tests
cmd/relayfile-cli/main.go, cmd/relayfile-cli/main_test.go
bulkWriteFile gains ContentIdentity; bulkWriteResponse/bulkWriteResult gain contentIdentity and writeback.state/provider fields. New types writebackPushResolvedPath, writebackPushReceipt, withoutBody. runWritebackPush encodes file content, detects content type, hashes, ensures delegated credentials, posts bulk FS write, and manages pending → acked/failed receipt lifecycle with op-status polling. runWritebackSkipStuck calls syncer.SkipStuck and reports results. runWriteback dispatch updated. Integration test asserts token mint, bulk write payload, op-status poll, receipt redaction, and draft-path rejection.
Workspace status subcommand, data shapes, mount-loop stuck logging, help
cmd/relayfile-cli/main.go, cmd/relayfile-cli/main_test.go
syncStateFile expanded with additional status/timing/daemon fields; statusError added. runWorkspaceStatus and workspaceHealthReport added for JSON/text local-mount health output. runWorkspace routes status. logStuckEventSummary added and called from runMountLoop after the initial cycle. CLI help strings updated. Status rendering test verifies output fields.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(70, 130, 180, 0.5)
    note over CLI,BulkWriteAPI: relayfile writeback push LOCAL_PATH
  end
  participant CLI
  participant loadDelegatedCredentialsForRequest
  participant bootstrapDelegatedCredentialsFromAgentRelay
  participant BulkWriteAPI
  participant OpStatusAPI

  CLI->>loadDelegatedCredentialsForRequest: workspace + scopes
  alt no scoped bundle
    loadDelegatedCredentialsForRequest->>bootstrapDelegatedCredentialsFromAgentRelay: bootstrap from agent-relay
    bootstrapDelegatedCredentialsFromAgentRelay-->>loadDelegatedCredentialsForRequest: scoped creds path + bundle
  end
  loadDelegatedCredentialsForRequest-->>CLI: delegated bundle
  CLI->>CLI: encode file, detect content type, SHA256 hash, compute contentIdentity
  CLI->>CLI: write pending receipt
  CLI->>BulkWriteAPI: POST bulk FS write (content + contentIdentity)
  alt bulk write error
    BulkWriteAPI-->>CLI: error
    CLI->>CLI: write failed receipt (body dropped)
  else bulk write success + opId
    BulkWriteAPI-->>CLI: opId
    loop poll until terminal
      CLI->>OpStatusAPI: GET op status
      OpStatusAPI-->>CLI: status
    end
    CLI->>CLI: write acked receipt (body dropped) or needsAttention
  end
Loading
sequenceDiagram
  rect rgba(100, 160, 100, 0.5)
    note over CLI,Syncer: relayfile writeback skip-stuck
  end
  participant CLI
  participant runWritebackSkipStuck
  participant Syncer

  CLI->>runWritebackSkipStuck: max (optional)
  runWritebackSkipStuck->>Syncer: SkipStuck(ctx, max)
  Syncer->>Syncer: set skipStuckMode, run reconcile
  loop unreadable 404 events up to max
    Syncer->>Syncer: drop event, advance incremental checkpoint
  end
  Syncer-->>runWritebackSkipStuck: skipped count
  runWritebackSkipStuck->>Syncer: BacklogDraining()
  Syncer-->>runWritebackSkipStuck: draining bool
  runWritebackSkipStuck-->>CLI: report skipped / backlog / error
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • AgentWorkforce/relayfile#274: Directly modifies the same delegated-relayfile credential lifecycle (mint/refresh/renew, persisted delegated token loading for mount/CLI operations) and the same delegated-auth + CLI/mount codepaths that this PR reworks for request-scoped storage.

Poem

🐰 A rabbit hops through mounted files,
Skipping stuck events by miles,
Scoped credentials, fresh and bright,
Push receipts sealed up just right,
The workspace status sings with cheer—
Health reports now crystal clear! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(cli): stabilize delegated credentials' directly addresses the main change—stabilizing delegated credentials handling in the CLI with request-scoped persistence, scope validation, and cross-process serialization.
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.
Description check ✅ Passed The PR description directly relates to the changeset, outlining key changes to delegated credentials, workspace scope handling, and legacy credential helpers that align with the file modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pear/workforce-e3c9d114

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 and usage tips.

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/relayfile-cli/main.go (1)

1680-1703: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve catalog scopes when minting command-scoped bundles.

This uses the requested mint scopes as record.Scopes, then persists them via persistDelegatedWorkspace. A writeback push bootstrap with provider-specific scopes can overwrite the workspace’s default catalog scopes, so later default refreshes may lose the original fs:read/fs:write set.

Proposed direction
-	recordScopes := append([]string(nil), scopes...)
-	if len(recordScopes) == 0 {
-		recordScopes = append([]string(nil), defaultJoinScopes...)
+	mintScopes := append([]string(nil), scopes...)
+	if len(mintScopes) == 0 {
+		mintScopes = append([]string(nil), defaultJoinScopes...)
+	}
+	recordScopes := append([]string(nil), mintScopes...)
+	if existing, ok := workspaceRecordByID(cloudWorkspaceID); ok && len(existing.Scopes) > 0 {
+		recordScopes = append([]string(nil), existing.Scopes...)
+	} else if relayID := strings.TrimSpace(workspace.RelayfileWorkspaceID); relayID != "" {
+		if existing, ok := workspaceRecordByID(relayID); ok && len(existing.Scopes) > 0 {
+			recordScopes = append([]string(nil), existing.Scopes...)
+		}
 	}
@@
-	delegated, err := delegatedRelayfileTokenViaCloud(cloudCreds, cloudWorkspaceID, record.AgentName, record.Scopes)
+	delegated, err := delegatedRelayfileTokenViaCloud(cloudCreds, cloudWorkspaceID, record.AgentName, mintScopes)
@@
-	credsPath := delegatedCredentialsPathForRequest(record.ID, record.Scopes)
+	credsPath := delegatedCredentialsPathForRequest(record.ID, mintScopes)
🤖 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 `@cmd/relayfile-cli/main.go` around lines 1680 - 1703, The current
implementation uses the command-specific scopes (from the mint request) to set
record.Scopes, which gets persisted via persistDelegatedWorkspace, causing the
original catalog scopes like fs:read/fs:write to be overwritten and lost in
later default refreshes. To fix this, preserve the original default catalog
scopes by keeping them in record.Scopes instead of replacing them with the
command-specific scopes, and pass the command-scoped scopes as a separate
parameter to persistDelegatedWorkspace so the function can handle both the
persistent catalog scopes and the command-specific scopes appropriately.
🤖 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 `@cmd/relayfile-cli/main.go`:
- Around line 6408-6415: In the resolveDelegatedCredentialsPathForRequest
function, resolve the active workspace to its concrete workspace name before
checking for scoped credentials. Currently, if workspaceValue is "active" or
empty, it gets passed literally to delegatedCredentialsPathForRequest, causing
the function to miss the correct scoped credentials file that was saved under
the concrete workspace key. Add a workspace resolution step (likely by calling a
function that converts "active" to the actual workspace name) before the
condition that checks if workspaceValue is not empty or scopes exist, ensuring
scoped credential lookup uses the resolved workspace name instead of the literal
"active" value.
- Around line 3788-3798: The code currently returns record.ID for workspace
identification, but for delegated workspaces this can be the Cloud workspace ID
rather than the Relayfile runtime workspace ID. In both the
workspaceRecordByName and workspaceRecordByID branches, modify the return
statements to prioritize returning local.RelayWorkspaceID when it is available
(not empty), and only fall back to using local.ID if RelayWorkspaceID is empty.
This ensures the correct workspace ID is returned to consumers like writeback
skip-stuck that need to instantiate the syncer against the proper workspace.
- Around line 4655-4672: The readLocalMountCursorHealth function returns
immediately after finding and parsing the first readable file, which prevents
aggregation of health data from multiple locations. Instead of returning early
from the loop when a valid state file is found, collect the stuck count and
backlog status from all readable files at different paths, then return the
maximum stuck count across all files and the OR of all backlog flags to ensure
no stuck backlog state is hidden by an older file in an earlier location.
- Around line 3182-3200: The delegatedBundleHasScopes function uses exact string
comparison (strings.TrimSpace(got) == want) which fails to handle wildcard
patterns in scopes. Replace the exact string equality check in the inner for
loop with a call to a wildcard-aware scope matcher function that can properly
evaluate whether a scope like relayfile:fs:write:/** satisfies a requirement for
relayfile:fs:write:/github/**. This will prevent unnecessary re-minting and
allow writeback push operations when the bundle has a sufficiently broad scope
grant.
- Around line 3491-3502: The skip-stuck functionality currently requires legacy
credentials.json through the loadCredentials call, which causes delegated-only
users (who use Agent Relay login without credentials.json) to fail before
SkipStuck can run. Modify the code block starting with loadCredentials() to
support delegated credentials as an alternative path: instead of returning an
error when credentials are not found, implement fallback logic to resolve the
token and server through delegated authentication (Agent Relay), and only fail
if both the legacy credentials path and delegated path fail to provide the
required token. This allows users without credentials.json to successfully use
the skip-stuck command through their delegated Agent Relay login.

---

Outside diff comments:
In `@cmd/relayfile-cli/main.go`:
- Around line 1680-1703: The current implementation uses the command-specific
scopes (from the mint request) to set record.Scopes, which gets persisted via
persistDelegatedWorkspace, causing the original catalog scopes like
fs:read/fs:write to be overwritten and lost in later default refreshes. To fix
this, preserve the original default catalog scopes by keeping them in
record.Scopes instead of replacing them with the command-specific scopes, and
pass the command-scoped scopes as a separate parameter to
persistDelegatedWorkspace so the function can handle both the persistent catalog
scopes and the command-specific scopes appropriately.
🪄 Autofix (Beta)

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3e389bb2-a447-49e4-9f12-e6358176b863

📥 Commits

Reviewing files that changed from the base of the PR and between 558ae63 and 6f0fb19.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • cmd/relayfile-cli/main.go
  • cmd/relayfile-cli/main_test.go
  • internal/mountsync/syncer.go
  • internal/mountsync/syncer_test.go

Comment thread cmd/relayfile-cli/main.go
Comment thread cmd/relayfile-cli/main.go Outdated
Comment thread cmd/relayfile-cli/main.go
Comment thread cmd/relayfile-cli/main.go
Comment thread cmd/relayfile-cli/main.go
@khaliqgant khaliqgant force-pushed the pear/workforce-e3c9d114 branch from 6f0fb19 to c501079 Compare June 17, 2026 11:06
@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown

Relayfile Eval Review

Run: .relayfile/evals/runs/2026-06-17T11-32-49-515Z-HEAD-provider
Mode: provider
Git SHA: ef4436c

Passed: 4 | Needs human: 0 | Reviewable: 0 | Missing output: 0 | Failed: 0 | Skipped: 0

Human Review Cases

No reviewable human-review cases captured Relayfile output.

…ntity

A direct `relayfile writeback push` deposits a pending receipt into the
shared `.relay/outbox/pending/` dir before its synchronous bulk write +
op-status poll completes. A running mount daemon can flush that pending
receipt during the wait. The durable-outbox flush keyed every record on the
per-record commandId (`mount-command`), while the direct push — and the
in-flight `bulkWriteFilesForPending` path — key draft creates on
(workspace, path, content hash) (`mount-writeback-create-draft`). The two
different idempotency keys let the server mint duplicate provider
drafts/tickets for identical content.

Make `outboxRecordsAsBulkFiles` derive the create-draft identity for draft
paths via `outboxRecordContentIdentity`, matching the in-flight path and the
CLI direct push. Non-draft mount commands keep the stable commandId identity.

This also closes a latent inconsistency: a daemon-originated draft write used
the create-draft identity on its first attempt but switched to the commandId
identity on persisted-outbox retry.

Addresses Codex P1 review on PR #294.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@khaliqgant

Copy link
Copy Markdown
Member Author

Review triage (commit e16f30f)

No merge conflicts — the branch is up to date with main and git merge-tree reports zero conflicts. Walked every open review finding:

Fixed now

  • Codex P1 — pending push receipt dedupe (main.go:3067): the durable-outbox flush keyed draft creates on the per-record commandId while the direct push / in-flight path key them on workspace:path:hash, risking duplicate provider drafts when a mount daemon flushes a pending receipt mid-push. outboxRecordsAsBulkFiles now uses the mount-writeback-create-draft identity for draft paths (outboxRecordContentIdentity). Also closes a latent inconsistency in the daemon's own draft retries. Test added + the test that had encoded the buggy assertion updated.

Already addressed in earlier commits on this branch (verified against current code)

  • Gemini (high) — syscall.Flock Windows break → resolved via build-tagged filelock_{unix,windows,other}.go; GOOS=windows build passes.
  • Gemini (high) — SkipStuck race → guarded by the syncActive mutual-exclusion reservation; a concurrent sync returns "sync already in progress" rather than running with skipStuckMode set.
  • Codex P1 / CodeRabbit — no-arg scoped credential path hashing literal activeresolveDelegatedCredentialsWorkspaceValue resolves empty/active to the active workspace before hashing.
  • CodeRabbit — wildcard-aware scope matcher → delegatedBundleHasScopes uses scopeSetAllows.
  • CodeRabbit — skip-stuck requiring legacy credentials.json → now routes through prepareWorkspaceCommandClient (delegated creds).
  • CodeRabbit — return Relayfile runtime workspace ID → resolveWorkspaceLikeStatus returns firstNonBlank(RelayWorkspaceID, ID).
  • CodeRabbit — aggregate cursor-health files → readLocalMountCursorHealth maxes stuck count and ORs the backlog flag across both state files.

Validation: go test ./internal/mountsync ./cmd/relayfile-cli ./internal/delegatedauth green; GOOS=windows GOARCH=amd64 go build ./cmd/relayfile-cli OK.

@khaliqgant khaliqgant merged commit 64de0d7 into main Jun 17, 2026
9 checks passed
@khaliqgant khaliqgant deleted the pear/workforce-e3c9d114 branch June 17, 2026 11:35
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.

Delegated-creds churn: disjoint stores (login resets the other), no auto-refresh despite valid refresh token, fs:read/fs:write scope clobber

1 participant