Skip to content

Push and pull commands#1

Merged
vtkovapi merged 4 commits intomainfrom
vitali/push-and-pull-commands
Jan 16, 2026
Merged

Push and pull commands#1
vtkovapi merged 4 commits intomainfrom
vitali/push-and-pull-commands

Conversation

@vtkovapi
Copy link
Copy Markdown
Collaborator

No description provided.

@vtkovapi vtkovapi merged commit d33df8b into main Jan 16, 2026
@vtkovapi vtkovapi deleted the vitali/push-and-pull-commands branch January 16, 2026 02:04
dhruva-reddy added a commit that referenced this pull request May 2, 2026
**Problem.** Today: you pull, your teammate edits the same assistant
on the dashboard during a live test, you push your unrelated branch,
and their dashboard edit disappears with no warning. Customer-success
reps update business hours via the dashboard; the next gitops push
silently reverts them. Even `git revert + push` rollbacks have the
same problem — they overwrite whatever's currently live, not just the
change being reverted. The engine had no way to detect this because
the state file only stored name→UUID, no record of the platform's
content at last pull.

**What this fix does.** Now that Stack F populates `lastPulledHash`,
drift detection becomes possible. Before each PATCH, the engine GETs
the current platform payload, hashes it, and compares to the
`lastPulledHash` in state.

  - Hashes match → continue silently.
  - Hashes differ + no flag → **refuse the push**, point at the
    drift, ask the operator to either pull-and-resolve or pass
    `--overwrite` to take ownership.
  - Hashes differ + `--overwrite` → log "overwriting drift" and
    proceed.
  - No baseline (legacy state, first push after Stack F) → log
    "drift unknown — proceeding" and don't block.

Also adds a specific helper for the **Cartesia voice picker**
footgun: if `pronunciationDictId` was set at last pull but isn't on
the platform now, surface that explicitly so the operator notices.

**Outcome you'll notice.** Concurrent dashboard edits no longer
disappear silently. If someone else touched a resource between your
pull and your push, you see the conflict at push time and have to
make an explicit call (overwrite, or pull and resolve). The engine
becomes a real safety rail rather than a blind PATCH machine.

---

Before each PATCH, GET the current platform payload, hash it, and
compare to the lastPulledHash recorded in state (Stack F). If the
hashes differ, the dashboard has drifted away from the version we last
pulled — refuse to push without --overwrite.

Behavior matrix:
- No lastPulledHash (legacy state, first push after Stack F): log
  "drift unknown — proceeding" and continue. Don't block.
- Hashes match: continue silently.
- Hashes differ + no --overwrite: refuse the push, return null.
- Hashes differ + --overwrite: log "overwriting drift" and continue.

Files:
- src/drift.ts (NEW): checkDriftForUpdate(endpoint, state, overwrite).
  GETs platform, strips server-managed fields (id/orgId/createdAt/etc)
  to align hash basis with cleanResource()'s output, sha256 compares.
  Returns DriftCheckResult with reason and message for caller logging.
- src/state-serialize.ts: checkPronunciationDictDrop helper for the
  Cartesia voice-picker case (improvements.md #7) — pure data, safe
  to import in tests.
- src/config.ts: --overwrite flag.
- src/push.ts: drift gate in upsertResourceWithStateRecovery before
  every PATCH. Skipped in dry-run (operator wants to see what would
  happen). Skipped if no baseline.
- tests/drift.test.ts: hash-match → ok, hash-differ-no-overwrite → ok=false,
  hash-differ-overwrite → ok=true, no-baseline → ok=true.

Closes improvements.md #1, #7. Partial #2 (push side caught; pull side
same-file conflict still requires manual resolution).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---

## Update — 11labs `pronunciationDictionaryLocators` array also covered

`checkPronunciationDictDrop` now detects drops in both pronunciation-
dictionary shapes Vapi exposes:

- **11labs** (the documented shape):
  `voice.pronunciationDictionaryLocators[]` — array of
  `{ pronunciationDictionaryId, versionId }`. We warn on N → M shrinks
  (M < N) including N → 0 and array-going-missing.
- **Cartesia** (passthrough — not in Vapi docs but observed):
  `voice.pronunciationDictId` — single string id. Existing 1 → 0
  detection unchanged.

