claude-mcp-workflow 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/.claude-plugin/plugin.json +4 -2
  2. package/README.md +37 -0
  3. package/build/engine.d.ts +13 -1
  4. package/build/engine.d.ts.map +1 -1
  5. package/build/engine.js +41 -2
  6. package/build/engine.js.map +1 -1
  7. package/build/types.d.ts +20 -20
  8. package/build/types.js +1 -1
  9. package/build/types.js.map +1 -1
  10. package/hooks/hooks.json +4 -2
  11. package/hooks/workflow-cleanup.sh +8 -4
  12. package/hooks/workflow-start.sh +60 -32
  13. package/package.json +1 -1
  14. package/templates/bug-fix.yaml +98 -16
  15. package/templates/code-review.yaml +30 -12
  16. package/templates/coding.yaml +49 -4
  17. package/templates/debugging.yaml +39 -14
  18. package/templates/explore.yaml +48 -14
  19. package/templates/file-code.yaml +13 -4
  20. package/templates/file-review.yaml +25 -6
  21. package/templates/github-init.yaml +24 -8
  22. package/templates/investigate.yaml +13 -4
  23. package/templates/master.yaml +16 -13
  24. package/templates/new-feature.yaml +24 -26
  25. package/templates/planning.yaml +34 -7
  26. package/templates/reflection.yaml +11 -4
  27. package/templates/review-push.yaml +26 -7
  28. package/templates/skills/architecture/SKILL.md +131 -1
  29. package/templates/skills/aws-lambda/SKILL.md +9 -9
  30. package/templates/skills/browser-verify/SKILL.md +58 -0
  31. package/templates/skills/build-cmake/SKILL.md +24 -8
  32. package/templates/skills/ci-github-actions/SKILL.md +34 -18
  33. package/templates/skills/claude-code-config/SKILL.md +41 -19
  34. package/templates/skills/cleanup/SKILL.md +57 -0
  35. package/templates/skills/coding-skill-selector/SKILL.md +2 -1
  36. package/templates/skills/debug-bridge-scaffold/SKILL.md +98 -0
  37. package/templates/skills/domain-gamedev/SKILL.md +56 -6
  38. package/templates/skills/domain-pixi/SKILL.md +256 -7
  39. package/templates/skills/domain-reid/SKILL.md +39 -5
  40. package/templates/skills/domain-yolo/SKILL.md +18 -7
  41. package/templates/skills/ide-zed/SKILL.md +23 -0
  42. package/templates/skills/lang-as3/SKILL.md +7 -5
  43. package/templates/skills/lang-haxe/SKILL.md +538 -19
  44. package/templates/skills/lang-python/SKILL.md +6 -2
  45. package/templates/skills/math/SKILL.md +64 -2
  46. package/templates/skills/mcp-setup/SKILL.md +14 -2
  47. package/templates/skills/preferences/SKILL.md +10 -10
  48. package/templates/skills/skill-manager/SKILL.md +264 -0
  49. package/templates/skills/target-openfl-native/SKILL.md +52 -17
  50. package/templates/skills/target-openfl-native/references/build-and-versions.md +7 -4
  51. package/templates/skills/task-delegation/SKILL.md +76 -4
  52. package/templates/skills/web-reading/SKILL.md +33 -25
  53. package/templates/skills/workflow-authoring/SKILL.md +47 -12
  54. package/templates/subagent.yaml +29 -8
  55. package/templates/testing.yaml +64 -10
  56. package/templates/web-research.yaml +23 -13
@@ -18,14 +18,14 @@ description: GitHub Actions workflow gotchas
18
18
  The action resolves the most recent tag **reachable from the current commit** via git history. With the default `fetch-depth: 1`, no tags are reachable — the action returns empty or stale results.
19
19
 
20
20
  ```yaml
21
- - uses: actions/checkout@v4
21
+ - uses: actions/checkout@v7
22
22
  with:
23
23
  fetch-depth: 0 # required for tag resolution
24
24
  ```
25
25
 
26
26
  Same applies to `git describe`, `git log --follow`, `gitversion`, and any changelog generator that walks commit history.
27
27
 
28
- ## `actions/checkout@v4` Default Fetches Only 1 Commit
28
+ ## `actions/checkout` Default Fetches Only 1 Commit (v4+)
29
29
 
30
30
  Unless `fetch-depth: 0` (full history) or `fetch-depth: N` is set, you get a shallow clone with depth 1. Breaks: tag lookup, `git log`-based changelogs, `git blame`, branch comparison diffs.
31
31
 
@@ -53,10 +53,12 @@ deploy:
53
53
  # RIGHT — run regardless, check status explicitly
54
54
  deploy:
55
55
  needs: [build]
56
- if: always() && needs.build.result == 'success'
56
+ if: ${{ !cancelled() && needs.build.result == 'success' }}
57
57
  steps: ...
