@tyroneross/build-loop 0.30.3

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 (389) hide show
  1. package/.agents/plugins/marketplace.json +10 -0
  2. package/.claude-plugin/marketplace.json +38 -0
  3. package/.claude-plugin/plugin.json +30 -0
  4. package/.codex-plugin/plugin.json +41 -0
  5. package/.cursor/rules/build-loop-surface.mdc +22 -0
  6. package/AGENTS.md +498 -0
  7. package/LICENSE +202 -0
  8. package/README.md +403 -0
  9. package/agents/alignment-checker.md +124 -0
  10. package/agents/api-assessor.md +136 -0
  11. package/agents/architecture-scout.md +185 -0
  12. package/agents/assessment-orchestrator.md +163 -0
  13. package/agents/build-orchestrator.md +198 -0
  14. package/agents/database-assessor.md +133 -0
  15. package/agents/design-contract-specialist.md +239 -0
  16. package/agents/fact-checker.md +69 -0
  17. package/agents/fix-critique.md +161 -0
  18. package/agents/frontend-assessor.md +136 -0
  19. package/agents/implementer.md +185 -0
  20. package/agents/independent-auditor.md +143 -0
  21. package/agents/mock-scanner.md +78 -0
  22. package/agents/optimize-runner.md +112 -0
  23. package/agents/overfitting-reviewer.md +96 -0
  24. package/agents/performance-assessor.md +159 -0
  25. package/agents/plan-critic.md +85 -0
  26. package/agents/promotion-reviewer.md +121 -0
  27. package/agents/recurring-pattern-detector.md +248 -0
  28. package/agents/retrospective-synthesizer.md +109 -0
  29. package/agents/root-cause-investigator.md +285 -0
  30. package/agents/scope-auditor.md +121 -0
  31. package/agents/security-reviewer.md +151 -0
  32. package/agents/self-improvement-architect.md +125 -0
  33. package/agents/synthesis-critic.md +99 -0
  34. package/agents/transcript-pattern-miner.md +165 -0
  35. package/agents/ui-validator.md +193 -0
  36. package/codex-skills/build-loop/SKILL.md +20 -0
  37. package/codex-skills/debug-loop/SKILL.md +18 -0
  38. package/codex-skills/handoff/SKILL.md +22 -0
  39. package/codex-skills/knowledge/SKILL.md +21 -0
  40. package/codex-skills/optimize/SKILL.md +18 -0
  41. package/codex-skills/research/SKILL.md +18 -0
  42. package/commands/agent-rally-point.md +301 -0
  43. package/commands/assess.md +41 -0
  44. package/commands/debug.md +20 -0
  45. package/commands/debugger-detail.md +34 -0
  46. package/commands/debugger-scan.md +15 -0
  47. package/commands/debugger-status.md +20 -0
  48. package/commands/debugger.md +32 -0
  49. package/commands/handoff.md +113 -0
  50. package/commands/knowledge-review.md +16 -0
  51. package/commands/optimize.md +31 -0
  52. package/commands/plan-verify.md +39 -0
  53. package/commands/promote-experiment.md +129 -0
  54. package/commands/research.md +22 -0
  55. package/commands/run.md +12 -0
  56. package/commands/self-review.md +51 -0
  57. package/commands/start-prd.md +57 -0
  58. package/commands/test.md +34 -0
  59. package/dist/src/assessment-orchestrator.d.ts +78 -0
  60. package/dist/src/assessment-orchestrator.d.ts.map +1 -0
  61. package/dist/src/assessment-orchestrator.js +304 -0
  62. package/dist/src/assessment-orchestrator.js.map +1 -0
  63. package/dist/src/audit-miner.d.ts +21 -0
  64. package/dist/src/audit-miner.d.ts.map +1 -0
  65. package/dist/src/audit-miner.js +421 -0
  66. package/dist/src/audit-miner.js.map +1 -0
  67. package/dist/src/batch-operations.d.ts +30 -0
  68. package/dist/src/batch-operations.d.ts.map +1 -0
  69. package/dist/src/batch-operations.js +339 -0
  70. package/dist/src/batch-operations.js.map +1 -0
  71. package/dist/src/config.d.ts +83 -0
  72. package/dist/src/config.d.ts.map +1 -0
  73. package/dist/src/config.js +176 -0
  74. package/dist/src/config.js.map +1 -0
  75. package/dist/src/context-engine.d.ts +45 -0
  76. package/dist/src/context-engine.d.ts.map +1 -0
  77. package/dist/src/context-engine.js +135 -0
  78. package/dist/src/context-engine.js.map +1 -0
  79. package/dist/src/debug-wrapper.d.ts +58 -0
  80. package/dist/src/debug-wrapper.d.ts.map +1 -0
  81. package/dist/src/debug-wrapper.js +285 -0
  82. package/dist/src/debug-wrapper.js.map +1 -0
  83. package/dist/src/index.d.ts +31 -0
  84. package/dist/src/index.d.ts.map +1 -0
  85. package/dist/src/index.js +149 -0
  86. package/dist/src/index.js.map +1 -0
  87. package/dist/src/interactive-verifier.d.ts +26 -0
  88. package/dist/src/interactive-verifier.d.ts.map +1 -0
  89. package/dist/src/interactive-verifier.js +435 -0
  90. package/dist/src/interactive-verifier.js.map +1 -0
  91. package/dist/src/lessons.d.ts +59 -0
  92. package/dist/src/lessons.d.ts.map +1 -0
  93. package/dist/src/lessons.js +172 -0
  94. package/dist/src/lessons.js.map +1 -0
  95. package/dist/src/log-reader.d.ts +39 -0
  96. package/dist/src/log-reader.d.ts.map +1 -0
  97. package/dist/src/log-reader.js +291 -0
  98. package/dist/src/log-reader.js.map +1 -0
  99. package/dist/src/logger.d.ts +59 -0
  100. package/dist/src/logger.d.ts.map +1 -0
  101. package/dist/src/logger.js +328 -0
  102. package/dist/src/logger.js.map +1 -0
  103. package/dist/src/parallel-retrieval.d.ts +52 -0
  104. package/dist/src/parallel-retrieval.d.ts.map +1 -0
  105. package/dist/src/parallel-retrieval.js +309 -0
  106. package/dist/src/parallel-retrieval.js.map +1 -0
  107. package/dist/src/pattern-extractor.d.ts +30 -0
  108. package/dist/src/pattern-extractor.d.ts.map +1 -0
  109. package/dist/src/pattern-extractor.js +386 -0
  110. package/dist/src/pattern-extractor.js.map +1 -0
  111. package/dist/src/result-aggregator.d.ts +44 -0
  112. package/dist/src/result-aggregator.d.ts.map +1 -0
  113. package/dist/src/result-aggregator.js +315 -0
  114. package/dist/src/result-aggregator.js.map +1 -0
  115. package/dist/src/retrieval.d.ts +139 -0
  116. package/dist/src/retrieval.d.ts.map +1 -0
  117. package/dist/src/retrieval.js +724 -0
  118. package/dist/src/retrieval.js.map +1 -0
  119. package/dist/src/setup/auto-setup.d.ts +15 -0
  120. package/dist/src/setup/auto-setup.d.ts.map +1 -0
  121. package/dist/src/setup/auto-setup.js +157 -0
  122. package/dist/src/setup/auto-setup.js.map +1 -0
  123. package/dist/src/setup/configure-hooks.d.ts +8 -0
  124. package/dist/src/setup/configure-hooks.d.ts.map +1 -0
  125. package/dist/src/setup/configure-hooks.js +16 -0
  126. package/dist/src/setup/configure-hooks.js.map +1 -0
  127. package/dist/src/setup/create-slash-commands.d.ts +8 -0
  128. package/dist/src/setup/create-slash-commands.d.ts.map +1 -0
  129. package/dist/src/setup/create-slash-commands.js +101 -0
  130. package/dist/src/setup/create-slash-commands.js.map +1 -0
  131. package/dist/src/setup/index.d.ts +8 -0
  132. package/dist/src/setup/index.d.ts.map +1 -0
  133. package/dist/src/setup/index.js +17 -0
  134. package/dist/src/setup/index.js.map +1 -0
  135. package/dist/src/setup/inject-claude-md.d.ts +10 -0
  136. package/dist/src/setup/inject-claude-md.d.ts.map +1 -0
  137. package/dist/src/setup/inject-claude-md.js +150 -0
  138. package/dist/src/setup/inject-claude-md.js.map +1 -0
  139. package/dist/src/setup/uninstall.d.ts +22 -0
  140. package/dist/src/setup/uninstall.d.ts.map +1 -0
  141. package/dist/src/setup/uninstall.js +235 -0
  142. package/dist/src/setup/uninstall.js.map +1 -0
  143. package/dist/src/storage.d.ts +192 -0
  144. package/dist/src/storage.d.ts.map +1 -0
  145. package/dist/src/storage.js +1037 -0
  146. package/dist/src/storage.js.map +1 -0
  147. package/dist/src/string-similarity.d.ts +8 -0
  148. package/dist/src/string-similarity.d.ts.map +1 -0
  149. package/dist/src/string-similarity.js +57 -0
  150. package/dist/src/string-similarity.js.map +1 -0
  151. package/dist/src/traces/adapters/browser.d.ts +69 -0
  152. package/dist/src/traces/adapters/browser.d.ts.map +1 -0
  153. package/dist/src/traces/adapters/browser.js +280 -0
  154. package/dist/src/traces/adapters/browser.js.map +1 -0
  155. package/dist/src/traces/adapters/index.d.ts +23 -0
  156. package/dist/src/traces/adapters/index.d.ts.map +1 -0
  157. package/dist/src/traces/adapters/index.js +67 -0
  158. package/dist/src/traces/adapters/index.js.map +1 -0
  159. package/dist/src/traces/adapters/langchain.d.ts +42 -0
  160. package/dist/src/traces/adapters/langchain.d.ts.map +1 -0
  161. package/dist/src/traces/adapters/langchain.js +151 -0
  162. package/dist/src/traces/adapters/langchain.js.map +1 -0
  163. package/dist/src/traces/adapters/opentelemetry.d.ts +46 -0
  164. package/dist/src/traces/adapters/opentelemetry.d.ts.map +1 -0
  165. package/dist/src/traces/adapters/opentelemetry.js +164 -0
  166. package/dist/src/traces/adapters/opentelemetry.js.map +1 -0
  167. package/dist/src/traces/adapters/sentry.d.ts +58 -0
  168. package/dist/src/traces/adapters/sentry.d.ts.map +1 -0
  169. package/dist/src/traces/adapters/sentry.js +212 -0
  170. package/dist/src/traces/adapters/sentry.js.map +1 -0
  171. package/dist/src/traces/index.d.ts +10 -0
  172. package/dist/src/traces/index.d.ts.map +1 -0
  173. package/dist/src/traces/index.js +45 -0
  174. package/dist/src/traces/index.js.map +1 -0
  175. package/dist/src/traces/storage.d.ts +78 -0
  176. package/dist/src/traces/storage.d.ts.map +1 -0
  177. package/dist/src/traces/storage.js +399 -0
  178. package/dist/src/traces/storage.js.map +1 -0
  179. package/dist/src/traces/summarizer.d.ts +83 -0
  180. package/dist/src/traces/summarizer.d.ts.map +1 -0
  181. package/dist/src/traces/summarizer.js +247 -0
  182. package/dist/src/traces/summarizer.js.map +1 -0
  183. package/dist/src/traces/types.d.ts +365 -0
  184. package/dist/src/traces/types.d.ts.map +1 -0
  185. package/dist/src/traces/types.js +14 -0
  186. package/dist/src/traces/types.js.map +1 -0
  187. package/dist/src/types.d.ts +495 -0
  188. package/dist/src/types.d.ts.map +1 -0
  189. package/dist/src/types.js +27 -0
  190. package/dist/src/types.js.map +1 -0
  191. package/docs/agent-surface-policy.md +45 -0
  192. package/docs/memory-setup.md +226 -0
  193. package/hooks/_arch_scan_bg.py +88 -0
  194. package/hooks/_plugin_drift_check_bg.py +85 -0
  195. package/hooks/_session_start_lib.sh +42 -0
  196. package/hooks/git/pre-push +141 -0
  197. package/hooks/hooks.json +126 -0
  198. package/hooks/post-commit +30 -0
  199. package/hooks/pre-commit +29 -0
  200. package/hooks/pre-edit-apppulse.sh +6 -0
  201. package/hooks/pre-edit-architecture.sh +106 -0
  202. package/hooks/pre-edit-rally-point.sh +92 -0
  203. package/hooks/session-start-apppulse.sh +6 -0
  204. package/hooks/session-start-architecture.sh +10 -0
  205. package/hooks/session-start-git-hooks.sh +12 -0
  206. package/hooks/session-start-plugin-drift.sh +27 -0
  207. package/hooks/session-start-rally-point.sh +44 -0
  208. package/hooks/session-start-retrieval.sh +24 -0
  209. package/hooks/session-start-worktree-gc.sh +105 -0
  210. package/hooks/test_apppulse_hooks.sh +6 -0
  211. package/hooks/test_rally_point_hooks.sh +388 -0
  212. package/hooks/test_sec008_hooks.sh +66 -0
  213. package/package.json +111 -0
  214. package/scripts/README.md +301 -0
  215. package/scripts/_db_url.py +61 -0
  216. package/scripts/_paths.py +445 -0
  217. package/scripts/blm.py +162 -0
  218. package/scripts/blm_api.py +263 -0
  219. package/scripts/check_cache_sync.py +490 -0
  220. package/scripts/embed_backend.py +475 -0
  221. package/scripts/install_memory.py +556 -0
  222. package/scripts/lessons_index/__init__.py +77 -0
  223. package/scripts/lessons_index/__main__.py +135 -0
  224. package/scripts/lessons_index/ingest.py +288 -0
  225. package/scripts/lessons_index/query.py +285 -0
  226. package/scripts/lessons_index/schema.py +114 -0
  227. package/scripts/memory_context/__init__.py +610 -0
  228. package/scripts/memory_graph/__init__.py +393 -0
  229. package/scripts/project_resolver.py +172 -0
  230. package/scripts/prune_codex_plugin_cache.py +23 -0
  231. package/scripts/prune_plugin_cache.py +359 -0
  232. package/scripts/semantic_index/__init__.py +482 -0
  233. package/scripts/semantic_index/_bench_hybrid.py +175 -0
  234. package/scripts/semantic_index/backfill.py +263 -0
  235. package/scripts/semantic_index/hybrid.py +174 -0
  236. package/scripts/semantic_index/test_backfill.py +292 -0
  237. package/scripts/semantic_index/test_hybrid.py +541 -0
  238. package/scripts/sync_navgator_lessons.py +666 -0
  239. package/scripts/sync_plugin_cache.py +388 -0
  240. package/skills/agent-rally-point/SKILL.md +165 -0
  241. package/skills/agent-rally-watcher/SKILL.md +83 -0
  242. package/skills/api-registry-bridge/SKILL.md +59 -0
  243. package/skills/architecture/dead/SKILL.md +56 -0
  244. package/skills/architecture/impact/SKILL.md +88 -0
  245. package/skills/architecture/review/SKILL.md +227 -0
  246. package/skills/architecture/rules/SKILL.md +72 -0
  247. package/skills/architecture/scan/SKILL.md +79 -0
  248. package/skills/architecture/trace/SKILL.md +65 -0
  249. package/skills/attribution-standard/SKILL.md +122 -0
  250. package/skills/authentication/SKILL.md +81 -0
  251. package/skills/authentication/references/better-auth-magic-link.md +135 -0
  252. package/skills/authentication/references/better-auth-setup.md +224 -0
  253. package/skills/authentication/references/google-calendar-sync.md +278 -0
  254. package/skills/authentication/references/google-cloud-console.md +159 -0
  255. package/skills/authentication/references/google-geocoding-directions.md +126 -0
  256. package/skills/authentication/references/google-lessons-example-web-app.md +177 -0
  257. package/skills/authentication/references/google-maps.md +162 -0
  258. package/skills/authentication/references/google-oauth-setup.md +343 -0
  259. package/skills/authentication/references/google-places.md +173 -0
  260. package/skills/authentication/references/lessons-example-web-app-better-auth.md +126 -0
  261. package/skills/authentication/references/resend-email.md +227 -0
  262. package/skills/authentication/references/resend-otp-magic-link.md +153 -0
  263. package/skills/authentication/references/supabase-auth.md +172 -0
  264. package/skills/auto-decision-capture/SKILL.md +419 -0
  265. package/skills/build-loop/SKILL.md +516 -0
  266. package/skills/build-loop/detect-plugins.mjs +82 -0
  267. package/skills/build-loop/eval-guide.md +65 -0
  268. package/skills/build-loop/fallbacks.md +549 -0
  269. package/skills/build-loop/phases/fact-check.md +42 -0
  270. package/skills/build-loop/phases/ui-validation.md +267 -0
  271. package/skills/build-loop/references/apple-native-planning.md +439 -0
  272. package/skills/build-loop/references/capability-routing.md +181 -0
  273. package/skills/build-loop/references/codex-subagents.md +98 -0
  274. package/skills/build-loop/references/coordination.md +161 -0
  275. package/skills/build-loop/references/correction-aware-capture.md +177 -0
  276. package/skills/build-loop/references/independent-auditor.md +72 -0
  277. package/skills/build-loop/references/intent-capability-pack.md +170 -0
  278. package/skills/build-loop/references/intent-exploration-prompts.md +96 -0
  279. package/skills/build-loop/references/leadership.md +72 -0
  280. package/skills/build-loop/references/memory.md +284 -0
  281. package/skills/build-loop/references/modular-systems-pack.md +96 -0
  282. package/skills/build-loop/references/output-style.md +129 -0
  283. package/skills/build-loop/references/pay-it-forward-arch.md +98 -0
  284. package/skills/build-loop/references/phase-1-assess.md +218 -0
  285. package/skills/build-loop/references/phase-2-plan.md +77 -0
  286. package/skills/build-loop/references/phase-3-execute.md +47 -0
  287. package/skills/build-loop/references/phase-4-review.md +310 -0
  288. package/skills/build-loop/references/phase-5-iterate.md +71 -0
  289. package/skills/build-loop/references/phase-6-learn.md +56 -0
  290. package/skills/build-loop/references/recent-design-structures.md +274 -0
  291. package/skills/build-loop/references/refactor-history/ASSESSMENT.md +85 -0
  292. package/skills/build-loop/references/refactor-history/STANDALONE_TEST_RUN.md +149 -0
  293. package/skills/build-loop/references/refactor-history/scenarios/01-simple-bugfix.md +32 -0
  294. package/skills/build-loop/references/refactor-history/scenarios/02-ui-build-with-iteration.md +48 -0
  295. package/skills/build-loop/references/refactor-history/scenarios/03-multi-failure-escalation.md +60 -0
  296. package/skills/build-loop/references/refactor-history/scenarios/04-ui-build-ibr-absent.md +51 -0
  297. package/skills/build-loop/references/refactor-history/scenarios/05-refactor-navgator-absent.md +71 -0
  298. package/skills/build-loop/references/refactor-history/scenarios/06-recurring-bug-debugger-absent.md +52 -0
  299. package/skills/build-loop/references/refactor-history/trace-comparison.md +202 -0
  300. package/skills/build-loop/references/self-recursive-dev.md +77 -0
  301. package/skills/build-loop/references/self-review.md +232 -0
  302. package/skills/build-loop/references/ui-io-contract.md +116 -0
  303. package/skills/build-loop/scanners/audit-design-rules.mjs +476 -0
  304. package/skills/build-loop/scanners/require-visual-evidence.mjs +239 -0
  305. package/skills/build-loop/templates/backlog-item.md +35 -0
  306. package/skills/build-loop/templates/codex-worker-prompt.md +89 -0
  307. package/skills/build-loop/templates/ui-subagent-prompt.md +179 -0
  308. package/skills/build-loop/templates/ux-fix-plan.md +40 -0
  309. package/skills/building-with-deepagents/SKILL.md +195 -0
  310. package/skills/building-with-deepagents/references/anti-patterns.md +141 -0
  311. package/skills/building-with-deepagents/references/api-quick-reference.md +184 -0
  312. package/skills/capabilities/SKILL.md +91 -0
  313. package/skills/debug-loop/SKILL.md +278 -0
  314. package/skills/debug-loop/references/convergence-rules.md +145 -0
  315. package/skills/debug-loop/references/swiftui-macos-clunky-ui-debugger.html +823 -0
  316. package/skills/debugging/assess/SKILL.md +111 -0
  317. package/skills/debugging/debug-loop/SKILL.md +231 -0
  318. package/skills/debugging/memory/SKILL.md +147 -0
  319. package/skills/debugging/store/SKILL.md +153 -0
  320. package/skills/debugging-memory/SKILL.md +440 -0
  321. package/skills/debugging-memory/examples/incident-example.json +67 -0
  322. package/skills/debugging-memory/references/incident-documentation.md +280 -0
  323. package/skills/debugging-memory/references/ios-notification-alarm-playbook.md +300 -0
  324. package/skills/debugging-memory/references/pattern-extraction.md +163 -0
  325. package/skills/debugging-memory/references/subagent-integration.md +312 -0
  326. package/skills/defenseclaw-bridge/SKILL.md +156 -0
  327. package/skills/defenseclaw-bridge/references/dc-config-mapping.md +162 -0
  328. package/skills/defenseclaw-bridge/references/output-format.md +108 -0
  329. package/skills/handoff/SKILL.md +94 -0
  330. package/skills/ibr-bridge/SKILL.md +68 -0
  331. package/skills/knowledge/SKILL.md +127 -0
  332. package/skills/knowledge/references/recall-integration.md +83 -0
  333. package/skills/knowledge/templates/madr-minimal.md +56 -0
  334. package/skills/knowledge-review/SKILL.md +112 -0
  335. package/skills/logging-tracer/SKILL.md +202 -0
  336. package/skills/logging-tracer/references/log-analysis.md +128 -0
  337. package/skills/logging-tracer/references/stack-templates.md +274 -0
  338. package/skills/logging-tracer-bridge/SKILL.md +70 -0
  339. package/skills/mcp-builder/SKILL.md +278 -0
  340. package/skills/mcp-builder/references/mcp-security.md +106 -0
  341. package/skills/model-tiering/SKILL.md +216 -0
  342. package/skills/native-ax-driver/SKILL.md +220 -0
  343. package/skills/native-ax-driver/scripts/layout_fill.py +222 -0
  344. package/skills/native-ax-driver/scripts/native_driver.py +495 -0
  345. package/skills/native-ax-driver/swift/bl-ax-driver/Package.swift +20 -0
  346. package/skills/native-ax-driver/swift/bl-ax-driver/Sources/main.swift +542 -0
  347. package/skills/optimize/SKILL.md +202 -0
  348. package/skills/optimize/profiles.md +61 -0
  349. package/skills/plan-verify/SKILL.md +115 -0
  350. package/skills/plan-verify/test-fixtures/example-app-v20-findings.json +27 -0
  351. package/skills/plan-verify/test-fixtures/example-app-v20.md +42 -0
  352. package/skills/plan-verify/test-fixtures/example-app-v22-findings.json +15 -0
  353. package/skills/plan-verify/test-fixtures/example-app-v22.md +1608 -0
  354. package/skills/plan-verify/test-fixtures/unrelated-good-plan-findings.json +15 -0
  355. package/skills/plan-verify/test-fixtures/unrelated-good-plan.md +55 -0
  356. package/skills/plugin-builder/SKILL.md +329 -0
  357. package/skills/plugin-builder/references/authoritative-sources.md +104 -0
  358. package/skills/plugin-builder/references/build-loop-phase-guidance.md +94 -0
  359. package/skills/plugin-builder/references/components-guide.md +221 -0
  360. package/skills/plugin-builder/references/distribution.md +317 -0
  361. package/skills/plugin-builder/references/dual-host-claude-codex.md +220 -0
  362. package/skills/plugin-builder/references/hooks-reference.md +210 -0
  363. package/skills/plugin-builder/references/manifest-schema.md +111 -0
  364. package/skills/plugin-builder/references/plugin-hygiene-lessons.md +307 -0
  365. package/skills/plugin-tests/SKILL.md +94 -0
  366. package/skills/prd-bridge/SKILL.md +135 -0
  367. package/skills/research/SKILL.md +80 -0
  368. package/skills/security-methodology/SKILL.md +105 -0
  369. package/skills/security-methodology/references/cross-source-matrix.md +77 -0
  370. package/skills/security-methodology/references/mitre-atlas-starter.md +83 -0
  371. package/skills/security-methodology/references/nist-600-1-mapping.md +119 -0
  372. package/skills/security-methodology/references/owasp-agentic-top-10.md +157 -0
  373. package/skills/security-methodology/references/owasp-llm-top-10.md +144 -0
  374. package/skills/security-methodology/references/owasp-web-top-10.md +65 -0
  375. package/skills/self-improve/SKILL.md +298 -0
  376. package/skills/spec-writing/SKILL.md +548 -0
  377. package/skills/spec-writing/scripts/check_checklist.py +478 -0
  378. package/skills/sync-skills/SKILL.md +132 -0
  379. package/skills/telemetry/SKILL.md +66 -0
  380. package/skills/ui-design/SKILL.md +75 -0
  381. package/skills/ui-design/references/design-patterns-multi.md +165 -0
  382. package/skills/ui-design/references/design-preferences-from-owned-apps.md +150 -0
  383. package/skills/ui-design/references/evidence-capture-policy.md +55 -0
  384. package/skills/ui-design/references/ui-guidance-sources.md +330 -0
  385. package/skills/ui-design/references/universal-design-principles.alt.md +182 -0
  386. package/skills/ui-design/references/universal-design-principles.md +419 -0
  387. package/templates/memory/MEMORY.md.template +55 -0
  388. package/templates/memory/constitution.md.template +64 -0
  389. package/templates/memory/manifest.json +47 -0
