[feat] Extend the delivery check to rules, agents and MCP
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 4.8k
- Forks
- 342
- Avg merge
- 13h 48m
- Merged PRs (30d)
- 211
Description
Problem
#598 phase 3 gave doctor its first checks that look at the payload rather than the plumbing, and #625 implements them for two of the seven resource primitives:
skills ✅ Skills delivered to <tool> one destination per tool
docs ✅ Team docs delivered one destination, tree compare
env ✅ (pre-existing env check)
hooks ✅ (pre-existing hook check)
rules ❌ nothing verifies what landed
agents ❌ nothing verifies what landed
mcp ❌ nothing verifies what landed
The three that are left are the same class of bug the issue opens with: the command reports success, the tool receives nothing. #598 said "skills first, because the destination resolver already exists" and predicted rules and agents would be "the same check with a different toolPath field". Implementing it showed that prediction is wrong in two places.
Why it is not just another toolPath field
Rules need their own destination resolver. The filename and the content change per tool:
rules/coding-style.md (team repo)
├─ claude → .claude/rules/coding-style.md verbatim copy
├─ cursor → .cursor/rules/coding-style.mdc derived frontmatter
└─ copilot → .github/instructions/coding-style.md rewritten body
ruleFileExtensionForTool and the two rewriters already exist (src/resources/rules.ts:195-215); nothing exposes "where would this rule land for this tool" as a read-only answer.
Agents break the "list × tools" shape. A skill is desired for every enabled tool. An agent carries spec.targets, so the desired set is a relation, not a product:
skills: desired[] × tools[] → every pair must exist
-agents: desired[] × tools[]
+agents: desired[].targets → per item, and rendered per format
Plus each agent renders into one of three formats (renderForTool, .md / .toml / .json), so validity is format-dependent.
MCP is a different shape entirely, closer to the existing hook check: servers land as an entry inside each tool's native config file, not as a file of their own.
Proposed shape
Rather than teaching doctor about each resource type, give the handlers the seam and let one generic check loop them:
abstract class ResourceHandler {
abstract scanTeamForPull(...): Promise<ResourceItem[]>
abstract pullItem(...): Promise<void>
+ /** Where `item` lands for each tool that can receive it. Read-only. */
+ deliveryTargets(teamConfig, localConfig, item): Promise<{ tool, dest }[]>
}
flowchart LR
subgraph today["today (#598)"]
D1[buildChecks] --> S1["skillTargetForTool<br>doctor knows skills"]
D1 --> S2["resolveDocsDestination<br>doctor knows docs"]
end
subgraph proposed
D2[buildChecks] --> G["one delivery check"]
G --> H1[SkillsHandler.deliveryTargets]
G --> H2[RulesHandler.deliveryTargets]
G --> H3[AgentsHandler.deliveryTargets]
G --> H4[DocsHandler.deliveryTargets]
end
skillTargetForTool (src/resources/skills.ts) and resolveDocsDestination (src/resources/docs.ts) are already that function under another name. This turns them into the first two implementations of a contract, and pullItem keeps using them so the write path and the check can never disagree.
The open decision
These checks now run at the end of every interactive teamai pull, not only in doctor. Skills and docs cost a stat per item. Rules and agents do not:
| cost per pull | |
|---|---|
| skills | stat per skill per tool |
| docs | stat per file in the bundle |
| rules | read + stat per rule per tool (.mdc needs its frontmatter parsed) |
| agents | parse every agent YAML to learn its targets and render extension |
Since #625 that cost has a ceiling. The post-pull pass runs under a 5 second budget that covers building the registry as well as running it, and going over is all or nothing:
teamai pull
buildChecks + runChecks
within 5s -> print each failure with its fix
over 5s -> "Post-pull checks did not run."
the member gets no check at all, not even the cheap ones
An agents check that parses every YAML therefore spends the budget the skills and docs checks need, and those are the ones that catch the bug this work started from. The pass also skips a scope another process has locked, so the concurrent case pays nothing either way.
The question to settle before implementing is whether rules and agents run in the post-pull pass at all, or only in teamai doctor. Check is where that would be expressed, and it now carries two independent axes rather than the one #598 left:
interface Check {
source: 'local' | 'provider' // #598: where the answer comes from
reportedByPull?: string // #625: the pull already said this itself
// a third one for cost?
}
Three optional flags on one object is the point where it stops reading. Worth deciding whether cost is a third axis, another value on source, or something the registry says by building two lists instead.
Scope
-
ResourceHandler.deliveryTargetsseam, with skills and docs migrated onto it, including the installed gate it subsumes:doctorand the sync answer "can this tool receive X" three different ways today (see comment below) - rules delivery check (per tool, per extension,
.mdcfrontmatter validity) - agents delivery check (
spec.targetsrelation, three render formats) - mcp check in the hook-check shape (entry present in each tool's native config)
- decide which of these run post-pull vs
doctor-only
Related: #598, #574, #525, #342, #372.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the existing delivery logic in src/resources/skills.ts and src/resources/docs.ts, then read the ResourceHandler seam and Check definition described in the issue. Trace how post-pull checks and doctor are built and run, including the five-second budget. Done means resolving the open execution-scope decision and covering rules, agents, and MCP without duplicating delivery logic.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- cli, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 28/100