create-walle 0.10.1 → 0.10.2
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/README.md +4 -0
- package/lib/owner-write-safety.js +135 -0
- package/lib/public-runtime.js +13 -5
- package/lib/release-schema.json +3 -2
- package/lib/runtime-closure-policy.json +6 -4
- package/package.json +1 -1
- package/template/bin/deps-doctor.js +1 -1
- package/template/bin/node-bin.sh +11 -0
- package/template/coding-task-manager/api-prompts.js +10 -22
- package/template/coding-task-manager/bin/claude-hook.js +7 -3
- package/template/coding-task-manager/bin/claude-table-parity.js +307 -0
- package/template/coding-task-manager/bin/conversation-storage-auto-migration-lib.js +16 -1
- package/template/coding-task-manager/bin/conversation-storage-generation-evidence.js +9 -2
- package/template/coding-task-manager/bin/conversation-storage-service-entry-lib.js +16 -1
- package/template/coding-task-manager/bin/conversation-storage-walle-owner.js +247 -0
- package/template/coding-task-manager/bin/ctm-machine-pair.js +160 -0
- package/template/coding-task-manager/bin/lead-wait-census.js +1348 -0
- package/template/coding-task-manager/bin/repair-foreign-clone.js +564 -0
- package/template/coding-task-manager/bin/repair-resume-echo-titles.js +248 -0
- package/template/coding-task-manager/bin/write-lock-bench.js +111 -0
- package/template/coding-task-manager/db.js +705 -286
- package/template/coding-task-manager/git-utils.js +44 -0
- package/template/coding-task-manager/lib/adopted-host-redial.js +4 -0
- package/template/coding-task-manager/lib/agent-presets.js +15 -0
- package/template/coding-task-manager/lib/agent-title-apply.js +88 -0
- package/template/coding-task-manager/lib/agent-title-backfill-read.js +92 -0
- package/template/coding-task-manager/lib/api/host-stats.js +98 -0
- package/template/coding-task-manager/lib/api/ingress.js +35 -12
- package/template/coding-task-manager/lib/api/machine-pairing.js +285 -0
- package/template/coding-task-manager/lib/api/machine-pool.js +307 -0
- package/template/coding-task-manager/lib/api/machines.js +461 -0
- package/template/coding-task-manager/lib/api/misc-ops.js +20 -5
- package/template/coding-task-manager/lib/api/providers.js +53 -6
- package/template/coding-task-manager/lib/api/resume-from-store.js +25 -0
- package/template/coding-task-manager/lib/api/sessions.js +160 -8
- package/template/coding-task-manager/lib/api/transcript-history.js +17 -4
- package/template/coding-task-manager/lib/api/worktrees.js +38 -37
- package/template/coding-task-manager/lib/approval-drift.js +16 -1
- package/template/coding-task-manager/lib/approval-logical-frame.js +5 -1
- package/template/coding-task-manager/lib/approval-scope.js +68 -4
- package/template/coding-task-manager/lib/auth-context.js +32 -2
- package/template/coding-task-manager/lib/auth-rate-limit.js +41 -1
- package/template/coding-task-manager/lib/auth-rules.js +186 -45
- package/template/coding-task-manager/lib/broadcast-serialize.js +2 -1
- package/template/coding-task-manager/lib/browser-session-broker.js +9 -1
- package/template/coding-task-manager/lib/browser-use-adapter.js +33 -2
- package/template/coding-task-manager/lib/claude-markdown-ansi.js +27 -85
- package/template/coding-task-manager/lib/claude-session-title.js +6 -30
- package/template/coding-task-manager/lib/claude-table-layout.js +322 -0
- package/template/coding-task-manager/lib/client-diag-handler.js +5 -0
- package/template/coding-task-manager/lib/codex-config-guard.js +33 -3
- package/template/coding-task-manager/lib/codex-harness-qualification.js +12 -0
- package/template/coding-task-manager/lib/codex-launch-health.js +29 -2
- package/template/coding-task-manager/lib/codex-link-preparation.js +9 -8
- package/template/coding-task-manager/lib/codex-paths.js +2 -1
- package/template/coding-task-manager/lib/coding-agent-models.js +21 -30
- package/template/coding-task-manager/lib/coding-session-model-adapters.js +27 -2
- package/template/coding-task-manager/lib/coding-session-model-projection.js +5 -0
- package/template/coding-task-manager/lib/coding-session-model-runtime.js +1 -2
- package/template/coding-task-manager/lib/coding-session-model-state.js +10 -0
- package/template/coding-task-manager/lib/coding-session-model-sync-state.js +0 -1
- package/template/coding-task-manager/lib/conversation-import/cursor-guard-owner.js +10 -0
- package/template/coding-task-manager/lib/create/resolve-launch-params.js +8 -1
- package/template/coding-task-manager/lib/ctm-boot-lifecycle.js +17 -1
- package/template/coding-task-manager/lib/db-domain-tables.js +7 -0
- package/template/coding-task-manager/lib/db-domains.js +21 -2
- package/template/coding-task-manager/lib/db-owner-op-registry.js +7 -0
- package/template/coding-task-manager/lib/db-schema-source-files.js +1 -0
- package/template/coding-task-manager/lib/feature-manifest.js +1 -1
- package/template/coding-task-manager/lib/fs-watcher.js +3 -2
- package/template/coding-task-manager/lib/harness-history-transition.js +3 -3
- package/template/coding-task-manager/lib/headless-inflight-ops.js +203 -0
- package/template/coding-task-manager/lib/headless-missing-reply.js +42 -0
- package/template/coding-task-manager/lib/headless-term-service.js +55 -17
- package/template/coding-task-manager/lib/host-control-dial.js +167 -0
- package/template/coding-task-manager/lib/host-handoff-orchestrator.js +16 -2
- package/template/coding-task-manager/lib/host-handoff.js +51 -13
- package/template/coding-task-manager/lib/host-health-ledger.js +164 -12
- package/template/coding-task-manager/lib/host-self-truth-watchdog.js +4 -0
- package/template/coding-task-manager/lib/host-stats.js +328 -0
- package/template/coding-task-manager/lib/host-threadpool-evacuation.js +160 -0
- package/template/coding-task-manager/lib/host-threadpool-monitor.js +14 -1
- package/template/coding-task-manager/lib/hosted-input-dispatch.js +203 -0
- package/template/coding-task-manager/lib/hosted-sync-worker.js +113 -5
- package/template/coding-task-manager/lib/instance-id-ownership-manifest.js +4 -0
- package/template/coding-task-manager/lib/instance-manifest-runtime.js +3 -0
- package/template/coding-task-manager/lib/instance-manifest.js +13 -1
- package/template/coding-task-manager/lib/instance-sync-status.js +15 -0
- package/template/coding-task-manager/lib/launch-model-override.js +160 -1
- package/template/coding-task-manager/lib/launch-plan-worker.js +59 -2
- package/template/coding-task-manager/lib/lead-model-switch-runtime.js +57 -4
- package/template/coding-task-manager/lib/lead-model-switch.js +3 -2
- package/template/coding-task-manager/lib/lead-model-transition-store.js +61 -3
- package/template/coding-task-manager/lib/legacy-draft-cancellation-recovery.js +111 -0
- package/template/coding-task-manager/lib/login-tripwire.js +1 -0
- package/template/coding-task-manager/lib/machine-pairing.js +238 -0
- package/template/coding-task-manager/lib/machine-pool.js +409 -0
- package/template/coding-task-manager/lib/machine-registry.js +749 -0
- package/template/coding-task-manager/lib/machine-self.js +66 -0
- package/template/coding-task-manager/lib/main-thread-lint-exemptions.json +52 -4
- package/template/coding-task-manager/lib/media-journal-control-reader.js +473 -0
- package/template/coding-task-manager/lib/media-journal.js +84 -27
- package/template/coding-task-manager/lib/media-store-options.js +43 -15
- package/template/coding-task-manager/lib/migrations/v56-host-samples-and-machine-pool.js +76 -0
- package/template/coding-task-manager/lib/mirror-feed-sanitize.js +12 -3
- package/template/coding-task-manager/lib/model-proxy/anthropic-face.js +4 -0
- package/template/coding-task-manager/lib/model-proxy/chat-response-translators.js +3 -0
- package/template/coding-task-manager/lib/model-proxy/handoff.js +494 -0
- package/template/coding-task-manager/lib/model-proxy/lead-worker-picks.js +25 -0
- package/template/coding-task-manager/lib/model-proxy/native-lead-listener.js +61 -7
- package/template/coding-task-manager/lib/model-proxy/proxy-cli.js +200 -0
- package/template/coding-task-manager/lib/model-proxy/proxy-instance-record.js +257 -0
- package/template/coding-task-manager/lib/model-proxy/stream-accounting.js +205 -0
- package/template/coding-task-manager/lib/model-proxy/tokens.js +78 -0
- package/template/coding-task-manager/lib/model-proxy/upstream-outcome.js +9 -0
- package/template/coding-task-manager/lib/native-history-control.js +2 -2
- package/template/coding-task-manager/lib/native-lead-settings-client.js +2 -1
- package/template/coding-task-manager/lib/native-lead-tls.js +18 -4
- package/template/coding-task-manager/lib/oauth-proxy-supervisor.js +628 -64
- package/template/coding-task-manager/lib/owner-action-manifest-data.json +14 -0
- package/template/coding-task-manager/lib/painted-row-recorder.js +9 -1
- package/template/coding-task-manager/lib/prompt-submission-contract.js +21 -1
- package/template/coding-task-manager/lib/provider-login-broker.js +192 -9
- package/template/coding-task-manager/lib/provider-login-capture.js +153 -0
- package/template/coding-task-manager/lib/queue-draft-status-overlay.js +36 -0
- package/template/coding-task-manager/lib/raw-codex-history-window.js +5 -3
- package/template/coding-task-manager/lib/read-pool-client.js +5 -2
- package/template/coding-task-manager/lib/read-pool-op-registry.js +10 -1
- package/template/coding-task-manager/lib/recent-catalog-query.js +26 -0
- package/template/coding-task-manager/lib/recent-session-catalog-response.js +9 -0
- package/template/coding-task-manager/lib/remote-command-context.js +12 -0
- package/template/coding-task-manager/lib/remote-command-runtime.js +13 -1
- package/template/coding-task-manager/lib/remote-machine-catalog.js +1062 -0
- package/template/coding-task-manager/lib/remote-machine-create.js +693 -0
- package/template/coding-task-manager/lib/remote-machine-link.js +939 -0
- package/template/coding-task-manager/lib/remote-machine-proxy.js +520 -0
- package/template/coding-task-manager/lib/remote-session-forward.js +306 -0
- package/template/coding-task-manager/lib/remote-submission-status.js +6 -0
- package/template/coding-task-manager/lib/restart-outcome-truth.js +20 -3
- package/template/coding-task-manager/lib/restart-preflight.js +9 -2
- package/template/coding-task-manager/lib/restore-backoff-gate.js +10 -2
- package/template/coding-task-manager/lib/restore-prepare-retry.js +12 -5
- package/template/coding-task-manager/lib/restore-resume-batch.js +22 -1
- package/template/coding-task-manager/lib/resume-blocked-wire.js +62 -1
- package/template/coding-task-manager/lib/resume-cwd.js +13 -0
- package/template/coding-task-manager/lib/resume-from-store.js +12 -1
- package/template/coding-task-manager/lib/resume-origin-facts.js +38 -1
- package/template/coding-task-manager/lib/resume-preflight.js +21 -16
- package/template/coding-task-manager/lib/review-repo-admission.js +39 -6
- package/template/coding-task-manager/lib/review-repo-registry.js +17 -0
- package/template/coding-task-manager/lib/root-delivery-runtime.js +72 -15
- package/template/coding-task-manager/lib/root-delivery-selection.js +37 -1
- package/template/coding-task-manager/lib/root-prompt-admission.js +33 -2
- package/template/coding-task-manager/lib/root-prompt-flow.js +167 -33
- package/template/coding-task-manager/lib/scrollback-archive-store.js +7 -4
- package/template/coding-task-manager/lib/session-analysis-memory.js +2 -0
- package/template/coding-task-manager/lib/session-create/codex-native-provider-gate.js +218 -0
- package/template/coding-task-manager/lib/session-create/codex-resume-exit-classify.js +52 -0
- package/template/coding-task-manager/lib/session-create/delegate-worker.js +95 -6
- package/template/coding-task-manager/lib/session-create/identity.js +20 -1
- package/template/coding-task-manager/lib/session-create/prepared.js +69 -1
- package/template/coding-task-manager/lib/session-create/provider-launch-context.js +77 -0
- package/template/coding-task-manager/lib/session-create/provider-process.js +34 -37
- package/template/coding-task-manager/lib/session-create/response.js +30 -12
- package/template/coding-task-manager/lib/session-create/transcript.js +13 -4
- package/template/coding-task-manager/lib/session-create/worker-continuation.js +43 -1
- package/template/coding-task-manager/lib/session-create/worker-notices.js +94 -5
- package/template/coding-task-manager/lib/session-create/worker-observability.js +99 -7
- package/template/coding-task-manager/lib/session-create/worker-stop.js +11 -3
- package/template/coding-task-manager/lib/session-create/worker-wait-watcher.js +315 -0
- package/template/coding-task-manager/lib/session-geometry-authority-handler.js +10 -1
- package/template/coding-task-manager/lib/session-host-manager.js +152 -206
- package/template/coding-task-manager/lib/session-jobs.js +17 -9
- package/template/coding-task-manager/lib/session-lifecycle.js +18 -0
- package/template/coding-task-manager/lib/session-model-action-preconditions.js +9 -0
- package/template/coding-task-manager/lib/session-resource-sampler.js +19 -9
- package/template/coding-task-manager/lib/session-standup.js +67 -4
- package/template/coding-task-manager/lib/session-status-ledger.js +42 -3
- package/template/coding-task-manager/lib/session-status-runtime.js +45 -1
- package/template/coding-task-manager/lib/session-title-mirror.js +25 -57
- package/template/coding-task-manager/lib/session-transcript-trash.js +8 -1
- package/template/coding-task-manager/lib/startup-pointer-write.js +57 -0
- package/template/coding-task-manager/lib/state-sync/painted-frame-field.js +11 -0
- package/template/coding-task-manager/lib/storage/coding-session-model-store.js +21 -22
- package/template/coding-task-manager/lib/surviving-owner-bootstrap.js +7 -2
- package/template/coding-task-manager/lib/surviving-owner-recovery.js +2 -1
- package/template/coding-task-manager/lib/terminal-draft-http-status.js +25 -0
- package/template/coding-task-manager/lib/transcript-archiver.js +229 -229
- package/template/coding-task-manager/lib/transcript-frame-cache.js +12 -2
- package/template/coding-task-manager/lib/transcript-guard-job.js +55 -4
- package/template/coding-task-manager/lib/transcript-link.js +77 -0
- package/template/coding-task-manager/lib/transcript-manifest-store.js +8 -4
- package/template/coding-task-manager/lib/transcript-source.js +8 -3
- package/template/coding-task-manager/lib/transcript-store.js +40 -0
- package/template/coding-task-manager/lib/tunnel-lane-governor.js +6 -3
- package/template/coding-task-manager/lib/typing-interaction-trace.js +92 -0
- package/template/coding-task-manager/lib/typing-worker-stages.js +60 -0
- package/template/coding-task-manager/lib/visible-revision-watchdog.js +8 -12
- package/template/coding-task-manager/lib/wake-skip-reasons.js +46 -0
- package/template/coding-task-manager/lib/walle-supervisor.js +98 -14
- package/template/coding-task-manager/lib/worker-failure-classify.js +13 -1
- package/template/coding-task-manager/lib/ws/state-sync.js +19 -2
- package/template/coding-task-manager/lib/ws/terminal-attach.js +32 -1
- package/template/coding-task-manager/lib/ws-router.js +68 -1
- package/template/coding-task-manager/lib/ws-send-admission.js +22 -4
- package/template/coding-task-manager/oauth-proxy.js +99 -55
- package/template/coding-task-manager/package-lock.json +24 -0
- package/template/coding-task-manager/package.json +3 -0
- package/template/coding-task-manager/providers/codex-mcp-extract.js +121 -19
- package/template/coding-task-manager/providers/codex-mcp.js +14 -5
- package/template/coding-task-manager/public/css/app.css +279 -1
- package/template/coding-task-manager/public/css/reviews.css +4 -0
- package/template/coding-task-manager/public/index.html +294 -223
- package/template/coding-task-manager/public/js/app/machines-page.js +1252 -0
- package/template/coding-task-manager/public/js/app/models-tab.js +546 -36
- package/template/coding-task-manager/public/js/app/worktrees-panel.js +7 -5
- package/template/coding-task-manager/public/js/background-work.js +13 -7
- package/template/coding-task-manager/public/js/coding-session-model-presentation.js +44 -4
- package/template/coding-task-manager/public/js/core/session-commands.js +676 -0
- package/template/coding-task-manager/public/js/core/session-selectors.js +5 -1
- package/template/coding-task-manager/public/js/history-app-seam.js +26 -2
- package/template/coding-task-manager/public/js/history-painted-rows.js +17 -2
- package/template/coding-task-manager/public/js/history-reader-park.js +13 -1
- package/template/coding-task-manager/public/js/history-scroller.js +187 -9
- package/template/coding-task-manager/public/js/history-transcript-pages.js +5 -5
- package/template/coding-task-manager/public/js/machine-pool-shared.js +436 -0
- package/template/coding-task-manager/public/js/private-state-scope.js +31 -3
- package/template/coding-task-manager/public/js/prompt-submission-client.js +173 -8
- package/template/coding-task-manager/public/js/prompt-submission-tray.js +281 -0
- package/template/coding-task-manager/public/js/queue-panel-submission-reconcile.js +174 -1
- package/template/coding-task-manager/public/js/reviews.js +7 -3
- package/template/coding-task-manager/public/js/root-draft-lifetime.js +139 -2
- package/template/coding-task-manager/public/js/root-queue-builder-lifetime.js +16 -0
- package/template/coding-task-manager/public/js/row-join.js +4 -2
- package/template/coding-task-manager/public/js/session-model-picker.js +1 -1
- package/template/coding-task-manager/public/js/session-search-utils.js +46 -19
- package/template/coding-task-manager/public/js/state-sync-client.js +4 -0
- package/template/coding-task-manager/public/js/terminal/runtime-02.js +16 -7
- package/template/coding-task-manager/public/js/terminal/runtime-03.js +41 -0
- package/template/coding-task-manager/public/js/terminal/runtime-04.js +41 -4
- package/template/coding-task-manager/public/js/terminal/runtime-12.js +52 -2
- package/template/coding-task-manager/public/js/terminal/runtime-16.js +12 -12
- package/template/coding-task-manager/public/js/terminal/runtime-18.js +14 -0
- package/template/coding-task-manager/public/js/terminal/runtime-19.js +8 -4
- package/template/coding-task-manager/public/js/terminal/runtime-20.js +210 -73
- package/template/coding-task-manager/public/js/terminal/runtime-21.js +19 -6
- package/template/coding-task-manager/public/js/terminal/runtime-22.js +86 -0
- package/template/coding-task-manager/public/js/terminal-history-viewport.js +1 -1
- package/template/coding-task-manager/public/js/terminal-wheel.js +39 -2
- package/template/coding-task-manager/public/js/walle/chat-conversation.js +11 -3
- package/template/coding-task-manager/public/js/walle-session.js +30 -7
- package/template/coding-task-manager/public/m/app.css +226 -8
- package/template/coding-task-manager/public/m/app.js +502 -9
- package/template/coding-task-manager/public/m/index.html +38 -1
- package/template/coding-task-manager/public/m/machines.js +1313 -0
- package/template/coding-task-manager/public/m/pool-failover.js +391 -0
- package/template/coding-task-manager/public/m/private-queue-actions.js +26 -7
- package/template/coding-task-manager/public/m/sw.js +5 -0
- package/template/coding-task-manager/queue-engine.js +13 -0
- package/template/coding-task-manager/server/approvals.js +5 -6
- package/template/coding-task-manager/server/boot.js +136 -17
- package/template/coding-task-manager/server.js +902 -239
- package/template/coding-task-manager/session-integrity.js +7 -9
- package/template/coding-task-manager/workers/db-owner-worker.js +53 -7
- package/template/coding-task-manager/workers/read-pool-worker.js +17 -6
- package/template/coding-task-manager/workers/session-host-pool-process.js +18 -4
- package/template/coding-task-manager/workers/session-host-process.js +14 -3
- package/template/coding-task-manager/workers/transcript-archive-worker.js +1 -1
- package/template/coding-task-manager/workers/worktree-worker.js +34 -0
- package/template/package-lock.json +2 -2
- package/template/package.json +1 -1
- package/template/scripts/conversation-storage-v2-migration.js +78 -11
- package/template/shared/conversation-storage-v2-contract.js +42 -6
- package/template/shared/fixtures/worker-status-payloads.json +392 -0
- package/template/shared/git-remote-key.js +83 -0
- package/template/shared/host-identity.js +98 -2
- package/template/shared/owner-write-safety.js +135 -0
- package/template/shared/public-runtime.js +13 -5
- package/template/shared/release-schema.json +3 -2
- package/template/shared/report-citations.js +14 -0
- package/template/shared/sqlite-write-lock.js +361 -223
- package/template/shared/storage/contract.js +35 -3
- package/template/wall-e/agent.js +21 -0
- package/template/wall-e/api-walle.js +5 -0
- package/template/wall-e/bin/brain-purge-legacy-ctm.js +3 -2
- package/template/wall-e/bin/walle-mcp-stdio.js +12 -6
- package/template/wall-e/brain/helper-rules-schema.js +129 -0
- package/template/wall-e/brain/migrations.js +51 -22
- package/template/wall-e/brain/model-deployment-facts.js +31 -1
- package/template/wall-e/brain/owner-call-whitelist.js +16 -0
- package/template/wall-e/brain/schema-ensures.js +22 -0
- package/template/wall-e/brain.js +1 -1
- package/template/wall-e/coding/journal-dispatcher.js +51 -0
- package/template/wall-e/coding/session-journal.js +28 -40
- package/template/wall-e/coding-orchestrator.js +23 -26
- package/template/wall-e/http/auth.js +18 -1
- package/template/wall-e/http/blog-views-api.js +318 -0
- package/template/wall-e/http/helper-rules-api.js +321 -0
- package/template/wall-e/http/model-proxy-providers.js +3 -1
- package/template/wall-e/http/telemetry-api.js +12 -0
- package/template/wall-e/lib/blog-view-policy.js +88 -0
- package/template/wall-e/lib/blog-view-store.js +169 -0
- package/template/wall-e/lib/client-config-lock.js +2 -1
- package/template/wall-e/lib/coding-journal-writer-child-client.js +67 -18
- package/template/wall-e/lib/error-kinds.js +10 -1
- package/template/wall-e/lib/feature-manifest.js +1 -1
- package/template/wall-e/lib/helper-rules/cell-state.js +123 -0
- package/template/wall-e/lib/helper-rules/evaluate.js +357 -0
- package/template/wall-e/lib/helper-rules/explore-rubric.md +12 -0
- package/template/wall-e/lib/helper-rules/grade.js +277 -0
- package/template/wall-e/lib/helper-rules/label.js +247 -0
- package/template/wall-e/lib/helper-rules/lead-call-hook.js +522 -0
- package/template/wall-e/lib/helper-rules/store.js +667 -0
- package/template/wall-e/lib/mcp-integration.js +5 -6
- package/template/wall-e/lib/runtime-worker-pool.js +1 -0
- package/template/wall-e/lib/scheduler-worker-jobs.js +6 -0
- package/template/wall-e/lib/telemetry-db-schema.js +37 -0
- package/template/wall-e/lib/telemetry-process-client.js +76 -1
- package/template/wall-e/llm/default-fallback.js +4 -0
- package/template/wall-e/llm/model-catalog.js +13 -4
- package/template/wall-e/llm/model-roles.js +6 -0
- package/template/wall-e/llm/portkey-http.js +150 -0
- package/template/wall-e/llm/portkey-sync.js +15 -145
- package/template/wall-e/llm/worker-mcp-routing.js +61 -0
- package/template/wall-e/llm/worker-route.js +43 -1
- package/template/wall-e/loops/brain-custom-writes.js +45 -0
- package/template/wall-e/mcp-protocol-contract.js +41 -1
- package/template/wall-e/mcp-server.js +13 -2
- package/template/wall-e/mcp-worker-schemas.js +20 -2
- package/template/wall-e/memory/ctm-context-client.js +25 -5
- package/template/wall-e/shared/release-schema.json +3 -2
- package/template/wall-e/shared/sqlite-write-lock.js +361 -223
- package/template/wall-e/shared/storage/contract.js +35 -3
- package/template/wall-e/skills/_templates/delegate-worker/VERSION +1 -1
- package/template/wall-e/skills/_templates/delegate-worker/released-sha256.json +6 -0
- package/template/wall-e/skills/_templates/delegate-worker/templates/claude/SKILL.md +7 -2
- package/template/wall-e/skills/_templates/delegate-worker/templates/codex/SKILL.md +7 -2
- package/template/wall-e/tools/local-tools.js +6 -4
- package/template/wall-e/workers/brain-owner-worker.js +9 -0
- package/template/wall-e/workers/coding-journal-writer-child.js +8 -1
- package/template/wall-e/workers/telemetry-worker.js +8 -0
|
@@ -20,7 +20,6 @@ const SAFE_ENV_KEYS = [
|
|
|
20
20
|
'WALL_E_SQLITE_STORAGE_POLICY',
|
|
21
21
|
'WALL_E_SQLITE_BUSY_TIMEOUT_MS',
|
|
22
22
|
'WALL_E_SQLITE_WRITE_LOCK_TIMEOUT_MS',
|
|
23
|
-
'WALL_E_SQLITE_WRITE_LOCK_STALE_MS',
|
|
24
23
|
'WALL_E_BRAIN_BOOT_LOCK_WAIT_MS',
|
|
25
24
|
'WALL_E_SQLITE_SYNCHRONOUS',
|
|
26
25
|
'WALL_E_SQLITE_WAL_AUTOCHECKPOINT_PAGES',
|
|
@@ -82,16 +81,48 @@ function writeJournalEntryInChild(entry, options = {}) {
|
|
|
82
81
|
serialization: 'json',
|
|
83
82
|
});
|
|
84
83
|
} catch (error) {
|
|
84
|
+
// C7: a fork that threw synchronously returned no child, so this call owns
|
|
85
|
+
// nothing to watch and nothing to terminate — reject with the original value.
|
|
85
86
|
return Promise.reject(error);
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
return new Promise((resolve, reject) => {
|
|
90
|
+
// C7: the application result (one reply envelope) and the native lifecycle
|
|
91
|
+
// (exit/close) are two separate facts about the same child. Only the child's
|
|
92
|
+
// own 'close' finalizes this operation — a reply envelope or 'exit' alone
|
|
93
|
+
// leaves the operation pending, because the child may still be running (or
|
|
94
|
+
// may still die badly) after either one.
|
|
95
|
+
let replied = false;
|
|
96
|
+
let replyResult = null;
|
|
97
|
+
let failed = false; // explicit Boolean: a falsy thrown value is still a failure
|
|
98
|
+
let failure = null;
|
|
99
|
+
let nativeExited = false; // 'exit' seen: the process is gone, its stdio is not necessarily closed
|
|
100
|
+
let nativeClosed = false; // 'close' seen: the only fact that finalizes this operation
|
|
101
|
+
let terminationRequested = false;
|
|
89
102
|
let settled = false;
|
|
103
|
+
|
|
104
|
+
const recordFailure = (error) => {
|
|
105
|
+
// The FIRST failure wins, verbatim (identity and value): later errors, the
|
|
106
|
+
// deadline and a nonzero native close must never replace it.
|
|
107
|
+
if (failed) return;
|
|
108
|
+
failed = true;
|
|
109
|
+
failure = error;
|
|
110
|
+
};
|
|
111
|
+
const requestTermination = () => {
|
|
112
|
+
if (terminationRequested) return;
|
|
113
|
+
// Marked BEFORE kill: an 'error' emitted by the kill call itself must not
|
|
114
|
+
// re-enter here and signal the same child twice.
|
|
115
|
+
terminationRequested = true;
|
|
116
|
+
if (nativeExited || nativeClosed) return; // never signal after the native exit/close was observed
|
|
117
|
+
try { child.kill('SIGKILL'); }
|
|
118
|
+
catch { /* expected: the child may already be gone; a throwing kill neither relinquishes ownership nor proves closure — the operation still joins the child's actual close below */ }
|
|
119
|
+
};
|
|
90
120
|
const cleanup = () => {
|
|
91
121
|
clearTimeout(timer);
|
|
92
122
|
child.off('message', onMessage);
|
|
93
123
|
child.off('error', onError);
|
|
94
124
|
child.off('exit', onExit);
|
|
125
|
+
child.off('close', onClose);
|
|
95
126
|
};
|
|
96
127
|
const settle = (fn, value) => {
|
|
97
128
|
if (settled) return false;
|
|
@@ -101,39 +132,57 @@ function writeJournalEntryInChild(entry, options = {}) {
|
|
|
101
132
|
return true;
|
|
102
133
|
};
|
|
103
134
|
const onMessage = (message) => {
|
|
104
|
-
if (!message || message.type !== 'coding-journal-result') return;
|
|
105
|
-
if (
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
135
|
+
if (!message || message.type !== 'coding-journal-result') return; // unrelated envelope
|
|
136
|
+
if (replied) return; // first valid result wins; duplicate and late envelopes are ignored
|
|
137
|
+
replied = true;
|
|
138
|
+
if (message.ok) { replyResult = message.result || null; return; }
|
|
139
|
+
const error = new Error(String(message.error || 'One-shot journal writer failed').slice(0, 500));
|
|
140
|
+
if (message.code) error.code = String(message.code).slice(0, 100);
|
|
141
|
+
recordFailure(error);
|
|
142
|
+
};
|
|
143
|
+
const onError = (error) => {
|
|
144
|
+
// A process/IPC error is a failure but not a closure: the operation keeps
|
|
145
|
+
// waiting for the child's own close, and this first failure is preserved.
|
|
146
|
+
recordFailure(error);
|
|
147
|
+
requestTermination();
|
|
111
148
|
};
|
|
112
|
-
|
|
113
|
-
|
|
149
|
+
// 'exit' only records that the process is gone; the verdict reads the close
|
|
150
|
+
// event's own (code, signal), so exit and close can never be conflated.
|
|
151
|
+
const onExit = () => { nativeExited = true; };
|
|
152
|
+
const onClose = (code, signal) => {
|
|
153
|
+
nativeClosed = true;
|
|
154
|
+
// A falsy thrown value must not be laundered into a success: the boolean,
|
|
155
|
+
// not the value, says whether a failure happened.
|
|
156
|
+
if (failed) { settle(reject, failure); return; }
|
|
114
157
|
const suffix = signal ? `signal ${signal}` : `code ${code}`;
|
|
115
|
-
settle(reject, new Error(`One-shot journal writer exited before replying (${suffix})`));
|
|
158
|
+
if (!replied) { settle(reject, new Error(`One-shot journal writer exited before replying (${suffix})`)); return; }
|
|
159
|
+
if (code === 0 && signal === null) { settle(resolve, replyResult); return; }
|
|
160
|
+
// Replied, then died badly: the reply alone is not proof the write survived.
|
|
161
|
+
const error = new Error(`One-shot journal writer exited after replying (${suffix})`);
|
|
162
|
+
error.code = 'WALL_E_JOURNAL_WRITER_EXIT';
|
|
163
|
+
settle(reject, error);
|
|
116
164
|
};
|
|
117
165
|
const timer = setTimeout(() => {
|
|
118
166
|
const error = new Error(`One-shot journal writer timed out after ${timeoutMs}ms`);
|
|
119
167
|
error.code = 'WALL_E_JOURNAL_WRITER_TIMEOUT';
|
|
120
|
-
|
|
121
|
-
|
|
168
|
+
recordFailure(error); // only the first failure is kept; the deadline joins the close
|
|
169
|
+
requestTermination();
|
|
122
170
|
}, timeoutMs);
|
|
123
171
|
|
|
124
172
|
child.on('message', onMessage);
|
|
125
|
-
child.
|
|
126
|
-
child.
|
|
173
|
+
child.on('error', onError);
|
|
174
|
+
child.on('exit', onExit);
|
|
175
|
+
child.on('close', onClose);
|
|
127
176
|
try {
|
|
128
177
|
child.send({
|
|
129
178
|
type: 'persist-coding-journal',
|
|
130
179
|
dbPath: path.resolve(dbPath),
|
|
131
180
|
entry: JSON.parse(entryJson),
|
|
132
181
|
});
|
|
182
|
+
// A false return is IPC backpressure, never a failure or a cancellation.
|
|
133
183
|
} catch (error) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
184
|
+
recordFailure(error);
|
|
185
|
+
requestTermination();
|
|
137
186
|
}
|
|
138
187
|
});
|
|
139
188
|
}
|
|
@@ -87,10 +87,19 @@ const CODE_KIND = Object.freeze({
|
|
|
87
87
|
ANTHROPIC_WIRE_TOOL_NAME_INVALID: 'FATAL',
|
|
88
88
|
// shared/sqlite-write-lock.js: process-liveness / lock-file-existence control-flow codes, not
|
|
89
89
|
// genuine transient failures a retry loop should chase — the lock module already has its own
|
|
90
|
-
//
|
|
90
|
+
// foreign-hold handling around these (protocol 2 has no stale window: a dead holder's kernel
|
|
91
|
+
// lock is dropped by the OS, D-4). EISDIR is the same family as EEXIST: an object at the record
|
|
92
|
+
// path that is not our record file (the fail-fast fixtures use a directory) is somebody else's
|
|
93
|
+
// claim, and SQLITE_WRITE_LOCK_STATE is the lock DB found inside a transaction — a bug state,
|
|
94
|
+
// never a race worth retrying.
|
|
91
95
|
ESRCH: 'FATAL',
|
|
92
96
|
ENOENT: 'FATAL',
|
|
93
97
|
EEXIST: 'FATAL',
|
|
98
|
+
EISDIR: 'FATAL',
|
|
99
|
+
SQLITE_WRITE_LOCK_STATE: 'FATAL',
|
|
100
|
+
// BEGIN EXCLUSIVE on the lock DB lost the race for the EXCLUSIVE lock: contention, exactly like
|
|
101
|
+
// SQLITE_WRITE_LOCK_BUSY above, and won by the same later attempt.
|
|
102
|
+
SQLITE_BUSY: 'WRITE_LOCK_BUSY',
|
|
94
103
|
CTM_MAIN_WRITE_ESCAPE: 'FATAL',
|
|
95
104
|
CTM_MAIN_THREAD_WRITE_FORBIDDEN: 'FATAL',
|
|
96
105
|
// D-7 (WP-2): a loop wrote directly to a brain handle this process opened
|
|
@@ -265,7 +265,7 @@ const FEATURES = [
|
|
|
265
265
|
// numeric-tunable suffix/read shape; 1 site(s); first: wall-e/brain.js:1800
|
|
266
266
|
{ id: "walle_wall_e_sqlite_wal_autocheckpoint_pages", env: "WALL_E_SQLITE_WAL_AUTOCHECKPOINT_PAGES", owner: "wall-e-core-team", stage: "ops", tunable: true, default: 10, since: "2026-07-02", expires: null, note: "numeric tunable" },
|
|
267
267
|
// numeric-tunable suffix/read shape; 1 site(s); first: wall-e/brain.js:1769
|
|
268
|
-
{ id: "walle_wall_e_sqlite_write_lock_stale_ms", env: "WALL_E_SQLITE_WRITE_LOCK_STALE_MS", owner: "wall-e-core-team", stage: "
|
|
268
|
+
{ id: "walle_wall_e_sqlite_write_lock_stale_ms", env: "WALL_E_SQLITE_WRITE_LOCK_STALE_MS", owner: "wall-e-core-team", stage: "removed", defaultOn: false, since: "2026-05-21", expires: null, note: "retired 2026-10-23 (fsevents-churn WP-1, coding-task-manager/docs/fsevents-churn-kernel-write-lock-2026-10-23.html D-4): the SQLite write lock is a kernel lock on a .mutex lock DB released by the OS when its holder dies, so stale-claim recovery and its age knob were deleted; no read of this name survives." },
|
|
269
269
|
// numeric-tunable suffix/read shape; 1 site(s); first: wall-e/skills/script-skill-runner.js:64
|
|
270
270
|
{ id: "walle_wall_e_sqlite_write_lock_timeout_ms", env: "WALL_E_SQLITE_WRITE_LOCK_TIMEOUT_MS", owner: "wall-e-core-team", stage: "ops", tunable: true, default: 0, since: "2026-05-21", expires: null, note: "numeric tunable" },
|
|
271
271
|
// bare truthy opt-in read; 1 site(s); first: wall-e/agent.js:55
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// WP-4 (lead-cost-routing-round1-2026-10-21.html §6.4): the ONE reader of a lead harness's
|
|
4
|
+
// `explore` rule cell, plus the transport-facing plumbing both MCP transports need to gate the
|
|
5
|
+
// exploration hand-off block appended to the `initialize` instructions.
|
|
6
|
+
//
|
|
7
|
+
// The cell is the row `cell_key = 'explore|default|<agent>'` in helper_rule_cells. WP-5 owns the
|
|
8
|
+
// table's migration; this module NEVER creates it. A missing table, a missing row or any read
|
|
9
|
+
// error means "never switched off" -- the state is 'on' (fail-open, the same shape as an absent
|
|
10
|
+
// flag), so a brain built before WP-5 keeps serving today's guidance.
|
|
11
|
+
//
|
|
12
|
+
// States: 'on' (block appended), 'probe' (block appended, WP-5 owns what probe means) and 'off'
|
|
13
|
+
// (plain SERVER_INSTRUCTIONS). Only those three values are recognized; anything else reads 'on'.
|
|
14
|
+
|
|
15
|
+
const EXPLORE_STATES = Object.freeze(['on', 'off', 'probe']);
|
|
16
|
+
const DEFAULT_EXPLORE_STATE = 'on';
|
|
17
|
+
const GUIDANCE_PATH = '/api/wall-e/helper-rules/guidance';
|
|
18
|
+
// The stdio proxy blocks its `initialize` reply on this fetch, so the deadline is short and the
|
|
19
|
+
// failure mode is the fail-open default: a slow or dead daemon may not stall a client handshake.
|
|
20
|
+
const GUIDANCE_TIMEOUT_MS = 1500;
|
|
21
|
+
|
|
22
|
+
function readExploreCellState(db, agent = 'claude') {
|
|
23
|
+
if (!db || typeof db.prepare !== 'function') return DEFAULT_EXPLORE_STATE;
|
|
24
|
+
try {
|
|
25
|
+
const row = db.prepare('SELECT state FROM helper_rule_cells WHERE cell_key = ?')
|
|
26
|
+
.get(`explore|default|${agent}`);
|
|
27
|
+
const state = row && typeof row.state === 'string' ? row.state.trim() : '';
|
|
28
|
+
return EXPLORE_STATES.includes(state) ? state : DEFAULT_EXPLORE_STATE;
|
|
29
|
+
} catch {
|
|
30
|
+
// expected: helper_rule_cells is absent until WP-5's migration runs; a missing row means 'on'
|
|
31
|
+
return DEFAULT_EXPLORE_STATE;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function leadAgentFromClientInfo(clientInfo) {
|
|
36
|
+
return /codex/i.test(clientInfo?.name) ? 'codex' : 'claude';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// The daemon transport's one call: mcp-server.js already holds the brain module, and its getDb()
|
|
40
|
+
// throws before initDb() -- which must not break the initialize reply, so the read is guarded here
|
|
41
|
+
// rather than at the call site (the call site stays a single line, see mcp-server.js).
|
|
42
|
+
function leadGuidanceForMessage(message, brainModule) {
|
|
43
|
+
// Only `initialize` carries instructions; every other MCP message skips the read entirely
|
|
44
|
+
// (an undefined leadGuidance leaves handleProtocolMessage on its no-option path).
|
|
45
|
+
if (!message || message.method !== 'initialize') return undefined;
|
|
46
|
+
let db = null;
|
|
47
|
+
try {
|
|
48
|
+
db = brainModule && typeof brainModule.getDb === 'function' ? brainModule.getDb() : brainModule;
|
|
49
|
+
} catch {
|
|
50
|
+
// expected: brain not initialized yet (initialize can arrive before/without a DB) -- 'on'
|
|
51
|
+
db = null;
|
|
52
|
+
}
|
|
53
|
+
return { explore: readExploreCellState(db, leadAgentFromClientInfo(message?.params?.clientInfo)) };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function guidancePathFor(agent) {
|
|
57
|
+
return `${GUIDANCE_PATH}?agent=${encodeURIComponent(agent)}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// The read-only GET the stdio transport performs against the daemon (the stdio process does not
|
|
61
|
+
// own the brain). Kept here so bin/walle-mcp-stdio.js -- a frozen-ceiling file -- gains no logic.
|
|
62
|
+
// `host`/`port` arrive from the caller: the transport owns endpoint resolution (no env read here).
|
|
63
|
+
function httpJsonGet({ host = '127.0.0.1', port, path: requestPath, timeoutMs = GUIDANCE_TIMEOUT_MS } = {}) {
|
|
64
|
+
return new Promise((resolve, reject) => {
|
|
65
|
+
const req = require('node:http').request({ host, port, path: requestPath, method: 'GET', timeout: timeoutMs }, (res) => {
|
|
66
|
+
let raw = '';
|
|
67
|
+
res.setEncoding('utf8');
|
|
68
|
+
res.on('data', (chunk) => { raw += chunk; });
|
|
69
|
+
res.on('end', () => {
|
|
70
|
+
if (res.statusCode !== 200) {
|
|
71
|
+
reject(new Error(`helper-rules guidance returned HTTP ${res.statusCode}`));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
resolve(JSON.parse(raw));
|
|
76
|
+
} catch (err) {
|
|
77
|
+
reject(err);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
res.on('error', reject);
|
|
81
|
+
});
|
|
82
|
+
req.on('timeout', () => { req.destroy(new Error(`helper-rules guidance timed out after ${timeoutMs}ms`)); });
|
|
83
|
+
req.on('error', reject);
|
|
84
|
+
req.end();
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// requestFn is the transport's own request machinery: it is handed the guidance path and returns
|
|
89
|
+
// the parsed JSON body (the stdio proxy passes its _executeDaemonRequest breaker wrapper around
|
|
90
|
+
// httpJsonGet). Every failure -- no requestFn, refused, non-2xx, malformed body, or the timeout --
|
|
91
|
+
// resolves to the fail-open default, so the transport always has an answer.
|
|
92
|
+
async function fetchExploreCellState({ requestFn, agent = 'claude', timeoutMs = GUIDANCE_TIMEOUT_MS } = {}) {
|
|
93
|
+
let timer = null;
|
|
94
|
+
try {
|
|
95
|
+
if (typeof requestFn !== 'function') return DEFAULT_EXPLORE_STATE;
|
|
96
|
+
const payload = await Promise.race([
|
|
97
|
+
Promise.resolve(requestFn(guidancePathFor(agent))),
|
|
98
|
+
new Promise((_, reject) => {
|
|
99
|
+
timer = setTimeout(() => reject(new Error(`helper-rules guidance fetch exceeded ${timeoutMs}ms`)), timeoutMs);
|
|
100
|
+
}),
|
|
101
|
+
]);
|
|
102
|
+
const state = payload && typeof payload.explore === 'string' ? payload.explore.trim() : '';
|
|
103
|
+
return EXPLORE_STATES.includes(state) ? state : DEFAULT_EXPLORE_STATE;
|
|
104
|
+
} catch {
|
|
105
|
+
// expected: daemon unavailable/refusing/malformed -- 'on' keeps today's guidance served
|
|
106
|
+
return DEFAULT_EXPLORE_STATE;
|
|
107
|
+
} finally {
|
|
108
|
+
if (timer) clearTimeout(timer);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
module.exports = {
|
|
113
|
+
DEFAULT_EXPLORE_STATE,
|
|
114
|
+
EXPLORE_STATES,
|
|
115
|
+
GUIDANCE_PATH,
|
|
116
|
+
GUIDANCE_TIMEOUT_MS,
|
|
117
|
+
fetchExploreCellState,
|
|
118
|
+
guidancePathFor,
|
|
119
|
+
httpJsonGet,
|
|
120
|
+
leadAgentFromClientInfo,
|
|
121
|
+
leadGuidanceForMessage,
|
|
122
|
+
readExploreCellState,
|
|
123
|
+
};
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Lead-cost routing WP-5a (design §6.8): the PURE rule evaluator. One function, no I/O —
|
|
4
|
+
// the caller (lib/helper-rules/store.js) reads the cell row and the trailing window and
|
|
5
|
+
// owns every write. This is what makes "why did the cell turn off?" answerable in a test
|
|
6
|
+
// and the panel honest: same inputs, same verdict.
|
|
7
|
+
|
|
8
|
+
const { LABEL_VERSION } = require('./label');
|
|
9
|
+
|
|
10
|
+
const WINDOW = 20;
|
|
11
|
+
const MIN_LABELLED = 10;
|
|
12
|
+
const WATCH_OFF_BELOW = 0.5;
|
|
13
|
+
const WATCH_ON_ABOVE = 0.7;
|
|
14
|
+
const EXPLORE_FALLBACK_MAX = 0.3;
|
|
15
|
+
const EXPLORE_REREAD_MAX = 0.5;
|
|
16
|
+
const EXPLORE_SUFFICIENT_MIN = 0.7;
|
|
17
|
+
const EXPLORE_PROBE_AFTER_DAYS = 7;
|
|
18
|
+
const PROBE_N = 10;
|
|
19
|
+
const GRADED_MIN = 10;
|
|
20
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
21
|
+
|
|
22
|
+
// Round 2 WP-2 (§5 D-7/D-10, §6.4): the two constants the measurability guard and the open-row
|
|
23
|
+
// sweep need. No round-1 threshold moves — D-7 is additive by design (the design's §5 note).
|
|
24
|
+
const EXPLORE_OPEN_TTL_MS = 4 * 60 * 60 * 1000; // D-10: worker deadline 1,800,000 ms × lease ceiling 4 = 2 h, doubled
|
|
25
|
+
const UNMEASURED_MAX_SHARE = 0.5; // D-7: an unmeasured/abandoned majority proves nothing
|
|
26
|
+
|
|
27
|
+
const CELL_STATES = Object.freeze(['on', 'off', 'probe']);
|
|
28
|
+
|
|
29
|
+
// The decisions an explore row may carry once it is no longer open. Anything else (a NULL from a
|
|
30
|
+
// round-1 write, or a value this rule has never produced) is unmeasured — D-7's "no data is not
|
|
31
|
+
// failure", applied to the row rather than to the window.
|
|
32
|
+
const EXPLORE_DECISIONS = Object.freeze(['open', 'handoff', 'fallback', 'abandoned', 'unmeasured']);
|
|
33
|
+
|
|
34
|
+
// Machine-readable reasons — short, stable strings the events table and the panel show.
|
|
35
|
+
const REASONS = Object.freeze({
|
|
36
|
+
USER_VETO: 'user_veto',
|
|
37
|
+
WATCH_PRECISION_BELOW: `precision_below_${WATCH_OFF_BELOW}`,
|
|
38
|
+
WATCH_SILENT_PRECISION_ABOVE: `silent_precision_above_${WATCH_ON_ABOVE}`,
|
|
39
|
+
EXPLORE_FALLBACK_ABOVE: `fallback_rate_above_${EXPLORE_FALLBACK_MAX}`,
|
|
40
|
+
EXPLORE_REREAD_ABOVE: `reread_rate_above_${EXPLORE_REREAD_MAX}`,
|
|
41
|
+
EXPLORE_HELPER_USD_AT_OR_ABOVE: 'helper_usd_at_or_above_lead_burst',
|
|
42
|
+
EXPLORE_SUFFICIENT_BELOW: `sufficient_rate_below_${EXPLORE_SUFFICIENT_MIN}`,
|
|
43
|
+
EXPLORE_PROBE_AFTER: `probe_after_${EXPLORE_PROBE_AFTER_DAYS}_days`,
|
|
44
|
+
EXPLORE_PROBE_PASSED: 'probe_passed',
|
|
45
|
+
EXPLORE_PROBE_FAILED: 'probe_failed',
|
|
46
|
+
// D-6: the audit row the one-time recovery pass writes on every cell it returns to `on` after
|
|
47
|
+
// the v1 labels it was demoted on turned out to be a labelling bug. Distinct from the reasons
|
|
48
|
+
// above so a query can tell "the evaluator demoted this" from "the record repaired itself".
|
|
49
|
+
LABEL_V2_RESET: 'label_v2_reset',
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// D-6: the DEMOTION set — the reasons that mean "the evaluator (not the user) turned this cell off
|
|
53
|
+
// or into a probe on a window it could not measure". Only a cell whose LAST event carries one of
|
|
54
|
+
// these may be returned to `on` by the relabel recovery pass; a `user_veto` never is.
|
|
55
|
+
const EXPLORE_DEMOTION_REASONS = Object.freeze(new Set([
|
|
56
|
+
REASONS.EXPLORE_FALLBACK_ABOVE,
|
|
57
|
+
REASONS.EXPLORE_REREAD_ABOVE,
|
|
58
|
+
REASONS.EXPLORE_HELPER_USD_AT_OR_ABOVE,
|
|
59
|
+
REASONS.EXPLORE_SUFFICIENT_BELOW,
|
|
60
|
+
REASONS.EXPLORE_PROBE_FAILED,
|
|
61
|
+
REASONS.EXPLORE_PROBE_AFTER,
|
|
62
|
+
]));
|
|
63
|
+
|
|
64
|
+
function _stateOf(cell) {
|
|
65
|
+
const state = cell && typeof cell.state === 'string' ? cell.state.trim() : '';
|
|
66
|
+
return CELL_STATES.includes(state) ? state : 'on';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function _vetoOf(cell) {
|
|
70
|
+
return Boolean(cell && Number(cell.user_veto) === 1);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function _toMs(value) {
|
|
74
|
+
if (value == null) return null;
|
|
75
|
+
if (value instanceof Date) return Number.isFinite(value.getTime()) ? value.getTime() : null;
|
|
76
|
+
if (typeof value === 'number') return Number.isFinite(value) ? value : null;
|
|
77
|
+
const text = String(value).trim();
|
|
78
|
+
if (!text) return null;
|
|
79
|
+
// SQLite's datetime('now') writes 'YYYY-MM-DD HH:MM:SS' in UTC.
|
|
80
|
+
const iso = /^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}$/.test(text) ? `${text.replace(' ', 'T')}Z` : text;
|
|
81
|
+
const ms = Date.parse(iso);
|
|
82
|
+
return Number.isFinite(ms) ? ms : null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function _ratio(numerator, denominator) {
|
|
86
|
+
return denominator > 0 ? numerator / denominator : null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function _median(values) {
|
|
90
|
+
const numbers = values.filter((value) => Number.isFinite(value)).sort((a, b) => a - b);
|
|
91
|
+
if (!numbers.length) return null;
|
|
92
|
+
const middle = Math.floor(numbers.length / 2);
|
|
93
|
+
return numbers.length % 2 === 1 ? numbers[middle] : (numbers[middle - 1] + numbers[middle]) / 2;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function _number(value) {
|
|
97
|
+
if (value == null || value === '') return null;
|
|
98
|
+
const number = Number(value);
|
|
99
|
+
return Number.isFinite(number) ? number : null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function _verdict(state, previousState, metrics, reason) {
|
|
103
|
+
return { state, metrics, reason: state === previousState ? null : reason };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// --- wait-watch -------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* D-9 (WP-4b): the watcher spend the window's cell actually accrued. The store reads it from
|
|
110
|
+
* `model_usage_ledger` and hands it over as ONE object, `{usd, rows}` — or null when the window
|
|
111
|
+
* names no worker or the ledger matched no row. These are REPORTED metrics: no threshold in this
|
|
112
|
+
* file reads them (the design's D-9 is a read, never a new rule).
|
|
113
|
+
*/
|
|
114
|
+
function _watcherUsdWindow(watcherUsd) {
|
|
115
|
+
if (!watcherUsd || typeof watcherUsd !== 'object') return null;
|
|
116
|
+
if (Number.isFinite(watcherUsd.rows) && watcherUsd.rows <= 0) return null;
|
|
117
|
+
return typeof watcherUsd.usd === 'number' && Number.isFinite(watcherUsd.usd) ? watcherUsd.usd : null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function _watchMetrics(outcomes, watcherUsd) {
|
|
121
|
+
const useful = outcomes.filter((row) => row.label === 'useful').length;
|
|
122
|
+
const empty = outcomes.filter((row) => row.label === 'empty').length;
|
|
123
|
+
const late = outcomes.filter((row) => row.label === 'late').length;
|
|
124
|
+
const silentUseful = outcomes.filter((row) => row.label === 'silent_useful').length;
|
|
125
|
+
const silentEmpty = outcomes.filter((row) => row.label === 'silent_empty').length;
|
|
126
|
+
const labelled = useful + empty;
|
|
127
|
+
const silentLabelled = silentUseful + silentEmpty;
|
|
128
|
+
const watcherUsdWindow = _watcherUsdWindow(watcherUsd);
|
|
129
|
+
return {
|
|
130
|
+
n_window: outcomes.length,
|
|
131
|
+
labelled,
|
|
132
|
+
useful,
|
|
133
|
+
empty,
|
|
134
|
+
late,
|
|
135
|
+
precision: _ratio(useful, labelled),
|
|
136
|
+
silent_labelled: silentLabelled,
|
|
137
|
+
silent_precision: _ratio(silentUseful, silentLabelled),
|
|
138
|
+
collecting: labelled < MIN_LABELLED,
|
|
139
|
+
watcher_usd_window: watcherUsdWindow,
|
|
140
|
+
// null (not 0) when there is no spend to divide or no useful wake to divide it by -- the panel
|
|
141
|
+
// must be able to say "not measured" rather than "$0.000 per wake".
|
|
142
|
+
usd_per_useful_wake: watcherUsdWindow !== null && useful > 0 ? watcherUsdWindow / useful : null,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function _evaluateWatch(cellState, outcomes, watcherUsd) {
|
|
147
|
+
const metrics = _watchMetrics(outcomes, watcherUsd);
|
|
148
|
+
if (cellState === 'on') {
|
|
149
|
+
if (metrics.labelled >= MIN_LABELLED && metrics.precision < WATCH_OFF_BELOW) {
|
|
150
|
+
return _verdict('off', cellState, metrics, REASONS.WATCH_PRECISION_BELOW);
|
|
151
|
+
}
|
|
152
|
+
return _verdict(cellState, cellState, metrics, null);
|
|
153
|
+
}
|
|
154
|
+
if (cellState === 'off') {
|
|
155
|
+
// While off, the silent ticks are the window: the lead's own next call is the only
|
|
156
|
+
// evidence left that the wake was not needed.
|
|
157
|
+
metrics.collecting = metrics.silent_labelled < MIN_LABELLED;
|
|
158
|
+
if (metrics.silent_labelled >= MIN_LABELLED && metrics.silent_precision >= WATCH_ON_ABOVE) {
|
|
159
|
+
return _verdict('on', cellState, metrics, REASONS.WATCH_SILENT_PRECISION_ABOVE);
|
|
160
|
+
}
|
|
161
|
+
return _verdict(cellState, cellState, metrics, null);
|
|
162
|
+
}
|
|
163
|
+
return _verdict(cellState, cellState, metrics, null);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// --- explore ----------------------------------------------------------------------
|
|
167
|
+
|
|
168
|
+
/** The row's parsed `grade_json` object, or null when it carries nothing readable. */
|
|
169
|
+
function _parseGrade(row) {
|
|
170
|
+
if (row.grade_json == null || row.grade_json === '') return null;
|
|
171
|
+
let parsed = row.grade_json;
|
|
172
|
+
if (typeof parsed === 'string') {
|
|
173
|
+
try {
|
|
174
|
+
parsed = JSON.parse(parsed);
|
|
175
|
+
} catch {
|
|
176
|
+
// expected: an unparsable grade_json carries no verdict — the row counts as ungraded
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (!parsed || typeof parsed !== 'object') return null;
|
|
181
|
+
return parsed;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function _gradeOf(row) {
|
|
185
|
+
const parsed = _parseGrade(row);
|
|
186
|
+
if (!parsed || parsed.unavailable === true) return null;
|
|
187
|
+
return parsed;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* D-1/D-7: a closed round-1 row stored no facts (label_version below the current version and
|
|
192
|
+
* facts_json empty), so its decision is a v1 label that cannot be recomputed -- the very labels
|
|
193
|
+
* that demoted the live cells. It is kept as evidence but measures nothing. A row without a
|
|
194
|
+
* label_version key (a bare test fixture) is read as current.
|
|
195
|
+
*/
|
|
196
|
+
function _isFactlessOldRow(row) {
|
|
197
|
+
if (row.label_version == null || row.decision === 'open') return false;
|
|
198
|
+
return Number(row.label_version) < LABEL_VERSION && (row.facts_json == null || row.facts_json === '');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** A handoff/fallback row the window may count as a measurement. */
|
|
202
|
+
function _isClosedRow(row) {
|
|
203
|
+
return (row.decision === 'handoff' || row.decision === 'fallback') && !_isFactlessOldRow(row);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** D-7: a row that carries no measurement — an explicit `unmeasured`, a decision the rule never produced, or a factless round-1 label. */
|
|
207
|
+
function _isUnmeasuredRow(row) {
|
|
208
|
+
return row.decision === 'unmeasured'
|
|
209
|
+
|| (row.decision !== 'open' && !EXPLORE_DECISIONS.includes(row.decision))
|
|
210
|
+
|| _isFactlessOldRow(row);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function _exploreMetrics(outcomes, leadBurstUsd) {
|
|
214
|
+
const closed = outcomes.filter(_isClosedRow);
|
|
215
|
+
const handoff = closed.filter((row) => row.decision === 'handoff');
|
|
216
|
+
const fallback = closed.length - handoff.length;
|
|
217
|
+
const gradedRows = closed.map((row) => _gradeOf(row)).filter(Boolean);
|
|
218
|
+
const sufficient = gradedRows.filter((grade) => grade.sufficient === true).length;
|
|
219
|
+
// D-8: how many of the window's rows carry an unavailable grade — the grader failed, was never
|
|
220
|
+
// pinned, or never parsed. `_gradeOf` still excludes them from `graded`; this only makes them
|
|
221
|
+
// visible, and it is a metric, never a threshold.
|
|
222
|
+
const gradeUnavailable = outcomes.filter((row) => _parseGrade(row)?.unavailable === true).length;
|
|
223
|
+
const unmeasured = outcomes.filter(_isUnmeasuredRow).length;
|
|
224
|
+
const abandoned = outcomes.filter((row) => row.decision === 'abandoned').length;
|
|
225
|
+
const open = outcomes.filter((row) => row.decision === 'open').length;
|
|
226
|
+
// D-7 (S-14): a verdict needs 10 MEASURED rows and a window that is not mostly no-data. Round 1
|
|
227
|
+
// read every non-handoff row as a fallback, so an all-unmeasured window ("the status read came
|
|
228
|
+
// back without a citation count") demoted a healthy cell. `closed` is unchanged — handoff +
|
|
229
|
+
// fallback only; the guard is on measurability, never on a threshold.
|
|
230
|
+
const measurable = closed.length >= MIN_LABELLED
|
|
231
|
+
&& (unmeasured + abandoned) <= outcomes.length * UNMEASURED_MAX_SHARE;
|
|
232
|
+
return {
|
|
233
|
+
n_window: outcomes.length,
|
|
234
|
+
closed: closed.length,
|
|
235
|
+
handoff: handoff.length,
|
|
236
|
+
fallback,
|
|
237
|
+
fallback_rate: _ratio(fallback, closed.length),
|
|
238
|
+
graded: gradedRows.length,
|
|
239
|
+
grade_unavailable: gradeUnavailable,
|
|
240
|
+
sufficient_rate: _ratio(sufficient, gradedRows.length),
|
|
241
|
+
median_helper_usd: _median(handoff.map((row) => _number(row.helper_usd)).filter((value) => value !== null)),
|
|
242
|
+
median_reread: _median(closed.map((row) => _number(row.reread_rate)).filter((value) => value !== null)),
|
|
243
|
+
lead_burst_usd: Number.isFinite(Number(leadBurstUsd)) ? Number(leadBurstUsd) : null,
|
|
244
|
+
unmeasured,
|
|
245
|
+
abandoned,
|
|
246
|
+
open,
|
|
247
|
+
measurable,
|
|
248
|
+
collecting: !measurable,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** The first off-condition the window trips, or null when it trips none. */
|
|
253
|
+
function _exploreFailureReason(metrics, leadBurstUsd) {
|
|
254
|
+
// D-7: neither an `on → off` demotion nor a failed probe may rest on a window that is mostly
|
|
255
|
+
// no-data — "unmeasured" is not evidence that the helper failed.
|
|
256
|
+
if (!metrics.measurable) return null;
|
|
257
|
+
if (metrics.fallback_rate > EXPLORE_FALLBACK_MAX) return REASONS.EXPLORE_FALLBACK_ABOVE;
|
|
258
|
+
if (metrics.median_reread !== null && metrics.median_reread > EXPLORE_REREAD_MAX) return REASONS.EXPLORE_REREAD_ABOVE;
|
|
259
|
+
if (metrics.median_helper_usd !== null && Number.isFinite(leadBurstUsd) && metrics.median_helper_usd >= leadBurstUsd) {
|
|
260
|
+
return REASONS.EXPLORE_HELPER_USD_AT_OR_ABOVE;
|
|
261
|
+
}
|
|
262
|
+
if (metrics.graded >= GRADED_MIN && metrics.sufficient_rate < EXPLORE_SUFFICIENT_MIN) {
|
|
263
|
+
return REASONS.EXPLORE_SUFFICIENT_BELOW;
|
|
264
|
+
}
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function _evaluateExplore(cell, outcomes, now, leadBurstUsd) {
|
|
269
|
+
const cellState = _stateOf(cell);
|
|
270
|
+
const metrics = _exploreMetrics(outcomes, leadBurstUsd);
|
|
271
|
+
|
|
272
|
+
if (cellState === 'on') {
|
|
273
|
+
const reason = _exploreFailureReason(metrics, leadBurstUsd);
|
|
274
|
+
if (reason) return _verdict('off', cellState, metrics, reason);
|
|
275
|
+
return _verdict(cellState, cellState, metrics, null);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (cellState === 'off') {
|
|
279
|
+
// The 7-day probe timer: only the daily tick can move an off cell, and only when the
|
|
280
|
+
// user has not vetoed it.
|
|
281
|
+
const changedAt = _toMs(cell.state_changed_at);
|
|
282
|
+
const nowMs = _toMs(now) ?? Date.now();
|
|
283
|
+
const days = changedAt == null ? 0 : (nowMs - changedAt) / DAY_MS;
|
|
284
|
+
if (!_vetoOf(cell) && days >= EXPLORE_PROBE_AFTER_DAYS) {
|
|
285
|
+
return _verdict('probe', cellState, metrics, REASONS.EXPLORE_PROBE_AFTER);
|
|
286
|
+
}
|
|
287
|
+
return _verdict(cellState, cellState, metrics, null);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// probe: the next PROBE_N closed rows that landed AFTER the probe started decide it.
|
|
291
|
+
const changedAt = _toMs(cell.state_changed_at);
|
|
292
|
+
const probeWindow = outcomes
|
|
293
|
+
.filter(_isClosedRow)
|
|
294
|
+
.filter((row) => {
|
|
295
|
+
const createdMs = _toMs(row.created_at);
|
|
296
|
+
return changedAt == null || createdMs == null || createdMs >= changedAt;
|
|
297
|
+
})
|
|
298
|
+
.slice(0, PROBE_N);
|
|
299
|
+
metrics.probed = probeWindow.length;
|
|
300
|
+
if (probeWindow.length < PROBE_N) return _verdict(cellState, cellState, metrics, null);
|
|
301
|
+
const failed = _exploreFailureReason(_exploreMetrics(probeWindow, leadBurstUsd), leadBurstUsd);
|
|
302
|
+
if (failed) return _verdict('off', cellState, metrics, REASONS.EXPLORE_PROBE_FAILED);
|
|
303
|
+
return _verdict('on', cellState, metrics, REASONS.EXPLORE_PROBE_PASSED);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Pure. Decide the next state of one rule cell from its stored state and its trailing
|
|
308
|
+
* window of outcomes. `reason` is null whenever the state does not change.
|
|
309
|
+
* @param {{state?:string, user_veto?:number, state_changed_at?:string}} cell
|
|
310
|
+
* @param {Array<object>} trailingOutcomes most-recent-first window of helper_outcomes rows
|
|
311
|
+
* @param {{now?: number|string|Date, leadBurstUsd?: number, watcherUsd?: {usd:number, rows:number}|null}} [options]
|
|
312
|
+
* @returns {{state:'on'|'off'|'probe', metrics:object, reason:string|null}}
|
|
313
|
+
*/
|
|
314
|
+
function evaluateCell(cell, trailingOutcomes, { now, leadBurstUsd, watcherUsd } = {}) {
|
|
315
|
+
const outcomes = Array.isArray(trailingOutcomes) ? trailingOutcomes : [];
|
|
316
|
+
const cellState = _stateOf(cell);
|
|
317
|
+
if (_vetoOf(cell)) {
|
|
318
|
+
// D-18: a vetoed cell is off, and it can never probe. Reason only when this is news.
|
|
319
|
+
return { state: 'off', metrics: _kindMetrics(cell, outcomes, leadBurstUsd, now, watcherUsd), reason: cellState === 'off' ? null : REASONS.USER_VETO };
|
|
320
|
+
}
|
|
321
|
+
const kind = String(cell?.kind || '').trim();
|
|
322
|
+
if (kind === 'explore' || String(cell?.cell_key || '').startsWith('explore|')) {
|
|
323
|
+
return _evaluateExplore(cell, outcomes, now, leadBurstUsd);
|
|
324
|
+
}
|
|
325
|
+
return _evaluateWatch(cellState, outcomes, watcherUsd);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** The metrics a short-circuited (vetoed) cell reports — same shape as its kind's. */
|
|
329
|
+
function _kindMetrics(cell, outcomes, leadBurstUsd, now, watcherUsd) {
|
|
330
|
+
void now;
|
|
331
|
+
const kind = String(cell?.kind || '').trim();
|
|
332
|
+
if (kind === 'explore' || String(cell?.cell_key || '').startsWith('explore|')) {
|
|
333
|
+
return _exploreMetrics(outcomes, leadBurstUsd);
|
|
334
|
+
}
|
|
335
|
+
return _watchMetrics(outcomes, watcherUsd);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
module.exports = {
|
|
339
|
+
CELL_STATES,
|
|
340
|
+
DAY_MS,
|
|
341
|
+
EXPLORE_DECISIONS,
|
|
342
|
+
EXPLORE_DEMOTION_REASONS,
|
|
343
|
+
EXPLORE_FALLBACK_MAX,
|
|
344
|
+
EXPLORE_OPEN_TTL_MS,
|
|
345
|
+
EXPLORE_PROBE_AFTER_DAYS,
|
|
346
|
+
EXPLORE_REREAD_MAX,
|
|
347
|
+
EXPLORE_SUFFICIENT_MIN,
|
|
348
|
+
GRADED_MIN,
|
|
349
|
+
MIN_LABELLED,
|
|
350
|
+
PROBE_N,
|
|
351
|
+
REASONS,
|
|
352
|
+
UNMEASURED_MAX_SHARE,
|
|
353
|
+
WATCH_OFF_BELOW,
|
|
354
|
+
WATCH_ON_ABOVE,
|
|
355
|
+
WINDOW,
|
|
356
|
+
evaluateCell,
|
|
357
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
You are a careful grader for an evaluation. A lead coding agent explored a codebase with several read-only steps, then made a decision. Separately, a helper model was given a short brief describing the same question and explored the same code on its own, then wrote a report. Your job: judge whether the helper's report would have served the lead as well as its own exploration did.
|
|
2
|
+
|
|
3
|
+
The card has: the brief given to the helper, the helper's report, and the list of tools and paths the helper touched.
|
|
4
|
+
|
|
5
|
+
Judge:
|
|
6
|
+
- coverage: did the report contain the facts the brief's "Stop when" condition needs? "all" | "most" | "little"
|
|
7
|
+
- inaccuracies: how many claims in the report are wrong or unsupported by a file:line the report cites? an integer
|
|
8
|
+
- sufficient: could the lead make its next decision from the report alone, without re-reading the files? true | false
|
|
9
|
+
- why: <= 40 words, concrete (name the missing or wrong fact if any).
|
|
10
|
+
Judge only facts the report itself supports with file:line; if the report marks something INFERENCE, do not count it as wrong. A "not found" row is valid when the brief's question has no answer in the code.
|
|
11
|
+
|
|
12
|
+
Answer with one JSON object only: {"sufficient": true|false, "coverage": "all"|"most"|"little", "inaccuracies": <integer>, "why": "<= 40 words"}
|