feat: add ETag support for optimistic concurrency control#59
Merged
hmans merged 1 commit intohmans:mainfrom Jan 17, 2026
Merged
feat: add ETag support for optimistic concurrency control#59hmans merged 1 commit intohmans:mainfrom
hmans merged 1 commit intohmans:mainfrom
Conversation
- Add ETag() method to Bean using FNV-1a 64-bit hash of rendered content - Include etag field in JSON output via custom MarshalJSON - Add etag field to GraphQL schema (Bean type) - Add ifMatch parameter to GraphQL mutations (updateBean, setParent, addBlocking, removeBlocking) - Add --if-match flag to beans update command for optimistic locking - Add --etag-only flag to beans show for easy etag extraction - Add typed errors (ETagMismatchError, ETagRequiredError) with ErrConflict code - Add require_if_match config option to enforce ETag usage - Add comprehensive tests for ETag functionality ETag enables concurrent modification detection: ETAG=$(beans show <id> --etag-only) beans update <id> --status completed --if-match "$ETAG" On conflict, returns CONFLICT error with current etag.
This was referenced Jan 16, 2026
hmans
approved these changes
Jan 17, 2026
Owner
|
Thank you! 🙏 |
hmans
added a commit
to divaltor/beans
that referenced
this pull request
Jan 17, 2026
* origin/main: feat(tui): Two-column layout with detail preview (hmans#42) feat: add ETag support for optimistic concurrency control (hmans#59) feat(plugin): improve OpenCode plugin robustness with availability checks (hmans#58) feat(cli): Add --prefix flag to create command (hmans#56) chore: clean up README.md docs: only push prime if it exists (hmans#52) fix: normalise short IDs when storing relationship links (hmans#50)
Contributor
Author
|
Thanks. I wanted to note that there is still a race condition between etag check and the actual writing of the new data. To remove that race condition would require larger refactoring - I didn't want to convolute this PR. |
hmans
added a commit
that referenced
this pull request
Jan 19, 2026
## Summary Adds CLI flags and GraphQL mutations for partial body modifications, enabling agents to update bean content without direct file access. Builds on #59 (ETag support) and supersedes #57. ## Changes ### CLI: Body Modification Flags - `--body-replace-old` / `--body-replace-new` for exact text replacement (must match exactly once) - `--body-append` for appending content (supports stdin with `-`) - Mutual exclusivity with existing `--body`/`--body-file` flags ### GraphQL: Partial Body Mutations - `replaceInBody(id, old, new, ifMatch)` - replace exactly one occurrence of text - `appendToBody(id, content, ifMatch)` - append content with blank line separator - Both support `ifMatch` for optimistic locking ### Shared Logic - Extract `ReplaceOnce` and `AppendWithSeparator` to `internal/bean/content.go` - Reused by both CLI and GraphQL resolvers ### Documentation - Add GraphQL API section to README with examples ## Usage ```bash # CLI: Check off a task beans update <id> --body-replace-old "- [ ] Task" --body-replace-new "- [x] Task" # CLI: Append notes beans update <id> --body-append "## Notes\n\nSome notes" # GraphQL: Check off a task beans query 'mutation { replaceInBody(id: "bean-xxx", old: "- [ ] Task", new: "- [x] Task") { body } }' # GraphQL: Append content beans query 'mutation { appendToBody(id: "bean-xxx", content: "## Notes") { body } }' ``` ## Why This Matters for Agents Agents can now modify bean bodies entirely through CLI/GraphQL without needing: - Direct file access to the `.beans/` directory - Knowledge of where beans are stored (which can be configured outside the repo) - Special file permissions ## Testing - Comprehensive tests for shared logic in `internal/bean/content_test.go` - GraphQL resolver tests for both mutations - CLI tests updated for new behavior --------- Co-authored-by: Hendrik Mans <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds ETag-based optimistic concurrency control to prevent lost updates when multiple agents work on the same bean concurrently.
Supersedes #57 (this PR extracts just the ETag feature; body modification will be a separate PR).
Changes
ETag()method to Bean using FNV-1a 64-bit hash of rendered contentetagfield in JSON output and GraphQL schema--if-matchflag tobeans updatefor optimistic locking--etag-onlyflag tobeans showfor easy etag extractionifMatchparameter to GraphQL mutationsETagMismatchError,ETagRequiredError) withCONFLICTcoderequire_if_matchconfig option to enforce ETag usageUsage