@loomfsm/bundle-code 0.1.0

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 (81) hide show
  1. package/LICENSE +201 -0
  2. package/agents/acceptance.md +141 -0
  3. package/agents/api-contract.md +89 -0
  4. package/agents/architect.md +52 -0
  5. package/agents/challenger-reviewer.md +104 -0
  6. package/agents/classifier.md +74 -0
  7. package/agents/code-analyzer.md +43 -0
  8. package/agents/context-doc-verifier.md +94 -0
  9. package/agents/dependency-auditor.md +42 -0
  10. package/agents/implementer.md +135 -0
  11. package/agents/logic-reviewer.md +132 -0
  12. package/agents/migration.md +55 -0
  13. package/agents/performance.md +95 -0
  14. package/agents/plan-conformance.md +127 -0
  15. package/agents/plan-grounding-check.md +106 -0
  16. package/agents/planner.md +143 -0
  17. package/agents/playwright.md +68 -0
  18. package/agents/research.md +52 -0
  19. package/agents/security.md +88 -0
  20. package/agents/style-reviewer.md +85 -0
  21. package/agents/test.md +206 -0
  22. package/agents/ui-consistency.md +75 -0
  23. package/dist/manifest.d.ts +2 -0
  24. package/dist/manifest.js +34 -0
  25. package/dist/manifest.js.map +1 -0
  26. package/dist/src/bundle.d.ts +2 -0
  27. package/dist/src/bundle.js +424 -0
  28. package/dist/src/bundle.js.map +1 -0
  29. package/dist/src/index.d.ts +5 -0
  30. package/dist/src/index.js +14 -0
  31. package/dist/src/index.js.map +1 -0
  32. package/dist/src/invariants.d.ts +10 -0
  33. package/dist/src/invariants.js +208 -0
  34. package/dist/src/invariants.js.map +1 -0
  35. package/dist/src/policy-resolver.d.ts +2 -0
  36. package/dist/src/policy-resolver.js +65 -0
  37. package/dist/src/policy-resolver.js.map +1 -0
  38. package/dist/src/sandbox-rules.d.ts +2 -0
  39. package/dist/src/sandbox-rules.js +40 -0
  40. package/dist/src/sandbox-rules.js.map +1 -0
  41. package/dist/test/bundle.test.d.ts +1 -0
  42. package/dist/test/bundle.test.js +289 -0
  43. package/dist/test/bundle.test.js.map +1 -0
  44. package/dist/test/sandbox-rules.test.d.ts +1 -0
  45. package/dist/test/sandbox-rules.test.js +73 -0
  46. package/dist/test/sandbox-rules.test.js.map +1 -0
  47. package/knowledge/references/api-design.md +188 -0
  48. package/knowledge/references/arch-patterns.md +106 -0
  49. package/knowledge/references/caching.md +190 -0
  50. package/knowledge/references/concurrency.md +195 -0
  51. package/knowledge/references/db-postgres.md +153 -0
  52. package/knowledge/references/e2e-flutter.md +56 -0
  53. package/knowledge/references/e2e-playwright.md +53 -0
  54. package/knowledge/references/error-handling.md +208 -0
  55. package/knowledge/references/next-app-router.md +231 -0
  56. package/knowledge/references/observability.md +169 -0
  57. package/knowledge/references/optimization-strategy.md +197 -0
  58. package/knowledge/references/perf-flutter.md +62 -0
  59. package/knowledge/references/perf-nestjs.md +59 -0
  60. package/knowledge/references/perf-python.md +50 -0
  61. package/knowledge/references/perf-react.md +52 -0
  62. package/knowledge/references/react19.md +176 -0
  63. package/knowledge/references/redis.md +175 -0
  64. package/knowledge/references/security-backend.md +219 -0
  65. package/knowledge/references/test-flutter.md +65 -0
  66. package/knowledge/references/test-nestjs.md +82 -0
  67. package/knowledge/references/test-python.md +76 -0
  68. package/knowledge/references/test-react.md +66 -0
  69. package/knowledge/references/test-strategy.md +175 -0
  70. package/knowledge/references/ui-flutter.md +56 -0
  71. package/knowledge/references/ui-web.md +51 -0
  72. package/package.json +34 -0
  73. package/schemas/agent-feedback.schema.json +80 -0
  74. package/schemas/category-vocab.json +170 -0
  75. package/schemas/classifier-output.schema.json +53 -0
  76. package/schemas/finding.schema.json +92 -0
  77. package/schemas/pipeline-state.schema.json +238 -0
  78. package/schemas/reviewer-output.schema.json +62 -0
  79. package/schemas/state-extension.schema.json +53 -0
  80. package/schemas/validator-output.schema.json +48 -0
  81. package/stack-candidates.yaml +248 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle.test.js","sourceRoot":"","sources":["../../test/bundle.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAEhE,OAAO,EACL,WAAW,EACX,WAAW,EACX,UAAU,EACV,OAAO,EACP,UAAU,EACV,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,UAAU,MAAM,kBAAkB,CAAC;AAC1C,OAAO,YAAY,MAAM,gBAAgB,CAAC;AAE1C,0EAA0E;AAC1E,wDAAwD;AACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAE3E,SAAS,YAAY;IACnB,OAAO,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,OAAO,CAAC,GAAW;IAC1B,IAAI,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACP,2BAA2B;IAC7B,CAAC;IACD,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,WAAW,CAAC,IAAI,GAAG,qBAAqB;IAC/C,OAAO;QACL,IAAI;QACJ,YAAY,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,gBAAgB,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;QACnF,KAAK,CAAC,KAAK;YACT,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC/D,CAAC;KACF,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,GAAW,EAAE,GAAa;IACvD,MAAM,mBAAmB,CAAC;QACxB,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,wBAAwB,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC;QAClE,WAAW,EAAE,GAAG;QAChB,GAAG;KACJ,CAAC,CAAC;AACL,CAAC;AAED,+EAA+E;AAC/E,uDAAuD;AACvD,+EAA+E;AAE/E,QAAQ,CAAC,mCAAmC,EAAE,GAAG,EAAE;IACjD,IAAI,UAAkB,CAAC;IACvB,UAAU,CAAC,GAAG,EAAE;QACd,UAAU,GAAG,YAAY,EAAE,CAAC;IAC9B,CAAC,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;IAErC,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;QAClF,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;QACzB,MAAM,eAAe,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QAEvC,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC;YAChC,MAAM,EAAE,UAAU;YAClB,iBAAiB,EAAE,QAAQ;YAC3B,WAAW,EAAE,UAAU;YACvB,SAAS,EAAE,CAAC,WAAW,EAAE,CAAC;YAC1B,GAAG;SACJ,CAAC,CAAC;QAEH,kEAAkE;QAClE,mBAAmB;QACnB,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACvC,oEAAoE;QACpE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QACzC,MAAM,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACvE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACrC,MAAM,CAAC,SAAS,CACd,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EAC5B;YACE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe;YAC3D,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW;YAC5C,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ;YAC5D,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa;YACrD,YAAY,EAAE,UAAU;SACzB,CACF,CAAC;QACF,8DAA8D;QAC9D,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC5C,oEAAoE;QACpE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,aAAa,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC;QACpE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4FAA4F,EAAE,KAAK,IAAI,EAAE;QAC1G,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;QACzB,MAAM,eAAe,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QAEvC,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC;YAChC,MAAM,EAAE,UAAU;YAClB,iBAAiB,EAAE,QAAQ;YAC3B,WAAW,EAAE,UAAU;YACvB,SAAS,EAAE,CAAC,WAAW,EAAE,CAAC;YAC1B,GAAG;SACJ,CAAC,CAAC;QAEH,uEAAuE;QACvE,kBAAkB;QAClB,MAAM,MAAM,GAAG,QAAQ,CAAC,cAAc,IAAI,EAAE,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,cAAc,CAAC,CAAC;QAC9D,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,0BAA0B,CAAC,CAAC;QAC3E,MAAM,CAAC,EAAE,CAAC,IAAI,KAAK,SAAS,EAAE,iCAAiC,CAAC,CAAC;QACjE,MAAM,CAAC,EAAE,CAAC,KAAK,KAAK,SAAS,EAAE,mCAAmC,CAAC,CAAC;QACpE,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;QAC9C,0EAA0E;QAC1E,iCAAiC;QACjC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,0CAA0C,CAAC,CAAC,CAAC;QAC1E,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,gDAAgD,CAAC,CAAC,CAAC;QAChF,sDAAsD;QACtD,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;QAE1C,sEAAsE;QACtE,4DAA4D;QAC5D,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAC1D,MAAM,CAAC,EAAE,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC;QACzC,MAAM,gBAAgB,GAAG,WAAW,CAAC,iBAAiB,EAAE,EAAE,eAAe,EAAE,QAAQ,CAAC,CAAC;QACrF,MAAM,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC;QACzD,MAAM,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,0CAA0C,CAAC,CAAC,CAAC;QACjF,MAAM,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,8BAA8B,CAAC,CAAC,CAAC;QAErE,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACvD,MAAM,CAAC,EAAE,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC;QACrC,MAAM,UAAU,GAAG,WAAW,CAAC,iBAAiB,EAAE,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;QAC3E,MAAM,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;AAEL,CAAC,CAAC,CAAC;AAEH,yEAAyE;AACzE,4EAA4E;AAC5E,8CAA8C;AAC9C,SAAS,iBAAiB;IACxB,OAAO;QACL,IAAI,EAAE,0BAA0B;QAChC,UAAU,EAAE,UAAU;QACtB,OAAO,EAAE,QAAQ;QACjB,eAAe,EAAE,MAAM;QACvB,WAAW,EAAE,YAAY;QACzB,SAAS,EAAE,EAAE;QACb,MAAM,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE;KACJ,CAAC;AAChC,CAAC;AAED,+EAA+E;AAC/E,6DAA6D;AAC7D,+EAA+E;AAE/E,QAAQ,CAAC,wCAAwC,EAAE,GAAG,EAAE;IACtD,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;YACtC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;YAChD,MAAM,CAAC,EAAE,CACP,UAAU,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EACzC,UAAU,KAAK,CAAC,IAAI,uBAAuB,KAAK,CAAC,aAAa,EAAE,CACjE,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1D,6DAA6D;QAC7D,KAAK,MAAM,QAAQ,IAAI,CAAC,mBAAmB,EAAE,qBAAqB,EAAE,gBAAgB,CAAC,EAAE,CAAC;YACtF,MAAM,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,GAAG,QAAQ,6BAA6B,CAAC,CAAC;QACjF,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,wEAAwE;QACxE,oEAAoE;QACpE,wDAAwD;QACxD,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,UAAU,EAAE;YACtC,eAAe,EAAE,UAAU;YAC3B,WAAW,EAAE,MAAM;YACnB,YAAY,EAAE,OAAO;SACtB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,+EAA+E;AAC/E,qEAAqE;AACrE,+EAA+E;AAE/E,QAAQ,CAAC,2CAA2C,EAAE,GAAG,EAAE;IACzD,IAAI,UAAkB,CAAC;IACvB,UAAU,CAAC,GAAG,EAAE;QACd,UAAU,GAAG,YAAY,EAAE,CAAC;IAC9B,CAAC,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;IAErC,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;QAClF,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;QACzB,MAAM,eAAe,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QAEvC,MAAM,MAAM,GAAW;YACrB,GAAG,UAAU;YACb,KAAK,EAAE;gBACL,GAAG,UAAU,CAAC,KAAK;gBACnB,MAAM,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,aAAa,CAAC;aAC/D;SACF,CAAC;QAEF,MAAM,MAAM,CAAC,OAAO,CAClB,UAAU,CAAC;YACT,MAAM,EAAE,MAAM;YACd,iBAAiB,EAAE,QAAQ;YAC3B,WAAW,EAAE,UAAU;YACvB,SAAS,EAAE,CAAC,WAAW,EAAE,CAAC;YAC1B,GAAG;SACJ,CAAC,EACF,CAAC,GAAY,EAAE,EAAE;YACf,MAAM,CAAC,EAAE,CAAC,GAAG,YAAY,WAAW,CAAC,CAAC;YACtC,MAAM,CAAC,KAAK,CAAE,GAAmB,CAAC,IAAI,EAAE,2BAA2B,CAAC,CAAC;YACrE,MAAM,CAAC,KAAK,CAAE,GAAmB,CAAC,MAAM,EAAE,CAAC,eAAe,CAAC,EAAE,aAAa,CAAC,CAAC;YAC5E,OAAO,IAAI,CAAC;QACd,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oFAAoF,EAAE,KAAK,IAAI,EAAE;QAClG,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;QACzB,MAAM,eAAe,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QAEvC,MAAM,MAAM,GAAW;YACrB,GAAG,UAAU;YACb,UAAU,EAAE,EAAE,GAAG,UAAU,CAAC,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE;SACrE,CAAC;QAEF,MAAM,MAAM,CAAC,OAAO,CAClB,UAAU,CAAC;YACT,MAAM,EAAE,MAAM;YACd,iBAAiB,EAAE,QAAQ;YAC3B,WAAW,EAAE,UAAU;YACvB,SAAS,EAAE,CAAC,WAAW,EAAE,CAAC;YAC1B,GAAG;SACJ,CAAC,EACF,CAAC,GAAY,EAAE,EAAE;YACf,MAAM,CAAC,EAAE,CAAC,GAAG,YAAY,WAAW,CAAC,CAAC;YACtC,MAAM,CAAC,KAAK,CAAE,GAAmB,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;YAC7D,MAAM,CAAC,KAAK,CAAE,GAAmB,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC,CAAC;YAClE,OAAO,IAAI,CAAC;QACd,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,KAAK,IAAI,EAAE;QAC3F,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;QACzB,MAAM,eAAe,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QAEvC,MAAM,MAAM,GAAW;YACrB,GAAG,UAAU;YACb,MAAM,EAAE;gBACN,GAAG,UAAU,CAAC,MAAM;gBACpB,EAAE,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE,mBAAmB,EAAE,WAAW,EAAE,WAAW,EAAE;aAClF;SACF,CAAC;QAEF,MAAM,MAAM,CAAC,OAAO,CAClB,UAAU,CAAC;YACT,MAAM,EAAE,MAAM;YACd,iBAAiB,EAAE,QAAQ;YAC3B,WAAW,EAAE,UAAU;YACvB,SAAS,EAAE,CAAC,WAAW,EAAE,CAAC;YAC1B,GAAG;SACJ,CAAC,EACF,CAAC,GAAY,EAAE,EAAE;YACf,MAAM,CAAC,EAAE,CAAC,GAAG,YAAY,WAAW,CAAC,CAAC;YACtC,MAAM,CAAC,KAAK,CAAE,GAAmB,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC;YAC9D,MAAM,CAAC,KAAK,CAAE,GAAmB,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,CAAC;YAChE,OAAO,IAAI,CAAC;QACd,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;QAC5F,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;QACzB,MAAM,eAAe,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QAEvC,MAAM,MAAM,GAAW;YACrB,GAAG,UAAU;YACb,MAAM,EAAE;gBACN,GAAG,UAAU,CAAC,MAAM;gBACpB,SAAS,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,aAAa,EAAE;aAC/F;SACF,CAAC;QAEF,MAAM,MAAM,CAAC,OAAO,CAClB,UAAU,CAAC;YACT,MAAM,EAAE,MAAM;YACd,iBAAiB,EAAE,QAAQ;YAC3B,WAAW,EAAE,UAAU;YACvB,SAAS,EAAE,CAAC,WAAW,EAAE,CAAC;YAC1B,GAAG;SACJ,CAAC,EACF,CAAC,GAAY,EAAE,EAAE;YACf,MAAM,CAAC,KAAK,CAAE,GAAmB,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAAC;YAChE,MAAM,CAAC,KAAK,CAAE,GAAmB,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC,CAAC;YACpE,OAAO,IAAI,CAAC;QACd,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,+EAA+E;AAC/E,2EAA2E;AAC3E,oEAAoE;AACpE,+EAA+E;AAE/E,QAAQ,CAAC,kDAAkD,EAAE,GAAG,EAAE;IAChE,IAAI,UAAkB,CAAC;IACvB,UAAU,CAAC,GAAG,EAAE;QACd,UAAU,GAAG,YAAY,EAAE,CAAC;IAC9B,CAAC,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;IAErC,EAAE,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;QACvE,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;QACzB,MAAM,eAAe,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QAEvC,MAAM,SAAS,GAAW;YACxB,GAAG,UAAU;YACb,qBAAqB,EAAE,EAAE,GAAG,UAAU,CAAC,qBAAqB,EAAE,KAAK,EAAE,MAAM,EAAE;SAC9E,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC;YAChC,MAAM,EAAE,SAAS;YACjB,iBAAiB,EAAE,QAAQ;YAC3B,WAAW,EAAE,UAAU;YACvB,SAAS,EAAE,CAAC,WAAW,EAAE,CAAC;YAC1B,GAAG;SACJ,CAAC,CAAC;QACH,wEAAwE;QACxE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAE,GAAyB,CAAC,IAAI,KAAK,wBAAwB,CAAC,CAAC,CAAC;IAC7G,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,73 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { afterEach, beforeEach, describe, it } from "node:test";
