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.
- package/.claude-plugin/marketplace.json +16 -0
- package/LICENSE +202 -0
- package/NOTICE +5 -0
- package/README.es.md +2021 -0
- package/README.md +1999 -0
- package/benchmarks/committee/README.md +39 -0
- package/benchmarks/committee/metrics.py +51 -0
- package/benchmarks/committee/run.py +64 -0
- package/benchmarks/committee/traces/01-single.json +8 -0
- package/benchmarks/committee/traces/02-mesh.json +16 -0
- package/benchmarks/committee/traces/03-chair.json +15 -0
- package/benchmarks/latency/README.md +35 -0
- package/benchmarks/latency/fake-claude-cli.mjs +119 -0
- package/benchmarks/latency/fake-native-server.mjs +340 -0
- package/benchmarks/latency/fake-tui.py +91 -0
- package/benchmarks/latency/live.py +380 -0
- package/benchmarks/stress/README.md +20 -0
- package/benchmarks/stress/run.py +487 -0
- package/bin/agents +4 -0
- package/bin/agents-city.js +116 -0
- package/bin/benchmark +17 -0
- package/bin/bus +6 -0
- package/bin/cities +3 -0
- package/bin/city +86 -0
- package/bin/committee +6 -0
- package/bin/demo +200 -0
- package/bin/exit +5 -0
- package/bin/hall +73 -0
- package/bin/hall.html +426 -0
- package/bin/logs +3 -0
- package/bin/report.py +14 -0
- package/bin/reset +3 -0
- package/bin/road +3 -0
- package/bin/seat +10 -0
- package/bin/serve.py +1428 -0
- package/bin/setup.py +753 -0
- package/bin/skills +3 -0
- package/bin/test +131 -0
- package/bin/test-adapter.py +183 -0
- package/bin/test-admision.py +99 -0
- package/bin/test-avatar.py +58 -0
- package/bin/test-benchmark.py +40 -0
- package/bin/test-broker.py +334 -0
- package/bin/test-cage.py +206 -0
- package/bin/test-card.py +402 -0
- package/bin/test-channel.py +379 -0
- package/bin/test-cities.py +453 -0
- package/bin/test-claude-runtime.py +252 -0
- package/bin/test-committee.py +340 -0
- package/bin/test-contracts.py +544 -0
- package/bin/test-crecimiento.py +164 -0
- package/bin/test-demo.py +308 -0
- package/bin/test-doctor.py +106 -0
- package/bin/test-domains.py +151 -0
- package/bin/test-evidencia.py +50 -0
- package/bin/test-exit.py +128 -0
- package/bin/test-hall-protocol.py +93 -0
- package/bin/test-launch.py +169 -0
- package/bin/test-live-feed.py +508 -0
- package/bin/test-pairing.py +99 -0
- package/bin/test-parcels.py +283 -0
- package/bin/test-runtime-failures.py +252 -0
- package/bin/test-runtime-ui.py +488 -0
- package/bin/test-runtime.py +268 -0
- package/bin/test-rutas.py +80 -0
- package/bin/test-seat.py +1386 -0
- package/bin/test-security.py +150 -0
- package/bin/test-serve.py +1057 -0
- package/bin/test-stress.py +64 -0
- package/bin/test-widgets.py +143 -0
- package/bin/test-workspace.py +188 -0
- package/bin/testlib.py +95 -0
- package/bin/tokens.py +14 -0
- package/bus/scripts/deploy.sh +60 -0
- package/bus/scripts/mint-token.sh +68 -0
- package/bus/scripts/setup-dev.sh +79 -0
- package/bus/scripts/test-channel.ts +8 -0
- package/bus/scripts/test-hub.ts +266 -0
- package/bus/scripts/test-local.sh +40 -0
- package/bus/scripts/test-queue.ts +132 -0
- package/bus/worker/package-lock.json +1971 -0
- package/bus/worker/package.json +17 -0
- package/bus/worker/src/index.ts +485 -0
- package/bus/worker/tsconfig.json +13 -0
- package/bus/worker/wrangler.toml +32 -0
- package/city/oven/README.md +59 -0
- package/city/oven/collect.py +43 -0
- package/city/oven/oven.html +4 -0
- package/city/scripts/history.py +120 -0
- package/city/scripts/seed.py +442 -0
- package/city/web/assets/modelos/License.txt +28 -0
- package/city/web/assets/modelos/PROCEDENCIA.txt +3 -0
- package/city/web/assets/modelos/building-a.glb +0 -0
- package/city/web/assets/modelos/building-b.glb +0 -0
- package/city/web/assets/modelos/building-c.glb +0 -0
- package/city/web/assets/modelos/building-d.glb +0 -0
- package/city/web/assets/modelos/building-e.glb +0 -0
- package/city/web/assets/modelos/building-f.glb +0 -0
- package/city/web/assets/modelos/building-g.glb +0 -0
- package/city/web/assets/modelos/building-h.glb +0 -0
- package/city/web/assets/sprites/building-a.png +0 -0
- package/city/web/assets/sprites/building-b.png +0 -0
- package/city/web/assets/sprites/building-c.png +0 -0
- package/city/web/assets/sprites/building-d.png +0 -0
- package/city/web/assets/sprites/building-e.png +0 -0
- package/city/web/assets/sprites/building-f.png +0 -0
- package/city/web/assets/sprites/building-g.png +0 -0
- package/city/web/assets/sprites/building-h.png +0 -0
- package/city/web/assets/sprites/building-i.png +0 -0
- package/city/web/assets/sprites/building-j.png +0 -0
- package/city/web/assets/sprites/building-k.png +0 -0
- package/city/web/assets/sprites/building-l.png +0 -0
- package/city/web/assets/sprites/building-m.png +0 -0
- package/city/web/assets/sprites/building-n.png +0 -0
- package/city/web/assets/sprites/building-skyscraper-a.png +0 -0
- package/city/web/assets/sprites/building-skyscraper-b.png +0 -0
- package/city/web/assets/sprites/building-skyscraper-c.png +0 -0
- package/city/web/assets/sprites/building-skyscraper-d.png +0 -0
- package/city/web/assets/sprites/building-skyscraper-e.png +0 -0
- package/city/web/assets/sprites/catalogo.json +47 -0
- package/city/web/assets/sprites/detail-awning-wide.png +0 -0
- package/city/web/assets/sprites/detail-awning.png +0 -0
- package/city/web/assets/sprites/detail-overhang-wide.png +0 -0
- package/city/web/assets/sprites/detail-overhang.png +0 -0
- package/city/web/assets/sprites/detail-parasol-a.png +0 -0
- package/city/web/assets/sprites/detail-parasol-b.png +0 -0
- package/city/web/assets/sprites/low-detail-building-a.png +0 -0
- package/city/web/assets/sprites/low-detail-building-b.png +0 -0
- package/city/web/assets/sprites/low-detail-building-c.png +0 -0
- package/city/web/assets/sprites/low-detail-building-d.png +0 -0
- package/city/web/assets/sprites/low-detail-building-e.png +0 -0
- package/city/web/assets/sprites/low-detail-building-f.png +0 -0
- package/city/web/assets/sprites/low-detail-building-g.png +0 -0
- package/city/web/assets/sprites/low-detail-building-h.png +0 -0
- package/city/web/assets/sprites/low-detail-building-i.png +0 -0
- package/city/web/assets/sprites/low-detail-building-j.png +0 -0
- package/city/web/assets/sprites/low-detail-building-k.png +0 -0
- package/city/web/assets/sprites/low-detail-building-l.png +0 -0
- package/city/web/assets/sprites/low-detail-building-m.png +0 -0
- package/city/web/assets/sprites/low-detail-building-n.png +0 -0
- package/city/web/assets/sprites/low-detail-building-wide-a.png +0 -0
- package/city/web/assets/sprites/low-detail-building-wide-b.png +0 -0
- package/city/web/assets/sprites/medidas.json +248 -0
- package/city/web/dist/city.js +939 -0
- package/city/web/dist/index.html +294 -0
- package/city/web/dist/modelos/License.txt +28 -0
- package/city/web/dist/modelos/PROCEDENCIA.txt +3 -0
- package/city/web/dist/modelos/building-a.glb +0 -0
- package/city/web/dist/modelos/building-b.glb +0 -0
- package/city/web/dist/modelos/building-c.glb +0 -0
- package/city/web/dist/modelos/building-d.glb +0 -0
- package/city/web/dist/modelos/building-e.glb +0 -0
- package/city/web/dist/modelos/building-f.glb +0 -0
- package/city/web/dist/modelos/building-g.glb +0 -0
- package/city/web/dist/modelos/building-h.glb +0 -0
- package/city/web/dist/sprites/building-a.png +0 -0
- package/city/web/dist/sprites/building-b.png +0 -0
- package/city/web/dist/sprites/building-c.png +0 -0
- package/city/web/dist/sprites/building-d.png +0 -0
- package/city/web/dist/sprites/building-e.png +0 -0
- package/city/web/dist/sprites/building-f.png +0 -0
- package/city/web/dist/sprites/building-g.png +0 -0
- package/city/web/dist/sprites/building-h.png +0 -0
- package/city/web/dist/sprites/building-i.png +0 -0
- package/city/web/dist/sprites/building-j.png +0 -0
- package/city/web/dist/sprites/building-k.png +0 -0
- package/city/web/dist/sprites/building-l.png +0 -0
- package/city/web/dist/sprites/building-m.png +0 -0
- package/city/web/dist/sprites/building-n.png +0 -0
- package/city/web/dist/sprites/building-skyscraper-a.png +0 -0
- package/city/web/dist/sprites/building-skyscraper-b.png +0 -0
- package/city/web/dist/sprites/building-skyscraper-c.png +0 -0
- package/city/web/dist/sprites/building-skyscraper-d.png +0 -0
- package/city/web/dist/sprites/building-skyscraper-e.png +0 -0
- package/city/web/dist/sprites/catalogo.json +47 -0
- package/city/web/dist/sprites/detail-awning-wide.png +0 -0
- package/city/web/dist/sprites/detail-awning.png +0 -0
- package/city/web/dist/sprites/detail-overhang-wide.png +0 -0
- package/city/web/dist/sprites/detail-overhang.png +0 -0
- package/city/web/dist/sprites/detail-parasol-a.png +0 -0
- package/city/web/dist/sprites/detail-parasol-b.png +0 -0
- package/city/web/dist/sprites/low-detail-building-a.png +0 -0
- package/city/web/dist/sprites/low-detail-building-b.png +0 -0
- package/city/web/dist/sprites/low-detail-building-c.png +0 -0
- package/city/web/dist/sprites/low-detail-building-d.png +0 -0
- package/city/web/dist/sprites/low-detail-building-e.png +0 -0
- package/city/web/dist/sprites/low-detail-building-f.png +0 -0
- package/city/web/dist/sprites/low-detail-building-g.png +0 -0
- package/city/web/dist/sprites/low-detail-building-h.png +0 -0
- package/city/web/dist/sprites/low-detail-building-i.png +0 -0
- package/city/web/dist/sprites/low-detail-building-j.png +0 -0
- package/city/web/dist/sprites/low-detail-building-k.png +0 -0
- package/city/web/dist/sprites/low-detail-building-l.png +0 -0
- package/city/web/dist/sprites/low-detail-building-m.png +0 -0
- package/city/web/dist/sprites/low-detail-building-n.png +0 -0
- package/city/web/dist/sprites/low-detail-building-wide-a.png +0 -0
- package/city/web/dist/sprites/low-detail-building-wide-b.png +0 -0
- package/city/web/dist/sprites/medidas.json +248 -0
- package/city/web/dist-hall/hall.js +264 -0
- package/city/web/index.html +294 -0
- package/city/web/package.json +18 -0
- package/city/web/sella.py +21 -0
- package/city/web/src/activity-actors.ts +47 -0
- package/city/web/src/activity.ts +172 -0
- package/city/web/src/ayuntamiento.ts +580 -0
- package/city/web/src/draw.ts +640 -0
- package/city/web/src/game-speech.ts +79 -0
- package/city/web/src/hall.ts +1867 -0
- package/city/web/src/main.ts +2225 -0
- package/city/web/src/oven.ts +124 -0
- package/city/web/src/people.ts +244 -0
- package/city/web/src/presencia.ts +193 -0
- package/city/web/src/puertas.ts +180 -0
- package/city/web/src/repo-roles.ts +59 -0
- package/city/worker/package.json +16 -0
- package/city/worker/schema.sql +117 -0
- package/city/worker/src/index.ts +702 -0
- package/city/worker/src/square.ts +218 -0
- package/city/worker/wrangler.toml +58 -0
- package/demo/ada.md +50 -0
- package/demo/bruno.md +62 -0
- package/demo/camila.md +56 -0
- package/demo/city/ada.md +40 -0
- package/demo/city/city.yml +8 -0
- package/demo/city/parcels.yml +15 -0
- package/demo/city/roads.json +4 -0
- package/demo/city/units.yml +10 -0
- package/demo/clinica/city.yml +8 -0
- package/demo/clinica/parcels.yml +15 -0
- package/demo/clinica/roads.json +4 -0
- package/demo/clinica/units.yml +9 -0
- package/demo/clinica/vera.md +46 -0
- package/demo/dante.md +54 -0
- package/demo/despacho/city.yml +8 -0
- package/demo/despacho/marta.md +46 -0
- package/demo/despacho/parcels.yml +15 -0
- package/demo/despacho/roads.json +4 -0
- package/demo/despacho/units.yml +9 -0
- package/demo/elsa.md +50 -0
- package/demo/farid.md +52 -0
- package/demo/greta.md +50 -0
- package/demo/hugo.md +49 -0
- package/demo/iris.md +49 -0
- package/demo/jonas.md +51 -0
- package/demo/kira.md +51 -0
- package/demo/luca.md +49 -0
- package/demo/parcels.yml +92 -0
- package/demo/seed.py +20 -0
- package/demo/show.py +161 -0
- package/demo/stories.py +824 -0
- package/demo/units.yml +33 -0
- package/docs/agents-first.md +99 -0
- package/docs/glossary.md +55 -0
- package/docs/map-live-layers.md +73 -0
- package/docs/security.md +231 -0
- package/docs/self-host.md +151 -0
- package/docs/testing.md +205 -0
- package/package.json +73 -0
- package/plugin/.claude-plugin/plugin.json +62 -0
- package/plugin/.mcp.json +15 -0
- package/plugin/channel/activity-cli.ts +16 -0
- package/plugin/channel/adapter-prompts.ts +113 -0
- package/plugin/channel/adapter.js +4292 -0
- package/plugin/channel/adapter.ts +133 -0
- package/plugin/channel/bus.js +20188 -0
- package/plugin/channel/bus.ts +131 -0
- package/plugin/channel/city-config.ts +125 -0
- package/plugin/channel/claude-channel.ts +58 -0
- package/plugin/channel/cli-args.ts +75 -0
- package/plugin/channel/client.js +4200 -0
- package/plugin/channel/client.ts +38 -0
- package/plugin/channel/committee/activity.ts +256 -0
- package/plugin/channel/committee/collection.ts +154 -0
- package/plugin/channel/committee/decision.ts +175 -0
- package/plugin/channel/committee/floor.ts +135 -0
- package/plugin/channel/committee/guards.ts +30 -0
- package/plugin/channel/committee/history.ts +38 -0
- package/plugin/channel/committee/render.ts +102 -0
- package/plugin/channel/committee/service.ts +72 -0
- package/plugin/channel/committee/storage.ts +92 -0
- package/plugin/channel/committee/types.ts +163 -0
- package/plugin/channel/committee/view.ts +63 -0
- package/plugin/channel/committee-cli.ts +109 -0
- package/plugin/channel/delivery-metrics.ts +61 -0
- package/plugin/channel/delivery-queue.ts +118 -0
- package/plugin/channel/hub/activity-controller.ts +72 -0
- package/plugin/channel/hub/activity-feed.ts +169 -0
- package/plugin/channel/hub/committee-controller.ts +60 -0
- package/plugin/channel/hub/connections.ts +48 -0
- package/plugin/channel/hub/diagnostics.ts +67 -0
- package/plugin/channel/hub/envelope-validity.ts +109 -0
- package/plugin/channel/hub/envelopes.ts +66 -0
- package/plugin/channel/hub/lifecycle.ts +91 -0
- package/plugin/channel/hub/local-roads.ts +89 -0
- package/plugin/channel/hub/remote-roads.ts +165 -0
- package/plugin/channel/hub/road-controller.ts +99 -0
- package/plugin/channel/hub-client.ts +149 -0
- package/plugin/channel/local-hub.js +5995 -0
- package/plugin/channel/local-hub.ts +349 -0
- package/plugin/channel/map-reporter.ts +48 -0
- package/plugin/channel/package-lock.json +1731 -0
- package/plugin/channel/package.json +19 -0
- package/plugin/channel/protocol.ts +98 -0
- package/plugin/channel/road-cli.ts +18 -0
- package/plugin/channel/run.sh +15 -0
- package/plugin/channel/runtime/claude.ts +458 -0
- package/plugin/channel/runtime/codex-config.ts +77 -0
- package/plugin/channel/runtime/codex.ts +669 -0
- package/plugin/channel/runtime/command.ts +62 -0
- package/plugin/channel/runtime/factory.ts +15 -0
- package/plugin/channel/runtime/json-rpc.ts +154 -0
- package/plugin/channel/runtime/kimi.ts +277 -0
- package/plugin/channel/runtime/opencode.ts +248 -0
- package/plugin/channel/runtime/process.ts +84 -0
- package/plugin/channel/runtime/types.ts +45 -0
- package/plugin/channel/runtime-files.ts +73 -0
- package/plugin/channel/runtime-gateway.js +6396 -0
- package/plugin/channel/runtime-gateway.ts +358 -0
- package/plugin/channel/runtime-metrics.ts +52 -0
- package/plugin/channel/runtime-subscription.ts +180 -0
- package/plugin/channel/terminal-delivery.ts +131 -0
- package/plugin/channel/untrusted.ts +60 -0
- package/plugin/commands/committee.md +31 -0
- package/plugin/commands/exit.md +32 -0
- package/plugin/commands/goals.md +20 -0
- package/plugin/commands/join.md +18 -0
- package/plugin/commands/notice.md +20 -0
- package/plugin/commands/propose.md +19 -0
- package/plugin/commands/round.md +17 -0
- package/plugin/commands/session.md +22 -0
- package/plugin/commands/settings.md +25 -0
- package/plugin/commands/setup.md +21 -0
- package/plugin/commands/team.md +23 -0
- package/plugin/domains/custom.md +27 -0
- package/plugin/domains/finance.md +31 -0
- package/plugin/domains/healthcare.md +34 -0
- package/plugin/domains/legal.md +31 -0
- package/plugin/domains/marketing.md +33 -0
- package/plugin/domains/operations.md +31 -0
- package/plugin/domains/research.md +32 -0
- package/plugin/domains/sales.md +30 -0
- package/plugin/domains/software.md +37 -0
- package/plugin/hooks/activity.sh +6 -0
- package/plugin/hooks/digging.sh +68 -0
- package/plugin/hooks/growth.sh +42 -0
- package/plugin/hooks/hooks.json +106 -0
- package/plugin/hooks/notice-on-pr.sh +35 -0
- package/plugin/hooks/notice-on-stop.sh +80 -0
- package/plugin/hooks/notice-pending.sh +58 -0
- package/plugin/hooks/solo-en-ciudad.sh +23 -0
- package/plugin/hooks/tokens.sh +47 -0
- package/plugin/roles/examples/account-executive.md +25 -0
- package/plugin/roles/examples/ai-manager.md +34 -0
- package/plugin/roles/examples/associate.md +24 -0
- package/plugin/roles/examples/brand-lead.md +20 -0
- package/plugin/roles/examples/cfo.md +20 -0
- package/plugin/roles/examples/city-lead.md +25 -0
- package/plugin/roles/examples/clinical-director.md +26 -0
- package/plugin/roles/examples/clinical-ops.md +25 -0
- package/plugin/roles/examples/clinician.md +25 -0
- package/plugin/roles/examples/compliance.md +27 -0
- package/plugin/roles/examples/content.md +24 -0
- package/plugin/roles/examples/controller.md +27 -0
- package/plugin/roles/examples/cpto.md +41 -0
- package/plugin/roles/examples/customer-success.md +25 -0
- package/plugin/roles/examples/data-engineer.md +37 -0
- package/plugin/roles/examples/data.md +60 -0
- package/plugin/roles/examples/dev.md +37 -0
- package/plugin/roles/examples/devops.md +39 -0
- package/plugin/roles/examples/enablement.md +24 -0
- package/plugin/roles/examples/ethics.md +24 -0
- package/plugin/roles/examples/fin-analytics.md +27 -0
- package/plugin/roles/examples/health-compliance.md +25 -0
- package/plugin/roles/examples/health-data.md +26 -0
- package/plugin/roles/examples/knowledge.md +25 -0
- package/plugin/roles/examples/lifecycle.md +26 -0
- package/plugin/roles/examples/llm-engineer.md +34 -0
- package/plugin/roles/examples/managing-partner.md +20 -0
- package/plugin/roles/examples/methods.md +23 -0
- package/plugin/roles/examples/operations-lead.md +23 -0
- package/plugin/roles/examples/ops.md +24 -0
- package/plugin/roles/examples/patient-safety.md +26 -0
- package/plugin/roles/examples/performance.md +27 -0
- package/plugin/roles/examples/po.md +36 -0
- package/plugin/roles/examples/process-owner.md +23 -0
- package/plugin/roles/examples/product-design.md +39 -0
- package/plugin/roles/examples/program-manager.md +24 -0
- package/plugin/roles/examples/quality.md +25 -0
- package/plugin/roles/examples/research-director.md +24 -0
- package/plugin/roles/examples/research-ops.md +25 -0
- package/plugin/roles/examples/researcher.md +25 -0
- package/plugin/roles/examples/revenue-lead.md +25 -0
- package/plugin/roles/examples/revops.md +26 -0
- package/plugin/roles/examples/seo.md +27 -0
- package/plugin/roles/examples/specialist.md +24 -0
- package/plugin/scripts/admision.py +165 -0
- package/plugin/scripts/apaga.py +232 -0
- package/plugin/scripts/avatar.py +211 -0
- package/plugin/scripts/broker.py +531 -0
- package/plugin/scripts/cage.py +252 -0
- package/plugin/scripts/capabilities.py +197 -0
- package/plugin/scripts/card.py +312 -0
- package/plugin/scripts/cities.py +520 -0
- package/plugin/scripts/city-env.sh +90 -0
- package/plugin/scripts/city-runtime.sh +87 -0
- package/plugin/scripts/city-session.sh +526 -0
- package/plugin/scripts/city_env.py +67 -0
- package/plugin/scripts/crecimiento.py +140 -0
- package/plugin/scripts/deliberations.py +48 -0
- package/plugin/scripts/doctor.py +172 -0
- package/plugin/scripts/domains.py +201 -0
- package/plugin/scripts/evidencia.py +64 -0
- package/plugin/scripts/find-repos.sh +111 -0
- package/plugin/scripts/gh.py +159 -0
- package/plugin/scripts/hall_protocol.py +147 -0
- package/plugin/scripts/hook_activity.py +139 -0
- package/plugin/scripts/launch.py +94 -0
- package/plugin/scripts/logs.py +98 -0
- package/plugin/scripts/pairing.py +185 -0
- package/plugin/scripts/parcels.py +132 -0
- package/plugin/scripts/read-card.py +51 -0
- package/plugin/scripts/report.py +201 -0
- package/plugin/scripts/reset.py +180 -0
- package/plugin/scripts/roads.py +230 -0
- package/plugin/scripts/roles.py +274 -0
- package/plugin/scripts/runtime_log.py +69 -0
- package/plugin/scripts/runtime_processes.py +132 -0
- package/plugin/scripts/rutas.py +83 -0
- package/plugin/scripts/seat.py +1349 -0
- package/plugin/scripts/tokens.py +194 -0
- package/plugin/scripts/trust-repos.py +59 -0
- package/plugin/scripts/ui.py +280 -0
- package/plugin/scripts/units.py +80 -0
- package/plugin/scripts/workspace.py +364 -0
- package/plugin/skills/city/SKILL.md +191 -0
- package/templates/blank.md +39 -0
- package/templates/finance.md +44 -0
- package/templates/legal.md +45 -0
- package/templates/marketing.md +48 -0
- package/templates/product.md +45 -0
package/bin/skills
ADDED
package/bin/test
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Every test in the repo, in one command.
|
|
3
|
+
#
|
|
4
|
+
# ./bin/test all of it
|
|
5
|
+
# ./bin/test card seat just these
|
|
6
|
+
#
|
|
7
|
+
# Twelve suites and two compatibility walks. Nothing here needs a network, an
|
|
8
|
+
# account, a terminal or a deployed anything: the widgets run against a fake curses
|
|
9
|
+
# window, the reporter against a temporary git repo, and the wizard against a file
|
|
10
|
+
# of answers. A suite that needs a browser open is a suite nobody runs.
|
|
11
|
+
set -uo pipefail
|
|
12
|
+
cd "$(dirname "$0")/.." || exit 1
|
|
13
|
+
|
|
14
|
+
# Arm the commit-time gate while we are here. Git refuses to ship hooks inside a
|
|
15
|
+
# repo (on purpose), so the first test run in a clone points this clone at ours.
|
|
16
|
+
# Installed copies are not git repos and skip this silently.
|
|
17
|
+
if [ -d .git ] && [ -x .githooks/pre-commit ] \
|
|
18
|
+
&& [ -z "$(git config core.hooksPath 2>/dev/null)" ]; then
|
|
19
|
+
git config core.hooksPath .githooks \
|
|
20
|
+
&& printf ' pre-commit gate armed (core.hooksPath .githooks)\n'
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
SUITES=(widgets card parcels domains serve seat cities channel committee live-feed runtime claude-runtime runtime-ui runtime-failures stress adapter benchmark demo contracts exit rutas cage broker evidencia admision pairing hall-protocol doctor security workspace avatar crecimiento launch)
|
|
24
|
+
[ $# -gt 0 ] && SUITES=("$@")
|
|
25
|
+
|
|
26
|
+
fallos=0
|
|
27
|
+
total=0
|
|
28
|
+
|
|
29
|
+
printf '\n'
|
|
30
|
+
for s in "${SUITES[@]}"; do
|
|
31
|
+
guion="bin/test-$s.py"
|
|
32
|
+
if [ ! -x "$guion" ]; then
|
|
33
|
+
printf ' %-10s \033[31mno such suite\033[0m (%s)\n' "$s" "$guion"
|
|
34
|
+
fallos=$((fallos + 1)); continue
|
|
35
|
+
fi
|
|
36
|
+
salida="$(./"$guion" 2>&1)"
|
|
37
|
+
if [ $? -eq 0 ]; then
|
|
38
|
+
n="$(printf '%s' "$salida" | grep -oE '[0-9]+ checks' | tail -1)"
|
|
39
|
+
printf ' %-10s \033[32mok\033[0m %s\n' "$s" "${n:-}"
|
|
40
|
+
total=$((total + $(printf '%s' "${n:-0}" | grep -oE '^[0-9]+' || echo 0)))
|
|
41
|
+
else
|
|
42
|
+
printf ' %-10s \033[31mFAILED\033[0m\n' "$s"
|
|
43
|
+
printf '%s\n' "$salida" | sed 's/^/ /'
|
|
44
|
+
fallos=$((fallos + 1))
|
|
45
|
+
fi
|
|
46
|
+
done
|
|
47
|
+
|
|
48
|
+
if [ $# -eq 0 ]; then
|
|
49
|
+
if ./bin/setup.py --help 2>&1 | grep -q 'one personal Agents City' \
|
|
50
|
+
&& ! ./bin/setup.py --help 2>&1 | grep -q 'legacy-team'; then
|
|
51
|
+
printf ' %-10s \033[32mok\033[0m public setup exposes only personal cities\n' "setup"
|
|
52
|
+
else
|
|
53
|
+
printf ' %-10s \033[31mFAILED\033[0m\n' "setup"
|
|
54
|
+
fallos=$((fallos + 1))
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# The map fixture remains a compatibility input for the visual seeder.
|
|
58
|
+
if [ "$(./city/scripts/seed.py --data demo --fake-history 2>/dev/null \
|
|
59
|
+
| grep -c 'INSERT INTO parcela')" = 34 ]; then
|
|
60
|
+
printf ' %-10s \033[32mok\033[0m the demo still seeds 34 parcels\n' "seeder"
|
|
61
|
+
else
|
|
62
|
+
printf ' %-10s \033[31mFAILED\033[0m\n' "seeder"
|
|
63
|
+
fallos=$((fallos + 1))
|
|
64
|
+
fi
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# The npm package: every path the shim dispatches to must actually ship. This is
|
|
68
|
+
# the check that catches "added a module, forgot the files whitelist" — the shim
|
|
69
|
+
# would break only on the installed machine, where nobody is looking.
|
|
70
|
+
if [ $# -eq 0 ] && command -v npm >/dev/null 2>&1; then
|
|
71
|
+
faltan=$(npm pack --dry-run --json 2>/dev/null | python3 -c '
|
|
72
|
+
import json, sys
|
|
73
|
+
salida = json.load(sys.stdin)
|
|
74
|
+
dentro = {f["path"] for f in salida[0]["files"]}
|
|
75
|
+
necesarios = ["bin/agents-city.js", "bin/hall", "bin/seat", "bin/city", "bin/demo",
|
|
76
|
+
"bin/setup.py", "bin/serve.py", "bin/hall.html",
|
|
77
|
+
"bin/ui.py" if "bin/ui.py" in dentro else "plugin/scripts/ui.py",
|
|
78
|
+
"plugin/scripts/seat.py", "plugin/scripts/card.py",
|
|
79
|
+
"plugin/scripts/parcels.py", "plugin/scripts/units.py",
|
|
80
|
+
"plugin/scripts/roles.py", "plugin/scripts/gh.py",
|
|
81
|
+
"plugin/scripts/domains.py", "plugin/domains/software.md",
|
|
82
|
+
"plugin/roles/examples/data-engineer.md",
|
|
83
|
+
"plugin/scripts/cities.py", "plugin/scripts/roads.py",
|
|
84
|
+
"plugin/scripts/cage.py", "plugin/scripts/broker.py",
|
|
85
|
+
"plugin/scripts/rutas.py", "plugin/scripts/evidencia.py",
|
|
86
|
+
"plugin/scripts/admision.py", "plugin/scripts/pairing.py",
|
|
87
|
+
"plugin/scripts/hall_protocol.py", "plugin/scripts/doctor.py",
|
|
88
|
+
"plugin/scripts/workspace.py", "plugin/scripts/avatar.py",
|
|
89
|
+
"plugin/scripts/crecimiento.py",
|
|
90
|
+
"plugin/scripts/reset.py", "plugin/scripts/capabilities.py",
|
|
91
|
+
"plugin/scripts/deliberations.py",
|
|
92
|
+
"plugin/scripts/runtime_processes.py",
|
|
93
|
+
"plugin/scripts/runtime_log.py", "plugin/scripts/logs.py",
|
|
94
|
+
"plugin/scripts/launch.py", "plugin/scripts/hook_activity.py",
|
|
95
|
+
"plugin/scripts/city-session.sh", "plugin/scripts/city-runtime.sh",
|
|
96
|
+
"plugin/scripts/find-repos.sh",
|
|
97
|
+
"plugin/channel/bus.js", "plugin/channel/local-hub.js",
|
|
98
|
+
"plugin/channel/client.js", "plugin/channel/adapter.js",
|
|
99
|
+
"plugin/channel/runtime-gateway.js",
|
|
100
|
+
"plugin/.mcp.json",
|
|
101
|
+
"bin/cities", "bin/road", "bin/bus", "bin/committee", "bin/agents",
|
|
102
|
+
"bin/reset", "bin/skills", "bin/benchmark", "bin/logs",
|
|
103
|
+
"benchmarks/committee/run.py",
|
|
104
|
+
"benchmarks/latency/live.py", "benchmarks/latency/README.md",
|
|
105
|
+
"benchmarks/latency/fake-native-server.mjs",
|
|
106
|
+
"benchmarks/latency/fake-claude-cli.mjs",
|
|
107
|
+
"benchmarks/stress/run.py", "benchmarks/stress/README.md",
|
|
108
|
+
"city/web/src/repo-roles.ts",
|
|
109
|
+
"demo/show.py", "demo/city/city.yml", "demo/city/ada.md",
|
|
110
|
+
"city/scripts/seed.py", "city/worker/schema.sql", "demo/parcels.yml"]
|
|
111
|
+
problemas = [f"missing: {n}" for n in necesarios if n not in dentro]
|
|
112
|
+
basura = ("/node_modules/", "/__pycache__/", "/.wrangler/")
|
|
113
|
+
for ruta in sorted(dentro):
|
|
114
|
+
if any(x in f"/{ruta}/" for x in basura) or ruta.endswith((".pyc", "/.DS_Store")):
|
|
115
|
+
problemas.append(f"development artifact shipped: {ruta}")
|
|
116
|
+
print("\n".join(problemas))')
|
|
117
|
+
if [ -z "$faltan" ]; then
|
|
118
|
+
printf ' %-10s \033[32mok\033[0m every path the shim needs ships in the tarball\n' "package"
|
|
119
|
+
else
|
|
120
|
+
printf ' %-10s \033[31mFAILED\033[0m — invalid npm package contents:\n' "package"
|
|
121
|
+
printf '%s\n' "$faltan" | sed 's/^/ /'
|
|
122
|
+
fallos=$((fallos + 1))
|
|
123
|
+
fi
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
printf '\n'
|
|
127
|
+
if [ "$fallos" -gt 0 ]; then
|
|
128
|
+
printf ' \033[31m%d suite(s) failed\033[0m\n\n' "$fallos"
|
|
129
|
+
exit 1
|
|
130
|
+
fi
|
|
131
|
+
printf ' \033[32mall green\033[0m — %d checks\n\n' "$total"
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Explicit unknown-CLI fallback remains safe, slow and visibly separate."""
|
|
3
|
+
import json
|
|
4
|
+
import datetime
|
|
5
|
+
import os
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
import tempfile
|
|
10
|
+
import time
|
|
11
|
+
|
|
12
|
+
AQUI = os.path.dirname(os.path.abspath(__file__))
|
|
13
|
+
RAIZ = os.path.dirname(AQUI)
|
|
14
|
+
sys.path.insert(0, AQUI)
|
|
15
|
+
from testlib import ( # noqa: E402
|
|
16
|
+
afirma, comprueba, detiene_hubs_de_ciudad, hubs_de_ciudad, resumen,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
ADAPTER = os.path.join(RAIZ, 'plugin', 'channel', 'adapter.js')
|
|
20
|
+
CLIENT = os.path.join(RAIZ, 'plugin', 'channel', 'client.js')
|
|
21
|
+
FAKE_TUI = os.path.join(RAIZ, 'benchmarks', 'latency', 'fake-tui.py')
|
|
22
|
+
RUNTIMES = {
|
|
23
|
+
'legacy-one': 'custom-tui',
|
|
24
|
+
'legacy-two': 'custom-tui',
|
|
25
|
+
'legacy-three': 'custom-tui',
|
|
26
|
+
'legacy-four': 'custom-tui',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def espera(condicion, segundos=8):
|
|
31
|
+
limite = time.monotonic() + segundos
|
|
32
|
+
while time.monotonic() < limite:
|
|
33
|
+
if condicion():
|
|
34
|
+
return True
|
|
35
|
+
time.sleep(.1)
|
|
36
|
+
return False
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def main():
|
|
40
|
+
print('\n explicit terminal fallback for unknown CLIs')
|
|
41
|
+
if not shutil.which('tmux'):
|
|
42
|
+
afirma('· tmux is installed', False, 'tmux missing')
|
|
43
|
+
return resumen('adapter')
|
|
44
|
+
base = tempfile.mkdtemp(prefix='agents-city-adapter-')
|
|
45
|
+
app = os.path.join(base, 'app')
|
|
46
|
+
city = os.path.join(app, 'alice', 'home')
|
|
47
|
+
os.makedirs(city)
|
|
48
|
+
repos = list(RUNTIMES)
|
|
49
|
+
open(os.path.join(city, 'city.yml'), 'w', encoding='utf-8').write(
|
|
50
|
+
'id: city_adapter\nname: Home\nslug: home\nowner: alice\n')
|
|
51
|
+
open(os.path.join(city, 'roads.json'), 'w', encoding='utf-8').write(
|
|
52
|
+
'{"version": 1, "roads": []}\n')
|
|
53
|
+
runs = ''.join(f'runs.{actor}: {runtime}\n'
|
|
54
|
+
for actor, runtime in RUNTIMES.items())
|
|
55
|
+
roles = ''.join(f'role.{actor}: {"seo" if actor == "legacy-two" else "blank"}\n'
|
|
56
|
+
for actor in RUNTIMES)
|
|
57
|
+
open(os.path.join(city, 'alice.md'), 'w', encoding='utf-8').write(
|
|
58
|
+
f'---\nuser: alice\nagent: alice/ceo\nrepos: [{", ".join(repos)}]\n'
|
|
59
|
+
f'{roles}{runs}---\n')
|
|
60
|
+
env = dict(os.environ, AGENTS_CITY_HOME=app, AGENTS_CITY_DATA=city,
|
|
61
|
+
AGENTS_CITY_USER='alice')
|
|
62
|
+
for key in ('CITY_BUS_URL', 'CITY_BUS_TOKEN', 'CITY_DIR'):
|
|
63
|
+
env.pop(key, None)
|
|
64
|
+
session = f'ac-adapter-{os.getpid()}'
|
|
65
|
+
adapters = []
|
|
66
|
+
endpoint = os.path.join(app, '.runtime', 'bus', 'city-adapter', 'endpoint.json')
|
|
67
|
+
hub_pid = 0
|
|
68
|
+
try:
|
|
69
|
+
for actor in repos:
|
|
70
|
+
capture = os.path.join(base, f'{actor}.txt')
|
|
71
|
+
subprocess.run(
|
|
72
|
+
['tmux', 'new-session' if actor == repos[0] else 'new-window',
|
|
73
|
+
'-d' if actor == repos[0] else '-d',
|
|
74
|
+
'-s' if actor == repos[0] else '-t',
|
|
75
|
+
session if actor == repos[0] else session,
|
|
76
|
+
'-n', actor, sys.executable, FAKE_TUI, capture,
|
|
77
|
+
'--settle-ms', '120'],
|
|
78
|
+
check=True, capture_output=True, text=True)
|
|
79
|
+
for actor, runtime in RUNTIMES.items():
|
|
80
|
+
process = subprocess.Popen(
|
|
81
|
+
['node', ADAPTER, '--data', city, '--actor', actor,
|
|
82
|
+
'--target', f'{session}:{actor}', '--runtime', runtime],
|
|
83
|
+
env=dict(env, CITY_BUS_ACTOR=actor, CITY_AGENT_RUNTIME=runtime,
|
|
84
|
+
CITY_TERMINAL_READY_DELAY_MS='40',
|
|
85
|
+
CITY_TERMINAL_SUBMIT_DELAY_MS='180'),
|
|
86
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, text=True)
|
|
87
|
+
adapters.append(process)
|
|
88
|
+
afirma('· all four explicit fallback adapters authenticate',
|
|
89
|
+
espera(lambda: all(p.poll() is None for p in adapters), .5))
|
|
90
|
+
|
|
91
|
+
payload = {
|
|
92
|
+
'question': 'Which runtime received this?',
|
|
93
|
+
'desiredOutcome': 'The same authenticated brief everywhere',
|
|
94
|
+
'definitionOfDone': ['every capture has the protocol prompt'],
|
|
95
|
+
'participants': repos,
|
|
96
|
+
}
|
|
97
|
+
opened = subprocess.run(
|
|
98
|
+
['node', CLIENT, 'committee', 'open', '--input', '-'],
|
|
99
|
+
input=json.dumps(payload), capture_output=True, text=True,
|
|
100
|
+
env=dict(env, CITY_BUS_ACTOR='seat'), timeout=12)
|
|
101
|
+
state = json.loads(opened.stdout) if opened.returncode == 0 else {}
|
|
102
|
+
thread = state.get('id', '')
|
|
103
|
+
afirma('· the chair emits one typed assignment per selected runtime',
|
|
104
|
+
bool(thread), opened.stderr.strip())
|
|
105
|
+
|
|
106
|
+
for actor, runtime in RUNTIMES.items():
|
|
107
|
+
capture = os.path.join(base, f'{actor}.txt')
|
|
108
|
+
delivered = espera(
|
|
109
|
+
lambda p=capture: os.path.exists(p) and bool(
|
|
110
|
+
open(p, encoding='utf-8', errors='ignore').read().strip()))
|
|
111
|
+
records = [json.loads(line) for line in open(
|
|
112
|
+
capture, encoding='utf-8', errors='ignore') if line.strip()
|
|
113
|
+
] if os.path.exists(capture) else []
|
|
114
|
+
body = records[-1]['body'] if records else ''
|
|
115
|
+
afirma(f'· {runtime} receives the same chaired assignment',
|
|
116
|
+
delivered and thread in body
|
|
117
|
+
and 'agents-city committee respond' in body
|
|
118
|
+
and 'Do not contact another repo agent directly' in body,
|
|
119
|
+
body[-160:])
|
|
120
|
+
esperado = (
|
|
121
|
+
'operating role is seo'
|
|
122
|
+
if actor == 'legacy-two'
|
|
123
|
+
else 'operating role is blank'
|
|
124
|
+
)
|
|
125
|
+
afirma(
|
|
126
|
+
f'· {runtime} receives its own operating role',
|
|
127
|
+
esperado in body,
|
|
128
|
+
body[-300:],
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
outbox = os.path.join(app, '.runtime', 'bus', 'city-adapter', 'outbox')
|
|
132
|
+
drained = espera(
|
|
133
|
+
lambda: all(not os.path.isdir(os.path.join(outbox, actor))
|
|
134
|
+
or not os.listdir(os.path.join(outbox, actor))
|
|
135
|
+
for actor in repos))
|
|
136
|
+
afirma('· delivery acknowledgements drain every durable actor outbox', drained)
|
|
137
|
+
metrics_path = os.path.join(
|
|
138
|
+
app, '.runtime', 'bus', 'city-adapter', 'delivery-latency.jsonl')
|
|
139
|
+
metrics = [json.loads(line) for line in open(metrics_path, encoding='utf-8')
|
|
140
|
+
if line.strip()] if os.path.exists(metrics_path) else []
|
|
141
|
+
actors = {row.get('actor') for row in metrics}
|
|
142
|
+
afirma('· every accepted prompt has an auditable latency trace',
|
|
143
|
+
actors == set(repos), str(actors))
|
|
144
|
+
afirma('· Enter is delayed until each multiline paste has settled',
|
|
145
|
+
metrics and all(row.get('pasteToSubmitMs', 0) >= 150
|
|
146
|
+
for row in metrics), str(metrics))
|
|
147
|
+
submitted = [row['submittedAt'] for row in metrics]
|
|
148
|
+
instants = [datetime.datetime.fromisoformat(
|
|
149
|
+
value.replace('Z', '+00:00')).timestamp() for value in submitted]
|
|
150
|
+
spread = max(instants) - min(instants) if instants else 99
|
|
151
|
+
afirma('· four terminal fallbacks submit in parallel, not one after another',
|
|
152
|
+
spread < 1.0, f'{spread:.3f}s')
|
|
153
|
+
data = json.load(open(endpoint, encoding='utf-8'))
|
|
154
|
+
hub_pid = int(data.get('pid', 0))
|
|
155
|
+
comprueba('· all runtimes used one city WebSocket endpoint',
|
|
156
|
+
data.get('protocol'), 'agents-city-bus/2')
|
|
157
|
+
comprueba('· concurrent adapter startup creates exactly one city hub',
|
|
158
|
+
len(hubs_de_ciudad(city)), 1)
|
|
159
|
+
finally:
|
|
160
|
+
for process in adapters:
|
|
161
|
+
if process.poll() is None:
|
|
162
|
+
process.terminate()
|
|
163
|
+
for process in adapters:
|
|
164
|
+
try:
|
|
165
|
+
process.wait(timeout=2)
|
|
166
|
+
except subprocess.TimeoutExpired:
|
|
167
|
+
process.kill()
|
|
168
|
+
subprocess.run(['tmux', 'kill-session', '-t', session],
|
|
169
|
+
capture_output=True)
|
|
170
|
+
if os.path.exists(endpoint):
|
|
171
|
+
try:
|
|
172
|
+
data = json.load(open(endpoint, encoding='utf-8'))
|
|
173
|
+
hub_pid = int(data['pid'])
|
|
174
|
+
except (OSError, ValueError, KeyError, json.JSONDecodeError):
|
|
175
|
+
pass
|
|
176
|
+
limpio = detiene_hubs_de_ciudad(city)
|
|
177
|
+
afirma('· fallback cleanup leaves no orphan city hub', limpio, str(hub_pid))
|
|
178
|
+
shutil.rmtree(base, ignore_errors=True)
|
|
179
|
+
return resumen('adapter')
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
if __name__ == '__main__':
|
|
183
|
+
sys.exit(main())
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Admission as an ordered gate graph: the decisive gate, the reason, redaction.
|
|
3
|
+
|
|
4
|
+
Pure and transport-free. The cases that matter: the first blocker is the
|
|
5
|
+
decisive gate; a road that matches but whose sender does not is an explicit
|
|
6
|
+
block, not a fallthrough; an unknown sender under pairing yields a pairing
|
|
7
|
+
requirement rather than a silent drop; and diagnostics never carry a raw
|
|
8
|
+
allowlist entry.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
AQUI = os.path.dirname(os.path.abspath(__file__))
|
|
15
|
+
RAIZ = os.path.dirname(AQUI)
|
|
16
|
+
sys.path.insert(0, os.path.join(RAIZ, "plugin", "scripts"))
|
|
17
|
+
sys.path.insert(0, AQUI)
|
|
18
|
+
|
|
19
|
+
import admision as ad # noqa: E402
|
|
20
|
+
from testlib import afirma, comprueba, resumen # noqa: E402
|
|
21
|
+
|
|
22
|
+
FELIZ = dict(road_existe=True, road_incidente=False, direccion_coincide=True,
|
|
23
|
+
emparejado=False, allowlist=["alice/home"], payload_valido=True,
|
|
24
|
+
texto_no_vacio=True)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def dispatch():
|
|
28
|
+
d = ad.decide_entrada(**FELIZ)
|
|
29
|
+
afirma("a fully valid inbound dispatches", d.permitido())
|
|
30
|
+
comprueba("its reason is road_allowed", d.razon, "road_allowed")
|
|
31
|
+
afirma("the graph records every phase it walked", len(d.grafo) >= 3)
|
|
32
|
+
afirma("a decision exposes no evidence field to mistake for authorization",
|
|
33
|
+
not hasattr(d, "evidencia") and "evidence" not in d.como_dict())
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def bloqueos():
|
|
37
|
+
d = ad.decide_entrada(**{**FELIZ, "road_existe": False})
|
|
38
|
+
comprueba("no road -> skip", d.admision, ad.Admision.OMITE)
|
|
39
|
+
comprueba("decisive gate is the road gate", d.puerta_decisiva, "road")
|
|
40
|
+
comprueba("reason is road_missing", d.razon, "road_missing")
|
|
41
|
+
|
|
42
|
+
d = ad.decide_entrada(**{**FELIZ, "road_incidente": True})
|
|
43
|
+
comprueba("a road under incident drops", d.admision, ad.Admision.DESCARTA)
|
|
44
|
+
afirma("a dropped decision never reports itself as permitted", not d.permitido())
|
|
45
|
+
|
|
46
|
+
d = ad.decide_entrada(**{**FELIZ, "dirigido_aqui": False})
|
|
47
|
+
comprueba("a message not addressed here is skipped", d.admision, ad.Admision.OMITE)
|
|
48
|
+
comprueba("and address is the decisive gate", d.puerta_decisiva, "address")
|
|
49
|
+
|
|
50
|
+
d = ad.decide_entrada(**{**FELIZ, "payload_valido": False})
|
|
51
|
+
comprueba("a malformed payload drops", d.admision, ad.Admision.DESCARTA)
|
|
52
|
+
d = ad.decide_entrada(**{**FELIZ, "texto_no_vacio": False})
|
|
53
|
+
comprueba("empty text drops", d.razon, "payload_empty")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def emparejamiento():
|
|
57
|
+
base = {**FELIZ, "direccion_coincide": False}
|
|
58
|
+
d = ad.decide_entrada(**{**base, "emparejado": False})
|
|
59
|
+
comprueba("an unknown sender under pairing asks to pair, not drops",
|
|
60
|
+
d.admision, ad.Admision.EMPAREJA)
|
|
61
|
+
comprueba("reason is sender_not_paired", d.razon, "sender_not_paired")
|
|
62
|
+
d = ad.decide_entrada(**{**base, "emparejado": True})
|
|
63
|
+
afirma("a paired-but-not-in-config sender dispatches", d.permitido())
|
|
64
|
+
afirma("and the pairing fact is recorded as a gate in the graph",
|
|
65
|
+
any(g.razon == "sender_paired" for g in d.grafo))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def orden_y_redaccion():
|
|
69
|
+
# Both the road and the payload are bad; the earlier (road) gate must win.
|
|
70
|
+
d = ad.decide_entrada(**{**FELIZ, "road_existe": False, "payload_valido": False})
|
|
71
|
+
comprueba("the FIRST blocker is decisive, not the last", d.puerta_decisiva, "road")
|
|
72
|
+
|
|
73
|
+
d = ad.decide_entrada(**{**FELIZ, "allowlist": ["alice/home", "bob/research"]})
|
|
74
|
+
diag = next(g.diagnostico for g in d.grafo if g.diagnostico)
|
|
75
|
+
comprueba("the diagnostic reveals the count", diag["n"], 2)
|
|
76
|
+
afirma("but never a raw allowlist entry",
|
|
77
|
+
"alice/home" not in str(diag) and "bob/research" not in str(diag))
|
|
78
|
+
afirma("opaque ids are stable and prefixed",
|
|
79
|
+
all(i.startswith("x") for i in diag["ids"]))
|
|
80
|
+
comprueba("the same entry hashes to the same opaque id",
|
|
81
|
+
ad.id_opaco("alice/home"), ad.id_opaco("alice/home"))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def contrato_razones():
|
|
85
|
+
try:
|
|
86
|
+
ad.Puerta("x", "route", False, "not_a_real_reason")
|
|
87
|
+
afirma("a reason outside the closed set is rejected", False)
|
|
88
|
+
except ValueError:
|
|
89
|
+
afirma("a reason outside the closed set is rejected", True)
|
|
90
|
+
afirma("como_dict is JSON-shaped for the wire",
|
|
91
|
+
set(ad.decide_entrada(**FELIZ).como_dict()) >= {"admission", "reasonCode", "gates"})
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
dispatch()
|
|
95
|
+
bloqueos()
|
|
96
|
+
emparejamiento()
|
|
97
|
+
orden_y_redaccion()
|
|
98
|
+
contrato_razones()
|
|
99
|
+
sys.exit(resumen("admision"))
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Deterministic agent avatars: same name → same face, self-contained SVG.
|
|
3
|
+
|
|
4
|
+
Pure. The load-bearing cases: determinism, distinctness, symmetry, that the
|
|
5
|
+
output is a self-contained SVG with no external reference (Hall CSP), and that
|
|
6
|
+
the kind tints the border.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
AQUI = os.path.dirname(os.path.abspath(__file__))
|
|
13
|
+
RAIZ = os.path.dirname(AQUI)
|
|
14
|
+
sys.path.insert(0, os.path.join(RAIZ, "plugin", "scripts"))
|
|
15
|
+
sys.path.insert(0, AQUI)
|
|
16
|
+
|
|
17
|
+
import avatar # noqa: E402
|
|
18
|
+
from testlib import afirma, comprueba, resumen # noqa: E402
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def determinismo():
|
|
22
|
+
comprueba("same name and kind produce identical SVG",
|
|
23
|
+
avatar.svg("nova", "code"), avatar.svg("nova", "code"))
|
|
24
|
+
afirma("different names produce different faces",
|
|
25
|
+
avatar.svg("nova") != avatar.svg("store-service"))
|
|
26
|
+
afirma("the empty/None name is handled, not crashed",
|
|
27
|
+
avatar.svg(None).startswith("<svg") and avatar.svg("").startswith("<svg"))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def autocontenido():
|
|
31
|
+
s = avatar.svg("nova")
|
|
32
|
+
afirma("it is an svg element", s.startswith("<svg") and s.endswith("</svg>"))
|
|
33
|
+
afirma("it references no external asset (Hall CSP)",
|
|
34
|
+
"http://www.w3.org/2000/svg" in s
|
|
35
|
+
and "https://" not in s.replace("http://www.w3.org/2000/svg", "")
|
|
36
|
+
and "url(" not in s and "<image" not in s and "<script" not in s)
|
|
37
|
+
afirma("a hostile name cannot break out of the aria-label",
|
|
38
|
+
"<script>" not in avatar.svg('<script>x</script>'))
|
|
39
|
+
afirma("data_uri is a base64 svg data URI",
|
|
40
|
+
avatar.data_uri("nova").startswith("data:image/svg+xml;base64,"))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def clase_y_simetria():
|
|
44
|
+
afirma("kind tints the border colour",
|
|
45
|
+
avatar.TINTE_CLASE["knowledge"] in avatar.svg("x", "knowledge"))
|
|
46
|
+
afirma("an unknown kind falls back to the default tint",
|
|
47
|
+
avatar._TINTE_DEFECTO in avatar.svg("x", "nonsense"))
|
|
48
|
+
# Symmetry: the on-cells are mirrored left↔right, so the set is closed under
|
|
49
|
+
# (row, col) -> (row, 4-col).
|
|
50
|
+
encendidas = avatar._celdas_encendidas(avatar._digest("nova"))
|
|
51
|
+
afirma("the cell pattern is left-right symmetric",
|
|
52
|
+
all((f, avatar._CELDAS - 1 - c) in encendidas for (f, c) in encendidas))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
determinismo()
|
|
56
|
+
autocontenido()
|
|
57
|
+
clase_y_simetria()
|
|
58
|
+
sys.exit(resumen("avatar"))
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""The governance benchmark stays honest and machine-readable."""
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
AQUI = os.path.dirname(os.path.abspath(__file__))
|
|
9
|
+
RAIZ = os.path.dirname(AQUI)
|
|
10
|
+
sys.path.insert(0, AQUI)
|
|
11
|
+
from testlib import afirma, comprueba, resumen # noqa: E402
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def main():
|
|
15
|
+
print('\n committee governance benchmark')
|
|
16
|
+
result = subprocess.run(
|
|
17
|
+
[sys.executable, os.path.join(RAIZ, 'benchmarks', 'committee', 'run.py'),
|
|
18
|
+
'--json'], capture_output=True, text=True)
|
|
19
|
+
afirma('· the benchmark runs without a model account',
|
|
20
|
+
result.returncode == 0, result.stderr.strip())
|
|
21
|
+
report = json.loads(result.stdout)
|
|
22
|
+
comprueba('· its scope refuses an unsupported quality claim',
|
|
23
|
+
report.get('scope'), 'protocol structure, not task-answer quality')
|
|
24
|
+
rows = {row['strategy']: row for row in report['results']}
|
|
25
|
+
afirma('· free mesh exposes lateral edges and pre-barrier influence',
|
|
26
|
+
rows['free-mesh']['lateral_member_edges'] > 0
|
|
27
|
+
and rows['free-mesh']['pre_barrier_leaks'] > 0)
|
|
28
|
+
afirma('· a single agent cannot independently verify itself',
|
|
29
|
+
rows['single-agent']['closures_without_pass'] == 1
|
|
30
|
+
and rows['single-agent']['self_verified_closures'] == 1
|
|
31
|
+
and rows['single-agent']['unattributed_decisions'] == 1)
|
|
32
|
+
afirma('· the chaired trace satisfies every measured invariant',
|
|
33
|
+
rows['chair-mediated']['protocol_violations'] == 0)
|
|
34
|
+
afirma('· that result includes evidence from distinct repo roles',
|
|
35
|
+
rows['chair-mediated']['evidence_sources'] >= 3)
|
|
36
|
+
return resumen('benchmark')
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
if __name__ == '__main__':
|
|
40
|
+
sys.exit(main())
|