58
58
  ```
59
59
 
60
+ Prefer `!cancelled()` over `always()`: `always()` forces the job to run even when the workflow run is being cancelled; `!cancelled()` lifts the "dependency failed/skipped" gate but still respects cancellation. (The `${{ }}` is required here — a bare `if:` starting with `!` is invalid YAML.)
61
+
60
62
  ## `$GITHUB_ENV` Changes Are Not Visible in the Same Step
61
63
 
62
64
  `echo "X=value" >> $GITHUB_ENV` exports `X` for all **subsequent** steps. The current step cannot read it.
@@ -99,46 +101,60 @@ jobs:
99
101
  test:
100
102
  continue-on-error: true # whole matrix ignores failures
101
103
 
102
- # Matrix-level — only the flagged cell continues, others still gate the job
103
- strategy:
104
- matrix:
105
- os: [ubuntu, windows]
106
- fail-fast: false
104
+ # Cell-level — only the flagged cell continues, others still gate the job
107
105
  jobs:
108
106
  test:
107
+ strategy:
108
+ fail-fast: false
109
+ matrix:
110
+ os: [ubuntu, windows]
109
111
  continue-on-error: ${{ matrix.os == 'windows' }} # only windows cell is optional
110
112
  ```
111
113
 
114
+ Note `strategy:` lives under `jobs.<job>` — it is not a top-level workflow key.
115
+
112
116
  `fail-fast: false` (strategy level) keeps sibling cells running after one fails. `continue-on-error: true` (job level) prevents the job from being marked failed. They're orthogonal.
113
117
 
114
- ## Artifact Names Must Be Unique Within a Run (actions/upload-artifact@v4)
118
+ ## Artifact Names Must Be Unique Within a Run (actions/upload-artifact v4+)
115
119
 
116
- v4 dropped the `overwrite` default — uploading two artifacts with the same name in one workflow run is an error, not a silent overwrite.
120
+ v4 made artifacts immutable — uploading two artifacts with the same name in one workflow run is an error (v3 silently merged files into one artifact). Since v4.4 an explicit `overwrite: true` input deletes and replaces the existing artifact; the default is still an error.
117
121
 
118
122
  ```yaml
119
123
  # WRONG — both matrix cells upload "test-results", second one errors
120
- - uses: actions/upload-artifact@v4
124
+ - uses: actions/upload-artifact@v7
121
125
  with:
122
126
  name: test-results
123
127
 
124
128
  # RIGHT — include matrix dimension in the name
125
- - uses: actions/upload-artifact@v4
129
+ - uses: actions/upload-artifact@v7
126
130
  with:
127
131
  name: test-results-${{ matrix.os }}
128
132
  ```
129
133
 
130
- ## `if:` Expressions Use `==` Not `===`, and Strings Are Case-Sensitive
134
+ ## `if:` Expressions: Single Quotes Only, Comparison Is Case-INsensitive
131
135
 
132
136
  ```yaml
133
- # WRONG — this never matches
134
- if: github.event_name == "Push"
137
+ # WRONG — double quotes are a validation error in expressions
138
+ if: github.event_name == "push"
135
139
 
136
140
  # RIGHT
137
141
  if: github.event_name == 'push'
138
142
  ```
139
143
 
