Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RESOLVER.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
| "recoup-internal" + account-health / account-status snapshot for a Recoup account | `recoup-internal-account-health-report` |
| "recoup-internal" + audit scheduled-task email health (what tasks emailed, how many empties the guard blocked) over a window | `recoup-internal-task-email-audit` |
| "recoup-internal" + draft/ship/measure LinkedIn or X posts | `recoup-internal-social-ship-posts` |
| "recoup-internal" + FaceTime-call style AI video ad (Grok Imagine 1.5 native-speech clips) | `recoup-internal-video-grok-1.5-imagine-facetime` |

---

Expand Down
1 change: 1 addition & 0 deletions resolver-eval.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
{"intent": "recoup-internal work the valuation funnel and advance this Attio lead", "expected": "recoup-internal-funnel-valuation-pipeline"}
{"intent": "recoup-internal account health snapshot for this account", "expected": "recoup-internal-account-health-report"}
{"intent": "recoup-internal draft and ship a launch post for LinkedIn and X", "expected": "recoup-internal-social-ship-posts"}
{"intent": "recoup-internal make a FaceTime-call style AI video ad with Grok Imagine", "expected": "recoup-internal-video-grok-1.5-imagine-facetime", "not": ["recoup-content-make-video"]}

# --- gating invariant: WITHOUT the recoup-internal keyword, internal skills must NOT fire ---
# Only the internal skills with a plausible customer-facing collision get a negative; the dev/eval
Expand Down
3 changes: 2 additions & 1 deletion scripts/check_resolvable.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
DEFAULT_PLUGIN = REPO_ROOT

# A skill reference in RESOLVER.md is a backtick-wrapped slug: `recoup-foo-bar`.
SKILL_TOKEN = re.compile(r"`(recoup-[a-z0-9-]+)`")
# Dots are allowed mid-slug (e.g. `recoup-internal-video-grok-1.5-imagine-facetime`).
SKILL_TOKEN = re.compile(r"`(recoup-[a-z0-9-]+(?:\.[a-z0-9-]+)*)`")


def skill_dirs(plugin: Path) -> set[str]:
Expand Down
3 changes: 2 additions & 1 deletion scripts/run_resolver_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
# Co-located with the plugin root (NOT under tests/, which is gitignored) so the
# fixtures ship and CI can run them on a fresh checkout.
FIXTURES = PLUGIN / "resolver-eval.jsonl"
SKILL_TOKEN = re.compile(r"`(recoup-[a-z0-9-]+)`")
# Dots are allowed mid-slug (e.g. `recoup-internal-video-grok-1.5-imagine-facetime`).
SKILL_TOKEN = re.compile(r"`(recoup-[a-z0-9-]+(?:\.[a-z0-9-]+)*)`")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The LLM tier of the resolver eval will always fail for the recoup-internal-video-grok-1.5-imagine-facetime skill fixture: SKILL_TOKEN_PLAIN (line 86) only matches [a-z0-9-] characters, so a dot-containing slug like recoup-internal-video-grok-1.5-imagine-facetime gets truncated at the dot to recoup-internal-video-grok-1, which doesn't match any real skill directory. The model could reply with the exact correct slug and _picked_skill would still return "none", causing a false mis-route failure.

Update SKILL_TOKEN_PLAIN to also allow dots, matching the same pattern as the updated SKILL_TOKEN but without the surrounding backticks.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/run_resolver_eval.py, line 52:

<comment>The LLM tier of the resolver eval will always fail for the `recoup-internal-video-grok-1.5-imagine-facetime` skill fixture: `SKILL_TOKEN_PLAIN` (line 86) only matches `[a-z0-9-]` characters, so a dot-containing slug like `recoup-internal-video-grok-1.5-imagine-facetime` gets truncated at the dot to `recoup-internal-video-grok-1`, which doesn't match any real skill directory. The model could reply with the exact correct slug and `_picked_skill` would still return `"none"`, causing a false mis-route failure.

Update `SKILL_TOKEN_PLAIN` to also allow dots, matching the same pattern as the updated `SKILL_TOKEN` but without the surrounding backticks.</comment>

