agents-city 0.3.0-beta.21

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 (440) hide show
  1. package/.claude-plugin/marketplace.json +16 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +5 -0
  4. package/README.es.md +2021 -0
  5. package/README.md +1999 -0
  6. package/benchmarks/committee/README.md +39 -0
  7. package/benchmarks/committee/metrics.py +51 -0
  8. package/benchmarks/committee/run.py +64 -0
  9. package/benchmarks/committee/traces/01-single.json +8 -0
  10. package/benchmarks/committee/traces/02-mesh.json +16 -0
  11. package/benchmarks/committee/traces/03-chair.json +15 -0
  12. package/benchmarks/latency/README.md +35 -0
  13. package/benchmarks/latency/fake-claude-cli.mjs +119 -0
  14. package/benchmarks/latency/fake-native-server.mjs +340 -0
  15. package/benchmarks/latency/fake-tui.py +91 -0
  16. package/benchmarks/latency/live.py +380 -0
  17. package/benchmarks/stress/README.md +20 -0
  18. package/benchmarks/stress/run.py +487 -0
  19. package/bin/agents +4 -0
  20. package/bin/agents-city.js +116 -0
  21. package/bin/benchmark +17 -0
  22. package/bin/bus +6 -0
  23. package/bin/cities +3 -0
  24. package/bin/city +86 -0
  25. package/bin/committee +6 -0
  26. package/bin/demo +200 -0
  27. package/bin/exit +5 -0
  28. package/bin/hall +73 -0
  29. package/bin/hall.html +426 -0
  30. package/bin/logs +3 -0
  31. package/bin/report.py +14 -0
  32. package/bin/reset +3 -0
  33. package/bin/road +3 -0
  34. package/bin/seat +10 -0
  35. package/bin/serve.py +1428 -0
  36. package/bin/setup.py +753 -0
  37. package/bin/skills +3 -0
  38. package/bin/test +131 -0
  39. package/bin/test-adapter.py +183 -0
  40. package/bin/test-admision.py +99 -0
  41. package/bin/test-avatar.py +58 -0
  42. package/bin/test-benchmark.py +40 -0
  43. package/bin/test-broker.py +334 -0
  44. package/bin/test-cage.py +206 -0
  45. package/bin/test-card.py +402 -0
  46. package/bin/test-channel.py +379 -0
  47. package/bin/test-cities.py +453 -0
  48. package/bin/test-claude-runtime.py +252 -0
  49. package/bin/test-committee.py +340 -0
  50. package/bin/test-contracts.py +544 -0
  51. package/bin/test-crecimiento.py +164 -0
  52. package/bin/test-demo.py +308 -0
  53. package/bin/test-doctor.py +106 -0
  54. package/bin/test-domains.py +151 -0
  55. package/bin/test-evidencia.py +50 -0
  56. package/bin/test-exit.py +128 -0
  57. package/bin/test-hall-protocol.py +93 -0
  58. package/bin/test-launch.py +169 -0
  59. package/bin/test-live-feed.py +508 -0
  60. package/bin/test-pairing.py +99 -0
  61. package/bin/test-parcels.py +283 -0
  62. package/bin/test-runtime-failures.py +252 -0
  63. package/bin/test-runtime-ui.py +488 -0
  64. package/bin/test-runtime.py +268 -0
  65. package/bin/test-rutas.py +80 -0
  66. package/bin/test-seat.py +1386 -0
  67. package/bin/test-security.py +150 -0
  68. package/bin/test-serve.py +1057 -0
  69. package/bin/test-stress.py +64 -0
  70. package/bin/test-widgets.py +143 -0
  71. package/bin/test-workspace.py +188 -0
  72. package/bin/testlib.py +95 -0
  73. package/bin/tokens.py +14 -0
  74. package/bus/scripts/deploy.sh +60 -0
  75. package/bus/scripts/mint-token.sh +68 -0
  76. package/bus/scripts/setup-dev.sh +79 -0
  77. package/bus/scripts/test-channel.ts +8 -0
  78. package/bus/scripts/test-hub.ts +266 -0
  79. package/bus/scripts/test-local.sh +40 -0
  80. package/bus/scripts/test-queue.ts +132 -0
  81. package/bus/worker/package-lock.json +1971 -0
  82. package/bus/worker/package.json +17 -0
  83. package/bus/worker/src/index.ts +485 -0
  84. package/bus/worker/tsconfig.json +13 -0
  85. package/bus/worker/wrangler.toml +32 -0
  86. package/city/oven/README.md +59 -0
  87. package/city/oven/collect.py +43 -0
  88. package/city/oven/oven.html +4 -0
  89. package/city/scripts/history.py +120 -0
  90. package/city/scripts/seed.py +442 -0
  91. package/city/web/assets/modelos/License.txt +28 -0
  92. package/city/web/assets/modelos/PROCEDENCIA.txt +3 -0
  93. package/city/web/assets/modelos/building-a.glb +0 -0
  94. package/city/web/assets/modelos/building-b.glb +0 -0
  95. package/city/web/assets/modelos/building-c.glb +0 -0
  96. package/city/web/assets/modelos/building-d.glb +0 -0
  97. package/city/web/assets/modelos/building-e.glb +0 -0
  98. package/city/web/assets/modelos/building-f.glb +0 -0
  99. package/city/web/assets/modelos/building-g.glb +0 -0
  100. package/city/web/assets/modelos/building-h.glb +0 -0
  101. package/city/web/assets/sprites/building-a.png +0 -0
  102. package/city/web/assets/sprites/building-b.png +0 -0
  103. package/city/web/assets/sprites/building-c.png +0 -0
  104. package/city/web/assets/sprites/building-d.png +0 -0
  105. package/city/web/assets/sprites/building-e.png +0 -0
  106. package/city/web/assets/sprites/building-f.png +0 -0
  107. package/city/web/assets/sprites/building-g.png +0 -0
  108. package/city/web/assets/sprites/building-h.png +0 -0
  109. package/city/web/assets/sprites/building-i.png +0 -0
  110. package/city/web/assets/sprites/building-j.png +0 -0
  111. package/city/web/assets/sprites/building-k.png +0 -0
  112. package/city/web/assets/sprites/building-l.png +0 -0
  113. package/city/web/assets/sprites/building-m.png +0 -0
  114. package/city/web/assets/sprites/building-n.png +0 -0
  115. package/city/web/assets/sprites/building-skyscraper-a.png +0 -0
  116. package/city/web/assets/sprites/building-skyscraper-b.png +0 -0
  117. package/city/web/assets/sprites/building-skyscraper-c.png +0 -0
  118. package/city/web/assets/sprites/building-skyscraper-d.png +0 -0
  119. package/city/web/assets/sprites/building-skyscraper-e.png +0 -0
  120. package/city/web/assets/sprites/catalogo.json +47 -0
  121. package/city/web/assets/sprites/detail-awning-wide.png +0 -0
  122. package/city/web/assets/sprites/detail-awning.png +0 -0
  123. package/city/web/assets/sprites/detail-overhang-wide.png +0 -0
  124. package/city/web/assets/sprites/detail-overhang.png +0 -0
  125. package/city/web/assets/sprites/detail-parasol-a.png +0 -0
  126. package/city/web/assets/sprites/detail-parasol-b.png +0 -0
  127. package/city/web/assets/sprites/low-detail-building-a.png +0 -0
  128. package/city/web/assets/sprites/low-detail-building-b.png +0 -0
  129. package/city/web/assets/sprites/low-detail-building-c.png +0 -0
  130. package/city/web/assets/sprites/low-detail-building-d.png +0 -0
  131. package/city/web/assets/sprites/low-detail-building-e.png +0 -0
  132. package/city/web/assets/sprites/low-detail-building-f.png +0 -0
  133. package/city/web/assets/sprites/low-detail-building-g.png +0 -0
  134. package/city/web/assets/sprites/low-detail-building-h.png +0 -0
  135. package/city/web/assets/sprites/low-detail-building-i.png +0 -0
  136. package/city/web/assets/sprites/low-detail-building-j.png +0 -0
  137. package/city/web/assets/sprites/low-detail-building-k.png +0 -0
  138. package/city/web/assets/sprites/low-detail-building-l.png +0 -0
  139. package/city/web/assets/sprites/low-detail-building-m.png +0 -0
  140. package/city/web/assets/sprites/low-detail-building-n.png +0 -0
  141. package/city/web/assets/sprites/low-detail-building-wide-a.png +0 -0
  142. package/city/web/assets/sprites/low-detail-building-wide-b.png +0 -0
  143. package/city/web/assets/sprites/medidas.json +248 -0
  144. package/city/web/dist/city.js +939 -0
  145. package/city/web/dist/index.html +294 -0
  146. package/city/web/dist/modelos/License.txt +28 -0
  147. package/city/web/dist/modelos/PROCEDENCIA.txt +3 -0
  148. package/city/web/dist/modelos/building-a.glb +0 -0
  149. package/city/web/dist/modelos/building-b.glb +0 -0
  150. package/city/web/dist/modelos/building-c.glb +0 -0
  151. package/city/web/dist/modelos/building-d.glb +0 -0
  152. package/city/web/dist/modelos/building-e.glb +0 -0
  153. package/city/web/dist/modelos/building-f.glb +0 -0
  154. package/city/web/dist/modelos/building-g.glb +0 -0
  155. package/city/web/dist/modelos/building-h.glb +0 -0
  156. package/city/web/dist/sprites/building-a.png +0 -0
  157. package/city/web/dist/sprites/building-b.png +0 -0
  158. package/city/web/dist/sprites/building-c.png +0 -0
  159. package/city/web/dist/sprites/building-d.png +0 -0
  160. package/city/web/dist/sprites/building-e.png +0 -0
  161. package/city/web/dist/sprites/building-f.png +0 -0
  162. package/city/web/dist/sprites/building-g.png +0 -0
  163. package/city/web/dist/sprites/building-h.png +0 -0
  164. package/city/web/dist/sprites/building-i.png +0 -0
  165. package/city/web/dist/sprites/building-j.png +0 -0
  166. package/city/web/dist/sprites/building-k.png +0 -0
  167. package/city/web/dist/sprites/building-l.png +0 -0
  168. package/city/web/dist/sprites/building-m.png +0 -0
  169. package/city/web/dist/sprites/building-n.png +0 -0
  170. package/city/web/dist/sprites/building-skyscraper-a.png +0 -0
  171. package/city/web/dist/sprites/building-skyscraper-b.png +0 -0
  172. package/city/web/dist/sprites/building-skyscraper-c.png +0 -0
  173. package/city/web/dist/sprites/building-skyscraper-d.png +0 -0
  174. package/city/web/dist/sprites/building-skyscraper-e.png +0 -0
  175. package/city/web/dist/sprites/catalogo.json +47 -0
  176. package/city/web/dist/sprites/detail-awning-wide.png +0 -0
  177. package/city/web/dist/sprites/detail-awning.png +0 -0
  178. package/city/web/dist/sprites/detail-overhang-wide.png +0 -0
  179. package/city/web/dist/sprites/detail-overhang.png +0 -0
  180. package/city/web/dist/sprites/detail-parasol-a.png +0 -0
  181. package/city/web/dist/sprites/detail-parasol-b.png +0 -0
  182. package/city/web/dist/sprites/low-detail-building-a.png +0 -0
  183. package/city/web/dist/sprites/low-detail-building-b.png +0 -0
  184. package/city/web/dist/sprites/low-detail-building-c.png +0 -0
  185. package/city/web/dist/sprites/low-detail-building-d.png +0 -0
  186. package/city/web/dist/sprites/low-detail-building-e.png +0 -0
  187. package/city/web/dist/sprites/low-detail-building-f.png +0 -0
  188. package/city/web/dist/sprites/low-detail-building-g.png +0 -0
  189. package/city/web/dist/sprites/low-detail-building-h.png +0 -0
  190. package/city/web/dist/sprites/low-detail-building-i.png +0 -0
  191. package/city/web/dist/sprites/low-detail-building-j.png +0 -0
  192. package/city/web/dist/sprites/low-detail-building-k.png +0 -0
  193. package/city/web/dist/sprites/low-detail-building-l.png +0 -0
  194. package/city/web/dist/sprites/low-detail-building-m.png +0 -0
  195. package/city/web/dist/sprites/low-detail-building-n.png +0 -0
  196. package/city/web/dist/sprites/low-detail-building-wide-a.png +0 -0
  197. package/city/web/dist/sprites/low-detail-building-wide-b.png +0 -0
  198. package/city/web/dist/sprites/medidas.json +248 -0
  199. package/city/web/dist-hall/hall.js +264 -0
  200. package/city/web/index.html +294 -0
  201. package/city/web/package.json +18 -0
  202. package/city/web/sella.py +21 -0
  203. package/city/web/src/activity-actors.ts +47 -0
  204. package/city/web/src/activity.ts +172 -0
  205. package/city/web/src/ayuntamiento.ts +580 -0
  206. package/city/web/src/draw.ts +640 -0
  207. package/city/web/src/game-speech.ts +79 -0
  208. package/city/web/src/hall.ts +1867 -0
  209. package/city/web/src/main.ts +2225 -0
  210. package/city/web/src/oven.ts +124 -0
  211. package/city/web/src/people.ts +244 -0
  212. package/city/web/src/presencia.ts +193 -0
  213. package/city/web/src/puertas.ts +180 -0
  214. package/city/web/src/repo-roles.ts +59 -0
  215. package/city/worker/package.json +16 -0
  216. package/city/worker/schema.sql +117 -0
  217. package/city/worker/src/index.ts +702 -0
  218. package/city/worker/src/square.ts +218 -0
  219. package/city/worker/wrangler.toml +58 -0
  220. package/demo/ada.md +50 -0
  221. package/demo/bruno.md +62 -0
  222. package/demo/camila.md +56 -0
  223. package/demo/city/ada.md +40 -0
  224. package/demo/city/city.yml +8 -0
  225. package/demo/city/parcels.yml +15 -0
  226. package/demo/city/roads.json +4 -0
  227. package/demo/city/units.yml +10 -0
  228. package/demo/clinica/city.yml +8 -0
  229. package/demo/clinica/parcels.yml +15 -0
  230. package/demo/clinica/roads.json +4 -0
  231. package/demo/clinica/units.yml +9 -0
  232. package/demo/clinica/vera.md +46 -0
  233. package/demo/dante.md +54 -0
  234. package/demo/despacho/city.yml +8 -0
  235. package/demo/despacho/marta.md +46 -0
  236. package/demo/despacho/parcels.yml +15 -0
  237. package/demo/despacho/roads.json +4 -0
  238. package/demo/despacho/units.yml +9 -0
  239. package/demo/elsa.md +50 -0
  240. package/demo/farid.md +52 -0
  241. package/demo/greta.md +50 -0
  242. package/demo/hugo.md +49 -0
  243. package/demo/iris.md +49 -0
  244. package/demo/jonas.md +51 -0
  245. package/demo/kira.md +51 -0
  246. package/demo/luca.md +49 -0
  247. package/demo/parcels.yml +92 -0
  248. package/demo/seed.py +20 -0
  249. package/demo/show.py +161 -0
  250. package/demo/stories.py +824 -0
  251. package/demo/units.yml +33 -0
  252. package/docs/agents-first.md +99 -0
  253. package/docs/glossary.md +55 -0
  254. package/docs/map-live-layers.md +73 -0
  255. package/docs/security.md +231 -0
  256. package/docs/self-host.md +151 -0
  257. package/docs/testing.md +205 -0
  258. package/package.json +73 -0
  259. package/plugin/.claude-plugin/plugin.json +62 -0
  260. package/plugin/.mcp.json +15 -0
  261. package/plugin/channel/activity-cli.ts +16 -0
  262. package/plugin/channel/adapter-prompts.ts +113 -0
  263. package/plugin/channel/adapter.js +4292 -0
  264. package/plugin/channel/adapter.ts +133 -0
  265. package/plugin/channel/bus.js +20188 -0
  266. package/plugin/channel/bus.ts +131 -0
  267. package/plugin/channel/city-config.ts +125 -0
  268. package/plugin/channel/claude-channel.ts +58 -0
  269. package/plugin/channel/cli-args.ts +75 -0
  270. package/plugin/channel/client.js +4200 -0
  271. package/plugin/channel/client.ts +38 -0
  272. package/plugin/channel/committee/activity.ts +256 -0
  273. package/plugin/channel/committee/collection.ts +154 -0
  274. package/plugin/channel/committee/decision.ts +175 -0
  275. package/plugin/channel/committee/floor.ts +135 -0
  276. package/plugin/channel/committee/guards.ts +30 -0
  277. package/plugin/channel/committee/history.ts +38 -0
  278. package/plugin/channel/committee/render.ts +102 -0
  279. package/plugin/channel/committee/service.ts +72 -0
  280. package/plugin/channel/committee/storage.ts +92 -0
  281. package/plugin/channel/committee/types.ts +163 -0
  282. package/plugin/channel/committee/view.ts +63 -0
  283. package/plugin/channel/committee-cli.ts +109 -0
  284. package/plugin/channel/delivery-metrics.ts +61 -0
  285. package/plugin/channel/delivery-queue.ts +118 -0
  286. package/plugin/channel/hub/activity-controller.ts +72 -0
  287. package/plugin/channel/hub/activity-feed.ts +169 -0
  288. package/plugin/channel/hub/committee-controller.ts +60 -0
  289. package/plugin/channel/hub/connections.ts +48 -0
  290. package/plugin/channel/hub/diagnostics.ts +67 -0
  291. package/plugin/channel/hub/envelope-validity.ts +109 -0
  292. package/plugin/channel/hub/envelopes.ts +66 -0
  293. package/plugin/channel/hub/lifecycle.ts +91 -0
  294. package/plugin/channel/hub/local-roads.ts +89 -0
  295. package/plugin/channel/hub/remote-roads.ts +165 -0
  296. package/plugin/channel/hub/road-controller.ts +99 -0
  297. package/plugin/channel/hub-client.ts +149 -0
  298. package/plugin/channel/local-hub.js +5995 -0
  299. package/plugin/channel/local-hub.ts +349 -0
  300. package/plugin/channel/map-reporter.ts +48 -0
  301. package/plugin/channel/package-lock.json +1731 -0
  302. package/plugin/channel/package.json +19 -0
  303. package/plugin/channel/protocol.ts +98 -0
  304. package/plugin/channel/road-cli.ts +18 -0
  305. package/plugin/channel/run.sh +15 -0
  306. package/plugin/channel/runtime/claude.ts +458 -0
  307. package/plugin/channel/runtime/codex-config.ts +77 -0
  308. package/plugin/channel/runtime/codex.ts +669 -0
  309. package/plugin/channel/runtime/command.ts +62 -0
  310. package/plugin/channel/runtime/factory.ts +15 -0
  311. package/plugin/channel/runtime/json-rpc.ts +154 -0
  312. package/plugin/channel/runtime/kimi.ts +277 -0
  313. package/plugin/channel/runtime/opencode.ts +248 -0
  314. package/plugin/channel/runtime/process.ts +84 -0
  315. package/plugin/channel/runtime/types.ts +45 -0
  316. package/plugin/channel/runtime-files.ts +73 -0
  317. package/plugin/channel/runtime-gateway.js +6396 -0
  318. package/plugin/channel/runtime-gateway.ts +358 -0
  319. package/plugin/channel/runtime-metrics.ts +52 -0
  320. package/plugin/channel/runtime-subscription.ts +180 -0
  321. package/plugin/channel/terminal-delivery.ts +131 -0
  322. package/plugin/channel/untrusted.ts +60 -0
  323. package/plugin/commands/committee.md +31 -0
  324. package/plugin/commands/exit.md +32 -0
  325. package/plugin/commands/goals.md +20 -0
  326. package/plugin/commands/join.md +18 -0
  327. package/plugin/commands/notice.md +20 -0
  328. package/plugin/commands/propose.md +19 -0
  329. package/plugin/commands/round.md +17 -0
  330. package/plugin/commands/session.md +22 -0
  331. package/plugin/commands/settings.md +25 -0
  332. package/plugin/commands/setup.md +21 -0
  333. package/plugin/commands/team.md +23 -0
  334. package/plugin/domains/custom.md +27 -0
  335. package/plugin/domains/finance.md +31 -0
  336. package/plugin/domains/healthcare.md +34 -0
  337. package/plugin/domains/legal.md +31 -0
  338. package/plugin/domains/marketing.md +33 -0
  339. package/plugin/domains/operations.md +31 -0
  340. package/plugin/domains/research.md +32 -0
  341. package/plugin/domains/sales.md +30 -0
  342. package/plugin/domains/software.md +37 -0
  343. package/plugin/hooks/activity.sh +6 -0
  344. package/plugin/hooks/digging.sh +68 -0
  345. package/plugin/hooks/growth.sh +42 -0
  346. package/plugin/hooks/hooks.json +106 -0
  347. package/plugin/hooks/notice-on-pr.sh +35 -0
  348. package/plugin/hooks/notice-on-stop.sh +80 -0
  349. package/plugin/hooks/notice-pending.sh +58 -0
  350. package/plugin/hooks/solo-en-ciudad.sh +23 -0
  351. package/plugin/hooks/tokens.sh +47 -0
  352. package/plugin/roles/examples/account-executive.md +25 -0
  353. package/plugin/roles/examples/ai-manager.md +34 -0
  354. package/plugin/roles/examples/associate.md +24 -0
  355. package/plugin/roles/examples/brand-lead.md +20 -0
  356. package/plugin/roles/examples/cfo.md +20 -0
  357. package/plugin/roles/examples/city-lead.md +25 -0
  358. package/plugin/roles/examples/clinical-director.md +26 -0
  359. package/plugin/roles/examples/clinical-ops.md +25 -0
  360. package/plugin/roles/examples/clinician.md +25 -0
  361. package/plugin/roles/examples/compliance.md +27 -0
  362. package/plugin/roles/examples/content.md +24 -0
  363. package/plugin/roles/examples/controller.md +27 -0
  364. package/plugin/roles/examples/cpto.md +41 -0
  365. package/plugin/roles/examples/customer-success.md +25 -0
  366. package/plugin/roles/examples/data-engineer.md +37 -0
  367. package/plugin/roles/examples/data.md +60 -0
  368. package/plugin/roles/examples/dev.md +37 -0
  369. package/plugin/roles/examples/devops.md +39 -0
  370. package/plugin/roles/examples/enablement.md +24 -0
  371. package/plugin/roles/examples/ethics.md +24 -0
  372. package/plugin/roles/examples/fin-analytics.md +27 -0
  373. package/plugin/roles/examples/health-compliance.md +25 -0
  374. package/plugin/roles/examples/health-data.md +26 -0
  375. package/plugin/roles/examples/knowledge.md +25 -0
  376. package/plugin/roles/examples/lifecycle.md +26 -0
  377. package/plugin/roles/examples/llm-engineer.md +34 -0
  378. package/plugin/roles/examples/managing-partner.md +20 -0
  379. package/plugin/roles/examples/methods.md +23 -0
  380. package/plugin/roles/examples/operations-lead.md +23 -0
  381. package/plugin/roles/examples/ops.md +24 -0
  382. package/plugin/roles/examples/patient-safety.md +26 -0
  383. package/plugin/roles/examples/performance.md +27 -0
  384. package/plugin/roles/examples/po.md +36 -0
  385. package/plugin/roles/examples/process-owner.md +23 -0
  386. package/plugin/roles/examples/product-design.md +39 -0
  387. package/plugin/roles/examples/program-manager.md +24 -0
  388. package/plugin/roles/examples/quality.md +25 -0
  389. package/plugin/roles/examples/research-director.md +24 -0
  390. package/plugin/roles/examples/research-ops.md +25 -0
  391. package/plugin/roles/examples/researcher.md +25 -0
  392. package/plugin/roles/examples/revenue-lead.md +25 -0
  393. package/plugin/roles/examples/revops.md +26 -0
  394. package/plugin/roles/examples/seo.md +27 -0
  395. package/plugin/roles/examples/specialist.md +24 -0
  396. package/plugin/scripts/admision.py +165 -0
  397. package/plugin/scripts/apaga.py +232 -0
  398. package/plugin/scripts/avatar.py +211 -0
  399. package/plugin/scripts/broker.py +531 -0
  400. package/plugin/scripts/cage.py +252 -0
  401. package/plugin/scripts/capabilities.py +197 -0
  402. package/plugin/scripts/card.py +312 -0
  403. package/plugin/scripts/cities.py +520 -0
  404. package/plugin/scripts/city-env.sh +90 -0
  405. package/plugin/scripts/city-runtime.sh +87 -0
  406. package/plugin/scripts/city-session.sh +526 -0
  407. package/plugin/scripts/city_env.py +67 -0
  408. package/plugin/scripts/crecimiento.py +140 -0
  409. package/plugin/scripts/deliberations.py +48 -0
  410. package/plugin/scripts/doctor.py +172 -0
  411. package/plugin/scripts/domains.py +201 -0
  412. package/plugin/scripts/evidencia.py +64 -0
  413. package/plugin/scripts/find-repos.sh +111 -0
  414. package/plugin/scripts/gh.py +159 -0
  415. package/plugin/scripts/hall_protocol.py +147 -0
  416. package/plugin/scripts/hook_activity.py +139 -0
  417. package/plugin/scripts/launch.py +94 -0
  418. package/plugin/scripts/logs.py +98 -0
  419. package/plugin/scripts/pairing.py +185 -0
  420. package/plugin/scripts/parcels.py +132 -0
  421. package/plugin/scripts/read-card.py +51 -0
  422. package/plugin/scripts/report.py +201 -0
  423. package/plugin/scripts/reset.py +180 -0
  424. package/plugin/scripts/roads.py +230 -0
  425. package/plugin/scripts/roles.py +274 -0
  426. package/plugin/scripts/runtime_log.py +69 -0
  427. package/plugin/scripts/runtime_processes.py +132 -0
  428. package/plugin/scripts/rutas.py +83 -0
  429. package/plugin/scripts/seat.py +1349 -0
  430. package/plugin/scripts/tokens.py +194 -0
  431. package/plugin/scripts/trust-repos.py +59 -0
  432. package/plugin/scripts/ui.py +280 -0
  433. package/plugin/scripts/units.py +80 -0
  434. package/plugin/scripts/workspace.py +364 -0
  435. package/plugin/skills/city/SKILL.md +191 -0
  436. package/templates/blank.md +39 -0
  437. package/templates/finance.md +44 -0
  438. package/templates/legal.md +45 -0
  439. package/templates/marketing.md +48 -0
  440. package/templates/product.md +45 -0
