You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(.vapi-ignore): honor patterns on push and apply, not just pull (#26)
`.vapi-ignore` is now bidirectional. Four wired pieces:
1. **Load-filter** — `src/resources.ts:loadResources` accepts
`{ ignorePatterns }`; matched ids emit `🚫 <id> (matched .vapi-ignore: <pattern>)`
and are filtered out before duplicate detection, validation, or any API call.
2. **Push wire** — `src/push.ts` reads
`const ignorePatterns = FORCE_DELETE ? [] : loadIgnorePatterns()` once and
passes it into every `loadResources` call. `--force` bypasses the
load-filter for deliberate overrides.
3. **Orphan-protect (CRITICAL DATA SAFETY)** — `src/delete.ts`
`findOrphanedResources` accepts `ignoredIds: Set<string>`; ids matched by
`.vapi-ignore` are excluded from the orphan list, and
`deleteOrphanedResources` emits a
`🚫 <type>/<id> retained (matched .vapi-ignore — orphan-protected)` line
for each retained id. **Orphan-protect ALWAYS honors the ignore list,
even under `--force`** — this prevents a `--force` push from silently
DELETE'ing a dashboard resource the repo has explicitly opted out of
managing.
4. **Ref-validate** — `src/validate.ts:validateNoIgnoredReferences` walks
each loaded resource's referenced ids and emits an `error`-severity
finding for any ref pointing at an ignored id; `--strict` push aborts
before any API call.
Docs updated (`AGENTS.md`, `docs/learnings/yaml-conventions.md`,
`docs/learnings/simulations.md`, `resources/.vapi-ignore.example`) to
describe the bidirectional semantics, the `--force` bypass and the
ref-validate rule. `improvements.md` gets a `[RESOLVED 2026-05-11]` entry
under #21.
Tests: `tests/vapi-ignore-push.test.ts` pins T1–T5 spawn-fixture
integration tests + in-process unit tests for each helper. All 128 tests
pass; biome + tsc clean.
Copy file name to clipboardExpand all lines: AGENTS.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,7 +10,7 @@ This project manages **Vapi voice agent configurations** as code. All resources
10
10
11
11
**Template-safe first run:** In a fresh clone, prefer `npm run pull -- <org> --bootstrap` to refresh `.vapi-state.<org>.json` and credential mappings without materializing the target org's resources into `resources/<org>/`. `npm run push -- <org>` will auto-run the same bootstrap sync when it detects empty or stale state for the resources being applied.
12
12
13
-
**Excluding resources from sync (`.vapi-ignore`):** To prevent specific resources from being pulled at all (e.g. assistants owned by another team or legacy resources you don't want to manage), create `resources/<org>/.vapi-ignore` with gitignore-style patterns. See `resources/.vapi-ignore.example` for syntax and examples. Ignored resources are silently skipped on every pull and never tracked in state — distinct from "locally deleted" which keeps an entry in state.
13
+
**Excluding resources from sync (`.vapi-ignore`):** To prevent specific resources from being touched in either direction (e.g. assistants owned by another team or legacy resources you don't want to manage), create `resources/<org>/.vapi-ignore` with gitignore-style patterns. See `resources/.vapi-ignore.example` for syntax and examples. The list is **bidirectional**: matched ids are skipped on pull (never written), on push and `apply` (never sent), and orphan-protected (a `--force` push will not DELETE a dashboard resource whose id matches the ignore). `--force` on push bypasses the load-filter so a deliberate override can flow through, but orphan-protect still applies. A resource that references an ignored resource (e.g. a squad pointing at `assistants/foo` while `assistants/foo` is ignored) is a validation ERROR — `--strict` push aborts before any API call.
14
14
15
15
**Learnings & recipes:** Before configuring resources or debugging issues, read the relevant file in **`docs/learnings/`**. Load only what you need:
Copy file name to clipboardExpand all lines: docs/learnings/simulations.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -356,7 +356,7 @@ Returns the org's voice-simulation concurrency budget:
356
356
357
357
**Why it matters for gitops:** in a freshly-scaffolded org (no local `simulations/personalities/` directory), the engine sees these 7 personalities as orphans on every push — "exist on the dashboard, not in local files." Without `--force` this is just log noise. With `--force`, the push halts on the FIRST delete attempt against an immortal default and exits non-zero, before reaching any legitimate orphans you actually wanted to clean.
358
358
359
-
**Also note:** `.vapi-ignore` does **not** suppress these from the "pending deletions" warning. `matchesIgnore` is only called during pull operations (`src/pull.ts:695`), not during the push-time deletion-detection sweep. Adding `simulations/personalities/**` to `.vapi-ignore` quiets future pulls from materializing the defaults locally, but the push warning persists.
359
+
**Also note:** `.vapi-ignore` is now **bidirectional** (symmetric on pull, push, and orphan-detect). Adding `simulations/personalities/**` to `.vapi-ignore` quiets future pulls from materializing the defaults locally AND orphan-protects them: ignored ids are excluded from the "pending deletions" sweep and a `🚫 personalities/<id> retained (matched .vapi-ignore — orphan-protected)` line is emitted instead. `--force` does NOT override this — the orphan-protect always wins. A resource that references an ignored resource is a hard validation error; `--strict` push aborts before any API call.
360
360
361
361
**Recommendations:**
362
362
1. **Skip `--force` against fresh orgs** that haven't had their stock fixtures touched. The pending-deletions warning is harmless without `--force`.
Copy file name to clipboardExpand all lines: docs/learnings/yaml-conventions.md
+8Lines changed: 8 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -187,6 +187,14 @@ The blank line after `---` is conventional; the strict requirement is just that
187
187
188
188
`.vapi-ignore`lives at `resources/<org>/.vapi-ignore` and excludes specific resources from pull and push so the dashboard stays the source of truth for them. See `AGENTS.md` (line 13) for the basic gitignore-style syntax.
189
189
190
+
The list is **bidirectional**:
191
+
192
+
- **Pull** skips matched ids (never writes them to disk, never tracks them in state).
193
+
- **Push** (and `apply`) skips matched ids in the load pass — they are filtered out before drift detection, validation, or any API call.
194
+
- **Orphan-detect** during push honors the list: a `--force` push will NOT silently DELETE a dashboard resource whose id matches the ignore, even if the state file maps it. Operators see a `🚫 <type>/<id> retained (matched .vapi-ignore — orphan-protected)` line so the retention is visible.
195
+
- **`--force`** on push bypasses the load-filter so a deliberate override can flow through (mirrors pull's `--force`), but the orphan-protect still applies — there is no `--force` escape hatch for the delete path.
196
+
- **References to ignored resources are a hard validation error.** A squad pointing at `assistants/foo` while `assistants/foo` is in `.vapi-ignore` produces an `❌ squads/<id> references assistants/foo, which is in .vapi-ignore` finding. `--strict` push aborts before any API call.
197
+
190
198
The recovery flow when a sync surfaces "drift" you didn't expect — typically prompted by "was that not in the .vapi-ignore?":
191
199
192
200
1. **Inspect first**, don't edit. Diff the file against `main` to see whether the path was already ignored:
0 commit comments