<file context>
@@ -48,7 +48,8 @@
 FIXTURES = PLUGIN / "resolver-eval.jsonl"
-SKILL_TOKEN = re.compile(r"`(recoup-[a-z0-9-]+)`")
+# Dots are allowed mid-slug (e.g. `recoup-internal-video-grok-1.5-imagine-facetime`).
+SKILL_TOKEN = re.compile(r"`(recoup-[a-z0-9-]+(?:\.[a-z0-9-]+)*)`")
 
 
</file context>



def skill_dirs() -> set[str]:
Expand Down
6 changes: 4 additions & 2 deletions skills/recoup-internal-social-ship-posts/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Authenticate to the Recoup API with an `x-api-key` (`RECOUP_API_KEY`) or `Author
## The loop

1. **Learn** — pull how recent posts performed, so copy is grounded in evidence, not vibes.
2. **Draft** — write topic-first copy that fits the platform and the goal.
2. **Draft** — write topic-first copy that fits the platform, the goal, and the account's story canon.
3. **Decide the CTA** — comment-gate for leads, or direct link for reach.
4. **Publish** — via the connector where supported, or hand off for manual posting.
5. **Log & re-measure** — record what went out; re-pull performance ~48h later.
Expand Down Expand Up @@ -65,6 +65,8 @@ Read the top performer against the flatliners and name the differences (framing,

## Step 2 — Draft

**Check the story canon first.** If the account workspace has a `NARRATIVE.md` (story bible: premise, recurring cast, storylines), read it and name **which arc and which character this post serves** before writing a word. A post that serves no arc is an ad — reframe it into the story or drop it. Serialized rules live in the canon itself (recurring catchphrases, who may be fictionalized vs. who gets only verified numbers, cliffhanger beats); follow them over generic copy instincts. If the workspace has no `NARRATIVE.md`, note that to the account owner — the account is posting without a story.

Comment on lines +68 to +69

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Keep the skill self-contained.

These steps now require reading NARRATIVE.md from the account workspace, but skills/recoup-*/** is supposed to read/execute only files inside the skill’s own directory. Please move the canon input into a skill-local reference or reword the contract so it doesn’t depend on external workspace files.

As per coding guidelines: skills/recoup-*/** must be self-contained and read/execute only files inside their own directory.

Also applies to: 118-118

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/recoup-internal-social-ship-posts/SKILL.md` around lines 68 - 69, The
story-canon check in the skill currently depends on an external workspace file,
which breaks the self-contained contract for recoup skills. Update the guidance
around the canon lookup so it only references skill-local inputs or embedded
canon guidance within the skill itself, and adjust the instructions in the
affected section of SKILL.md so the workflow does not require reading
NARRATIVE.md from outside the skill directory.

Source: Coding guidelines

Write to the brief from step 1 plus the principles above. Structure that travels well:

```
Expand Down Expand Up @@ -113,7 +115,7 @@ Native video works, but the bytes can't go inline: `media` won't fetch a URL, an

## Step 5 — Log and re-measure

Record each post immediately: account, platform, the post id/URN, UTC time, the CTA used, and the copy. Then **re-pull performance ~48h later**: one bulk scrape (step 1) covers all four platforms; add `LINKEDIN_LIST_REACTIONS` only when you need reactor identities, and read gated posts' comment bodies manually. Compare week-over-week — report deltas, not all-time totals. Feed the result back into step 1 for the next post.
Record each post immediately: account, platform, the post id/URN, UTC time, the CTA used, the copy, and — when the account has a `NARRATIVE.md` — **the arc and character the post served** (continuity is what makes the next episode land; an unlogged arc breaks the serial). Then **re-pull performance ~48h later**: one bulk scrape (step 1) covers all four platforms; add `LINKEDIN_LIST_REACTIONS` only when you need reactor identities, and read gated posts' comment bodies manually. Compare week-over-week — report deltas, not all-time totals. Feed the result back into step 1 for the next post.

## Producing a demo video?

Expand Down
Loading