6
+ import { KERNEL_SENSITIVE_PATH_RULES, createPathRestrictedSandbox, fileReadTool, mergeSensitivePathRules, } from "@loomfsm/kernel";
7
+ import { CODE_BUNDLE_SENSITIVE_PATH_RULES } from "../src/sandbox-rules.js";
8
+ // Build a tool context. When `rules` is omitted the tool falls back to the
9
+ // bare kernel floor — exactly the production fallback path.
10
+ function makeCtx(projectDir, rules) {
11
+ const audited = [];
12
+ const ctx = {
13
+ project_dir: projectDir,
14
+ sandbox: createPathRestrictedSandbox(projectDir),
15
+ audit_emit: (p) => audited.push(p),
16
+ ...(rules !== undefined ? { sensitive_path_rules: rules } : {}),
17
+ };
18
+ return { ctx, audited };
19
+ }
20
+ const MERGED = mergeSensitivePathRules(KERNEL_SENSITIVE_PATH_RULES, CODE_BUNDLE_SENSITIVE_PATH_RULES);
21
+ describe("@loomfsm/bundle-code — dev-ecosystem sandbox rules", () => {
22
+ let project;
23
+ beforeEach(() => {
24
+ project = mkdtempSync(join(tmpdir(), "loom-bundle-sandbox-"));
25
+ });
26
+ afterEach(() => {
27
+ rmSync(project, { recursive: true, force: true });
28
+ });
29
+ it("refuses .npmrc once the bundle rules are merged, allows it under the bare kernel floor", async () => {
30
+ writeFileSync(join(project, ".npmrc"), "//registry.example/:_authToken=secret", "utf8");
31
+ // Bare floor (no bundle rules wired): .npmrc is NOT a kernel-floor
32
+ // secret, so the read is allowed and returns content.
33
+ {
34
+ const { ctx } = makeCtx(project);
35
+ const r = await fileReadTool.handler({ path: ".npmrc" }, ctx);
36
+ assert.ok("content" in r, ".npmrc should be readable under the bare kernel floor");
37
+ }
38
+ // Merged rules (the production wiring): .npmrc is refused as a
39
+ // sensitive file. This fails if the file tool ignores the carried
40
+ // rules — i.e. if the bundle rules are not actually wired through.
41
+ {
42
+ const { ctx, audited } = makeCtx(project, MERGED);
43
+ const r = await fileReadTool.handler({ path: ".npmrc" }, ctx);
44
+ assert.ok("error" in r, ".npmrc must be refused once the bundle rules are merged");
45
+ assert.equal(audited[0]?.error_class, "sandbox-violation");
46
+ assert.match(String(audited[0]?.reason), /^sensitive-file:/);
47
+ }
48
+ });
49
+ it("refuses .kube/config under the merged rules, allows it under the bare floor", async () => {
50
+ mkdirSync(join(project, ".kube"), { recursive: true });
51
+ writeFileSync(join(project, ".kube", "config"), "apiVersion: v1\nclusters: []", "utf8");
52
+ {
53
+ const { ctx } = makeCtx(project);
54
+ const r = await fileReadTool.handler({ path: ".kube/config" }, ctx);
55
+ assert.ok("content" in r, ".kube/config should be readable under the bare kernel floor");
56
+ }
57
+ {
58
+ const { ctx, audited } = makeCtx(project, MERGED);
59
+ const r = await fileReadTool.handler({ path: ".kube/config" }, ctx);
60
+ assert.ok("error" in r, ".kube/config must be refused once the bundle rules are merged");
61
+ assert.equal(audited[0]?.error_class, "sandbox-violation");
62
+ assert.match(String(audited[0]?.reason), /^sensitive-dir:\/\.kube\//);
63
+ }
64
+ });
65
+ it("keeps the kernel floor intact under the merge (.env still refused)", async () => {
66
+ writeFileSync(join(project, ".env"), "SECRET=1", "utf8");
67
+ const { ctx, audited } = makeCtx(project, MERGED);
68
+ const r = await fileReadTool.handler({ path: ".env" }, ctx);
69
+ assert.ok("error" in r, "the merge must preserve the kernel floor");
70
+ assert.equal(audited[0]?.error_class, "sandbox-violation");
71
+ });
72
+ });
73
+ //# sourceMappingURL=sandbox-rules.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-rules.test.js","sourceRoot":"","sources":["../../test/sandbox-rules.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAEhE,OAAO,EACL,2BAA2B,EAC3B,2BAA2B,EAC3B,YAAY,EACZ,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAE3E,2EAA2E;AAC3E,4DAA4D;AAC5D,SAAS,OAAO,CACd,UAAkB,EAClB,KAA0B;IAE1B,MAAM,OAAO,GAA8B,EAAE,CAAC;IAC9C,MAAM,GAAG,GAAgB;QACvB,WAAW,EAAE,UAAU;QACvB,OAAO,EAAE,2BAA2B,CAAC,UAAU,CAAC;QAChD,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAClC,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,oBAAoB,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChE,CAAC;IACF,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,MAAM,GAAG,uBAAuB,CACpC,2BAA2B,EAC3B,gCAAgC,CACjC,CAAC;AAEF,QAAQ,CAAC,oDAAoD,EAAE,GAAG,EAAE;IAClE,IAAI,OAAe,CAAC;IACpB,UAAU,CAAC,GAAG,EAAE;QACd,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,sBAAsB,CAAC,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wFAAwF,EAAE,KAAK,IAAI,EAAE;QACtG,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,uCAAuC,EAAE,MAAM,CAAC,CAAC;QAExF,mEAAmE;QACnE,sDAAsD;QACtD,CAAC;YACC,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;YACjC,MAAM,CAAC,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,GAAG,CAAC,CAAC;YAC9D,MAAM,CAAC,EAAE,CAAC,SAAS,IAAI,CAAC,EAAE,uDAAuD,CAAC,CAAC;QACrF,CAAC;QAED,+DAA+D;QAC/D,kEAAkE;QAClE,mEAAmE;QACnE,CAAC;YACC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAClD,MAAM,CAAC,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,GAAG,CAAC,CAAC;YAC9D,MAAM,CAAC,EAAE,CAAC,OAAO,IAAI,CAAC,EAAE,yDAAyD,CAAC,CAAC;YACnF,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,mBAAmB,CAAC,CAAC;YAC3D,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,kBAAkB,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,KAAK,IAAI,EAAE;QAC3F,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,8BAA8B,EAAE,MAAM,CAAC,CAAC;QAExF,CAAC;YACC,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;YACjC,MAAM,CAAC,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,CAAC;YACpE,MAAM,CAAC,EAAE,CAAC,SAAS,IAAI,CAAC,EAAE,6DAA6D,CAAC,CAAC;QAC3F,CAAC;QACD,CAAC;YACC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAClD,MAAM,CAAC,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,CAAC;YACpE,MAAM,CAAC,EAAE,CAAC,OAAO,IAAI,CAAC,EAAE,+DAA+D,CAAC,CAAC;YACzF,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,mBAAmB,CAAC,CAAC;YAC3D,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,2BAA2B,CAAC,CAAC;QACxE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;QAClF,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;QACzD,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAClD,MAAM,CAAC,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,CAAC;QAC5D,MAAM,CAAC,EAAE,CAAC,OAAO,IAAI,CAAC,EAAE,0CAA0C,CAAC,CAAC;QACpE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,mBAAmB,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,188 @@
1
+ ---
2
+ tags: [api, contract, rest, graphql, grpc, versioning, idempotency, pagination, backend]
3
+ stack_signals:
4
+ - project_type: [backend, monorepo]
5
+ summary: |
6
+ Public-contract design for HTTP/RPC/GraphQL endpoints — idempotency, pagination,
7
+ error envelopes, versioning. Treats the API signature as forever-ish and
8
+ prioritises contract shape over implementation detail.
9
+ when_to_load: |
10
+ Task touches HTTP/RPC endpoints, GraphQL schema, gRPC proto, OpenAPI spec,
11
+ route handlers, or anything that's a public contract between services /
12
+ frontend and backend / between teams. Diff under routes/, controllers/,
13
+ *.proto, openapi.yaml, GraphQL *.graphql / resolvers, or new public-facing
14
+ functions also qualifies.
15
+ agent_hints: [logic-reviewer, challenger-reviewer, security, api-contract]
16
+ ---
17
+
18
+ # API Design — Senior Stance
19
+
20
+ ## When this applies
21
+ Load when task touches HTTP/RPC endpoints, GraphQL schema, gRPC proto, OpenAPI spec, route handlers, or anything that's a public contract between services / between frontend and backend / between teams. Reviewer auto-loads when diff includes `routes/`, `controllers/`, `*.proto`, `openapi.yaml`, GraphQL `*.graphql` / resolvers, or new public-facing functions.
22
+
23
+ ## Default Stance
24
+ The signature you ship is the signature you live with. Internal code can refactor freely; public contracts are forever-ish. Spend more time on shape than on implementation. Idempotency, pagination, errors, versioning — design these explicitly before the first request hits prod. Default to boring REST until you have a concrete reason for GraphQL or gRPC.
25
+
26
+ ## Patterns (use these)
27
+
28
+ ### Resource modeling
29
+ - Nouns, not verbs: `POST /users` to create, not `POST /createUser`.
30
+ - Hierarchy when entities truly nest: `/users/{id}/orders/{order_id}`. Stop nesting at 2 levels — `/a/{}/b/{}/c/{}/d/{}` becomes unreadable and inflexible.
31
+ - Plural collections: `/users` returns many, `/users/{id}` returns one.
32
+ - Filtering via query: `GET /users?status=active&created_after=2026-01-01`. Avoid `POST /users/search` with body unless you need huge filters that don't fit URL.
33
+
34
+ ### Idempotency keys
35
+ Every state-mutating endpoint accepts `Idempotency-Key` header (or body field). Server stores `(key → response)` for 24h. Replay of same key returns the cached response, NOT a duplicate write. This is the single most-important defense against duplicate writes from retries/network blips.
36
+
37
+ ### Pagination patterns
38
+ - **Cursor-based** (preferred for large/changing collections): `?cursor=eyJ...&limit=50`. Stable under inserts, indexable, no offset cost.
39
+ - **Page-based** OK for stable small datasets: `?page=2&size=50`. Simple but breaks under concurrent inserts.
40
+ - **NEVER unbounded**: every list endpoint has a default limit and a max limit. Reject `limit > max` with 400.
41
+
42
+ ### Versioning
43
+ - **URL versioning** (`/v1/users`, `/v2/users`) — clearest, easiest to route, easiest for caches.
44
+ - **Header versioning** (`Accept: application/vnd.foo.v2+json`) — cleaner URLs but invisible to logs/CDN.
45
+ - For internal APIs, version-on-spec-change is fine. For public APIs, version-on-breaking-change only — additive changes (new optional field) don't warrant a new major.
46
+ - Document the deprecation timeline. Sunset old versions on a calendar, not "when we feel like it".
47
+
48
+ ### Error envelope (consistent shape)
49
+ Every error response has the same shape:
50
+ ```json
51
+ {
52
+ "error": {
53
+ "code": "VALIDATION_ERROR",
54
+ "message": "Email is required",
55
+ "details": [{ "field": "email", "rule": "required" }],
56
+ "request_id": "req_abc123"
57
+ }
58
+ }
59
+ ```
60
+ - `code` is machine-parseable, stable, SCREAMING_SNAKE_CASE.
61
+ - `message` is human-readable, may be localized.
62
+ - `details` is structured per error type; clients can opt-in to handle.
63
+ - `request_id` echoes back from logs for debugging.
64
+
65
+ ### HTTP status code discipline
66
+ - `200` — success with body.
67
+ - `201` — created (POST returning a new resource), include `Location` header.
68
+ - `204` — success, no body (DELETE, void mutations).
69
+ - `400` — client sent malformed/invalid input. Body explains.
70
+ - `401` — not authenticated.
71
+ - `403` — authenticated but not authorized.
72
+ - `404` — resource doesn't exist OR you don't want to leak that it does (auth-by-existence).
73
+ - `409` — conflict (idempotency-key reuse, optimistic-lock failure, unique constraint).
74
+ - `422` — semantically valid but unprocessable (validation rules failed). Some teams use `400` for this; pick one and stay consistent.
75
+ - `429` — rate limited. Include `Retry-After` header.
76
+ - `5xx` — server's fault. Don't leak stack traces.
77
+
78
+ ### Rate limiting
79
+ - Per-user (authenticated) AND per-IP (unauthenticated).
80
+ - Token bucket via `redis-cell` or equivalent. Document limits in API docs.
81
+ - Return `429` with `Retry-After: <seconds>` and `X-RateLimit-Remaining: <n>` headers.
82
+ - Different limits per endpoint based on cost (cheap reads vs expensive computes).
83
+
84
+ ### Auth at the edge
85
+ - Validate token at the API gateway / first middleware. Internal services trust the validated context.
86
+ - Don't mix authn (who are you) and authz (what can you do). Authn produces a principal; authz checks the principal against the resource per request.
87
+ - Time-box tokens. JWT exp ≤ 1 hour, refresh tokens longer with revocation.
88
+
89
+ ### REST vs GraphQL vs gRPC
90
+
91
+ | Need | Default |
92
+ |---|---|
93
+ | Public API for humans/external | REST + OpenAPI |
94
+ | Internal service-to-service, low latency, strong typing | gRPC |
95
+ | Frontend with widely-varying data needs | GraphQL — only if you're paying the operational cost |
96
+ | Real-time bidirectional | WebSocket / gRPC streaming |
97
+
98
+ Don't pick GraphQL because it's trendy. Operational cost (N+1, query depth limits, auth-per-field, persisted queries, schema evolution, caching) is real. REST + a few well-chosen aggregating endpoints often wins.
99
+
100
+ ## Anti-Patterns (DO NOT)
101
+
102
+ ### `POST /verb` instead of `POST /resource`
103
+ **Why it bites:** `POST /createUser`, `POST /updateUser`, `POST /deleteUser` — you've reinvented RPC over HTTP, lost all REST conventions (caching, methods, status codes). Tools, gateways, CDNs assume REST shape.
104
+ **Rule:** Use HTTP methods for verbs. `POST /users` to create, `PATCH /users/{id}` to update, `DELETE /users/{id}` to delete.
105
+
106
+ ### Returning everything always
107
+ `GET /users` returns full user with addresses, preferences, audit log embedded.
108
+ **Why it bites:** payload sizes balloon, mobile clients pay for bytes they don't render, evolving the response shape breaks consumers. Privacy: you may leak fields.
109
+ **Rule:** lean default response. Use `?fields=` or `?include=addresses` for opt-in expansion.
110
+
111
+ ### No idempotency on writes
112
+ **Why it bites:** retry storm = N duplicate writes. Mobile network blip = duplicate charge. Distributed clients without idempotency keys is a guaranteed prod incident.
113
+ **Rule:** every mutating endpoint accepts `Idempotency-Key`, server enforces it.
114
+
115
+ ### Inconsistent error shapes
116
+ `POST /a` returns `{"error": "..."}`, `POST /b` returns `{"errors": [...]}`, `POST /c` returns `"failed"`. Frontend has 5 error parsers.
117
+ **Rule:** one error envelope for the whole API.
118
+
119
+ ### Breaking changes without versioning
120
+ Renaming `userName` → `user_name`, removing a field, changing types — all without a version bump.
121
+ **Rule:** breaking change = new major. Old version stays until sunset date. Communicate via deprecation header `Deprecation: <date>` and `Sunset: <date>`.
122
+
123
+ ### Exposing internal IDs / database structure
124
+ `GET /users/42` where `42` is the autoincrement DB primary key.
125
+ **Why it bites:** id leaks user count, sequencing reveals signup velocity, easy to enumerate. Internal refactor (DB swap, sharding) breaks contract.
126
+ **Rule:** opaque identifiers (UUID v7, ULID, or surrogate slug). DB id stays internal.
127
+
128
+ ### Hidden state in query params
129
+ `GET /users?limit=50&filter[status]=active&sort[name]=asc&include=addresses,roles&fields=id,name,email&page=3...`
130
+ Six concepts in URL, parsing nightmare, no schema validation.
131
+ **Rule:** if a "list" endpoint has > 4 query params, design a structured filter type or split into multiple endpoints.
132
+
133
+ ### `200 OK` with `{"error": ...}` body
134
+ Failed but returned 200. Frontend has to parse body to know if it succeeded. CDN caches the "error" response.
135
+ **Rule:** HTTP status reflects success/failure. `4xx` for client errors, `5xx` for server errors. Always.
136
+
137
+ ### Returning partial success silently
138
+ Bulk endpoint accepts 100 items, succeeds for 87, fails for 13, returns 200 with no breakdown.
139
+ **Rule:** explicit per-item result array, OR fail the whole request, OR use `207 Multi-Status` with per-item details.
140
+
141
+ ### Versioning everything as v1 forever
142
+ Adding optional fields, deprecating old ones, but never bumping the version. After 2 years, "v1" looks nothing like the original.
143
+ **Rule:** name versions honestly. Or use header-based versioning with explicit deprecation.
144
+
145
+ ### CORS wildcard with credentials
146
+ `Access-Control-Allow-Origin: *` AND `Access-Control-Allow-Credentials: true` — browsers reject this anyway, but seeing it in code = misconfigured intent.
147
+ **Rule:** explicit origin allowlist; never `*` with credentials.
148
+
149
+ ## Decision Framework
150
+
151
+ | Situation | Choice |
152
+ |---|---|
153
+ | Mutating endpoint | POST/PUT/PATCH + idempotency key |
154
+ | Listing changing collection | Cursor pagination |
155
+ | Listing stable small set | Page/size pagination acceptable |
156
+ | Need to filter on 5+ axes | Reconsider — split endpoints, or POST /search with structured body |
157
+ | Bulk operation | Limit batch size; return per-item status array |
158
+ | Long-running operation | `202 Accepted` + `Location: /jobs/{id}` polling, OR webhook on completion |
159
+ | Sensitive update | Require fresh auth (re-auth or step-up) |
160
+ | Adding new optional field | Minor version (or no bump) |
161
+ | Removing/renaming/changing type | Major version, deprecation timeline |
162
+ | Internal vs external API | More flexibility internally; stricter discipline externally |
163
+
164
+ ## Cost Model
165
+
166
+ | Decision | Cost when wrong |
167
+ |---|---|
168
+ | No idempotency key | Duplicate writes from retry storm; fix is per-endpoint, takes weeks per service |
169
+ | `OFFSET` pagination on big tables | Page 1000 = 50ms → 5s as data grows; users see "loading…" forever |
170
+ | Status code wrong | Client error handlers get fragile, observability dashboards mislead |
171
+ | Breaking change without version | Every consumer breaks at deploy time; rollback may take hours |
172
+ | Exposing internal DB id | One refactor away from leaking implementation; can't easily migrate to sharding |
173
+ | `Cache-Control: public` on per-user data | Privacy incident, cross-user data leak |
174
+
175
+ ## Red Flags in Diff
176
+
177
+ - New endpoint missing `Idempotency-Key` handling on POST/PUT/PATCH → flag.
178
+ - New list endpoint without `limit` or with no max-limit enforcement → flag.
179
+ - New error response not matching the project's error envelope shape → flag.
180
+ - New endpoint reusing an existing version (`/v1/...`) but adding a breaking change → flag major-bump needed.
181
+ - `POST /api/<verb>` style introduced when project uses RESTful nouns → flag.
182
+ - Internal DB primary key (autoincrement integer) exposed in a response → flag.
183
+ - New endpoint returning `200` on logical failure → flag.
184
+ - Bulk endpoint without per-item result array → flag.
185
+ - New 5xx code being returned for client-side validation errors → flag.
186
+ - CORS config allowing wildcard origin AND credentials → flag immediately.
187
+ - New endpoint without rate-limit middleware → flag if it's potentially expensive (DB writes, computes, external calls).
188
+ - Response payload includes fields not declared in the project's API spec / OpenAPI doc → flag (drift).
@@ -0,0 +1,106 @@
1
+ ---
2
+ tags: [architecture, design, complexity, refactor, boundaries, service-split]
3
+ stack_signals: []
4
+ summary: |
5
+ Architectural decision patterns — prefer the cheapest abstraction that
6
+ survives 6 months. Boundary cost framing for service splits, shared
7
+ modules, and async hops.
8
+ when_to_load: |
9
+ COMPLEX tasks; task requires architectural decisions; Architect or Planner
10
+ is reasoning about new components, services, or boundaries; refactor scope
11
+ spans multiple modules or introduces a new abstraction layer.
12
+ agent_hints: [architect, planner, logic-reviewer, challenger-reviewer]
13
+ ---
14
+
15
+ # Architecture Patterns — Senior Stance
16
+
17
+ ## When this applies
18
+ Load on COMPLEX tasks, when task requires architectural decisions, or when Architect/Planner is reasoning about new components, services, or boundaries.
19
+
20
+ ## Default Stance
21
+ Pick the cheapest abstraction that survives the next 6 months — not the next 5 years. Premature abstraction costs more than wrong abstraction. Reuse > generalize. Boundaries are expensive: every async hop, every service split, every shared module is a maintenance liability that must pay rent in actual decoupling, scaling, or team-ownership benefit. Default to inline / function / module before service / queue / framework.
22
+
23
+ ## Patterns (use these)
24
+
25
+ ### Sync vs Async boundary
26
+ - **Sync** when caller needs result for next decision. HTTP request-response, function call, DB query.
27
+ - **Async** when caller doesn't block on the work AND failure can be retried independently. Email send, analytics push, cache warmup.
28
+ - **Choose async only if**: (a) latency budget allows it, (b) downstream can be retried idempotently, (c) you have observability for the queue.
29
+
30
+ ### Idempotency by design
31
+ Every external-facing write endpoint accepts an `idempotency-key` (header or body). Server stores `(key, request-hash) → response` for 24h. Replays return cached response. Without this, every retry is a potential dup-write bug.
32
+
33
+ ### Strong invariants at the edge
34
+ Validate input at the system boundary (controller / route handler), then trust internal code. Don't re-validate in every layer. The cost of "defensive everywhere" is unreadable code AND it doesn't actually catch bugs — bugs come from missing validation, not from "we only validated once".
35
+
36
+ ### Failure modes named explicitly
37
+ For every new service/handler, list in the plan: what happens on (a) downstream timeout, (b) downstream 5xx, (c) partial write, (d) duplicate request, (e) caller disconnects mid-write. If you can't answer, you don't have a design.
38
+
39
+ ### Single source of truth
40
+ For any piece of state, exactly one component owns the write. Everyone else reads. Multi-writer state without a coordination protocol = race condition waiting to ship.
41
+
42
+ ## Anti-Patterns (DO NOT)
43
+
44
+ ### Premature service split
45
+ Splitting a function into a microservice because "it might scale" but it has zero independent scaling pressure today.
46
+ **Why it bites:** every cross-service call adds 1-50ms latency, retry logic, network failure modes, deploy coordination, observability gap, and a team boundary that may not match the actual ownership. Roll back via merge is much harder than roll forward via split.
47
+ **Sign:** the new service has the same deploy cadence and team as the caller.
48
+
49
+ ### Generic abstraction layer at the start
50
+ "Repository" / "Service" / "Strategy" pattern wrapping a single concrete implementation, "for future extension".
51
+ **Why it bites:** you pay the indirection cost (3 files instead of 1, harder to navigate, mental model overhead) for an extension that 80% of the time never comes. When it comes, the abstraction usually doesn't fit anyway because the second use case has different shape.
52
+ **Rule:** wait for the second concrete use case, then refactor.
53
+
54
+ ### Shared mutable state across modules
55
+ Singletons holding cache, config, or connection state mutated from multiple call sites.
56
+ **Why it bites:** test isolation breaks. Hot reload breaks. Race conditions appear at scale, not in dev. One service touched the singleton in a different module last week and you don't know.
57
+ **Rule:** if it's mutable and shared, put it behind a single owning module with a narrow interface, or pass it explicitly.
58
+
59
+ ### Async for what's actually sync
60
+ Wrapping a fast in-process function in a queue + worker because "it's cleaner".
61
+ **Why it bites:** debug surface explodes (queue, worker, retry, DLQ, observability), latency goes up (queue lag), and you have eventually-consistent state where strongly-consistent state was correct.
62
+ **Sign:** the worker reads result from DB and the caller polls for it.
63
+
64
+ ### Cross-cutting framework before need
65
+ Building a generic event bus / plugin system / DSL before there are 3 concrete users.
66
+ **Why it bites:** you're designing in the dark. By the time real users appear, the API is wrong.
67
+ **Rule:** copy-paste 3 times, then extract.
68
+
69
+ ### Layered architecture as ritual
70
+ Auto-creating Controller → Service → Repository → Entity layers for endpoints that just `SELECT` and return.
71
+ **Why it bites:** 4 files to add a column. Reading any single piece of code requires opening 4 tabs.
72
+ **Rule:** if the layer adds zero logic, delete it. Keep layers where logic lives.
73
+
74
+ ## Decision Framework
75
+
76
+ | Situation | Default | Reason |
77
+ |---|---|---|
78
+ | New CRUD endpoint, no logic | Inline in route handler | Layer count = logic count |
79
+ | New write endpoint with side-effects | Sync core + async side-effect via queue | Side-effects retry independently |
80
+ | Need to share state between requests | Redis / DB / explicit cache layer | Never module-level globals |
81
+ | New cross-team boundary needed | Library / interface in same repo first | Service split only when team owns deploy |
82
+ | State machine with 3+ states | Explicit state column + transition fn | Don't infer state from booleans |
83
+ | Read-heavy aggregation | Materialized view / read replica | Don't do app-side aggregation across millions of rows |
84
+ | Write contention point | Queue + single worker | Avoid multi-writer races |
85
+
86
+ ## Cost Model (rough, defaults)
87
+
88
+ | Decision | Cost when wrong |
89
+ |---|---|
90
+ | Service split | 2-4 weeks to undo (re-merge, re-deploy, re-observability) |
91
+ | Async for sync work | Latency +50ms minimum, debug 5x harder |
92
+ | Generic abstraction prematurely | 2-3 days dev time, plus ongoing reading cost forever |
93
+ | Missing idempotency key | First prod retry → duplicate write → data corruption incident |
94
+ | Shared mutable singleton | Race condition at scale; not reproducible in dev |
95
+ | Layered architecture without logic | 4x code volume, 2x review time, no benefit |
96
+
97
+ ## Red Flags in Diff (reviewer hunts these)
98
+
99
+ - New service / module / package introduced for a single caller — flag for justification.
100
+ - New abstraction with one concrete impl — flag for "wait for second user".
101
+ - Async path added but caller polls for result — flag as accidental complexity.
102
+ - Module-level `let` / `var` mutated in handler code — flag as shared mutable state.
103
+ - Write endpoint without idempotency key in route signature — flag as retry hazard.
104
+ - New layer (controller/service/repo) with no logic, just pass-through — flag as ritual.
105
+ - "TODO: handle retry" / "for future extension" comments — fail-fast: either handle now or remove the comment.
106
+ - New interface with single implementer + `*Impl` suffix — almost always wrong.
@@ -0,0 +1,190 @@
1
+ ---
2
+ tags: [caching, cdn, redis, http-cache, react-query, swr, invalidation, staleness]
3
+ stack_signals: []
4
+ summary: |
5
+ Caching design and invalidation discipline — every cache layer is a stale
6
+ copy. Patterns for HTTP cache, CDN, Redis, React Query / SWR, Next.js
7
+ Data/Route caches, and the invalidation contract that makes them safe.
8
+ when_to_load: |
9
+ Task touches HTTP cache headers, CDN config, in-memory cache, Redis cache,
10
+ browser cache, query cache (React Query / SWR / RTK Query / Apollo),
11
+ Next.js Data/Route/Full-Route cache, server-side render cache, or
12
+ materialized views. Diff with cache TTLs, invalidation logic, revalidate,
13
+ cacheTag, cacheLife, staleTime, Cache-Control, ETag, or mutate() qualifies.
14
+ agent_hints: [logic-reviewer, performance, challenger-reviewer]
15
+ ---
16
+
17
+ # Caching — Senior Stance
18
+
19
+ ## When this applies
20
+ Load when task touches: HTTP cache headers, CDN config, in-memory cache, Redis cache, browser cache, query cache (React Query / SWR / RTK Query / Apollo), Next.js Data/Route/Full-Route cache, server-side render cache, materialized views. Reviewer auto-loads when diff includes cache TTLs, invalidation logic, `revalidate`, `cacheTag`, `cacheLife`, `staleTime`, `Cache-Control`, `ETag`, `mutate()`.
21
+
22
+ ## Default Stance
23
+ The hardest part of caching isn't speed — it's correctness under invalidation. Every cache layer is a copy that may be stale. Add a cache only when (a) you've measured the underlying call is expensive, (b) the data has a clear invalidation event or a tolerable staleness window, (c) you've designed how the cache empties. If you can't answer "what happens when the source changes?" — don't cache yet.
24
+
25
+ ## Patterns (use these)
26
+
27
+ ### Choose the layer based on shape
28
+
29
+ | Layer | Good for | Cost |
30
+ |---|---|---|
31
+ | HTTP / CDN | Public, immutable or long-TTL responses | Hard to invalidate per-user |
32
+ | Browser HTTP cache | Static assets, idempotent GET | User-controlled, can be bypassed |
33
+ | Service-worker cache | Offline-first, PWA | Complexity, version skew |
34
+ | In-memory app cache (per-instance LRU) | Hot small data with short TTL, per-process | Inconsistent across instances, lost on restart |
35
+ | Redis cache | Cross-instance hot reads, sessions, rate state | Network round-trip, single point |
36
+ | DB query cache (built-in or materialized view) | Aggregations, joins refreshed periodically | Refresh window staleness |
37
+ | Client-side query cache (React Query / SWR) | UI data, deduped fetches | Per-tab; needs explicit invalidation |
38
+ | Edge cache (Vercel, Cloudflare) | Per-user-segment static-ish content | Tag-based invalidation needed |
39
+
40
+ Pick the **outermost** layer that matches the access pattern. Caching deeper than necessary multiplies invalidation surfaces.
41
+
42
+ ### Invalidation strategy — name it explicitly
43
+
44
+ For every cache, document one of:
45
+ - **TTL only** — accept staleness up to TTL. Simplest, default for read-heavy approximate data.
46
+ - **Event-driven** — write path also invalidates (delete key, bump tag, increment version). Use when stale = wrong.
47
+ - **Versioned key** — key includes a version/etag; on change, new version auto-evicts old via TTL. Avoids the "did we forget to invalidate?" class of bug.
48
+ - **Write-through** — write goes to both cache and source. Cache is always fresh; cost is doubled write latency.
49
+ - **Read-through with refresh-ahead** — refresh proactively before expiry. Hides cold misses from users.
50
+
51
+ ### TTL — pick it deliberately
52
+
53
+ - **Public read-mostly with weak freshness** — minutes to hours.
54
+ - **User-specific read-mostly** — seconds to minutes.
55
+ - **Computed aggregations** — depends on tolerable staleness; document it.
56
+ - **Auth / permission decisions** — seconds at most. Stale auth = security bug.
57
+ - **Always set a backup TTL even with event-driven invalidation** — bugs happen.
58
+
59
+ ### Cache-Aside (lazy loading) — the canonical pattern
60
+
61
+ ```
62
+ read(key):
63
+ v = cache.get(key)
64
+ if v: return v
65
+ v = source.load(key)
66
+ cache.set(key, v, ttl=T)
67
+ return v
68
+ ```
69
+
70
+ This is correct ONLY with stampede protection (see anti-patterns). Add it from day one.
71
+
72
+ ### Stampede protection
73
+ Cold cache + high traffic = N concurrent loads of the same expensive thing. Pick one:
74
+ - **Lock-around-fill** — first miss acquires a key-scoped lock, others wait or serve stale.
75
+ - **Probabilistic early refresh (XFetch)** — on read, with probability proportional to how-close-to-expiry, refresh proactively. Smooths traffic.
76
+ - **Single-flight in-process** — dedupe concurrent fills inside one instance. Doesn't help across instances; combine with one of the above.
77
+
78
+ ### Versioned keys for safe schema changes
79
+ When the cached payload shape may change, include a version:
80
+ ```
81
+ key = `user:v3:${id}`
82
+ ```
83
+ Old version's keys evict naturally via TTL. No mass-purge needed. No "old version stuck in cache for a week" bugs.
84
+
85
+ ### Tag-based invalidation
86
+ For Next.js / Cloudflare / Vercel edge: tag related entries (`['user:123', 'team:42']`), invalidate by tag on write. Avoids enumerating keys; matches "what changed in the source" to "what to evict".
87
+
88
+ ### Cache the result, not the request
89
+ Cache key = a normalized representation of the **answer**, not the URL. Two different URLs that produce the same answer should hit the same cache slot. Two URLs with same query params in different order should normalize.
90
+
91
+ ### Negative caching
92
+ Cache "not found" too — with shorter TTL. Otherwise a 404 hammered repeatedly causes repeated DB lookups.
93
+
94
+ ## Anti-Patterns (DO NOT)
95
+
96
+ ### No TTL ("we'll invalidate manually")
97
+ **Why it bites:** invalidation bugs are forever. One missed write path = stale data permanently. Memory grows.
98
+ **Rule:** TTL is mandatory. Even with event-driven invalidation, set a backup TTL ("at most this stale even if we screw up").
99
+
100
+ ### Cache stampede on hot key
101
+ **Why it bites:** cache miss → 1000 simultaneous loads → DB CPU spike → DB latency up → all caches expire while loads run → worse stampede next minute.
102
+ **Rule:** stampede protection from day one on any cache key with > 100 reads/sec.
103
+
104
+ ### Caching auth/permission decisions for minutes
105
+ **Why it bites:** revoke a user's permission, they keep working for the cache TTL. Real security incident.
106
+ **Rule:** auth cache TTL ≤ 30 seconds, OR invalidate on revoke event, OR don't cache auth at all.
107
+
108
+ ### Caching at multiple layers without invalidation alignment
109
+ **Why it bites:** invalidate L1 (Redis) but L2 (CDN) still serves stale. Or browser HTTP cache holds the old response. Each layer has independent TTL.
110
+ **Rule:** if you cache at multiple layers, invalidation must hit all of them. Document which layer holds what for how long.
111
+
112
+ ### `Cache-Control: public` on per-user data
113
+ **Why it bites:** user A's response cached by CDN, served to user B. Privacy breach, data leak.
114
+ **Rule:** per-user data → `Cache-Control: private` always. Or include user identifier in URL/key and use edge cache with proper key.
115
+
116
+ ### Storing huge payloads in cache
117
+ **Why it bites:** 5MB cache entry × 10000 users = 50GB Redis. Cost and latency explode.
118
+ **Rule:** cache references / IDs / small projections. Fetch full data on demand if needed. Or cache the bits actually rendered.
119
+
120
+ ### Reading-only cache, never refreshed
121
+ **Why it bites:** TTL expires, cache empty, all reads go to DB until refilled. Cold-start tax on every TTL boundary.
122
+ **Rule:** refresh-ahead or lock-around-fill so users never see the cold path.
123
+
124
+ ### Cache invalidation by enumerating keys
125
+ **Why it bites:** "delete all `user:*` keys" → `KEYS user:*` → blocks Redis / iterates massive keyspace. On 10M keys = outage.
126
+ **Rule:** tag-based invalidation, or version bump (atomic, instant), or accept TTL window.
127
+
128
+ ### Conditional caching ("only cache if user is logged out")
129
+ **Why it bites:** branching makes invalidation logic non-uniform. Bug: a logged-in user's query slips into a public cache slot.
130
+ **Rule:** different cache namespaces for different access categories. Don't share keys across auth boundaries.
131
+
132
+ ### Caching the bug
133
+ You found a slow query → cache it → slow query is now hidden but still fires on miss. Cache hides production load, masks the real issue (missing index, N+1, wrong query).
134
+ **Rule:** cache after fixing the underlying cost where reasonable. Cache as a layer, not a band-aid.
135
+
136
+ ### `revalidate: 0` / `cache: 'no-store'` everywhere "to be safe"
137
+ **Why it bites:** every request = full backend hit. Wastes the entire caching infrastructure.
138
+ **Rule:** opt-out caching is the wrong default. Pick a TTL that matches the staleness tolerance.
139
+
140
+ ### Different TTLs for the same data across endpoints
141
+ **Why it bites:** one endpoint serves 10s-stale, another serves 5min-stale, user sees inconsistent state across pages.
142
+ **Rule:** the staleness tolerance is a property of the data, not the endpoint. Document and align.
143
+
144
+ ## Decision Framework
145
+
146
+ | Situation | Choice |
147
+ |---|---|
148
+ | Same data fetched many places in UI | Client query cache (React Query / SWR), shared key |
149
+ | Public mostly-static page | CDN/edge cache + tag invalidation |
150
+ | Per-user dashboard data | Per-user Redis key, short TTL, event-invalidate on write |
151
+ | Expensive aggregation refreshed nightly | Materialized view in DB |
152
+ | Hot read of a single small value (1000+/sec) | In-memory LRU per instance + Redis fallback |
153
+ | Mutation invalidates many entries | Tag-based, OR version-bump pattern |
154
+ | Auth/permission check | Don't cache, OR ≤30s TTL with explicit invalidate on grant change |
155
+ | Analytics counter | Redis `INCR`, batched flush to source |
156
+ | Image/asset | CDN + immutable URL with version hash |
157
+ | Search results page 1, 2, 3 | Cache by `(query, page)`; invalidate by query on data change |
158
+
159
+ ## Cost Model
160
+
161
+ | Layer | Hit cost | Miss cost (approx) |
162
+ |---|---|---|
163
+ | Browser HTTP | < 1ms | + full request to server |
164
+ | CDN/edge | 5-30ms | + origin fetch |
165
+ | In-memory LRU | < 100µs | + downstream load |
166
+ | Redis (same region) | 0.5-1ms | + source load |
167
+ | DB query cache | varies by warmness | + planning + execution |
168
+
169
+ | Anti-pattern | Cost when wrong |
170
+ |---|---|
171
+ | No stampede protection | DB CPU spike during high traffic + cache flush |
172
+ | No TTL with bug | Stale data accumulates, eventual user-facing wrongness |
173
+ | Long auth TTL | Revoked perms still active = security |
174
+ | Public cache on private data | Cross-user data leak |
175
+ | Mass enumeration invalidate | Redis stall, possible outage |
176
+
177
+ ## Red Flags in Diff
178
+
179
+ - New cache write without TTL set inline → flag.
180
+ - New cache fill without stampede mitigation on a path the planner described as hot → flag.
181
+ - `Cache-Control: public, max-age=...` on per-user response → flag immediately.
182
+ - TTL > 60 seconds on auth / permission / billing-related data → flag.
183
+ - `KEYS pattern*` or `SCAN` over the entire keyspace called from a write path → flag.
184
+ - Cache invalidation that loops over a list to delete keys, list size unbounded → flag.
185
+ - Cache key without version/namespace prefix in a codebase that uses them → flag.
186
+ - Two endpoints caching the same logical data with different TTLs → flag.
187
+ - Cache fill block under a single mutex covering unrelated keys → flag (lock too coarse).
188
+ - New cache layer added without a "how does this get invalidated?" comment or doc → flag.
189
+ - `staleTime: Infinity` / `revalidate: false` on a query whose data clearly changes → flag.
190
+ - Cache configured with no `maxmemory` / eviction policy → flag (unbounded memory).