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
- src/dep-dedup.ts: drop "Gap #10" issue marker from the file header (it
rots; the rationale is what matters, not the tracker reference).
- docs/learnings/tools.md: new section "Renaming a tool file is safe — the
engine dedups by `function.name`" — explains the auto-apply dedup safety
net, the 🔁 / ⚠️ log line semantics, and the cleanup path. Counterpart in
docs/learnings/structured-outputs.md cross-references it.
- AGENTS.md: add `outbound-campaigns.md` to the Learnings & recipes table
(was missing); refresh the docs/learnings/ tree in the Project Structure
section to be complete; add an explicit "Where new knowledge goes" table
pinning the convention (per-resource tips → docs/learnings/<topic>.md;
engine-friction log → improvements.md; rationale → code comments;
onboarding → README.md).
- CLAUDE.md: sync the Required Reading Order list with AGENTS.md's table
(was missing voice-providers, outbound-agents, outbound-campaigns,
voicemail-detection); add a brief "Where new knowledge goes" reminder
pointing back at AGENTS.md as the canonical convention table.
No source behavior changes. Build clean, 114/114 tests pass.
| Per-resource gotchas, recipes, troubleshooting |`docs/learnings/<topic>.md`| One file per resource type or topic. Add a row to this table AND to `docs/learnings/README.md` when you add a new file. `CLAUDE.md` mirrors this list — keep both in sync. |
43
+
| Engine-friction log (push/pull/state/cleanup pain points + fixes) |`improvements.md`| Format: Problem → Current behavior → Risk → Current mitigation → Possible fix → Status. Mark `[RESOLVED YYYY-MM-DD] (#<PR>)` when fixed; never delete. |
44
+
| Code-level rationale (why a function works the way it does) | Code comments | Only when the WHY is non-obvious — not what the code does. Don't reference PR/issue numbers; they rot. |
This list mirrors the "Learnings & recipes" table in `AGENTS.md`. Keep both in sync — if you add a new learnings file, update both files plus `docs/learnings/README.md`.
35
+
36
+
## Where new knowledge goes
37
+
38
+
Per-resource tips/recipes/troubleshooting → `docs/learnings/<topic>.md`. Engine-friction log (push/pull/state/cleanup pain points + their fixes) → `improvements.md`. Code-level rationale → comments only when the *why* is non-obvious; never reference PR/issue numbers in code comments (they rot). One-time onboarding/install → `README.md`. When unsure, default to `docs/learnings/`. The full convention table lives in `AGENTS.md` under "Where new knowledge goes" — read it once, then this reminder is enough.
39
+
30
40
## Improvements log
31
41
32
42
This repo maintains an upstream-only running log at `improvements.md` (repo
Copy file name to clipboardExpand all lines: docs/learnings/structured-outputs.md
+4Lines changed: 4 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -32,6 +32,10 @@ evaluations.5.structuredOutput.Name must be between 1 and 40 characters
32
32
33
33
Long, descriptive evaluator names like `assistant_left_voicemail_and_ended_call_promptly` (48 chars) or `assistant_detected_hostile_recording_and_ended_call` (51 chars) will silently exceed the limit until you POST. Keep names compact (`assistant_ended_call_after_message`, `assistant_handled_hostile_recording`) and put the descriptive nuance in the `description` field, which has no length cap. The constraint applies to the field on every structured output type — both standalone resources and inline evaluations within scenarios.
34
34
35
+
### Renaming a structured-output file is safe — the engine dedups by `name`
36
+
37
+
Same dedup behavior as for tools: if you rename a structured-output file but keep its `name` field stable, the push pipeline detects the existing dashboard resource (by slugified `name` against state and the live dashboard list) and adopts its UUID instead of creating a duplicate. You'll see `🔁 Reusing existing structured output: <localKey> → <uuid>` in the push log. See [tools.md → "Renaming a tool file is safe"](tools.md#renaming-a-tool-file-is-safe--the-engine-dedups-by-functionname) for the full mechanism, ambiguity warning semantics, and `npm run cleanup` workflow — they're identical for SOs.
Copy file name to clipboardExpand all lines: docs/learnings/tools.md
+20Lines changed: 20 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -39,6 +39,26 @@ Vapi enforces a hard **1000-character maximum** on `function.description` across
39
39
40
40
Tool names are validated against this regex by Vapi. Spaces, dots, slashes, parentheses, or unicode characters cause a 400 at push time. Use snake_case or camelCase (e.g. `end_call_vapi_testing`, `handoffToiFormSales`). The name is what the LLM emits in its function call, so keep it stable across config changes — renaming a tool invalidates any prompt rule that mentions the old name.
41
41
42
+
### Renaming a tool file is safe — the engine dedups by `function.name`
43
+
44
+
The push pipeline includes a name-based dedup safety net that prevents minting duplicate dashboard tools when:
45
+
46
+
- You renamed the local file (e.g. `end-call.yml` → `b2b-invoice-end-call.yml`) but kept `function.name` the same.
47
+
- Bootstrap pull stored the dashboard tool under a slug-suffixed state key (e.g. `end-call-67aea057`) and your assistant references the original local key.
48
+
- The tool exists on the dashboard but isn't yet in your local state file (e.g. fresh clone, partial pull).
49
+
50
+
In all three cases the engine looks up the tool by slugified `function.name` against both state entries and the live dashboard tool list, then **adopts** the existing UUID instead of creating a new one. You'll see this log line:
51
+
52
+
```
53
+
🔁 Reusing existing tool: <localKey> → <uuid> (matched via state|dashboard|both)
54
+
```
55
+
56
+
Adoption then routes through the standard PATCH path, so any local edits to the tool's payload are pushed normally with drift detection. Your old state-key entries are dropped automatically so the next full push doesn't orphan-delete the just-adopted dashboard tool.
57
+
58
+
**When you see `⚠️ Multiple dashboard tools share the name "<n>" — adopting <uuid> (lex-smallest)`**, real duplicate dashboard resources exist (typically from before the dedup was added). Run `npm run cleanup -- <org>` to inspect and prune; the engine adopts the lex-smallest UUID deterministically so subsequent pushes stay stable.
59
+
60
+
**What this does NOT do:** if you rename `function.name` (not just the file), that's a new logical tool — the engine creates a new dashboard resource. Function-name renames need an explicit `npm run cleanup` of the old one.
0 commit comments