@@ -0,0 +1,280 @@
1
+ <!-- SPDX-FileCopyrightText: 2025-2026 Tyrone Ross, Jr <46267523+tyroneross@users.noreply.github.com> | SPDX-License-Identifier: Apache-2.0 -->
2
+
3
+ # Incident Documentation Guide
4
+
5
+ Complete guide to documenting debugging incidents for effective future retrieval.
6
+
7
+ ## Incident Structure
8
+
9
+ Each incident contains these fields:
10
+
11
+ ### Identification
12
+
13
+ ```json
14
+ {
15
+ "incident_id": "INC_20241225_143052_abc1",
16
+ "timestamp": 1735135852000,
17
+ "session_id": "SESSION_1735135800000_xyz"
18
+ }
19
+ ```
20
+
21
+ - **incident_id**: Auto-generated, format `INC_YYYYMMDD_HHMMSS_random`
22
+ - **timestamp**: Unix timestamp of when incident was stored
23
+ - **session_id**: Links to the debugging session that produced this fix
24
+
25
+ ### Symptom
26
+
27
+ ```json
28
+ {
29
+ "symptom": "Search results not displaying after typing in the search box",
30
+ "symptom_type": "ui"
31
+ }
32
+ ```
33
+
34
+ - **symptom**: User-facing description of what went wrong
35
+ - **symptom_type**: Category (ui, api, logic, crash, performance, build)
36
+
37
+ **Writing effective symptoms:**
38
+ - Describe observable behavior
39
+ - Include context (what action triggered it)
40
+ - Avoid implementation details
41
+ - Keep it searchable
42
+
43
+ ### Root Cause
44
+
45
+ ```json
46
+ {
47
+ "root_cause": {
48
+ "description": "The debounce hook was resetting the search query on every keystroke because the dependency array included a reference-unstable callback. Each render created a new callback reference, triggering the debounce reset.",
49
+ "file": "src/hooks/useDebounce.ts",
50
+ "line_range": [15, 28],
51
+ "code_snippet": "const debouncedValue = useMemo(() => {...}, [value, callback])",
52
+ "category": "react-hooks",
53
+ "confidence": 0.95
54
+ }
55
+ }
56
+ ```
57
+
58
+ - **description**: Detailed technical explanation (minimum 50 characters)
59
+ - **file**: Primary file where the bug existed
60
+ - **line_range**: Affected line numbers
61
+ - **code_snippet**: Relevant code (helps future matching)
62
+ - **category**: Technical category (react-hooks, api, config, dependency, logic)
63
+ - **confidence**: How certain the diagnosis is (0.0-1.0)
64
+
65
+ **Root cause quality checklist:**
66
+ - Explains WHY the bug occurred, not just WHERE
67
+ - Includes enough detail to understand without reading the code
68
+ - References specific code patterns or concepts
69
+ - Assigns appropriate confidence based on certainty
70
+
71
+ ### Fix
72
+
73
+ ```json
74
+ {
75
+ "fix": {
76
+ "approach": "Wrapped the callback in useCallback to stabilize the reference, then updated the debounce hook's dependency array to only include the memoized callback.",
77
+ "changes": [
78
+ {
79
+ "file": "src/hooks/useDebounce.ts",
80
+ "lines_changed": 8,
81
+ "change_type": "modify",
82
+ "summary": "Added useCallback wrapper and updated dependencies"
83
+ },
84
+ {
85
+ "file": "src/components/SearchBar.tsx",
86
+ "lines_changed": 3,
87
+ "change_type": "modify",
88
+ "summary": "Memoized the search handler callback"
89
+ }
90
+ ],
91
+ "pattern_used": null,
92
+ "time_to_fix": 25
93
+ }
94
+ }
95
+ ```
96
+
97
+ - **approach**: High-level description of the solution
98
+ - **changes**: Array of file changes with details
99
+ - **pattern_used**: Pattern ID if an existing pattern was applied
100
+ - **time_to_fix**: Minutes spent (helps estimate similar bugs)
101
+
102
+ ### Verification
103
+
104
+ ```json
105
+ {
106
+ "verification": {
107
+ "status": "verified",
108
+ "regression_tests_passed": true,
109
+ "user_journey_tested": true,
110
+ "tests_run": ["search.test.ts", "debounce.test.ts"],
111
+ "success_criteria_met": true
112
+ }
113
+ }
114
+ ```
115
+
116
+ - **status**: verified | partial | unverified
117
+ - **regression_tests_passed**: Existing tests still pass
118
+ - **user_journey_tested**: Manual testing of the affected feature
119
+ - **tests_run**: Specific test files executed
120
+ - **success_criteria_met**: Bug no longer reproducible
121
+
122
+ ### Quality Gates
123
+
124
+ ```json
125
+ {
126
+ "quality_gates": {
127
+ "guardian_validated": true,
128
+ "tested_e2e": false,
129
+ "tested_from_ui": true,
130
+ "security_reviewed": false,
131
+ "architect_reviewed": false
132
+ }
133
+ }
134
+ ```
135
+
136
+ Optional validation checkpoints for higher-stakes fixes.
137
+
138
+ ### Tags
139
+
140
+ ```json
141
+ {
142
+ "tags": ["react", "hooks", "useMemo", "debounce", "search"],
143
+ "files_changed": ["src/hooks/useDebounce.ts", "src/components/SearchBar.tsx"],
144
+ "agent_used": "coder"
145
+ }
146
+ ```
147
+
148
+ - **tags**: Searchable keywords for categorization
149
+ - **files_changed**: All modified files
150
+ - **agent_used**: Which Claude agent fixed this
151
+
152
+ ## Quality Scoring
153
+
154
+ The memory system calculates a quality score (0-100%) based on:
155
+
156
+ | Component | Weight | Criteria |
157
+ |-----------|--------|----------|
158
+ | Root Cause | 30% | Description length, confidence, file reference |
159
+ | Fix | 30% | Approach detail, change documentation |
160
+ | Verification | 20% | Status, tests run, criteria met |
161
+ | Documentation | 20% | Tags, files listed, agent noted |
162
+
163
+ **Quality targets:**
164
+ - 75%+: Excellent - highly reliable for future matching
165
+ - 50-74%: Good - useful but may need verification
166
+ - <50%: Fair - stored but lower priority in results
167
+
168
+ ## How Claude Code Stores Incidents
169
+
170
+ Claude Code should **directly write JSON files** to store incidents. No CLI command needed.
171
+
172
+ ### Step-by-Step
173
+
174
+ **1. Create directory (if needed):**
175
+ ```bash
176
+ mkdir -p .claude/memory/incidents
177
+ ```
178
+
179
+ **2. Generate incident ID:**
180
+ ```
181
+ INC_YYYYMMDD_HHMMSS_xxxx
182
+ ```
183
+ Example: `INC_20241231_143052_a7b2` where:
184
+ - `20241231` = date (Dec 31, 2024)
185
+ - `143052` = time (14:30:52)
186
+ - `a7b2` = 4 random alphanumeric chars
187
+
188
+ **3. Write JSON file:**
189
+ ```
190
+ .claude/memory/incidents/INC_20241231_143052_a7b2.json
191
+ ```
192
+
193
+ **4. Include required fields:**
194
+ ```json
195
+ {
196
+ "incident_id": "INC_20241231_143052_a7b2",
197
+ "timestamp": 1735654252000,
198
+ "symptom": "Description of the bug",
199
+ "root_cause": {
200
+ "description": "Technical explanation",
201
+ "file": "path/to/file.ts",
202
+ "category": "logic",
203
+ "confidence": 0.85
204
+ },
205
+ "fix": {
206
+ "approach": "How it was fixed",
207
+ "changes": []
208
+ },
209
+ "tags": ["searchable", "keywords"],
210
+ "quality_score": 0.75
211
+ }
212
+ ```
213
+
214
+ ### When to Store
215
+
216
+ - **After fixing a bug** - Immediately document while context is fresh
217
+ - **After debugging session** - Even if fix wasn't found, document the investigation
218
+ - **After pattern identified** - If you notice recurring issues
219
+
220
+ ### Workflow Example
221
+
222
+ ```
223
+ 1. Bug reported: "Search not working"
224
+ 2. Search memory via `debugger search` MCP tool: "search not working"
225
+ 3. Found match? → Apply fix
226
+ No match? → Investigate and fix
227
+ 4. Store via `debugger store` MCP tool (or write JSON to .claude/memory/incidents/INC_xxx.json)
228
+ 5. Future searches will find this incident
229
+ ```
230
+
231
+ ## Storage Modes
232
+
233
+ ### Local Mode (default)
234
+
235
+ Incidents stored in project's `.claude/memory/incidents/`:
236
+ - Project-specific context
237
+ - Doesn't pollute other projects
238
+ - Best for project-specific bugs
239
+
240
+ ### Shared Mode
241
+
242
+ Incidents stored in `.build-loop/issues/`:
243
+ - Cross-project learning
244
+ - Common patterns accessible everywhere
245
+ - Best for reusable solutions
246
+
247
+ Set via environment:
248
+ ```bash
249
+ export CLAUDE_MEMORY_MODE=shared
250
+ ```
251
+
252
+ ## Retrieval Strategy
253
+
254
+ When searching for matches, the system:
255
+
256
+ 1. **Pattern match first** (90% confidence threshold)
257
+ 2. **Incident search** (70% confidence threshold)
258
+ 3. **Keyword similarity** using Jaccard scoring
259
+ 4. **Temporal weighting** - prefer recent incidents (90-day window)
260
+
261
+ Higher quality incidents rank higher in results.
262
+
263
+ ## Interactive Documentation
264
+
265
+ Use interactive mode for guided documentation:
266
+
267
+ ```typescript
268
+ import { storeIncident } from '<coding-debugger-package>';
269
+
270
+ await storeIncident(incident, {
271
+ interactive: true,
272
+ validate_schema: true
273
+ });
274
+ ```
275
+
276
+ The system prompts for:
277
+ - Missing required fields
278
+ - Short descriptions that need expansion
279
+ - Verification status confirmation
280
+ - Tag suggestions based on symptom
@@ -0,0 +1,300 @@
1
+ <!-- SPDX-FileCopyrightText: 2025-2026 Tyrone Ross, Jr <46267523+tyroneross@users.noreply.github.com> | SPDX-License-Identifier: Apache-2.0 -->
2
+
3
+ # iOS Notification / Alarm Diagnostic Playbook
4
+
5
+ Domain reference for diagnosing "the alarm doesn't fire" on iOS apps that use `UNUserNotificationCenter` for timed completion alerts (Pomodoro timers, meditation apps, fasting trackers, sleep alarms, anything with a scheduled future notification).
6
+
7
+ Source: build-loop run on a sample timer app (2026-04-26). Single high-confidence root cause; the playbook generalizes the diagnostic moves.
8
+
9
+ ## Smoking-gun pattern
10
+
11
+ > Notification scheduled ONLY at background transition + unconditionally cancelled on every foreground return.
12
+
13
+ If the codebase has both of these, several normal user flows leave nothing armed. Before doing anything else, search for these two patterns; if both exist, you have your root cause in under 60 seconds.
14
+
15
+ ```bash
16
+ grep -n "scheduleTimedNotification\|scheduleCompletion" --include="*.swift" -r .
17
+ grep -n "removeAllPendingNotificationRequests\|cancelScheduled" --include="*.swift" -r .
18
+ ```
19
+
20
+ If schedule sites are inside `applicationDidEnterBackground`, `scenePhase == .background`, or a single `handleBackgroundTransition`, **and** cancel sites run on every foreground return without checking timer state, file the finding as `KNOWN_FIX` against `INC_IOS_ALARM_SCHEDULE_TIMING`.
21
+
22
+ ## Hypothesis tree (8 priorities)
23
+
24
+ When the smoking-gun pattern doesn't apply, descend in this order. Stop at the first confirmed cause.
25
+
26
+ 1. **Schedule timing** — Where is `UNUserNotificationCenterAdd` actually called? If the only call site is a background-transition hook, force-quit / fast suspend / foreground-only sessions never arm.
27
+ 2. **Foreground cancel** — Is `removePendingNotificationRequests` called unconditionally on foreground? After fixing #1, this is the new bug.
28
+ 3. **Authorization timing** — When is `requestAuthorization` invoked? If "after first completion," the first session runs unauthorized and silently fails to deliver.
29
+ 4. **Identifier collisions** — Are multiple call sites adding requests with different UUIDs? Cancellation by-identifier won't catch them; the OS may dedupe or the app may leak pending requests.
30
+ 5. **Trigger interval** — Is `timeInterval` ever 0 or negative (clock skew, timer overshoot)? `UNTimeIntervalNotificationTrigger` requires `> 0`.
31
+ 6. **Sound asset path** — Is the chime asset actually copied into `Bundle.main`? `UNNotificationSound(named:)` silently falls back to no sound if the file isn't found.
32
+ 7. **Delegate registration** — Is `UNUserNotificationCenter.current().delegate` set, and does `willPresent` return `[.banner, .sound]`? If not, foreground delivery is invisible.
33
+ 8. **Interruption level + Focus** — Is `interruptionLevel = .timeSensitive` set, and is the iOS Time Sensitive entitlement active? Without it, Focus modes suppress the banner.
34
+
35
+ ## Parallel-investigator decomposition
36
+
37
+ For "alarm doesn't fire," dispatch 3 concurrent investigators. None share state.
38
+
39
+ | Investigator | Scope | Files / Symbols |
40
+ |---|---|---|
41
+ | **Timer architecture** | Where is the notification scheduled, where is it cancelled, what other paths affect lifetime | `*TimerEngine*.swift`, `*Notification*.swift`, every `scheduleCompletion` / `cancelScheduled*` call site |
42
+ | **Audio + haptic routing** | Foreground completion path, AVAudioSession config, ringer-bypass, CoreHaptics fallbacks | `*AudioService*.swift`, `AudioSessionManager*`, `playChime`, `UINotificationFeedbackGenerator`, `CHHapticEngine` |
43
+ | **Lifecycle + cancel** | scenePhase transitions, app delegate hooks, force-quit handling, ActiveSessionRecovery | `SampleTimerApp+iOS.swift` or `App+iOS.swift`, `applicationWillTerminate`, `scenePhase.onChange`, recovery code |
44
+
45
+ ## Symbol search lists
46
+
47
+ Run these in any iOS project as a starting cluster.
48
+
49
+ ```bash
50
+ # Notification lifecycle
51
+ grep -rn "UNUserNotificationCenter\|UNTimeIntervalNotificationTrigger\|UNNotificationRequest" --include="*.swift" .
52
+ grep -rn "removePendingNotificationRequests\|removeAllPendingNotificationRequests" --include="*.swift" .
53
+
54
+ # Authorization
55
+ grep -rn "requestAuthorization\|getNotificationSettings\|UNAuthorizationStatus" --include="*.swift" .
56
+
57
+ # Foreground completion path
58
+ grep -rn "willPresent\|UNNotificationPresentationOptions\|interruptionLevel" --include="*.swift" .
59
+
60
+ # Audio routing
61
+ grep -rn "AVAudioSession\|AVAudioPlayer\|AudioServicesPlaySystemSound" --include="*.swift" .
62
+
63
+ # Haptics
64
+ grep -rn "CHHapticEngine\|UINotificationFeedbackGenerator\|UIImpactFeedbackGenerator" --include="*.swift" .
65
+
66
+ # Lifecycle
67
+ grep -rn "scenePhase\|applicationDidEnterBackground\|applicationWillTerminate" --include="*.swift" .
68
+ ```
69
+
70
+ ## File anti-patterns to flag
71
+
72
+ Treat the presence of any of these as a high-priority finding.
73
+
74
+ | Anti-pattern | Where it bites |
75
+ |---|---|
76
+ | `removeAllPendingNotificationRequests()` called outside reset/teardown | Wipes unrelated app notifications, masks identifier mistakes, blocks per-session debugging |
77
+ | Schedule call inside `if scenePhase == .background` only | Force-quit, fast suspend, foreground-only sessions go unarmed |
78
+ | Foreground transition unconditionally calls `cancelScheduledNotifications()` | Lock-then-quick-unlock cancels the alarm while the timer is still running |
79
+ | `requestAuthorization` invoked after first session completion | First session is silent; user-perceived "alarm broken" |
80
+ | `AVAudioPlayer` chime with no fallback to `.default` | Missing or corrupted bundled asset = silent foreground completion |
81
+ | `UNNotificationSound(named: "chime.caf")` without `Bundle.main.url(forResource:withExtension:)` check | Silent fail at delivery time, no log |
82
+ | Single delegate impl on a multi-platform target without `#if os(...)` for `badgeSetting`, `UIImpactFeedbackGenerator`, etc. | watchOS / macOS build breaks; or worse, runtime crashes |
83
+ | `UIBackgroundModes: [audio]` to keep the timer "alive" | App Store rejection; not the fix |
84
+
85
+ ## Verification matrix (15-state iPhone)
86
+
87
+ Tier the matrix by what each state actually exercises. **Sim-verifiable** runs in `xcrun simctl`; **real-device** needs hardware.
88
+
89
+ | # | Scenario | Expected | Sim | Device |
90
+ |---|---|---|---|---|
91
+ | 1 | Foreground tick to zero | banner + sound + haptic | sound | haptic |
92
+ | 2 | Background then zero | banner + sound while locked | ✅ | ✅ |
93
+ | 3 | Locked then zero | lock-screen banner + sound | ✅ | ✅ |
94
+ | 4 | Force-quit then zero | banner still fires | ✅ | ✅ |
95
+ | 5 | Pause → resume → zero | fires at new endDate | ✅ | ✅ |
96
+ | 6 | Reset mid-session | no banner | ✅ | ✅ |
97
+ | 7 | Scrub: bg → fg → bg → zero | banner fires once | ✅ | ✅ |
98
+ | 8 | Focus mode allowed | banner fires | ✅ | ✅ |
99
+ | 9 | Focus mode blocked | banner suppressed (expected) | ✅ | ✅ |
100
+ | 10 | Silent switch on | sound through (AVAudioSession.playback) | n/a | device-only |
101
+ | 11 | DND on | banner suppressed unless Critical Alerts | n/a | device-only |
102
+ | 12 | Permissions: notDetermined | first Start triggers prompt | ✅ | ✅ |
103
+ | 13 | Permissions: denied | no banner, no crash, log warns | ✅ | ✅ |
104
+ | 14 | Backgrounded > 90 min | recovery flow | ✅ | ✅ |
105
+ | 15 | Watch session running concurrently | iPhone alarm independent | partial | ✅ |
106
+
107
+ ## Diagnostic logging template
108
+
109
+ Every iOS notification fix should ship with structured logging on day 1, not as a follow-up. Recommended subsystem and category:
110
+
111
+ ```swift
112
+ import os.log
113
+
114
+ enum AlertDiagnostics {
115
+ private static let logger = Logger(
116
+ subsystem: Bundle.main.bundleIdentifier ?? "com.example",
117
+ category: "alert-diag"
118
+ )
119
+ static func scheduled(identifier: String, afterSeconds: Int, source: String) { /* ... */ }
120
+ static func cancelled(identifier: String, source: String) { /* ... */ }
121
+ static func cancelSkipped(reason: String, source: String) { /* ... */ }
122
+ static func scenePhase(_ phase: String) { /* ... */ }
123
+ static func dumpAuthSettings() { /* ... */ }
124
+ static func dumpPendingRequests(reason: String) { /* ... */ }
125
+ }
126
+ ```
127
+
128
+ Filter Console.app on `subsystem:com.example category:alert-diag` to read the trail. The `[alert-diag]` prefix and `id=...` tokens make grep one-shot.
129
+
130
+ ## Verdict templates
131
+
132
+ When this playbook applies, use one of the following incident IDs.
133
+
134
+ - `INC_IOS_ALARM_SCHEDULE_TIMING` — schedule call site only at background transition
135
+ - `INC_IOS_ALARM_UNCONDITIONAL_CANCEL` — foreground cancel runs while timer still active
136
+ - `INC_IOS_ALARM_AUTH_LATE` — requestAuthorization after first completion
137
+ - `INC_IOS_ALARM_CHIME_MISSING` — bundled audio asset not in Bundle.main
138
+ - `INC_IOS_ALARM_FOCUS_BLOCKED` — interruption level missing or Focus suppressing
139
+
140
+ ## Confidence
141
+
142
+ This playbook is built from one canonical incident (sample timer app, 2026-04-26). Promote to "pattern" status (`PTN_IOS_ALARM_*`) once 3+ incidents share the smoking-gun signature.
143
+
144
+ ---
145
+
146
+ ## Multi-alert identifier-set pattern
147
+
148
+ Added 2026-04-26 (sample timer app build 72) when alert customization landed: count, persistence (re-fire every N seconds), and backup escalation expanded the per-transition notification stack from 1 request to up to ~10. The schedule and cancel paths must agree on the identifier set or the cancel will silently leak requests.
149
+
150
+ ### The bug pattern this prevents
151
+
152
+ > User changes alert count from 3 to 1 mid-session. Pause cancels with the legacy single-identifier `[timerCompletionIdentifier]`. The other two requests survive. Pomodoro fires three notifications during a paused session — exactly the failure mode persistence+backup were trying to avoid.
153
+
154
+ The same shape appears whenever a feature changes the cardinality or naming of pending requests (persistence toggles, A/B sound tests, multi-stage celebrations). The cancel path is written first against a smaller cardinality, then someone bumps the cardinality on the schedule side without updating cancel.
155
+
156
+ ### The rule
157
+
158
+ **Schedule and cancel must call a single identifier-set generator function, parameterized by the same inputs.** Any feature that adds requests to a session bumps the cardinality through that one function, and both sides update for free.
159
+
160
+ ```swift
161
+ static func pomodoroNotificationIdentifiers(sessionTag: String, config: AlertConfig) -> [String] {
162
+ var ids: [String] = [timerCompletionIdentifier] // legacy single id, always cleared
163
+ let prefix = "\(timerCompletionIdentifier).tag.\(sessionTag)"
164
+ for i in 0..<max(1, min(3, config.count)) {
165
+ ids.append("\(prefix).alert.\(i)")
166
+ }
167
+ if config.persistenceEnabled {
168
+ let n = min(6, max(0, config.persistenceMaxSec / max(15, config.persistenceIntervalSec)))
169
+ for j in 0..<n { ids.append("\(prefix).persist.\(j)") }
170
+ }
171
+ return ids
172
+ }
173
+ ```
174
+
175
+ `scheduleCompletion(...)` calls this and adds requests under each id. `cancelScheduledCompletion(config:sessionTag:source:)` calls the same function and passes the array to `removePendingNotificationRequests(withIdentifiers:)`. The legacy bare identifier stays in the set forever so a clean upgrade absorbs any in-flight build-71 request.
176
+
177
+ ### Caller responsibility
178
+
179
+ The caller (TimerEngine) remembers `armedSessionTag` and `armedConfig` at schedule time and passes them back at cancel time:
180
+
181
+ ```swift
182
+ private func armCompletionStack(...) {
183
+ let config = alertSettings.config(forBreak: forBreak)
184
+ let tag = currentSessionTag(forBreak: forBreak)
185
+ armedSessionTag = tag
186
+ armedConfig = config
187
+ delegate?.scheduleTimedNotificationWithConfig(..., config: config, sessionTag: tag, ...)
188
+ }
189
+
190
+ private func cancelCompletionStack(source: String) {
191
+ if let tag = armedSessionTag, let config = armedConfig {
192
+ delegate?.cancelScheduledNotificationsWithConfig(config: config, sessionTag: tag, source: source)
193
+ } else {
194
+ delegate?.cancelScheduledNotifications(source: source) // legacy fallback
195
+ }
196
+ armedSessionTag = nil
197
+ armedConfig = nil
198
+ }
199
+ ```
200
+
201
+ `sessionTag` is derived from `phaseStartDate` so re-arming during a phase keeps the same tag (idempotent replace), but a new phase gets a fresh tag (so the prior phase's stack is not accidentally cancelled by a subsequent cancel call).
202
+
203
+ ### Diagnostics
204
+
205
+ Log the full identifier list, not the count alone, on every schedule and cancel. `[alert-diag] schedule-stack ids=[a,b,c,...] cfg-count=3 persist=true` is greppable; "scheduled 3 notifications" is not.
206
+
207
+ ### Smoking gun for the bug
208
+
209
+ `getPendingNotificationRequests` returns N entries after a "cancel everything" lifecycle hook (pause, reset, foreground-not-running). The fix is always: route the cancel through the same generator the schedule used.
210
+
211
+ ---
212
+
213
+ ## iOS Local Network discovery checklist
214
+
215
+ Added 2026-04-26 (sample timer app build 72) when "Listener failed: NWError -65569 DefunctConnection" appeared in Profile → Nearby Devices. iOS Local Network has three independent failure modes; check them in this order.
216
+
217
+ ### 1. Plist stack (one-time setup, easy to forget)
218
+
219
+ ```xml
220
+ <!-- Info.plist for iOS, INFOPLIST_KEY_* in xcconfig/project.yml for macOS -->
221
+ <key>NSLocalNetworkUsageDescription</key>
222
+ <string>Why your app needs the local network — user-readable, shown in the system permission prompt.</string>
223
+ <key>NSBonjourServices</key>
224
+ <array>
225
+ <string>_yourapp._tcp</string>
226
+ </array>
227
+ ```
228
+
229
+ If `NSLocalNetworkUsageDescription` is missing, iOS 14+ kills the listener on creation and the user never sees a permission prompt — so they cannot grant access. Symptom is identical to "permission denied". The service type in `NSBonjourServices` MUST match the string passed to `NWBrowser` and `NWListener.Service(type:)` exactly. A typo silently produces empty browser results.
230
+
231
+ ### 2. NWListener / NWBrowser retain rule
232
+
233
+ Listener and browser must be retained as instance properties:
234
+
235
+ ```swift
236
+ final class LocalNetworkSync {
237
+ private var listener: NWListener? // strong reference required
238
+ private var browser: NWBrowser?
239
+ }
240
+ ```
241
+
242
+ A local `let listener = try NWListener(...)` inside a function is destroyed at scope exit. The mDNSResponder XPC connection drops with it, the user sees nothing, and the bug presents as "no devices found" with no error path.
243
+
244
+ ### 3. Distinguish -65569 (transient) from -65570 (permission)
245
+
246
+ | Code | NWError shape | Meaning | Action |
247
+ |--------|-----------------------|-------------------------------------|---------------------------------|
248
+ | -65569 | `.dns(-65569)` | DefunctConnection — mDNSResponder XPC dropped (wifi change, simulator quirk, suspension) | Restart with bounded backoff |
249
+ | -65570 | `.dns(-65570)` | Permission denied / not granted | Show "Open Settings" — DO NOT restart |
250
+
251
+ The historical fix of "show NWError.localizedDescription" surfaces both as raw jargon. Map them:
252
+
253
+ ```swift
254
+ nonisolated private static func isPermissionDenied(_ error: NWError) -> Bool {
255
+ if case .dns(let code) = error, Int(code) == -65570 { return true }
256
+ return false
257
+ }
258
+ ```
259
+
260
+ UI translation: -65569 should say "Searching… reconnecting after a network change." (recoverable). -65570 should show a permission card with an Open Settings button (`UIApplication.openSettingsURLString`).
261
+
262
+ ### 4. Restart-on-defunct backoff
263
+
264
+ `.failed(-65569)` on a retained listener requires recreation — the existing `NWListener` instance is permanently dead. Bounded backoff prevents battery drain on a permanently broken environment:
265
+
266
+ ```swift
267
+ private static let restartDelaysSec: [Double] = [1.0, 3.0, 8.0]
268
+ private var listenerRestartAttempts: Int = 0
269
+
270
+ private func scheduleListenerRestart() {
271
+ let delay = Self.restartDelaysSec[min(listenerRestartAttempts, Self.restartDelaysSec.count - 1)]
272
+ listenerRestartAttempts += 1
273
+ Task { @MainActor [weak self] in
274
+ try? await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
275
+ guard let self, self.isActive, !self.permissionDenied else { return }
276
+ self.listener?.cancel()
277
+ self.listener = nil
278
+ self.startListener()
279
+ }
280
+ }
281
+ ```
282
+
283
+ Reset `listenerRestartAttempts = 0` on `.ready` so a successful restart re-enables fast retry on the next failure. **Suspend restart while `permissionDenied` is true** — there is no point restarting a listener the system is going to kill again until the user grants permission.
284
+
285
+ ### 5. Smoking gun signatures
286
+
287
+ | Symptom | Cause |
288
+ |------------------------------------------------------------------------|--------------------------------------|
289
+ | Listener fails on first launch, no permission prompt ever shown | Missing `NSLocalNetworkUsageDescription` |
290
+ | Browser results always empty despite peers running | Service type mismatch between Plist and code |
291
+ | Listener works in foreground, dies after backgrounding | Listener stored in a local, not retained |
292
+ | -65569 appears intermittently after wifi changes / app resume | Normal — needs restart-on-defunct backoff |
293
+ | -65570 with no in-Settings entry for the app | Plist string was added after first launch — uninstall/reinstall to retrigger |
294
+
295
+ ### Incident IDs
296
+
297
+ - `INC_IOS_LOCAL_NETWORK_DEFUNCT` — -65569 with no restart logic
298
+ - `INC_IOS_LOCAL_NETWORK_PLIST` — missing usage description or Bonjour service entry
299
+ - `INC_IOS_LOCAL_NETWORK_RETAIN` — listener/browser destroyed at scope exit
300
+ - `INC_IOS_LOCAL_NETWORK_PERMISSION_UI` — raw NWError shown to user instead of actionable Open Settings prompt