Skip to content

Make the leadtype skill activate for docs authoring and review - #158

Open
KayleeWilliams wants to merge 3 commits into
mainfrom
dx/150-skill-activation
Open

Make the leadtype skill activate for docs authoring and review#158
KayleeWilliams wants to merge 3 commits into
mainfrom
dx/150-skill-activation

Conversation

@KayleeWilliams

@KayleeWilliams KayleeWilliams commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Closes #150. First of a 7-PR stack for #157.

A skill's frontmatter description is the entire activation signal — a client reads name + description from the discovery manifest and decides whether to load the body. Both the repo skill and the generated docs-skill described retrieval only ("read and search the docs"), so the most common documentation task there is — writing, editing, reviewing, restructuring the pages — routed elsewhere.

What changed

  • The repo skill description names the authoring verbs, and an Activation section documents that repository signals (a leadtype dependency, docs.config.ts / leadtype.config.ts, Leadtype components or frontmatter, generated artifacts) are sufficient when the prompt never says "Leadtype".
  • A Writing or editing docs route in the skill body: establish the project, read the smallest relevant topic, match sibling-page conventions, update config-owned navigation, verify with lint + generate, inspect the flattened markdown when agent readability is at stake.
  • The generated docs-skill description broadens the same way, and the AGENTS.md pointer leadtype init writes is aligned with it.
  • docs/reference/skills.mdx gains a "When does a skill activate?" section — the two rules that carry most of the benefit, and why this is routing rather than distribution.

Activation evals

The deterministic half runs in CI (evals/lib/activation.test.ts): the shipped description must name the authoring verbs and the repository signals, and stay under the 1024-character discovery cap. That catches a narrowed description without spending a token.

The model-driven half (bun run evals:activation) routes 17 labelled prompts and reports precision and recall. The negatives matter as much as the positives: two are docs-shaped prompts in a project with no leadtype signal, and two are non-docs work inside a leadtype repo — so a description that wins by activating on repository signal alone scores as overreach.

A skill's frontmatter description is the entire activation signal: a client
reads name + description from the discovery manifest and decides whether to
load the body. Both the repo skill and the generated docs-skill described
retrieval only ("read and search the docs"), so the most common documentation
task there is — writing, editing, reviewing, restructuring the pages — routed
elsewhere.

- Expand the repo skill description to name the authoring verbs, and document
  that repository signals (a `leadtype` dependency, `docs.config.ts` /
  `leadtype.config.ts`, Leadtype components/frontmatter, generated artifacts)
  are sufficient context when the prompt never says "Leadtype".
- Add an authoring route to the skill body: establish the project, read the
  smallest relevant topic, match sibling-page conventions, update config-owned
  navigation, verify with lint + generate, inspect the flattened markdown when
  agent readability is at stake.
- Broaden the generated docs-skill description the same way, and align the
  AGENTS.md pointer `leadtype init` writes.
- Add activation evals. The deterministic half runs in CI: the shipped
  description must name the authoring verbs and repository signals and stay
  under the 1024-character discovery cap. The model-driven half
  (`evals:activation`) routes 17 labelled prompts and reports precision and
  recall — including negatives that are docs-shaped but in a project with no
  leadtype signal, and non-docs work inside a leadtype repo, so activating on
  repository signal alone scores as overreach.
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Expanded documentation assistance for writing, editing, reviewing, restructuring, and maintaining Markdown/MDX content.
    • Improved automatic skill activation based on project context and documentation tasks.
    • Updated generated project guidance to help agents discover bundled documentation workflows.
  • Documentation

    • Added guidance for effective skill descriptions, activation criteria, navigation updates, and generated documentation review.
  • Tests

    • Added activation scenarios and evaluations covering skill selection, recall, precision, validation, and unrelated projects.
    • Added continuous integration checks for activation evaluation coverage.

Walkthrough

The change expands Leadtype documentation skill activation guidance and generated descriptions. It adds labelled activation cases, frontmatter and scoring libraries, validation tests, package scripts, and a concurrent model-routing evaluator with JSON result output.

Changes

Skill activation

Layer / File(s) Summary
Activation guidance and generated skill output
.agents/skills/leadtype/SKILL.md, docs/reference/skills.mdx, packages/leadtype/src/llm/skills.ts, packages/leadtype/src/cli/init.ts, packages/leadtype/src/llm/skills.test.ts, evals/EVAL-AREAS.md, .changeset/skill-activation-authoring.md, docs/paths.lock.json
The skill and generated discovery metadata now cover documentation writing, editing, reviewing, maintenance, and restructuring. Documentation explains description-based activation and its validation rules.
Activation cases and scoring library
evals/lib/activation.ts, evals/activation/cases.json, evals/lib/activation.test.ts
The evaluation library parses skill frontmatter, validates activation signals and cases, and calculates accuracy, recall, precision, false positives, and false negatives.
Concurrent model evaluation runner
evals/run-activation-eval.ts, evals/package.json, .github/workflows/ci.yml
The evaluator validates CLI options, runs concurrent retried model decisions, reports aggregate and per-model metrics, writes detailed JSON results, and runs deterministic checks in CI.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant EvalRunner as evals/run-activation-eval.ts
  participant RepoSkill as Repository skill
  participant Cases as Activation cases
  participant Model as Evaluation model
  participant Scorer as scoreActivation
  participant Results as JSON results

  EvalRunner->>RepoSkill: Load skill frontmatter
  EvalRunner->>Cases: Load labelled profiles and prompts
  EvalRunner->>Model: Send routing prompts
  Model-->>EvalRunner: Return activate or skip
  EvalRunner->>Scorer: Score collected outcomes
  Scorer-->>EvalRunner: Return accuracy, recall, and precision
  EvalRunner->>Results: Write metrics and case decisions
