···
652
+
### Implement PutRecord in PDS Client
653
+
**Added:** 2025-12-04 | **Effort:** 2-3 hours | **Priority:** Technical Debt
657
+
The PDS client (`internal/atproto/pds/client.go`) only has `CreateRecord` but lacks `PutRecord`. This means updates use `CreateRecord` with an existing rkey, which:
658
+
1. Loses optimistic locking (no CID swap check)
659
+
2. Is semantically incorrect (creates vs updates)
660
+
3. Could cause race conditions on concurrent updates
662
+
**atProto Best Practice:**
663
+
- `com.atproto.repo.putRecord` should be used for updates
664
+
- Accepts `swapRecord` (expected CID) for optimistic locking
665
+
- Returns conflict error if CID doesn't match (concurrent modification detected)
668
+
Add `PutRecord` method to the PDS client interface:
671
+
// Client interface addition
672
+
type Client interface {
673
+
// ... existing methods ...
675
+
// PutRecord creates or updates a record with optional optimistic locking.
676
+
// If swapRecord is provided, the operation fails if the current CID doesn't match.
677
+
PutRecord(ctx context.Context, collection string, rkey string, record any, swapRecord string) (uri string, cid string, err error)
681
+
func (c *client) PutRecord(ctx context.Context, collection string, rkey string, record any, swapRecord string) (string, string, error) {
682
+
payload := map[string]any{
684
+
"collection": collection,
689
+
// Optional: optimistic locking via CID swap check
690
+
if swapRecord != "" {
691
+
payload["swapRecord"] = swapRecord
694
+
var result struct {
695
+
URI string `json:"uri"`
696
+
CID string `json:"cid"`
699
+
err := c.apiClient.Post(ctx, syntax.NSID("com.atproto.repo.putRecord"), payload, &result)
701
+
return "", "", wrapAPIError(err, "putRecord")
704
+
return result.URI, result.CID, nil
708
+
**Error Handling:**
709
+
Add new error type for conflict detection:
711
+
var ErrConflict = errors.New("record was modified by another operation")
714
+
Map HTTP 409 in `wrapAPIError`:
717
+
return fmt.Errorf("%s: %w: %s", operation, ErrConflict, apiErr.Message)
720
+
**Files to Modify:**
721
+
- `internal/atproto/pds/client.go` - Add `PutRecord` method and interface
722
+
- `internal/atproto/pds/errors.go` - Add `ErrConflict` error type
725
+
- Unit test: Verify payload includes `swapRecord` when provided
726
+
- Integration test: Concurrent updates detect conflict
727
+
- Integration test: Update without `swapRecord` still works (backwards compatible)
729
+
**Blocked By:** Nothing
730
+
**Blocks:** "Migrate UpdateComment to use PutRecord"
734
+
### Migrate UpdateComment to Use PutRecord
735
+
**Added:** 2025-12-04 | **Effort:** 1 hour | **Priority:** Technical Debt
736
+
**Status:** 📋 TODO (Blocked)
737
+
**Blocked By:** "Implement PutRecord in PDS Client"
740
+
`UpdateComment` in `internal/core/comments/comment_service.go` uses `CreateRecord` for updates instead of `PutRecord`. This lacks optimistic locking and is semantically incorrect.
742
+
**Current Code (lines 687-690):**
744
+
// TODO: Use PutRecord instead of CreateRecord for proper update semantics with optimistic locking.
745
+
// PutRecord should accept the existing CID (existingRecord.CID) to ensure concurrent updates are detected.
746
+
// However, PutRecord is not yet implemented in internal/atproto/pds/client.go.
747
+
uri, cid, err := pdsClient.CreateRecord(ctx, commentCollection, rkey, updatedRecord)
751
+
Once `PutRecord` is implemented in the PDS client, update to:
753
+
// Use PutRecord with optimistic locking via existing CID
754
+
uri, cid, err := pdsClient.PutRecord(ctx, commentCollection, rkey, updatedRecord, existingRecord.CID)
756
+
if errors.Is(err, pds.ErrConflict) {
757
+
// Record was modified by another operation - return appropriate error
758
+
return nil, fmt.Errorf("comment was modified, please refresh and try again: %w", err)
760
+
// ... existing error handling
764
+
**Files to Modify:**
765
+
- `internal/core/comments/comment_service.go` - UpdateComment method
766
+
- `internal/core/comments/errors.go` - Add `ErrConcurrentModification` if needed
769
+
- Unit test: Verify `PutRecord` is called with correct CID
770
+
- Integration test: Simulate concurrent update, verify conflict handling
772
+
**Impact:** Proper optimistic locking prevents lost updates from race conditions
### Consolidate Environment Variable Validation
**Added:** 2025-10-11 | **Effort:** 2-3 hours