@cohortapp/agent-sdk 2.17.0 → 2.18.4
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/settings.json +18 -0
- package/.env.example +18 -5
- package/README.md +1 -0
- package/bin/maestro.mjs +62 -0
- package/docs/guides/billing-console-keys.md +60 -0
- package/docs/guides/front-door-session.md +54 -9
- package/docs/guides/mac-mini.md +20 -25
- package/docs/guides/setup-wizard.md +1 -1
- package/docs/runbooks/fleet-rollout.md +156 -0
- package/docs/runbooks/mac-mini-bootstrap.md +12 -14
- package/lib/action-executor.js +19 -3
- package/lib/budget-guard.mjs +279 -3
- package/lib/channels/base-adapter.mjs +3 -1
- package/lib/channels/contract.mjs +2 -1
- package/lib/channels/inbox-item.mjs +8 -0
- package/lib/claude-bin.mjs +5 -6
- package/lib/cli/doctor-checks.mjs +141 -10
- package/lib/cli/global-setup-extras.mjs +5 -1
- package/lib/cli/inbox.mjs +100 -15
- package/lib/cli/seat-auth.mjs +463 -0
- package/lib/cli/session.mjs +80 -12
- package/lib/collective/capture.mjs +8 -6
- package/lib/collective/global-config.mjs +63 -1
- package/lib/collective/presence.mjs +142 -5
- package/lib/comms/send-gate.mjs +559 -1
- package/lib/diagnostics/alerts.mjs +49 -0
- package/lib/diagnostics/cadence-output-freshness.mjs +288 -0
- package/lib/engine/agents/definitions.mjs +343 -0
- package/lib/engine/agents/persist.mjs +275 -0
- package/lib/engine/agents/runtime.mjs +748 -0
- package/lib/engine/agents/usage.mjs +95 -0
- package/lib/engine/auth-status.mjs +139 -0
- package/lib/engine/budget.mjs +194 -0
- package/lib/engine/cli.mjs +1204 -0
- package/lib/engine/commands/index.mjs +269 -0
- package/lib/engine/context/budget.mjs +219 -0
- package/lib/engine/context/cache.mjs +125 -0
- package/lib/engine/context/child-env.mjs +215 -0
- package/lib/engine/context/compaction.mjs +342 -0
- package/lib/engine/context/images.mjs +90 -0
- package/lib/engine/context/instructions.mjs +327 -0
- package/lib/engine/context/lazy-instructions.mjs +169 -0
- package/lib/engine/context/manager.mjs +182 -0
- package/lib/engine/context/real-path.mjs +91 -0
- package/lib/engine/context/secret-values.mjs +163 -0
- package/lib/engine/context/settings.mjs +274 -0
- package/lib/engine/context/stream-input.mjs +159 -0
- package/lib/engine/guard.mjs +152 -0
- package/lib/engine/hooks.mjs +713 -0
- package/lib/engine/loop.mjs +560 -0
- package/lib/engine/mcp/client.mjs +254 -0
- package/lib/engine/mcp/config.mjs +301 -0
- package/lib/engine/mcp/http.mjs +201 -0
- package/lib/engine/mcp/index.mjs +146 -0
- package/lib/engine/mcp/jsonrpc.mjs +147 -0
- package/lib/engine/mcp/naming.mjs +66 -0
- package/lib/engine/mcp/resources.mjs +89 -0
- package/lib/engine/mcp/results.mjs +133 -0
- package/lib/engine/mcp/stdio.mjs +137 -0
- package/lib/engine/mcp/supervisor.mjs +116 -0
- package/lib/engine/messages.mjs +104 -0
- package/lib/engine/output/json.mjs +164 -0
- package/lib/engine/output/stream-json.mjs +266 -0
- package/lib/engine/permissions.mjs +845 -0
- package/lib/engine/process-identity.mjs +164 -0
- package/lib/engine/process-tree.mjs +551 -0
- package/lib/engine/prompt.mjs +60 -0
- package/lib/engine/session/store.mjs +299 -0
- package/lib/engine/session-runtime/args.mjs +97 -0
- package/lib/engine/session-runtime/host.mjs +143 -0
- package/lib/engine/session-runtime/inbox.mjs +122 -0
- package/lib/engine/session-runtime/notifications.mjs +129 -0
- package/lib/engine/session-runtime/registry.mjs +328 -0
- package/lib/engine/session-runtime/runner.mjs +344 -0
- package/lib/engine/session-runtime/socket.mjs +212 -0
- package/lib/engine/session-runtime/wakeup.mjs +115 -0
- package/lib/engine/skills/index.mjs +321 -0
- package/lib/engine/tools/bash-background.mjs +533 -0
- package/lib/engine/tools/bash.mjs +216 -0
- package/lib/engine/tools/edit.mjs +97 -0
- package/lib/engine/tools/glob.mjs +81 -0
- package/lib/engine/tools/grep.mjs +224 -0
- package/lib/engine/tools/index.mjs +84 -0
- package/lib/engine/tools/list-agents.mjs +32 -0
- package/lib/engine/tools/ls.mjs +127 -0
- package/lib/engine/tools/monitor.mjs +82 -0
- package/lib/engine/tools/notebook-edit.mjs +218 -0
- package/lib/engine/tools/read.mjs +103 -0
- package/lib/engine/tools/schedule-wakeup.mjs +45 -0
- package/lib/engine/tools/schema.mjs +144 -0
- package/lib/engine/tools/send-message.mjs +77 -0
- package/lib/engine/tools/session.mjs +70 -0
- package/lib/engine/tools/todo.mjs +144 -0
- package/lib/engine/tools/toolsearch.mjs +217 -0
- package/lib/engine/tools/walk.mjs +193 -0
- package/lib/engine/tools/web-switch.mjs +31 -0
- package/lib/engine/tools/webfetch-html.mjs +387 -0
- package/lib/engine/tools/webfetch-net.mjs +340 -0
- package/lib/engine/tools/webfetch.mjs +198 -0
- package/lib/engine/tools/websearch.mjs +91 -0
- package/lib/engine/tools/workflow.mjs +95 -0
- package/lib/engine/tools/write.mjs +76 -0
- package/lib/engine/tui/line-editor.mjs +137 -0
- package/lib/engine/tui/render.mjs +86 -0
- package/lib/engine/tui/tui.mjs +274 -0
- package/lib/engine/wire/anthropic-messages.mjs +263 -0
- package/lib/engine/wire/effort.mjs +36 -0
- package/lib/engine/wire/errors.mjs +496 -0
- package/lib/engine/wire/http.mjs +441 -0
- package/lib/engine/wire/index.mjs +76 -0
- package/lib/engine/wire/openai-chat.mjs +332 -0
- package/lib/engine/wire/prompt-cache.mjs +79 -0
- package/lib/engine/wire/search.mjs +140 -0
- package/lib/engine/wire/sse.mjs +114 -0
- package/lib/engine/wire/stall.mjs +349 -0
- package/lib/engine/wire/token-provider.mjs +175 -0
- package/lib/engine/wire/usage.mjs +192 -0
- package/lib/engine/workflow/host.mjs +524 -0
- package/lib/engine/workflow/journal.mjs +188 -0
- package/lib/engine/workflow/json-schema.mjs +171 -0
- package/lib/engine/workflow/meta.mjs +329 -0
- package/lib/engine/workflow/notifications.mjs +52 -0
- package/lib/engine/workflow/runtime.mjs +447 -0
- package/lib/engine/workflow/sandbox.mjs +534 -0
- package/lib/engine/workflow/worker.mjs +141 -0
- package/lib/engine/workflow/worktree.mjs +74 -0
- package/lib/execution/disposition.mjs +1 -1
- package/lib/execution/intake.mjs +10 -0
- package/lib/execution/surface-policy.mjs +15 -0
- package/lib/learning/curator.mjs +8 -6
- package/lib/learning/reflect.mjs +8 -6
- package/lib/model-router/catalog/cohort.yaml +137 -0
- package/lib/model-router/catalog.mjs +118 -1
- package/lib/model-router/failover.mjs +67 -16
- package/lib/model-router/llm-task.mjs +39 -3
- package/lib/model-router/resolve.mjs +89 -3
- package/lib/model-router/spawn.mjs +46 -47
- package/lib/model-router/taxonomy.mjs +126 -4
- package/lib/org/cost-sync.mjs +141 -11
- package/lib/org/inbound/broadcast.mjs +289 -0
- package/lib/org/inbound/collective.mjs +375 -0
- package/lib/org/inbound/directedness.mjs +96 -8
- package/lib/org/inbound/facts.mjs +78 -2
- package/lib/org/inbound/project.mjs +22 -0
- package/lib/org/inbound/surfaces.mjs +14 -0
- package/lib/org/llm-token.mjs +879 -0
- package/lib/org/mesh.mjs +61 -0
- package/lib/org/messaging.mjs +3 -1
- package/lib/org/protocol.checksum +1 -1
- package/lib/org/protocol.mjs +15 -0
- package/lib/org/quota.mjs +520 -0
- package/lib/org/tool-surface.mjs +104 -16
- package/lib/org/ui-parity.mjs +16 -1
- package/lib/org/work-ledger.mjs +37 -6
- package/lib/rate-guard.mjs +114 -1
- package/lib/resource-governor.mjs +41 -6
- package/lib/runtime/adapter.mjs +823 -0
- package/lib/runtime/child-env.mjs +191 -0
- package/lib/runtime/legacy-shell-guard.mjs +97 -0
- package/lib/runtime/seat-engine.mjs +162 -0
- package/lib/session/ask-ledger.mjs +271 -0
- package/lib/session/current-work.mjs +676 -0
- package/lib/session/feed-core.mjs +40 -3
- package/lib/session/launch-args.mjs +56 -4
- package/lib/session/status-summary.mjs +26 -9
- package/lib/session/upgrade-notice.mjs +42 -0
- package/lib/setup/claude-probe.mjs +117 -13
- package/lib/setup/enrich.mjs +13 -10
- package/lib/setup/sections/model.mjs +39 -13
- package/lib/telemetry/collect.mjs +208 -9
- package/lib/upgrade/ignored-drift.mjs +105 -0
- package/lib/voice/post-call-brief.mjs +30 -17
- package/package.json +13 -3
- package/plugins/maestro-skills/skills/board-work.md +5 -0
- package/plugins/maestro-skills/skills/inbound-triage.md +56 -15
- package/plugins/maestro-skills/skills/main-session.md +18 -7
- package/scripts/ci/check-tarball-fidelity.mjs +126 -2
- package/scripts/ci/run-tests.mjs +47 -19
- package/scripts/cohort-llm/api-key-helper.mjs +92 -0
- package/scripts/collective/hook-runner.mjs +29 -2
- package/scripts/continuous-monitor.sh +13 -0
- package/scripts/cost/track-claude-usage.mjs +15 -0
- package/scripts/daemon/agent-daemon.mjs +408 -20
- package/scripts/daemon/assurance.mjs +48 -12
- package/scripts/daemon/cadence-consumer.mjs +218 -68
- package/scripts/daemon/cadence-handlers.mjs +73 -4
- package/scripts/daemon/classifier.mjs +75 -26
- package/scripts/daemon/context-compiler.mjs +51 -37
- package/scripts/daemon/deliver.mjs +30 -1
- package/scripts/daemon/dispatcher.mjs +595 -149
- package/scripts/daemon/health.mjs +14 -1
- package/scripts/daemon/maestro-daemon.mjs +11 -0
- package/scripts/daemon/prompt-builder.mjs +24 -0
- package/scripts/daemon/responder.mjs +246 -79
- package/scripts/daemon/sdk-version.mjs +98 -16
- package/scripts/eval/probe-gateway.mjs +635 -0
- package/scripts/eval/replay/extract.mjs +270 -0
- package/scripts/eval/replay/grade.mjs +260 -0
- package/scripts/eval/replay/lib/config.mjs +50 -0
- package/scripts/eval/replay/lib/effects.mjs +65 -0
- package/scripts/eval/replay/lib/fixture.mjs +188 -0
- package/scripts/eval/replay/lib/judge.mjs +72 -0
- package/scripts/eval/replay/lib/redact.mjs +136 -0
- package/scripts/eval/replay/lib/sandbox.mjs +170 -0
- package/scripts/eval/replay/lib/schema-check.mjs +63 -0
- package/scripts/eval/replay/lib/transcript.mjs +76 -0
- package/scripts/eval/replay/mcp-replay-stub.mjs +101 -0
- package/scripts/eval/replay/report.mjs +185 -0
- package/scripts/eval/replay/run.mjs +404 -0
- package/scripts/fleet/rollout.mjs +1094 -0
- package/scripts/hooks/pre-send-audit.sh +36 -245
- package/scripts/hooks/pre-write-yaml-validate.mjs +275 -0
- package/scripts/hooks/validate-state-yaml.sh +190 -0
- package/scripts/huddle/huddle-llm.mjs +361 -0
- package/scripts/huddle/huddle-server.mjs +46 -121
- package/scripts/local-triggers/autoupdate.sh +448 -78
- package/scripts/local-triggers/run-trigger.sh +13 -0
- package/scripts/maintenance/pin-integrity.mjs +364 -0
- package/scripts/poll-slack-events.sh +41 -9
- package/scripts/poller/slack-socket-mode.mjs +28 -3
- package/scripts/session/supervisor.mjs +80 -13
- package/scripts/spawn-session.sh +13 -0
- package/bin/maestro.test.mjs +0 -1574
- package/lib/action-executor.test.mjs +0 -871
- package/lib/archetype.test.mjs +0 -132
- package/lib/assurance/plan-note.test.mjs +0 -234
- package/lib/assurance/room-budget.test.mjs +0 -486
- package/lib/assurance/tier.test.mjs +0 -174
- package/lib/autonomy.test.mjs +0 -66
- package/lib/backlog.test.mjs +0 -302
- package/lib/backup/policy.test.mjs +0 -305
- package/lib/budget-escalate.test.mjs +0 -232
- package/lib/budget-guard.envelope.test.mjs +0 -476
- package/lib/budget-guard.test.mjs +0 -427
- package/lib/cadence-bus-requeue.test.mjs +0 -83
- package/lib/cadence-bus-schedule.test.mjs +0 -194
- package/lib/cadence-bus.test.mjs +0 -720
- package/lib/cadences.test.mjs +0 -230
- package/lib/capability/inventory.test.mjs +0 -232
- package/lib/capability.test.mjs +0 -78
- package/lib/channels/base-adapter.test.mjs +0 -590
- package/lib/channels/channels.test.mjs +0 -371
- package/lib/channels/contract.test.mjs +0 -162
- package/lib/channels/inbox-item.test.mjs +0 -368
- package/lib/channels/orgmail/adapter.test.mjs +0 -448
- package/lib/channels/pairing.test.mjs +0 -270
- package/lib/channels/repeat-suppressor.test.mjs +0 -134
- package/lib/channels/slack-adapter.test.mjs +0 -212
- package/lib/channels/telegram-adapter.test.mjs +0 -306
- package/lib/channels/voice/adapter.test.mjs +0 -278
- package/lib/channels/whatsapp/adapter-baileys.test.mjs +0 -359
- package/lib/channels/whatsapp/baileys-typing.test.mjs +0 -154
- package/lib/charter.test.mjs +0 -89
- package/lib/claude-bin.test.mjs +0 -131
- package/lib/cli/board.test.mjs +0 -227
- package/lib/cli/design.test.mjs +0 -270
- package/lib/cli/doctor-checks.test.mjs +0 -336
- package/lib/cli/global-setup-extras.test.mjs +0 -462
- package/lib/cli/inbox.test.mjs +0 -230
- package/lib/cli/session-ack.test.mjs +0 -63
- package/lib/cli/session.test.mjs +0 -613
- package/lib/collective/capture.test.mjs +0 -121
- package/lib/collective/cards.test.mjs +0 -114
- package/lib/collective/config.test.mjs +0 -123
- package/lib/collective/global-config.test.mjs +0 -220
- package/lib/collective/global-skills.test.mjs +0 -126
- package/lib/collective/presence.test.mjs +0 -95
- package/lib/collective/recall.test.mjs +0 -116
- package/lib/collective/vendor-skills.test.mjs +0 -306
- package/lib/comms/send-gate.test.mjs +0 -770
- package/lib/comms.test.mjs +0 -41
- package/lib/context/budget.test.mjs +0 -252
- package/lib/context/history-scope.test.mjs +0 -79
- package/lib/cost/ledger-row.test.mjs +0 -183
- package/lib/design/design-md.test.mjs +0 -318
- package/lib/design/fixtures/DESIGN.golden.md +0 -238
- package/lib/design/fixtures/PRODUCT.golden.md +0 -67
- package/lib/design/fixtures/foundation.json +0 -133
- package/lib/design/refresh-gate.test.mjs +0 -144
- package/lib/design/write.test.mjs +0 -241
- package/lib/diagnostics/alerts.test.mjs +0 -318
- package/lib/diagnostics/backup-freshness.test.mjs +0 -185
- package/lib/diagnostics/counters.test.mjs +0 -206
- package/lib/diagnostics/events.test.mjs +0 -290
- package/lib/diagnostics/otel.test.mjs +0 -196
- package/lib/diagnostics/trace.test.mjs +0 -251
- package/lib/env-compat.test.mjs +0 -104
- package/lib/execution/disposition.test.mjs +0 -553
- package/lib/execution/drive.test.mjs +0 -270
- package/lib/execution/effects.test.mjs +0 -344
- package/lib/execution/intake.test.mjs +0 -389
- package/lib/execution/journal.test.mjs +0 -261
- package/lib/execution/match.test.mjs +0 -235
- package/lib/execution/pipeline.test.mjs +0 -392
- package/lib/execution/route.test.mjs +0 -186
- package/lib/execution/surface-policy.test.mjs +0 -162
- package/lib/fs-atomic.test.mjs +0 -72
- package/lib/fs-ownership.test.mjs +0 -158
- package/lib/goals/admission.test.mjs +0 -164
- package/lib/goals/classify.test.mjs +0 -167
- package/lib/goals/collaborate.test.mjs +0 -336
- package/lib/goals/gaps.test.mjs +0 -284
- package/lib/goals/loop.test.mjs +0 -845
- package/lib/hooks/bus.test.mjs +0 -387
- package/lib/identity/persona.test.mjs +0 -142
- package/lib/kpi-sensors.test.mjs +0 -278
- package/lib/kpi.test.mjs +0 -244
- package/lib/learning/config.test.mjs +0 -75
- package/lib/learning/counters.test.mjs +0 -69
- package/lib/learning/curator-consolidate.test.mjs +0 -238
- package/lib/learning/curator.test.mjs +0 -106
- package/lib/learning/reflect.test.mjs +0 -0
- package/lib/learning/session-index.test.mjs +0 -125
- package/lib/learning/skill-writer.test.mjs +0 -210
- package/lib/mandate/audit.test.mjs +0 -195
- package/lib/mandate/contract.test.mjs +0 -185
- package/lib/mandate/derive.test.mjs +0 -274
- package/lib/mandate/model.test.mjs +0 -164
- package/lib/mandate/refresh.test.mjs +0 -389
- package/lib/mcp/server.test.mjs +0 -426
- package/lib/model-router/auth-profiles.test.mjs +0 -580
- package/lib/model-router/catalog.test.mjs +0 -385
- package/lib/model-router/economics.test.mjs +0 -438
- package/lib/model-router/failover.test.mjs +0 -439
- package/lib/model-router/health.test.mjs +0 -338
- package/lib/model-router/integration-coverage.test.mjs +0 -831
- package/lib/model-router/integration.test.mjs +0 -564
- package/lib/model-router/ledger.test.mjs +0 -415
- package/lib/model-router/llm-task.test.mjs +0 -392
- package/lib/model-router/org-credentials.test.mjs +0 -265
- package/lib/model-router/pricing-refresh.test.mjs +0 -286
- package/lib/model-router/reconcile.test.mjs +0 -316
- package/lib/model-router/repair.test.mjs +0 -180
- package/lib/model-router/spawn.test.mjs +0 -446
- package/lib/model-router/taxonomy.test.mjs +0 -410
- package/lib/model-router.test.mjs +0 -1207
- package/lib/org/activity.test.mjs +0 -134
- package/lib/org/approvals.test.mjs +0 -216
- package/lib/org/awareness.test.mjs +0 -159
- package/lib/org/board-mine-cache.test.mjs +0 -53
- package/lib/org/board.test.mjs +0 -187
- package/lib/org/bootstrap-context.test.mjs +0 -153
- package/lib/org/client.test.mjs +0 -1206
- package/lib/org/cohort-client.test.mjs +0 -126
- package/lib/org/cost-sync.test.mjs +0 -153
- package/lib/org/doctor.test.mjs +0 -346
- package/lib/org/engagement-ledger.test.mjs +0 -112
- package/lib/org/engagement.test.mjs +0 -739
- package/lib/org/handoff.test.mjs +0 -269
- package/lib/org/inbound/directedness.test.mjs +0 -668
- package/lib/org/inbound/facts.test.mjs +0 -471
- package/lib/org/inbound/hydrate.test.mjs +0 -908
- package/lib/org/inbound/index.test.mjs +0 -429
- package/lib/org/inbound/project.test.mjs +0 -287
- package/lib/org/integration-tools.test.mjs +0 -160
- package/lib/org/keys.test.mjs +0 -92
- package/lib/org/knowledge.test.mjs +0 -326
- package/lib/org/leases.test.mjs +0 -235
- package/lib/org/mesh-directives.test.mjs +0 -110
- package/lib/org/mesh-integration.test.mjs +0 -127
- package/lib/org/mesh.test.mjs +0 -400
- package/lib/org/messaging.test.mjs +0 -471
- package/lib/org/param-contract.test.mjs +0 -477
- package/lib/org/policy.test.mjs +0 -237
- package/lib/org/protocol.checksum.test.mjs +0 -90
- package/lib/org/protocol.test.mjs +0 -323
- package/lib/org/push.test.mjs +0 -792
- package/lib/org/registry.test.mjs +0 -100
- package/lib/org/resource-tools.test.mjs +0 -361
- package/lib/org/tool-access.test.mjs +0 -144
- package/lib/org/tool-surface-integration.test.mjs +0 -120
- package/lib/org/tool-surface.test.mjs +0 -1268
- package/lib/org/typing.test.mjs +0 -291
- package/lib/org/ui-parity.test.mjs +0 -560
- package/lib/org/verify.test.mjs +0 -194
- package/lib/org/work-ledger.test.mjs +0 -273
- package/lib/plan/adoption-e2e.test.mjs +0 -366
- package/lib/plan/budget-enforcement.test.mjs +0 -400
- package/lib/plan/compile.test.mjs +0 -382
- package/lib/plan/emit.test.mjs +0 -269
- package/lib/plan/explain.test.mjs +0 -188
- package/lib/prompts/parallelism.test.mjs +0 -177
- package/lib/rag/rag.test.mjs +0 -505
- package/lib/rate-guard.test.mjs +0 -272
- package/lib/reactive-gate.test.mjs +0 -57
- package/lib/render.test.mjs +0 -68
- package/lib/resource-governor.test.mjs +0 -488
- package/lib/scheduling/dynamic-jobs.test.mjs +0 -344
- package/lib/scheduling/jitter.test.mjs +0 -140
- package/lib/secrets/broker.test.mjs +0 -280
- package/lib/secrets/providers.test.mjs +0 -274
- package/lib/security/audit-engine.test.mjs +0 -424
- package/lib/security/coerce-args.test.mjs +0 -281
- package/lib/security/dangerous-tools.test.mjs +0 -68
- package/lib/security/external-content.test.mjs +0 -84
- package/lib/security/redact.test.mjs +0 -441
- package/lib/security/secret-equal.test.mjs +0 -55
- package/lib/session/config.test.mjs +0 -92
- package/lib/session/feed-core.test.mjs +0 -198
- package/lib/session/first-run.test.mjs +0 -121
- package/lib/session/frontdoor.test.mjs +0 -205
- package/lib/session/handoffs.test.mjs +0 -183
- package/lib/session/identity.test.mjs +0 -180
- package/lib/session/inbox-claims.test.mjs +0 -286
- package/lib/session/launch-args.test.mjs +0 -157
- package/lib/session/liveness.test.mjs +0 -100
- package/lib/session/status-summary.test.mjs +0 -118
- package/lib/session-permissions.test.mjs +0 -120
- package/lib/setup/claude-probe.test.mjs +0 -187
- package/lib/setup/completeness.test.mjs +0 -110
- package/lib/setup/context-pack.test.mjs +0 -89
- package/lib/setup/enrich.test.mjs +0 -115
- package/lib/setup/enroll-from-cohort.test.mjs +0 -300
- package/lib/setup/integration.test.mjs +0 -162
- package/lib/setup/io.test.mjs +0 -77
- package/lib/setup/runner.test.mjs +0 -132
- package/lib/setup/sections/identity.test.mjs +0 -234
- package/lib/setup/sections/inventory.test.mjs +0 -198
- package/lib/setup/sections/learning.test.mjs +0 -81
- package/lib/setup/sections/mandate.test.mjs +0 -388
- package/lib/setup/sections/messaging.test.mjs +0 -127
- package/lib/setup/sections/model.test.mjs +0 -240
- package/lib/setup/sections/org.test.mjs +0 -346
- package/lib/setup/sections/orgmail.test.mjs +0 -118
- package/lib/setup/sections/recovery.test.mjs +0 -98
- package/lib/setup/sections/subagents.test.mjs +0 -429
- package/lib/setup/sections/verify.test.mjs +0 -175
- package/lib/setup/sot.test.mjs +0 -81
- package/lib/setup/state.test.mjs +0 -115
- package/lib/singleton.test.mjs +0 -151
- package/lib/subagents/cli.test.mjs +0 -389
- package/lib/subagents/client.test.mjs +0 -309
- package/lib/subagents/gap.test.mjs +0 -234
- package/lib/subagents/lock.test.mjs +0 -248
- package/lib/subagents/manifest.test.mjs +0 -175
- package/lib/subagents/refs.test.mjs +0 -204
- package/lib/subagents/resolve.test.mjs +0 -422
- package/lib/subagents/schema.test.mjs +0 -328
- package/lib/telemetry/alerts.test.mjs +0 -109
- package/lib/telemetry/collect.test.mjs +0 -1274
- package/lib/tool-definitions-integration.test.mjs +0 -83
- package/lib/tool-definitions.test.mjs +0 -437
- package/lib/upgrade/global-refresh.test.mjs +0 -65
- package/lib/upgrade/launchd-reconcile.test.mjs +0 -272
- package/lib/upgrade/post-steps.test.mjs +0 -200
- package/lib/upgrade/verify.test.mjs +0 -164
- package/lib/util/fetch-timeout.test.mjs +0 -202
- package/lib/util/reconnect.test.mjs +0 -369
- package/lib/util/unhandled.test.mjs +0 -216
- package/lib/voice/outbound.test.mjs +0 -69
- package/lib/voice/session-rotation.test.mjs +0 -114
- package/lib/voice/stt.test.mjs +0 -226
- package/lib/voice/voice.test.mjs +0 -990
- package/scripts/cadence/enqueue-cadence-tick.test.mjs +0 -187
- package/scripts/ci/check-docs-accuracy.test.mjs +0 -409
- package/scripts/ci/check-durable-write-seam.test.mjs +0 -90
- package/scripts/ci/check-no-build-artifacts.test.mjs +0 -71
- package/scripts/ci/check-no-residual-identity.test.mjs +0 -202
- package/scripts/ci/check-skill-packs.test.mjs +0 -495
- package/scripts/ci/check-subagent-frontmatter.test.mjs +0 -124
- package/scripts/ci/check.test.mjs +0 -194
- package/scripts/ci/conformance-org-api.test.mjs +0 -425
- package/scripts/cloud-relay/voice/relay-identity.test.mjs +0 -96
- package/scripts/collective/hook-runner.test.mjs +0 -173
- package/scripts/cost/fleet-digest.test.mjs +0 -207
- package/scripts/cost/track-claude-usage-pricing.test.mjs +0 -183
- package/scripts/cost/track-claude-usage.test.mjs +0 -148
- package/scripts/daemon/agent-daemon-board-mine.test.mjs +0 -96
- package/scripts/daemon/agent-daemon-design.test.mjs +0 -238
- package/scripts/daemon/agent-daemon-frontdoor.test.mjs +0 -60
- package/scripts/daemon/agent-daemon.test.mjs +0 -995
- package/scripts/daemon/assurance-e2e.test.mjs +0 -613
- package/scripts/daemon/assurance.test.mjs +0 -1791
- package/scripts/daemon/board-mirror.test.mjs +0 -165
- package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +0 -393
- package/scripts/daemon/cadence-consumer-governance.test.mjs +0 -276
- package/scripts/daemon/cadence-consumer.test.mjs +0 -776
- package/scripts/daemon/cadence-handlers.test.mjs +0 -837
- package/scripts/daemon/classifier-identity.test.mjs +0 -137
- package/scripts/daemon/classifier.test.mjs +0 -266
- package/scripts/daemon/classify-kind.test.mjs +0 -40
- package/scripts/daemon/context-compiler.test.mjs +0 -406
- package/scripts/daemon/deliver.test.mjs +0 -564
- package/scripts/daemon/dispatcher-cooldown.test.mjs +0 -122
- package/scripts/daemon/dispatcher-governance.test.mjs +0 -1013
- package/scripts/daemon/dispatcher-resume.test.mjs +0 -166
- package/scripts/daemon/dispatcher-session-continuity.test.mjs +0 -365
- package/scripts/daemon/execution-ladder.test.mjs +0 -470
- package/scripts/daemon/goal-steward-cadence.test.mjs +0 -312
- package/scripts/daemon/inbox-deferral-session.test.mjs +0 -49
- package/scripts/daemon/inbox-deferral.test.mjs +0 -336
- package/scripts/daemon/inbox-wake.test.mjs +0 -199
- package/scripts/daemon/integration.test.mjs +0 -149
- package/scripts/daemon/lib/self-echo.test.mjs +0 -153
- package/scripts/daemon/lib/session-router.test.mjs +0 -554
- package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
- package/scripts/daemon/prompt-builder.test.mjs +0 -556
- package/scripts/daemon/responder-cost.test.mjs +0 -68
- package/scripts/daemon/responder-history.test.mjs +0 -221
- package/scripts/daemon/sdk-version.test.mjs +0 -31
- package/scripts/daemon/session-lock.test.mjs +0 -252
- package/scripts/daemon/session-outcomes.test.mjs +0 -533
- package/scripts/daemon/typing-registry.test.mjs +0 -102
- package/scripts/hooks/pre-send-audit.test.mjs +0 -354
- package/scripts/huddle/huddle-prompt.test.mjs +0 -176
- package/scripts/local-triggers/autoupdate.test.mjs +0 -518
- package/scripts/local-triggers/generate-plists.test.mjs +0 -456
- package/scripts/media-generation/brand-clause.test.mjs +0 -135
- package/scripts/org/send-orgmail.first-contact.test.mjs +0 -102
- package/scripts/poller/inbox-privilege-injection.test.mjs +0 -167
- package/scripts/poller/inbox-scan-poller.test.mjs +0 -295
- package/scripts/poller/lib/cloud-relay-dedup.test.mjs +0 -133
- package/scripts/poller/slack-socket-mode.test.mjs +0 -805
- package/scripts/poller-launchd/install.test.mjs +0 -243
- package/scripts/restore-from-backup.test.mjs +0 -181
- package/scripts/session/feed.test.mjs +0 -196
- package/scripts/session/supervisor-sh.test.mjs +0 -218
- package/scripts/session/supervisor.test.mjs +0 -482
- package/scripts/setup/configure-macos.test.mjs +0 -306
- package/scripts/setup/gen-subagent-manifest.test.mjs +0 -124
- package/scripts/setup/generate-agent-package-json.test.mjs +0 -143
- package/scripts/setup/generate-capability.test.mjs +0 -134
- package/scripts/setup/init-agent.test.mjs +0 -370
- package/scripts/setup/init-skill-marketplace.test.mjs +0 -193
- package/scripts/vendor/sync-skill-packs.test.mjs +0 -103
- package/scripts/watchdog/memory-watchdog.test.mjs +0 -64
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/mandate/derive.test.mjs — the pillars model, derived at init from the
|
|
3
|
-
* charter and the role.
|
|
4
|
-
*
|
|
5
|
-
* This is the step that makes the inert PILLAR sections in an agent's
|
|
6
|
-
* `config/agent.json` load-bearing: charter clause → PILLAR → OBJECTIVE → GOAL,
|
|
7
|
-
* with a sensor bound to a capability the manifest says is actually REACHABLE.
|
|
8
|
-
*
|
|
9
|
-
* The negative cases matter more than the happy path here: an objective whose
|
|
10
|
-
* sensor cannot be reached must be proposed as a HUMAN measurement obligation
|
|
11
|
-
* with a recorded degradation, never bound to a capability that does not exist.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
"use strict";
|
|
15
|
-
|
|
16
|
-
import { test } from "node:test";
|
|
17
|
-
import assert from "node:assert/strict";
|
|
18
|
-
|
|
19
|
-
import { deriveMandate, toMandateBody, pickSensor, slug, firstClause, isAdoptable } from "./derive.mjs";
|
|
20
|
-
import { validateMandateBody, pillars, ancestry, provenanceOf } from "./model.mjs";
|
|
21
|
-
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
// Fixtures
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
|
|
26
|
-
const CHARTER = [
|
|
27
|
-
{ id: "cs_1", kind: "PILLAR", order: 1, title: "Revenue growth", body: "Own the revenue line end to end." },
|
|
28
|
-
{ id: "cs_2", kind: "PILLAR", order: 2, title: "Customer retention", body: "Keep the customers we win." },
|
|
29
|
-
{ id: "cs_9", kind: "PRINCIPLE", order: 9, title: "Be direct", body: "Say the hard thing early." },
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
/** A manifest where only the CRM read is actually reachable. */
|
|
33
|
-
const ENTRIES = [
|
|
34
|
-
{ id: "crm_list_deals", plane: "org", reachable: true },
|
|
35
|
-
{ id: "crm_list_contacts", plane: "org", reachable: false },
|
|
36
|
-
];
|
|
37
|
-
/** The shape `lib/capability/inventory.buildInventory` actually returns. */
|
|
38
|
-
const MANIFEST = { version: 1, entries: ENTRIES };
|
|
39
|
-
|
|
40
|
-
// ---------------------------------------------------------------------------
|
|
41
|
-
// Pure helpers
|
|
42
|
-
// ---------------------------------------------------------------------------
|
|
43
|
-
|
|
44
|
-
test("slug and firstClause are deterministic and bounded", () => {
|
|
45
|
-
assert.equal(slug("Revenue Growth!"), "revenue-growth");
|
|
46
|
-
assert.equal(slug(" Multiple spaces "), "multiple-spaces");
|
|
47
|
-
assert.equal(firstClause("Own the revenue line. And then some more."), "Own the revenue line.");
|
|
48
|
-
assert.ok(firstClause("x".repeat(500)).length <= 120);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test("pickSensor binds only to a REACHABLE capability", () => {
|
|
52
|
-
const reachable = new Set(["crm_list_deals"]);
|
|
53
|
-
const bound = pickSensor("pipeline coverage across open deals", reachable);
|
|
54
|
-
if (bound.capability) {
|
|
55
|
-
assert.equal(bound.source, "method");
|
|
56
|
-
assert.ok(reachable.has(bound.capability), "a bound sensor must name a reachable capability");
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Same text, nothing reachable: the family matched but no capability can be
|
|
60
|
-
// cited — that is a measurement DEBT, and it must be visible.
|
|
61
|
-
const unbound = pickSensor("pipeline coverage across open deals", new Set());
|
|
62
|
-
assert.equal(unbound.capability, null);
|
|
63
|
-
assert.equal(unbound.source, "human");
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// ---------------------------------------------------------------------------
|
|
67
|
-
// The derivation
|
|
68
|
-
// ---------------------------------------------------------------------------
|
|
69
|
-
|
|
70
|
-
test("charter PILLAR sections become PILLAR nodes carrying their charterSectionId", () => {
|
|
71
|
-
const r = deriveMandate({ charterSections: CHARTER, kpiCategories: [{ id: "pipeline", name: "Pipeline" }] });
|
|
72
|
-
const ps = pillars(r.objectives);
|
|
73
|
-
assert.equal(ps.length, 2, "only PILLAR/CAPABILITY sections become pillars — a PRINCIPLE is not an outcome");
|
|
74
|
-
assert.deepEqual(ps.map((p) => p.charterSectionId), ["cs_1", "cs_2"]);
|
|
75
|
-
assert.deepEqual(ps.map((p) => p.text), ["Revenue growth", "Customer retention"]);
|
|
76
|
-
for (const p of ps) {
|
|
77
|
-
assert.equal(p.parentKey, null);
|
|
78
|
-
assert.equal(p.state, "proposed", "nothing is adopted by derivation — a human adopts");
|
|
79
|
-
assert.equal(p.source.origin, "charter");
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
test("EVERYTHING derived is proposed — derivation can never adopt", () => {
|
|
84
|
-
const r = deriveMandate({
|
|
85
|
-
charterSections: CHARTER,
|
|
86
|
-
// Both of these overlap a pillar's own words, so both are PLACEABLE — see
|
|
87
|
-
// the unplaced-node test below for the other half.
|
|
88
|
-
kpiCategories: [{ id: "revenue-coverage", name: "Revenue pipeline coverage", examples: ["pipeline_coverage_x"] }],
|
|
89
|
-
priorities: [{ id: "p1", title: "Lift retention among customers we win" }],
|
|
90
|
-
manifest: MANIFEST,
|
|
91
|
-
});
|
|
92
|
-
assert.ok(r.objectives.length >= 4);
|
|
93
|
-
for (const o of r.objectives) {
|
|
94
|
-
assert.equal(o.state, "proposed", `"${o.key}" must be proposed, not ${o.state}`);
|
|
95
|
-
assert.ok(!o.adoptedById, "derivation must never stamp an adopter");
|
|
96
|
-
}
|
|
97
|
-
// And therefore nothing here can drive work: adoptedObjectives is empty.
|
|
98
|
-
const validation = validateMandateBody(r.objectives, { ownerMemberId: "mem_self" });
|
|
99
|
-
assert.equal(validation.errors.length, 0, `derived tree must be structurally valid: ${validation.errors.join("; ")}`);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test("the derived tree is structurally valid: every objective hangs off a pillar", () => {
|
|
103
|
-
const r = deriveMandate({
|
|
104
|
-
charterSections: CHARTER,
|
|
105
|
-
kpiCategories: [
|
|
106
|
-
{ id: "revenue-coverage", name: "Revenue pipeline coverage", examples: ["pipeline_coverage_x"] },
|
|
107
|
-
{ id: "churn", name: "Customer retention rate", examples: ["logo_retention_pct"] },
|
|
108
|
-
],
|
|
109
|
-
manifest: MANIFEST,
|
|
110
|
-
});
|
|
111
|
-
const validation = validateMandateBody(r.objectives);
|
|
112
|
-
assert.equal(validation.ok, true, validation.errors.join("; "));
|
|
113
|
-
|
|
114
|
-
for (const o of r.objectives.filter((x) => x.kind !== "PILLAR")) {
|
|
115
|
-
assert.ok(o.parentKey, `"${o.key}" must have a parent`);
|
|
116
|
-
const chain = ancestry(r.objectives, o.key);
|
|
117
|
-
assert.ok(
|
|
118
|
-
chain.some((n) => n.kind === "PILLAR"),
|
|
119
|
-
`"${o.key}" must trace to a PILLAR — provenance is the whole point`
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
test("provenance walks objective → pillar → charter clause, offline", () => {
|
|
125
|
-
const r = deriveMandate({
|
|
126
|
-
charterSections: CHARTER,
|
|
127
|
-
kpiCategories: [{ id: "revenue-coverage", name: "Revenue pipeline coverage", examples: ["pipeline_coverage_x"] }],
|
|
128
|
-
manifest: MANIFEST,
|
|
129
|
-
});
|
|
130
|
-
const obj = r.objectives.find((o) => o.kind === "OBJECTIVE");
|
|
131
|
-
const prov = provenanceOf(r.objectives, obj.key);
|
|
132
|
-
assert.ok(prov.pillar, "an objective must resolve to a pillar");
|
|
133
|
-
assert.match(prov.clause, /^cs_/, "and the pillar must carry the charter clause id");
|
|
134
|
-
assert.ok(prov.chain.includes(obj.key));
|
|
135
|
-
// The clause id is also stamped on the OBJECTIVE ITSELF, not only on its
|
|
136
|
-
// pillar. hq's `mandate.propose` takes `charterSectionId`; it has no idea what
|
|
137
|
-
// a `parentKey` is, so a node that carried provenance only through its parent
|
|
138
|
-
// reached the server with BOTH provenance columns null — which is why all 35
|
|
139
|
-
// of A016's proposals had `charterSectionId: null`.
|
|
140
|
-
assert.equal(obj.charterSectionId, prov.clause);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
test("an objective that overlaps NO pillar is carried UNPLACED, never auto-attached to the first one", () => {
|
|
144
|
-
// The old fallback parented anything unmatched onto `pillarKeys[0]`. On A016
|
|
145
|
-
// that put `global-footprint` and `capital-formation-aum-growth` under
|
|
146
|
-
// `own-the-agent-platform-substrate`: an edge that existed, rendered, and was
|
|
147
|
-
// fiction.
|
|
148
|
-
const r = deriveMandate({
|
|
149
|
-
charterSections: CHARTER,
|
|
150
|
-
kpiCategories: [{ id: "warehouse-latency", name: "Warehouse latency", examples: ["p95_ms"] }],
|
|
151
|
-
manifest: MANIFEST,
|
|
152
|
-
});
|
|
153
|
-
const orphan = r.objectives.find((o) => o.key === "warehouse-latency");
|
|
154
|
-
assert.ok(orphan, "the node is KEPT — dropping it would delete work somebody asked for");
|
|
155
|
-
assert.equal(orphan.parentKey, null, "and it is NOT attached to an unrelated pillar");
|
|
156
|
-
assert.equal(orphan.charterSectionId, null);
|
|
157
|
-
assert.ok(
|
|
158
|
-
r.degradations.some((d) => d.kind === "no_pillar_trace" && d.key === "warehouse-latency"),
|
|
159
|
-
`expected a no_pillar_trace degradation; got ${JSON.stringify(r.degradations)}`
|
|
160
|
-
);
|
|
161
|
-
assert.equal(isAdoptable(orphan), false, "an unplaced objective is not adoptable");
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
test("a slug collision folds into the existing objective instead of minting an `x-2` mirror", () => {
|
|
165
|
-
// Ten of A016's 35 proposals were `x` (OBJECTIVE/weekly) + `x-2` (GOAL/monthly)
|
|
166
|
-
// pairs: one accountability, two rows, two obligations, two gaps.
|
|
167
|
-
const r = deriveMandate({
|
|
168
|
-
charterSections: CHARTER,
|
|
169
|
-
kpiCategories: [{ id: "revenue-coverage", name: "Revenue pipeline coverage", examples: ["pipeline_coverage_x"] }],
|
|
170
|
-
priorities: [{ id: "p1", title: "Revenue coverage" }],
|
|
171
|
-
manifest: MANIFEST,
|
|
172
|
-
});
|
|
173
|
-
const keys = r.objectives.map((o) => o.key);
|
|
174
|
-
assert.equal(keys.filter((k) => k === "revenue-coverage").length, 1);
|
|
175
|
-
assert.ok(!keys.some((k) => /-\d+$/.test(k)), `no mirror key may be minted; got ${keys.join(", ")}`);
|
|
176
|
-
assert.ok(r.degradations.some((d) => d.kind === "duplicate_key"));
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
test("an objective with no reachable sensor is a recorded DEGRADATION, not a silent human task", () => {
|
|
180
|
-
const r = deriveMandate({
|
|
181
|
-
charterSections: CHARTER,
|
|
182
|
-
// A KPI category nothing in the manifest can measure.
|
|
183
|
-
kpiCategories: [{ id: "brand-sentiment", name: "Customer brand sentiment", examples: ["nps_score"] }],
|
|
184
|
-
manifest: MANIFEST,
|
|
185
|
-
});
|
|
186
|
-
const obj = r.objectives.find((o) => o.key === "brand-sentiment");
|
|
187
|
-
assert.ok(obj);
|
|
188
|
-
assert.notEqual(obj.sensor && obj.sensor.source, "method");
|
|
189
|
-
assert.ok(
|
|
190
|
-
r.degradations.some((d) => d.kind === "no_reachable_sensor" && d.key === "brand-sentiment"),
|
|
191
|
-
`expected a no_reachable_sensor degradation; got ${JSON.stringify(r.degradations)}`
|
|
192
|
-
);
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
test("an empty charter is reported, not silently accepted", () => {
|
|
196
|
-
const r = deriveMandate({ charterSections: [], kpiCategories: [] });
|
|
197
|
-
assert.ok(r.degradations.some((d) => d.kind === "no_charter_pillars"));
|
|
198
|
-
assert.ok(r.degradations.some((d) => d.kind === "no_kpi_categories"));
|
|
199
|
-
assert.equal(r.objectives.length, 0);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
test("derivation is deterministic — same inputs, byte-identical tree", () => {
|
|
203
|
-
const input = {
|
|
204
|
-
charterSections: CHARTER,
|
|
205
|
-
kpiCategories: [{ id: "pipeline", name: "Pipeline coverage", examples: ["pipeline_coverage_x"] }],
|
|
206
|
-
priorities: [{ id: "p1", title: "Land three lighthouse accounts" }],
|
|
207
|
-
manifest: MANIFEST,
|
|
208
|
-
};
|
|
209
|
-
assert.equal(JSON.stringify(deriveMandate(input)), JSON.stringify(deriveMandate(input)));
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
test("duplicate titles never collide into one key", () => {
|
|
213
|
-
const r = deriveMandate({
|
|
214
|
-
charterSections: [
|
|
215
|
-
{ id: "cs_1", kind: "PILLAR", order: 1, title: "Growth", body: "Growth" },
|
|
216
|
-
{ id: "cs_2", kind: "PILLAR", order: 2, title: "Growth", body: "Growth again" },
|
|
217
|
-
],
|
|
218
|
-
});
|
|
219
|
-
const keys = r.objectives.map((o) => o.key);
|
|
220
|
-
assert.equal(new Set(keys).size, keys.length, `keys must be unique; got ${keys.join(",")}`);
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
test("overrides let a human set the real target without re-deriving", () => {
|
|
224
|
-
const r = deriveMandate({
|
|
225
|
-
charterSections: CHARTER,
|
|
226
|
-
kpiCategories: [{ id: "pipeline", name: "Pipeline coverage", examples: ["pipeline_coverage_x"] }],
|
|
227
|
-
manifest: MANIFEST,
|
|
228
|
-
overrides: { pipeline: { target: 3.5, tolerance: 0.2, cadence: "monthly", direction: "up" } },
|
|
229
|
-
});
|
|
230
|
-
const obj = r.objectives.find((o) => o.key === "pipeline");
|
|
231
|
-
assert.equal(obj.target, 3.5);
|
|
232
|
-
assert.equal(obj.tolerance, 0.2);
|
|
233
|
-
assert.equal(obj.cadence, "monthly");
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
test("toMandateBody produces the shape the cache and compiler consume", () => {
|
|
237
|
-
const r = deriveMandate({ charterSections: CHARTER, kpiCategories: [{ id: "pipeline", name: "Pipeline" }] });
|
|
238
|
-
const body = toMandateBody({ memberId: "mem_self", objectives: r.objectives });
|
|
239
|
-
assert.equal(body.memberId, "mem_self");
|
|
240
|
-
assert.ok(Array.isArray(body.objectives));
|
|
241
|
-
// NOT a number. A locally-derived body is the OFFLINE fallback, and an offline
|
|
242
|
-
// agent cannot know what it is funded for. This assertion used to demand a
|
|
243
|
-
// number and got the hardcoded 500 that made "I never asked" look exactly like
|
|
244
|
-
// "I asked and was told 500". Null is the true answer, and it must be PRESENT.
|
|
245
|
-
assert.ok(Object.prototype.hasOwnProperty.call(body, "budgetCentsPerPeriod"));
|
|
246
|
-
assert.equal(body.budgetCentsPerPeriod, null);
|
|
247
|
-
assert.ok(body.degradations.some((d) => d.field === "budgetCentsPerPeriod"));
|
|
248
|
-
assert.ok(Array.isArray(body.reactsTo));
|
|
249
|
-
// And it must round-trip through the validator the loop uses.
|
|
250
|
-
assert.equal(validateMandateBody(body).ok, true);
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
test("a bare ENTRY ARRAY manifest is tolerated, not a crash", () => {
|
|
254
|
-
// `[].entries` is Array.prototype.entries — a function. Walking it naively
|
|
255
|
-
// threw "function is not iterable" and took the whole derivation with it.
|
|
256
|
-
const fromObject = deriveMandate({
|
|
257
|
-
charterSections: CHARTER,
|
|
258
|
-
kpiCategories: [{ id: "pipeline", name: "Pipeline coverage", examples: ["pipeline_coverage_x"] }],
|
|
259
|
-
manifest: MANIFEST,
|
|
260
|
-
});
|
|
261
|
-
const fromArray = deriveMandate({
|
|
262
|
-
charterSections: CHARTER,
|
|
263
|
-
kpiCategories: [{ id: "pipeline", name: "Pipeline coverage", examples: ["pipeline_coverage_x"] }],
|
|
264
|
-
manifest: ENTRIES,
|
|
265
|
-
});
|
|
266
|
-
assert.deepEqual(fromArray.objectives, fromObject.objectives);
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
test("toMandateBody tolerates junk without throwing", () => {
|
|
270
|
-
const body = toMandateBody({});
|
|
271
|
-
assert.equal(body.memberId, null);
|
|
272
|
-
assert.deepEqual(body.objectives, []);
|
|
273
|
-
assert.equal(validateMandateBody(body).ok, true);
|
|
274
|
-
});
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* model.test.mjs — the pillar spine: validation, provenance, anti-Goodhart.
|
|
3
|
-
* Run: `node --test lib/mandate/model.test.mjs`
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
"use strict";
|
|
7
|
-
|
|
8
|
-
import { test } from "node:test";
|
|
9
|
-
import assert from "node:assert/strict";
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
objectivesOf,
|
|
13
|
-
validateMandateBody,
|
|
14
|
-
buildTree,
|
|
15
|
-
ancestry,
|
|
16
|
-
pillarFor,
|
|
17
|
-
provenanceOf,
|
|
18
|
-
adoptedObjectives,
|
|
19
|
-
isSelfAdopted,
|
|
20
|
-
collaboratorsOf,
|
|
21
|
-
} from "./model.mjs";
|
|
22
|
-
|
|
23
|
-
const ME = "mem_self";
|
|
24
|
-
const BOSS = "mem_boss";
|
|
25
|
-
|
|
26
|
-
function tree(over = {}) {
|
|
27
|
-
return {
|
|
28
|
-
memberId: ME,
|
|
29
|
-
objectives: [
|
|
30
|
-
{ key: "growth", kind: "PILLAR", parentKey: null, charterSectionId: "cs_12", state: "active", adoptedById: BOSS, source: { origin: "charter", ref: "cs_12" } },
|
|
31
|
-
{
|
|
32
|
-
key: "pipeline",
|
|
33
|
-
kind: "OBJECTIVE",
|
|
34
|
-
parentKey: "growth",
|
|
35
|
-
state: "active",
|
|
36
|
-
metric: "pipeline_coverage_x",
|
|
37
|
-
target: 3,
|
|
38
|
-
tolerance: 0.2,
|
|
39
|
-
direction: "up",
|
|
40
|
-
cadence: "weekly",
|
|
41
|
-
sensor: { capability: "crm_list_deals", params: {}, source: "method" },
|
|
42
|
-
adoptedById: BOSS,
|
|
43
|
-
collaborators: [{ memberId: "mem_peer", role: "REVIEWER" }],
|
|
44
|
-
...over.pipeline,
|
|
45
|
-
},
|
|
46
|
-
{ key: "win-rate", kind: "GOAL", parentKey: "pipeline", state: "proposed", metric: "win_rate", target: null, cadence: "monthly" },
|
|
47
|
-
],
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
test("objectivesOf accepts a cache record, a bare body, or an array", () => {
|
|
52
|
-
const body = tree();
|
|
53
|
-
assert.equal(objectivesOf(body).length, 3);
|
|
54
|
-
assert.equal(objectivesOf({ body }).length, 3);
|
|
55
|
-
assert.equal(objectivesOf(body.objectives).length, 3);
|
|
56
|
-
assert.deepEqual(objectivesOf(null), []);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test("a well-formed tree validates and yields exactly the adopted, measurable nodes", () => {
|
|
60
|
-
const body = tree();
|
|
61
|
-
const v = validateMandateBody(body, { ownerMemberId: ME });
|
|
62
|
-
assert.equal(v.ok, true, v.errors.join("; "));
|
|
63
|
-
const adopted = adoptedObjectives(body, { ownerMemberId: ME, badKeys: v.badKeys });
|
|
64
|
-
assert.deepEqual(adopted.map((o) => o.key), ["pipeline"], "the PILLAR is not measurable; the GOAL is only proposed");
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test("ANTI-GOODHART: a seat that adopted its own objective is an ERROR and drives no work", () => {
|
|
68
|
-
const body = tree({ pipeline: { adoptedById: ME } });
|
|
69
|
-
const v = validateMandateBody(body, { ownerMemberId: ME });
|
|
70
|
-
assert.equal(v.ok, false);
|
|
71
|
-
assert.match(v.errors.join(" "), /self-adopted/);
|
|
72
|
-
assert.ok(v.badKeys.includes("pipeline"));
|
|
73
|
-
assert.deepEqual(adoptedObjectives(body, { ownerMemberId: ME }), [], "a self-adopted objective may never drive work");
|
|
74
|
-
assert.equal(isSelfAdopted(body.objectives[1], ME), true);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test("an active objective with no target / no metric fails closed", () => {
|
|
78
|
-
const body = tree({ pipeline: { target: null, metric: null } });
|
|
79
|
-
const v = validateMandateBody(body, { ownerMemberId: ME });
|
|
80
|
-
assert.equal(v.ok, false);
|
|
81
|
-
assert.match(v.errors.join(" "), /no metric/);
|
|
82
|
-
assert.match(v.errors.join(" "), /no numeric target/);
|
|
83
|
-
assert.ok(v.badKeys.includes("pipeline"));
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test("structural errors: bad kind, missing parent, illegal parentage, duplicate key, cycle", () => {
|
|
87
|
-
const v = validateMandateBody({
|
|
88
|
-
memberId: ME,
|
|
89
|
-
objectives: [
|
|
90
|
-
{ key: "a", kind: "WIDGET", state: "proposed" },
|
|
91
|
-
{ key: "b", kind: "GOAL", parentKey: "nope", state: "proposed" },
|
|
92
|
-
{ key: "c", kind: "PILLAR", parentKey: "d", state: "proposed" },
|
|
93
|
-
{ key: "d", kind: "PILLAR", parentKey: null, state: "proposed" },
|
|
94
|
-
{ key: "d", kind: "PILLAR", parentKey: null, state: "proposed" },
|
|
95
|
-
{ key: "x", kind: "OBJECTIVE", parentKey: "y", state: "proposed" },
|
|
96
|
-
{ key: "y", kind: "OBJECTIVE", parentKey: "x", state: "proposed" },
|
|
97
|
-
],
|
|
98
|
-
});
|
|
99
|
-
const joined = v.errors.join(" | ");
|
|
100
|
-
assert.match(joined, /unknown kind "WIDGET"/);
|
|
101
|
-
assert.match(joined, /parent "nope" not found/);
|
|
102
|
-
assert.match(joined, /PILLAR may not hang from a PILLAR/);
|
|
103
|
-
assert.match(joined, /duplicate key "d"/);
|
|
104
|
-
assert.match(joined, /cycle in the objective tree/);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
test("an active objective with no adoptedById WARNS (unverifiable) but is not an error", () => {
|
|
108
|
-
const body = tree({ pipeline: { adoptedById: null } });
|
|
109
|
-
const v = validateMandateBody(body, { ownerMemberId: ME });
|
|
110
|
-
assert.equal(v.ok, true, v.errors.join("; "));
|
|
111
|
-
assert.match(v.warnings.join(" "), /carries no adoptedById/);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
test("provenance walks node → objective → pillar → charter clause, offline", () => {
|
|
115
|
-
const body = tree();
|
|
116
|
-
assert.deepEqual(ancestry(body, "win-rate").map((n) => n.key), ["win-rate", "pipeline", "growth"]);
|
|
117
|
-
assert.equal(pillarFor(body, "win-rate").key, "growth");
|
|
118
|
-
const p = provenanceOf(body, "pipeline");
|
|
119
|
-
assert.deepEqual(p.chain, ["pipeline", "growth"]);
|
|
120
|
-
assert.equal(p.clause, "cs_12");
|
|
121
|
-
assert.equal(p.adoptedBy, BOSS);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
test("buildTree nests children and surfaces orphans as roots rather than dropping them", () => {
|
|
125
|
-
const roots = buildTree({ objectives: [
|
|
126
|
-
{ key: "p", kind: "PILLAR" },
|
|
127
|
-
{ key: "o", kind: "OBJECTIVE", parentKey: "p" },
|
|
128
|
-
{ key: "orphan", kind: "GOAL", parentKey: "vanished" },
|
|
129
|
-
] });
|
|
130
|
-
assert.deepEqual(roots.map((r) => r.key).sort(), ["orphan", "p"]);
|
|
131
|
-
assert.equal(roots.find((r) => r.key === "p").children[0].key, "o");
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
test("collaboratorsOf normalises strings, objects and casing", () => {
|
|
135
|
-
assert.deepEqual(collaboratorsOf({ collaborators: ["m1", { memberId: "m2", role: "reviewer" }, { id: "m3" }, null] }), [
|
|
136
|
-
{ memberId: "m1", role: "CONTRIBUTOR", source: null },
|
|
137
|
-
{ memberId: "m2", role: "REVIEWER", source: null },
|
|
138
|
-
{ memberId: "m3", role: "CONTRIBUTOR", source: null },
|
|
139
|
-
]);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
test("collaboratorsOf PRESERVES hq's provenance `source` on the edge", () => {
|
|
143
|
-
// hq's projectObjective stamps this, and goals/classify.mjs is the consumer:
|
|
144
|
-
// an APPROVER derived from `objective.adoptedById` is a standing governance
|
|
145
|
-
// edge, not per-task sign-off. Dropping `source` made every self-directed
|
|
146
|
-
// candidate classify NEEDS_REVIEW.
|
|
147
|
-
assert.deepEqual(
|
|
148
|
-
collaboratorsOf({
|
|
149
|
-
collaborators: [
|
|
150
|
-
{ memberId: "boss", role: "APPROVER", source: "objective.adoptedById" },
|
|
151
|
-
{ memberId: "peer", role: "REVIEWER" },
|
|
152
|
-
],
|
|
153
|
-
}),
|
|
154
|
-
[
|
|
155
|
-
{ memberId: "boss", role: "APPROVER", source: "objective.adoptedById" },
|
|
156
|
-
{ memberId: "peer", role: "REVIEWER", source: null },
|
|
157
|
-
]
|
|
158
|
-
);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
test("badKeys are excluded from adoptedObjectives so a broken node cannot drive work", () => {
|
|
162
|
-
const body = tree();
|
|
163
|
-
assert.deepEqual(adoptedObjectives(body, { ownerMemberId: ME, badKeys: ["pipeline"] }), []);
|
|
164
|
-
});
|