Loading

Poem

I’m a rabbit with docs in my den,
Now writing and editing guide every pen.
Cases hop in, scores measure the way,
Models choose activate or skip each day.
JSON carrots record what they say.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the main change: enabling Leadtype skill activation for documentation authoring and review.
Description check ✅ Passed The description directly explains the activation problem, implementation, aligned discovery surfaces, and evaluation coverage.
Linked Issues check ✅ Passed The changes satisfy issue #150 by expanding activation signals, adding authoring guidance, aligning discovery surfaces, and adding positive and negative evaluations.
Out of Scope Changes check ✅ Passed The documentation, evaluation, package, generated-surface, and CI changes all support the linked issue objectives.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dx/150-skill-activation

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Prompt for all review comments with 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.

Inline comments:
In @.agents/skills/leadtype/SKILL.md:
- Line 41: Update the “Establish the project” step to preserve the
workspace-documentation fallback from the earlier instructions: when
node_modules/leadtype is absent, use the workspace package metadata or
documentation source instead of requiring node_modules/leadtype/package.json.
Keep the installed-package version check as the preferred path when that file
exists.

In `@docs/reference/skills.mdx`:
- Line 43: Define a single discovery-input contract for activation evaluation:
update docs/reference/skills.mdx at line 43 to state that both skill.name and
skill.description inform routing if the name is part of the client contract, and
update evals/run-activation-eval.ts lines 84-106 to omit skill.name when
measuring description-only activation.

In `@evals/run-activation-eval.ts`:
- Around line 159-167: Update the ActivationOutcome construction and type in the
evaluation flow to retain cell.model and cell.run alongside case, decision, and
correct. Ensure these fields remain present when outcomes are serialized to
activation JSON, including every entry grouped through byModel.
- Around line 54-59: Update parsePositiveInt to require
Number.isSafeInteger(Number(value)) and enforce the explicit MAX_RUNS and
MAX_CONCURRENCY limits for their respective flags before evaluation cells are
created, rejecting values outside those bounds while preserving the existing
positive-integer validation and error behavior.
- Around line 109-111: Update parseDecision to trim the response and accept only
exact, case-insensitive ACTIVATE or SKIP values, throwing a descriptive error
for all other inputs instead of inferring a decision. Add regression coverage
for invalid text and mixed responses such as “SKIP, not ACTIVATE,” while
preserving valid decision handling.

In `@packages/leadtype/src/llm/skills.test.ts`:
- Around line 115-121: Update the discovery JSON handling in the test around
index and index.skills[0] to parse the file as unknown, validate that it
contains a skills array with a valid first entry including name and description,
and only then read description. Replace the unchecked type assertion with an
explicit narrowing step that fails clearly for malformed discovery output.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a723f1e4-30e4-49df-bedd-bfbbe5ee15a3

📥 Commits

Reviewing files that changed from the base of the PR and between b5857b4 and ecd4eba.

📒 Files selected for processing (13)
  • .agents/skills/leadtype/SKILL.md
  • .changeset/skill-activation-authoring.md
  • docs/paths.lock.json
  • docs/reference/skills.mdx
  • evals/EVAL-AREAS.md
  • evals/activation/cases.json
  • evals/lib/activation.test.ts
  • evals/lib/activation.ts
  • evals/package.json
  • evals/run-activation-eval.ts
  • packages/leadtype/src/cli/init.ts
  • packages/leadtype/src/llm/skills.test.ts
  • packages/leadtype/src/llm/skills.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: pullfrog
⚠️ CI failures not shown inline (2)

GitHub Actions: CI / 0_Validate & test.txt: Make the leadtype skill activate for docs authoring and review

Conclusion: failure

View job details

