mirror of
https://github.com/addyosmani/agent-skills.git
synced 2026-10-26 15:05:04 +00:00
fix(performance): document the missing guard step
This commit is contained in:
1 parent
f63ec56a3c
commit
6a268d7710
4 files changed
+67
-5
No files matched your search
@@ -38,14 +38,15 @@
|
||||
{
|
||||
"id": 1,
|
||||
"prompt": "The products page renders slowly with 1000 items. Improve its performance.",
|
||||
"expected_output": "A measured baseline, an identified bottleneck, a targeted fix, and a verified improvement",
|
||||
"expected_output": "A measured baseline, an identified bottleneck, a targeted fix, a verified improvement, and a regression guard",
|
||||
"files": [
|
||||
"performance-optimization"
|
||||
],
|
||||
"expectations": [
|
||||
"Performance is measured before any optimization is applied",
|
||||
"The fix targets the measured bottleneck rather than guessing",
|
||||
"Improvement is verified against the baseline after the change"
|
||||
"Improvement is verified against the baseline after the change",
|
||||
"A concrete guard is added after verification using a synthetic performance budget or field monitoring tied to the measured user-facing metric"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -109,6 +109,31 @@ test('reports frontmatter name that disagrees with the directory', () => {
|
||||
assert.match(errors[0], /does not match directory name/);
|
||||
});
|
||||
|
||||
test('reports a workflow step declared without a matching process section', () => {
|
||||
const content = withAllSections(VALID_FRONTMATTER).replace(
|
||||
'## Common Rationalizations',
|
||||
[
|
||||
'## The Optimization Workflow',
|
||||
'',
|
||||
'```',
|
||||
'1. MEASURE → Establish a baseline',
|
||||
'2. GUARD → Prevent regression',
|
||||
'```',
|
||||
'',
|
||||
'### Step 1: Measure',
|
||||
'',
|
||||
'Measure first.',
|
||||
'',
|
||||
'## Common Rationalizations',
|
||||
].join('\n'),
|
||||
);
|
||||
|
||||
const { errors } = lintSkillContent('alpha', content, KNOWN);
|
||||
|
||||
assert.equal(errors.length, 1);
|
||||
assert.match(errors[0], /Workflow declares Step 2 but has no matching process section/);
|
||||
});
|
||||
|
||||
test('reports a missing frontmatter block', () => {
|
||||
const { errors } = lintSkillContent('alpha', '## Overview\nx\n', KNOWN);
|
||||
assert.equal(errors.length, 1);
|
||||
|
||||
@@ -209,6 +209,27 @@ function lintSkillContent(dirName, content, knownSkills) {
|
||||
}
|
||||
}
|
||||
|
||||
// A named workflow that advertises numbered steps must document each step
|
||||
// before the next level-two section. Otherwise the summary promises a
|
||||
// process stage that the skill never teaches agents how to perform.
|
||||
const workflowSections = content.matchAll(
|
||||
/^## The [^\n]+ Workflow\s*\r?\n([\s\S]*?)(?=^## |(?![\s\S]))/gm
|
||||
);
|
||||
for (const match of workflowSections) {
|
||||
const section = match[1];
|
||||
const declared = [...section.matchAll(/^\s*(\d+)\.\s+[A-Z][A-Z -]*\s+→/gm)];
|
||||
if (declared.length < 2) continue;
|
||||
|
||||
const documented = new Set(
|
||||
[...section.matchAll(/^### Step\s+(\d+):/gm)].map(step => step[1])
|
||||
);
|
||||
for (const step of declared) {
|
||||
if (!documented.has(step[1])) {
|
||||
errors.push(`Workflow declares Step ${step[1]} but has no matching process section`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Cross-skill references ───────────────────────────────────────────────
|
||||
const refs = extractSkillReferences(content);
|
||||
for (const ref of refs) {
|
||||
|
||||
@@ -400,9 +400,24 @@ Reverted work leaves no trace in git history, which is exactly why the same dead
|
||||
|
||||
A section in the PR description or a `PERF.md` in the repo both work. What matters is that the next person (or the next agent) reads it before proposing an experiment, and doesn't re-run one that already failed.
|
||||
|
||||
## Performance Budget
|
||||
### Step 5: Guard Against Regression
|
||||
|
||||
Set budgets and enforce them:
|
||||
Guard the metric the user actually feels, not every available number. Use the
|
||||
same LCP, INP, p95 latency, or other primary metric that justified the fix.
|
||||
|
||||
Use two complementary layers when the surface is user-facing:
|
||||
|
||||
- **Synthetic CI gate:** Catch reproducible regressions before merge with a
|
||||
performance budget. Repeat noisy measurements or compare a median/trend so
|
||||
normal run-to-run variance does not turn the gate into a flaky check.
|
||||
- **Field monitoring:** Alert on a meaningful p75 movement in RUM data. Use
|
||||
attributed `web-vitals` data to locate the cause; treat CrUX's rolling window
|
||||
as confirmation rather than an immediate alert.
|
||||
|
||||
When either guard fires, return to Step 1 and establish a fresh baseline before
|
||||
proposing another fix.
|
||||
|
||||
**Set budgets and enforce them:**
|
||||
|
||||
```
|
||||
JavaScript bundle: < 200KB gzipped (initial load)
|
||||
@@ -477,5 +492,5 @@ After any performance-related change:
|
||||
- [ ] No N+1 queries in new data fetching code
|
||||
- [ ] Any new index is justified by a query plan before and after, and its write cost was considered
|
||||
- [ ] Any new cache states what it keys on and how it goes stale
|
||||
- [ ] Performance budget passes in CI (if configured)
|
||||
- [ ] The measured user-facing metric has a synthetic budget or field monitor that can detect regression
|
||||
- [ ] Existing tests still pass (optimization didn't break behavior)
|
||||
Reference in new issue
Block a user