Fix release for 1.1.1: the minimal path end to end, engine commands that run on Godot, Unity and Unreal, hook warnings you can see, and verdicts that say when they could not check. Details in CHANGELOG.md; upgrade steps in UPGRADING.md (v1.1.1 → v1.1.2). Fixes #67 Fixes #131 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WuJn2FdynEybfL5KC697nJ
11 KiB
Skill Test Spec: /perf-profile
Skill Summary
/perf-profile is a static-analysis performance profiler. It takes a system
name or full as its scope, records every input it needs as FOUND or
ABSENT before producing anything, resolves performance.enforce from the
config block injected at its top (Phase 0), and reads the committed budgets
with get_effective_yaml_key for performance.target_framerate,
performance.frame_budget_ms, performance.draw_call_limit and
performance.memory_ceiling_mb before falling back to design docs or
CLAUDE.md (Phase 2). It analyses source code for CPU, memory, rendering and
I/O hotspot candidates (Phase 3) — it does not ingest runtime profiler exports.
The report rates each budgeted row OK / WARNING / OVER; a metric with no
committed budget is NOT ASSESSED, and the summary gives headroom only against a
budget that is set (no budget set — headroom not assessed otherwise). The skill asks "May I write this profiling
report to production/polish/[scope]-report-[date].md?" before writing (Phase
4), offers implement / cut scope / defer / escalate choices for M- or L-effort
hotspots (Phase 5), and hands off to /architecture-decision, /scope-check
or /sprint-plan update (Phase 6). No director gates are invoked. Verdicts:
COMPLETE, or NOT ASSESSED — NO DATA when every required input is absent.
Static Assertions (Structural)
Checked against the SKILL.md by /skill-test spec — no fixture needed.
- Has required frontmatter fields:
name,description,argument-hint,user-invocable,allowed-tools - Has ≥2 phase headings
- Contains verdict keywords: COMPLETE, NOT ASSESSED — NO DATA, and the per-metric statuses OK / WARNING / OVER
- Contains "May I write" language naming
production/polish/[scope]-report-[date].md - Has a next-step handoff (Phase 6:
/architecture-decision,/scope-check,/sprint-plan update)
Director Gate Checks
None. Performance profiling is an advisory analysis skill; no gates are invoked in any review mode.
Test Cases
Case 1: Happy Path — Budgets committed, combat hotspots found
Fixture:
project.yaml:engine.name: godot,performance.target_framerate: 60,performance.frame_budget_ms: 16.6,performance.draw_call_limit: 200,performance.memory_ceiling_mb: 2048;performance.enforceunset (resolves towarn);modes.automationunset (collaborative)- No config key, design doc or
CLAUDE.mdsection states a load-time target src/gameplay/combat/enemy_manager.gdhas a_process()that loops over every enemy and, inside that loop, over every active projectile, and casts a ray per enemy every frame- Reworking the nested loop into a spatial partition is a multi-day change
Input: /perf-profile combat
Expected behavior:
- Skill records its inputs: source code FOUND, committed budgets FOUND
- Phase 0:
performance.enforceresolves towarn— violations are findings, not blockers - Phase 2: the four
performance.*keys are read withget_effective_yaml_keybefore any fallback - Phase 3: the combat
_process()functions are listed; the nested loop and the per-frame raycast are identified - Phase 4: the report's Performance Budgets table uses 16.6 ms, 2048 MB and 200 draw calls as budgets, each of those three rows with an OK / WARNING / OVER status; the Load time row has no committed budget and is
NOT ASSESSED; hotspots and recommendations citeenemy_manager.gdwith a line number - Summary gives the top 3 hotspots, estimated headroom against the three set budgets,
no budget set — headroom not assessedfor load time, and a recommended next action - Skill asks "May I write this profiling report to
production/polish/combat-report-[date].md?" - The nested-loop hotspot is rated Fix Effort M or L, so Phase 5 offers: implement, reduce scope via
/scope-check, defer to Polish, or escalate via/architecture-decision - Verdict: COMPLETE
Assertions:
- Budgets come from the four
performance.*keys read withget_effective_yaml_key, not from design docs or a hardcoded 16.67 ms - The nested loop and the per-frame raycast are reported as hotspots with a
file:linelocation - Each optimization recommendation states location, expected gain, risk and approach
- The frame time, memory and draw call rows carry an OK / WARNING / OVER status; the Load time row is
NOT ASSESSED, not OK - No headroom is claimed for load time; the summary says
no budget set — headroom not assessed - "May I write" names
production/polish/combat-report-[date].mdand precedes any write - The M/L-effort hotspot triggers the Phase 5 choice (implement /
/scope-check/ defer to Polish //architecture-decision) - Verdict is COMPLETE
Case 2: No Committed Budget — Budget rows are NOT ASSESSED
Fixture:
project.yamlhasengine.name: godotand noperformanceblock- No design doc and no
CLAUDE.mdsection states a frame-rate, memory, load-time or draw-call target src/contains gameplay scripts with_process()functions
Input: /perf-profile full
Expected behavior:
get_effective_yaml_keyreturns nothing for the four budget keys; the design-doc andCLAUDE.mdfallback finds no target either- Source code is FOUND, so Phase 3 analysis still runs and hotspots are reported
- Every row of the Performance Budgets table is
NOT ASSESSED— none is measured against the template's[16.67ms]placeholder - The summary does not claim headroom against a budget nobody set: it says
no budget set — headroom not assessed - Skill asks "May I write this profiling report to
production/polish/full-report-[date].md?"; theNOT ASSESSEDrows stay in the file offered for writing
Assertions:
- No budget row shows OK, WARNING or OVER; each is
NOT ASSESSED - The
[16.67ms]placeholder is never used as a budget, and no headroom figure is claimed - Hotspot analysis still runs and is reported
- The
NOT ASSESSEDrows are kept in the report offered for writing, not edited out
Case 3: Nothing to Profile — Whole verdict NOT ASSESSED — NO DATA
Fixture:
- Fresh project:
project.yamlhasengine.name: godotand noperformanceblock, and the code rootsrc/is absent - No design docs exist
- No profiler output exists anywhere
Input: /perf-profile full
Expected behavior:
- Skill lists its inputs and records each: source code ABSENT, budgets ABSENT, profiler output ABSENT
- Every required input is ABSENT, so the skill stops before Phase 3
- Skill reports
NOT ASSESSED — NO DATAas the whole verdict, naming what was missing and which skill produces it - No report template is filled in and nothing is offered for writing
Assertions:
- Each input is recorded as FOUND or ABSENT, not assumed present
- The whole verdict is
NOT ASSESSED — NO DATA, not COMPLETE - Output names each missing input and which skill produces it
- No filled-in report, no headroom figure, and no "May I write" prompt
Case 4: Local Enforcement Override block — Violations are blockers
Fixture:
project.yaml:engine.name: godot,performance.enforce: warn,performance.draw_call_limit: 200, the other three budgets setproject.local.yaml:performance.enforce: block, so the resolved block at the top of the skill showsblocksrc/levels/forest/forest_builder.gdinstantiates 600 individual foliageMeshInstance3Dnodes, each with its own material and no instancingmodes.automationunset (collaborative)
Input: /perf-profile forest
Expected behavior:
- Phase 0 uses
blockfrom the resolved config block at the top of the skill, not thewarna fresh read ofproject.yamlwould give - The draw-call estimate for the forest scope exceeds 200; the Draw calls row is OVER
- The report states explicitly that the violation is a blocker — release-stopping — and that
/gate-checkwill FAIL the Polish gate on it - A recommendation (e.g., batching or instancing the foliage) is given with its expected gain
- Skill asks "May I write" before writing
production/polish/forest-report-[date].md
Assertions:
- The local
blockapplies, not the committedwarn—performance.enforcecomes from the resolved block - The Draw calls row is OVER against the 200 budget
- The report states the violation is a blocker and that
/gate-checkwill FAIL the Polish gate on it - "May I write" precedes any write
Case 5: Enforcement Level off in Full Review Mode — Informational only, no gates
Fixture:
project.yaml:engine.name: godot,modes.review_mode: full,performance.enforce: off, all four budgets setsrc/contains a_process()whose estimated frame cost exceedsperformance.frame_budget_ms
Input: /perf-profile full
Expected behavior:
- No director gate is invoked, whatever the review mode
- The estimated frame time is still reported in the Performance Budgets table
- Because budgets are informational under
off, the over-budget frame time is not raised as a violation finding or as a recommendation to fix - Skill asks "May I write" before writing the report
- Output ends with the Phase 6 next steps:
/architecture-decision,/scope-check [feature],/sprint-plan update
Assertions:
- No director gate is invoked in any review mode
- The estimated frame time still appears in the budget table
- The over-budget metric is not raised as a violation finding or a fix recommendation
- "May I write" precedes any write
- Next steps name
/architecture-decision,/scope-checkand/sprint-plan update
Protocol Compliance
- Records each input as FOUND or ABSENT before producing any report
- Reads the four
performance.*budget keys withget_effective_yaml_keybefore falling back to design docs orCLAUDE.md - A metric with no committed budget is
NOT ASSESSED— never measured against the[16.67ms]placeholder — and the summary claims headroom only against a budget that is set - Applies
performance.enforcefrom the resolved block:warn→ findings,block→ blockers,off→ informational - Asks "May I write" naming
production/polish/[scope]-report-[date].mdbefore writing, and keepsNOT ASSESSEDsections in the written file - No director gates are invoked
- Ends with the Phase 6 next-step handoff
Coverage Notes
- An unrecognized
performance.enforcevalue (surfaced to the user, then treated aswarn) is not tested here. - The skill is static analysis only; confirming a hotspot with runtime profiling is listed under the report's "Requires Investigation" section and is not exercised by these cases.
- Deferring M/L hotspots under Phase 5 choice C records them under
### Deferred to Polish; that recording is not asserted separately.