##[group]Run bun run --filter leadtype test
 �[36;1mbun run --filter leadtype test�[0m
 shell: /usr/bin/bash -e {0}
 ##[endgroup]
 leadtype test:
 leadtype test: �[1m�[30m�[46m RUN �[49m�[39m�[22m �[36mv4.1.5 �[39m�[90m/home/runner/work/leadtype/leadtype/packages/leadtype�[39m
 leadtype test:
 leadtype test:  �[32m✓�[39m src/llm/llm.test.ts �[2m(�[22m�[2m86 tests�[22m�[2m)�[22m�[33m 328�[2mms�[22m�[39m
 leadtype test:  �[32m✓�[39m src/openapi/openapi.test.ts �[2m(�[22m�[2m34 tests�[22m�[2m)�[22m�[33m 423�[2mms�[22m�[39m
 leadtype test:  �[32m✓�[39m src/lint/lint.test.ts �[2m(�[22m�[2m45 tests�[22m�[2m)�[22m�[33m 1185�[2mms�[22m�[39m
 leadtype test:      �[33m�[2m✓�[22m�[39m warns on rendered components with no flattener but not on code-block examples �[33m 338�[2mms�[22m�[39m
 leadtype test:      �[33m�[2m✓�[22m�[39m discovers docs.config in --src, applies mounts and lint.rules �[33m 304�[2mms�[22m�[39m
 leadtype test: Converted 1 docs in 23 ms
 leadtype test: Converted 2 docs in 19 ms
 leadtype test: Converted 2 docs in 11 ms
 leadtype test: Pruned 1 orphaned .md file(s) from /tmp/leadtype-convert-mZnbHV/public
 leadtype test: Converted 2 docs in 3 ms
 leadtype test: Converted 1 docs in 2 ms
 leadtype test: Pruned 2 orphaned .md file(s) from /tmp/leadtype-convert-LzjUqm/public
 leadtype test: Converted 1 docs in 2 ms
 leadtype test: Pruned 1 orphaned .md file(s) from /tmp/leadtype-convert-kgTg6S/public
 leadtype test: Converted 1 docs in 2 ms
 leadtype test: Pruned 1 orphaned .md file(s) from /tmp/leadtype-convert-z6AEcy/public
 leadtype test: Converted 1 docs in 1 ms
 leadtype test: Error: failed to process /tmp/leadtype-convert-W04IKa/docs/broken.mdx: 3:1: Unexpected character after `<`, expected a valid JSX tag (note: to create a link in MDX, use `[text](url)`) (mdx-jsx:unexpected-character)
 leadtype test: Converted 1 docs in 2 ms (1 failed)
 leadtype test: Warning: prune skipped: 1 file(s) failed to convert, so the expected output set is incomplete.
 leadty...

GitHub Actions: CI / Validate & test: Make the leadtype skill activate for docs authoring and review

Conclusion: failure

View job details

##[group]Run bun run --filter leadtype test
 �[36;1mbun run --filter leadtype test�[0m
 shell: /usr/bin/bash -e {0}
 ##[endgroup]
 leadtype test:
 leadtype test: �[1m�[30m�[46m RUN �[49m�[39m�[22m �[36mv4.1.5 �[39m�[90m/home/runner/work/leadtype/leadtype/packages/leadtype�[39m
 leadtype test:
 leadtype test:  �[32m✓�[39m src/llm/llm.test.ts �[2m(�[22m�[2m86 tests�[22m�[2m)�[22m�[33m 328�[2mms�[22m�[39m
 leadtype test:  �[32m✓�[39m src/openapi/openapi.test.ts �[2m(�[22m�[2m34 tests�[22m�[2m)�[22m�[33m 423�[2mms�[22m�[39m
 leadtype test:  �[32m✓�[39m src/lint/lint.test.ts �[2m(�[22m�[2m45 tests�[22m�[2m)�[22m�[33m 1185�[2mms�[22m�[39m
 leadtype test:      �[33m�[2m✓�[22m�[39m warns on rendered components with no flattener but not on code-block examples �[33m 338�[2mms�[22m�[39m
 leadtype test:      �[33m�[2m✓�[22m�[39m discovers docs.config in --src, applies mounts and lint.rules �[33m 304�[2mms�[22m�[39m
 leadtype test: Converted 1 docs in 23 ms
 leadtype test: Converted 2 docs in 19 ms
 leadtype test: Converted 2 docs in 11 ms
 leadtype test: Pruned 1 orphaned .md file(s) from /tmp/leadtype-convert-mZnbHV/public
 leadtype test: Converted 2 docs in 3 ms
 leadtype test: Converted 1 docs in 2 ms
 leadtype test: Pruned 2 orphaned .md file(s) from /tmp/leadtype-convert-LzjUqm/public
 leadtype test: Converted 1 docs in 2 ms
 leadtype test: Pruned 1 orphaned .md file(s) from /tmp/leadtype-convert-kgTg6S/public
 leadtype test: Converted 1 docs in 2 ms
 leadtype test: Pruned 1 orphaned .md file(s) from /tmp/leadtype-convert-z6AEcy/public
 leadtype test: Converted 1 docs in 1 ms
 leadtype test: Error: failed to process /tmp/leadtype-convert-W04IKa/docs/broken.mdx: 3:1: Unexpected character after `<`, expected a valid JSX tag (note: to create a link in MDX, use `[text](url)`) (mdx-jsx:unexpected-character)
 leadtype test: Converted 1 docs in 2 ms (1 failed)
 leadtype test: Warning: prune skipped: 1 file(s) failed to convert, so the expected output set is incomplete.
 leadty...
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity
Prefer unknown over any when the type is genuinely unknown
Use const assertions (as const) for immutable values and literal types
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/leadtype/src/cli/init.ts
  • packages/leadtype/src/llm/skills.ts
  • evals/lib/activation.test.ts
  • packages/leadtype/src/llm/skills.test.ts
  • evals/run-activation-eval.ts
  • evals/lib/activation.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions
Prefer for...of loops over .forEach() and indexed for loops
Use optional chaining (?.) and nullish coalescing (??) for safer property access
Prefer template literals over string concatenation
Use destructuring for object and array assignments
Use const by default, let only when reassignment is needed, never var
Always await promises in async functions - don't forget to use the return value
Use async/await syntax instead of promise chains for better readability
Handle errors appropriately in async code with try-catch blocks
Don't use async functions as Promise executors
Remove console.log, debugger, and alert statements from production code
Throw Error objects with descriptive messages, not strings or other values
Use try-catch blocks meaningfully - don't catch errors just to rethrow them
Prefer early returns over nested conditionals for error cases
Extract complex conditions into well-named boolean variables
Use early returns to reduce nesting
Prefer simple conditionals over nested ternary operators
Don't use eval() or assign directly to document.cookie
Avoid spread syntax in accumulators within loops
Use top-level regex literals instead of creating them in loops
Prefer specific imports over namespace imports
Use descriptive names for functions, variables, and types for meaningful naming
Add comments for complex logic, but prefer self-documenting code

Files:

  • packages/leadtype/src/cli/init.ts
  • packages/leadtype/src/llm/skills.ts
  • evals/lib/activation.test.ts
  • packages/leadtype/src/llm/skills.test.ts
  • evals/run-activation-eval.ts
  • evals/lib/activation.ts
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{test,spec}.{js,ts,jsx,tsx}: Write assertions inside it() or test() blocks
Avoid done callbacks in async tests - use async/await instead
Don't use .only or .skip in committed code
Keep test suites reasonably flat - avoid excessive describe nesting

Files:

  • evals/lib/activation.test.ts
  • packages/leadtype/src/llm/skills.test.ts
🧠 Learnings (1)
📚 Learning: 2026-06-09T18:30:08.038Z
Learnt from: KayleeWilliams
Repo: inthhq/leadtype PR: 97
File: .changeset/search-prototype-safety-and-scaling.md:5-5
Timestamp: 2026-06-09T18:30:08.038Z
Learning: In this repo, `.changeset/*.md` files must not start the body with an H1/first-line heading (`#`) immediately after the YAML frontmatter. The changesets tool inlines the body as bullet entries into `CHANGELOG.md` during release, and a leading `#` heading would break the generated changelog format. As a result, MD041 (`first-line-heading`) warnings for files under `.changeset/` are expected false positives and should be ignored.

Applied to files:

  • .changeset/skill-activation-authoring.md
🪛 LanguageTool
.agents/skills/leadtype/SKILL.md

[uncategorized] ~54-~54: Did you mean the formatting language “Markdown” (= proper noun)?
Context: ...t components carried their content into markdown instead of vanishing, that internal lin...

(MARKDOWN_NNP)

🪛 markdownlint-cli2 (0.23.1)
.changeset/skill-activation-authoring.md

[warning] 5-5: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

🪛 OpenGrep (1.26.0)
evals/lib/activation.ts

[ERROR] 52-52: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)


[ERROR] 58-58: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)


[ERROR] 63-63: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🔇 Additional comments (12)
.agents/skills/leadtype/SKILL.md (3)

4-11: LGTM!


30-33: 🎯 Functional Correctness

Verify that generic markers preserve activation precision.

docs.config.ts, docs.config.js, group:, and related: are generic names. A non-Leadtype project can contain them. If any one marker activates the skill, unrelated documentation work can load Leadtype guidance. Require a Leadtype-specific marker, or verify a negative case with these names but no Leadtype dependency or artifact.


31-41: 🗄️ Data Integrity & Integration

Resolve the config that owns the target docs tree.

Line 31 accepts configs anywhere in the project. Line 41 only directs the agent to root or docs-directory configs. In a monorepo, this can select the wrong navigation and docs root. Locate the authoritative config before editing, or restrict the activation rule to supported locations.

packages/leadtype/src/llm/skills.ts (1)

70-74: LGTM!

packages/leadtype/src/cli/init.ts (1)

222-223: LGTM!

.changeset/skill-activation-authoring.md (1)

1-5: LGTM!

packages/leadtype/src/llm/skills.test.ts (1)

115-120: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Remove the duplicate JSON cast.

The code contains the same closing cast three times at Line 120. The extra statements make the test invalid TypeScript. Keep one cast statement.

Proposed fix
     )
-    ) as { skills: { name: string; description: string }[] };
-    ) as { skills: { name: string; description: string }[] };
-    ) as { skills: { name: string; description: string }[] };
+    ) as { skills: { name: string; description: string }[] };
			> Likely an incorrect or invalid review comment.
docs/paths.lock.json (1)

166-166: 🗄️ Data Integrity & Integration

Regenerate and compare the path lock hash.

docs/paths.lock.json stores the SHA-256 hash of the emitted Markdown body, not the raw MDX source. Confirm that generation produces 26835c94d375b0e1 for /docs/reference/skills; otherwise update the lockfile.

evals/lib/activation.ts (1)

1-206: LGTM!

evals/activation/cases.json (1)

1-137: LGTM!

evals/EVAL-AREAS.md (1)

45-65: LGTM!

evals/package.json (1)

14-15: LGTM!

Comment thread .agents/skills/leadtype/SKILL.md Outdated
Comment thread docs/reference/skills.mdx Outdated
Comment thread evals/run-activation-eval.ts Outdated
Comment thread evals/run-activation-eval.ts Outdated
Comment thread evals/run-activation-eval.ts
Comment thread packages/leadtype/src/llm/skills.test.ts Outdated

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Important

The deterministic guard this PR presents as its CI safety net does not run in CI. The evals workspace has no test, lint, or typecheck step in any workflow, so a future narrowing of the skill description would ship silently — which is exactly the regression the guard exists to catch.

Reviewed changes — full review of the single commit ecd4eba, covering all 13 files: the skill descriptions, the SKILL.md body additions, the init pointer, and the new activation eval surface.

  • Repo skill description widened.agents/skills/leadtype/SKILL.md frontmatter now names the authoring verbs (write/edit/review/restructure/maintain) and declares that repository signals (leadtype dependency, docs.config.ts / leadtype.config.ts) suffice when the prompt never says "Leadtype".
  • Three new SKILL.md body sections## Activation (the signal list, plus an explicit "absent every one of those signals, skip this skill"), a 6-step ## Writing or editing docs route, and ## Reviewing docs for agent readability.
  • Generated docs-skill description widenedbuildDocsSkill() in packages/leadtype/src/llm/skills.ts:74 goes from "Read and search…" to "Read, search, and maintain… and when writing, editing, reviewing, or restructuring its documentation." Guarded by a new skills.test.ts case that does run in CI.
  • leadtype init pointer aligned — the AGENTS.md block at packages/leadtype/src/cli/init.ts:222 now names the same verbs.
  • New activation evalevals/lib/activation.ts (frontmatter reader + scoreActivation precision/recall), evals/lib/activation.test.ts (12 deterministic guards), evals/activation/cases.json (3 project profiles, 17 labelled cases: 12 activate / 5 skip, including negatives that punish activating on repository signal alone), evals/run-activation-eval.ts, the evals:activation scripts, and section "3b" in evals/EVAL-AREAS.md.
  • Docs + changeset — a new "When does a skill activate?" section in docs/reference/skills.mdx, the corresponding docs/paths.lock.json hash, and a patch changeset.

Verified locally at ecd4eba: the 12 new eval tests pass (under both vitest and the root bun test the pre-commit hook uses), skills.test.ts passes 10/10, the evals workspace typechecks clean, and ultracite is clean on every changed source file. The 1024-character cap the new docs section describes is real and enforced (skills.ts:20 and skills.ts:252), so that claim is accurate.

ℹ️ CI is red at ecd4eba, but not because of this PR

Worth stating plainly so nobody burns time chasing it. The failure is src/cli.test.ts > leadtype CLI > cleans up mirrored sources when the generate pipeline fails, which trips on a leaked /tmp/leadtype-generate-<hash>.lock/ directory — a generate-lock.ts mkdir lock. 673 of 674 tests pass. Nothing in this diff touches generate, temp directories, or locking.

Technical details
# Unrelated pre-existing CI failure

## The actual failure (CI job 91845470496)
```
FAIL src/cli.test.ts > leadtype CLI > cleans up mirrored sources when the generate pipeline fails
AssertionError: expected [ Array(1) ] to deeply equal []
- []
+ [ "/tmp/leadtype-generate-24a4c3a2adcda87f.lock/" ]
  at src/cli.test.ts:2239  →  expect(leakedTempDirs).toEqual([])
```

## Why it is not attributable to this PR
- The diff touches skill descriptions, SKILL.md prose, `init.ts` string content, and the
  `evals` workspace. It does not touch `generate`, `convertAllMdx`, temp-dir handling, or
  `generate-lock.ts`.
- `leadtype lint docs` was captured in full on `b5857b4` and on `ecd4eba` and the finding
  sets are identical (51 each), so the PR introduces no new docs-lint findings.
- The leaked path is a lock directory, not a mirrored source directory — consistent with a
  concurrently-running `generate` test in the same file leaving its per-`outDir` mkdir lock
  behind and this test's `/tmp` scan picking it up.

## Required outcome
Nothing in this PR. Re-run CI; if it recurs, the isolation bug belongs in its own change.

ℹ️ The activation eval ships a harness but no measured baseline

The PR's premise is that the old description lost authoring prompts, and this adds the machinery to measure exactly that — but evals:activation is opt-in and no result is recorded, so there is no before/after number anywhere. Every other EVAL-AREAS.md section carries a Finding:; section 3b has none. The deterministic guard only asserts that certain substrings are present, which is a proxy for routing rather than evidence of it.

Technical details
# No recorded routing baseline for section 3b

## Affected sites
- `evals/EVAL-AREAS.md:45` — section "3b" is marked "🟡 just added" with no `Finding:` line,
  unlike sections 3 and 4 which record their conclusions.
- `evals/run-activation-eval.ts:202-234` — writes `evals/results/activation-<label>.json`, but
  no result is committed.

## Required outcome
A recorded precision/recall figure for the new description, so a later change can be compared
against it and so the PR's premise is evidenced rather than asserted.

## Suggested approach
Run `bun run evals:activation --label baseline-authoring` and record the headline numbers in
the section 3b prose the way sections 3 and 4 do. If the old description is still reachable,
routing both and reporting the recall delta would directly demonstrate the motivating claim.

ℹ️ Nitpicks

  • evals/lib/activation.ts:51-92 hand-rolls a YAML reader where the monorepo already depends on yaml and already has packages/leadtype/src/internal/frontmatter.ts doing this properly. It returns a wrong value rather than throwing for several valid YAML shapes — quoted scalars keep their quote characters, a plain multi-line scalar (no >) is silently truncated to its first line, and | is folded to spaces like > instead of preserving newlines. None are hit by today's SKILL.md, and a truncation would currently fail the verb/signal assertions loudly rather than pass, so this is maintainability rather than a live bug — but adding yaml to evals/package.json would delete ~40 lines, five regexes, and four dedicated tests.
  • evals/run-activation-eval.ts:84-106 shows the router exactly one skill and asks ACTIVATE/SKIP. Real clients pick among many competing skills, so precision here is measured against an empty field and will read optimistically. Given how carefully EVAL-AREAS.md caveats the other areas, that framing limit seems worth a sentence in section 3b.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using Claude Opus𝕏

Comment thread evals/EVAL-AREAS.md Outdated
Comment thread .agents/skills/leadtype/SKILL.md Outdated
Review caught a real bug: decisions were read with a substring match, so a
reply of "SKIP, not ACTIVATE" — or "do not activate" — scored as ACTIVATE.
That inflates recall and deflates precision in the direction you were hoping
for, which is the worst way for an eval to be wrong.

`parseDecision` now accepts exactly "ACTIVATE" or "SKIP", case-insensitive and
trimmed, and throws on anything else rather than guessing. It moved into
`lib/activation.ts` so it is covered by tests, including the replies that used
to be misread.

Also from review: `--runs` and `--concurrency` are bounded, since both multiply
into gateway load; outcomes now carry `model` and `run` so the results file
stays analyzable per model and across repeats; and the skill's "establish the
project" step names the workspace fallback that the Path Priority section
already defines, instead of assuming `node_modules/leadtype` exists.

The activation contract is now stated once and consistently: a client reads
name *and* description from the discovery manifest, so the eval passes both.
The docs previously said description was the entire signal while the eval
supplied both — measuring a contract that doesn't exist.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ℹ️ No critical issues in the new commit — one robustness suggestion inline. Note that the two inline threads from the previous review are still open.

Reviewed changes — the delta since the prior pullfrog review at ecd4eba: the single new commit 7967d82, which replaces the eval's verdict parsing and applies three prose/doc adjustments.

  • Exact verdict parsing replaces substring matchingparseDecision moves from run-activation-eval.ts into evals/lib/activation.ts and is now exported. It matches /^(activate|skip)$/i against the trimmed reply instead of testing for \bactivate\b, and throws on anything else. The old form scored "SKIP, not ACTIVATE" as an activation, which inflated recall — this is the right correction.
  • Coverage for the new parser — two cases in evals/lib/activation.test.ts pin the accepted shapes and assert that four ambiguous replies (a negated verdict, a prose refusal, the echoed question, and empty) throw. 14/14 pass locally.
  • --runs and --concurrency are bounded — new MAX_RUNS / MAX_CONCURRENCY constants threaded through parsePositiveInt.
  • Per-outcome model and run recordedActivationOutcome gains both as optional fields and the results JSON now carries them, so per-model and run-to-run variance stay analyzable after the fact.
  • Discovery contract restateddocs/reference/skills.mdx:43 now says a client reads name and description, which matches what buildPrompt actually feeds the router; a new JSDoc block on buildPrompt explains why both go in.
  • SKILL.md "Establish the project" extended — names the workspace fallback under packages/leadtype/ for when node_modules/leadtype is absent.

Verified at 7967d82: the eval suite passes 14/14, and the regenerated docs/paths.lock.json hash for /docs/reference/skills is correct — recomputing it from source (convertMdxFile + hashRedirectContent, with the CLI's exact transform array) yields b0ced16c084e3ecb, matching the committed entry.

ℹ️ Nitpicks

  • evals/run-activation-eval.ts:63 — the Number.isSafeInteger(parsed) clause can never decide the branch. The ^[1-9]\d*$ guard on the line above means parsed is only unsafe once it exceeds 2^53, which already fails parsed > max at both call sites (max is 100 and 64). The bound alone is sufficient.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using Claude Opus𝕏

temperature: 0,
})
);
const decision = parseDecision(result.text);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Throwing here takes the whole matrix down over one bad reply. runPool awaits its workers under Promise.all (evals/lib/pool.ts:26), withRetry wraps only the generateText call and retries transient gateway errors only (evals/lib/retry.ts:1-12), and the results file isn't written until line 228 — so a single non-conforming response rejects main() and discards every completed cell, which for evals:activation:full is 340 gateway calls with nothing persisted. The sibling runner deliberately does the opposite: judgeAnswer documents "Never throws … instead of crashing the whole matrix" (evals/lib/judge.ts:121-126), and invokeAgent records a failed call as an error on its own cell (run-llms-eval.ts:219-222).

Technical details
# A malformed router reply aborts the entire activation run

## Affected sites
- `evals/run-activation-eval.ts:175``parseDecision(result.text)` is called inside the
  `runPool` worker with no `try`/`catch`.
- `evals/lib/pool.ts:26``await Promise.all(workers)`, so one rejected worker rejects
  `runPool` and therefore `main()`.
- `evals/run-activation-eval.ts:228` — the results JSON is written only after `runPool`
  resolves, so an abort persists nothing at all.
- `evals/lib/retry.ts:1-12``withRetry` only retries errors matching `TRANSIENT_PATTERN`;
  a parse error is terminal. It also wraps only the `generateText` call, not the parse.

## Why this is reachable
- `evals:activation:full` is 4 models × 17 cases × 5 runs = 340 calls. One stray token
  anywhere in that matrix loses all of them.
- An empty `result.text` is a realistic outcome for a reasoning model whose output budget
  went to reasoning tokens — the sibling runner guards for exactly that with
  `result.text ?? ""` (`run-llms-eval.ts:211`). The new test already asserts that `""`
  throws.
- `"ACTIVATE."` (bare verdict, trailing period) also throws and is not among the tested
  shapes, despite being the most likely way a compliant-ish model deviates from
  "No punctuation, no explanation".

## Required outcome
A malformed reply must cost that one cell, not the run. Whatever the run produced up to
that point should still be scored and written.

## Suggested approach
Keep the strict parse — reverting to substring matching would restore the recall inflation
this commit set out to fix. Instead catch at the call site and record the cell, following
the convention `judge.ts` already documents: count the case as incorrect (fail closed) and
carry the raw reply plus the error string into the outcome so the results file shows what
the router actually said. Surfacing a malformed-reply count in the printed summary would
keep it from being silently absorbed.

## Open questions for the human
- Should a malformed reply also be retried once against the same model before it is scored
  as a miss? `withRetry` would need a non-transient retry path for that, which may be more
  machinery than an opt-in eval warrants.

The eval guard was not actually running. EVAL-AREAS said the deterministic half
runs in CI, but the only test step is `--filter leadtype`, which selects the
package of that name — this workspace is `leadtype-evals`, and nothing invoked
its tests. So the guard against someone narrowing the skill description existed
and never ran. Added as its own step, and the claim now says why it needs one.

The skill routed to `writing.md`, which is not a topic file — the authored
pages are `write-for-agents`, `frontmatter`, and `components`, so that route
resolved to nothing under every Path Priority root.

Discovery JSON is validated before indexing, so malformed output fails as
malformed rather than as an undefined-property read.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ℹ️ No new issues — both threads from the initial review are fixed and now resolved. Two nitpicks below. The run-activation-eval.ts:175 thread from the previous review is still open, which is the only thing holding back an approval.

Reviewed changes — the delta since the prior pullfrog review at 7967d82: the single new commit 5227964 (4 files, +25/-8), which is entirely a response to outstanding review feedback.

  • The deterministic activation guard now actually gates PRs.github/workflows/ci.yml gains a Test evals step running bun run --filter leadtype-evals test, with a comment explaining why it needs its own step. This closes the initial review's [!IMPORTANT] finding.
  • The CI claim in EVAL-AREAS.md:61 is now true and explains itself — reworded to "runs in CI as its own step", plus the reason: the main test job is --filter leadtype, which selects the package of that name, and this workspace is leadtype-evals.
  • SKILL.md:43 routes to a page that existswriting.mdwrite-for-agents.md.
  • Discovery JSON is narrowed before indexing in skills.test.tsJSON.parse into unknown followed by expect checks for skills and a first entry, in place of the single type assertion.

Verified at 5227964: bun run --filter leadtype-evals test passes 46/46 across all six evals/lib/*.test.ts files, exit 0, ~560ms, with no network or .env dependency — so the new step is a real gate, and pulling in the five previously-unrun test files adds no flakiness. skills.test.ts still passes 10/10 after the narrowing refactor. docs/writing/write-for-agents.mdx exists, and the bare filename matches the pre-existing convention in the untouched ## Topic Routing list, so the prefix-less form is deliberate rather than a second bug. The activation.ts header comment claiming "Deterministic (runs in CI)" is now accurate too. docs/paths.lock.json is untouched by this commit, correctly — docs/reference/skills.mdx did not change.

ℹ️ Nitpicks

  • packages/leadtype/src/llm/skills.test.ts:115-128 — this rewrite answers a CodeRabbit comment that CodeRabbit itself flagged as "likely an incorrect or invalid review comment" (it claimed a duplicated cast that was never in the file, which compiled and passed 10/10 throughout). The result trades one idiomatic cast for four expect steps and three as casts, so it does not actually replace assertions with narrowing — and in a test reading its own generated fixture, a malformed shape already fails loudly on the next line. The original ) as { skills: { name: string; description: string }[] }; read better; worth considering a revert.
  • Now that evals has a test gate, the asymmetry with lint/typecheck is a little more visible: vitest run lib covers evals/lib/, so run-activation-eval.ts — the file this PR is actively developing — is still neither tested nor typechecked by any workflow. Pre-existing for the other runners, and reasonable to leave for a follow-up.

Pullfrog  | Fix it ➔View workflow run | Using Claude Opus𝕏

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with 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.

Inline comments:
In @.agents/skills/leadtype/SKILL.md:
- Around line 43-46: Update the documentation references in the Leadtype skill
to use the exact generated topic paths, including their source directory
prefixes, for frontmatter.md, components.md, markdown.md, and llm.md. Preserve
the existing descriptions and ensure the links resolve under the appropriate
writing/ and reference/ directories.

In `@packages/leadtype/src/llm/skills.test.ts`:
- Around line 123-128: Update the discovery index test around the skills entry
to use an explicit TypeScript type guard or assertion helper that narrows
index.skills to an array and verifies the first entry has string name and
description properties. Remove the existing type assertions, then destructure
the narrowed entry so malformed data fails at the intended validation point.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7734e8d3-511e-4d3e-a208-edce04603db9

📥 Commits

Reviewing files that changed from the base of the PR and between 7967d82 and 5227964.

📒 Files selected for processing (4)
  • .agents/skills/leadtype/SKILL.md
  • .github/workflows/ci.yml
  • evals/EVAL-AREAS.md
  • packages/leadtype/src/llm/skills.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: pullfrog
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity
Prefer unknown over any when the type is genuinely unknown
Use const assertions (as const) for immutable values and literal types
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/leadtype/src/llm/skills.test.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions
Prefer for...of loops over .forEach() and indexed for loops
Use optional chaining (?.) and nullish coalescing (??) for safer property access
Prefer template literals over string concatenation
Use destructuring for object and array assignments
Use const by default, let only when reassignment is needed, never var
Always await promises in async functions - don't forget to use the return value
Use async/await syntax instead of promise chains for better readability
Handle errors appropriately in async code with try-catch blocks
Don't use async functions as Promise executors
Remove console.log, debugger, and alert statements from production code
Throw Error objects with descriptive messages, not strings or other values
Use try-catch blocks meaningfully - don't catch errors just to rethrow them
Prefer early returns over nested conditionals for error cases
Extract complex conditions into well-named boolean variables
Use early returns to reduce nesting
Prefer simple conditionals over nested ternary operators
Don't use eval() or assign directly to document.cookie
Avoid spread syntax in accumulators within loops
Use top-level regex literals instead of creating them in loops
Prefer specific imports over namespace imports
Use descriptive names for functions, variables, and types for meaningful naming
Add comments for complex logic, but prefer self-documenting code

Files:

  • packages/leadtype/src/llm/skills.test.ts
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{test,spec}.{js,ts,jsx,tsx}: Write assertions inside it() or test() blocks
Avoid done callbacks in async tests - use async/await instead
Don't use .only or .skip in committed code
Keep test suites reasonably flat - avoid excessive describe nesting

Files:

  • packages/leadtype/src/llm/skills.test.ts
🔇 Additional comments (4)
evals/EVAL-AREAS.md (1)

61-66: 📐 Maintainability & Code Quality

Verify the CI claim against the current workflow.

Lines [61]-[66] state that lib/activation.test.ts runs in CI as a separate step. The workflow is not included in this review. Confirm that CI invokes the leadtype-evals workspace or an equivalent activation command, rather than only running --filter leadtype. If the step is absent, remove this claim or add the CI step before merge.

#!/usr/bin/env bash
set -euo pipefail

rg -n -C 5 \
  'leadtype-evals|activation\.test|evals:activation|--filter leadtype' \
  .github/workflows/ci.yml evals/package.json
.agents/skills/leadtype/SKILL.md (2)

16-16: LGTM!

Also applies to: 26-40, 47-55


41-41: 🎯 Functional Correctness

Keep configuration discovery scoped to the selected project. loadDocsConfig checks leadtype.config.* at the project root first, then docs.config.* in the selected docs directories. The activation signal does not change this resolution rule.

			> Likely an incorrect or invalid review comment.
.github/workflows/ci.yml (1)

55-61: LGTM!

Comment on lines +43 to +46
- `write-for-agents.md` / `frontmatter.md` for page structure, required frontmatter, and `related:` links.
- `components.md` for Leadtype MDX components and when each is appropriate.
- `markdown.md` for how a component flattens into agent-readable markdown.
- `llm.md` when the edit changes `llms.txt`, `AGENTS.md`, or navigation-derived artifacts.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use generated topic paths with their directory prefixes.

The authoring route names frontmatter.md, components.md, markdown.md, and llm.md as direct topic paths. Generated documentation preserves the source directories, so these pages resolve under writing/ and reference/. Use the exact generated paths.

Proposed fix
-   - `write-for-agents.md` / `frontmatter.md` for page structure, required frontmatter, and `related:` links.
-   - `components.md` for Leadtype MDX components and when each is appropriate.
-   - `markdown.md` for how a component flattens into agent-readable markdown.
-   - `llm.md` when the edit changes `llms.txt`, `AGENTS.md`, or navigation-derived artifacts.
+   - `writing/write-for-agents.md` / `writing/frontmatter.md` for page structure, required frontmatter, and `related:` links.
+   - `writing/components.md` for Leadtype MDX components and when each is appropriate.
+   - `reference/markdown.md` for how a component flattens into agent-readable markdown.
+   - `reference/llm.md` when the edit changes `llms.txt`, `AGENTS.md`, or navigation-derived artifacts.
🤖 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 @.agents/skills/leadtype/SKILL.md around lines 43 - 46, Update the
documentation references in the Leadtype skill to use the exact generated topic
paths, including their source directory prefixes, for frontmatter.md,
components.md, markdown.md, and llm.md. Preserve the existing descriptions and
ensure the links resolve under the appropriate writing/ and reference/
directories.

Comment on lines +123 to +128
expect(index).toHaveProperty("skills");
const { skills } = index as { skills: unknown };
expect(Array.isArray(skills)).toBe(true);
const first = (skills as unknown[])[0];
expect(first).toHaveProperty("description");
const { description } = first as { description: string };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Narrow the discovery entry before destructuring it.

The expect calls do not narrow unknown for TypeScript. Lines [124] and [128] use type assertions, and the test does not verify that skills[0].name and skills[0].description are strings. A malformed discovery index can therefore fail later with an unrelated error. Add an explicit type guard or assertion helper, then destructure the narrowed entry.

As per coding guidelines, use TypeScript type narrowing instead of type assertions.

🤖 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 `@packages/leadtype/src/llm/skills.test.ts` around lines 123 - 128, Update the
discovery index test around the skills entry to use an explicit TypeScript type
guard or assertion helper that narrows index.skills to an array and verifies the
first entry has string name and description properties. Remove the existing type
assertions, then destructure the narrowed entry so malformed data fails at the
intended validation point.

Source: Coding guidelines

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.

Make the Leadtype skill activate for docs authoring and review

1 participant