Reference: https://docs.vapi.ai/assistants/pronunciation-dictionaries

Six new test cases pin the 11labs behavior: array clear (1 → 0), shrink
(2 → 1), array-going-missing entirely, no-op when unchanged, no-op when
locators are added (additive growth shouldn't warn), and the defensive
hybrid case where a payload carries both shapes.
dhruva-reddy added a commit that referenced this pull request May 2, 2026
**Problem.** Today: you pull, your teammate edits the same assistant
on the dashboard during a live test, you push your unrelated branch,
and their dashboard edit disappears with no warning. Customer-success
reps update business hours via the dashboard; the next gitops push
silently reverts them. Even `git revert + push` rollbacks have the
same problem — they overwrite whatever's currently live, not just the
change being reverted. The engine had no way to detect this because
the state file only stored name→UUID, no record of the platform's
content at last pull.

**What this fix does.** Now that Stack F populates `lastPulledHash`,
drift detection becomes possible. Before each PATCH, the engine GETs
the current platform payload, hashes it, and compares to the
`lastPulledHash` in state.

  - Hashes match → continue silently.
  - Hashes differ + no flag → **refuse the push**, point at the
    drift, ask the operator to either pull-and-resolve or pass
    `--overwrite` to take ownership.
  - Hashes differ + `--overwrite` → log "overwriting drift" and
    proceed.
  - No baseline (legacy state, first push after Stack F) → log
    "drift unknown — proceeding" and don't block.

Also adds a specific helper for the **Cartesia voice picker**
footgun: if `pronunciationDictId` was set at last pull but isn't on
the platform now, surface that explicitly so the operator notices.

**Outcome you'll notice.** Concurrent dashboard edits no longer
disappear silently. If someone else touched a resource between your
pull and your push, you see the conflict at push time and have to
make an explicit call (overwrite, or pull and resolve). The engine
becomes a real safety rail rather than a blind PATCH machine.

---

Before each PATCH, GET the current platform payload, hash it, and
compare to the lastPulledHash recorded in state (Stack F). If the
hashes differ, the dashboard has drifted away from the version we last
pulled — refuse to push without --overwrite.

Behavior matrix:
- No lastPulledHash (legacy state, first push after Stack F): log
  "drift unknown — proceeding" and continue. Don't block.
- Hashes match: continue silently.
- Hashes differ + no --overwrite: refuse the push, return null.
- Hashes differ + --overwrite: log "overwriting drift" and continue.

Files:
- src/drift.ts (NEW): checkDriftForUpdate(endpoint, state, overwrite).
  GETs platform, strips server-managed fields (id/orgId/createdAt/etc)
  to align hash basis with cleanResource()'s output, sha256 compares.
  Returns DriftCheckResult with reason and message for caller logging.
- src/state-serialize.ts: checkPronunciationDictDrop helper for the
  Cartesia voice-picker case (improvements.md #7) — pure data, safe
  to import in tests.
- src/config.ts: --overwrite flag.
- src/push.ts: drift gate in upsertResourceWithStateRecovery before
  every PATCH. Skipped in dry-run (operator wants to see what would
  happen). Skipped if no baseline.
- tests/drift.test.ts: hash-match → ok, hash-differ-no-overwrite → ok=false,
  hash-differ-overwrite → ok=true, no-baseline → ok=true.

Closes improvements.md #1, #7. Partial #2 (push side caught; pull side
same-file conflict still requires manual resolution).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---

## Update — 11labs `pronunciationDictionaryLocators` array also covered

`checkPronunciationDictDrop` now detects drops in both pronunciation-
dictionary shapes Vapi exposes:

- **11labs** (the documented shape):
  `voice.pronunciationDictionaryLocators[]` — array of
  `{ pronunciationDictionaryId, versionId }`. We warn on N → M shrinks
  (M < N) including N → 0 and array-going-missing.
- **Cartesia** (passthrough — not in Vapi docs but observed):
  `voice.pronunciationDictId` — single string id. Existing 1 → 0
  detection unchanged.

Reference: https://docs.vapi.ai/assistants/pronunciation-dictionaries

Six new test cases pin the 11labs behavior: array clear (1 → 0), shrink
(2 → 1), array-going-missing entirely, no-op when unchanged, no-op when
locators are added (additive growth shouldn't warn), and the defensive
hybrid case where a payload carries both shapes.
dhruva-reddy added a commit that referenced this pull request May 2, 2026
**Problem.** Today: you pull, your teammate edits the same assistant
on the dashboard during a live test, you push your unrelated branch,
and their dashboard edit disappears with no warning. Customer-success
reps update business hours via the dashboard; the next gitops push
silently reverts them. Even `git revert + push` rollbacks have the
same problem — they overwrite whatever's currently live, not just the
change being reverted. The engine had no way to detect this because
the state file only stored name→UUID, no record of the platform's
content at last pull.

**What this fix does.** Now that Stack F populates `lastPulledHash`,
drift detection becomes possible. Before each PATCH, the engine GETs
the current platform payload, hashes it, and compares to the
`lastPulledHash` in state.

  - Hashes match → continue silently.
  - Hashes differ + no flag → **refuse the push**, point at the
    drift, ask the operator to either pull-and-resolve or pass
    `--overwrite` to take ownership.
  - Hashes differ + `--overwrite` → log "overwriting drift" and
    proceed.
  - No baseline (legacy state, first push after Stack F) → log
    "drift unknown — proceeding" and don't block.

Also adds a specific helper for the **Cartesia voice picker**
footgun: if `pronunciationDictId` was set at last pull but isn't on
the platform now, surface that explicitly so the operator notices.

**Outcome you'll notice.** Concurrent dashboard edits no longer
disappear silently. If someone else touched a resource between your
pull and your push, you see the conflict at push time and have to
make an explicit call (overwrite, or pull and resolve). The engine
becomes a real safety rail rather than a blind PATCH machine.

---

Before each PATCH, GET the current platform payload, hash it, and
compare to the lastPulledHash recorded in state (Stack F). If the
hashes differ, the dashboard has drifted away from the version we last
pulled — refuse to push without --overwrite.

Behavior matrix:
- No lastPulledHash (legacy state, first push after Stack F): log
  "drift unknown — proceeding" and continue. Don't block.
- Hashes match: continue silently.
- Hashes differ + no --overwrite: refuse the push, return null.
- Hashes differ + --overwrite: log "overwriting drift" and continue.

Files:
- src/drift.ts (NEW): checkDriftForUpdate(endpoint, state, overwrite).
  GETs platform, strips server-managed fields (id/orgId/createdAt/etc)
  to align hash basis with cleanResource()'s output, sha256 compares.
  Returns DriftCheckResult with reason and message for caller logging.
- src/state-serialize.ts: checkPronunciationDictDrop helper for the
  Cartesia voice-picker case (improvements.md #7) — pure data, safe
  to import in tests.
- src/config.ts: --overwrite flag.
- src/push.ts: drift gate in upsertResourceWithStateRecovery before
  every PATCH. Skipped in dry-run (operator wants to see what would
  happen). Skipped if no baseline.
- tests/drift.test.ts: hash-match → ok, hash-differ-no-overwrite → ok=false,
  hash-differ-overwrite → ok=true, no-baseline → ok=true.

Closes improvements.md #1, #7. Partial #2 (push side caught; pull side
same-file conflict still requires manual resolution).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---

## Update — 11labs `pronunciationDictionaryLocators` array also covered

`checkPronunciationDictDrop` now detects drops in both pronunciation-
dictionary shapes Vapi exposes:

- **11labs** (the documented shape):
  `voice.pronunciationDictionaryLocators[]` — array of
  `{ pronunciationDictionaryId, versionId }`. We warn on N → M shrinks
  (M < N) including N → 0 and array-going-missing.
- **Cartesia** (passthrough — not in Vapi docs but observed):
  `voice.pronunciationDictId` — single string id. Existing 1 → 0
  detection unchanged.

Reference: https://docs.vapi.ai/assistants/pronunciation-dictionaries

Six new test cases pin the 11labs behavior: array clear (1 → 0), shrink
(2 → 1), array-going-missing entirely, no-op when unchanged, no-op when
locators are added (additive growth shouldn't warn), and the defensive
hybrid case where a payload carries both shapes.
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