create-walle 0.10.0 → 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 +88 -41
- 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 +7 -5
- package/package.json +10 -4
- 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/capture-codex-rollout-records.js +417 -0
- package/template/coding-task-manager/bin/census-codex-lead-history.js +325 -0
- 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/seam-replay-codex.js +301 -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 +25 -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-history-terminal-renderer.js +33 -33
- 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-local-links.js +267 -0
- package/template/coding-task-manager/lib/codex-markdown-lists.js +160 -0
- package/template/coding-task-manager/lib/codex-native-history-prefix.js +4 -0
- package/template/coding-task-manager/lib/codex-paths.js +2 -1
- package/template/coding-task-manager/lib/codex-source-handoff.js +186 -0
- 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-owner-worker-client.js +22 -0
- package/template/coding-task-manager/lib/db-schema-source-files.js +1 -0
- package/template/coding-task-manager/lib/feature-manifest.js +1 -3
- package/template/coding-task-manager/lib/fs-watcher.js +3 -2
- package/template/coding-task-manager/lib/harness-history-transition.js +41 -14
- 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 +60 -23
- package/template/coding-task-manager/lib/history-transition-read-op.js +179 -23
- 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/kernel/diag-log.js +12 -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-observer.js +17 -2
- package/template/coding-task-manager/lib/lead-model-switch-runtime.js +57 -4
- package/template/coding-task-manager/lib/lead-model-switch.js +11 -2
- package/template/coding-task-manager/lib/lead-model-transition-runtime.js +42 -4
- 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 +18 -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 +56 -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-execution-recipe-metadata.js +16 -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/collaboration-codec.js +94 -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/responses-face.js +24 -2
- 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 +62 -14
- 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-admission-trace.js +69 -0
- 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/raw-history-block.js +3 -1
- package/template/coding-task-manager/lib/read-pool-client.js +36 -24
- package/template/coding-task-manager/lib/read-pool-op-registry.js +10 -1
- package/template/coding-task-manager/lib/read-pool-telemetry.js +14 -5
- 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 +18 -5
- 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 +43 -11
- package/template/coding-task-manager/lib/root-prompt-flow.js +175 -38
- package/template/coding-task-manager/lib/scrollback-archive-store.js +7 -4
- package/template/coding-task-manager/lib/server-memory-sampler-runtime.js +10 -0
- package/template/coding-task-manager/lib/server-memory-sampler.js +41 -2
- 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 +38 -39
- 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 +20 -50
- package/template/coding-task-manager/lib/session-model-action-preconditions.js +68 -3
- package/template/coding-task-manager/lib/session-resource-sampler.js +67 -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-status.js +2 -2
- 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 +136 -2
- package/template/coding-task-manager/lib/typing-worker-stages.js +208 -1
- package/template/coding-task-manager/lib/usage-ledger/claude-parse.js +53 -2
- package/template/coding-task-manager/lib/usage-ledger/ingest.js +13 -4
- package/template/coding-task-manager/lib/usage-ledger/read.js +14 -0
- package/template/coding-task-manager/lib/usage-ledger/schema.js +49 -1
- package/template/coding-task-manager/lib/usage-ledger/store.js +337 -6
- package/template/coding-task-manager/lib/usage-ledger/task-read.js +123 -5
- 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/input.js +1 -0
- 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/protocol/message-table.json +0 -10
- package/template/coding-task-manager/protocol/messages.js +0 -10
- package/template/coding-task-manager/protocol/ws-schema.json +0 -20
- package/template/coding-task-manager/protocol/ws-schema.md +0 -2
- 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 -2
- package/template/coding-task-manager/public/css/reviews.css +4 -0
- package/template/coding-task-manager/public/index.html +297 -240
- 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/client-health-core.js +81 -0
- package/template/coding-task-manager/public/js/client-health-main.js +61 -0
- 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 -4
- package/template/coding-task-manager/public/js/prompt-submission-tray.js +281 -0
- package/template/coding-task-manager/public/js/protocol.js +0 -10
- 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 +18 -3
- 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/session-usage-view.js +1 -0
- 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 -3
- 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 +964 -425
- 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/host-capabilities.js +2 -2
- 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/fly.toml +1 -1
- 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 +12 -5
- 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
- package/template/coding-task-manager/lib/giant-freeze-park.js +0 -31
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// WP-1 (docs/codex-lead-routed-switch-history-handoff-2026-10-21.html §1, §7 first row, §8 WP-1):
|
|
5
|
+
// the §1 census, promoted from a scratch script to the tool the report quotes.
|
|
6
|
+
//
|
|
7
|
+
// WHY IT EXISTS. The naive census calls the scanner's inner function and counts record TYPES, which
|
|
8
|
+
// reported "no new record types" for 0.163.0 while CTM refused 265 of 505 real sessions because of
|
|
9
|
+
// the KEYS inside a known type (`thread_settings_applied`). This tool calls the PRODUCTION entry
|
|
10
|
+
// point instead: readCodexHistoryTransitionSnapshot = owned-rollout resolution + the native prefix
|
|
11
|
+
// reader + the scanner; when the scan completes it also runs the planner (inspectTransition), so the
|
|
12
|
+
// first refusal of the REAL chain is what gets reported. See §1 "The naive count is wrong".
|
|
13
|
+
//
|
|
14
|
+
// OQ-1 (the census harness, not production, showed 65 `canonical_history_path_unavailable`): this
|
|
15
|
+
// tool derives each session's storage root from the session's OWN jsonl_path -- the directory whose
|
|
16
|
+
// last `sessions` segment holds the rollout -- instead of assuming one ~/.codex home for every row.
|
|
17
|
+
// The corresponding root receipt is built the same way CTM's launch path builds it
|
|
18
|
+
// (lib/codex-paths.js codexSessionsRootReceipt), so what is measured here is what production reads.
|
|
19
|
+
//
|
|
20
|
+
// READ-ONLY. The database is opened `readonly` + `fileMustExist` and is never written; no file is
|
|
21
|
+
// created anywhere; no port is contacted.
|
|
22
|
+
//
|
|
23
|
+
// usage:
|
|
24
|
+
// node bin/census-codex-lead-history.js [--since YYYY-MM-DD] [--limit N] [--only <ctm-session-prefix>]
|
|
25
|
+
// [--db <path to core.db>] [--explain <ctm-session-prefix>]
|
|
26
|
+
//
|
|
27
|
+
// --since oldest modified_at to include (default 2026-10-01, the §1 population)
|
|
28
|
+
// --limit newest sessions to scan (default 1000: above the 505 of §1, so a full table by default)
|
|
29
|
+
// --only only sessions whose ctm_session_id starts with this prefix
|
|
30
|
+
// --explain also print the snapshot's summary fields for the matching session(s), and for a
|
|
31
|
+
// `thread_settings_applied` refusal the lineage set vs the failing record's thread_id (OQ-3)
|
|
32
|
+
//
|
|
33
|
+
// exit 0 the table printed · 1 usage error · 2 the database could not be opened read-only.
|
|
34
|
+
|
|
35
|
+
const fs = require('node:fs');
|
|
36
|
+
const os = require('node:os');
|
|
37
|
+
const path = require('node:path');
|
|
38
|
+
const Database = require('better-sqlite3');
|
|
39
|
+
|
|
40
|
+
const op = require('../lib/history-transition-read-op');
|
|
41
|
+
const { readNativeHistoryPrefix } = require('../lib/codex-native-history-prefix');
|
|
42
|
+
const { inspectTransition } = require('../lib/harness-history-transition');
|
|
43
|
+
const codexPaths = require('../lib/codex-paths');
|
|
44
|
+
const { CODEX_CONTROL_ADAPTER, CODEX_COMPACTION_SCHEMA_RECEIPT } = require('../lib/codex-harness-qualification');
|
|
45
|
+
|
|
46
|
+
const DEFAULT_DB = path.join(os.homedir(), '.walle', 'data', 'core.db');
|
|
47
|
+
const DEFAULT_SINCE = '2026-10-01';
|
|
48
|
+
const DEFAULT_LIMIT = 1000;
|
|
49
|
+
const EXAMPLES_PER_REASON = 4;
|
|
50
|
+
const EXPLAIN_GENERATION = 'census-codex-lead-history';
|
|
51
|
+
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
52
|
+
|
|
53
|
+
// The §1 target recipe: a routed DeepSeek V4.1 Flash target (any provider id other than the
|
|
54
|
+
// source's; the planner only compares them) with the
|
|
55
|
+
// qualified harness version and the qualified compaction receipt. Per-session fields (approval
|
|
56
|
+
// policy, sandbox policy, cyber access program) are copied from the scanned snapshot.
|
|
57
|
+
const TARGET_RECIPE_BASE = Object.freeze({
|
|
58
|
+
providerId: 'census-routed-target',
|
|
59
|
+
routeId: 'census-routed-target',
|
|
60
|
+
origin: 'routed-proxy',
|
|
61
|
+
harness: 'codex-cli',
|
|
62
|
+
controlAdapterVersion: CODEX_CONTROL_ADAPTER,
|
|
63
|
+
harnessVersion: '0.163.0',
|
|
64
|
+
wireModelId: 'deepseek-v4p1-flash',
|
|
65
|
+
contextTokens: 1049000,
|
|
66
|
+
usableContextTokens: 996550,
|
|
67
|
+
nativeCompactThresholdTokens: 944100,
|
|
68
|
+
plannedRequestOutputTokens: 65536,
|
|
69
|
+
compactHash: null,
|
|
70
|
+
compactHashStatus: 'known-absent-with-native-schema-receipt',
|
|
71
|
+
compactHashSchemaReceipt: CODEX_COMPACTION_SCHEMA_RECEIPT,
|
|
72
|
+
catalogSha256: '0'.repeat(64),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// One row per CTM session, newest agent_sessions row first (§1: "the newest row per session").
|
|
76
|
+
const SESSION_QUERY = `SELECT ctm_session_id, agent_session_id, jsonl_path, max(modified_at) AS modified_at
|
|
77
|
+
FROM agent_sessions
|
|
78
|
+
WHERE lower(provider) = 'codex' AND ctm_session_id IS NOT NULL AND ctm_session_id <> ''
|
|
79
|
+
AND jsonl_path IS NOT NULL AND modified_at > ?
|
|
80
|
+
GROUP BY ctm_session_id
|
|
81
|
+
ORDER BY modified_at DESC
|
|
82
|
+
LIMIT ?`;
|
|
83
|
+
|
|
84
|
+
class UsageError extends Error {}
|
|
85
|
+
|
|
86
|
+
function usage() {
|
|
87
|
+
return [
|
|
88
|
+
'usage: node bin/census-codex-lead-history.js [--since YYYY-MM-DD] [--limit N] [--only <ctm-session-prefix>] [--db <path>] [--explain <ctm-session-prefix>]',
|
|
89
|
+
' --since <date> oldest modified_at to include (default 2026-10-01)',
|
|
90
|
+
' --limit <n> newest sessions to scan (default 1000)',
|
|
91
|
+
' --only <prefix> only sessions whose ctm_session_id starts with this prefix',
|
|
92
|
+
' --db <path> core.db to read (default ~/.walle/data/core.db, opened read-only)',
|
|
93
|
+
' --explain <prefix> print the snapshot summary (and, for a thread_settings_applied refusal,',
|
|
94
|
+
' the lineage set vs the failing record thread_id) for matching session(s)',
|
|
95
|
+
].join('\n');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function parseArgs(argv) {
|
|
99
|
+
const parsed = { help: false, since: DEFAULT_SINCE, limit: DEFAULT_LIMIT, only: null, db: DEFAULT_DB, explain: null };
|
|
100
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
101
|
+
const arg = argv[index];
|
|
102
|
+
if (arg === '--help' || arg === '-h') { parsed.help = true; continue; }
|
|
103
|
+
if (['--since', '--limit', '--only', '--db', '--explain'].includes(arg)) {
|
|
104
|
+
const value = argv[index + 1];
|
|
105
|
+
if (value === undefined || value.startsWith('--')) throw new UsageError(`${arg} needs a value`);
|
|
106
|
+
index += 1;
|
|
107
|
+
if (arg === '--since') parsed.since = value;
|
|
108
|
+
else if (arg === '--limit') parsed.limit = value;
|
|
109
|
+
else if (arg === '--only') parsed.only = value;
|
|
110
|
+
else if (arg === '--explain') parsed.explain = value;
|
|
111
|
+
else parsed.db = value;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
throw new UsageError(`unknown option ${arg}`);
|
|
115
|
+
}
|
|
116
|
+
if (!DATE_RE.test(parsed.since)) throw new UsageError(`--since must be YYYY-MM-DD, got ${parsed.since}`);
|
|
117
|
+
const limit = Number(parsed.limit);
|
|
118
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new UsageError(`--limit must be a positive integer, got ${parsed.limit}`);
|
|
119
|
+
parsed.limit = limit;
|
|
120
|
+
return parsed;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function openReadOnly(dbPath) {
|
|
124
|
+
if (!fs.existsSync(dbPath)) throw new Error(`${dbPath} does not exist`);
|
|
125
|
+
const stat = fs.statSync(dbPath);
|
|
126
|
+
if (!stat.isFile()) throw new Error(`${dbPath} is not a file`);
|
|
127
|
+
return new Database(dbPath, { readonly: true, fileMustExist: true });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// OQ-1: the storage root of THIS session, taken from its own rollout path. Codex writes
|
|
131
|
+
// <codexHome>/sessions/<date>/rollout-<ts>-<uuid>.jsonl, so the codex home is the parent of the
|
|
132
|
+
// `sessions` segment; the receipt is the same object the launch path captures. A jsonl_path with no
|
|
133
|
+
// `sessions` segment is not a Codex rollout path this tool can speak about -- reported, not guessed.
|
|
134
|
+
function storageRootForSession(jsonlPath) {
|
|
135
|
+
const segments = path.resolve(jsonlPath).split(path.sep);
|
|
136
|
+
const index = segments.lastIndexOf('sessions');
|
|
137
|
+
if (index <= 0) return { codexHome: null, sessionsDir: null, receipt: null };
|
|
138
|
+
const codexHome = segments.slice(0, index).join(path.sep) || path.sep;
|
|
139
|
+
const sessionsDir = segments.slice(0, index + 1).join(path.sep);
|
|
140
|
+
return { codexHome, sessionsDir, receipt: codexPaths.codexSessionsRootReceipt({ codexHomePath: codexHome, sessionsDir }) };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// The §1 methodology, one session at a time: the production read path, then the planner. Keeping
|
|
144
|
+
// only the key (never the snapshot) is what lets this run over every session in the window.
|
|
145
|
+
function scanSession(row) {
|
|
146
|
+
const storage = storageRootForSession(row.jsonl_path);
|
|
147
|
+
if (!storage.receipt) {
|
|
148
|
+
return { key: `scan:${storage.codexHome ? 'history_storage_root_unavailable' : 'census_jsonl_path_unexpected'}`, storage };
|
|
149
|
+
}
|
|
150
|
+
let snapshot;
|
|
151
|
+
try {
|
|
152
|
+
snapshot = op.readCodexHistoryTransitionSnapshot({
|
|
153
|
+
db: row.db,
|
|
154
|
+
ctmSessionId: row.ctm_session_id,
|
|
155
|
+
sourceThreadId: row.agent_session_id,
|
|
156
|
+
processGeneration: 0,
|
|
157
|
+
storageRootReceipt: storage.receipt,
|
|
158
|
+
maxBytes: op.MAX_SCAN_BYTES,
|
|
159
|
+
});
|
|
160
|
+
} catch (error) {
|
|
161
|
+
// expected: a rollout deleted or replaced mid-scan is reported as a throw, never as a pass
|
|
162
|
+
return { key: `scan:THROW:${String(error?.message || error).slice(0, 60)}`, storage, snapshot: null };
|
|
163
|
+
}
|
|
164
|
+
if (!snapshot.complete) return { key: `scan:${snapshot.reason}`, storage, snapshot };
|
|
165
|
+
const previousSettings = snapshot.previousSettings || {};
|
|
166
|
+
const decision = inspectTransition({
|
|
167
|
+
sourceRecipe: {
|
|
168
|
+
providerId: previousSettings.modelProvider || 'openai',
|
|
169
|
+
origin: 'native-codex',
|
|
170
|
+
harness: 'codex-cli',
|
|
171
|
+
controlAdapterVersion: CODEX_CONTROL_ADAPTER,
|
|
172
|
+
programVersion: previousSettings.programVersion,
|
|
173
|
+
cyberAccessProgram: previousSettings.cyberAccessProgram,
|
|
174
|
+
approvalPolicy: previousSettings.approvalPolicy,
|
|
175
|
+
sandboxPolicy: previousSettings.sandboxPolicy,
|
|
176
|
+
wireModelId: previousSettings.model,
|
|
177
|
+
providerThreadId: row.agent_session_id,
|
|
178
|
+
},
|
|
179
|
+
targetRecipe: {
|
|
180
|
+
...TARGET_RECIPE_BASE,
|
|
181
|
+
approvalPolicy: previousSettings.approvalPolicy,
|
|
182
|
+
sandboxPolicy: previousSettings.sandboxPolicy,
|
|
183
|
+
cyberAccessProgram: previousSettings.cyberAccessProgram,
|
|
184
|
+
},
|
|
185
|
+
snapshot,
|
|
186
|
+
});
|
|
187
|
+
return { key: decision.action === 'unsupported' ? `plan:${decision.reason}` : `OK:${decision.action}`, storage, snapshot };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function prefixed(value) {
|
|
191
|
+
return String(value || '').slice(0, 8);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function printTable(rows, tallies, examples, { since, limit }) {
|
|
195
|
+
const lines = [];
|
|
196
|
+
lines.push(`sessions ${rows.length} (since ${since}, limit ${limit})`); const ordered = [...tallies.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
|
|
197
|
+
const width = ordered.reduce((max, [reason]) => Math.max(max, reason.length), 6);
|
|
198
|
+
for (const [reason, count] of ordered) {
|
|
199
|
+
lines.push(`${String(count).padStart(4)} ${reason.padEnd(width)} ${examples.get(reason).join(', ')}`);
|
|
200
|
+
}
|
|
201
|
+
if (!ordered.length) lines.push(' (no sessions matched)');
|
|
202
|
+
lines.push(`total ${[...tallies.values()].reduce((sum, count) => sum + count, 0)}`);
|
|
203
|
+
process.stdout.write(`${lines.join('\n')}\n`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// OQ-3: the refusal the scanner returns for a `thread_settings_applied` record is the generic
|
|
207
|
+
// `unsupported_native_history_event`, so the failing RECORD has to be located. The scanner stops at
|
|
208
|
+
// its first refusal, and summarizing a slice that ends after the last `compacted` record uses the
|
|
209
|
+
// same start index as the full list -- so "does this slice refuse" is monotone there, and a binary
|
|
210
|
+
// search finds the refusing index in O(log n) scans instead of one scan per record.
|
|
211
|
+
function firstRefusalIndex(records, { threadId, lineageIds }) {
|
|
212
|
+
const lastCompaction = records.findLastIndex((record) => record?.type === 'compacted');
|
|
213
|
+
const startIndex = lastCompaction >= 0 ? lastCompaction : 0;
|
|
214
|
+
let low = startIndex + 1;
|
|
215
|
+
let high = records.length;
|
|
216
|
+
let found = -1;
|
|
217
|
+
while (low <= high) {
|
|
218
|
+
const mid = (low + high) >> 1;
|
|
219
|
+
const summary = op.summarizeCodexHistoryRecords(records.slice(0, mid), {
|
|
220
|
+
threadId, generation: EXPLAIN_GENERATION, lineageThreadIds: lineageIds,
|
|
221
|
+
});
|
|
222
|
+
if (summary.complete) low = mid + 1;
|
|
223
|
+
else { found = mid - 1; high = mid - 1; }
|
|
224
|
+
}
|
|
225
|
+
return found;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function describeRefusal(root, threadId) {
|
|
229
|
+
const lines = [];
|
|
230
|
+
const prefix = readNativeHistoryPrefix(root.jsonl_path, { expectedThreadId: threadId, storageRoots: [root.storage.receipt.canonicalPath] });
|
|
231
|
+
if (!prefix.ok) {
|
|
232
|
+
lines.push(` raw prefix read refused: ${prefix.reason}`);
|
|
233
|
+
return lines;
|
|
234
|
+
}
|
|
235
|
+
lines.push(` lineage ids (${prefix.lineageIds.length}): ${prefix.lineageIds.join(', ')}`);
|
|
236
|
+
const index = firstRefusalIndex(prefix.records, { threadId, lineageIds: prefix.lineageIds });
|
|
237
|
+
if (index < 0) {
|
|
238
|
+
lines.push(' no refusing record found by re-summarizing prefixes (the refusal may be outside the scanned window)');
|
|
239
|
+
} else {
|
|
240
|
+
const record = prefix.records[index];
|
|
241
|
+
const payload = record?.payload && typeof record.payload === 'object' ? record.payload : {};
|
|
242
|
+
lines.push(` first refusing record: index ${index}, type=${record?.type}, payload.type=${payload.type}`);
|
|
243
|
+
if ('thread_id' in payload) {
|
|
244
|
+
const owned = new Set(prefix.lineageIds.map((id) => String(id).toLowerCase()));
|
|
245
|
+
lines.push(` payload.thread_id=${payload.thread_id} in lineage=${owned.has(String(payload.thread_id).toLowerCase())}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// OQ-3 verbatim: every thread_settings_applied record's thread_id against the computed lineage,
|
|
249
|
+
// with its key set, so "the keys are in the D-1 set, so the thread_id must be outside it" is
|
|
250
|
+
// answerable from this output alone.
|
|
251
|
+
const settingsRecords = prefix.records
|
|
252
|
+
.map((record, at) => ({ record, at }))
|
|
253
|
+
.filter(({ record }) => record?.type === 'event_msg' && record.payload?.type === 'thread_settings_applied');
|
|
254
|
+
const owned = new Set(prefix.lineageIds.map((id) => String(id).toLowerCase()));
|
|
255
|
+
lines.push(` thread_settings_applied records: ${settingsRecords.length}`);
|
|
256
|
+
for (const { record, at } of settingsRecords.slice(0, 8)) {
|
|
257
|
+
const payload = record.payload;
|
|
258
|
+
const keys = payload.thread_settings && typeof payload.thread_settings === 'object' ? Object.keys(payload.thread_settings) : [];
|
|
259
|
+
lines.push(` index ${at}: thread_id=${payload.thread_id ?? '(absent)'} in lineage=${payload.thread_id === undefined || payload.thread_id === null ? 'n/a' : owned.has(String(payload.thread_id).toLowerCase())} settings_keys=${keys.length}`);
|
|
260
|
+
}
|
|
261
|
+
return lines;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function explainSessions(rows, db) {
|
|
265
|
+
for (const row of rows) {
|
|
266
|
+
process.stdout.write(`\nexplain ${row.ctm_session_id}\n`);
|
|
267
|
+
process.stdout.write(` agent_session_id: ${row.agent_session_id}\n`);
|
|
268
|
+
process.stdout.write(` jsonl_path: ${row.jsonl_path}\n`);
|
|
269
|
+
const storage = storageRootForSession(row.jsonl_path);
|
|
270
|
+
process.stdout.write(` codex_home: ${storage.codexHome || '(no sessions segment)'}\n`);
|
|
271
|
+
process.stdout.write(` storage_root: ${storage.receipt ? storage.receipt.canonicalPath : '(no receipt)'}`
|
|
272
|
+
+ `${storage.receipt ? ` fingerprint=${storage.receipt.fingerprint.slice(0, 12)}` : ''}\n`);
|
|
273
|
+
const result = scanSession({ ...row, db });
|
|
274
|
+
process.stdout.write(` verdict: ${result.key}\n`);
|
|
275
|
+
process.stdout.write(` snapshot: ${result.snapshot ? JSON.stringify(result.snapshot, null, 2).split('\n').join('\n ') : '(not captured: the scan never ran or threw)'}\n`);
|
|
276
|
+
if (result.key === 'scan:unsupported_native_history_event' && storage.receipt) {
|
|
277
|
+
for (const line of describeRefusal({ jsonl_path: row.jsonl_path, storage }, row.agent_session_id)) {
|
|
278
|
+
process.stdout.write(`${line}\n`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function run(argv) {
|
|
285
|
+
const args = parseArgs(argv);
|
|
286
|
+
if (args.help) {
|
|
287
|
+
process.stdout.write(`${usage()}\n`);
|
|
288
|
+
return 0;
|
|
289
|
+
}
|
|
290
|
+
const db = openReadOnly(args.db);
|
|
291
|
+
try {
|
|
292
|
+
const rows = db.prepare(SESSION_QUERY).all(args.since, args.limit)
|
|
293
|
+
.filter((row) => !args.only || String(row.ctm_session_id).startsWith(args.only));
|
|
294
|
+
const tallies = new Map();
|
|
295
|
+
const examples = new Map();
|
|
296
|
+
for (const row of rows) {
|
|
297
|
+
const result = scanSession({ ...row, db });
|
|
298
|
+
tallies.set(result.key, (tallies.get(result.key) || 0) + 1);
|
|
299
|
+
const list = examples.get(result.key) || [];
|
|
300
|
+
if (list.length < EXAMPLES_PER_REASON) list.push(prefixed(row.ctm_session_id));
|
|
301
|
+
examples.set(result.key, list);
|
|
302
|
+
}
|
|
303
|
+
printTable(rows, tallies, examples, args);
|
|
304
|
+
if (args.explain) {
|
|
305
|
+
const matching = rows.filter((row) => String(row.ctm_session_id).startsWith(args.explain));
|
|
306
|
+
if (!matching.length) process.stdout.write(`\nexplain ${args.explain}: no matching session\n`);
|
|
307
|
+
else explainSessions(matching, db);
|
|
308
|
+
}
|
|
309
|
+
} finally {
|
|
310
|
+
db.close();
|
|
311
|
+
}
|
|
312
|
+
return 0;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (require.main === module) {
|
|
316
|
+
try {
|
|
317
|
+
process.exitCode = run(process.argv.slice(2));
|
|
318
|
+
} catch (error) {
|
|
319
|
+
if (error instanceof UsageError) process.stderr.write(`census-codex-lead-history: ${error.message}\n${usage()}\n`);
|
|
320
|
+
else process.stderr.write(`census-codex-lead-history: ${error?.stack || error}\n`);
|
|
321
|
+
process.exitCode = error instanceof UsageError ? 1 : 2;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
module.exports = { run, parseArgs, storageRootForSession, scanSession, firstRefusalIndex };
|
|
@@ -15,16 +15,20 @@ const http = require('http');
|
|
|
15
15
|
const crypto = require('crypto');
|
|
16
16
|
const modelGeneration = require('../lib/env-flags').strFlag('CTM_MODEL_PROCESS_GENERATION', '');
|
|
17
17
|
|
|
18
|
-
// Only these routes can carry a notice back to the model (Claude hook stdout JSON).
|
|
19
|
-
|
|
18
|
+
// Only these routes can carry a notice back to the model (Claude hook stdout JSON). `stop` is
|
|
19
|
+
// here for RC-1 (docs/session-stops-mid-job-2026-10-24.html D-1): the Stop hook is answered
|
|
20
|
+
// synchronously so a lead that still owns a live worker is told to keep waiting.
|
|
21
|
+
const NOTICE_ROUTES = new Set(['PostToolUse', 'start', 'stop']);
|
|
20
22
|
|
|
21
23
|
// The hook stdout for a CTM response body: one JSON object when it carries notices, else ''.
|
|
24
|
+
// Against an OLD server that answers {"matched":true} there is no `notices` array, so nothing is
|
|
25
|
+
// printed (D-8, safe in either rollout order).
|
|
22
26
|
function formatHookOutput(responseText, hookEventName) {
|
|
23
27
|
let parsed;
|
|
24
28
|
try { parsed = JSON.parse(responseText); } catch { return ''; }
|
|
25
29
|
const notices = parsed && Array.isArray(parsed.notices) ? parsed.notices : [];
|
|
26
30
|
const texts = notices.map((n) => (n && typeof n.text === 'string' ? n.text : '')).filter(Boolean);
|
|
27
|
-
if (!texts.length ||
|
|
31
|
+
if (!texts.length || !['PostToolUse', 'UserPromptSubmit', 'Stop'].includes(hookEventName)) return '';
|
|
28
32
|
return JSON.stringify({ hookSpecificOutput: { hookEventName, additionalContext: texts.join('\n') } });
|
|
29
33
|
}
|
|
30
34
|
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// WP-2 (docs/reader-claude-table-layout-parity-2026-10-25.html §1.2 the census, §6 the CLI sketch,
|
|
5
|
+
// §8 WP-2, D-7): the census that produced the design's parity floor, kept as a repo tool so a
|
|
6
|
+
// Claude Code upgrade that changes the Markdown-table layout is MEASURED, not guessed (R-8: the
|
|
7
|
+
// port pins one version and there is no runtime switch -- drift is caught here and by the golden
|
|
8
|
+
// fixtures).
|
|
9
|
+
//
|
|
10
|
+
// READ-ONLY. It opens the painted-row stores (`~/.walle/data/painted-rows`) and the provider
|
|
11
|
+
// transcripts (`~/.claude/projects`) read-only: never a database, never a file write. It prints
|
|
12
|
+
// numbers and 8-character session-id prefixes only -- never a painted row, never a cell.
|
|
13
|
+
//
|
|
14
|
+
// node bin/claude-table-parity.js [--painted DIR] [--projects DIR] [--json]
|
|
15
|
+
//
|
|
16
|
+
// For every session directory that has a painted-row store and a transcript: lex every assistant
|
|
17
|
+
// text block with the repo's own `marked`; every GFM `table` token is one table. Locate the drawing
|
|
18
|
+
// (the first body cell of at least 8 characters, its first 24 characters, searched in the painted
|
|
19
|
+
// rows -- one record per stretch file that holds a hit) and re-draw the table with the SHIPPED
|
|
20
|
+
// renderer (`lib/claude-markdown-ansi.emitMarkdownRows` -> `lib/claude-table-layout`) at every
|
|
21
|
+
// `cols` the session's stretch headers recorded. `predicted` is the form that re-draw took (a row
|
|
22
|
+
// carrying `│` means the box), `identical` the most rows that matched the painted window.
|
|
23
|
+
//
|
|
24
|
+
// Exits 0 always -- a missing store, a torn line or an unknown flag is not a census failure.
|
|
25
|
+
|
|
26
|
+
const fs = require('fs');
|
|
27
|
+
const path = require('path');
|
|
28
|
+
const os = require('os');
|
|
29
|
+
const marked = require('marked');
|
|
30
|
+
const { emitMarkdownRows } = require('../lib/claude-markdown-ansi');
|
|
31
|
+
|
|
32
|
+
// The renderer's own style object (lib/claude-transcript-terminal-renderer.js MARKDOWN_STYLE, as
|
|
33
|
+
// its test copies it). It is not exported; the comparison strips SGR anyway, but `cardLines`
|
|
34
|
+
// styles the `Header:` key through it, so the re-draw must carry it.
|
|
35
|
+
const MD_STYLE = Object.freeze({
|
|
36
|
+
bold: [1], boldOff: [22],
|
|
37
|
+
italic: [3], italicOff: [23],
|
|
38
|
+
del: [9], delOff: [29],
|
|
39
|
+
code: [38, 5, 180], codeOff: [39],
|
|
40
|
+
dim: [2],
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const SGR_RE = /\x1b\[[0-9;?]*[ -\/]*[@-~]/g;
|
|
44
|
+
const CARD_RULE = '─'.repeat(40); // the vertical form's card separator (RULE_MAX)
|
|
45
|
+
const PROBE_MIN = 8; // a probe cell must be at least this long
|
|
46
|
+
const PROBE_LEN = 24; // ... and only its first 24 characters are searched for
|
|
47
|
+
const WINDOW_BEFORE = 40; // painted rows kept before the hit
|
|
48
|
+
const WINDOW_AFTER = 400; // painted rows kept after the hit
|
|
49
|
+
|
|
50
|
+
const stripSgr = (text) => String(text == null ? '' : text).replace(SGR_RE, '');
|
|
51
|
+
const trimEnd = (text) => String(text == null ? '' : text).replace(/\s+$/, '');
|
|
52
|
+
|
|
53
|
+
function parseArgs(argv) {
|
|
54
|
+
const opts = {
|
|
55
|
+
painted: path.join(os.homedir(), '.walle', 'data', 'painted-rows'),
|
|
56
|
+
projects: path.join(os.homedir(), '.claude', 'projects'),
|
|
57
|
+
json: false,
|
|
58
|
+
};
|
|
59
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
60
|
+
const arg = String(argv[i]);
|
|
61
|
+
const eq = arg.indexOf('=');
|
|
62
|
+
const name = eq < 0 ? arg : arg.slice(0, eq);
|
|
63
|
+
const inline = eq < 0 ? null : arg.slice(eq + 1);
|
|
64
|
+
if (name === '--painted') opts.painted = (inline != null ? inline : argv[i + 1]) || opts.painted;
|
|
65
|
+
else if (name === '--projects') opts.projects = (inline != null ? inline : argv[i + 1]) || opts.projects;
|
|
66
|
+
else if (name === '--json') opts.json = true;
|
|
67
|
+
}
|
|
68
|
+
return opts;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Immediate sub-directory names of `dir`, sorted (deterministic output). A missing root is a
|
|
72
|
+
// legitimate empty census on a machine that never ran the reader.
|
|
73
|
+
function listDirs(dir) {
|
|
74
|
+
let entries = [];
|
|
75
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return []; } /* expected: the store/projects root may not exist here */
|
|
76
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function readText(file) {
|
|
80
|
+
try { return fs.readFileSync(file, 'utf8'); } catch { return ''; } /* expected: a store file may vanish under a live writer */
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// One `JSON<TAB>crc` line; the CRC is not verified (the stores are append-only and the census
|
|
84
|
+
// reads numbers, not truth).
|
|
85
|
+
function parseLine(prefix) {
|
|
86
|
+
try { return JSON.parse(prefix); } catch { return null; } // a torn / non-JSON line is not a record
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function findTranscript(projectsRoot, projectDirs, sessionId) {
|
|
90
|
+
for (const project of projectDirs) {
|
|
91
|
+
const file = path.join(projectsRoot, project, `${sessionId}.jsonl`);
|
|
92
|
+
if (fs.existsSync(file)) return file;
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Every painted row of one session's store: `{ r, f, cols, t }` with `t` = SGR-stripped,
|
|
98
|
+
// right-trimmed text. `cols` is the width the surrounding stretch header recorded (a row written
|
|
99
|
+
// before the first header of the file keeps `null`).
|
|
100
|
+
function readPainted(dir) {
|
|
101
|
+
const rows = [];
|
|
102
|
+
for (const file of listFiles(dir).filter((f) => /^stretch-\d+\.jsonl$/.test(f)).sort()) {
|
|
103
|
+
let cols = null;
|
|
104
|
+
const text = readText(path.join(dir, file));
|
|
105
|
+
if (!text) continue;
|
|
106
|
+
for (const line of text.split('\n')) {
|
|
107
|
+
const tab = line.lastIndexOf('\t');
|
|
108
|
+
if (tab < 0) continue;
|
|
109
|
+
const record = parseLine(line.slice(0, tab));
|
|
110
|
+
if (!record) continue;
|
|
111
|
+
if (record.stretch != null && record.cols) cols = record.cols;
|
|
112
|
+
if (record.r != null) rows.push({ r: record.r, f: file, cols, t: trimEnd(stripSgr(record.a)) });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return rows;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function listFiles(dir) {
|
|
119
|
+
try { return fs.readdirSync(dir); } catch { return []; } /* expected: the session store may be pruned between readdir and read */
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Every `table` token of one assistant text block. The `|` pre-check only skips blocks that cannot
|
|
123
|
+
// hold a GFM table -- the decision is `marked`'s, never a local grammar.
|
|
124
|
+
function lexTables(text) {
|
|
125
|
+
if (!text.includes('|')) return [];
|
|
126
|
+
let tokens = [];
|
|
127
|
+
try { tokens = marked.lexer(text, { gfm: true }); } catch { return []; } // an unlexable block holds no table we can census
|
|
128
|
+
return tokens.filter((token) => token.type === 'table');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// A cell's plain text: `label (href)` for a link (R-3 -- the rendering, not the raw markdown, is
|
|
132
|
+
// what the layout measures) and the literal text everywhere else.
|
|
133
|
+
function cellText(tokens) {
|
|
134
|
+
let out = '';
|
|
135
|
+
for (const token of tokens || []) {
|
|
136
|
+
switch (token.type) {
|
|
137
|
+
case 'text': case 'escape': out += token.text; break;
|
|
138
|
+
case 'codespan': out += token.text; break;
|
|
139
|
+
case 'strong': case 'em': case 'del': out += cellText(token.tokens); break;
|
|
140
|
+
case 'link': {
|
|
141
|
+
const label = cellText(token.tokens);
|
|
142
|
+
out += label === token.href ? label : `${label} (${token.href})`;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
case 'image': out += `${token.text} (${token.href})`; break;
|
|
146
|
+
case 'br': out += ' '; break;
|
|
147
|
+
case 'checkbox': break;
|
|
148
|
+
default: out += token.text != null ? token.text : (token.raw || '');
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// The form the LIVE screen drew, read off the hit row and its neighbours: a row starting with the
|
|
155
|
+
// box border is `bordered`; otherwise a 40-dash card rule or a `Header:` label within
|
|
156
|
+
// `2 * columns + 2` rows of the hit says `cards`; anything else is `other` (a drawing we could not
|
|
157
|
+
// classify -- counted, never predicted against).
|
|
158
|
+
function observedForm(hit, painted, labels) {
|
|
159
|
+
if (/^\s*│/.test(hit.t)) return 'bordered';
|
|
160
|
+
const near = painted
|
|
161
|
+
.filter((p) => p.f === hit.f && p.r >= hit.r - 2 && p.r <= hit.r + 2 * labels.length + 2)
|
|
162
|
+
.map((p) => p.t)
|
|
163
|
+
.join('\n');
|
|
164
|
+
if (near.includes(CARD_RULE) || labels.some((label) => label && near.includes(`${label}:`))) return 'cards';
|
|
165
|
+
return 'other';
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// The re-draw: the shipped renderer at `cols`, SGR-stripped and right-trimmed, exactly as the
|
|
169
|
+
// painted rows are.
|
|
170
|
+
function renderRows(markdown, cols) {
|
|
171
|
+
return emitMarkdownRows(markdown, cols, { markdown: marked, style: MD_STYLE })
|
|
172
|
+
.map((line) => trimEnd(stripSgr(line)));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// The prediction for one drawing: try every width the session's stretch headers recorded, align the
|
|
176
|
+
// emitted rows to the painted window by the offset with the most identical rows, keep the best
|
|
177
|
+
// width. `predicted` is the form the re-draw took (no `│` row => the stacked cards).
|
|
178
|
+
function predict(raw, widths, window) {
|
|
179
|
+
let best = null;
|
|
180
|
+
for (const cols of widths) {
|
|
181
|
+
const lines = renderRows(raw, cols);
|
|
182
|
+
let identical = 0;
|
|
183
|
+
for (let off = 0; off < window.length; off += 1) {
|
|
184
|
+
let same = 0;
|
|
185
|
+
for (let i = 0; i < lines.length; i += 1) if (lines[i] === window[off + i]) same += 1;
|
|
186
|
+
if (same > identical) identical = same;
|
|
187
|
+
}
|
|
188
|
+
const candidate = {
|
|
189
|
+
cols,
|
|
190
|
+
predicted: lines.some((line) => line.includes('│')) ? 'bordered' : 'cards',
|
|
191
|
+
identical,
|
|
192
|
+
emitted: lines.length,
|
|
193
|
+
};
|
|
194
|
+
if (!best || candidate.identical > best.identical) best = candidate;
|
|
195
|
+
}
|
|
196
|
+
return best;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// One table token -> its record(s): one per stretch file that holds the probe, or a single
|
|
200
|
+
// `notfound` record when no cell is long enough / no painted row matches.
|
|
201
|
+
function locateTable({ sid, index, token, painted, widths }) {
|
|
202
|
+
const header = (token.header || []).map((cell) => cellText(cell.tokens));
|
|
203
|
+
const rows = (token.rows || []).map((row) => row.map((cell) => cellText(cell.tokens)));
|
|
204
|
+
const base = { sid: sid.slice(0, 8), table: index, columns: header.length, rows: rows.length };
|
|
205
|
+
const probe = (rows[0] || []).find((cell) => cell && cell.length >= PROBE_MIN)
|
|
206
|
+
|| header.find((cell) => cell && cell.length >= PROBE_MIN)
|
|
207
|
+
|| '';
|
|
208
|
+
if (!probe) return [{ ...base, form: 'notfound', predicted: null, identical: 0, emitted: 0, cols: null }];
|
|
209
|
+
const key = probe.slice(0, PROBE_LEN);
|
|
210
|
+
const hits = new Map(); // file -> the first painted row of that file holding the probe
|
|
211
|
+
for (const row of painted) if (!hits.has(row.f) && row.t.includes(key)) hits.set(row.f, row);
|
|
212
|
+
if (hits.size === 0) return [{ ...base, form: 'notfound', predicted: null, identical: 0, emitted: 0, cols: null }];
|
|
213
|
+
const out = [];
|
|
214
|
+
for (const hit of hits.values()) {
|
|
215
|
+
const window = painted
|
|
216
|
+
.filter((p) => p.f === hit.f && p.r >= hit.r - WINDOW_BEFORE && p.r <= hit.r + WINDOW_AFTER)
|
|
217
|
+
.map((p) => p.t);
|
|
218
|
+
const candidates = widths.length ? widths : (hit.cols ? [hit.cols] : []);
|
|
219
|
+
const best = predict(token.raw, candidates, window) || { cols: hit.cols, predicted: null, identical: 0, emitted: 0 };
|
|
220
|
+
out.push({ ...base, at: hit.r, form: observedForm(hit, painted, header), ...best });
|
|
221
|
+
}
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function census(opts) {
|
|
226
|
+
const storeDirs = listDirs(opts.painted);
|
|
227
|
+
const projectDirs = listDirs(opts.projects);
|
|
228
|
+
const records = [];
|
|
229
|
+
let sessionsWithStore = 0;
|
|
230
|
+
let sessionsPaired = 0;
|
|
231
|
+
let tables = 0;
|
|
232
|
+
for (const sid of storeDirs) {
|
|
233
|
+
sessionsWithStore += 1;
|
|
234
|
+
const transcript = findTranscript(opts.projects, projectDirs, sid);
|
|
235
|
+
if (!transcript) continue;
|
|
236
|
+
sessionsPaired += 1;
|
|
237
|
+
const painted = readPainted(path.join(opts.painted, sid));
|
|
238
|
+
const widths = [...new Set(painted.map((row) => row.cols).filter(Boolean))];
|
|
239
|
+
let index = 0;
|
|
240
|
+
for (const line of readText(transcript).split('\n')) {
|
|
241
|
+
const message = parseLine(line);
|
|
242
|
+
if (!message || !message.message || message.message.role !== 'assistant') continue;
|
|
243
|
+
const parts = Array.isArray(message.message.content)
|
|
244
|
+
? message.message.content
|
|
245
|
+
: [{ type: 'text', text: message.message.content }];
|
|
246
|
+
for (const part of parts) {
|
|
247
|
+
if (!part || part.type !== 'text' || typeof part.text !== 'string') continue;
|
|
248
|
+
for (const token of lexTables(part.text)) {
|
|
249
|
+
index += 1;
|
|
250
|
+
tables += 1;
|
|
251
|
+
records.push(...locateTable({ sid, index, token, painted, widths }));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const observed = { bordered: 0, cards: 0, other: 0, notfound: 0 };
|
|
258
|
+
for (const record of records) observed[record.form] = (observed[record.form] || 0) + 1;
|
|
259
|
+
// `drawings` = the drawings whose form we could read (bordered + cards) -- the denominator of
|
|
260
|
+
// predictedAgree, the number the design's floor (313 of 328, §1.2) is stated against. `other`
|
|
261
|
+
// hits and `notfound` tables are carried in `observed` and never predicted against.
|
|
262
|
+
const drawings = observed.bordered + observed.cards;
|
|
263
|
+
const found = records.filter((record) => record.form === 'bordered' || record.form === 'cards');
|
|
264
|
+
const predictedAgree = found.filter((record) => record.predicted === record.form).length;
|
|
265
|
+
const byteIdentical = found.filter((record) => record.emitted > 0 && record.identical === record.emitted).length;
|
|
266
|
+
const within2 = found.filter((record) => record.emitted > 0 && record.identical >= record.emitted - 2).length;
|
|
267
|
+
return {
|
|
268
|
+
summary: {
|
|
269
|
+
sessionsWithStore,
|
|
270
|
+
sessionsPaired,
|
|
271
|
+
tables,
|
|
272
|
+
drawings,
|
|
273
|
+
observed,
|
|
274
|
+
predictedAgree,
|
|
275
|
+
predictedDisagree: found.length - predictedAgree,
|
|
276
|
+
byteIdentical,
|
|
277
|
+
within2,
|
|
278
|
+
},
|
|
279
|
+
records,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function main() {
|
|
284
|
+
const opts = parseArgs(process.argv.slice(2));
|
|
285
|
+
const { summary, records } = census(opts);
|
|
286
|
+
process.stdout.write(`${JSON.stringify(summary)}\n`);
|
|
287
|
+
if (opts.json) return;
|
|
288
|
+
// Disagreement list: the observed form the prediction missed, or a re-draw that lost more than
|
|
289
|
+
// two rows of the painted drawing. Numbers and an 8-character session prefix only -- no cell.
|
|
290
|
+
const disagreements = records
|
|
291
|
+
.filter((record) => record.form !== 'notfound'
|
|
292
|
+
&& (record.predicted !== record.form || record.identical < record.emitted - 2))
|
|
293
|
+
.sort((a, b) => (a.sid === b.sid ? a.table - b.table : a.sid.localeCompare(b.sid)));
|
|
294
|
+
for (const record of disagreements) {
|
|
295
|
+
process.stdout.write(`${record.sid} table#${record.table} cols=${record.cols} `
|
|
296
|
+
+ `observed=${record.form} predicted=${record.predicted} identical=${record.identical}/${record.emitted}\n`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
try {
|
|
301
|
+
main();
|
|
302
|
+
} catch (error) {
|
|
303
|
+
// The census is a report, not a test: an unexpected failure is printed on stderr and still
|
|
304
|
+
// exits 0 (numbers only -- never a painted row or a cell).
|
|
305
|
+
process.stderr.write(`claude-table-parity: ${error && error.message ? error.message : String(error)}\n`);
|
|
306
|
+
}
|
|
307
|
+
process.exitCode = 0;
|