140
- `if:` expressions do NOT require `${{ }}` wrapping (they're evaluated as expressions already). Adding `${{ }}` works but is redundant.
144
+ String literals in expressions must use single quotes double quotes fail workflow validation. String comparison with `==` ignores case (`'Push' == 'push'` is true). The operator is `==`; there is no `===`.
145
+
146
+ `if:` expressions do NOT require `${{ }}` wrapping (they're evaluated as expressions already). Adding `${{ }}` works but is redundant — except when the expression starts with `!` (reserved in YAML), e.g. `if: ${{ !cancelled() }}`.
147
+
148
+ ## `env:` Does Not Propagate Into Reusable Workflow Calls (`jobs.<job>.uses`)
149
+
150
+ A reusable workflow is called at the JOB level (`jobs.<job>.uses`), not as a step. Environment variables set at workflow or job level are available to `run:` steps but are NOT passed into the called reusable workflow. Pass values explicitly via `with:` inputs or `secrets:` instead.
151
+
152
+ ## Default Community Health Files (FUNDING.yml, CODEOWNERS, ISSUE_TEMPLATE) Load ONLY From a Repo Named `.github`
153
+
154
+ Account-wide defaults for community health files come **only** from a public repo named literally `.github` (e.g. `owner/.github`), in its root or a `.github`/`docs` folder. A repo's own file overrides the default.
155
+
156
+ **Wrong:** Putting `FUNDING.yml` in the username/profile repo (`owner/owner`, the one with the profile README) expecting it to apply org-wide — it does NOT. It only affects that single repo.
141
157
 
142
- ## `env:` at Job Level Is Not Inherited by `uses:` (Reusable Workflow) Steps
158
+ **Right:** Create `owner/.github` (must be public) its `FUNDING.yml` gives every repo without its own a Sponsor button.
143
159
 
144
- Environment variables set at job level are available to `run:` steps but NOT passed into called reusable workflows (`jobs.<job>.uses`). Pass them explicitly via `with:` inputs or `secrets:` instead.
160
+ `FUNDING.yml` keys are platform-specific usernames, not URLs: `buy_me_a_coffee: <user>`, `patreon: <user>`. There is **no** `paypal:` key PayPal goes under `custom: ['https://paypal.me/<user>']`.
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: claude-code-config
3
+ description: Claude Code configuration gotchas — permission rule syntax and evaluation order, settings hierarchy, plugin install scopes, hook behavior
3
4
  ---
4
5
 
5
6
  # Claude Code Configuration Gotchas
@@ -14,9 +15,11 @@ name: claude-code-config
14
15
  **Common mistake**: Writing `Read(/tmp/**)` when you mean absolute path. Correct: `Read(//tmp/**)`.
15
16
 
16
17
  **Pattern format must match tool signature**:
17
- - Correct: `Read(//tmp/**)`, `Bash(npm run *)`
18
+ - Correct: `Read(//tmp/**)`, `Bash(npm run:*)`
18
19
  - Wrong: `Read(file_path://tmp/**)` — tool name only, no parameter labels
19
20
 
21
+ **Bash rules use `:*` for prefix matching**: `Bash(npm run:*)` matches any command starting with `npm run `. `Bash(npm run *)` (space-star) is NOT the prefix syntax and matches unreliably.
22
+
20
23
  ## Wildcard Matching
21
24
 
22
25
  **Recursive vs Single Level**:
@@ -29,8 +32,8 @@ name: claude-code-config
29
32
 
30
33
  **Deny always wins**:
31
34
  1. Deny rules checked first (block regardless of anything else)
32
- 2. Allow rules checked second (permit if matched)
33
- 3. Ask rules checked last (prompt user)
35
+ 2. Ask rules checked second (prompt user if matched)
36
+ 3. Allow rules checked last (permit if matched)
34
37
 
35
38
  **First match wins** within each category.
36
39
 
@@ -48,12 +51,8 @@ name: claude-code-config
48
51
 
49
52
  ## Known Bugs (as of 2026)
50
53
 
51
- **Deny rules not enforced**: Multiple reported issues (GH #13785, #6631, #4467, #6699) where deny patterns in settings.json are ignored. Tool still gets access despite explicit deny.
52
-
53
54
  **Allow rules ignored**: Issue #18160 reports global allow permissions being ignored, requiring manual approval despite being in allowlist.
54
55
 
55
- **Wildcard pattern bugs**: `Bash(command *)` patterns may fail to match commands with flags or tilde expansion.
56
-
57
56
  ## Domain-Specific Rules
58
57
 
59
58
  **WebFetch requires domain syntax**:
@@ -64,9 +63,7 @@ name: claude-code-config
64
63
 
65
64
  **Overly broad wildcards**: Starting with `Bash(*)` or `Read(**)` creates security holes. Be specific.
66
65
 
67
- **Forgetting .gitignore**: By default, Claude respects `.gitignore`. If files missing from suggestions, check ignore rules or set `respectGitignore: false`.
68
-
69
- **Environment variables in Bash**: Don't persist between commands. Use `CLAUDE_ENV_FILE` or hooks instead.
66
+ **Environment variables in Bash**: Don't persist between commands. Use the `env` map in settings.json or hooks instead.
70
67
 
71
68
  **Testing permissions**: Use Shift+Tab to switch permission modes mid-session without editing files.
72
69
 
@@ -76,16 +73,16 @@ name: claude-code-config
76
73
  {
77
74
  "permissions": {
78
75
  "allow": [
79
- "Read(./.env)", // relative: .env in settings file dir
76
+ "Read(./config.json)", // relative: config.json in settings file dir
80
77
  "Read(//tmp/**)", // absolute: all files in /tmp
81
78
  "Read(~/.config/**)", // home: all files in ~/.config
82
- "Read(**/.env)", // recursive: .env anywhere in subtree
83
- "Bash(npm run *)", // command prefix matching
79
+ "Read(**/config.json)", // recursive: config.json anywhere in subtree
80
+ "Bash(npm run:*)", // command prefix matching (:* suffix)
84
81
  "WebFetch(domain:docs.anthropic.com)" // domain-specific
85
82
  ],
86
83
  "deny": [
87
84
  "Read(**/secrets/**)", // block entire directory tree
88
- "Bash(rm *)" // block dangerous commands
85
+ "Bash(rm:*)" // block dangerous commands
89
86
  ]
90
87
  }
91
88
  }
@@ -94,14 +91,14 @@ name: claude-code-config
94
91
  ## Debugging Permission Issues
95
92
 
96
93
  1. Check hierarchy: local > project > user > enterprise
97
- 2. Check deny rules first (they always win)
94
+ 2. Check rule order: deny ask → allow (deny always wins)
98
95
  3. Test pattern manually: Does it match the exact tool signature shown in prompt?
99
- 4. Check for known bugs (deny rules may be ignored)
96
+ 4. Check for known bugs (allow rules may be ignored — #18160)
100
97
  5. Use `claude mcp list` to verify MCP server status if permission involves MCP tool
101
98
 
102
99
  ## Plugin Install Scope
103
100
 
104
- `claude plugins install <pkg>@<marketplace>` takes `-s, --scope <user|project|local>` (default: `user`).
101
+ `claude plugin install <pkg>@<marketplace>` (singular `plugin`, not `plugins`) takes `-s, --scope <user|project|local>` (default: `user`).
105
102
 
106
103
  - `user` — global, ~/.claude/plugins/installed_plugins.json with `"scope": "user"`
107
104
  - `project` — must run from within the target project dir; registry entry gets `"scope": "project"` + `"projectPath": "<cwd>"`. Plugin activates only when Claude Code runs inside that dir
@@ -109,14 +106,39 @@ name: claude-code-config
109
106
 
110
107
  Swap scope = uninstall + reinstall (no in-place conversion):
111
108
  ```bash
112
- claude plugins uninstall <pkg>@<mkt> --scope user
113
- cd /path/to/project && claude plugins install <pkg>@<mkt> --scope project
109
+ claude plugin uninstall <pkg>@<mkt> --scope user
110
+ cd /path/to/project && claude plugin install <pkg>@<mkt> --scope project
114
111
  ```
115
112
 
116
113
  All scopes share the same `~/.claude/plugins/cache/` payload — only the registry entry differs.
117
114
 
115
+ ## Stop Hook Fires Per Turn, Not on Process Exit
116
+
117
+ **Wrong assumption**: `Stop` hook = "CLI process is shutting down / session ending".
118
+ **Correct**: `Stop` fires **each time the main agent finishes responding** (end of every turn). One CLI process lifetime can trigger it dozens of times. Docs: *"when Claude finishes responding"* — [docs.claude.com/en/docs/claude-code/hooks](https://docs.claude.com/en/docs/claude-code/hooks).
119
+
120
+ `SubagentStop` fires when a subagent (Task tool) finishes — separate from the main agent's Stop.
121
+
122
+ **Practical implication**: Any state registered on `UserPromptSubmit` and released on `Stop` churns every turn. For per-process state (e.g. a background daemon, a lock file, an IPC socket) key by **CLI PID** — `session_id` is wrong because it also changes on `/clear`, `/compact`, or resume within the same process.
123
+
124
+ ```
125
+ UserPromptSubmit → fires before each user turn
126
+ Stop → fires after each assistant response (not at exit)
127
+ SubagentStop → fires when a Task-tool subagent completes
128
+ PostToolUse → fires after each individual tool call within a turn
129
+ ```
130
+
118
131
  ## npx Argument Re-parsing Under PreToolUse Hooks
119
132
 
120
133
  PreToolUse Bash hooks that rewrite commands can mis-parse `npx -y <pkg>@latest <subcmd>`, with `<pkg>@latest` landing as an **npm** subcommand, producing `Unknown command: "<pkg>@latest"`.
121
134
 
122
135
  Workaround: invoke through a wrapper that bypasses the rewriter (e.g. an explicit `proxy` subcommand of the hook tool, or call the binary directly without `npx -y`). The same `npx` call may succeed via workflow-engine exec while failing via Bash tool — the difference is whether the hook intercepts.
136
+
137
+ ## Multiple Hook Entries for the Same Event Run in Parallel
138
+
139
+ **Wrong assumption**: Two `PreToolUse` entries in `hooks.json` run in declaration order — first entry first, second entry second.
140
+ **Correct**: All entries for the same event fire **simultaneously**. Declaration order is not execution order.
141
+
142
+ **Race-condition trap**: If entry A writes a marker file and entry B deletes it, the outcome depends on timing, not position. A fast B (~0 ms) completes before a slow A (~30–50 ms), so A's delete wins even when B is declared last. Symptom: your write appears to have no effect.
143
+
144
+ **Fix**: One hook entry per event. Inspect stdin JSON inside that single script/command and branch on `tool_name` or other fields to dispatch logic. This guarantees deterministic ordering because it's a single process.
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: cleanup
3
+ description: Clean .claude directory data
4
+ ---
5
+
6
+ ## .claude directory cleanup
7
+
8
+ Run steps in order. **Ask user confirmation before each step.**
9
+
10
+ ### Step 1: Temp data (always safe)
11
+
12
+ Delete contents of (keep directories themselves):
13
+ - `~/.claude/debug/` — session debug logs
14
+ - `~/.claude/shell-snapshots/` — zsh state snapshots
15
+ - `~/.claude/todos/` — task lists from past sessions
16
+ - `~/.claude/plans/` — plan files from past sessions
17
+
18
+ Show size before/after.
19
+
20
+ ### Step 2: Project session logs
21
+
22
+ For each project in `~/.claude/projects/`:
23
+ - **DELETE**: `*.jsonl` files (conversation logs), `UUID/` directories (session caches), `agent-*.jsonl` (subagent logs)
24
+ - **KEEP**: `memory/` directory, `CLAUDE.md` file
25
+
26
+ ⚠️ This breaks `/resume` for old sessions. Warn user.
27
+
28
+ Show size before/after.
29
+
30
+ ### Step 3: Empty project directories
31
+
32
+ After step 2, list projects that have no remaining files (no memory/, no CLAUDE.md).
33
+ Offer to remove them — they recreate automatically when Claude Code runs in that project.
34
+
35
+ ### Step 4: Memory audit
36
+
37
+ For each remaining `memory/MEMORY.md`:
38
+ 1. Read its contents
39
+ 2. Check each entry against existing skills — is it already covered?
40
+ 3. Check if Claude already knows this without any skill
41
+ 4. Present table to user:
42
+
43
+ | Entry | Duplicates? | Verdict |
44
+ |---|---|---|
45
+ | ... | skill X / common knowledge / project-specific | delete / keep |
46
+
47
+ Only modify after user approval.
48
+
49
+ ### Other cleanable files
50
+
51
+ Also check and report sizes of:
52
+ - `~/.claude/history.jsonl` — conversation history (can be large)
53
+ - `~/.claude/file-history/` — file change history
54
+ - `~/.claude/paste-cache/` — clipboard cache
55
+ - `~/.claude/tasks/` — task data
56
+
57
+ These are less critical. Mention sizes, let user decide.
@@ -21,7 +21,7 @@ Load coding skills for files/context in this task.
21
21
 
22
22
  3. By domain (detect from imports, paths, or task description):
23
23
  - YOLO / object detection → `Skill("domain-yolo")`
24
- - Pixi.js / pixi.js imports → `Skill("domain-pixi")`
24
+ - Pixi.js (imports from `pixi.js`, PIXI globals) → `Skill("domain-pixi")`
25
25
  - Person re-identification (ReID) → `Skill("domain-reid")`
26
26
  - Game dev (physics, precision, camera, sprites) → `Skill("domain-gamedev")`
27
27
  - OpenFL / hxcpp native target → `Skill("target-openfl-native")`
@@ -35,6 +35,7 @@ Load coding skills for files/context in this task.
35
35
  - Claude Code settings.json / hooks / permissions / plugin config → `Skill("claude-code-config")`
36
36
  - Workflow YAML in templates/ or .claude/workflows/ → `Skill("workflow-authoring")`
37
37
  - Fetching web docs / external content → `Skill("web-reading")`
38
+ - Verifying a web/canvas app via Playwright or a browser MCP (screenshots, captures, perf) → `Skill("browser-verify")`
38
39
 
39
40
  5. If 3+ independent files or parallel work → `Skill("task-delegation")`
40
41
 
@@ -0,0 +1,98 @@
1
+ ---
2
+ name: debug-bridge-scaffold
3
+ description: Meta-skill for creating debug bridges for new technologies
4
+ ---
5
+
6
+ # Debug Bridge Scaffold
7
+
8
+ ## When to Use
9
+
10
+ When you need runtime inspection of an application built with a technology that has no existing debug bridge.
11
+
12
+ ## Step 1: Check Existing Solutions First
13
+
14
+ Before building a custom bridge, check if a standard tool already works:
15
+
16
+ | Technology | Existing Solution |
17
+ |-----------|-------------------|
18
+ | Web/Electron/browser apps | Playwright MCP, browser DevTools |
19
+ | Native apps with standard UI toolkit | Accessibility API MCP |
20
+ | Any app with visible UI | Computer Use (screenshot + click) |
21
+ | OpenFL/hxcpp | `debug-bridge-openfl` skill (already exists) |
22
+
23
+ **If an existing solution covers your needs — use it. Don't build a bridge.**
24
+
25
+ ## Step 2: Build a Custom Bridge
26
+
27
+ If no existing tool works, create an HTTP bridge following this pattern:
28
+
29
+ ### Architecture
30
+
31
+ ```
32
+ [Background Thread] HTTP Server (127.0.0.1:PORT)
33
+ ↓ accept()
34
+ [Worker Thread] Parse request → dispatch to main thread
35
+ ↓ runOnMainThread(fn)
36
+ [Main Thread] Execute handler → return result
37
+ ↑ result via Lock/Mutex
38
+ [Worker Thread] Send HTTP response
39
+ ```
40
+
41
+ ### Required Endpoints
42
+
43
+ | Endpoint | Method | Purpose |
44
+ |----------|--------|---------|
45
+ | `/ping` | GET | Health check |
46
+ | `/screenshot` | GET | Capture visual state → save to /tmp → return path |
47
+ | `/shutdown` | POST | Graceful exit |
48
+
49
+ ### Framework-Specific Endpoints
50
+
51
+ Add endpoints relevant to the technology:
52
+ - **UI frameworks**: display tree, component inspection, event simulation
53
+ - **Game engines**: scene graph, entity list, physics state
54
+ - **Servers**: request log, connection pool, cache state
55
+
56
+ ### Implementation Checklist
57
+
58
+ 1. HTTP server on background thread (127.0.0.1 only — no external access)
59
+ 2. Main-thread dispatch for all UI/rendering operations
60
+ 3. JSON envelope: `{"ok": true, "data": ...}` / `{"ok": false, "error": "..."}`
61
+ 4. Conditional compilation (only in debug builds)
62
+ 5. Graceful shutdown (close socket, flush stdout, exit)
63
+
64
+ ## Step 3: Create a Tech-Specific Skill
65
+
66
+ After building the bridge, create `~/.claude/skills/debug-bridge-<tech>/SKILL.md` with:
67
+
68
+ 1. Build command with the debug flag
69
+ 2. Binary path pattern
70
+ 3. All endpoints with parameters and example responses
71
+ 4. Technology-specific gotchas (threading, rendering pipeline, etc.)
72
+
73
+ Register it in `coding-skill-selector` under the appropriate domain.
74
+
75
+ ## Step 4: Create an Interactive Debugger Agent
76
+
77
+ Create `~/.claude/agents/interactive-<tech>-debugger.md` with pre-loaded skills:
78
+
79
+ ```yaml
80
+ ---
81
+ name: interactive-<tech>-debugger
82
+ description: <Tech> debug agent with visual debug bridge — ...
83
+ tools: Read, Glob, Grep, Bash, Skill
84
+ model: sonnet
85
+ skills:
86
+ - debugging
87
+ - debug-bridge
88
+ - debug-bridge-<tech>
89
+ - <app-specific-interactive-debug-skill if exists>
90
+ ---
91
+ ```
92
+
93
+ Body: describe the debug loop for the agent — PID tracking, separate build and run steps, timeout rules.
94
+
95
+ Then update:
96
+ 1. `task-delegation` — add agent to Available Agents table and routing rules
97
+ 2. `testing` — reference the new agent for integration verification
98
+ 3. `debugging` — reference the new agent in Step 4 (Debug Bridge)
@@ -12,19 +12,21 @@ See also: math skill for overflow boundaries (factorial, Fibonacci, float absorp
12
12
  | Distance from origin | ULP (precision) | Effect |
13
13
  |---------------------|-----------------|--------|
14
14
  | 100km | 0.78cm | Fine for most games |
15
- | 524km (≈2^19m) | 3.1cm → 6.25cm | Unity/Unreal world edge zone |
15
+ | 524km (≈2^19m) | 3.1cm → 6.25cm | Unity world edge zone |
16
16
  | 2,097km (2^21m) | 25cm | 5m/s @ 60fps movement ABSORBED — character stops moving |
17
17
  | 8,389km (≈2^23m) | 1m | Positions snap to meter grid |
18
18
 
19
19
  **Absorption formula**: movement absorbed when `velocity * dt < ULP(position) / 2`.
20
20
 
21
+ **Unreal gotcha**: Unreal units are CENTIMETERS, not meters — the same ULP boundaries apply per UNIT, so the metric distances above divide by 100 (e.g. 2^21 units ≈ 21km, not 2,097km).
22
+
21
23
  ## Deterministic Physics
22
24
 
23
25
  **DLL FPU contamination**: Direct3D, printer drivers, sound libraries can silently change FPU control word (precision/rounding mode) without restoring. Assert FPU state after every external call.
24
26
 
25
27
  **Debug vs release breaks replays**: compiler optimization level alone changes float results even with identical source. All replay participants must use identical binary.
26
28
 
27
- **FMA cross-platform**: PowerPC uses fused multiply-add (one rounding), Intel uses separate mul+add (two roundings) → different results from identical math. Cross-platform determinism requires controlling instruction selection.
29
+ **FMA cross-platform**: fused multiply-add (one rounding) vs separate mul+add (two roundings) → different results from identical math. (The classic PowerPC-has-FMA / Intel-doesn't framing is stale — modern x86 has FMA3 since Haswell and ARM has FMA; today the divergence comes from whether the COMPILER contracts `a*b+c` into FMA, which varies by compiler and flags like `-ffp-contract`.) Cross-platform determinism requires controlling instruction selection.
28
30
 
29
31
  **x87 vs SSE**: x87 uses 80-bit extended precision internally → double rounding when storing to 64-bit. SSE uses exact 32/64-bit → more predictable. But SSE FTZ/DAZ modes may differ across platforms.
30
32
 
@@ -32,7 +34,7 @@ See also: math skill for overflow boundaries (factorial, Fibonacci, float absorp
32
34
 
33
35
  ## CCD Threshold (verified)
34
36
 
35
- Enable continuous collision detection when: `velocity × dt > object_size`.
37
+ Enable continuous collision detection when: `velocity × dt >= object_size`.
36
38
  - 1m object at 60m/s, 60fps: travels 1m/frame → equals object size → CCD needed
37
39
  - Below threshold: discrete detection misses contacts (tunneling)
38
40
 
@@ -49,7 +51,7 @@ When a state machine waits for an animation-completion event (e.g. xstate `on.AN
49
51
 
50
52
  ## Parallax Tile Assets: Native Width = Integration Signal
51
53
 
52
- When integrating an artist-authored asset (e.g. `hole_flag.png`) into a row of cyclically-stitched parallax tiles (e.g. `front_1..6.png`), **check native dimensions first**.
54
+ When integrating an artist-authored asset (e.g. `landmark.png`) into a row of cyclically-stitched parallax tiles (e.g. `tile_1..6.png`), **check native dimensions first**.
53
55
 
54
56
  - If the asset has the **same native width** as the cyclic tiles → it was designed as ONE of the tiles. Insert it into the stitch sequence at a specific anchor position. Don't overlay.
55
57
  - If the asset has **different dimensions** → it's a prop sprite meant to sit on top of the tile row (e.g. a character, tree, bush).
@@ -86,7 +88,7 @@ In most 2D engines (Pixi, Phaser, etc.), `sprite.anchor.set(ax, ay)` serves TWO
86
88
 
87
89
  **Use case**: to rotate a sprite around a specific feature of its PNG (e.g. the left rim of a hazard/foot of a character), put the anchor at that feature's normalized texture coords. Then set `sprite.position` to the world location of that feature and `sprite.rotation` — the feature stays locked in place while the rest swings.
88
90
 
89
- **Gotcha**: anchor and pivot are NOT separate in Pixi's Sprite. If you need them decoupled, wrap the Sprite in a Container and rotate the Container.
91
+ **Gotcha**: in Pixi the Sprite's `anchor` drives BOTH roles — placement point and rotation origin are not independently settable through it (a separate `pivot` property exists on every Container, but changing it shifts where `position` lands too). If you need placement and rotation origin decoupled, wrap the Sprite in a Container and rotate the Container.
90
92
 
91
93
  ## Heightmap From PNG Alpha — Pre-sample Once
92
94
 
@@ -127,7 +129,7 @@ When the follow formula is `cameraY = targetScreenY − worldY × zoom` (standar
127
129
  - Ball RISES in world → `worldY` DECREASES → `cameraY` INCREASES.
128
130
  - `Math.min(flightTarget, idleCameraY)` reads as "never go ABOVE idle" in human terms, but `cameraY` for a higher ball is NUMERICALLY LARGER than the idle value — so `Math.min` pins the camera to idle and BLOCKS the follow-up. The correct direction is `Math.max` (if clamping against an upper bound on camera Y) or no clamp at all.
129
131
 
130
- **Verify before shipping**: plug in two concrete numbers — ball at rest and ball at peak — and compute `cameraY` for each. The one with higher `worldY` (lower on screen) should yield a MORE NEGATIVE `cameraY` if the pivot is above the horizon pivot, or more positive if below. If the clamp picks the wrong side, the camera becomes useless at exactly the moment it needs to work.
132
+ **Verify before shipping**: plug in two concrete numbers — ball at rest and ball at peak — and compute `cameraY` for each. With `cameraY = targetScreenY worldY × zoom`, the endpoint with the LARGER `worldY` (ball lower in the world) yields the SMALLER `cameraY`. If the clamp picks the wrong side, the camera becomes useless at exactly the moment it needs to work.
131
133
 
132
134
  **Rule**: every time you combine `screenAnchor − worldPos × zoom` with a `Math.min`/`Math.max`, write the two endpoints on paper. Don't trust the human-language reading of the clamp.
133
135
 
@@ -160,3 +162,51 @@ Sub-pixel for flat terrain, but visible (1+ px sliver) on noticeably tilted band
160
162
  | > 40ms | Unacceptable for rhythm/FPS |
161
163
 
162
164
  Humans react to audio faster than visual → audio lag more noticeable than dropped frames.
165
+
166
+ ## Crash / Multiplier-Game RTP (Aviator / Bustabit style)
167
+
168
+ **Canonical crash math**: `crashPoint = (1−he)/(1−U)`, `U∈[0,1)`. This gives `P(crashPoint ≥ x) = (1−he)/x` for `x≥1`. EV at any cashout target `x` = `P(survive to x)·x = (1−he)/x · x = 1−he`. RTP = `1−houseEdge`, **strategy-independent** — the only clean way to get that property.
169
+
170
+ **GOTCHA — forced/consolation payouts break RTP and strategy-independence.**
171
+ Any guaranteed payout on a step the round did NOT crash on (a "soft-loss step saves your accumulated winnings" mechanic, forced cashout on miss, consolation bonus) is EV-positive: it pays value the crash-CDF budget never debited. Effect:
172
+ - Global RTP > target.
173
+ - "Ride longer" becomes the dominant strategy → strategy-independence destroyed.
174
+
175
+ Only voluntary cashouts and a correctly-priced cap/jackpot are EV-neutral. "Soft-loss that saves X" is fundamentally incompatible with strategy-independent crash RTP without full game-math re-optimization around the save mechanics.
176
+
177
+ **GOTCHA — flat auto-paid cap inflates AFK strategy EV.**
178
+ A jackpot cap paid regardless of player action gives the never-cash (AFK) strategy `EV = P(cap)·cap`. EV-neutral only if `P(cap) = (1−he)/cap` exactly (the same CDF). Combine with ANY other forced payout and AFK RTP overshoots the target by tens of percent.
179
+
180
+ **GOTCHA — finite step chain caps reachable multiplier.**
181
+ An N-step chain with per-step multiplier `∈[lo,hi]` tops out at `≈hi^(N−1)`. Crash points above that are structurally unreachable → RTP sags BELOW target for high cashout targets and for never-cash. "Exactly `1−he`, globally strategy-independent" is FALSE for a finite chain; it only holds approximately for reachable targets.
182
+
183
+ | N steps | per-step max | reachable ceiling |
184
+ |---------|-------------|------------------|
185
+ | 5 | ×3 | ×81 |
186
+ | 7 | ×2.5 | ×244 |
187
+ | 10 | ×2 | ×512 |
188
+
189
+ **GOTCHA — PRNG stream independence for crashPoint.**
190
+ `crashPoint` must be statistically independent of the per-step multiplier draws. Drawing it as the first output then the curve from subsequent draws of the same stream is fine (good PRNG outputs are uncorrelated). A separate stream keyed by a near-identical seed through a weak hash (e.g. `seed` vs `seed+":crash"` via FNV-1a → mulberry32) can silently correlate and inflate or deflate RTP. Verify empirically with a large Monte Carlo sweep — don't assume independence from the construction.
191
+
192
+ **GOTCHA — conditional sub-population EV slices carry structural bias.**
193
+ Filtering a Monte Carlo run to "rounds containing event X" and asserting the per-target RTP corridor of the slice ≈ global RTP is wrong whenever X correlates with round length or terminal outcome. Two independent causes:
194
+ (a) *Chain-truncating event*: bonus steps that mostly emit at the crash-branch terminal — the round ends there — so high targets are structurally unreachable. Per-target RTPs of the slice decline monotonically with target; no tolerance widening fixes it.
195
+ (b) *Compensation/dampening gate*: forced-miss rounds replace the event step with a miss variant and never enter the filtered slice — rounds that do enter carry inherited selection bias → the slice mean lands well below the global RTP.
196
+ Pin only the upper bound (no target > 1.10–1.15) to catch +EV regressions, plus a wide mean band (±0.10). Drop per-target corridor, spread, and ride-RTP assertions on any slice with structural correlation between the filter condition and the round's terminal outcome.
197
+
198
+ **PROCESS GOTCHA — Monte Carlo BEFORE porting or regenerating fixtures.**
199
+ Validate the EV model with a sweep across multiple fixed cashout targets before porting to a second language or regenerating golden fixtures. An analytically "correct" model can hide EV-design bugs (forced payouts, chain ceiling, PRNG correlation) that only a per-target simulation exposes. Porting first = double rework when the math turns out wrong.
200
+
201
+ **GOTCHA — economic model must not silently override a player-visible physics event.**
202
+ When a deterministic outcome layer (pre-drawn crash point / book-based result) is draped over a physics renderer, scope any economic reclassification to outcomes the player CANNOT SEE. A physical impact the player watches happen (the ball rams an obstacle and bounces) MUST terminate consistently with what was shown — overriding it back to "safe, continue" so the book stays tidy produces a contradiction caught in the first playtest (observed: the ball visibly hit an obstacle yet the multiplier kept climbing). Rule: renderer-derived classification wins for any event the player can see; the economic model may only override outcomes that are visually indistinguishable.
203
+
204
+ ## Virtual-Scroll / Treadmill Takeover — Match Speed at the Handoff Seam
205
+
206
+ When forward motion transitions from **real translation** (object moves in world-X, camera follows) to a **virtual scroll / treadmill** (object parked at a fixed screen X, background streamed past it to fake motion), the streamed background speed must MATCH the object's prior real forward speed at the seam, then ease down. A speed discontinuity — especially a collapse — reads as the object STOPPING, not as continued motion.
207
+
208
+ **Concrete case**: a flying ball soared at ~1700 px/s (real X travel, camera following), then a "cruise" phase parked the ball and streamed the sky at a constant 280 px/s — a ~6× speed collapse at the seam. Players read it as "the ball stopped." Fix: start the treadmill at the soar's exit speed (`≈ (end.x − start.x) / duration`, clamped to a sane max), then smoothstep-decelerate to the idle drift speed (280 px/s). The background keeps moving at the prior rate and gently settles — reads as continuous forward flight arriving into a hover.
209
+
210
+ **Measurement gotcha**: if the engine time-scales animation (e.g. `gsap.globalTimeline.timeScale(speed)` for a `?speed` param), the sampled background px/s is `game-speed × timeScale`. Multiply back by `1/timeScale` before comparing to your intended game-time speed, or a correct value reads as "too slow."
211
+
212
+ **Secondary motion gotcha**: a vertical overlay (e.g. wind weave) at large amplitude (±70 px) on a now-slow background DOMINATES perception — reads as "bobbing in place." Keep secondary motion small relative to apparent forward speed (±70 → ±26 px fixed it).