@@ -0,0 +1,580 @@
1
+ /**
2
+ * The town hall: where a committee is something you can watch.
3
+ *
4
+ * The committee is the most elaborate machine in the product — isolated
5
+ * positions, a synthesis, a moderated floor, a verified decision — and until
6
+ * now the map said nothing about it. This draws the machine as an event in the
7
+ * city: positions fly in face down from each member's house, the synthesis
8
+ * turns them over, the floor is a request you can see granted or denied, and
9
+ * closing files the act. Everything here is driven by the same activity events
10
+ * the Hall already relays; nothing is invented at render time.
11
+ *
12
+ * One session on stage at a time: a second `committee.opened` replaces the
13
+ * scene. The transcript is the Hall's job — this is the town noticing that its
14
+ * committee is sitting.
15
+ */
16
+ import { Container, Graphics, Text, TextStyle } from 'pixi.js';
17
+ import { TH, TW, tono } from './draw';
18
+ import { colorDe } from './people';
19
+ import type { ActivityEvent } from './activity';
20
+
21
+ const PIEDRA = 0xb9a67c; // civic stone: warmer than any district, owned by none
22
+ const CREMA = 0xe8e2d4;
23
+ const AMBAR = 0xf5c451;
24
+ const VENTANA = 0xffd98a;
25
+ const BIEN = 0x3fb8a0;
26
+ const MAL = 0xe2604f;
27
+
28
+ export interface Ancla {
29
+ x: number;
30
+ y: number;
31
+ }
32
+
33
+ /** What each committee event does to the badge over the door. The kinds are
34
+ * the engine's own — the same list `isSpeechEvent` filters on. */
35
+ const FASES: Record<string, { texto: string; color: number }> = {
36
+ 'committee.opened': { texto: 'IN SESSION · COLLECTING', color: AMBAR },
37
+ 'committee.position.submitted': { texto: 'IN SESSION · COLLECTING', color: AMBAR },
38
+ 'committee.position.revealed': { texto: 'REVIEW · POSITIONS OPEN', color: AMBAR },
39
+ 'committee.positions.revealed': { texto: 'REVIEW · POSITIONS OPEN', color: AMBAR },
40
+ 'committee.synthesis.published': { texto: 'SYNTHESIS', color: AMBAR },
41
+ 'committee.floor.requested': { texto: 'FLOOR REQUESTED', color: AMBAR },
42
+ 'committee.floor.granted': { texto: 'FLOOR GRANTED', color: BIEN },
43
+ 'committee.floor.denied': { texto: 'FLOOR DENIED', color: MAL },
44
+ 'committee.floor.spoke': { texto: 'FLOOR · SPOKEN', color: AMBAR },
45
+ 'committee.decision.recorded': { texto: 'DECIDED', color: AMBAR },
46
+ 'committee.verification.assigned': { texto: 'VERIFYING', color: AMBAR },
47
+ // The activity feed says `.pass`/`.fail`; the internal deliveries say
48
+ // `.passed`/`.failed`. The stage answers to both so a rename upstream can
49
+ // never silently unhook the stamp.
50
+ 'committee.verification.pass': { texto: 'VERIFIED ✓', color: BIEN },
51
+ 'committee.verification.fail': { texto: 'VERIFICATION FAILED', color: MAL },
52
+ 'committee.verification.passed': { texto: 'VERIFIED ✓', color: BIEN },
53
+ 'committee.verification.failed': { texto: 'VERIFICATION FAILED', color: MAL },
54
+ 'committee.replanned': { texto: 'REPLANNING', color: AMBAR },
55
+ 'committee.closed': { texto: 'CLOSED · ACT FILED', color: CREMA },
56
+ 'committee.cancelled': { texto: 'CANCELLED', color: MAL },
57
+ };
58
+
59
+ export class Ayuntamiento {
60
+ /** The building itself, in world coordinates, under the people layer. */
61
+ readonly edificio = new Container();
62
+ /** The badge, in the banner layer, so the zoom keeps it readable. */
63
+ readonly cartel = new Container();
64
+
65
+ private readonly luces: Graphics;
66
+ private readonly halo: Graphics;
67
+ private fase: Text;
68
+ private quien: Text;
69
+ private chapa: Graphics;
70
+ private hilo: string | null = null;
71
+ private naipes = new Map<string, Container>();
72
+ private encendido = false;
73
+ private manoChip: Container | null = null;
74
+ private manoActor: string | null = null;
75
+
76
+ constructor(
77
+ private readonly capaVuelo: Container,
78
+ private readonly pos: Ancla,
79
+ private readonly casaDe: (actor: string) => Ancla | null,
80
+ private readonly anima: (f: (dt: number) => boolean) => void,
81
+ private readonly sinMovimiento: boolean,
82
+ ) {
83
+ const { edificio } = this;
84
+ edificio.position.set(pos.x, pos.y);
85
+ const w = TW * 0.82,
86
+ d = TH * 0.82,
87
+ alto = 72;
88
+
89
+ // The paved forecourt: what makes the hall read as a landmark when nothing
90
+ // is happening, instead of one more hut past the square.
91
+ edificio.addChild(
92
+ new Graphics()
93
+ .poly([0, -d * 2.1, w * 2.1, 0, 0, d * 2.1, -w * 2.1, 0])
94
+ .fill({ color: 0xc8b48a, alpha: 0.05 })
95
+ .poly([0, -d * 2.1, w * 2.1, 0, 0, d * 2.1, -w * 2.1, 0])
96
+ .stroke({ color: tono(PIEDRA, 0.1), width: 1, alpha: 0.35 }),
97
+ );
98
+
99
+ // The same three-tone prism as every house: the hall is of the city, not a
100
+ // UI element floating over it.
101
+ edificio.addChild(
102
+ new Graphics().ellipse(0, d * 0.55, w * 1.4, d * 0.95).fill({ color: 0x000000, alpha: 0.32 }),
103
+ );
104
+ const cuerpo = new Graphics();
105
+ cuerpo
106
+ .poly([0, d, -w, 0, -w, -alto, 0, d - alto])
107
+ .fill({ color: tono(PIEDRA, -0.28) })
108
+ .poly([0, d, w, 0, w, -alto, 0, d - alto])
109
+ .fill({ color: tono(PIEDRA, -0.52) })
110
+ .poly([0, -d - alto, w, -alto, 0, d - alto, -w, -alto])
111
+ .fill({ color: tono(PIEDRA, 0.24) });
112
+ edificio.addChild(cuerpo);
113
+
114
+ // Columns and a door on the left face — what says "civic" instead of
115
+ // "one more house". The pediment is the top face's near edge, lifted.
116
+ const cara = new Graphics();
117
+ for (const u of [0.22, 0.78]) {
118
+ const px = -w + u * w,
119
+ py = u * d;
120
+ cara.rect(px - 1.8, py - alto * 0.72, 3.6, alto * 0.66).fill({ color: tono(PIEDRA, 0.12) });
121
+ }
122
+ cara
123
+ .rect(-w * 0.5 - 4, d * 0.5 - 19, 8, 19)
124
+ .fill({ color: 0x0b1119, alpha: 0.85 })
125
+ .poly([-w - 3, -alto, 0, d - alto, 0, d - alto - 12, -w - 3, -alto - 12])
126
+ .stroke({ color: tono(PIEDRA, 0.3), width: 1.1, alpha: 0.6 });
127
+ edificio.addChild(cara);
128
+
129
+ // Two door lamps, always lit — the same faint civic warmth the street lamps
130
+ // give the districts, so the hall is never a black box even between sessions.
131
+ for (const [px, py] of [
132
+ [-w * 0.78, d * 0.72],
133
+ [-w * 0.16, d * 1.04],
134
+ ] as const) {
135
+ const luz = new Graphics().ellipse(px, py, 11, 5).fill({ color: VENTANA, alpha: 0.09 });
136
+ luz.blendMode = 'add';
137
+ const poste = new Graphics()
138
+ .moveTo(px, py)
139
+ .lineTo(px, py - 14)
140
+ .stroke({ color: 0x8d99a8, width: 0.9, alpha: 0.75 })
141
+ .circle(px, py - 15, 1.6)
142
+ .fill({ color: VENTANA, alpha: 0.95 });
143
+ edificio.addChild(luz, poste);
144
+ }
145
+
146
+ // The flag: up while the committee sits, at rest otherwise.
147
+ const asta = new Graphics()
148
+ .moveTo(0, -d - alto)
149
+ .lineTo(0, -d - alto - 16)
150
+ .stroke({ color: 0x8d99a8, width: 1.1, alpha: 0.8 });
151
+ const bandera = new Graphics()
152
+ .poly([0, -d - alto - 16, 11, -d - alto - 12.5, 0, -d - alto - 9])
153
+ .fill({ color: AMBAR });
154
+ edificio.addChild(asta, bandera);
155
+
156
+ // Two window rows per lit face, off until a session opens: the committee
157
+ // sitting is exactly the kind of thing lit windows exist to say.
158
+ this.luces = new Graphics();
159
+ for (const fila of [0.32, 0.56]) {
160
+ for (const u of [0.36, 0.6]) {
161
+ const px = -w + u * w,
162
+ py = u * d - alto * fila;
163
+ this.luces.rect(px - 1.6, py - 3.4, 3.2, 4.6).fill({ color: VENTANA, alpha: 0.95 });
164
+ }
165
+ for (const u of [0.3, 0.62]) {
166
+ const px = u * w,
167
+ py = d - u * d - alto * fila;
168
+ this.luces.rect(px - 1.6, py - 3.4, 3.2, 4.6).fill({ color: VENTANA, alpha: 0.75 });
169
+ }
170
+ }
171
+ this.halo = new Graphics()
172
+ .ellipse(0, -alto * 0.45, w * 0.9, alto * 0.6)
173
+ .fill({ color: VENTANA, alpha: 0.12 });
174
+ this.halo.blendMode = 'add';
175
+ this.luces.visible = false;
176
+ this.halo.visible = false;
177
+ edificio.addChild(this.halo, this.luces);
178
+
179
+ // The badge: phase in the display face, actor in the data face. It lives in
180
+ // the banner layer so it keeps its size on screen at every zoom.
181
+ this.fase = new Text({
182
+ text: 'TOWN HALL',
183
+ style: new TextStyle({
184
+ fontFamily: 'Chakra Petch, sans-serif',
185
+ fontSize: 11,
186
+ fontWeight: '700',
187
+ letterSpacing: 1.2,
188
+ fill: 0x131a24,
189
+ }),
190
+ });
191
+ this.quien = new Text({
192
+ text: '',
193
+ style: new TextStyle({ fontFamily: 'IBM Plex Mono, monospace', fontSize: 9, fill: 0x8d99a8 }),
194
+ });
195
+ this.chapa = new Graphics();
196
+ this.cartel.addChild(this.chapa, this.fase, this.quien);
197
+ this.cartel.position.set(pos.x, pos.y - d - alto - 34);
198
+ this.rotula('TOWN HALL', CREMA, '');
199
+ }
200
+
201
+ /** What the building would say if you asked it — which is what clicking it
202
+ * does. The full transcript stays the Hall's job. */
203
+ estado(): { fase: string; actor: string; posiciones: number; activo: boolean } {
204
+ return {
205
+ fase: this.fase.text,
206
+ actor: this.quien.text,
207
+ posiciones: this.naipes.size,
208
+ activo: this.encendido,
209
+ };
210
+ }
211
+
212
+ /** Feed every activity event through here; anything non-committee is ignored. */
213
+ recibe(evento: ActivityEvent): void {
214
+ if (!evento.kind.startsWith('committee.')) return;
215
+ const fase = FASES[evento.kind];
216
+ if (!fase) return;
217
+
218
+ if (evento.kind === 'committee.opened') {
219
+ this.limpia();
220
+ this.hilo = evento.thread;
221
+ this.enciende(true);
222
+ } else if (this.hilo !== null && evento.thread !== null && evento.thread !== this.hilo) {
223
+ // Another thread's event while a session is on stage: the stage is not a
224
+ // multiplexer. The Hall's rail carries every thread; here one sits.
225
+ return;
226
+ }
227
+
228
+ this.rotula(fase.texto, fase.color, evento.actor !== 'system' ? evento.actor : '');
229
+
230
+ switch (evento.kind) {
231
+ case 'committee.position.submitted':
232
+ // Sealed: the card arrives face down. That the chair cannot see it yet
233
+ // is the committee's one hard rule, so the map shows the rule.
234
+ this.llegaNaipe(evento.actor, false);
235
+ break;
236
+ case 'committee.position.revealed':
237
+ this.llegaNaipe(evento.actor, true);
238
+ this.voltea(evento.actor);
239
+ break;
240
+ case 'committee.positions.revealed':
241
+ case 'committee.synthesis.published':
242
+ this.voltea();
243
+ break;
244
+ case 'committee.floor.requested':
245
+ this.mano(evento.actor, 'palabra?', AMBAR, false);
246
+ break;
247
+ // Granting is the chair's act, so the actor is the seat: the chip stays
248
+ // where the hand went up, found through the target or remembered.
249
+ case 'committee.floor.granted':
250
+ this.mano(evento.target ?? this.manoActor ?? evento.actor, 'concedida', BIEN, true);
251
+ break;
252
+ case 'committee.floor.denied':
253
+ this.mano(evento.target ?? this.manoActor ?? evento.actor, 'denegada', MAL, true);
254
+ break;
255
+ case 'committee.verification.pass':
256
+ case 'committee.verification.passed':
257
+ this.sello(true);
258
+ break;
259
+ case 'committee.verification.fail':
260
+ case 'committee.verification.failed':
261
+ this.sello(false);
262
+ break;
263
+ case 'committee.closed':
264
+ this.archiva(true);
265
+ break;
266
+ case 'committee.cancelled':
267
+ this.archiva(false);
268
+ break;
269
+ }
270
+ }
271
+
272
+ /** Badge text and plate, sized to the text — the same shape as a banner. */
273
+ private rotula(texto: string, color: number, actor: string): void {
274
+ this.fase.text = texto;
275
+ this.quien.text = actor;
276
+ const ancho = Math.max(this.fase.width, this.quien.width) + 16;
277
+ this.fase.position.set(-this.fase.width / 2, -9);
278
+ this.quien.position.set(-this.quien.width / 2, 7);
279
+ this.chapa
280
+ .clear()
281
+ .roundRect(-ancho / 2, -13, ancho, actor ? 32 : 22, 3)
282
+ .fill({ color, alpha: 0.92 })
283
+ .roundRect(-ancho / 2, -13, ancho, actor ? 32 : 22, 3)
284
+ .stroke({ color: 0x131a24, width: 1, alpha: 0.25 });
285
+ this.quien.style.fill = 0x131a24;
286
+ this.quien.alpha = 0.75;
287
+ this.quien.visible = Boolean(actor);
288
+ }
289
+
290
+ private enciende(si: boolean): void {
291
+ const ya = this.encendido;
292
+ this.encendido = si;
293
+ this.luces.visible = si;
294
+ this.halo.visible = si;
295
+ if (!si || ya) return; // off, or the breath is already running
296
+ let t = 0;
297
+ this.anima((dt) => {
298
+ if (!this.encendido) {
299
+ this.halo.alpha = 1;
300
+ return false;
301
+ }
302
+ t += 0.045 * dt;
303
+ this.halo.alpha = 0.7 + Math.sin(t) * 0.3; // the slow breath of a lit room
304
+ return true;
305
+ });
306
+ }
307
+
308
+ /**
309
+ * A member's position arrives as a card, face down, flying from their house.
310
+ * Face down is the point: isolated positions are the committee's one hard
311
+ * rule, and the map should show the rule, not just the traffic.
312
+ */
313
+ private llegaNaipe(actor: string, bocarriba: boolean): void {
314
+ if (this.naipes.has(actor)) return;
315
+ const color = colorDe(actor);
316
+ const naipe = new Container();
317
+ const dorso = new Graphics()
318
+ .roundRect(-6, -8.5, 12, 17, 2)
319
+ .fill({ color: 0x1b2431 })
320
+ .roundRect(-6, -8.5, 12, 17, 2)
321
+ .stroke({ color: 0x39465a, width: 1 })
322
+ .moveTo(-3, -4.5)
323
+ .lineTo(3, 4.5)
324
+ .moveTo(3, -4.5)
325
+ .lineTo(-3, 4.5)
326
+ .stroke({ color: 0x39465a, width: 1 });
327
+ const cara = new Graphics()
328
+ .roundRect(-6, -8.5, 12, 17, 2)
329
+ .fill({ color })
330
+ .roundRect(-6, -8.5, 12, 17, 2)
331
+ .stroke({ color: tono(color, 0.3), width: 1 });
332
+ for (let i = 0; i < 3; i++) {
333
+ cara
334
+ .moveTo(-3.4, -4 + i * 4)
335
+ .lineTo(3.4, -4 + i * 4)
336
+ .stroke({ color: 0x121821, width: 1, alpha: 0.55 });
337
+ }
338
+ // A card the map only met after its reveal arrives already open: showing it
339
+ // sealed would claim a secrecy that no longer exists.
340
+ cara.visible = bocarriba;
341
+ dorso.visible = !bocarriba;
342
+ naipe.addChild(dorso, cara);
343
+ (naipe as unknown as { __cara: Graphics; __dorso: Graphics }).__cara = cara;
344
+ (naipe as unknown as { __cara: Graphics; __dorso: Graphics }).__dorso = dorso;
345
+
346
+ const sitio = this.naipes.size;
347
+ this.naipes.set(actor, naipe);
348
+ // Parked in a fan to the hall's right, clear of the badge above the roof —
349
+ // the badge scales with the zoom and would cover a row parked under it.
350
+ const destino = { x: this.pos.x + 46 + sitio * 20, y: this.pos.y - TH - 36 };
351
+ const de = this.casaDe(actor) ?? { x: this.pos.x - 120, y: this.pos.y + 40 };
352
+ naipe.position.set(de.x, de.y - 30);
353
+ this.capaVuelo.addChild(naipe);
354
+
355
+ let t = 0;
356
+ const p0 = { x: naipe.x, y: naipe.y };
357
+ this.anima((dt) => {
358
+ if (!naipe.parent) return false;
359
+ t = Math.min(1, t + 0.02 * dt);
360
+ const s = t < 0.5 ? 2 * t * t : 1 - 2 * (1 - t) * (1 - t);
361
+ naipe.x = p0.x + (destino.x - p0.x) * s;
362
+ naipe.y = p0.y + (destino.y - p0.y) * s - Math.sin(s * Math.PI) * 52;
363
+ naipe.rotation = Math.sin(t * 9) * 0.18;
364
+ if (t >= 1) {
365
+ naipe.position.set(destino.x, destino.y);
366
+ naipe.rotation = 0;
367
+ this.mece(naipe, sitio);
368
+ return false;
369
+ }
370
+ return true;
371
+ });
372
+ }
373
+
374
+ /** Parked cards breathe a little; a frozen prop reads as a glitch. */
375
+ private mece(naipe: Container, sitio: number): void {
376
+ if (this.sinMovimiento) return;
377
+ const base = naipe.y;
378
+ let t = sitio * 1.3;
379
+ this.anima((dt) => {
380
+ if (!naipe.parent) return false;
381
+ t += 0.03 * dt;
382
+ naipe.y = base + Math.sin(t) * 2.2;
383
+ return true;
384
+ });
385
+ }
386
+
387
+ /** The reveal: a sealed position turns over — one actor's card, or the whole
388
+ * table at once. Nobody saw anybody's hand until the chair had all of them,
389
+ * and the flip is that rule, animated. */
390
+ private voltea(solo?: string): void {
391
+ for (const [actor, naipe] of this.naipes) {
392
+ if (solo !== undefined && actor !== solo) continue;
393
+ const partes = naipe as unknown as { __cara?: Graphics; __dorso?: Graphics };
394
+ if (!partes.__cara || partes.__cara.visible) continue;
395
+ const cara = partes.__cara,
396
+ dorso = partes.__dorso!;
397
+ let t = 0;
398
+ this.anima((dt) => {
399
+ if (!naipe.parent) return false;
400
+ t = Math.min(1, t + 0.06 * dt);
401
+ naipe.scale.x = Math.abs(Math.cos(t * Math.PI));
402
+ if (t >= 0.5 && !cara.visible) {
403
+ cara.visible = true;
404
+ dorso.visible = false;
405
+ }
406
+ if (t >= 1) {
407
+ naipe.scale.x = 1;
408
+ return false;
409
+ }
410
+ return true;
411
+ });
412
+ }
413
+ }
414
+
415
+ /**
416
+ * The raised hand: a floor request is the one moment a member interrupts the
417
+ * chair's order, and it happens at THEIR house — the chip hangs over whoever
418
+ * asked, then answers in colour when the chair rules on it.
419
+ */
420
+ private mano(quien: string, texto: string, color: number, resuelve: boolean): void {
421
+ if (!resuelve) {
422
+ this.manoChip?.parent?.removeChild(this.manoChip);
423
+ this.manoChip?.destroy({ children: true });
424
+ this.manoChip = null;
425
+ this.manoActor = quien;
426
+ }
427
+ let chip = this.manoChip;
428
+ if (!chip) {
429
+ const donde = this.casaDe(quien) ?? { x: this.pos.x - 90, y: this.pos.y + 30 };
430
+ chip = new Container();
431
+ chip.position.set(donde.x, donde.y - 64);
432
+ this.capaVuelo.addChild(chip);
433
+ this.manoChip = chip;
434
+ }
435
+ chip.removeChildren().forEach((child) => child.destroy({ children: true }));
436
+ const rotulo = new Text({
437
+ text: `✋ ${texto}`,
438
+ style: new TextStyle({
439
+ fontFamily: 'IBM Plex Mono, monospace',
440
+ fontSize: 10,
441
+ fontWeight: '500',
442
+ fill: 0x131a24,
443
+ }),
444
+ });
445
+ const ancho = rotulo.width + 12;
446
+ chip.addChild(
447
+ new Graphics()
448
+ .roundRect(-ancho / 2, -9, ancho, 18, 9)
449
+ .fill({ color, alpha: 0.94 })
450
+ .roundRect(-ancho / 2, -9, ancho, 18, 9)
451
+ .stroke({ color: 0x131a24, width: 1, alpha: 0.25 }),
452
+ );
453
+ rotulo.position.set(-rotulo.width / 2, -6.5);
454
+ chip.addChild(rotulo);
455
+
456
+ if (!resuelve) {
457
+ // Pop in, then hold: an open request stays visible until it is ruled on.
458
+ let t = 0;
459
+ const mio = chip;
460
+ this.anima((dt) => {
461
+ if (!mio.parent || this.manoChip !== mio) return false;
462
+ t += 0.05 * dt;
463
+ mio.scale.set(Math.min(1, 0.5 + t));
464
+ if (this.sinMovimiento) return false;
465
+ mio.y += Math.sin(t * 1.4) * 0.06 * dt;
466
+ return true;
467
+ });
468
+ return;
469
+ }
470
+ // Ruled on: show the answer, then let it go.
471
+ this.manoActor = null;
472
+ const mio = chip;
473
+ let t = 0;
474
+ this.anima((dt) => {
475
+ if (!mio.parent) return false;
476
+ t += 0.008 * dt;
477
+ if (t > 0.55) {
478
+ mio.alpha = Math.max(0, (1 - t) / 0.45);
479
+ mio.y -= 0.25 * dt;
480
+ }
481
+ if (t >= 1) {
482
+ mio.parent?.removeChild(mio);
483
+ mio.destroy({ children: true });
484
+ if (this.manoChip === mio) this.manoChip = null;
485
+ return false;
486
+ }
487
+ return true;
488
+ });
489
+ }
490
+
491
+ /** The verifier's stamp: one clean mark over the door, then gone. */
492
+ private sello(paso: boolean): void {
493
+ const g = new Graphics();
494
+ if (paso) {
495
+ g.moveTo(-7, 0).lineTo(-2, 5).lineTo(8, -6).stroke({ color: BIEN, width: 3 });
496
+ } else {
497
+ g.moveTo(-6, -6).lineTo(6, 6).moveTo(6, -6).lineTo(-6, 6).stroke({ color: MAL, width: 3 });
498
+ }
499
+ g.position.set(this.pos.x, this.pos.y - 102);
500
+ this.capaVuelo.addChild(g);
501
+ let t = 0;
502
+ this.anima((dt) => {
503
+ t += 0.014 * dt;
504
+ g.alpha = t > 0.7 ? Math.max(0, (1 - t) / 0.3) : 1;
505
+ g.scale.set(t < 0.15 ? 0.6 + t * 3 : 1.05);
506
+ if (t >= 1) {
507
+ g.parent?.removeChild(g);
508
+ g.destroy();
509
+ return false;
510
+ }
511
+ return true;
512
+ });
513
+ }
514
+
515
+ /** Closing files the act: a document rises from the hall, signed, and the
516
+ * cards leave with it. The durable record is on disk; this is the ceremony. */
517
+ private archiva(conActa: boolean): void {
518
+ this.enciende(false);
519
+ let acta: Container | null = null;
520
+ if (conActa) {
521
+ acta = new Container();
522
+ const papel = new Graphics()
523
+ .roundRect(-7, -9, 14, 18, 1.5)
524
+ .fill({ color: 0xf2ecdd })
525
+ .roundRect(-7, -9, 14, 18, 1.5)
526
+ .stroke({ color: 0x131a24, width: 1, alpha: 0.6 });
527
+ for (let i = 0; i < 4; i++) {
528
+ papel
529
+ .moveTo(-4, -5 + i * 3.6)
530
+ .lineTo(4, -5 + i * 3.6)
531
+ .stroke({ color: 0x131a24, width: 1, alpha: 0.4 });
532
+ }
533
+ papel.moveTo(-3, 6).lineTo(3, 6).stroke({ color: AMBAR, width: 1.6 });
534
+ acta.addChild(papel);
535
+ acta.position.set(this.pos.x, this.pos.y - 70);
536
+ this.capaVuelo.addChild(acta);
537
+ }
538
+
539
+ const naipes = [...this.naipes.values()];
540
+ if (this.manoChip) naipes.push(this.manoChip);
541
+ this.manoChip = null;
542
+ this.manoActor = null;
543
+ this.naipes.clear();
544
+ this.hilo = null;
545
+ let t = 0;
546
+ this.anima((dt) => {
547
+ t = Math.min(1, t + 0.012 * dt);
548
+ if (acta) {
549
+ acta.y -= 0.5 * dt;
550
+ acta.rotation = Math.sin(t * 7) * 0.12;
551
+ acta.alpha = t > 0.6 ? (1 - t) / 0.4 : 1;
552
+ }
553
+ for (const naipe of naipes) naipe.alpha = 1 - t;
554
+ if (t >= 1) {
555
+ if (acta) {
556
+ acta.parent?.removeChild(acta);
557
+ acta.destroy({ children: true });
558
+ }
559
+ for (const naipe of naipes) {
560
+ naipe.parent?.removeChild(naipe);
561
+ naipe.destroy({ children: true });
562
+ }
563
+ return false;
564
+ }
565
+ return true;
566
+ });
567
+ }
568
+
569
+ private limpia(): void {
570
+ for (const naipe of this.naipes.values()) {
571
+ naipe.parent?.removeChild(naipe);
572
+ naipe.destroy({ children: true });
573
+ }
574
+ this.naipes.clear();
575
+ this.manoChip?.parent?.removeChild(this.manoChip);
576
+ this.manoChip?.destroy({ children: true });
577
+ this.manoChip = null;
578
+ this.manoActor = null;
579
+ }
580
+ }