diff --git a/scripts/architecture-baseline.json b/scripts/architecture-baseline.json index c836ce8..01953e6 100644 --- a/scripts/architecture-baseline.json +++ b/scripts/architecture-baseline.json @@ -1,7 +1,9 @@ { "version": 1, "violations": [ + "clone 003d2210c257 src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-fleet-results.test.ts", "clone 00803486e758 src/engine/__tests__/loop-helpers-history.test.ts,src/engine/__tests__/loop-helpers-readonly.test.ts,src/engine/__tests__/parser.test.ts,src/engine/__tests__/summarize-tool-arg.test.ts,src/memory/__tests__/embed.test.ts,src/memory/__tests__/summarizer.test.ts", + "clone 00815c1c8497 src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-integrity.test.ts", "clone 0081a7d462ab src/orchestrator/__tests__/cost-history-cli.test.ts,src/orchestrator/__tests__/cost-history.test.ts", "clone 0152e2318e4e src/engine/__tests__/loop-helpers-history.test.ts,src/engine/__tests__/loop-helpers-readonly.test.ts,src/engine/__tests__/new-task.test.ts,src/engine/__tests__/parser.test.ts,src/engine/__tests__/summarize-tool-arg.test.ts,src/engine/__tests__/switch-mode.test.ts", "clone 01a56a433e82 src/orchestrator/task-files.ts", @@ -99,6 +101,7 @@ "clone 1a7604033aad src/orchestrator/resume.ts", "clone 1a974e3f1b8f src/engine/__tests__/condense.test.ts", "clone 1a9bcaf25eea src/orchestrator/__tests__/cli.test.ts", + "clone 1ab283ab6f33 src/rsi/controller.ts", "clone 1ad74508c766 src/orchestrator/task-files.ts", "clone 1b0114e0a60e src/orchestrator/__tests__/cli.test.ts", "clone 1b2b48c0199b src/orchestrator/cli.ts,src/watcher/cli.ts", @@ -126,9 +129,11 @@ "clone 20543a8e606e src/orchestrator/resume.ts", "clone 2074b9803423 src/orchestrator/reviewer.ts,src/qa/qa.ts", "clone 20b2f94a6422 src/orchestrator/__tests__/spawn-model.test.ts,src/orchestrator/__tests__/spawn-state-reset.test.ts", + "clone 20c881b5b0c4 src/rsi/controller.ts", "clone 217efafa63c3 src/orchestrator/__tests__/spawn-model.test.ts,src/orchestrator/__tests__/spawn-state-reset.test.ts", "clone 21fd3bfb944d src/dashboard/__tests__/cost-history-routes.test.ts,src/orchestrator/__tests__/cost-history-cli.test.ts", "clone 224a1f3d5365 src/orchestrator/resume.ts", + "clone 22f7d06a0abd src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-fleet-results.test.ts", "clone 230967798bac src/engine/__tests__/global-modes.test.ts,src/engine/__tests__/prompt.test.ts,src/engine/__tests__/stack-rules-content.test.ts,src/engine/__tests__/stacks.test.ts,src/llm/__tests__/muse.test.ts,src/llm/__tests__/openrouter.test.ts,src/stack-rules-content.test.ts", "clone 231bc945ff50 src/control/emergency-controller.ts", "clone 232f1b0a8918 src/engine/__tests__/loop.test.ts", @@ -184,6 +189,7 @@ "clone 3341cac7ce47 src/orchestrator/task-files.ts", "clone 338cc9087bf2 src/dashboard/__tests__/cost-history-routes.test.ts,src/orchestrator/__tests__/cost-history-cli.test.ts,src/orchestrator/__tests__/cost-history.test.ts", "clone 3390241b01d9 src/budget/__tests__/budget.test.ts,src/budget/__tests__/concurrency.test.ts,src/budget/__tests__/cost.test.ts,src/checkpoints/__tests__/service.test.ts,src/cloud/__tests__/provider.test.ts,src/codeintel/__tests__/call-graph.test.ts,src/codeintel/__tests__/call-hierarchy.test.ts,src/codeintel/__tests__/find-references.test.ts,src/codeintel/__tests__/go-to-definition.test.ts,src/codeintel/__tests__/hover.test.ts,src/codeintel/__tests__/import-graph.test.ts,src/codeintel/__tests__/outline.test.ts,src/codeintel/__tests__/rename-symbol.test.ts,src/codemap/__tests__/build.test.ts,src/codesearch/__tests__/airunner-embedder.test.ts,src/codesearch/__tests__/chunk.test.ts,src/codesearch/__tests__/files.test.ts,src/codesearch/__tests__/ollama-embedder.test.ts,src/dashboard/__tests__/aggregate.test.ts,src/dashboard/__tests__/chat-thread.test.ts,src/dashboard/__tests__/checkpoints-routes.test.ts,src/dashboard/__tests__/codemap-routes.test.ts,src/dashboard/__tests__/cost-history-routes.test.ts,src/dashboard/__tests__/events.test.ts,src/dashboard/__tests__/projects-routes.test.ts,src/dashboard/__tests__/server.test.ts,src/dashboard/__tests__/session-launch.test.ts,src/dashboard/__tests__/timeline.test.ts,src/deploy/__tests__/gate.test.ts,src/engine/__tests__/condense-async.test.ts,src/engine/__tests__/condense.test.ts,src/engine/__tests__/events.test.ts,src/engine/__tests__/inject-message.test.ts,src/engine/__tests__/local-explore.test.ts,src/engine/__tests__/loop.test.ts,src/engine/__tests__/new-task.test.ts,src/engine/__tests__/pause.test.ts,src/engine/__tests__/switch-mode.test.ts,src/github/__tests__/app-auth.test.ts,src/github/__tests__/cli.test.ts,src/github/__tests__/pr.test.ts,src/github/__tests__/provision.test.ts,src/github/__tests__/push.test.ts,src/index-util.test.ts,src/init/__tests__/cli.test.ts,src/init/__tests__/gitignore.test.ts,src/memory/__tests__/local.test.ts,src/memory/__tests__/memory-wiring.test.ts,src/memory/__tests__/uwuchat.test.ts,src/migrate/cli.test.ts,src/orchestrator/__tests__/arg-cursor.test.ts,src/orchestrator/__tests__/auto-split.test.ts,src/orchestrator/__tests__/cleanup.test.ts,src/orchestrator/__tests__/git-sync.test.ts,src/orchestrator/__tests__/merge-check.test.ts,src/orchestrator/__tests__/pipeline.test.ts,src/orchestrator/__tests__/reviewer.test.ts,src/orchestrator/__tests__/split.test.ts,src/orchestrator/__tests__/state.test.ts,src/orchestrator/__tests__/status-cli.test.ts,src/orchestrator/__tests__/stop-cli.test.ts,src/permissions/__tests__/commands.test.ts,src/permissions/__tests__/store-protection.test.ts,src/project-store.test.ts,src/projects/__tests__/cli.test.ts,src/qa/__tests__/qa.test.ts,src/tools/__tests__/apply_diff.test.ts,src/tools/__tests__/browser-action.test.ts,src/tools/__tests__/decision.test.ts,src/tools/__tests__/describe_image.test.ts,src/tools/__tests__/edit_file.test.ts,src/tools/__tests__/execute_command.test.ts,src/tools/__tests__/language-detect.test.ts,src/tools/__tests__/list-files.test.ts,src/tools/__tests__/local-summarization-gate.test.ts,src/tools/__tests__/output-summarizer.test.ts,src/tools/__tests__/read-cache.test.ts,src/tools/__tests__/run-tests.test.ts,src/tools/__tests__/search_replace.test.ts,src/tools/__tests__/update_todo_list.test.ts,src/tools/__tests__/write_to_file.test.ts,src/vision/__tests__/describe.test.ts,src/watcher/__tests__/github.test.ts,src/watcher/__tests__/state.test.ts,src/watcher/__tests__/watch.test.ts", + "clone 33c630390d21 src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-integrity.test.ts", "clone 33e68b8dfd03 src/llm/muse.ts,src/llm/openrouter.ts", "clone 3437313b4dfa src/llm/__tests__/muse.test.ts,src/llm/__tests__/openrouter.test.ts", "clone 34f0de1797fc src/orchestrator/task-files.ts", @@ -209,6 +215,7 @@ "clone 3bd848fdccd6 src/orchestrator/__tests__/spawn-model.test.ts,src/orchestrator/__tests__/spawn-state-reset.test.ts", "clone 3d0068ebfcec src/engine/__tests__/condense.test.ts", "clone 3d081917c13c src/orchestrator/__tests__/reviewer.test.ts,src/qa/__tests__/qa.test.ts", + "clone 3db2ffdde10b src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-fleet-results.test.ts", "clone 3dc5be5d75d0 src/orchestrator/task-files.ts", "clone 3dce629a0b04 src/dashboard/cli.ts,src/dashboard/trend-cli.ts", "clone 3e0115ad88b7 src/engine/loop.ts", @@ -341,9 +348,11 @@ "clone 5fff6e3ebed7 src/dashboard/__tests__/cost-history-routes.test.ts,src/orchestrator/__tests__/cost-history-cli.test.ts", "clone 60c4fdaf31bd src/codesearch/airunner-embedder.ts,src/codesearch/ollama-embedder.ts", "clone 61046dbb6410 src/budget/__tests__/budget.test.ts,src/budget/__tests__/concurrency.test.ts,src/budget/__tests__/cost.test.ts,src/checkpoints/__tests__/service.test.ts,src/cloud/__tests__/provider.test.ts,src/codeintel/__tests__/call-graph.test.ts,src/codeintel/__tests__/call-hierarchy.test.ts,src/codeintel/__tests__/find-references.test.ts,src/codeintel/__tests__/go-to-definition.test.ts,src/codeintel/__tests__/hover.test.ts,src/codeintel/__tests__/import-graph.test.ts,src/codeintel/__tests__/outline.test.ts,src/codeintel/__tests__/rename-symbol.test.ts,src/codemap/__tests__/build.test.ts,src/codesearch/__tests__/airunner-embedder.test.ts,src/codesearch/__tests__/chunk.test.ts,src/codesearch/__tests__/files.test.ts,src/codesearch/__tests__/ollama-embedder.test.ts,src/dashboard/__tests__/aggregate.test.ts,src/dashboard/__tests__/chat-thread.test.ts,src/dashboard/__tests__/checkpoints-routes.test.ts,src/dashboard/__tests__/codemap-routes.test.ts,src/dashboard/__tests__/cost-history-routes.test.ts,src/dashboard/__tests__/events.test.ts,src/dashboard/__tests__/projects-routes.test.ts,src/dashboard/__tests__/server.test.ts,src/dashboard/__tests__/session-launch.test.ts,src/dashboard/__tests__/timeline.test.ts,src/deploy/__tests__/gate.test.ts,src/engine/__tests__/condense-async.test.ts,src/engine/__tests__/condense.test.ts,src/engine/__tests__/events.test.ts,src/engine/__tests__/inject-message.test.ts,src/engine/__tests__/local-explore.test.ts,src/engine/__tests__/loop.test.ts,src/engine/__tests__/new-task.test.ts,src/engine/__tests__/pause.test.ts,src/engine/__tests__/switch-mode.test.ts,src/github/__tests__/app-auth.test.ts,src/github/__tests__/cli.test.ts,src/github/__tests__/pr.test.ts,src/github/__tests__/provision.test.ts,src/github/__tests__/push.test.ts,src/index-util.test.ts,src/init/__tests__/cli.test.ts,src/init/__tests__/gitignore.test.ts,src/memory/__tests__/local.test.ts,src/memory/__tests__/memory-wiring.test.ts,src/memory/__tests__/uwuchat.test.ts,src/migrate/cli.test.ts,src/orchestrator/__tests__/arg-cursor.test.ts,src/orchestrator/__tests__/auto-split.test.ts,src/orchestrator/__tests__/cleanup.test.ts,src/orchestrator/__tests__/git-sync.test.ts,src/orchestrator/__tests__/merge-check.test.ts,src/orchestrator/__tests__/pipeline.test.ts,src/orchestrator/__tests__/reviewer.test.ts,src/orchestrator/__tests__/split.test.ts,src/orchestrator/__tests__/state.test.ts,src/orchestrator/__tests__/status-cli.test.ts,src/orchestrator/__tests__/stop-cli.test.ts,src/permissions/__tests__/commands.test.ts,src/permissions/__tests__/store-protection.test.ts,src/project-store.test.ts,src/projects/__tests__/cli.test.ts,src/qa/__tests__/qa.test.ts,src/tools/__tests__/apply_diff.test.ts,src/tools/__tests__/browser-action.test.ts,src/tools/__tests__/decision.test.ts,src/tools/__tests__/describe_image.test.ts,src/tools/__tests__/edit_file.test.ts,src/tools/__tests__/execute_command.test.ts,src/tools/__tests__/language-detect.test.ts,src/tools/__tests__/list-files.test.ts,src/tools/__tests__/local-summarization-gate.test.ts,src/tools/__tests__/output-summarizer.test.ts,src/tools/__tests__/path-safety.test.ts,src/tools/__tests__/read-cache.test.ts,src/tools/__tests__/run-tests.test.ts,src/tools/__tests__/search_replace.test.ts,src/tools/__tests__/update_todo_list.test.ts,src/tools/__tests__/write_to_file.test.ts,src/vision/__tests__/describe.test.ts,src/watcher/__tests__/github.test.ts,src/watcher/__tests__/state.test.ts,src/watcher/__tests__/watch.test.ts", + "clone 61b9caaf613a src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-fleet-results.test.ts", "clone 6255fbea917d src/orchestrator/__tests__/cost-history-cli.test.ts,src/orchestrator/__tests__/cost-history.test.ts", "clone 6287c1413aa4 src/budget/__tests__/budget.test.ts,src/budget/__tests__/concurrency.test.ts,src/budget/__tests__/cost.test.ts,src/checkpoints/__tests__/service.test.ts,src/cloud/__tests__/provider.test.ts,src/codeintel/__tests__/call-graph.test.ts,src/codeintel/__tests__/call-hierarchy.test.ts,src/codeintel/__tests__/find-references.test.ts,src/codeintel/__tests__/go-to-definition.test.ts,src/codeintel/__tests__/hover.test.ts,src/codeintel/__tests__/import-graph.test.ts,src/codeintel/__tests__/outline.test.ts,src/codeintel/__tests__/rename-symbol.test.ts,src/codemap/__tests__/build.test.ts,src/codesearch/__tests__/airunner-embedder.test.ts,src/codesearch/__tests__/chunk.test.ts,src/codesearch/__tests__/files.test.ts,src/codesearch/__tests__/ollama-embedder.test.ts,src/dashboard/__tests__/aggregate.test.ts,src/dashboard/__tests__/chat-thread.test.ts,src/dashboard/__tests__/checkpoints-routes.test.ts,src/dashboard/__tests__/codemap-routes.test.ts,src/dashboard/__tests__/cost-history-routes.test.ts,src/dashboard/__tests__/events.test.ts,src/dashboard/__tests__/projects-routes.test.ts,src/dashboard/__tests__/server.test.ts,src/dashboard/__tests__/session-launch.test.ts,src/dashboard/__tests__/timeline.test.ts,src/deploy/__tests__/gate.test.ts,src/engine/__tests__/condense-async.test.ts,src/engine/__tests__/condense.test.ts,src/engine/__tests__/events.test.ts,src/engine/__tests__/inject-message.test.ts,src/engine/__tests__/local-explore.test.ts,src/engine/__tests__/loop-helpers-history.test.ts,src/engine/__tests__/loop-helpers-readonly.test.ts,src/engine/__tests__/loop.test.ts,src/engine/__tests__/new-task.test.ts,src/engine/__tests__/parser.test.ts,src/engine/__tests__/pause.test.ts,src/engine/__tests__/summarize-tool-arg.test.ts,src/engine/__tests__/switch-mode.test.ts,src/github/__tests__/app-auth.test.ts,src/github/__tests__/cli.test.ts,src/github/__tests__/pr.test.ts,src/github/__tests__/provision.test.ts,src/github/__tests__/push.test.ts,src/index-util.test.ts,src/init/__tests__/cli.test.ts,src/init/__tests__/gitignore.test.ts,src/memory/__tests__/embed.test.ts,src/memory/__tests__/local.test.ts,src/memory/__tests__/memory-wiring.test.ts,src/memory/__tests__/summarizer.test.ts,src/memory/__tests__/uwuchat.test.ts,src/migrate/cli.test.ts,src/orchestrator/__tests__/arg-cursor.test.ts,src/orchestrator/__tests__/auto-split.test.ts,src/orchestrator/__tests__/cleanup.test.ts,src/orchestrator/__tests__/git-sync.test.ts,src/orchestrator/__tests__/merge-check.test.ts,src/orchestrator/__tests__/pipeline.test.ts,src/orchestrator/__tests__/reviewer.test.ts,src/orchestrator/__tests__/split.test.ts,src/orchestrator/__tests__/state.test.ts,src/orchestrator/__tests__/status-cli.test.ts,src/orchestrator/__tests__/stop-cli.test.ts,src/permissions/__tests__/commands.test.ts,src/permissions/__tests__/store-protection.test.ts,src/project-store.test.ts,src/projects/__tests__/cli.test.ts,src/qa/__tests__/qa.test.ts,src/tools/__tests__/apply_diff.test.ts,src/tools/__tests__/browser-action.test.ts,src/tools/__tests__/decision.test.ts,src/tools/__tests__/describe_image.test.ts,src/tools/__tests__/edit_file.test.ts,src/tools/__tests__/execute_command.test.ts,src/tools/__tests__/language-detect.test.ts,src/tools/__tests__/list-files.test.ts,src/tools/__tests__/local-summarization-gate.test.ts,src/tools/__tests__/output-summarizer.test.ts,src/tools/__tests__/read-cache.test.ts,src/tools/__tests__/run-tests.test.ts,src/tools/__tests__/search_replace.test.ts,src/tools/__tests__/update_todo_list.test.ts,src/tools/__tests__/write_to_file.test.ts,src/vision/__tests__/describe.test.ts,src/watcher/__tests__/github.test.ts,src/watcher/__tests__/state.test.ts,src/watcher/__tests__/watch.test.ts", "clone 62d28c84ccfe src/orchestrator/cli.ts", + "clone 62fb95ada976 src/rsi/controller.ts", "clone 62ff58c84315 src/engine/__tests__/loop-helpers-history.test.ts,src/engine/__tests__/loop-helpers-readonly.test.ts,src/engine/__tests__/parser.test.ts,src/engine/__tests__/summarize-tool-arg.test.ts,src/memory/__tests__/embed.test.ts,src/memory/__tests__/summarizer.test.ts,src/stack-rules-content.test.ts", "clone 6376e4c9bfd3 src/github/__tests__/provision.test.ts", "clone 63fb58a6271f src/orchestrator/__tests__/cli.test.ts", @@ -366,6 +375,7 @@ "clone 66d67e5adea9 src/dashboard/__tests__/cost-history-routes.test.ts,src/orchestrator/__tests__/cost-history-cli.test.ts,src/orchestrator/__tests__/cost-history.test.ts", "clone 66ec447e84b0 src/engine/__tests__/switch-mode.test.ts", "clone 672782f20b04 src/orchestrator/__tests__/resume.test.ts", + "clone 67833e34f406 src/rsi/controller.ts", "clone 67cd395b3adb src/checkpoints/__tests__/cli.test.ts,src/codemap/__tests__/cli.test.ts,src/codemap/__tests__/units.test.ts,src/dashboard/__tests__/cli.test.ts,src/dashboard/__tests__/page-route.test.ts,src/dashboard/__tests__/self-improvement-metrics.test.ts,src/dashboard/__tests__/sigint-shutdown.test.ts,src/github/__tests__/installations.test.ts,src/orchestrator/__tests__/analyze-cli.test.ts,src/orchestrator/__tests__/cost-estimate.test.ts,src/orchestrator/__tests__/log-analysis.test.ts,src/orchestrator/__tests__/spawn-state-reset.test.ts,src/orchestrator/__tests__/status.test.ts,src/run-tests-script.test.ts,src/tools/__tests__/set-indentation.test.ts", "clone 6846b0b81f2a src/llm/__tests__/preflight.test.ts,src/orchestrator/__tests__/cli.test.ts", "clone 68612079ddc5 src/llm/preflight.ts", @@ -386,6 +396,7 @@ "clone 6bf4008bd80c src/orchestrator/__tests__/cli.test.ts", "clone 6c4b7719d621 src/engine/__tests__/loop.test.ts", "clone 6c650d02957b src/codeintel/__tests__/registration.test.ts,src/tools/__tests__/batch.test.ts", + "clone 6cade99be57e src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-fleet-results.test.ts", "clone 6cc5e85990d1 src/orchestrator/__tests__/spawn-model.test.ts,src/orchestrator/__tests__/spawn-state-reset.test.ts", "clone 6cd34638ecf4 src/codesearch/airunner-embedder.ts,src/codesearch/ollama-embedder.ts", "clone 6da4bb48c44f src/dashboard/__tests__/projects-routes.test.ts,src/projects/__tests__/cli.test.ts", @@ -413,6 +424,7 @@ "clone 7442b675f932 src/checkpoints/__tests__/cli.test.ts,src/codemap/__tests__/cli.test.ts,src/dashboard/__tests__/cli.test.ts", "clone 74aff4c78d51 src/budget/__tests__/concurrency.test.ts,src/watcher/__tests__/watch.test.ts", "clone 7555b7afa021 src/github/pr.ts", + "clone 75974748618c src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-fleet-results.test.ts", "clone 75a2f737103a src/dashboard/server.ts", "clone 75f4bd5083d9 src/orchestrator/__tests__/cli.test.ts", "clone 761c74a611a2 src/codesearch/embedder.ts,src/memory/embed.ts", @@ -446,11 +458,14 @@ "clone 7b4511a4efb0 src/orchestrator/__tests__/spawn-model.test.ts,src/orchestrator/__tests__/spawn-state-reset.test.ts", "clone 7bab51178bf0 src/orchestrator/task-files.ts", "clone 7bae656083ca src/dashboard/__tests__/server.test.ts,src/dashboard/__tests__/session-launch.test.ts", + "clone 7c2951693384 src/rsi/controller.ts", "clone 7c53f8bda90c src/orchestrator/__tests__/cli.test.ts", + "clone 7cb849d1bd52 src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-fleet-results.test.ts", "clone 7cf1a4f8c0d9 src/engine/__tests__/inject-message.test.ts,src/engine/__tests__/pause.test.ts", "clone 7d2fcb96c253 src/orchestrator/resume.ts", "clone 7d743a1a43b3 src/dashboard/__tests__/projects-routes.test.ts,src/projects/__tests__/cli.test.ts", "clone 7d9fa9503333 src/engine/__tests__/condense.test.ts,src/engine/__tests__/events.test.ts", + "clone 7dd51a187649 src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-fleet-results.test.ts,src/rsi/__tests__/controller-integrity.test.ts", "clone 7ddc99484482 src/dashboard/server.ts", "clone 7dfc9cbdc2b8 src/orchestrator/task-files.ts", "clone 7e1bcf45c7b2 src/engine/__tests__/loop-helpers-history.test.ts,src/engine/__tests__/loop-helpers-readonly.test.ts,src/engine/__tests__/parser.test.ts,src/engine/__tests__/summarize-tool-arg.test.ts,src/memory/__tests__/embed.test.ts,src/memory/__tests__/summarizer.test.ts", @@ -569,6 +584,7 @@ "clone 99ba2224e54b src/llm/muse.ts,src/llm/openrouter.ts", "clone 99c8753a4cab src/checkpoints/__tests__/cli.test.ts,src/codemap/__tests__/cli.test.ts,src/codemap/__tests__/units.test.ts,src/dashboard/__tests__/cli.test.ts,src/dashboard/__tests__/page-route.test.ts,src/dashboard/__tests__/self-improvement-metrics.test.ts,src/dashboard/__tests__/sigint-shutdown.test.ts,src/dashboard/__tests__/trend.test.ts,src/github/__tests__/installations.test.ts,src/orchestrator/__tests__/analyze-cli.test.ts,src/orchestrator/__tests__/cost-estimate.test.ts,src/orchestrator/__tests__/log-analysis.test.ts,src/orchestrator/__tests__/spawn-state-reset.test.ts,src/orchestrator/__tests__/status.test.ts,src/run-tests-script.test.ts,src/tools/__tests__/set-indentation.test.ts", "clone 99ebebf858ee src/codesearch/__tests__/airunner-embedder.test.ts,src/codesearch/__tests__/ollama-embedder.test.ts", + "clone 9a2d9c8ece5a src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-integrity.test.ts", "clone 9a34727f0f71 src/engine/__tests__/condense.test.ts", "clone 9a5822d78a2b src/orchestrator/task-files.ts", "clone 9a65695334e0 src/orchestrator/cli.ts", @@ -594,6 +610,7 @@ "clone 9ed4248bc98d src/engine/local-explore.ts,src/llm/ollama.ts", "clone 9ee98e35bf15 src/orchestrator/cli.ts,src/watcher/cli.ts", "clone 9f5a1c3e6be5 src/orchestrator/resume.ts", + "clone 9fd2184a9f18 src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-integrity.test.ts", "clone a003fde0f022 src/llm/muse.ts,src/llm/openrouter.ts", "clone a023cb3be29b src/budget/__tests__/budget.test.ts,src/budget/__tests__/concurrency.test.ts,src/budget/__tests__/cost.test.ts,src/checkpoints/__tests__/cli.test.ts,src/checkpoints/__tests__/service.test.ts,src/cloud/__tests__/provider.test.ts,src/codemap/__tests__/cli.test.ts,src/codemap/__tests__/units.test.ts,src/codesearch/__tests__/airunner-embedder.test.ts,src/codesearch/__tests__/chunk.test.ts,src/codesearch/__tests__/files.test.ts,src/codesearch/__tests__/ollama-embedder.test.ts,src/dashboard/__tests__/aggregate.test.ts,src/dashboard/__tests__/chat-thread.test.ts,src/dashboard/__tests__/checkpoints-routes.test.ts,src/dashboard/__tests__/cli.test.ts,src/dashboard/__tests__/events.test.ts,src/dashboard/__tests__/page-route.test.ts,src/dashboard/__tests__/projects-routes.test.ts,src/dashboard/__tests__/self-improvement-metrics.test.ts,src/dashboard/__tests__/server.test.ts,src/dashboard/__tests__/session-launch.test.ts,src/dashboard/__tests__/sigint-shutdown.test.ts,src/dashboard/__tests__/timeline.test.ts,src/deploy/__tests__/gate.test.ts,src/engine/__tests__/condense-async.test.ts,src/engine/__tests__/condense.test.ts,src/engine/__tests__/events.test.ts,src/engine/__tests__/inject-message.test.ts,src/engine/__tests__/new-task.test.ts,src/engine/__tests__/pause.test.ts,src/engine/__tests__/prompt.test.ts,src/engine/__tests__/switch-mode.test.ts,src/github/__tests__/app-auth.test.ts,src/github/__tests__/cli.test.ts,src/github/__tests__/installations.test.ts,src/github/__tests__/pr.test.ts,src/github/__tests__/provision.test.ts,src/github/__tests__/push.test.ts,src/index-util.test.ts,src/init/__tests__/cli.test.ts,src/init/__tests__/gitignore.test.ts,src/llm/__tests__/muse.test.ts,src/llm/__tests__/openrouter.test.ts,src/memory/__tests__/local.test.ts,src/memory/__tests__/memory-wiring.test.ts,src/memory/__tests__/uwuchat.test.ts,src/migrate/cli.test.ts,src/orchestrator/__tests__/analyze-cli.test.ts,src/orchestrator/__tests__/arg-cursor.test.ts,src/orchestrator/__tests__/auto-split.test.ts,src/orchestrator/__tests__/cleanup.test.ts,src/orchestrator/__tests__/cost-estimate.test.ts,src/orchestrator/__tests__/git-sync.test.ts,src/orchestrator/__tests__/log-analysis.test.ts,src/orchestrator/__tests__/merge-check.test.ts,src/orchestrator/__tests__/pipeline.test.ts,src/orchestrator/__tests__/reviewer.test.ts,src/orchestrator/__tests__/spawn-state-reset.test.ts,src/orchestrator/__tests__/split.test.ts,src/orchestrator/__tests__/state.test.ts,src/orchestrator/__tests__/status-cli.test.ts,src/orchestrator/__tests__/status.test.ts,src/orchestrator/__tests__/stop-cli.test.ts,src/permissions/__tests__/commands.test.ts,src/permissions/__tests__/store-protection.test.ts,src/project-store.test.ts,src/projects/__tests__/cli.test.ts,src/qa/__tests__/qa.test.ts,src/run-tests-script.test.ts,src/tools/__tests__/apply_diff.test.ts,src/tools/__tests__/decision.test.ts,src/tools/__tests__/describe_image.test.ts,src/tools/__tests__/edit_file.test.ts,src/tools/__tests__/execute_command.test.ts,src/tools/__tests__/language-detect.test.ts,src/tools/__tests__/list-files.test.ts,src/tools/__tests__/local-summarization-gate.test.ts,src/tools/__tests__/output-summarizer.test.ts,src/tools/__tests__/path-safety.test.ts,src/tools/__tests__/read-cache.test.ts,src/tools/__tests__/run-tests.test.ts,src/tools/__tests__/search_replace.test.ts,src/tools/__tests__/set-indentation.test.ts,src/tools/__tests__/update_todo_list.test.ts,src/tools/__tests__/write_to_file.test.ts,src/vision/__tests__/describe.test.ts,src/watcher/__tests__/github.test.ts,src/watcher/__tests__/state.test.ts,src/watcher/__tests__/watch.test.ts", "clone a0e38675893c src/checkpoints/__tests__/cli.test.ts,src/codemap/__tests__/cli.test.ts,src/codemap/__tests__/units.test.ts,src/dashboard/__tests__/cli.test.ts,src/dashboard/__tests__/page-route.test.ts,src/dashboard/__tests__/self-improvement-metrics.test.ts,src/dashboard/__tests__/sigint-shutdown.test.ts,src/github/__tests__/installations.test.ts,src/orchestrator/__tests__/analyze-cli.test.ts,src/orchestrator/__tests__/cost-estimate.test.ts,src/orchestrator/__tests__/log-analysis.test.ts,src/orchestrator/__tests__/spawn-state-reset.test.ts,src/orchestrator/__tests__/status.test.ts,src/run-tests-script.test.ts,src/tools/__tests__/set-indentation.test.ts", @@ -603,6 +620,7 @@ "clone a1a26fbf0a56 src/orchestrator/__tests__/status-cli.test.ts", "clone a1c96da14be8 src/codesearch/__tests__/backend-selection.test.ts,src/codesearch/__tests__/index.test.ts,src/codesearch/__tests__/search.test.ts,src/config/__tests__/mode-models.test.ts,src/dashboard/__tests__/file-browser-permissions.test.ts,src/dashboard/__tests__/settings.test.ts,src/orchestrator/__tests__/cli.test.ts,src/orchestrator/__tests__/model-resolution.test.ts,src/orchestrator/__tests__/resume.test.ts,src/orchestrator/__tests__/watch.test.ts,src/permissions/__tests__/protected-files.test.ts,src/tools/__tests__/codebase_search.test.ts", "clone a1cf343dc5c6 src/orchestrator/cli.ts", + "clone a1f6b098886c src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-integrity.test.ts", "clone a1fd626e0e34 src/dashboard/__tests__/checkpoints-routes.test.ts,src/dashboard/__tests__/file-browser-permissions.test.ts", "clone a21a36c1e0f2 src/engine/__tests__/new-task.test.ts,src/engine/__tests__/switch-mode.test.ts", "clone a24e8a7cfdb1 src/codesearch/files.ts,src/engine/stacks.ts", @@ -627,9 +645,10 @@ "clone a85870eb1a5b src/decision-proxy/__tests__/proxy.test.ts,src/engine/__tests__/switch-mode.test.ts", "clone a89b7f2f4a03 src/checkpoints/cli.ts,src/decision-proxy/cli.ts", "clone a8a18bc5e095 src/engine/local-explore.ts,src/llm/ollama.ts", + "clone a8b8282008b3 src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-fleet-results.test.ts", "clone a990dfadf953 src/tools/__tests__/apply_diff.test.ts,src/tools/__tests__/edit_file.test.ts,src/tools/__tests__/search_replace.test.ts", "clone a99cd9437614 src/permissions/commands.ts", - "clone aa35046643a4 src/rsi/controller.ts", + "clone aa35046643a4 src/rsi/controller-proof-archive.ts", "clone aad401c751bd src/engine/__tests__/loop.test.ts", "clone aba0b8f12dbd src/cloud/provider.ts,src/orchestrator/cli.ts", "clone ac3f8901458e src/github/pr.ts", @@ -637,16 +656,20 @@ "clone acec34e10a71 src/orchestrator/__tests__/cli.test.ts", "clone acf9debed815 src/engine/__tests__/condense.test.ts", "clone ad1018c63a58 src/codesearch/embedder.ts,src/memory/embed.ts", + "clone ad27fea0fdb7 src/rsi/controller.ts", "clone ad790702316b src/dashboard/server.ts", "clone ad89dc95661f src/codesearch/files.ts,src/engine/stacks.ts", "clone adb5f83390b2 src/orchestrator/__tests__/reviewer.test.ts,src/qa/__tests__/qa.test.ts", + "clone adbad4e12ddb src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-fleet-results.test.ts", "clone adee3e1601be src/permissions/commands.ts", "clone ae359f859bef src/orchestrator/__tests__/reviewer.test.ts,src/qa/__tests__/qa.test.ts", "clone ae3ad3a02f91 src/llm/preflight.ts", "clone ae4d76d322a1 src/codemap/cli.ts,src/codesearch/cli.ts,src/dashboard/cli.ts,src/dashboard/trend-cli.ts,src/init/cli.ts,src/orchestrator/cli.ts", + "clone ae8d9ceae4b2 src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-fleet-results.test.ts,src/rsi/__tests__/controller-integrity.test.ts", "clone aec4219b0469 src/codesearch/__tests__/search.test.ts,src/tools/__tests__/codebase_search.test.ts", "clone af8cb24fef81 src/engine/__tests__/condense.test.ts,src/engine/__tests__/events.test.ts", "clone af9c5a7a19dd src/orchestrator/cli.ts,src/watcher/cli.ts", + "clone afbcfb5dcc72 src/rsi/controller.ts", "clone afec75fcac46 src/dashboard/server.ts", "clone b013f5230fd0 src/dashboard/server.ts", "clone b0d2cf6ccae4 src/codeintel/__tests__/call-graph.test.ts,src/codeintel/__tests__/call-hierarchy.test.ts,src/codeintel/__tests__/find-references.test.ts,src/codeintel/__tests__/go-to-definition.test.ts,src/codeintel/__tests__/hover.test.ts,src/codeintel/__tests__/import-graph.test.ts,src/codeintel/__tests__/outline.test.ts,src/codeintel/__tests__/rename-symbol.test.ts,src/codemap/__tests__/build.test.ts,src/dashboard/__tests__/codemap-routes.test.ts,src/dashboard/__tests__/cost-history-routes.test.ts", @@ -710,6 +733,7 @@ "clone c1e1b41c0b87 src/orchestrator/__tests__/spawn-model.test.ts,src/orchestrator/__tests__/spawn-state-reset.test.ts", "clone c1f158e49080 src/orchestrator/reviewer.ts,src/qa/qa.ts", "clone c25de2e7a85a src/orchestrator/cli.ts", + "clone c26e4fd9d44b src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-integrity.test.ts", "clone c28d9afd9221 src/engine/__tests__/loop.test.ts,src/tools/__tests__/execute_command.test.ts", "clone c3486aeec199 src/codesearch/__tests__/airunner-embedder.test.ts,src/codesearch/__tests__/ollama-embedder.test.ts", "clone c3571181b0ad src/orchestrator/__tests__/cli.test.ts,src/orchestrator/__tests__/resume.test.ts", @@ -732,6 +756,7 @@ "clone c7273bc39436 src/codeintel/__tests__/call-graph.test.ts,src/codeintel/__tests__/hover.test.ts,src/codeintel/__tests__/import-graph.test.ts,src/codeintel/__tests__/outline.test.ts,src/codeintel/__tests__/registration.test.ts,src/codeintel/__tests__/rename-symbol.test.ts", "clone c74ec5f935d2 src/orchestrator/resume.ts", "clone c7616f2d88be src/llm/openrouter.ts", + "clone c7b8a9659098 src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-fleet-results.test.ts", "clone c7d2cfba43b6 src/engine/__tests__/new-task.test.ts,src/engine/__tests__/switch-mode.test.ts", "clone c9648e69e45e src/orchestrator/__tests__/cli.test.ts", "clone c9726dcfaf3b src/engine/local-explore.ts,src/llm/ollama.ts", @@ -749,6 +774,7 @@ "clone cdc8a6d9d9f7 src/orchestrator/task-files.ts", "clone cddcf3ae0313 src/orchestrator/cli.ts,src/orchestrator/resume.ts", "clone cdff5c34ea22 src/orchestrator/task-files.ts", + "clone ce27f097c90c src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-fleet-results.test.ts", "clone ce4455f3e99f src/orchestrator/task-files.ts", "clone ce8ca6626552 src/engine/__tests__/global-modes.test.ts,src/engine/__tests__/prompt.test.ts,src/engine/__tests__/stack-rules-content.test.ts,src/engine/__tests__/stacks.test.ts,src/llm/__tests__/muse.test.ts,src/llm/__tests__/openrouter.test.ts,src/stack-rules-content.test.ts", "clone cebb36184bf4 src/codesearch/__tests__/backend-selection.test.ts,src/codesearch/__tests__/index.test.ts,src/codesearch/__tests__/search.test.ts,src/config/__tests__/mode-models.test.ts,src/dashboard/__tests__/file-browser-permissions.test.ts,src/dashboard/__tests__/settings.test.ts,src/orchestrator/__tests__/cli.test.ts,src/orchestrator/__tests__/model-resolution.test.ts,src/orchestrator/__tests__/resume.test.ts,src/orchestrator/__tests__/spawn-model.test.ts,src/orchestrator/__tests__/watch.test.ts,src/permissions/__tests__/protected-files.test.ts,src/tools/__tests__/codebase_search.test.ts", @@ -834,6 +860,7 @@ "clone e34ac968803e src/engine/__tests__/inject-message.test.ts,src/engine/__tests__/pause.test.ts", "clone e34daa28a463 src/llm/muse.ts,src/llm/openrouter.ts", "clone e364d48be386 src/watcher/state.ts", + "clone e3677d2289a3 src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-fleet-results.test.ts", "clone e37c0c4b3387 src/orchestrator/cli.ts,src/watcher/cli.ts", "clone e3a4e8050308 src/codesearch/files.ts,src/engine/stacks.ts", "clone e3d13fb47c32 src/orchestrator/__tests__/reviewer.test.ts,src/qa/__tests__/qa.test.ts", @@ -841,6 +868,7 @@ "clone e498eb38bb29 src/orchestrator/resume.ts", "clone e4c0c632d2e8 src/engine/__tests__/condense.test.ts", "clone e4cd97daa489 src/__tests__/cli-env.test.ts,src/codesearch/__tests__/embedder-types.test.ts,src/evidence/__tests__/verify-files.test.ts,src/orchestrator/__tests__/task-files.test.ts,src/orchestrator/__tests__/terminal-status.test.ts,src/tools/__tests__/batch-types.test.ts,src/tools/__tests__/paths.test.ts", + "clone e4f3cf1fa942 src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-fleet-results.test.ts", "clone e5b435a1ec6d src/llm/openrouter.ts", "clone e5c94c55dadd src/orchestrator/__tests__/spawn-model.test.ts,src/orchestrator/__tests__/spawn-state-reset.test.ts", "clone e5e1bcaff8b1 src/orchestrator/__tests__/spawn-model.test.ts,src/orchestrator/__tests__/spawn-state-reset.test.ts", @@ -864,6 +892,7 @@ "clone ea87d3664530 src/llm/preflight.ts", "clone eb1f923409cf src/tools/__tests__/describe_image.test.ts,src/vision/__tests__/describe.test.ts", "clone ec1ebb8ca755 src/cloud/docker-provider.ts,src/cloud/provider.ts", + "clone ec284ffedf87 src/rsi/controller.ts", "clone ec2949b2c9e9 src/engine/local-explore.ts,src/llm/ollama.ts", "clone ecbe60afad5e src/rsi/__tests__/controller-fleet.test.ts", "clone ece60f8d2445 src/dashboard/server.ts", @@ -890,6 +919,7 @@ "clone f31bbac5eba1 src/orchestrator/task-files.ts", "clone f351b20bf33d src/dashboard/__tests__/session-launch.test.ts", "clone f38d7696e8d7 src/dashboard/server.ts", + "clone f3e54421384c src/rsi/__tests__/controller-candidates.test.ts,src/rsi/__tests__/controller-integrity.test.ts", "clone f41cb7e808cc src/engine/__tests__/loop.test.ts", "clone f43217cc6864 src/orchestrator/cli.ts,src/orchestrator/resume.ts", "clone f4a6bb52ddb3 src/orchestrator/resume.ts", @@ -1270,24 +1300,24 @@ "src/rsi/benchmark/snapshot-package.ts complexity e7d4e1093229", "src/rsi/cli.ts complexity 268718b1e085", "src/rsi/config.ts complexity 31abfaf8e209", - "src/rsi/controller.ts complexity 0505c814fd59", - "src/rsi/controller.ts complexity 22ac85361362", + "src/rsi/controller-candidates.ts complexity 4227bbd1f07d", + "src/rsi/controller-fleet.ts complexity 054c224b2e97", + "src/rsi/controller-fleet.ts complexity 8c15614e76ef", + "src/rsi/controller-fleet.ts complexity 9aa59757da08", + "src/rsi/controller-fleet.ts complexity df790257a2ac", + "src/rsi/controller-fleet.ts complexity e95057d04b68", + "src/rsi/controller-integrity-seal.ts complexity 88130fc94ac2", + "src/rsi/controller-integrity-seal.ts complexity e655674c6670", + "src/rsi/controller-integrity.ts complexity 8f359a40cc31", + "src/rsi/controller-proof-archive.ts complexity 5175664a92e4", + "src/rsi/controller-proof-archive.ts complexity cc7992bb1b11", + "src/rsi/controller-proof-archive.ts complexity e8ddde3545b5", + "src/rsi/controller-proof-manifest.ts complexity 87d1cb7ce243", + "src/rsi/controller-proof-manifest.ts nesting 87d1cb7ce243", "src/rsi/controller.ts complexity 299ca83bdc30", "src/rsi/controller.ts complexity 2fe4d2883ba2", - "src/rsi/controller.ts complexity 4227bbd1f07d", - "src/rsi/controller.ts complexity 4f9290ce038a", - "src/rsi/controller.ts complexity 579c3c5df137", - "src/rsi/controller.ts complexity 87d1cb7ce243", - "src/rsi/controller.ts complexity 8c15614e76ef", "src/rsi/controller.ts complexity b10cc4f313d9", - "src/rsi/controller.ts complexity b42f91892bb1", - "src/rsi/controller.ts complexity b5f15d2ace7d", - "src/rsi/controller.ts complexity cc7992bb1b11", "src/rsi/controller.ts complexity dbfa3e824204", - "src/rsi/controller.ts complexity df790257a2ac", - "src/rsi/controller.ts complexity e1c281e2da03", - "src/rsi/controller.ts complexity e8ddde3545b5", - "src/rsi/controller.ts nesting 87d1cb7ce243", "src/rsi/controller.ts nesting b10cc4f313d9", "src/rsi/controller.ts params 2fe4d2883ba2", "src/rsi/controller.ts params 4000ffc408d7", diff --git a/scripts/code-standards-baseline.json b/scripts/code-standards-baseline.json index d5d4405..465f46b 100644 --- a/scripts/code-standards-baseline.json +++ b/scripts/code-standards-baseline.json @@ -12683,6 +12683,21 @@ "src/rsi/__tests__/config.test.ts line-length bc2ed42663aa", "src/rsi/__tests__/config.test.ts line-length c2badb2a40c3", "src/rsi/__tests__/config.test.ts line-length ee90a70abe62", + "src/rsi/__tests__/controller-candidates.test.ts function-lines 431ae751dbcb", + "src/rsi/__tests__/controller-candidates.test.ts line-length 0f9419f86920", + "src/rsi/__tests__/controller-candidates.test.ts line-length 2d54fe14e74b", + "src/rsi/__tests__/controller-candidates.test.ts line-length 47c5dbd786b9", + "src/rsi/__tests__/controller-candidates.test.ts line-length d411949751c8", + "src/rsi/__tests__/controller-candidates.test.ts line-length e2aed9ca9165", + "src/rsi/__tests__/controller-fleet-results.test.ts function-lines 431ae751dbcb", + "src/rsi/__tests__/controller-fleet-results.test.ts function-lines 77d510d1e93c", + "src/rsi/__tests__/controller-fleet-results.test.ts function-lines b2d4f81a04f1", + "src/rsi/__tests__/controller-fleet-results.test.ts function-lines e463c1803613", + "src/rsi/__tests__/controller-fleet-results.test.ts line-length 1ba095711626", + "src/rsi/__tests__/controller-fleet-results.test.ts line-length 7f204b8fb3b0", + "src/rsi/__tests__/controller-fleet-results.test.ts line-length 8d284ff61528", + "src/rsi/__tests__/controller-fleet-results.test.ts line-length a0edb59a8cc8", + "src/rsi/__tests__/controller-fleet-results.test.ts line-length d80beb6b1917", "src/rsi/__tests__/controller-fleet.test.ts class-lines 4d689fe8feb8", "src/rsi/__tests__/controller-fleet.test.ts explicit-return-type 2049a1bda380", "src/rsi/__tests__/controller-fleet.test.ts explicit-return-type 27ee8ea558a4", @@ -12917,6 +12932,9 @@ "src/rsi/__tests__/controller-fleet.test.ts line-length fc80c9cd9b1e", "src/rsi/__tests__/controller-fleet.test.ts line-length fde79343b7df", "src/rsi/__tests__/controller-fleet.test.ts line-length fe09066c9a90", + "src/rsi/__tests__/controller-integrity.test.ts line-length 2cdeae7118c9", + "src/rsi/__tests__/controller-integrity.test.ts line-length 4105cfc9825b", + "src/rsi/__tests__/controller-integrity.test.ts line-length 6092257e79c0", "src/rsi/__tests__/controller.test.ts function-lines 5a1db74d0b54", "src/rsi/__tests__/controller.test.ts function-lines 6ee28c464759", "src/rsi/__tests__/controller.test.ts function-lines a8cec4b9e9f9", @@ -16609,65 +16627,307 @@ "src/rsi/config.ts line-length ea64519a7a53", "src/rsi/config.ts line-length ea78781c089f", "src/rsi/config.ts line-length fd5ba871887a", + "src/rsi/controller-candidates.ts function-lines 6abef9c366d1", + "src/rsi/controller-candidates.ts line-length 005a36c317bd", + "src/rsi/controller-candidates.ts line-length 387bbaa5c44a", + "src/rsi/controller-candidates.ts line-length 4227bbd1f07d", + "src/rsi/controller-candidates.ts line-length 53451963d0a9", + "src/rsi/controller-candidates.ts line-length 579227720e72", + "src/rsi/controller-candidates.ts line-length 5c684efbe877", + "src/rsi/controller-candidates.ts line-length 69e7a210a12e", + "src/rsi/controller-candidates.ts line-length 6a02887c9f96", + "src/rsi/controller-candidates.ts line-length 6abef9c366d1", + "src/rsi/controller-candidates.ts line-length 6d8e55562fad", + "src/rsi/controller-candidates.ts line-length 71bbc06842b0", + "src/rsi/controller-candidates.ts line-length 8774972a8f32", + "src/rsi/controller-candidates.ts line-length 8ca21212eba8", + "src/rsi/controller-candidates.ts line-length a04ea4c13d9f", + "src/rsi/controller-candidates.ts line-length af4a61ee144a", + "src/rsi/controller-candidates.ts line-length b45e7721b90a", + "src/rsi/controller-candidates.ts line-length c4d0c4b54747", + "src/rsi/controller-candidates.ts line-length ccb502f3a919", + "src/rsi/controller-candidates.ts line-length d3a80b5a9c2c", + "src/rsi/controller-fleet.ts function-lines 054c224b2e97", + "src/rsi/controller-fleet.ts function-lines 8c15614e76ef", + "src/rsi/controller-fleet.ts function-lines dc20bdf2056b", + "src/rsi/controller-fleet.ts line-length 0043b1596fcf", + "src/rsi/controller-fleet.ts line-length 007fe254c44c", + "src/rsi/controller-fleet.ts line-length 0249add3e96a", + "src/rsi/controller-fleet.ts line-length 054c224b2e97", + "src/rsi/controller-fleet.ts line-length 09b3cd456e88", + "src/rsi/controller-fleet.ts line-length 0cf0651c183b", + "src/rsi/controller-fleet.ts line-length 112634ccbd46", + "src/rsi/controller-fleet.ts line-length 13fa22340eb0", + "src/rsi/controller-fleet.ts line-length 21a7df0908a5", + "src/rsi/controller-fleet.ts line-length 229030fce4e1", + "src/rsi/controller-fleet.ts line-length 3028c9171cd0", + "src/rsi/controller-fleet.ts line-length 38cad9a41af5", + "src/rsi/controller-fleet.ts line-length 39ecaf34fb9f", + "src/rsi/controller-fleet.ts line-length 3b78277c5362", + "src/rsi/controller-fleet.ts line-length 40e42ec5355a", + "src/rsi/controller-fleet.ts line-length 4147daf1f692", + "src/rsi/controller-fleet.ts line-length 4295bfcdfa9c", + "src/rsi/controller-fleet.ts line-length 48ab74c7266a", + "src/rsi/controller-fleet.ts line-length 48d88230709f", + "src/rsi/controller-fleet.ts line-length 49dcb82dc27c", + "src/rsi/controller-fleet.ts line-length 4c45979d311d", + "src/rsi/controller-fleet.ts line-length 4f7b3f72fd8c", + "src/rsi/controller-fleet.ts line-length 53b107cef55f", + "src/rsi/controller-fleet.ts line-length 5486641c542c", + "src/rsi/controller-fleet.ts line-length 5909d0c346f5", + "src/rsi/controller-fleet.ts line-length 5effdbccc323", + "src/rsi/controller-fleet.ts line-length 6733badf1f9f", + "src/rsi/controller-fleet.ts line-length 74b8fea7e59a", + "src/rsi/controller-fleet.ts line-length 7ea86e318891", + "src/rsi/controller-fleet.ts line-length 80276ddaa0f0", + "src/rsi/controller-fleet.ts line-length 8178d986b4c6", + "src/rsi/controller-fleet.ts line-length 820e87c32148", + "src/rsi/controller-fleet.ts line-length 84e063bd3865", + "src/rsi/controller-fleet.ts line-length 84f1ef789dad", + "src/rsi/controller-fleet.ts line-length 859ae850335d", + "src/rsi/controller-fleet.ts line-length 8c15614e76ef", + "src/rsi/controller-fleet.ts line-length 8d313d0b328a", + "src/rsi/controller-fleet.ts line-length 988beb375327", + "src/rsi/controller-fleet.ts line-length 9aa59757da08", + "src/rsi/controller-fleet.ts line-length 9e0a09e50cb6", + "src/rsi/controller-fleet.ts line-length 9fcb5a70899c", + "src/rsi/controller-fleet.ts line-length a11e08e615d5", + "src/rsi/controller-fleet.ts line-length a810982f06d4", + "src/rsi/controller-fleet.ts line-length aa6063feb6bf", + "src/rsi/controller-fleet.ts line-length b5754aa04203", + "src/rsi/controller-fleet.ts line-length b7596f88003e", + "src/rsi/controller-fleet.ts line-length be42720d55b1", + "src/rsi/controller-fleet.ts line-length c04167fc664d", + "src/rsi/controller-fleet.ts line-length c2e0feb380e3", + "src/rsi/controller-fleet.ts line-length c39d3b1ac4c5", + "src/rsi/controller-fleet.ts line-length c453279a8b0b", + "src/rsi/controller-fleet.ts line-length c4ceccf500dd", + "src/rsi/controller-fleet.ts line-length c608e9c8326a", + "src/rsi/controller-fleet.ts line-length cb6e302e85be", + "src/rsi/controller-fleet.ts line-length ce295f79428c", + "src/rsi/controller-fleet.ts line-length d6096c05a82f", + "src/rsi/controller-fleet.ts line-length dc20bdf2056b", + "src/rsi/controller-fleet.ts line-length e4708a8d542a", + "src/rsi/controller-fleet.ts line-length e637499f9b54", + "src/rsi/controller-fleet.ts line-length e776125defcf", + "src/rsi/controller-fleet.ts line-length e8ee4d25006c", + "src/rsi/controller-fleet.ts line-length e95057d04b68", + "src/rsi/controller-fleet.ts line-length f6e66c25ac14", + "src/rsi/controller-fleet.ts line-length fb102dab9403", + "src/rsi/controller-fleet.ts line-length fbfbd6baf1f5", + "src/rsi/controller-fleet.ts line-length fe40885fa2db", + "src/rsi/controller-integrity-seal.ts function-lines 88130fc94ac2", + "src/rsi/controller-integrity-seal.ts function-lines e145cb0629b6", + "src/rsi/controller-integrity-seal.ts function-lines e655674c6670", + "src/rsi/controller-integrity-seal.ts line-length 1d9b80e221d3", + "src/rsi/controller-integrity-seal.ts line-length 1ecf48b9ee40", + "src/rsi/controller-integrity-seal.ts line-length 2330266ea3eb", + "src/rsi/controller-integrity-seal.ts line-length 23c3714a2832", + "src/rsi/controller-integrity-seal.ts line-length 272acd5d2f79", + "src/rsi/controller-integrity-seal.ts line-length 49f760e04286", + "src/rsi/controller-integrity-seal.ts line-length 51329f1f9b21", + "src/rsi/controller-integrity-seal.ts line-length 5c684efbe877", + "src/rsi/controller-integrity-seal.ts line-length 5d05822a7101", + "src/rsi/controller-integrity-seal.ts line-length 6669e9ed254c", + "src/rsi/controller-integrity-seal.ts line-length 76216ddb6a97", + "src/rsi/controller-integrity-seal.ts line-length 767f64a08f5b", + "src/rsi/controller-integrity-seal.ts line-length 7ed726a5834e", + "src/rsi/controller-integrity-seal.ts line-length 830f778cb7e3", + "src/rsi/controller-integrity-seal.ts line-length 837e087db68b", + "src/rsi/controller-integrity-seal.ts line-length 84b9f4ce0a6e", + "src/rsi/controller-integrity-seal.ts line-length 8c478ebffd0a", + "src/rsi/controller-integrity-seal.ts line-length 8f88af73beea", + "src/rsi/controller-integrity-seal.ts line-length 933a1edec6e4", + "src/rsi/controller-integrity-seal.ts line-length 98ba51775089", + "src/rsi/controller-integrity-seal.ts line-length 9b5a491383e8", + "src/rsi/controller-integrity-seal.ts line-length 9fe6413c7818", + "src/rsi/controller-integrity-seal.ts line-length a1a6fc8028fa", + "src/rsi/controller-integrity-seal.ts line-length ac0aec349f20", + "src/rsi/controller-integrity-seal.ts line-length b27da14f1c50", + "src/rsi/controller-integrity-seal.ts line-length bc2a91a43ff4", + "src/rsi/controller-integrity-seal.ts line-length be24465fc930", + "src/rsi/controller-integrity-seal.ts line-length d001f7c0dde9", + "src/rsi/controller-integrity-seal.ts line-length d3824247d0fc", + "src/rsi/controller-integrity-seal.ts line-length e9df26ebe365", + "src/rsi/controller-integrity-seal.ts line-length ec2c0a0ae538", + "src/rsi/controller-integrity-seal.ts line-length ecdbca648c94", + "src/rsi/controller-integrity-seal.ts line-length ed0aed785df6", + "src/rsi/controller-integrity-seal.ts line-length f7fb05568dfc", + "src/rsi/controller-integrity.ts line-length 0668187fbfa5", + "src/rsi/controller-integrity.ts line-length 08ed84ab44d8", + "src/rsi/controller-integrity.ts line-length 17df174b2b61", + "src/rsi/controller-integrity.ts line-length 24c1ea33e62a", + "src/rsi/controller-integrity.ts line-length 2a87d64a1f90", + "src/rsi/controller-integrity.ts line-length 2c2c8e659e8e", + "src/rsi/controller-integrity.ts line-length 334476b739d9", + "src/rsi/controller-integrity.ts line-length 3ab0a8c3a1ce", + "src/rsi/controller-integrity.ts line-length 3f45856cb7c8", + "src/rsi/controller-integrity.ts line-length 50cd528e55a6", + "src/rsi/controller-integrity.ts line-length 51fef3b8b1d0", + "src/rsi/controller-integrity.ts line-length 5de754eecc14", + "src/rsi/controller-integrity.ts line-length 5dee2fcc5fdb", + "src/rsi/controller-integrity.ts line-length 7238b71c363d", + "src/rsi/controller-integrity.ts line-length 734db330c544", + "src/rsi/controller-integrity.ts line-length 8f359a40cc31", + "src/rsi/controller-integrity.ts line-length a4088b76c26d", + "src/rsi/controller-integrity.ts line-length a45926dd7f0b", + "src/rsi/controller-integrity.ts line-length a70ac11e9ed2", + "src/rsi/controller-integrity.ts line-length aad2d5ebb1b3", + "src/rsi/controller-integrity.ts line-length aec6115c30e9", + "src/rsi/controller-integrity.ts line-length b537437748ba", + "src/rsi/controller-integrity.ts line-length b9109975feda", + "src/rsi/controller-integrity.ts line-length ba7ca25e4e0f", + "src/rsi/controller-integrity.ts line-length cae20e341d6a", + "src/rsi/controller-integrity.ts line-length ced918645d08", + "src/rsi/controller-integrity.ts line-length d34951674c19", + "src/rsi/controller-integrity.ts line-length e43eee409cb2", + "src/rsi/controller-integrity.ts line-length e4eaf2b5a452", + "src/rsi/controller-integrity.ts line-length f6c148513665", + "src/rsi/controller-proof-archive.ts function-lines 5175664a92e4", + "src/rsi/controller-proof-archive.ts function-lines cc7992bb1b11", + "src/rsi/controller-proof-archive.ts function-lines e8ddde3545b5", + "src/rsi/controller-proof-archive.ts line-length 0c65928c5f6a", + "src/rsi/controller-proof-archive.ts line-length 0ee77559a24c", + "src/rsi/controller-proof-archive.ts line-length 149270b3b237", + "src/rsi/controller-proof-archive.ts line-length 1cce54c83ea4", + "src/rsi/controller-proof-archive.ts line-length 209500bdc364", + "src/rsi/controller-proof-archive.ts line-length 28c3d6fca1c6", + "src/rsi/controller-proof-archive.ts line-length 2f7a3ebc32f8", + "src/rsi/controller-proof-archive.ts line-length 30498a460b0a", + "src/rsi/controller-proof-archive.ts line-length 32646a5bc0da", + "src/rsi/controller-proof-archive.ts line-length 342c05d5e031", + "src/rsi/controller-proof-archive.ts line-length 39662fb4d279", + "src/rsi/controller-proof-archive.ts line-length 3d1b6740495a", + "src/rsi/controller-proof-archive.ts line-length 3eda5d1df583", + "src/rsi/controller-proof-archive.ts line-length 432130e4474c", + "src/rsi/controller-proof-archive.ts line-length 449944b10eae", + "src/rsi/controller-proof-archive.ts line-length 5175664a92e4", + "src/rsi/controller-proof-archive.ts line-length 52fc803a0b99", + "src/rsi/controller-proof-archive.ts line-length 5ddaeb95ac47", + "src/rsi/controller-proof-archive.ts line-length 5e1cce386eee", + "src/rsi/controller-proof-archive.ts line-length 6140e9234eb7", + "src/rsi/controller-proof-archive.ts line-length 65bc1a4efef1", + "src/rsi/controller-proof-archive.ts line-length 6e44788ab27b", + "src/rsi/controller-proof-archive.ts line-length 770463e88836", + "src/rsi/controller-proof-archive.ts line-length 784766dffc7a", + "src/rsi/controller-proof-archive.ts line-length 7c3bc718bb66", + "src/rsi/controller-proof-archive.ts line-length 7eb8cd61e4b7", + "src/rsi/controller-proof-archive.ts line-length 817b40d1a28a", + "src/rsi/controller-proof-archive.ts line-length 93f134d75c50", + "src/rsi/controller-proof-archive.ts line-length 9bda0c6ad6c8", + "src/rsi/controller-proof-archive.ts line-length a209a294dd36", + "src/rsi/controller-proof-archive.ts line-length a4d508d91383", + "src/rsi/controller-proof-archive.ts line-length bca105c4085b", + "src/rsi/controller-proof-archive.ts line-length c342bb5fc935", + "src/rsi/controller-proof-archive.ts line-length c487f6ddb69d", + "src/rsi/controller-proof-archive.ts line-length cca018112319", + "src/rsi/controller-proof-archive.ts line-length d33775e192bd", + "src/rsi/controller-proof-archive.ts line-length d6ce2d8aef42", + "src/rsi/controller-proof-archive.ts line-length e7b2dbd8c795", + "src/rsi/controller-proof-archive.ts line-length e7dd9a682e12", + "src/rsi/controller-proof-archive.ts line-length e8739478e19e", + "src/rsi/controller-proof-archive.ts line-length f36f24c02a2b", + "src/rsi/controller-proof-archive.ts line-length f864ea1ec460", + "src/rsi/controller-proof-archive.ts line-length fb4a88465a13", + "src/rsi/controller-proof-archive.ts line-length feb8ce2ca4d2", + "src/rsi/controller-proof-manifest.ts function-lines 87d1cb7ce243", + "src/rsi/controller-proof-manifest.ts function-lines df44a4bfc35f", + "src/rsi/controller-proof-manifest.ts line-length 00ad55b3d616", + "src/rsi/controller-proof-manifest.ts line-length 045b9335dc09", + "src/rsi/controller-proof-manifest.ts line-length 05d2d06a3f44", + "src/rsi/controller-proof-manifest.ts line-length 0a3acfa9540b", + "src/rsi/controller-proof-manifest.ts line-length 0de150ba584f", + "src/rsi/controller-proof-manifest.ts line-length 149d491f4f20", + "src/rsi/controller-proof-manifest.ts line-length 21f6079e1a0f", + "src/rsi/controller-proof-manifest.ts line-length 2592850143c7", + "src/rsi/controller-proof-manifest.ts line-length 283a2140644c", + "src/rsi/controller-proof-manifest.ts line-length 2cba894a7973", + "src/rsi/controller-proof-manifest.ts line-length 2d2dfa1d05ae", + "src/rsi/controller-proof-manifest.ts line-length 2ef4e8aa1680", + "src/rsi/controller-proof-manifest.ts line-length 2ef6cd1e9485", + "src/rsi/controller-proof-manifest.ts line-length 305758a7aff9", + "src/rsi/controller-proof-manifest.ts line-length 41f8e5795685", + "src/rsi/controller-proof-manifest.ts line-length 42bff0a23ca2", + "src/rsi/controller-proof-manifest.ts line-length 4872440ae744", + "src/rsi/controller-proof-manifest.ts line-length 4c92c43cafb6", + "src/rsi/controller-proof-manifest.ts line-length 4cf5c15bc220", + "src/rsi/controller-proof-manifest.ts line-length 4e43af7e504f", + "src/rsi/controller-proof-manifest.ts line-length 500ecd8af3fe", + "src/rsi/controller-proof-manifest.ts line-length 582f8c204e62", + "src/rsi/controller-proof-manifest.ts line-length 5fec1d1bb959", + "src/rsi/controller-proof-manifest.ts line-length 6856428b6e08", + "src/rsi/controller-proof-manifest.ts line-length 6c9a1a19ffb4", + "src/rsi/controller-proof-manifest.ts line-length 6f449bcdf836", + "src/rsi/controller-proof-manifest.ts line-length 70bba78326bb", + "src/rsi/controller-proof-manifest.ts line-length 71e56c4655cf", + "src/rsi/controller-proof-manifest.ts line-length 73d940e8dd69", + "src/rsi/controller-proof-manifest.ts line-length 784feb3f53ae", + "src/rsi/controller-proof-manifest.ts line-length 7ac05a4a8e27", + "src/rsi/controller-proof-manifest.ts line-length 7fc27d873c51", + "src/rsi/controller-proof-manifest.ts line-length 842dc294c4ee", + "src/rsi/controller-proof-manifest.ts line-length 8702969a1360", + "src/rsi/controller-proof-manifest.ts line-length 948b227f25c4", + "src/rsi/controller-proof-manifest.ts line-length 9527d5444765", + "src/rsi/controller-proof-manifest.ts line-length 964f109c2ae0", + "src/rsi/controller-proof-manifest.ts line-length 97535e279929", + "src/rsi/controller-proof-manifest.ts line-length 97d121f24ecf", + "src/rsi/controller-proof-manifest.ts line-length 97ee906666a5", + "src/rsi/controller-proof-manifest.ts line-length 9a3e247a5c61", + "src/rsi/controller-proof-manifest.ts line-length 9a56cbb336ff", + "src/rsi/controller-proof-manifest.ts line-length 9f82fa1c2415", + "src/rsi/controller-proof-manifest.ts line-length a200cee024c1", + "src/rsi/controller-proof-manifest.ts line-length a29f7acb6792", + "src/rsi/controller-proof-manifest.ts line-length a87e27f6f502", + "src/rsi/controller-proof-manifest.ts line-length ab50151f5b66", + "src/rsi/controller-proof-manifest.ts line-length adab68d7f7ee", + "src/rsi/controller-proof-manifest.ts line-length b013d9b03a0a", + "src/rsi/controller-proof-manifest.ts line-length bee40fe11ec3", + "src/rsi/controller-proof-manifest.ts line-length c2cce65db5c7", + "src/rsi/controller-proof-manifest.ts line-length c83799d6b177", + "src/rsi/controller-proof-manifest.ts line-length cdbb6cbe84d5", + "src/rsi/controller-proof-manifest.ts line-length d092485d43e7", + "src/rsi/controller-proof-manifest.ts line-length d178d787dbba", + "src/rsi/controller-proof-manifest.ts line-length d1d87a4ecbb9", + "src/rsi/controller-proof-manifest.ts line-length d8810fcdfb13", + "src/rsi/controller-proof-manifest.ts line-length daa7fb2b7cea", + "src/rsi/controller-proof-manifest.ts line-length e0d84fbf6f30", + "src/rsi/controller-proof-manifest.ts line-length e5c210fd3897", + "src/rsi/controller-proof-manifest.ts line-length ea3bcbf1fbff", + "src/rsi/controller-proof-manifest.ts line-length eca609ecc724", + "src/rsi/controller-proof-manifest.ts line-length f328ba935b5b", + "src/rsi/controller-proof-manifest.ts line-length f5a73cfbcb45", + "src/rsi/controller-proof-manifest.ts line-length f9086a6bb35c", + "src/rsi/controller-proof-manifest.ts line-length f945a088afac", + "src/rsi/controller-proof-manifest.ts line-length fdf26b2812d5", "src/rsi/controller.ts file-lines", - "src/rsi/controller.ts function-lines 0505c814fd59", - "src/rsi/controller.ts function-lines 065bab1bdd55", - "src/rsi/controller.ts function-lines 22ac85361362", "src/rsi/controller.ts function-lines 299ca83bdc30", - "src/rsi/controller.ts function-lines 2eba696893d1", "src/rsi/controller.ts function-lines 2fe4d2883ba2", - "src/rsi/controller.ts function-lines 4f9290ce038a", "src/rsi/controller.ts function-lines 7bbf2521fcb7", "src/rsi/controller.ts function-lines 7fbc5d697cd8", - "src/rsi/controller.ts function-lines 8537b73e8f62", - "src/rsi/controller.ts function-lines 87d1cb7ce243", - "src/rsi/controller.ts function-lines 8c15614e76ef", "src/rsi/controller.ts function-lines b10cc4f313d9", - "src/rsi/controller.ts function-lines b5f15d2ace7d", - "src/rsi/controller.ts function-lines cc7992bb1b11", "src/rsi/controller.ts function-lines da4e69a068b9", - "src/rsi/controller.ts function-lines df44a4bfc35f", - "src/rsi/controller.ts function-lines e8ddde3545b5", - "src/rsi/controller.ts line-length 0043b1596fcf", "src/rsi/controller.ts line-length 00496b40031a", - "src/rsi/controller.ts line-length 007fe254c44c", - "src/rsi/controller.ts line-length 00ad55b3d616", - "src/rsi/controller.ts line-length 0249add3e96a", "src/rsi/controller.ts line-length 032d6e84f1f5", "src/rsi/controller.ts line-length 03762b3ba102", "src/rsi/controller.ts line-length 037bc95866c3", "src/rsi/controller.ts line-length 0391b0dd3962", - "src/rsi/controller.ts line-length 045b9335dc09", "src/rsi/controller.ts line-length 045c1b41d55c", "src/rsi/controller.ts line-length 046f523288c6", "src/rsi/controller.ts line-length 04721ac1af63", - "src/rsi/controller.ts line-length 0505c814fd59", "src/rsi/controller.ts line-length 058465aff109", "src/rsi/controller.ts line-length 059002125fa7", "src/rsi/controller.ts line-length 059f3ac0d070", - "src/rsi/controller.ts line-length 05d2d06a3f44", - "src/rsi/controller.ts line-length 065bab1bdd55", - "src/rsi/controller.ts line-length 0668187fbfa5", "src/rsi/controller.ts line-length 06a04c49265e", "src/rsi/controller.ts line-length 07bc723cc1f3", "src/rsi/controller.ts line-length 08c59ac3dc0f", "src/rsi/controller.ts line-length 08e8d0505b61", - "src/rsi/controller.ts line-length 08ed84ab44d8", "src/rsi/controller.ts line-length 094b78fb23c8", - "src/rsi/controller.ts line-length 09b3cd456e88", "src/rsi/controller.ts line-length 0a30c4880aec", - "src/rsi/controller.ts line-length 0a3acfa9540b", "src/rsi/controller.ts line-length 0bcf9eadf818", "src/rsi/controller.ts line-length 0bf7524e2511", - "src/rsi/controller.ts line-length 0c65928c5f6a", - "src/rsi/controller.ts line-length 0c7cfa623da8", - "src/rsi/controller.ts line-length 0cf0651c183b", - "src/rsi/controller.ts line-length 0de150ba584f", "src/rsi/controller.ts line-length 0e8da3ec1be3", - "src/rsi/controller.ts line-length 0ee77559a24c", "src/rsi/controller.ts line-length 0fe6cacc71b9", "src/rsi/controller.ts line-length 110fa8327fee", - "src/rsi/controller.ts line-length 112634ccbd46", "src/rsi/controller.ts line-length 113ad29911c9", "src/rsi/controller.ts line-length 1162426bec6e", "src/rsi/controller.ts line-length 12178fd64036", @@ -16675,15 +16935,11 @@ "src/rsi/controller.ts line-length 12aa4a2fe474", "src/rsi/controller.ts line-length 12df24b032b2", "src/rsi/controller.ts line-length 13b1a6484389", - "src/rsi/controller.ts line-length 13fa22340eb0", - "src/rsi/controller.ts line-length 149270b3b237", - "src/rsi/controller.ts line-length 149d491f4f20", "src/rsi/controller.ts line-length 15ed38f98476", "src/rsi/controller.ts line-length 1613e42408eb", "src/rsi/controller.ts line-length 1623c4364005", "src/rsi/controller.ts line-length 164b37c01591", "src/rsi/controller.ts line-length 1791a9aeec16", - "src/rsi/controller.ts line-length 17df174b2b61", "src/rsi/controller.ts line-length 180493bd2420", "src/rsi/controller.ts line-length 194cbe36d167", "src/rsi/controller.ts line-length 19638d4395e0", @@ -16692,102 +16948,61 @@ "src/rsi/controller.ts line-length 1aad26e1ce7c", "src/rsi/controller.ts line-length 1b2d02ae7756", "src/rsi/controller.ts line-length 1c20e97cb11e", - "src/rsi/controller.ts line-length 1cce54c83ea4", "src/rsi/controller.ts line-length 1d558def7e68", "src/rsi/controller.ts line-length 1d8511f2b143", - "src/rsi/controller.ts line-length 1d9b80e221d3", "src/rsi/controller.ts line-length 1da5264d74d0", - "src/rsi/controller.ts line-length 1ecf48b9ee40", "src/rsi/controller.ts line-length 1f8ba1428039", "src/rsi/controller.ts line-length 1fbdd95bd97c", "src/rsi/controller.ts line-length 2049fd7183b2", "src/rsi/controller.ts line-length 2050e75ad520", - "src/rsi/controller.ts line-length 209500bdc364", "src/rsi/controller.ts line-length 21078128d0a1", - "src/rsi/controller.ts line-length 21a7df0908a5", - "src/rsi/controller.ts line-length 21f6079e1a0f", "src/rsi/controller.ts line-length 222c61fb3b72", - "src/rsi/controller.ts line-length 229030fce4e1", "src/rsi/controller.ts line-length 23191ab0aef4", - "src/rsi/controller.ts line-length 2330266ea3eb", "src/rsi/controller.ts line-length 23a6f61779ca", - "src/rsi/controller.ts line-length 23c3714a2832", "src/rsi/controller.ts line-length 23d9c0017ff0", - "src/rsi/controller.ts line-length 24c1ea33e62a", "src/rsi/controller.ts line-length 256cd913dc74", - "src/rsi/controller.ts line-length 2592850143c7", - "src/rsi/controller.ts line-length 272acd5d2f79", - "src/rsi/controller.ts line-length 283a2140644c", "src/rsi/controller.ts line-length 286d107aa393", "src/rsi/controller.ts line-length 2899fb1a54f8", - "src/rsi/controller.ts line-length 28c3d6fca1c6", "src/rsi/controller.ts line-length 299ca83bdc30", "src/rsi/controller.ts line-length 2a33ab74544a", - "src/rsi/controller.ts line-length 2a87d64a1f90", "src/rsi/controller.ts line-length 2a8d71643928", - "src/rsi/controller.ts line-length 2c2c8e659e8e", "src/rsi/controller.ts line-length 2c6612c0f8da", - "src/rsi/controller.ts line-length 2cba894a7973", - "src/rsi/controller.ts line-length 2d2dfa1d05ae", "src/rsi/controller.ts line-length 2d7bdf143bb4", "src/rsi/controller.ts line-length 2df44e858cb8", "src/rsi/controller.ts line-length 2e76d25faff6", - "src/rsi/controller.ts line-length 2ef4e8aa1680", - "src/rsi/controller.ts line-length 2ef6cd1e9485", - "src/rsi/controller.ts line-length 2f7a3ebc32f8", - "src/rsi/controller.ts line-length 3028c9171cd0", "src/rsi/controller.ts line-length 302ab5c982bf", - "src/rsi/controller.ts line-length 30498a460b0a", - "src/rsi/controller.ts line-length 305758a7aff9", "src/rsi/controller.ts line-length 3075cb5d8211", "src/rsi/controller.ts line-length 31f23cf9bab2", "src/rsi/controller.ts line-length 3212debcf93d", - "src/rsi/controller.ts line-length 32646a5bc0da", "src/rsi/controller.ts line-length 32abad8d1732", "src/rsi/controller.ts line-length 32f26c9d6ccd", - "src/rsi/controller.ts line-length 342c05d5e031", "src/rsi/controller.ts line-length 34c266b6cd15", "src/rsi/controller.ts line-length 34f57590ed69", + "src/rsi/controller.ts line-length 35245e11151e", "src/rsi/controller.ts line-length 371b57b2fbb7", "src/rsi/controller.ts line-length 373f896b4d24", - "src/rsi/controller.ts line-length 38cad9a41af5", - "src/rsi/controller.ts line-length 39662fb4d279", "src/rsi/controller.ts line-length 39cedd9149e6", "src/rsi/controller.ts line-length 39ded1abfbdd", - "src/rsi/controller.ts line-length 39ecaf34fb9f", "src/rsi/controller.ts line-length 3ae7dcb87971", "src/rsi/controller.ts line-length 3b12087032c3", - "src/rsi/controller.ts line-length 3b78277c5362", "src/rsi/controller.ts line-length 3bf14397a867", "src/rsi/controller.ts line-length 3c0c7db6082d", "src/rsi/controller.ts line-length 3c127ef30ac9", "src/rsi/controller.ts line-length 3c74e99d0588", "src/rsi/controller.ts line-length 3c8297a4c272", "src/rsi/controller.ts line-length 3d05a5b39a5c", - "src/rsi/controller.ts line-length 3d1b6740495a", "src/rsi/controller.ts line-length 3d9ae7ab0c16", "src/rsi/controller.ts line-length 3e30f7bd6352", "src/rsi/controller.ts line-length 3e38d33a9691", "src/rsi/controller.ts line-length 3eb563ca3b27", "src/rsi/controller.ts line-length 3ed7d42a6af3", - "src/rsi/controller.ts line-length 3eda5d1df583", "src/rsi/controller.ts line-length 3ef46ef03a4d", - "src/rsi/controller.ts line-length 3f0b946a939c", - "src/rsi/controller.ts line-length 3f45856cb7c8", "src/rsi/controller.ts line-length 3f992a259cb0", "src/rsi/controller.ts line-length 400f892799ea", "src/rsi/controller.ts line-length 40673808c0d0", - "src/rsi/controller.ts line-length 40e42ec5355a", - "src/rsi/controller.ts line-length 4147daf1f692", - "src/rsi/controller.ts line-length 41f8e5795685", - "src/rsi/controller.ts line-length 4227bbd1f07d", "src/rsi/controller.ts line-length 426edb59f1e3", - "src/rsi/controller.ts line-length 4295bfcdfa9c", "src/rsi/controller.ts line-length 42a0ed5e2a0d", - "src/rsi/controller.ts line-length 42bff0a23ca2", - "src/rsi/controller.ts line-length 432130e4474c", "src/rsi/controller.ts line-length 43465f01cc53", - "src/rsi/controller.ts line-length 449944b10eae", "src/rsi/controller.ts line-length 44be2d53cfe2", "src/rsi/controller.ts line-length 4512028933ba", "src/rsi/controller.ts line-length 454abec10567", @@ -16798,175 +17013,99 @@ "src/rsi/controller.ts line-length 46964b4f55d7", "src/rsi/controller.ts line-length 46c8b3669f4f", "src/rsi/controller.ts line-length 4790943397f6", - "src/rsi/controller.ts line-length 4872440ae744", "src/rsi/controller.ts line-length 488182ff1393", - "src/rsi/controller.ts line-length 48ab74c7266a", - "src/rsi/controller.ts line-length 48d88230709f", "src/rsi/controller.ts line-length 496baf72a809", - "src/rsi/controller.ts line-length 49dcb82dc27c", "src/rsi/controller.ts line-length 49e3789e2bda", "src/rsi/controller.ts line-length 49e52ce0be8c", - "src/rsi/controller.ts line-length 49f760e04286", "src/rsi/controller.ts line-length 4b325ece5f4a", "src/rsi/controller.ts line-length 4bd8d16b278c", - "src/rsi/controller.ts line-length 4c45979d311d", - "src/rsi/controller.ts line-length 4c92c43cafb6", - "src/rsi/controller.ts line-length 4cf5c15bc220", "src/rsi/controller.ts line-length 4cf95c18c733", "src/rsi/controller.ts line-length 4d85da373b93", - "src/rsi/controller.ts line-length 4e43af7e504f", "src/rsi/controller.ts line-length 4f2b93b7f5b5", - "src/rsi/controller.ts line-length 4f7b3f72fd8c", "src/rsi/controller.ts line-length 4fafc86bf76a", "src/rsi/controller.ts line-length 4fe31d3df58c", - "src/rsi/controller.ts line-length 500ecd8af3fe", "src/rsi/controller.ts line-length 50ab60378e82", - "src/rsi/controller.ts line-length 51329f1f9b21", "src/rsi/controller.ts line-length 51cbd27eb8b9", - "src/rsi/controller.ts line-length 51fef3b8b1d0", "src/rsi/controller.ts line-length 525ba1e66fac", "src/rsi/controller.ts line-length 52a4b131fcab", "src/rsi/controller.ts line-length 52bfef5c386a", "src/rsi/controller.ts line-length 52ebba0a6011", - "src/rsi/controller.ts line-length 52fc803a0b99", - "src/rsi/controller.ts line-length 53451963d0a9", - "src/rsi/controller.ts line-length 535ae9e85999", - "src/rsi/controller.ts line-length 53b107cef55f", "src/rsi/controller.ts line-length 53b2ef87408b", "src/rsi/controller.ts line-length 53c1d3af259d", "src/rsi/controller.ts line-length 5464457cceb4", - "src/rsi/controller.ts line-length 5486641c542c", "src/rsi/controller.ts line-length 54b4326a0920", "src/rsi/controller.ts line-length 54df212c1def", "src/rsi/controller.ts line-length 552c9f392ec5", "src/rsi/controller.ts line-length 5593a1ce80a5", "src/rsi/controller.ts line-length 55bf41a12d12", "src/rsi/controller.ts line-length 56355d440127", - "src/rsi/controller.ts line-length 579227720e72", - "src/rsi/controller.ts line-length 579c3c5df137", "src/rsi/controller.ts line-length 5816c5b2620c", - "src/rsi/controller.ts line-length 582f8c204e62", "src/rsi/controller.ts line-length 58867058c1d7", "src/rsi/controller.ts line-length 58eec4c1733e", - "src/rsi/controller.ts line-length 5909d0c346f5", "src/rsi/controller.ts line-length 5b1374c43948", "src/rsi/controller.ts line-length 5b832a0ce6f2", "src/rsi/controller.ts line-length 5b87cabf0142", "src/rsi/controller.ts line-length 5c1a7ccce0cd", "src/rsi/controller.ts line-length 5c5bf6db483c", - "src/rsi/controller.ts line-length 5c684efbe877", "src/rsi/controller.ts line-length 5cc5cf9d159d", - "src/rsi/controller.ts line-length 5d05822a7101", - "src/rsi/controller.ts line-length 5ddaeb95ac47", - "src/rsi/controller.ts line-length 5de754eecc14", - "src/rsi/controller.ts line-length 5dee2fcc5fdb", - "src/rsi/controller.ts line-length 5e1cce386eee", "src/rsi/controller.ts line-length 5e3d7bfbd21f", "src/rsi/controller.ts line-length 5e9903132451", - "src/rsi/controller.ts line-length 5effdbccc323", "src/rsi/controller.ts line-length 5f068699f71a", "src/rsi/controller.ts line-length 5f22ff9f4107", "src/rsi/controller.ts line-length 5fb83239da41", - "src/rsi/controller.ts line-length 5fec1d1bb959", "src/rsi/controller.ts line-length 5ff7b69a191d", "src/rsi/controller.ts line-length 604c7b2675fd", - "src/rsi/controller.ts line-length 6140e9234eb7", "src/rsi/controller.ts line-length 6163cd6431b9", "src/rsi/controller.ts line-length 616f1d5612bb", "src/rsi/controller.ts line-length 625fff38002e", "src/rsi/controller.ts line-length 64d87ec34974", "src/rsi/controller.ts line-length 657d6c726cc0", - "src/rsi/controller.ts line-length 65bc1a4efef1", - "src/rsi/controller.ts line-length 6669e9ed254c", "src/rsi/controller.ts line-length 668b47a15658", "src/rsi/controller.ts line-length 6711b6fcec72", - "src/rsi/controller.ts line-length 6733badf1f9f", "src/rsi/controller.ts line-length 673b174247ea", "src/rsi/controller.ts line-length 67ccddc6f3e4", - "src/rsi/controller.ts line-length 6856428b6e08", "src/rsi/controller.ts line-length 68a5cd600934", "src/rsi/controller.ts line-length 68b0ad3302c2", "src/rsi/controller.ts line-length 698d1670c9b0", "src/rsi/controller.ts line-length 69901429aa2d", - "src/rsi/controller.ts line-length 69e7a210a12e", - "src/rsi/controller.ts line-length 6a02887c9f96", "src/rsi/controller.ts line-length 6a258b6a9512", "src/rsi/controller.ts line-length 6a7533ff4c3a", "src/rsi/controller.ts line-length 6b8601d88af6", "src/rsi/controller.ts line-length 6ba939433e76", "src/rsi/controller.ts line-length 6c0680ef9d83", "src/rsi/controller.ts line-length 6c330a974efa", - "src/rsi/controller.ts line-length 6c9a1a19ffb4", "src/rsi/controller.ts line-length 6c9db4b6b9d6", - "src/rsi/controller.ts line-length 6d8e55562fad", "src/rsi/controller.ts line-length 6d90ae353329", - "src/rsi/controller.ts line-length 6e44788ab27b", "src/rsi/controller.ts line-length 6ed912aa74c5", - "src/rsi/controller.ts line-length 6f449bcdf836", "src/rsi/controller.ts line-length 6fad947f84b7", "src/rsi/controller.ts line-length 70c08053adff", "src/rsi/controller.ts line-length 70fdc3833455", - "src/rsi/controller.ts line-length 715088cf3389", - "src/rsi/controller.ts line-length 71bbc06842b0", - "src/rsi/controller.ts line-length 71e56c4655cf", "src/rsi/controller.ts line-length 71fd1e4c8f60", - "src/rsi/controller.ts line-length 7238b71c363d", - "src/rsi/controller.ts line-length 724e9c8cdfc5", "src/rsi/controller.ts line-length 72df7e5e47e1", "src/rsi/controller.ts line-length 72f1a09836a1", - "src/rsi/controller.ts line-length 734db330c544", "src/rsi/controller.ts line-length 736ea4638265", - "src/rsi/controller.ts line-length 73d940e8dd69", "src/rsi/controller.ts line-length 74a867daaf15", - "src/rsi/controller.ts line-length 74b8fea7e59a", "src/rsi/controller.ts line-length 75732ca8d0c5", "src/rsi/controller.ts line-length 761f67021f42", - "src/rsi/controller.ts line-length 76216ddb6a97", - "src/rsi/controller.ts line-length 767f64a08f5b", - "src/rsi/controller.ts line-length 770463e88836", - "src/rsi/controller.ts line-length 784766dffc7a", "src/rsi/controller.ts line-length 784bbfff5186", - "src/rsi/controller.ts line-length 784feb3f53ae", "src/rsi/controller.ts line-length 7860638d7ef9", "src/rsi/controller.ts line-length 7a7d0effdd57", - "src/rsi/controller.ts line-length 7ac05a4a8e27", "src/rsi/controller.ts line-length 7afe46b44310", - "src/rsi/controller.ts line-length 7aff4c199bbc", - "src/rsi/controller.ts line-length 7b4fa91c9969", "src/rsi/controller.ts line-length 7bbf2521fcb7", "src/rsi/controller.ts line-length 7c2b0d342292", "src/rsi/controller.ts line-length 7c3a3f3c203b", - "src/rsi/controller.ts line-length 7c3bc718bb66", "src/rsi/controller.ts line-length 7dc48a70c581", - "src/rsi/controller.ts line-length 7ea86e318891", - "src/rsi/controller.ts line-length 7eb8cd61e4b7", "src/rsi/controller.ts line-length 7ec785ce9ad1", - "src/rsi/controller.ts line-length 7ed726a5834e", "src/rsi/controller.ts line-length 7f7969ffe870", - "src/rsi/controller.ts line-length 7fc27d873c51", "src/rsi/controller.ts line-length 7ff293d26852", - "src/rsi/controller.ts line-length 80276ddaa0f0", - "src/rsi/controller.ts line-length 8178d986b4c6", - "src/rsi/controller.ts line-length 817b40d1a28a", - "src/rsi/controller.ts line-length 820e87c32148", "src/rsi/controller.ts line-length 829034aa8a7f", "src/rsi/controller.ts line-length 82e0db7b5961", "src/rsi/controller.ts line-length 82e8834c8d2a", - "src/rsi/controller.ts line-length 830f778cb7e3", - "src/rsi/controller.ts line-length 837e087db68b", "src/rsi/controller.ts line-length 83d2d3858e0b", "src/rsi/controller.ts line-length 83dc2b9f026e", - "src/rsi/controller.ts line-length 842dc294c4ee", - "src/rsi/controller.ts line-length 84b9f4ce0a6e", - "src/rsi/controller.ts line-length 84e063bd3865", - "src/rsi/controller.ts line-length 84f1ef789dad", "src/rsi/controller.ts line-length 8504ab5e6201", - "src/rsi/controller.ts line-length 8537b73e8f62", - "src/rsi/controller.ts line-length 859ae850335d", "src/rsi/controller.ts line-length 863b27360c59", - "src/rsi/controller.ts line-length 8702969a1360", "src/rsi/controller.ts line-length 872934ca0b80", - "src/rsi/controller.ts line-length 8774972a8f32", "src/rsi/controller.ts line-length 8786d67feb7b", "src/rsi/controller.ts line-length 8802d8c72539", "src/rsi/controller.ts line-length 88f30f474517", @@ -16976,172 +17115,106 @@ "src/rsi/controller.ts line-length 8a2edcaa2cb7", "src/rsi/controller.ts line-length 8a349dd31b19", "src/rsi/controller.ts line-length 8aec0b7baada", - "src/rsi/controller.ts line-length 8c15614e76ef", "src/rsi/controller.ts line-length 8c22fbc0a31c", - "src/rsi/controller.ts line-length 8c478ebffd0a", - "src/rsi/controller.ts line-length 8ca21212eba8", "src/rsi/controller.ts line-length 8d0ec0998502", "src/rsi/controller.ts line-length 8d13a9c863ec", "src/rsi/controller.ts line-length 8d271555a6e7", - "src/rsi/controller.ts line-length 8d313d0b328a", "src/rsi/controller.ts line-length 8d798c1c1f92", - "src/rsi/controller.ts line-length 8d9d15162da4", "src/rsi/controller.ts line-length 8eb2435e98c2", "src/rsi/controller.ts line-length 8ee09f1b1c79", "src/rsi/controller.ts line-length 8f1c7c6d97bd", - "src/rsi/controller.ts line-length 8f88af73beea", "src/rsi/controller.ts line-length 8fdd96946d41", "src/rsi/controller.ts line-length 90131525314d", "src/rsi/controller.ts line-length 90ae18486876", "src/rsi/controller.ts line-length 90dc74b57b93", "src/rsi/controller.ts line-length 91e39a24b8a1", "src/rsi/controller.ts line-length 92edfd43298e", - "src/rsi/controller.ts line-length 933a1edec6e4", "src/rsi/controller.ts line-length 93d57924697e", "src/rsi/controller.ts line-length 93e9b903363a", - "src/rsi/controller.ts line-length 93f134d75c50", - "src/rsi/controller.ts line-length 948b227f25c4", "src/rsi/controller.ts line-length 94de7aaf6c63", - "src/rsi/controller.ts line-length 9527d5444765", "src/rsi/controller.ts line-length 9545198a8402", - "src/rsi/controller.ts line-length 964f109c2ae0", - "src/rsi/controller.ts line-length 97535e279929", "src/rsi/controller.ts line-length 97924b7ce732", "src/rsi/controller.ts line-length 979cb7c7e47f", - "src/rsi/controller.ts line-length 97d121f24ecf", - "src/rsi/controller.ts line-length 97ee906666a5", "src/rsi/controller.ts line-length 97f4f6c3ccc6", "src/rsi/controller.ts line-length 985bd046bc0c", - "src/rsi/controller.ts line-length 988beb375327", "src/rsi/controller.ts line-length 98929f07839a", - "src/rsi/controller.ts line-length 98ba51775089", "src/rsi/controller.ts line-length 98fab6009e53", "src/rsi/controller.ts line-length 99356929c7ee", "src/rsi/controller.ts line-length 9960edc57f95", "src/rsi/controller.ts line-length 99629f4f8a5c", "src/rsi/controller.ts line-length 996a03e83d1d", "src/rsi/controller.ts line-length 99d179f2d516", - "src/rsi/controller.ts line-length 9a3e247a5c61", - "src/rsi/controller.ts line-length 9a56cbb336ff", "src/rsi/controller.ts line-length 9a89e8167b27", - "src/rsi/controller.ts line-length 9b5a491383e8", "src/rsi/controller.ts line-length 9bc9c827bb85", - "src/rsi/controller.ts line-length 9bda0c6ad6c8", "src/rsi/controller.ts line-length 9bf92fe293a9", "src/rsi/controller.ts line-length 9c3cc8db8804", "src/rsi/controller.ts line-length 9cb6c96a6413", - "src/rsi/controller.ts line-length 9e0a09e50cb6", "src/rsi/controller.ts line-length 9e8c35643801", "src/rsi/controller.ts line-length 9ea65f49eb63", "src/rsi/controller.ts line-length 9ee52554d31b", "src/rsi/controller.ts line-length 9ef5856dd875", "src/rsi/controller.ts line-length 9f138189ccd8", - "src/rsi/controller.ts line-length 9f82fa1c2415", "src/rsi/controller.ts line-length 9fb29b133f81", - "src/rsi/controller.ts line-length 9fcb5a70899c", "src/rsi/controller.ts line-length 9fd76ccb3188", - "src/rsi/controller.ts line-length 9fe6413c7818", - "src/rsi/controller.ts line-length a04ea4c13d9f", "src/rsi/controller.ts line-length a0d649e0c34d", "src/rsi/controller.ts line-length a169ee791f36", - "src/rsi/controller.ts line-length a1a6fc8028fa", - "src/rsi/controller.ts line-length a200cee024c1", - "src/rsi/controller.ts line-length a209a294dd36", "src/rsi/controller.ts line-length a273e04bf9c2", - "src/rsi/controller.ts line-length a29f7acb6792", "src/rsi/controller.ts line-length a2d47b9e3583", "src/rsi/controller.ts line-length a3f26af53c64", - "src/rsi/controller.ts line-length a4088b76c26d", - "src/rsi/controller.ts line-length a45926dd7f0b", - "src/rsi/controller.ts line-length a4d508d91383", "src/rsi/controller.ts line-length a56b7275faa5", "src/rsi/controller.ts line-length a649f14cf2d8", - "src/rsi/controller.ts line-length a70ac11e9ed2", - "src/rsi/controller.ts line-length a810982f06d4", - "src/rsi/controller.ts line-length a87e27f6f502", "src/rsi/controller.ts line-length a886d09c14f1", "src/rsi/controller.ts line-length a9c744ebf626", "src/rsi/controller.ts line-length a9e1eeb1bc13", "src/rsi/controller.ts line-length a9e3aa83fab4", "src/rsi/controller.ts line-length aa5cc64bdcef", - "src/rsi/controller.ts line-length aa6063feb6bf", - "src/rsi/controller.ts line-length aad2d5ebb1b3", - "src/rsi/controller.ts line-length ab50151f5b66", - "src/rsi/controller.ts line-length ac0aec349f20", "src/rsi/controller.ts line-length ad86b3912201", - "src/rsi/controller.ts line-length adab68d7f7ee", "src/rsi/controller.ts line-length ae2ae3e6ac13", "src/rsi/controller.ts line-length af2b60042d70", - "src/rsi/controller.ts line-length af4a61ee144a", "src/rsi/controller.ts line-length af7f7cbc3dc2", - "src/rsi/controller.ts line-length b013d9b03a0a", "src/rsi/controller.ts line-length b03c2bdd9187", "src/rsi/controller.ts line-length b0e5c1dbe541", "src/rsi/controller.ts line-length b10cc4f313d9", "src/rsi/controller.ts line-length b123e25782ce", "src/rsi/controller.ts line-length b12da80b9d6b", "src/rsi/controller.ts line-length b1bcaa543b24", - "src/rsi/controller.ts line-length b27da14f1c50", "src/rsi/controller.ts line-length b2ec8566e1e8", "src/rsi/controller.ts line-length b3071380ea88", "src/rsi/controller.ts line-length b31ebdd25b89", "src/rsi/controller.ts line-length b335b1e6a633", "src/rsi/controller.ts line-length b42ba5bbe17e", - "src/rsi/controller.ts line-length b42f91892bb1", - "src/rsi/controller.ts line-length b45e7721b90a", - "src/rsi/controller.ts line-length b537437748ba", - "src/rsi/controller.ts line-length b5754aa04203", - "src/rsi/controller.ts line-length b5f15d2ace7d", "src/rsi/controller.ts line-length b652458ca652", "src/rsi/controller.ts line-length b6e34699bc5a", "src/rsi/controller.ts line-length b7304df10449", - "src/rsi/controller.ts line-length b7596f88003e", + "src/rsi/controller.ts line-length b777b0cfc4c6", "src/rsi/controller.ts line-length b78a21bb9610", - "src/rsi/controller.ts line-length b78edac26145", "src/rsi/controller.ts line-length b841f3c8982b", - "src/rsi/controller.ts line-length b9109975feda", "src/rsi/controller.ts line-length b9aef733863e", "src/rsi/controller.ts line-length b9d2943dd170", "src/rsi/controller.ts line-length b9fdcd28c893", - "src/rsi/controller.ts line-length ba7ca25e4e0f", "src/rsi/controller.ts line-length bbade7dfd8ae", "src/rsi/controller.ts line-length bbcc4d63f361", "src/rsi/controller.ts line-length bc16065efa8e", - "src/rsi/controller.ts line-length bc2a91a43ff4", "src/rsi/controller.ts line-length bc34e3274673", "src/rsi/controller.ts line-length bc53c3e5e154", "src/rsi/controller.ts line-length bc7e778b161e", - "src/rsi/controller.ts line-length bca105c4085b", - "src/rsi/controller.ts line-length be24465fc930", "src/rsi/controller.ts line-length be979166c916", - "src/rsi/controller.ts line-length bee40fe11ec3", "src/rsi/controller.ts line-length bf042453a040", "src/rsi/controller.ts line-length bf92c001ca2f", "src/rsi/controller.ts line-length bfbde0418c83", - "src/rsi/controller.ts line-length c04167fc664d", "src/rsi/controller.ts line-length c0b38c68ce00", - "src/rsi/controller.ts line-length c2cce65db5c7", - "src/rsi/controller.ts line-length c2e0feb380e3", - "src/rsi/controller.ts line-length c342bb5fc935", "src/rsi/controller.ts line-length c36f42a76e93", "src/rsi/controller.ts line-length c39afef8fec7", - "src/rsi/controller.ts line-length c39d3b1ac4c5", - "src/rsi/controller.ts line-length c453279a8b0b", - "src/rsi/controller.ts line-length c487f6ddb69d", "src/rsi/controller.ts line-length c49908c5b49b", "src/rsi/controller.ts line-length c4c2b6f5fd1b", - "src/rsi/controller.ts line-length c4ceccf500dd", - "src/rsi/controller.ts line-length c4d0c4b54747", "src/rsi/controller.ts line-length c5620ba1bdb2", "src/rsi/controller.ts line-length c56243f180af", "src/rsi/controller.ts line-length c5843c1389fd", "src/rsi/controller.ts line-length c5ff8c45701f", - "src/rsi/controller.ts line-length c608e9c8326a", "src/rsi/controller.ts line-length c63c2ed31dc1", "src/rsi/controller.ts line-length c6f20a7c25d3", "src/rsi/controller.ts line-length c7a03a508919", "src/rsi/controller.ts line-length c82891963b10", - "src/rsi/controller.ts line-length c83799d6b177", "src/rsi/controller.ts line-length c841db709c66", "src/rsi/controller.ts line-length c863ccabd048", "src/rsi/controller.ts line-length c89c11b300e0", @@ -17150,44 +17223,27 @@ "src/rsi/controller.ts line-length c904cb530ad4", "src/rsi/controller.ts line-length c96b39c25b9f", "src/rsi/controller.ts line-length c9cc92c468a5", - "src/rsi/controller.ts line-length cb6e302e85be", "src/rsi/controller.ts line-length cc0ce27dd508", "src/rsi/controller.ts line-length cc399f0a9f63", - "src/rsi/controller.ts line-length cca018112319", - "src/rsi/controller.ts line-length ccb502f3a919", "src/rsi/controller.ts line-length cd1a92352c1d", "src/rsi/controller.ts line-length cdaa03b98a38", - "src/rsi/controller.ts line-length cdbb6cbe84d5", "src/rsi/controller.ts line-length cde598dcdaa3", "src/rsi/controller.ts line-length ce69576162b3", - "src/rsi/controller.ts line-length ced918645d08", - "src/rsi/controller.ts line-length d001f7c0dde9", - "src/rsi/controller.ts line-length d092485d43e7", "src/rsi/controller.ts line-length d0a149788458", "src/rsi/controller.ts line-length d0c46354789e", - "src/rsi/controller.ts line-length d178d787dbba", - "src/rsi/controller.ts line-length d1d87a4ecbb9", "src/rsi/controller.ts line-length d2480d8c974b", "src/rsi/controller.ts line-length d2a62d60fbf4", - "src/rsi/controller.ts line-length d33775e192bd", - "src/rsi/controller.ts line-length d34951674c19", - "src/rsi/controller.ts line-length d3824247d0fc", - "src/rsi/controller.ts line-length d3a80b5a9c2c", "src/rsi/controller.ts line-length d4072c5deee5", "src/rsi/controller.ts line-length d481e6c165c4", "src/rsi/controller.ts line-length d4fb99749665", "src/rsi/controller.ts line-length d53bd28d7d92", - "src/rsi/controller.ts line-length d6096c05a82f", "src/rsi/controller.ts line-length d69d13a6845f", - "src/rsi/controller.ts line-length d6ce2d8aef42", "src/rsi/controller.ts line-length d6eb198f8127", "src/rsi/controller.ts line-length d84c37cdc4e3", - "src/rsi/controller.ts line-length d8810fcdfb13", "src/rsi/controller.ts line-length d92592d630f8", "src/rsi/controller.ts line-length d92ccac0b5ad", "src/rsi/controller.ts line-length d951fdec625a", "src/rsi/controller.ts line-length d9fc085d9c16", - "src/rsi/controller.ts line-length daa7fb2b7cea", "src/rsi/controller.ts line-length dbc9d7d1debd", "src/rsi/controller.ts line-length dbfa3e824204", "src/rsi/controller.ts line-length dd0a14c55f84", @@ -17196,39 +17252,22 @@ "src/rsi/controller.ts line-length df390f41a944", "src/rsi/controller.ts line-length df9d474c333f", "src/rsi/controller.ts line-length dfe1ec0ce2a8", - "src/rsi/controller.ts line-length e0d84fbf6f30", "src/rsi/controller.ts line-length e0d96950a8e1", - "src/rsi/controller.ts line-length e1c281e2da03", "src/rsi/controller.ts line-length e2e6845809c4", "src/rsi/controller.ts line-length e333d09a86a5", "src/rsi/controller.ts line-length e37c175e96d2", "src/rsi/controller.ts line-length e3d8438631cf", "src/rsi/controller.ts line-length e3f94b793d7b", - "src/rsi/controller.ts line-length e4708a8d542a", - "src/rsi/controller.ts line-length e4eaf2b5a452", "src/rsi/controller.ts line-length e4eea9df4182", - "src/rsi/controller.ts line-length e5c210fd3897", "src/rsi/controller.ts line-length e608bb663474", - "src/rsi/controller.ts line-length e637499f9b54", "src/rsi/controller.ts line-length e6b476f6c71f", - "src/rsi/controller.ts line-length e776125defcf", "src/rsi/controller.ts line-length e79c519c642d", - "src/rsi/controller.ts line-length e7b2dbd8c795", - "src/rsi/controller.ts line-length e7dd9a682e12", "src/rsi/controller.ts line-length e8524799139b", - "src/rsi/controller.ts line-length e8739478e19e", - "src/rsi/controller.ts line-length e8ee4d25006c", "src/rsi/controller.ts line-length e9221f956521", "src/rsi/controller.ts line-length e9550a2630d2", - "src/rsi/controller.ts line-length e9df26ebe365", - "src/rsi/controller.ts line-length ea3bcbf1fbff", "src/rsi/controller.ts line-length ea914f311f3e", "src/rsi/controller.ts line-length ebd680a48545", - "src/rsi/controller.ts line-length ec2c0a0ae538", - "src/rsi/controller.ts line-length eca609ecc724", "src/rsi/controller.ts line-length ecc6b5f2b703", - "src/rsi/controller.ts line-length ecdbca648c94", - "src/rsi/controller.ts line-length ed0aed785df6", "src/rsi/controller.ts line-length ed3776ad6d7e", "src/rsi/controller.ts line-length ed6f26bffb8a", "src/rsi/controller.ts line-length ed79e3e57974", @@ -17240,39 +17279,23 @@ "src/rsi/controller.ts line-length f1bb366cf73e", "src/rsi/controller.ts line-length f2873f51ce46", "src/rsi/controller.ts line-length f31f2c673560", - "src/rsi/controller.ts line-length f328ba935b5b", - "src/rsi/controller.ts line-length f36f24c02a2b", "src/rsi/controller.ts line-length f488c637d21c", "src/rsi/controller.ts line-length f59e3558d1f1", - "src/rsi/controller.ts line-length f5a73cfbcb45", - "src/rsi/controller.ts line-length f6c148513665", - "src/rsi/controller.ts line-length f6e66c25ac14", "src/rsi/controller.ts line-length f6ea4be0c529", "src/rsi/controller.ts line-length f756e0a6736f", "src/rsi/controller.ts line-length f77f98cc71c3", - "src/rsi/controller.ts line-length f7fb05568dfc", - "src/rsi/controller.ts line-length f864ea1ec460", - "src/rsi/controller.ts line-length f9086a6bb35c", - "src/rsi/controller.ts line-length f945a088afac", "src/rsi/controller.ts line-length f94a527cf747", "src/rsi/controller.ts line-length f960b2dad103", "src/rsi/controller.ts line-length fa04de568e0b", "src/rsi/controller.ts line-length fa142685995a", "src/rsi/controller.ts line-length fa91efcbc30a", - "src/rsi/controller.ts line-length fb102dab9403", "src/rsi/controller.ts line-length fb44cc8f9404", - "src/rsi/controller.ts line-length fb4a88465a13", "src/rsi/controller.ts line-length fbb1fe606b4c", "src/rsi/controller.ts line-length fbc851890f09", - "src/rsi/controller.ts line-length fbfbd6baf1f5", "src/rsi/controller.ts line-length fcfbb9f0c488", "src/rsi/controller.ts line-length fd9091d30619", "src/rsi/controller.ts line-length fde9fd0db57b", - "src/rsi/controller.ts line-length fdf26b2812d5", - "src/rsi/controller.ts line-length fe40885fa2db", - "src/rsi/controller.ts line-length feb8ce2ca4d2", "src/rsi/controller.ts line-length ff509ab848f1", - "src/rsi/controller.ts line-length ffa6885d633d", "src/rsi/curriculum.ts function-lines 1a9b6a9564f8", "src/rsi/curriculum.ts function-lines 9ae53d8c26ea", "src/rsi/curriculum.ts function-lines a0fa4b25f42f", diff --git a/src/rsi/__tests__/controller-candidates.test.ts b/src/rsi/__tests__/controller-candidates.test.ts new file mode 100644 index 0000000..8cb651b --- /dev/null +++ b/src/rsi/__tests__/controller-candidates.test.ts @@ -0,0 +1,183 @@ +import assert from "node:assert/strict" +import test from "node:test" +import { + combinedArchive, + improveRunOutcome, + initialRun, + recoverInterruptedCandidates, + replaceJob, + validateDeterministicWorkerHarnessRunId, +} from "../controller.js" +import { baseModelCandidate } from "../models.js" +import type { + CandidateRecord, + ExperimentJob, + RsiArchive, + RsiConfig, + RsiRunRecord, +} from "../types.js" + +function config(overrides: Partial = {}): RsiConfig { + return { + repoRoot: "/tmp/repo", + model: "fixture-model", + population: 2, + generations: 2, + maxConcurrent: 1, + mutationTask: "fixture evolution", + regressionCommand: "npm test", + evalCommands: ["visible"], + hiddenEvalCommands: [], + archiveDir: "/tmp/archive", + worktreeDir: "/tmp/worktrees", + baseRef: "HEAD", + seed: "candidate-test", + dryRun: false, + keepWorktrees: false, + maxIterations: 2, + protectedPaths: [], + commandTimeoutMs: 1000, + ...overrides, + } +} + +function candidate(overrides: Partial = {}): CandidateRecord { + return { + id: "candidate-a", + generation: 0, + parent: "baseline", + branch: "rsi/candidate-a", + worktree: "/tmp/worktree", + baseCommit: "a".repeat(40), + status: "evaluating", + model: "fixture-model", + mutation: "fixture", + createdAt: "2026-09-20T00:00:00.000Z", + updatedAt: "2026-09-20T00:00:01.000Z", + commits: [], + changedFiles: [], + protectedPathViolations: [], + ...overrides, + } +} + +function job(overrides: Partial = {}): ExperimentJob { + return { + id: "job-1", + kind: "mutation", + status: "queued", + resource: { class: "CPU", units: 1 }, + createdAt: "2026-09-20T00:00:00.000Z", + updatedAt: "2026-09-20T00:00:00.000Z", + attempts: 0, + ...overrides, + } +} + +function run(overrides: Partial = {}): RsiRunRecord { + return { + runId: "rsi-run", + startedAt: "2026-09-20T00:00:00.000Z", + model: "fixture-model", + baseRef: "HEAD", + baseCommit: "a".repeat(40), + generations: 1, + candidates: [], + reports: [], + ...overrides, + } +} + +test("initial run freezes the run identity and model candidate", () => { + const started = initialRun(config(), "a".repeat(40), "2026-09-20T00:00:00.000Z") + assert.equal(started.baseCommit, "a".repeat(40)) + assert.equal(started.generations, 2) + assert.deepEqual( + started.modelCandidates, + [baseModelCandidate("fixture-model", "2026-09-20T00:00:00.000Z", "model-base")], + ) + assert.ok(started.runId.startsWith("rsi-20260920000000-")) +}) + +test("initial run honors deterministic harness identity", () => { + const pairId = "rsi-proof-a1b2c3d4e5f60718293a" + const started = initialRun(config({ + workerHarnessMode: "fixed-control", + workerHarnessPairId: pairId, + workerHarnessRunId: `${pairId}-fixed`, + }), "a".repeat(40), "2026-09-20T00:00:00.000Z") + assert.equal(started.runId, `${pairId}-fixed`) +}) + +test("deterministic harness run id rejects mismatched pairs", () => { + assert.throws(() => validateDeterministicWorkerHarnessRunId(config({ + workerHarnessMode: "fixed-control", + workerHarnessRunId: "rsi-proof-wrong", + })), /must match its paired campaign/) + assert.throws(() => validateDeterministicWorkerHarnessRunId(config({ + workerHarnessMode: "fixed-control", + workerHarnessPairId: "rsi-proof-a1b2c3d4e5f60718293", + workerHarnessRunId: "rsi-proof-a1b2c3d4e5f60718293-self", + })), /must match its paired campaign/) + validateDeterministicWorkerHarnessRunId(config()) +}) + +test("replace job upserts by identity", () => { + const current = run({ jobs: [job()] }) + replaceJob(current, job({ status: "completed" })) + assert.equal(current.jobs?.length, 1) + assert.equal(current.jobs?.[0]?.status, "completed") + replaceJob(current, job({ id: "job-2" })) + assert.equal(current.jobs?.length, 2) +}) + +test("combined archive prefers live candidates over stale copies", () => { + const archive = { + schemaVersion: 2, + updatedAt: "2026-09-20T00:00:00.000Z", + runs: [], + activeRuns: [], + candidates: [candidate({ id: "stale", status: "failed" })], + modelCandidates: [], + combinations: [], + jobs: [], + trajectoryRefs: [], + curriculumTasks: [], + } as RsiArchive + const merged = combinedArchive(archive, run({ + candidates: [candidate({ id: "stale", status: "accepted" }), candidate()], + })) + assert.deepEqual(merged.candidates.map((entry) => entry.id), ["stale", "candidate-a"]) + assert.equal(merged.candidates[0]?.status, "accepted") +}) + +test("interrupted candidates fail without touching terminal states", () => { + const current = run({ + candidates: [ + candidate({ id: "active", status: "mutating" }), + candidate({ id: "settled", status: "accepted" }), + ], + }) + recoverInterruptedCandidates(current, "2026-09-20T00:00:02.000Z") + assert.equal(current.candidates[0]?.status, "failed") + assert.equal(current.candidates[0]?.failure, "run interrupted before candidate reached a terminal state") + assert.equal(current.candidates[0]?.updatedAt, "2026-09-20T00:00:02.000Z") + assert.equal(current.candidates[1]?.status, "accepted") +}) + +test("improve outcome reports campaign state and selection", () => { + const incomplete = improveRunOutcome({ + runId: "run-a", + selected: "candidate-a", + proofCampaign: { state: "incomplete" }, + }, false) + assert.equal(incomplete.exitCode, 1) + assert.match(incomplete.output, /incomplete: run-a/) + const selected = improveRunOutcome({ runId: "run-a", selected: "candidate-a" }, false) + assert.equal(selected.exitCode, 0) + assert.match(selected.output, /complete: run-a; selected=candidate-a/) + const unselected = improveRunOutcome({ runId: "run-a" }, false) + assert.equal(unselected.exitCode, 1) + const dry = improveRunOutcome({ runId: "run-a" }, true) + assert.equal(dry.exitCode, 0) +}) diff --git a/src/rsi/__tests__/controller-fleet-results.test.ts b/src/rsi/__tests__/controller-fleet-results.test.ts new file mode 100644 index 0000000..a3d856c --- /dev/null +++ b/src/rsi/__tests__/controller-fleet-results.test.ts @@ -0,0 +1,230 @@ +import assert from "node:assert/strict" +import test from "node:test" +import { + fleetEvaluationResult, + fleetRuntimeDeadlineMs, + fleetWorkerHarnessExecution, + recordFleetInferenceDenials, + validateCandidateWorkerHarnessExecution, +} from "../controller.js" +import { workerHarnessCliPath } from "../worker-harness.js" +import type { + CandidateRecord, + RsiConfig, + RsiRunRecord, + TrialResult, + WorkerHarnessExecution, +} from "../types.js" +import type { FleetJobPayload } from "../postgres-queue.js" + +const workerRuntime = { + imageName: "harness", + imageDigest: `sha256:${"d".repeat(64)}`, + harnessCommit: "e".repeat(40), + dependencyLockDigest: "f".repeat(64), +} + +function config(overrides: Partial = {}): RsiConfig { + return { + repoRoot: "/tmp/repo", + model: "fixture-model", + population: 1, + generations: 1, + maxConcurrent: 1, + mutationTask: "fixture", + regressionCommand: "npm test", + evalCommands: [], + hiddenEvalCommands: [], + archiveDir: "/tmp/archive", + worktreeDir: "/tmp/worktrees", + baseRef: "HEAD", + seed: "fleet-test", + dryRun: false, + keepWorktrees: false, + maxIterations: 1, + protectedPaths: [], + commandTimeoutMs: 1000, + ...overrides, + } +} + +function candidate(overrides: Partial = {}): CandidateRecord { + return { + id: "candidate-a", + generation: 0, + parent: "baseline", + branch: "rsi/candidate-a", + worktree: "/tmp/worktree", + baseCommit: "a".repeat(40), + status: "evaluating", + model: "fixture-model", + mutation: "fixture", + createdAt: "2026-09-20T00:00:00.000Z", + updatedAt: "2026-09-20T00:00:01.000Z", + commits: [], + changedFiles: [], + protectedPathViolations: [], + ...overrides, + } +} + +function trial(command: string, sha: string): TrialResult { + return { + ok: true, + command, + exitCode: 0, + durationMs: 2, + stdout: "", + stderr: "", + outputArtifactSha256: sha, + outputArtifactBytes: 8, + } +} + +function fleetPayload(overrides: Partial = {}): FleetJobPayload { + return { + schemaVersion: 1, + snapshotSha256: "s".repeat(64), + snapshotBytes: 8, + snapshotCommit: "b".repeat(40), + task: "fixture", + model: "fixture-model", + maxIterations: 1, + timeoutMs: 1000, + protectedPaths: [], + candidate: { id: "candidate-a" }, + ...overrides, + } +} + +test("fleet runtime deadline follows the run ceiling", () => { + const started = { + runId: "run-a", + startedAt: "2026-09-20T00:00:00.000Z", + } as RsiRunRecord + assert.equal( + fleetRuntimeDeadlineMs(config({ maxRuntimeMs: 60_000 }), started), + Date.parse("2026-09-20T00:00:00.000Z") + 60_000, + ) + assert.equal(fleetRuntimeDeadlineMs(config(), started), undefined) +}) + +test("fleet evaluation result compacts bounded trials", () => { + const sha = "c".repeat(64) + const result = fleetEvaluationResult({ + evaluation: { + regression: trial("regression", sha), + typecheck: trial("typecheck", sha), + visible: [trial("visible", sha)], + }, + outputArtifact: { sha256: sha, bytes: 8 }, + }) + assert.equal(result.regression.command, "regression") + assert.equal(result.typecheck.command, "typecheck") + assert.equal(result.visible.length, 1) +}) + +test("fleet evaluation result rejects malformed envelopes", () => { + assert.throws(() => fleetEvaluationResult(null), /malformed/) + assert.throws(() => fleetEvaluationResult({ + evaluation: { regression: null, typecheck: null, visible: [] }, + outputArtifact: { sha256: "c".repeat(64), bytes: 8 }, + }), /malformed/) + assert.throws(() => fleetEvaluationResult({ + evaluation: { + regression: trial("regression", "c".repeat(64)), + typecheck: trial("typecheck", "c".repeat(64)), + visible: [], + }, + outputArtifact: { sha256: "short", bytes: 8 }, + }), /artifact reference/) +}) + +test("fleet harness execution matches the frozen payload", () => { + const tree = "a".repeat(64) + const commit = "b".repeat(40) + const payload = fleetPayload({ + workerHarness: { + runMode: "fixed-control", + mode: "fixed-control", + parentCommit: "a".repeat(40), + snapshotTreeDigest: tree, + runtime: workerRuntime, + executedCliPath: workerHarnessCliPath("fixed-control"), + }, + }) + const execution: WorkerHarnessExecution = { + mode: "fixed-control", + parentCommit: "a".repeat(40), + snapshotCommit: commit, + snapshotTreeDigest: tree, + runtime: workerRuntime, + executedCliPath: workerHarnessCliPath("fixed-control"), + } + assert.deepEqual( + fleetWorkerHarnessExecution({ workerHarnessExecution: execution }, payload), + execution, + ) + assert.equal(fleetWorkerHarnessExecution({}, fleetPayload()), undefined) + assert.throws(() => fleetWorkerHarnessExecution({ + workerHarnessExecution: { ...execution, mode: "self-hosted" }, + }, payload), /frozen worker harness/) + assert.throws(() => fleetWorkerHarnessExecution(null, payload), /omitted/) +}) + +test("candidate harness validation pins mode and runtime", () => { + const tree = "a".repeat(64) + const commit = "b".repeat(40) + const execution: WorkerHarnessExecution = { + mode: "fixed-control", + parentCommit: "a".repeat(40), + snapshotCommit: commit, + snapshotTreeDigest: tree, + runtime: workerRuntime, + executedCliPath: workerHarnessCliPath("fixed-control"), + } + const record = candidate() + validateCandidateWorkerHarnessExecution( + record, + { workerHarnessRuntime: workerRuntime } as RsiRunRecord, + config({ workerHarnessMode: "fixed-control" }), + execution, + ) + assert.deepEqual(record.workerHarnessExecution, execution) + assert.throws(() => validateCandidateWorkerHarnessExecution( + candidate(), + { workerHarnessRuntime: workerRuntime } as RsiRunRecord, + config({ workerHarnessMode: "fixed-control" }), + undefined, + ), /frozen worker harness execution/) + validateCandidateWorkerHarnessExecution(candidate(), {} as RsiRunRecord, config(), undefined) +}) + +test("fleet denial audit records only well-formed summaries", () => { + const payload = fleetPayload({ provider: "openrouter", model: "fixture-model" }) + const record = candidate() + recordFleetInferenceDenials(record, { + provider: "openrouter", + model: "fixture-model", + inferenceAudit: { + accounting: "incomplete", + jobId: "job-1", + denials: [{ statusCode: 429, count: 2, reason: "rate-limited" }], + }, + }, payload, "job-1") + assert.deepEqual(record.inferenceDenials, [{ statusCode: 429, count: 2, reason: "rate-limited" }]) + const ignored = candidate() + recordFleetInferenceDenials(ignored, { + provider: "openrouter", + model: "fixture-model", + inferenceAudit: { + accounting: "incomplete", + jobId: "job-1", + denials: [{ statusCode: 200, count: 1, reason: "ok" }], + }, + }, payload, "job-1") + assert.equal(ignored.inferenceDenials, undefined) + const ollama = candidate() + recordFleetInferenceDenials(ollama, {}, fleetPayload({ provider: "ollama" }), "job-1") + assert.equal(ollama.inferenceDenials, undefined) +}) diff --git a/src/rsi/__tests__/controller-integrity.test.ts b/src/rsi/__tests__/controller-integrity.test.ts new file mode 100644 index 0000000..2467cab --- /dev/null +++ b/src/rsi/__tests__/controller-integrity.test.ts @@ -0,0 +1,194 @@ +import assert from "node:assert/strict" +import test from "node:test" +import { + adversarialOutputRecords, + evaluationOutputRecords, + evaluatorArchiveStateDigest, + isTerminal, + mergeIntegrityOutputs, + proofCandidateSlot, + trialOutputRecords, + verifyArchivedIntegrityOutputs, + type IntegrityOutput, +} from "../controller.js" +import { evaluatorIntegrityDigest } from "../evaluation-integrity.js" +import type { + AdversarialReviewRecord, + CandidateRecord, + EvaluationSummary, + RsiRunRecord, + TrialResult, +} from "../types.js" + +function trial(overrides: Partial = {}): TrialResult { + return { + ok: true, + command: "npm test", + exitCode: 0, + durationMs: 3, + stdout: "pass", + stderr: "", + ...overrides, + } +} + +function evaluation(): EvaluationSummary { + return { + regression: trial({ command: "regression" }), + typecheck: trial({ command: "typecheck" }), + visible: [trial({ command: "visible-0" })], + hidden: [trial({ command: "hidden-0" })], + completed: true, + crashed: false, + protectedPathViolation: false, + changedFiles: [], + } +} + +function review(): AdversarialReviewRecord { + return { + candidateId: "candidate-a", + role: "adversary", + provider: "ollama", + model: "adversary-model", + promptVersion: "v1", + promptSha256: "p".repeat(64), + promptArtifactSha256: "a".repeat(64), + resultArtifactSha256: "b".repeat(64), + createdAt: "2026-09-20T00:00:00.000Z", + status: "passed", + findings: [], + testResults: [{ id: "t1", passed: true }], + penaltyPoints: 0, + } +} + +function candidate(overrides: Partial = {}): CandidateRecord { + return { + id: "candidate-a", + generation: 0, + parent: "baseline", + branch: "rsi/candidate-a", + worktree: "/tmp/worktree", + baseCommit: "a".repeat(40), + status: "accepted", + model: "fixture-model", + mutation: "fixture", + createdAt: "2026-09-20T00:00:00.000Z", + updatedAt: "2026-09-20T00:00:01.000Z", + commits: [], + changedFiles: [], + protectedPathViolations: [], + ...overrides, + } +} + +function run(overrides: Partial = {}): RsiRunRecord { + return { + runId: "rsi-run", + startedAt: "2026-09-20T00:00:00.000Z", + model: "fixture-model", + baseRef: "HEAD", + baseCommit: "a".repeat(40), + generations: 1, + candidates: [], + reports: [], + ...overrides, + } +} + +test("trial output records hash the command identity", () => { + const [record] = trialOutputRecords("candidate-a", "regression", trial()) + assert.equal(record?.candidateId, "candidate-a") + assert.equal(record?.taskId, "regression") + assert.equal(record?.outputSha256, evaluatorIntegrityDigest({ + command: "npm test", + exitCode: 0, + stdout: "pass", + stderr: "", + timedOut: false, + })) +}) + +test("trial output records carry artifact references", () => { + const sha = "c".repeat(64) + const [record] = trialOutputRecords("c", "t", trial({ + outputArtifactSha256: sha, + outputArtifactBytes: 12, + })) + assert.equal(record?.artifactSha256, sha) + assert.equal(record?.artifactBytes, 12) +}) + +test("evaluation output records index every trial slot", () => { + const records = evaluationOutputRecords("candidate-a", evaluation()) + const tasks = records.map((entry) => entry.taskId).sort() + assert.deepEqual(tasks, ["hidden-0", "regression", "typecheck", "visible-0"]) +}) + +test("adversarial output records skip missing references", () => { + const records = adversarialOutputRecords(review()) + const tasks = records.map((entry) => entry.taskId).sort() + assert.deepEqual(tasks, ["adversarial-prompt", "adversarial-result"]) + assert.ok(records.every((entry) => entry.candidateId === "candidate-a")) +}) + +test("merge integrity outputs upserts and sorts without touching identity", () => { + const record = { status: "unsealed", outputs: [], generations: [] } as + NonNullable + const first = trialOutputRecords("candidate-b", "regression", trial())[0]! + const second = trialOutputRecords("candidate-a", "regression", trial())[0]! + mergeIntegrityOutputs(record, [first, second]) + assert.deepEqual(record.outputs.map((entry) => entry.candidateId), [ + "candidate-a", + "candidate-b", + ]) + mergeIntegrityOutputs(record, [structuredClone(second)]) + assert.equal(record.outputs.length, 2) + assert.throws(() => mergeIntegrityOutputs(record, [{ + ...second, + outputSha256: "d".repeat(64), + }]), /identity changed/) +}) + +test("archived integrity verification accepts a matching seal", () => { + const evaluated = evaluation() + const candidateRecord = candidate({ result: trial() }) + const reviewRecord = review() + const signed: IntegrityOutput[] = [ + ...evaluationOutputRecords("baseline", evaluated), + ...trialOutputRecords(candidateRecord.id, "regression", candidateRecord.result!), + ...adversarialOutputRecords(reviewRecord), + ] + verifyArchivedIntegrityOutputs(run({ + baselineEvaluation: evaluated, + candidates: [candidateRecord], + adversarialReviews: [reviewRecord], + }), signed) +}) + +test("archived integrity verification rejects drift and orphan seals", () => { + const evaluated = evaluation() + const signed = evaluationOutputRecords("baseline", evaluated) + const drifted = structuredClone(signed) + drifted[0]!.outputSha256 = "d".repeat(64) + assert.throws(() => verifyArchivedIntegrityOutputs(run({ + baselineEvaluation: evaluated, + }), drifted), /differs from signed identity/) + assert.throws(() => verifyArchivedIntegrityOutputs(run(), signed), /missing/) +}) + +test("evaluator archive digest is stable and content-sensitive", () => { + const before = evaluatorArchiveStateDigest(run({ candidates: [candidate()] })) + assert.equal(before, evaluatorArchiveStateDigest(run({ candidates: [candidate()] }))) + const after = evaluatorArchiveStateDigest(run({ + candidates: [candidate({ status: "rejected" })], + })) + assert.notEqual(before, after) +}) + +test("terminal states and proof slots format as expected", () => { + assert.equal(isTerminal(candidate({ status: "accepted" })), true) + assert.equal(isTerminal(candidate({ status: "mutating" })), false) + assert.equal(proofCandidateSlot(2, 3), "g2-candidate-3") +}) diff --git a/src/rsi/controller-candidates.ts b/src/rsi/controller-candidates.ts new file mode 100644 index 0000000..be53268 --- /dev/null +++ b/src/rsi/controller-candidates.ts @@ -0,0 +1,80 @@ +import { execFileSync } from "node:child_process" +import { randomUUID } from "node:crypto" +import { baseModelCandidate } from "./models.js" +import { mutationSnapshotProtectedPathViolations } from "./sandbox.js" +import type { + CandidateRecord, + ExperimentJob, + RsiArchive, + RsiConfig, + RsiRunRecord, +} from "./types.js" + +export function isTerminal(candidate: CandidateRecord): boolean { + return candidate.status === "accepted" || candidate.status === "rejected" || candidate.status === "failed" +} + +export class CandidateLineageError extends Error {} + +export function validateCandidateParent(candidate: CandidateRecord, run: RsiRunRecord, config: RsiConfig, protocol: NonNullable): void { + if (candidate.parent === "baseline") { + if (candidate.baseCommit !== run.baseCommit || candidate.parentCommit !== run.baseCommit) throw new CandidateLineageError("baseline child does not use the frozen run base commit") + return + } + const parent = run.candidates.find((entry) => entry.id === candidate.parent) + if (!parent || parent.status !== "accepted" || parent.developmentProof?.accepted !== true || parent.developmentProof.runId !== run.runId || parent.developmentProof.protocolDigest !== protocol.protocolDigest || !parent.fitness || Object.values(parent.fitness.hardGates).some((passed) => passed === false)) { + throw new CandidateLineageError(`candidate parent ${candidate.parent} is missing, rejected, from another run/protocol, or fails hard gates`) + } + const parentCommit = parent.commits[0] + if (!parentCommit || candidate.baseCommit !== parentCommit || candidate.parentCommit !== parentCommit) throw new CandidateLineageError(`candidate parent ${candidate.parent} commit does not match its mutation base`) + try { + execFileSync("git", ["merge-base", "--is-ancestor", run.baseCommit, parentCommit], { cwd: config.repoRoot, stdio: "ignore" }) + } catch { throw new CandidateLineageError(`candidate parent ${candidate.parent} is outside the frozen run base lineage`) } + const inheritedPaths = execFileSync("git", ["diff", "--name-only", "-z", "--no-renames", run.baseCommit, parentCommit], { cwd: config.repoRoot, encoding: "buffer" }) as Buffer + const violations = mutationSnapshotProtectedPathViolations(inheritedPaths.toString("utf8").split("\0").filter(Boolean), config.protectedPaths) + if (violations.length > 0) throw new CandidateLineageError(`candidate parent ${candidate.parent} inherits protected path changes: ${violations.join(", ")}`) +} + +export function replaceJob(run: RsiRunRecord, job: ExperimentJob): void { + run.jobs ??= [] + const index = run.jobs.findIndex((entry) => entry.id === job.id) + if (index >= 0) run.jobs[index] = job + else run.jobs.push(job) +} + +export function combinedArchive(archive: RsiArchive, run: RsiRunRecord): RsiArchive { + const currentIds = new Set(run.candidates.map((candidate) => candidate.id)) + return { ...archive, candidates: [...archive.candidates.filter((candidate) => !currentIds.has(candidate.id)), ...run.candidates] } +} + +export function validateDeterministicWorkerHarnessRunId(config: RsiConfig): void { + if (config.workerHarnessRunId) { + const suffix = config.workerHarnessMode === "fixed-control" ? "fixed" : config.workerHarnessMode === "self-hosted" ? "self" : undefined + if (!config.workerHarnessPairId || !suffix || config.workerHarnessRunId !== `${config.workerHarnessPairId}-${suffix}` || !/^rsi-proof-[a-f0-9]{20}-(fixed|self)$/.test(config.workerHarnessRunId)) throw new Error("deterministic worker harness run ID must match its paired campaign and arm") + } + } + +export function initialRun(config: RsiConfig, baseCommit: string, now: string): RsiRunRecord { + const modelCandidateId = config.modelCandidateId ?? "model-base" + validateDeterministicWorkerHarnessRunId(config) + return { + runId: config.workerHarnessRunId ?? `rsi-${now.replace(/[^0-9]/g, "").slice(0, 14)}-${randomUUID().slice(0, 8)}`, + startedAt: now, + model: config.model, + baseRef: config.baseRef, + baseCommit, + generations: config.generations, + parentSelectionPolicy: config.parentSelectionPolicy ?? "champion-specialist-novelty", + selectedCandidates: [], + parentChoices: {}, + modelCandidates: [baseModelCandidate(config.model, now, modelCandidateId)], + combinations: [], + jobs: [], + trajectoryRefs: [], + curriculumTasks: [], + adversarialReviews: [], + candidates: [], + reports: [], + confirmationEvidence: "unmeasured", + } +} diff --git a/src/rsi/controller-fleet.ts b/src/rsi/controller-fleet.ts new file mode 100644 index 0000000..763307a --- /dev/null +++ b/src/rsi/controller-fleet.ts @@ -0,0 +1,187 @@ +import { setTimeout as delay } from "node:timers/promises" +import type { EvaluatorRuntimeIdentity } from "./evaluator-runtime.js" +import { + openRouterAllocatedEnvironment, + openRouterBrokerLimitsFromEnvironment, + openRouterProfileForModel, + RSI_DEFAULT_OPENROUTER_PROFILE_ID, +} from "./inference-broker.js" +import type { + OpenRouterDenialSummary, + OpenRouterEndpointPriceSnapshot, +} from "./inference-broker.js" +import type { + FleetJob, + FleetJobPayload, + PostgresRsiJobQueue, +} from "./postgres-queue.js" +import type { + CandidateRecord, + RsiConfig, + RsiRunRecord, + TrialResult, + WorkerHarnessExecution, +} from "./types.js" +import { + effectiveWorkerHarnessMode, + validWorkerHarnessRuntimeIdentity, + workerHarnessCliPath, +} from "./worker-harness.js" + +export async function waitForFleetJob(queue: PostgresRsiJobQueue, jobId: string, timeoutMs: number, runtimeDeadlineMs?: number): Promise { + const deadline = Math.min(Date.now() + timeoutMs, runtimeDeadlineMs ?? Number.POSITIVE_INFINITY) + for (;;) { + const job = await queue.getJob(jobId) + if (!job) throw new Error(`fleet job ${jobId} disappeared`) + if (job.status === "completed" || job.status === "failed" || job.status === "cancelled") return job + if (Date.now() >= deadline) break + await delay(500) + } + const reason = runtimeDeadlineMs !== undefined && Date.now() >= runtimeDeadlineMs ? "RSI run runtime ceiling" : "coordinator wait timeout" + await queue.cancel(jobId, reason) + const teardownDeadline = Date.now() + 60_000 + while (Date.now() < teardownDeadline) { + const job = await queue.getJob(jobId) + if (!job) throw new Error(`fleet job ${jobId} disappeared during cancellation`) + if (job.status === "completed" || job.status === "failed" || job.status === "cancelled") return job + await delay(200) + } + throw new Error(`fleet job ${jobId} exceeded ${reason}; cancellation is pending worker teardown and its lease remains capacity-reserved`) +} + +export function fleetRuntimeDeadlineMs(config: RsiConfig, run: RsiRunRecord): number | undefined { + return config.maxRuntimeMs === undefined ? undefined : Date.parse(run.startedAt) + config.maxRuntimeMs +} + +export function fleetPayload(config: RsiConfig, candidate: CandidateRecord, snapshot: { content: Buffer; snapshotCommit: string; snapshotTreeDigest?: string }, options: { campaignId?: string; boundedInference?: boolean; includeWorkerHarness?: boolean; proofCampaign?: FleetJobPayload["proofCampaign"]; remainingCampaignMs?: number; evaluatorRuntime?: Pick } = {}): FleetJobPayload { + const provider = config.roles?.worker?.provider === "openrouter" ? "openrouter" : "ollama" + const openRouterProfile = provider === "openrouter" ? openRouterProfileForModel(candidate.model, config.roles?.worker?.openRouterProfile) : undefined + const inferenceLimits = provider === "openrouter" && options.boundedInference + ? openRouterBrokerLimitsFromEnvironment(openRouterAllocatedEnvironment(process.env, "mutation"), options.campaignId ?? "", { + maxCalls: config.maxIterations, + ...(config.maxTokens ? { maxOutputTokensPerCall: config.maxTokens } : {}), + maxDurationMs: Math.max(1, Math.min(config.commandTimeoutMs, options.remainingCampaignMs ?? config.commandTimeoutMs)), + }) + : undefined + const effectiveMode = effectiveWorkerHarnessMode(config.workerHarnessMode ?? "fixed-control", candidate.generation) + const runtime = config.workerHarnessRuntime + if (config.workerHarnessMode && options.includeWorkerHarness && (!runtime || !validWorkerHarnessRuntimeIdentity(runtime) || !snapshot.snapshotTreeDigest)) throw new Error("worker harness job lacks its frozen runtime and sanitized snapshot identity") + return { + schemaVersion: 1, + snapshotSha256: "", // replaced with the verified artifact digest before enqueue + snapshotBytes: snapshot.content.byteLength, + snapshotCommit: snapshot.snapshotCommit, + task: config.mutationTask, + model: candidate.model, + provider, + ...(openRouterProfile && openRouterProfile.id !== RSI_DEFAULT_OPENROUTER_PROFILE_ID ? { openRouterProfile: openRouterProfile.id } : {}), + ...(options.proofCampaign ? { proofCampaign: options.proofCampaign } : {}), + ...(inferenceLimits ? { inferenceLimits } : {}), + ...(options.evaluatorRuntime ? { evaluatorRuntime: { ...options.evaluatorRuntime } } : {}), + maxIterations: config.maxIterations, + timeoutMs: config.commandTimeoutMs, + protectedPaths: config.protectedPaths, + candidate: { + id: candidate.id, generation: candidate.generation, parent: candidate.parent, + ...(config.workerHarnessMode && options.includeWorkerHarness ? { baseCommit: candidate.baseCommit } : {}), + mutationKind: candidate.mutationKind, hypothesis: candidate.hypothesis, + modelCandidateId: candidate.modelCandidateId, computePolicy: config.computePolicy, + }, + regressionCommand: config.regressionCommand, + evalCommands: config.evalCommands, + ...(config.workerHarnessMode && options.includeWorkerHarness ? { workerHarness: { + runMode: config.workerHarnessMode, + mode: effectiveMode, + ...(config.workerHarnessPairId ? { pairId: config.workerHarnessPairId } : {}), + parentCommit: candidate.baseCommit, + snapshotTreeDigest: snapshot.snapshotTreeDigest!, + runtime: runtime!, + executedCliPath: workerHarnessCliPath(effectiveMode), + } } : {}), + } +} + +export function recordFleetInferenceUsage(candidate: CandidateRecord, resultValue: unknown, payload: FleetJobPayload, jobId: string): void { + candidate.modelProvider = `openshell-${payload.provider ?? "ollama"}` + if ((payload.provider ?? "ollama") !== "openrouter") return + if (!resultValue || typeof resultValue !== "object" || !payload.inferenceLimits) throw new Error("OpenRouter fleet mutation result is missing its broker identity") + const result = resultValue as { provider?: unknown; model?: unknown; inferenceReceipt?: Record } + const receipt = result.inferenceReceipt + const profile = openRouterProfileForModel(payload.model, payload.openRouterProfile) + const profileIdMatches = profile.id === RSI_DEFAULT_OPENROUTER_PROFILE_ID ? receipt?.profileId === undefined || receipt.profileId === profile.id : receipt?.profileId === profile.id + const price = receipt?.endpointPrice as OpenRouterEndpointPriceSnapshot | undefined + const priceMatches = profile.id === RSI_DEFAULT_OPENROUTER_PROFILE_ID || Boolean(price && price.model === profile.model && price.provider === profile.endpointProvider && price.endpointTag === profile.endpointTag + && price.quantization === profile.quantization && profile.requiredParameters.every((parameter) => price.supportedParameters.includes(parameter)) + && Number.isFinite(Date.parse(price.observedAt)) && /^[a-f0-9]{64}$/.test(price.responseSha256) + && Number.isFinite(price.inputPricePerMillionUsd) && price.inputPricePerMillionUsd > 0 && price.inputPricePerMillionUsd <= payload.inferenceLimits.maxInputPricePerMillionUsd + && Number.isFinite(price.outputPricePerMillionUsd) && price.outputPricePerMillionUsd > 0 && price.outputPricePerMillionUsd <= payload.inferenceLimits.maxOutputPricePerMillionUsd) + if (result.provider !== "openrouter" || result.model !== payload.model || !receipt || receipt.provider !== "openrouter" || !profileIdMatches || !priceMatches || receipt.endpointProvider !== profile.endpointProvider || receipt.jobId !== jobId || receipt.campaignId !== payload.inferenceLimits.campaignId || receipt.model !== payload.model || receipt.accounting !== "supervisor-enforced" || receipt.qualified !== true || !Array.isArray(receipt.returnedModels) || receipt.returnedModels.length !== 1 || !profile.returnedModels.includes(String(receipt.returnedModels[0])) || !Array.isArray(receipt.returnedProviders) || receipt.returnedProviders.length !== 1 || receipt.returnedProviders[0] !== profile.endpointProvider) { + throw new Error("OpenRouter fleet mutation result lacks a qualified receipt bound to the admitted provider, model, job and campaign") + } + const values = [receipt.calls, receipt.promptTokens, receipt.completionTokens, receipt.totalTokens, receipt.costUsd] + if (!values.every((value) => typeof value === "number" && Number.isFinite(value) && value >= 0) || !Number.isSafeInteger(receipt.calls) || !Number.isSafeInteger(receipt.promptTokens) || !Number.isSafeInteger(receipt.completionTokens) || !Number.isSafeInteger(receipt.totalTokens)) throw new Error("OpenRouter fleet mutation receipt contains malformed resource totals") + if (!Array.isArray(receipt.denials) || !receipt.denials.every((entry) => entry && typeof entry === "object" && Number.isSafeInteger((entry as OpenRouterDenialSummary).statusCode) && (entry as OpenRouterDenialSummary).statusCode >= 400 && (entry as OpenRouterDenialSummary).statusCode <= 599 && Number.isSafeInteger((entry as OpenRouterDenialSummary).count) && (entry as OpenRouterDenialSummary).count >= 1 && /^[a-z-]+$/.test((entry as OpenRouterDenialSummary).reason))) throw new Error("OpenRouter fleet mutation receipt contains malformed denial summaries") + candidate.inferenceUsage = { + provider: "openrouter", endpointProvider: profile.endpointProvider, ...(profile.id !== RSI_DEFAULT_OPENROUTER_PROFILE_ID ? { profileId: profile.id } : {}), model: payload.model, returnedModels: receipt.returnedModels as string[], returnedProviders: receipt.returnedProviders as string[], calls: receipt.calls as number, + promptTokens: receipt.promptTokens as number, completionTokens: receipt.completionTokens as number, totalTokens: receipt.totalTokens as number, + costUsd: receipt.costUsd as number, accounting: "supervisor-enforced", qualified: true, jobId, + campaignId: payload.inferenceLimits.campaignId, + denials: receipt.denials as OpenRouterDenialSummary[], + } +} + +export function recordFleetInferenceDenials(candidate: CandidateRecord, resultValue: unknown, payload: FleetJobPayload, jobId: string): void { + if (payload.provider !== "openrouter" || !resultValue || typeof resultValue !== "object") return + const result = resultValue as { provider?: unknown; model?: unknown; inferenceAudit?: { accounting?: unknown; jobId?: unknown; denials?: unknown } } + const audit = result.inferenceAudit + if (result.provider !== "openrouter" || result.model !== payload.model || audit?.accounting !== "incomplete" || audit.jobId !== jobId || !Array.isArray(audit.denials)) return + const denials = audit.denials as OpenRouterDenialSummary[] + if (!denials.every((entry) => entry && typeof entry === "object" && Number.isSafeInteger(entry.statusCode) && entry.statusCode >= 400 && entry.statusCode <= 599 && Number.isSafeInteger(entry.count) && entry.count >= 1 && /^[a-z-]+$/.test(entry.reason))) return + candidate.inferenceDenials = structuredClone(denials) +} + +export function fleetEvaluationResult(value: unknown): { regression: TrialResult; typecheck: TrialResult; visible: TrialResult[] } { + if (!value || typeof value !== "object") throw new Error("fleet evaluation result is malformed") + const result = value as { + evaluation?: { regression?: unknown; typecheck?: unknown; visible?: unknown[] } + outputArtifact?: { sha256?: unknown; bytes?: unknown } + } + const output = result.outputArtifact + if (!output || typeof output.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(output.sha256) || !Number.isSafeInteger(output.bytes) || Number(output.bytes) < 1) { + throw new Error("fleet evaluation output artifact reference is missing or malformed") + } + const compactTrial = (trial: unknown): TrialResult => { + if (!trial || typeof trial !== "object") throw new Error("fleet evaluation trial is malformed") + const item = trial as TrialResult + if (typeof item.ok !== "boolean" || (item.exitCode !== null && !Number.isSafeInteger(item.exitCode)) || !Number.isFinite(item.durationMs) || item.durationMs < 0 || typeof item.command !== "string" || item.command.length > 512 || typeof item.stdout !== "string" || item.stdout.length > 2048 || typeof item.stderr !== "string" || item.stderr.length > 2048 || item.outputArtifactSha256 !== output.sha256 || item.outputArtifactBytes !== output.bytes) { + throw new Error("fleet evaluation trial exceeds bounded result fields or lacks its output artifact reference") + } + return item + } + const evaluation = result.evaluation + if (!evaluation || !Array.isArray(evaluation.visible)) throw new Error("fleet evaluation trial list is malformed") + return { regression: compactTrial(evaluation.regression), typecheck: compactTrial(evaluation.typecheck), visible: evaluation.visible.map(compactTrial) } +} + +export function fleetWorkerHarnessExecution(value: unknown, expected: FleetJobPayload): WorkerHarnessExecution | undefined { + if (!expected.workerHarness) return undefined + if (!value || typeof value !== "object") throw new Error("fleet mutation result omitted its worker harness identity") + const execution = (value as { workerHarnessExecution?: WorkerHarnessExecution }).workerHarnessExecution + const plan = expected.workerHarness + if (!execution || execution.mode !== plan.mode || execution.parentCommit !== plan.parentCommit + || execution.snapshotTreeDigest !== plan.snapshotTreeDigest || execution.executedCliPath !== plan.executedCliPath + || JSON.stringify(execution.runtime) !== JSON.stringify(plan.runtime) + || !/^[a-f0-9]{40}$/.test(execution.snapshotCommit)) throw new Error("fleet mutation result does not match its frozen worker harness identity") + return execution +} + +export function validateCandidateWorkerHarnessExecution(candidate: CandidateRecord, run: RsiRunRecord, config: RsiConfig, execution?: WorkerHarnessExecution): void { + if (!config.workerHarnessMode) return + const expectedMode = effectiveWorkerHarnessMode(config.workerHarnessMode, candidate.generation) + if (!execution || execution.mode !== expectedMode || execution.parentCommit !== candidate.baseCommit + || execution.executedCliPath !== workerHarnessCliPath(expectedMode) + || !/^[a-f0-9]{64}$/.test(execution.snapshotTreeDigest) + || !/^[a-f0-9]{40}$/.test(execution.snapshotCommit) + || JSON.stringify(execution.runtime) !== JSON.stringify(run.workerHarnessRuntime)) throw new Error("mutation result lacks the frozen worker harness execution identity") + candidate.workerHarnessExecution = execution +} diff --git a/src/rsi/controller-integrity-seal.ts b/src/rsi/controller-integrity-seal.ts new file mode 100644 index 0000000..8ad7265 --- /dev/null +++ b/src/rsi/controller-integrity-seal.ts @@ -0,0 +1,188 @@ +import * as path from "node:path" +import { + canonicalJson, + evaluatorBaseTreeIdentity, + evaluatorGenerationDecisionDigest, + evaluatorIntegrityDigest, + evaluatorSourceIdentity, + hasEvaluatorIntegrityManifests, + readEvaluatorIntegrityManifest, + signEvaluatorIntegrity, + verifyEvaluatorIntegrity, + writeEvaluatorIntegrityManifest, +} from "./evaluation-integrity.js" +import type { + EvaluatorIntegrityPayload, + EvaluatorIntegrityTrust, +} from "./evaluation-integrity.js" +import type { EvaluatorRuntimeIdentity } from "./evaluator-runtime.js" +import { freezeDevelopmentProofProtocol } from "./development-proof.js" +import { compareFitness } from "./fitness.js" +import { paretoFront } from "./selection.js" +import { workerHarnessConfigDigest } from "./worker-harness.js" +import { + evaluatorArchiveStateDigest, + verifyArchivedIntegrityOutputs, +} from "./controller-integrity.js" +import type { RsiConfig, RsiRunRecord } from "./types.js" + +export async function persistEvaluatorIntegrity( + config: RsiConfig, + run: RsiRunRecord, + trust: EvaluatorIntegrityTrust | undefined, + input: Record, +): Promise { + const record = run.evaluatorIntegrity + if (!record) throw new Error("evaluator integrity record was not initialized") + if (!trust) { + record.status = "unsealed" + return + } + const payload: EvaluatorIntegrityPayload = { + schemaVersion: 1, + runId: run.runId, + input, + inputSha256: evaluatorIntegrityDigest(input), + archiveStateSha256: evaluatorArchiveStateDigest(run), + outputs: record.outputs, + generations: record.generations, + ...(record.selectedCandidateId ? { selectedCandidateId: record.selectedCandidateId } : {}), + } + const saved = await writeEvaluatorIntegrityManifest(config.archiveDir, run.runId, signEvaluatorIntegrity(trust, payload)) + record.status = "sealed" + record.keyId = trust.keyId + record.inputSha256 = payload.inputSha256 + record.archiveStateSha256 = payload.archiveStateSha256 + record.manifestPath = saved.path + record.manifestSha256 = saved.sha256 +} + +export async function verifyResumedEvaluatorIntegrity( + config: RsiConfig, + run: RsiRunRecord, + trust: EvaluatorIntegrityTrust | undefined, + input: Record, + attested: boolean, +): Promise { + const existing = run.evaluatorIntegrity + const hasSealArtifacts = await hasEvaluatorIntegrityManifests(config.archiveDir, run.runId) + if ((!existing || existing.status !== "sealed") && hasSealArtifacts) throw new Error("evaluator integrity seal artifacts exist but archive metadata is missing or downgraded; refusing unsealed resume") + if (!existing) { + if (attested) throw new Error("attested RSI resume requires a signed evaluator integrity manifest; this historical run is unsealed") + if (trust) throw new Error("cannot retroactively seal a historical RSI run; start a new run with evaluator integrity trust configured") + return + } + const currentInputSha256 = evaluatorIntegrityDigest(input) + if (existing.status === "sealed") { + if (!trust) throw new Error("sealed RSI run cannot resume without its independently pinned evaluator integrity trust") + if (!existing.manifestSha256 || existing.manifestPath !== path.posix.join("evaluator-integrity", `${run.runId}.${existing.manifestSha256}.json`)) throw new Error("evaluator integrity manifest pointer is malformed") + const saved = await readEvaluatorIntegrityManifest(config.archiveDir, run.runId, existing.manifestSha256) + if (saved.sha256 !== existing.manifestSha256) throw new Error("evaluator integrity manifest bytes changed since the run was checkpointed") + const envelope = verifyEvaluatorIntegrity(trust, saved.envelope) + const payload = envelope.payload as Partial + if (payload.schemaVersion !== 1 || payload.runId !== run.runId || payload.inputSha256 !== currentInputSha256 || canonicalJson(payload.input) !== canonicalJson(input)) throw new Error("evaluator source, base tree, configuration, commands, or inputs changed since the signed RSI snapshot") + if (payload.archiveStateSha256 !== evaluatorArchiveStateDigest(run) || existing.archiveStateSha256 !== payload.archiveStateSha256) throw new Error("archived evaluator state changed since its last signed checkpoint") + if (canonicalJson(payload.outputs) !== canonicalJson(existing.outputs) || canonicalJson(payload.generations) !== canonicalJson(existing.generations) || payload.selectedCandidateId !== existing.selectedCandidateId) throw new Error("signed evaluator outputs or selection differ from the RSI archive checkpoint") + if (existing.keyId !== trust.keyId || existing.inputSha256 !== currentInputSha256) throw new Error("evaluator integrity trust or manifest identity differs from the frozen run identity") + verifyArchivedIntegrityOutputs(run, existing.outputs) + for (const generation of existing.generations) { + const generationCandidates = run.candidates.filter((candidate) => candidate.generation === generation.generation) + const accepted = generationCandidates.filter((candidate) => candidate.status === "accepted") + const expectedSelectedIds = paretoFront(accepted).map((candidate) => candidate.id) + const expectedSelected = [...accepted].sort((left, right) => compareFitness(right.fitness, left.fitness))[0]?.id + const reviews = (run.adversarialReviews ?? []).filter((review) => generationCandidates.some((candidate) => candidate.id === review.candidateId)) + if (canonicalJson(generation.selectedCandidateIds) !== canonicalJson(expectedSelectedIds) || generation.selectedCandidateId !== expectedSelected || + generation.decisionsSha256 !== evaluatorGenerationDecisionDigest(generationCandidates, reviews, expectedSelected)) { + throw new Error(`archived evaluator decisions differ from the signed selection for generation ${generation.generation}`) + } + } + const latestGeneration = existing.generations.at(-1) + if ((latestGeneration && canonicalJson(latestGeneration.selectedCandidateIds) !== canonicalJson(run.selectedCandidates ?? [])) || existing.selectedCandidateId !== run.selected) throw new Error("selected candidates differ from the signed evaluator selection") + return + } + if (trust) throw new Error("cannot retroactively seal an unsealed RSI run; start a new run with evaluator integrity trust configured") + if (attested) throw new Error("attested RSI resume requires an independently signed evaluator integrity manifest") +} + +export async function buildEvaluatorIntegrityInput(args: { + config: RsiConfig + run: RsiRunRecord + packageRoot: string + roles: RsiConfig["roles"] + workerModel: string + workerProvider: string + workerOpenRouterProfile?: string + proofProtocol: ReturnType + benchmarkManifestSha256: string + evaluatorRuntime?: EvaluatorRuntimeIdentity + hiddenVerifierIdentity?: RsiRunRecord["hiddenVerifierIdentity"] + syntheticRuntimeIdentity?: boolean +}): Promise> { + const source = await evaluatorSourceIdentity(args.packageRoot) + const baseTreeSha256 = evaluatorBaseTreeIdentity(args.config.repoRoot, args.run.baseCommit) + const config = args.config + return { + schemaVersion: 1, + runId: args.run.runId, + baseCommit: args.run.baseCommit, + baseTreeSha256, + evaluatorSource: source, + imageIdentity: { + requestedImage: args.evaluatorRuntime ? `headlesscode-openshell-rsi@${args.evaluatorRuntime.imageDigest}` : null, + pinnedImageDigest: args.evaluatorRuntime?.imageDigest ?? null, + identitySource: args.evaluatorRuntime?.identitySource ?? "not-executed-dry-run", + workerHarnessRuntime: args.run.workerHarnessRuntime ?? null, + gatewayAlias: process.env.OPENSHELL_GATEWAY ?? null, + }, + evaluatorIsolationPolicy: args.evaluatorRuntime ? { source: args.evaluatorRuntime.policySource, sha256: args.evaluatorRuntime.policySha256 } : null, + storageAndWorkspace: { + repository: path.resolve(config.repoRoot), + archive: path.resolve(config.archiveDir), + worktrees: path.resolve(config.worktreeDir), + trajectoryOutput: path.resolve(config.trajectoryDir ?? path.join(config.archiveDir, "trajectories")), + curriculumOutput: path.resolve(config.curriculumDir ?? path.join(config.archiveDir, "curriculum")), + }, + commands: { + regression: config.regressionCommand, + visible: [...config.evalCommands], + hidden: [...config.hiddenEvalCommands], + }, + effectiveArguments: { + baseRef: config.baseRef, + model: args.workerModel, + provider: args.workerProvider, + openRouterProfile: args.workerOpenRouterProfile ?? null, + roles: args.roles ?? null, + population: config.population, + generations: args.run.generations, + maxConcurrent: config.maxConcurrent, + maxIterations: config.maxIterations, + maxTokens: config.maxTokens ?? null, + commandTimeoutMs: config.commandTimeoutMs, + computePolicy: config.computePolicy ?? "single", + maxTrajectories: config.maxTrajectories ?? null, + maxTotalIterations: config.maxTotalIterations ?? null, + maxRuntimeMs: config.maxRuntimeMs ?? null, + seed: config.seed, + mutationTask: config.mutationTask, + mutationKind: config.mutationKind ?? "corrective", + hypothesis: config.hypothesis ?? null, + protectedPaths: [...config.protectedPaths].sort(), + parentSelectionPolicy: config.parentSelectionPolicy ?? "champion-specialist-novelty", + developmentProofRule: config.developmentProofRule ?? null, + developmentProofProtocolSha256: args.proofProtocol.protocolDigest, + workerHarness: { + mode: args.run.workerHarnessMode ?? "fixed-control", + pairId: args.run.workerHarnessPairId ?? null, + configDigest: args.run.workerHarnessConfigDigest ?? null, + plannedGenerations: args.run.workerHarnessPlannedGenerations ?? null, + phase: config.workerHarnessPhase ?? null, + engineeringOnly: config.workerHarnessEngineeringOnly ?? false, + }, + }, + inputArtifacts: [ + { id: "development-benchmark-manifest", sha256: args.benchmarkManifestSha256 }, + ...(args.hiddenVerifierIdentity ? [{ id: "hidden-verifier-suite", sha256: args.hiddenVerifierIdentity.suiteSha256 }, { id: "hidden-verifier-public-key", sha256: args.hiddenVerifierIdentity.publicKeySha256 }, { id: "hidden-verifier-image", sha256: args.hiddenVerifierIdentity.imageDigest }] : []), + ], + } +} diff --git a/src/rsi/controller-integrity.ts b/src/rsi/controller-integrity.ts new file mode 100644 index 0000000..73f770f --- /dev/null +++ b/src/rsi/controller-integrity.ts @@ -0,0 +1,102 @@ +import { createHash } from "node:crypto" +import { + canonicalJson, + evaluatorIntegrityDigest, +} from "./evaluation-integrity.js" +import type { PostgresRsiJobQueue } from "./postgres-queue.js" +import type { + AdversarialReviewRecord, + EvaluationSummary, + RsiRunRecord, + TrialResult, +} from "./types.js" + +export type IntegrityOutput = NonNullable["outputs"][number] + +export function trialOutputRecords(candidateId: string, taskId: string, trial: TrialResult): IntegrityOutput[] { + return [{ + candidateId, + taskId, + outputSha256: evaluatorIntegrityDigest({ command: trial.command, exitCode: trial.exitCode, stdout: trial.stdout, stderr: trial.stderr, timedOut: trial.timedOut ?? false }), + ...(trial.outputArtifactSha256 ? { artifactSha256: trial.outputArtifactSha256, artifactBytes: trial.outputArtifactBytes } : {}), + }] +} + +export function evaluationOutputRecords(candidateId: string, evaluation: EvaluationSummary): IntegrityOutput[] { + const trials: Array<{ taskId: string; trial: TrialResult }> = [ + { taskId: "regression", trial: evaluation.regression }, + ...(evaluation.typecheck ? [{ taskId: "typecheck", trial: evaluation.typecheck }] : []), + ...evaluation.visible.map((trial, index) => ({ taskId: `visible-${index}`, trial })), + ...evaluation.hidden.map((trial, index) => ({ taskId: `hidden-${index}`, trial })), + ] + return trials.flatMap(({ taskId, trial }) => trialOutputRecords(candidateId, taskId, trial)) +} + +export function adversarialOutputRecords(review: AdversarialReviewRecord): IntegrityOutput[] { + const refs: Array<[string, string | undefined]> = [ + ["prompt", review.promptArtifactSha256], ["result", review.resultArtifactSha256], + ["error", review.errorArtifactSha256], ["tests", review.testArtifactSha256], + ["snapshot", review.snapshotArtifactSha256], + ...review.testResults.map((test, index): [string, string | undefined] => [`test-output-${index}`, test.outputArtifactSha256]), + ] + return refs.filter((entry): entry is [string, string] => !!entry[1]).map(([taskId, artifactSha256]) => ({ + candidateId: review.candidateId, + taskId: `adversarial-${taskId}`, + outputSha256: artifactSha256, + artifactSha256, + })) +} + +export function verifyArchivedIntegrityOutputs(run: RsiRunRecord, signedOutputs: IntegrityOutput[]): void { + const expected: IntegrityOutput[] = [] + if (run.baselineEvaluation) expected.push(...evaluationOutputRecords("baseline", run.baselineEvaluation)) + for (const candidate of run.candidates) { + if (candidate.result) expected.push(...trialOutputRecords(candidate.id, "regression", candidate.result)) + } + for (const review of run.adversarialReviews ?? []) expected.push(...adversarialOutputRecords(review)) + const signed = new Map(signedOutputs.map((entry) => [`${entry.candidateId}\0${entry.taskId}`, entry])) + for (const output of expected) { + const recorded = signed.get(`${output.candidateId}\0${output.taskId}`) + if (!recorded || canonicalJson(recorded) !== canonicalJson(output)) throw new Error(`archived evaluator output differs from signed identity: ${output.candidateId}/${output.taskId}`) + } + for (const output of signedOutputs) { + if (output.candidateId === "baseline" && !run.baselineEvaluation) throw new Error("signed baseline evaluator outputs are missing from the RSI archive") + if (output.candidateId !== "baseline" && output.taskId === "regression" && !run.candidates.some((candidate) => candidate.id === output.candidateId && candidate.result)) { + throw new Error(`signed regression output is missing from the RSI archive: ${output.candidateId}`) + } + } +} + +export function mergeIntegrityOutputs(record: NonNullable, outputs: IntegrityOutput[]): void { + const byTask = new Map(record.outputs.map((entry) => [`${entry.candidateId}\0${entry.taskId}`, entry])) + for (const output of outputs) { + const key = `${output.candidateId}\0${output.taskId}` + const existing = byTask.get(key) + if (existing && canonicalJson(existing) !== canonicalJson(output)) throw new Error(`evaluator output identity changed for ${output.candidateId}/${output.taskId}`) + byTask.set(key, output) + } + record.outputs = [...byTask.values()].sort((left, right) => left.candidateId.localeCompare(right.candidateId) || left.taskId.localeCompare(right.taskId)) +} + +export function evaluatorArchiveStateDigest(run: RsiRunRecord): string { + const state = JSON.parse(JSON.stringify({ + baselineEvaluation: run.baselineEvaluation ?? null, + baselineFitness: run.baselineFitness ?? null, + candidates: run.candidates, + adversarialReviews: run.adversarialReviews ?? [], + selected: run.selected ?? null, + selectedCandidates: run.selectedCandidates ?? [], + })) as Record + return evaluatorIntegrityDigest(state) +} + +export async function verifyIntegrityArtifacts(queue: PostgresRsiJobQueue | undefined, outputs: IntegrityOutput[]): Promise { + const references = outputs.filter((entry) => entry.artifactSha256) + if (references.length > 0 && !queue) throw new Error("evaluator integrity artifact verification requires the configured RSI artifact store") + for (const reference of references) { + const bytes = await queue!.getArtifact(reference.artifactSha256!) + if (createHash("sha256").update(bytes).digest("hex") !== reference.artifactSha256 || reference.artifactBytes !== undefined && bytes.byteLength !== reference.artifactBytes) { + throw new Error(`evaluator output artifact differs from its signed identity: ${reference.candidateId}/${reference.taskId}`) + } + } +} diff --git a/src/rsi/controller-proof-archive.ts b/src/rsi/controller-proof-archive.ts new file mode 100644 index 0000000..c5a6682 --- /dev/null +++ b/src/rsi/controller-proof-archive.ts @@ -0,0 +1,137 @@ +import { createHash } from "node:crypto" +import * as fs from "node:fs/promises" +import * as path from "node:path" +import { appendRun } from "./archive.js" +import { isTerminal } from "./controller-candidates.js" +import { compareFitness } from "./fitness.js" +import { + openRouterProfileForModel, + RSI_DEFAULT_OPENROUTER_PROFILE_ID, +} from "./inference-broker.js" +import { + fleetJobMatches, + type FleetJob, + type FleetJobIdentity, + type PostgresRsiJobQueue, +} from "./postgres-queue.js" +import { + proofCampaignManifestDigest, + type ProofCampaignArmLease, + type ProofCampaignManifest, +} from "./proof-campaign.js" +import { formatRunReport } from "./reports.js" +import type { RsiConfig, RsiRunRecord } from "./types.js" + +export async function reconcileProofCampaignJob(queue: PostgresRsiJobQueue, existing: FleetJob | undefined, expected: FleetJobIdentity, lease: ProofCampaignArmLease): Promise { + if (!existing || fleetJobMatches(existing, expected)) return existing + if (existing.status === "queued") { + const rebound = await queue.rebindQueuedProofJob(existing.jobId, expected, lease) + if (fleetJobMatches(rebound, expected)) return rebound + } + throw new Error("existing proof campaign job is not the same completed result or a safely rebindable queued job") +} + +export async function rebuildFinishedProofCampaignArchive(args: { + run: RsiRunRecord + config: RsiConfig + snapshot: Record + manifest: ProofCampaignManifest + armId: string + now: () => string +}): Promise { + const campaign = args.snapshot.campaign as Record | undefined + const arms = args.snapshot.arms as Array> | undefined + const arm = arms?.find((entry) => entry.arm_id === args.armId) + const expectedDigest = proofCampaignManifestDigest(args.manifest) + if (!campaign || !arm || arm.state !== "finished" || String(campaign.manifest_sha256).trim() !== expectedDigest) throw new Error("finished proof campaign snapshot does not match the frozen manifest and arm") + if (arm.run_id !== args.run.runId || args.run.candidates.some((candidate) => !isTerminal(candidate))) throw new Error("finished proof campaign arm cannot rebuild an incomplete or foreign run archive") + if (args.manifest.phaseMode === "engineering-only") { + const events = args.snapshot.events as Array> | undefined + if (!events?.some((event) => event.arm_id === args.armId && event.event_type === "engineering-development-finished") || args.run.proofConfirmation || args.run.confirmationAttempts?.length) throw new Error("engineering-only recovery must have a terminal development event and no confirmation evidence") + } else await verifyFinishedConfirmationCheckpoint(args.run, args.config.archiveDir, args.snapshot, args.manifest, args.armId) + args.run.proofCampaign = { manifest: args.manifest, manifestSha256: expectedDigest, campaignId: args.manifest.campaignId, armId: args.armId, epoch: Number(arm.epoch), state: "finished", ledgerSnapshot: args.snapshot } + const selections = (args.snapshot.events as Array>).filter((event) => event.arm_id === args.armId && event.event_type === "generation-selected").sort((left, right) => Number(left.sequence) - Number(right.sequence)) + const finalSelection = selections.at(-1)?.payload as { generation?: number; selectedCandidateIds?: string[] } | undefined + if (finalSelection && Number.isSafeInteger(finalSelection.generation)) { + args.run.selectedCandidates = (finalSelection.selectedCandidateIds ?? []).map((slot) => { + const generationCandidates = args.run.candidates.filter((candidate) => candidate.generation === finalSelection.generation) + const index = Number(/candidate-(\d+)$/.exec(slot)?.[1] ?? -1) + return generationCandidates[index]?.id ?? "" + }).filter(Boolean) + args.run.selected ??= [...args.run.candidates.filter((candidate) => candidate.status === "accepted")].sort((a, b) => compareFitness(b.fitness, a.fitness))[0]?.id + } + args.run.finishedAt ??= args.now() + const recoveredReportPath = `${args.config.archiveDir}/${args.run.runId}.md` + await fs.mkdir(args.config.archiveDir, { recursive: true }) + await fs.writeFile(recoveredReportPath, formatRunReport(args.run, args.config), "utf8") + args.run.reports = [...new Set([...args.run.reports, recoveredReportPath])] + await appendRun(args.config.archiveDir, args.run) + return args.run +} + +export async function verifyFinishedConfirmationCheckpoint(run: RsiRunRecord, archiveDir: string, snapshot: Record, manifest: ProofCampaignManifest, armId: string): Promise { + const events = snapshot.events as Array> | undefined + const attempts = snapshot.attempts as Array> | undefined + const selection = events?.filter((event) => event.arm_id === armId && event.event_type === "final-harness-selected") + const payload = selection?.length === 1 ? selection[0]!.payload as { generation?: number; candidateId?: string; commit?: string } : undefined + const saved = run.proofConfirmation + if (!payload || !saved || saved.selectedHarness.slotId !== payload.candidateId || saved.selectedHarness.generation !== payload.generation || saved.selectedHarness.commit !== payload.commit || saved.rootHarness.commit !== manifest.rootCommit || run.confirmationEvidence !== "complete") throw new Error("finished campaign recovery requires a matching checkpointed final-harness selection and confirmation result") + const cells = manifest.schedule.filter((cell) => cell.armId === armId && cell.phase === "confirmation" && (cell.candidateId === "root-control" || (cell.candidateId === payload.candidateId && cell.generation === payload.generation))) + const byCandidateKind = (candidateId: string, kind: string) => cells.filter((cell) => cell.candidateId === candidateId && cell.attemptKind === kind) + const selectedRows = byCandidateKind(String(payload.candidateId), payload.candidateId === "root-control" ? "fixed-control" : "confirmation") + const rootRows = byCandidateKind("root-control", "fixed-control") + const rowFor = (cell: typeof cells[number]) => attempts?.filter((row) => row.arm_id === armId && Number(row.generation) === cell.generation && row.candidate_id === cell.candidateId && row.task_id === cell.taskId && row.seed === cell.seed && row.attempt_kind === cell.attemptKind && row.status === "completed") ?? [] + const expectedSelectedRows = selectedRows.flatMap((cell) => rowFor(cell)) + const expectedRootRows = rootRows.flatMap((cell) => rowFor(cell)) + const orderedIds = (rows: Array>) => rows.map((row) => String(row.broker_job_id)).sort() + if (expectedSelectedRows.length !== selectedRows.length || expectedRootRows.length !== rootRows.length || JSON.stringify(orderedIds(expectedSelectedRows)) !== JSON.stringify([...saved.selectedHarness.attemptIds].sort()) || JSON.stringify(orderedIds(expectedRootRows)) !== JSON.stringify([...saved.rootHarness.attemptIds].sort())) throw new Error("finished campaign recovery checkpoint does not name the completed selected and root confirmation cells") + const artifacts = new Map((run.confirmationAttempts ?? []).map((attempt) => [attempt.attemptId, attempt])) + for (const row of [...expectedSelectedRows, ...expectedRootRows]) { + const attemptId = String(row.broker_job_id) + const attempt = artifacts.get(attemptId) + const expectedProfile = attempt ? openRouterProfileForModel(attempt.model.id, run.proofCampaign?.manifest.model.profileId) : undefined + const profileIdMatches = expectedProfile?.id === RSI_DEFAULT_OPENROUTER_PROFILE_ID ? attempt?.inferenceReceipt?.profileId === undefined || attempt?.inferenceReceipt?.profileId === expectedProfile.id : attempt?.inferenceReceipt?.profileId === expectedProfile?.id + if (!attempt || attempt.validity !== "valid" || !attempt.proofQualified || attempt.resourceAccounting !== "supervisor-enforced" || !attempt.inferenceReceipt?.qualified || !profileIdMatches || attempt.inferenceReceipt.endpointProvider !== expectedProfile?.endpointProvider || attempt.inferenceReceipt.returnedProviders?.join(",") !== expectedProfile?.endpointProvider || createHash("sha256").update(JSON.stringify(attempt)).digest("hex") !== String(row.result_sha256).trim()) throw new Error(`finished campaign recovery attempt ${attemptId} does not match its verified durable result`) + const storedRefs = row.artifact_refs as Record | undefined + if (!storedRefs || Object.entries(attempt.artifactRefs).some(([name, digest]) => storedRefs[name]?.sha256 !== digest)) throw new Error(`finished campaign recovery attempt ${attemptId} has different artifact references`) + const candidateId = String(row.candidate_id) + const kind = String(row.attempt_kind) + const outputRoot = path.join(archiveDir, "confirmation", manifest.campaignId, armId, candidateId, kind, "harness", attempt.harnessCommit) + for (const digest of Object.values(attempt.artifactRefs)) { + if (!/^[a-f0-9]{64}$/.test(digest)) throw new Error(`finished campaign recovery attempt ${attemptId} has a malformed artifact digest`) + const artifact = path.join(outputRoot, "artifacts", "sha256", digest.slice(0, 2), digest) + const stat = await fs.lstat(artifact) + if (!stat.isFile() || stat.isSymbolicLink() || createHash("sha256").update(await fs.readFile(artifact)).digest("hex") !== digest) throw new Error(`finished campaign recovery artifact ${digest} is missing or changed`) + } + } +} + +/** Rebuild only the durable development-phase checkpoint after its DB commit. */ +export async function rebuildEvaluatedProofCampaignArchive(args: { + run: RsiRunRecord + config: RsiConfig + snapshot: Record + manifest: ProofCampaignManifest + armId: string + now: () => string +}): Promise { + const campaign = args.snapshot.campaign as Record | undefined + const arms = args.snapshot.arms as Array> | undefined + const events = args.snapshot.events as Array> | undefined + const arm = arms?.find((entry) => entry.arm_id === args.armId) + const expectedDigest = proofCampaignManifestDigest(args.manifest) + const finishEvent = events?.some((event) => event.arm_id === args.armId && event.event_type === "development-phase-finished" && (event.payload as { remainingPhase?: unknown })?.remainingPhase === "confirmation") + if (!campaign || !arm || !finishEvent || arm.phase !== "confirmation" || !["evaluated", "selected", "rejected"].includes(String(arm.state)) || String(campaign.manifest_sha256).trim() !== expectedDigest) throw new Error("evaluated proof campaign snapshot does not match the frozen manifest and development completion") + if (arm.run_id !== args.run.runId || args.run.candidates.some((candidate) => !isTerminal(candidate))) throw new Error("evaluated proof campaign arm cannot rebuild from an incomplete or foreign run checkpoint") + const expectedMode = args.armId === args.manifest.armAssignment.fixedControl ? "fixed-control" : "self-hosted" + if (args.run.workerHarnessPairId !== args.manifest.campaignId || args.run.workerHarnessMode !== expectedMode) throw new Error("evaluated proof campaign checkpoint has a foreign paired-run identity") + if (arm.lease_expires_at && new Date(String(arm.lease_expires_at)).getTime() > Date.now()) throw new Error("evaluated proof campaign arm has an active coordinator; refusing read-only recovery during confirmation work") + args.run.proofCampaign = { manifest: args.manifest, manifestSha256: expectedDigest, campaignId: args.manifest.campaignId, armId: args.armId, epoch: Number(arm.epoch), state: "evaluated", ledgerSnapshot: args.snapshot } + args.run.finishedAt ??= args.now() + const reportPath = `${args.config.archiveDir}/${args.run.runId}.md` + await fs.mkdir(args.config.archiveDir, { recursive: true }) + await fs.writeFile(reportPath, formatRunReport(args.run, args.config), "utf8") + args.run.reports = [...new Set([...args.run.reports, reportPath])] + await appendRun(args.config.archiveDir, args.run) + return args.run +} diff --git a/src/rsi/controller-proof-manifest.ts b/src/rsi/controller-proof-manifest.ts new file mode 100644 index 0000000..462c75d --- /dev/null +++ b/src/rsi/controller-proof-manifest.ts @@ -0,0 +1,160 @@ +import { createHash } from "node:crypto" +import type { BenchmarkManifest } from "./benchmark/types.js" +import { + openRouterAllocatedEnvironment, + openRouterBrokerLimitsFromEnvironment, + openRouterCampaignBudgetAllocation, +} from "./inference-broker.js" +import type { ProofCampaignManifest } from "./proof-campaign.js" +import { workerHarnessConfigDigest } from "./worker-harness.js" +import type { RsiConfig, RsiRunRecord } from "./types.js" + +export function proofCandidateSlot(generation: number, index: number): string { return `g${generation}-candidate-${index}` } +export const RSI_CONFIRMATION_RETENTION_TASK_IDS = ["redirect-escape", "qa-baseline-counts", "strict-tool-schema", "dashboard-git-owner-api"] as const + +export function proofCampaignTaskWallBoundMs(config: RsiConfig, plannedGenerations: number, developmentTasks: BenchmarkManifest["tasks"], confirmationTasks: BenchmarkManifest["tasks"], includeConfirmation = true): number { + const slotsPerArm = plannedGenerations * config.population + const candidateCountBothArms = 2 * slotsPerArm + const candidateTaskMs = config.commandTimeoutMs * (3 + config.evalCommands.length) + + 3 * developmentTasks.reduce((sum, task) => sum + task.ceilings.timeoutMs, 0) + const confirmationTaskMs = includeConfirmation ? 4 * confirmationTasks.reduce((sum, task) => sum + task.ceilings.timeoutMs, 0) : 0 + const bound = candidateCountBothArms * candidateTaskMs + confirmationTaskMs + if (!Number.isSafeInteger(bound) || bound <= 0) throw new Error("proof campaign task-time ceiling exceeds safe integer accounting") + return bound +} + +export interface ProofCampaignSpendReservationMicros { + mutation: number + benchmark: number + total: number +} + +export function proofCampaignSpendReservationMicros(args: { + mutationJobs: number + mutationMaxJobSpendUsd: number + mutationAllocationUsd: number + benchmarkJobs: number + benchmarkMaxJobSpendUsd: number + benchmarkAllocationUsd: number + totalAllocationUsd: number + benchmarkShortfallMessage: string +}): ProofCampaignSpendReservationMicros { + const floorMicros = (usd: number): number => { + const micros = Math.floor(usd * 1_000_000) + if (!Number.isSafeInteger(micros) || micros < 0) throw new Error("proof campaign spend allocation exceeds safe microUSD accounting") + return micros + } + const reservationMicros = (jobs: number, perJobUsd: number): number => { + const perJobMicros = Math.ceil(perJobUsd * 1_000_000) + const totalMicros = jobs * perJobMicros + if (!Number.isSafeInteger(jobs) || jobs < 0 || !Number.isSafeInteger(perJobMicros) || perJobMicros < 0 || !Number.isSafeInteger(totalMicros)) throw new Error("proof campaign job reservations exceed safe microUSD accounting") + return totalMicros + } + const mutation = reservationMicros(args.mutationJobs, args.mutationMaxJobSpendUsd) + const benchmark = reservationMicros(args.benchmarkJobs, args.benchmarkMaxJobSpendUsd) + if (floorMicros(args.mutationAllocationUsd) < mutation) throw new Error("mutation broker campaign allocation is below the sum of frozen mutation job reservations") + if (floorMicros(args.benchmarkAllocationUsd) < benchmark) throw new Error(args.benchmarkShortfallMessage) + const total = mutation + benchmark + if (!Number.isSafeInteger(total)) throw new Error("proof campaign job reservations exceed safe microUSD accounting") + if (floorMicros(args.totalAllocationUsd) < total) throw new Error("proof campaign total allocation is below the sum of frozen job reservations") + return { mutation, benchmark, total } +} + +export function createProofCampaignManifest(args: { + config: RsiConfig + campaignId: string + baseCommit: string + model: string + provider: string + openRouterProfileId?: string + runtime: NonNullable + developmentManifest: BenchmarkManifest + developmentManifestDigest: string + plannedGenerations: number + proofProtocol: NonNullable +}): ProofCampaignManifest { + const engineeringOnly = args.config.workerHarnessEngineeringOnly === true + if (engineeringOnly && args.config.workerHarnessPhase === "confirmation") throw new Error("engineering-only proof runs cannot enter confirmation") + if (args.provider !== "openshell-openrouter") throw new Error("restart-safe proof campaigns require supervisor-accounted OpenRouter inference") + if (args.config.computePolicy === "adaptive-independent") throw new Error("restart-safe proof campaigns require every frozen generation to run; adaptive early-stop is not admitted") + const benchmarkTasks = args.developmentManifest.tasks.filter((task) => task.split === "development") + const confirmationTasks = args.developmentManifest.tasks.filter((task) => task.split === "confirmation") + if (!benchmarkTasks.length || (!engineeringOnly && !confirmationTasks.length)) throw new Error("proof campaign requires nonempty development tasks and, unless engineering-only, confirmation tasks") + if (!engineeringOnly && RSI_CONFIRMATION_RETENTION_TASK_IDS.some((taskId) => !confirmationTasks.some((task) => task.id === taskId))) throw new Error("frozen benchmark manifest is missing a preregistered confirmation retention task") + const taskDigest = (tasks: typeof args.developmentManifest.tasks) => createHash("sha256").update(JSON.stringify(tasks)).digest("hex") + const mutationCampaignId = proofBrokerCampaignId(args.campaignId, "mutation") + const benchmarkCampaignId = proofBrokerCampaignId(args.campaignId, "benchmark") + const mutationLimits = openRouterBrokerLimitsFromEnvironment(openRouterAllocatedEnvironment(process.env, "mutation"), mutationCampaignId, { maxCalls: args.config.maxIterations, ...(args.config.maxTokens ? { maxOutputTokensPerCall: args.config.maxTokens } : {}), maxDurationMs: args.config.commandTimeoutMs }) + const benchmarkEnv = openRouterAllocatedEnvironment(process.env, "benchmark") + const benchmarkLimits = openRouterBrokerLimitsFromEnvironment(benchmarkEnv, benchmarkCampaignId, { maxDurationMs: Math.min(...benchmarkTasks.map((task) => task.ceilings.timeoutMs)) }) + const confirmationLimits = openRouterBrokerLimitsFromEnvironment(benchmarkEnv, benchmarkCampaignId, { maxDurationMs: Math.min(...confirmationTasks.map((task) => task.ceilings.timeoutMs)) }) + const jobCostCeiling = (limits: typeof mutationLimits, maxCalls: number, maxOutputTokensPerCall: number): number => { + const perCallTokens = Math.min(limits.maxTotalTokens, limits.maxInputTokensPerCall + maxOutputTokensPerCall) + const jobTokens = Math.min(limits.maxTotalTokens, maxCalls * (limits.maxInputTokensPerCall + maxOutputTokensPerCall)) + const unitPrice = Math.max(limits.maxInputPricePerMillionUsd, limits.maxOutputPricePerMillionUsd) + const micros = (tokens: number) => Math.ceil(tokens * unitPrice) + if (micros(perCallTokens) > limits.maxCallSpendUsd * 1_000_000) throw new Error("OpenRouter per-call spend cap is below the price ceiling for the admitted token ceiling") + return micros(jobTokens) / 1_000_000 + } + if (jobCostCeiling(mutationLimits, mutationLimits.maxCalls, mutationLimits.maxOutputTokensPerCall) > mutationLimits.maxJobSpendUsd) throw new Error("OpenRouter mutation job spend cap is below its admitted token and price ceilings") + for (const task of benchmarkTasks) if (jobCostCeiling(benchmarkLimits, task.ceilings.maxCalls, task.ceilings.maxOutputTokensPerCall) > benchmarkLimits.maxJobSpendUsd) throw new Error(`OpenRouter benchmark job spend cap is below the frozen token and price ceiling for ${task.id}`) + if (!engineeringOnly) for (const task of confirmationTasks) if (jobCostCeiling(confirmationLimits, task.ceilings.maxCalls, task.ceilings.maxOutputTokensPerCall) > confirmationLimits.maxJobSpendUsd) throw new Error(`OpenRouter confirmation job spend cap is below the frozen token and price ceiling for ${task.id}`) + const candidateCounts = Array.from({ length: args.plannedGenerations }, (_, generation) => args.config.computePolicy === "adaptive-independent" && generation > 0 ? 1 : args.config.population) + const schedule: ProofCampaignManifest["schedule"] = [] + for (const armId of ["fixed-control", "self-hosted"]) for (let generation = 0; generation < args.plannedGenerations; generation++) { + for (let index = 0; index < candidateCounts[generation]!; index++) { + const slot = proofCandidateSlot(generation, index) + schedule.push({ armId, generation, candidateId: slot, taskId: "mutation", seed: args.config.seed, attemptKind: "mutation", phase: "development" }) + schedule.push({ armId, generation, candidateId: slot, taskId: "regression", seed: args.config.seed, attemptKind: "verification", phase: "development" }) + schedule.push({ armId, generation, candidateId: slot, taskId: "typecheck", seed: args.config.seed, attemptKind: "verification", phase: "development" }) + for (const [taskIndex] of args.config.evalCommands.entries()) schedule.push({ armId, generation, candidateId: slot, taskId: `visible-${taskIndex}`, seed: args.config.seed, attemptKind: "verification", phase: "development" }) + for (const role of ["child", "parent", "base"]) for (const task of benchmarkTasks) schedule.push({ armId, generation, candidateId: `${slot}:${role}`, taskId: task.id, seed: String(task.seed), attemptKind: "development-proof", phase: "development" }) + } + } + const finalGeneration = args.plannedGenerations - 1 + if (!engineeringOnly) for (const armId of ["fixed-control", "self-hosted"]) { + for (let generation = 0; generation < args.plannedGenerations; generation++) { + for (let index = 0; index < candidateCounts[generation]!; index++) for (const task of confirmationTasks) schedule.push({ armId, generation, candidateId: proofCandidateSlot(generation, index), taskId: task.id, seed: String(task.seed), attemptKind: "confirmation", phase: "confirmation" }) + } + for (const task of confirmationTasks) schedule.push({ armId, generation: finalGeneration, candidateId: "root-control", taskId: task.id, seed: String(task.seed), attemptKind: "fixed-control", phase: "confirmation" }) + } + const developmentCallsPerProofRole = benchmarkTasks.reduce((sum, task) => sum + task.ceilings.maxCalls, 0) + const confirmationCalls = engineeringOnly ? 0 : 4 * confirmationTasks.reduce((sum, task) => sum + task.ceilings.maxCalls, 0) // selected final and root-control, for both arms + const rootCalls = 2 * candidateCounts.reduce((sum, count) => sum + count * (mutationLimits.maxCalls + 3 * developmentCallsPerProofRole), 0) + confirmationCalls + const developmentTokensPerProofRole = benchmarkTasks.reduce((sum, task) => sum + Math.min(benchmarkLimits.maxTotalTokens, task.ceilings.maxCalls * (benchmarkLimits.maxInputTokensPerCall + task.ceilings.maxOutputTokensPerCall)), 0) + const confirmationTokensPerHarness = confirmationTasks.reduce((sum, task) => sum + Math.min(confirmationLimits.maxTotalTokens, task.ceilings.maxCalls * (confirmationLimits.maxInputTokensPerCall + task.ceilings.maxOutputTokensPerCall)), 0) + const mutationTokens = Math.min(mutationLimits.maxTotalTokens, mutationLimits.maxCalls * (mutationLimits.maxInputTokensPerCall + mutationLimits.maxOutputTokensPerCall)) + const rootTokens = 2 * candidateCounts.reduce((sum, count) => sum + count * (mutationTokens + 3 * developmentTokensPerProofRole), 0) + (engineeringOnly ? 0 : 4 * confirmationTokensPerHarness) + if (!Number.isSafeInteger(rootCalls) || !Number.isSafeInteger(rootTokens)) throw new Error("proof campaign root call or token ceiling exceeds safe integer accounting") + const campaignBudget = openRouterCampaignBudgetAllocation(process.env) + const slotsPerArm = candidateCounts.reduce((sum, count) => sum + count, 0) + const mutationBudgetLimits = openRouterBrokerLimitsFromEnvironment(openRouterAllocatedEnvironment(process.env, "mutation"), mutationCampaignId) + const benchmarkJobCount = 2 * slotsPerArm * 3 * benchmarkTasks.length + (engineeringOnly ? 0 : 4 * confirmationTasks.length) + const reservations = proofCampaignSpendReservationMicros({ + mutationJobs: 2 * slotsPerArm, mutationMaxJobSpendUsd: mutationLimits.maxJobSpendUsd, mutationAllocationUsd: mutationBudgetLimits.maxCampaignSpendUsd, + benchmarkJobs: benchmarkJobCount, benchmarkMaxJobSpendUsd: benchmarkLimits.maxJobSpendUsd, benchmarkAllocationUsd: benchmarkLimits.maxCampaignSpendUsd, + totalAllocationUsd: campaignBudget.totalUsd, + benchmarkShortfallMessage: engineeringOnly ? "benchmark broker campaign allocation is below development proof job reservations" : "benchmark broker campaign allocation is below development plus selected-final/root confirmation job reservations", + }) + const taskWallBoundMs = proofCampaignTaskWallBoundMs(args.config, args.plannedGenerations, benchmarkTasks, confirmationTasks, !engineeringOnly) + const maxRuntimeMs = args.config.maxRuntimeMs ?? 60 * 60_000 + if (maxRuntimeMs < taskWallBoundMs) throw new Error(`proof campaign runtime ceiling ${maxRuntimeMs}ms is below the summed frozen task-time ceiling ${taskWallBoundMs}ms`) + return { + schemaVersion: 1, phaseMode: engineeringOnly ? "engineering-only" : "paired-confirmation", campaignId: args.campaignId, rootCommit: args.baseCommit, + benchmarkDigests: { manifest: args.developmentManifestDigest, development: taskDigest(benchmarkTasks), confirmation: taskDigest(engineeringOnly ? [] : confirmationTasks) }, + armAssignment: { fixedControl: "fixed-control", selfHosted: "self-hosted" }, + model: { provider: args.provider, id: args.model, ...(args.openRouterProfileId ? { profileId: args.openRouterProfileId } : {}), settings: { sampling: args.proofProtocol.model.sampling, roles: args.config.roles ?? {}, maxIterations: args.config.maxIterations, maxTokens: args.config.maxTokens ?? null } }, + seed: args.config.seed, taskOrder: ["mutation", "regression", "typecheck", ...args.config.evalCommands.map((_, index) => `visible-${index}`), ...benchmarkTasks.map((task) => task.id), ...(engineeringOnly ? [] : confirmationTasks.map((task) => task.id))], schedule, + budgets: { + attempt: { maxCalls: Math.max(mutationLimits.maxCalls, benchmarkLimits.maxCalls, ...(!engineeringOnly ? [confirmationLimits.maxCalls] : [])), maxTokens: Math.max(mutationLimits.maxTotalTokens, benchmarkLimits.maxTotalTokens, ...(!engineeringOnly ? [confirmationLimits.maxTotalTokens] : [])), maxSpendUsd: Math.max(mutationLimits.maxJobSpendUsd, benchmarkLimits.maxJobSpendUsd, ...(!engineeringOnly ? [confirmationLimits.maxJobSpendUsd] : [])), maxDurationMs: Math.max(args.config.commandTimeoutMs, ...benchmarkTasks.map((task) => task.ceilings.timeoutMs), ...(!engineeringOnly ? confirmationTasks.map((task) => task.ceilings.timeoutMs) : [])) }, + root: { maxCalls: rootCalls, maxTokens: rootTokens, maxSpendUsd: Math.min(campaignBudget.totalUsd, reservations.total / 1_000_000), maxRuntimeMs }, + }, + acceptanceRule: { ...args.proofProtocol.rule, confirmationRetentionTaskIds: engineeringOnly ? [] : [...RSI_CONFIRMATION_RETENTION_TASK_IDS], minimumSelfHostedCampaigns: 2, requiredSuccessiveTransitions: 2, clusterConfidence: 0.9 }, runtime: { imageDigest: args.runtime.imageDigest, harnessCommit: args.runtime.harnessCommit, dependencyLockDigest: args.runtime.dependencyLockDigest }, + analysisVersion: args.proofProtocol.schemaVersion.toString(), settings: { phaseMode: engineeringOnly ? "engineering-only" : "paired-confirmation", configDigest: workerHarnessConfigDigest(args.config, { baseCommit: args.baseCommit, model: args.model, provider: args.provider, proofProtocolDigest: args.proofProtocol.protocolDigest }), effectiveConfig: Object.fromEntries(Object.entries(args.config).filter(([key]) => !["workerHarnessMode", "workerHarnessPhase", "workerHarnessEngineeringOnly", "workerHarnessPairId", "workerHarnessRunId", "resumeRunId", "candidateIdNamespace", "workerHarnessParentCommit", "workerHarnessSnapshotTreeDigest", "hiddenSuitePath", "hiddenPublicKeyPath", "hiddenPrivateKeyPath", "hiddenNonceStorePath", "evaluatorSigningKeyPath", "evaluatorTrustedPublicKeyPath", "proofBenchmarkFixtures"].includes(key))), candidateCounts, taskWallBoundMs, evaluationCells: ["regression", "typecheck", ...args.config.evalCommands.map((_, index) => `visible-${index}`)], developmentTaskIdentities: benchmarkTasks.map((task) => ({ id: task.id, family: task.family, seed: task.seed, startDigest: task.startDigest, verifierDigest: task.verifierDigest, ceilings: task.ceilings })), confirmationTaskIdentities: engineeringOnly ? [] : confirmationTasks.map((task) => ({ id: task.id, family: task.family, seed: task.seed, startDigest: task.startDigest, verifierDigest: task.verifierDigest, ceilings: task.ceilings })), inferenceCeilings: { maxInputTokensPerCall: benchmarkLimits.maxInputTokensPerCall, maxOutputTokensPerCall: benchmarkLimits.maxOutputTokensPerCall, maxCallsPerAttempt: benchmarkLimits.maxCalls, maxTotalTokens: benchmarkLimits.maxTotalTokens, maxCallSpendUsd: benchmarkLimits.maxCallSpendUsd, maxJobSpendUsd: benchmarkLimits.maxJobSpendUsd, maxInputPricePerMillionUsd: benchmarkLimits.maxInputPricePerMillionUsd, maxOutputPricePerMillionUsd: benchmarkLimits.maxOutputPricePerMillionUsd }, inferenceCampaignIds: { mutation: mutationCampaignId, benchmark: benchmarkCampaignId }, inferenceAllocationsUsd: { mutation: mutationLimits.maxCampaignSpendUsd, benchmark: benchmarkLimits.maxCampaignSpendUsd } }, + } +} + +export function proofBrokerCampaignId(rootCampaignId: string, allocation: "mutation" | "benchmark"): string { + if (!rootCampaignId.trim()) throw new Error("proof campaign ID is required to derive broker campaign identity") + return `${rootCampaignId}:${allocation}` +} diff --git a/src/rsi/controller.ts b/src/rsi/controller.ts index 8d91aa3..c972b68 100644 --- a/src/rsi/controller.ts +++ b/src/rsi/controller.ts @@ -54,747 +54,95 @@ function log(hooks: RsiHooks, line: string): void { (hooks.log ?? ((message) => process.stdout.write(`${message}\n`)))(line) } -type IntegrityOutput = NonNullable["outputs"][number] - -function trialOutputRecords(candidateId: string, taskId: string, trial: TrialResult): IntegrityOutput[] { - return [{ - candidateId, - taskId, - outputSha256: evaluatorIntegrityDigest({ command: trial.command, exitCode: trial.exitCode, stdout: trial.stdout, stderr: trial.stderr, timedOut: trial.timedOut ?? false }), - ...(trial.outputArtifactSha256 ? { artifactSha256: trial.outputArtifactSha256, artifactBytes: trial.outputArtifactBytes } : {}), - }] -} - -function evaluationOutputRecords(candidateId: string, evaluation: EvaluationSummary): IntegrityOutput[] { - const trials: Array<{ taskId: string; trial: TrialResult }> = [ - { taskId: "regression", trial: evaluation.regression }, - ...(evaluation.typecheck ? [{ taskId: "typecheck", trial: evaluation.typecheck }] : []), - ...evaluation.visible.map((trial, index) => ({ taskId: `visible-${index}`, trial })), - ...evaluation.hidden.map((trial, index) => ({ taskId: `hidden-${index}`, trial })), - ] - return trials.flatMap(({ taskId, trial }) => trialOutputRecords(candidateId, taskId, trial)) -} - -function adversarialOutputRecords(review: AdversarialReviewRecord): IntegrityOutput[] { - const refs: Array<[string, string | undefined]> = [ - ["prompt", review.promptArtifactSha256], ["result", review.resultArtifactSha256], - ["error", review.errorArtifactSha256], ["tests", review.testArtifactSha256], - ["snapshot", review.snapshotArtifactSha256], - ...review.testResults.map((test, index): [string, string | undefined] => [`test-output-${index}`, test.outputArtifactSha256]), - ] - return refs.filter((entry): entry is [string, string] => !!entry[1]).map(([taskId, artifactSha256]) => ({ - candidateId: review.candidateId, - taskId: `adversarial-${taskId}`, - outputSha256: artifactSha256, - artifactSha256, - })) -} - -function verifyArchivedIntegrityOutputs(run: RsiRunRecord, signedOutputs: IntegrityOutput[]): void { - const expected: IntegrityOutput[] = [] - if (run.baselineEvaluation) expected.push(...evaluationOutputRecords("baseline", run.baselineEvaluation)) - for (const candidate of run.candidates) { - if (candidate.result) expected.push(...trialOutputRecords(candidate.id, "regression", candidate.result)) - } - for (const review of run.adversarialReviews ?? []) expected.push(...adversarialOutputRecords(review)) - const signed = new Map(signedOutputs.map((entry) => [`${entry.candidateId}\0${entry.taskId}`, entry])) - for (const output of expected) { - const recorded = signed.get(`${output.candidateId}\0${output.taskId}`) - if (!recorded || canonicalJson(recorded) !== canonicalJson(output)) throw new Error(`archived evaluator output differs from signed identity: ${output.candidateId}/${output.taskId}`) - } - for (const output of signedOutputs) { - if (output.candidateId === "baseline" && !run.baselineEvaluation) throw new Error("signed baseline evaluator outputs are missing from the RSI archive") - if (output.candidateId !== "baseline" && output.taskId === "regression" && !run.candidates.some((candidate) => candidate.id === output.candidateId && candidate.result)) { - throw new Error(`signed regression output is missing from the RSI archive: ${output.candidateId}`) - } - } -} - -function mergeIntegrityOutputs(record: NonNullable, outputs: IntegrityOutput[]): void { - const byTask = new Map(record.outputs.map((entry) => [`${entry.candidateId}\0${entry.taskId}`, entry])) - for (const output of outputs) { - const key = `${output.candidateId}\0${output.taskId}` - const existing = byTask.get(key) - if (existing && canonicalJson(existing) !== canonicalJson(output)) throw new Error(`evaluator output identity changed for ${output.candidateId}/${output.taskId}`) - byTask.set(key, output) - } - record.outputs = [...byTask.values()].sort((left, right) => left.candidateId.localeCompare(right.candidateId) || left.taskId.localeCompare(right.taskId)) -} - -function evaluatorArchiveStateDigest(run: RsiRunRecord): string { - const state = JSON.parse(JSON.stringify({ - baselineEvaluation: run.baselineEvaluation ?? null, - baselineFitness: run.baselineFitness ?? null, - candidates: run.candidates, - adversarialReviews: run.adversarialReviews ?? [], - selected: run.selected ?? null, - selectedCandidates: run.selectedCandidates ?? [], - })) as Record - return evaluatorIntegrityDigest(state) -} - -async function verifyIntegrityArtifacts(queue: PostgresRsiJobQueue | undefined, outputs: IntegrityOutput[]): Promise { - const references = outputs.filter((entry) => entry.artifactSha256) - if (references.length > 0 && !queue) throw new Error("evaluator integrity artifact verification requires the configured RSI artifact store") - for (const reference of references) { - const bytes = await queue!.getArtifact(reference.artifactSha256!) - if (createHash("sha256").update(bytes).digest("hex") !== reference.artifactSha256 || reference.artifactBytes !== undefined && bytes.byteLength !== reference.artifactBytes) { - throw new Error(`evaluator output artifact differs from its signed identity: ${reference.candidateId}/${reference.taskId}`) - } - } -} - -async function persistEvaluatorIntegrity( - config: RsiConfig, - run: RsiRunRecord, - trust: EvaluatorIntegrityTrust | undefined, - input: Record, -): Promise { - const record = run.evaluatorIntegrity - if (!record) throw new Error("evaluator integrity record was not initialized") - if (!trust) { - record.status = "unsealed" - return - } - const payload: EvaluatorIntegrityPayload = { - schemaVersion: 1, - runId: run.runId, - input, - inputSha256: evaluatorIntegrityDigest(input), - archiveStateSha256: evaluatorArchiveStateDigest(run), - outputs: record.outputs, - generations: record.generations, - ...(record.selectedCandidateId ? { selectedCandidateId: record.selectedCandidateId } : {}), - } - const saved = await writeEvaluatorIntegrityManifest(config.archiveDir, run.runId, signEvaluatorIntegrity(trust, payload)) - record.status = "sealed" - record.keyId = trust.keyId - record.inputSha256 = payload.inputSha256 - record.archiveStateSha256 = payload.archiveStateSha256 - record.manifestPath = saved.path - record.manifestSha256 = saved.sha256 -} - -async function verifyResumedEvaluatorIntegrity( - config: RsiConfig, - run: RsiRunRecord, - trust: EvaluatorIntegrityTrust | undefined, - input: Record, - attested: boolean, -): Promise { - const existing = run.evaluatorIntegrity - const hasSealArtifacts = await hasEvaluatorIntegrityManifests(config.archiveDir, run.runId) - if ((!existing || existing.status !== "sealed") && hasSealArtifacts) throw new Error("evaluator integrity seal artifacts exist but archive metadata is missing or downgraded; refusing unsealed resume") - if (!existing) { - if (attested) throw new Error("attested RSI resume requires a signed evaluator integrity manifest; this historical run is unsealed") - if (trust) throw new Error("cannot retroactively seal a historical RSI run; start a new run with evaluator integrity trust configured") - return - } - const currentInputSha256 = evaluatorIntegrityDigest(input) - if (existing.status === "sealed") { - if (!trust) throw new Error("sealed RSI run cannot resume without its independently pinned evaluator integrity trust") - if (!existing.manifestSha256 || existing.manifestPath !== path.posix.join("evaluator-integrity", `${run.runId}.${existing.manifestSha256}.json`)) throw new Error("evaluator integrity manifest pointer is malformed") - const saved = await readEvaluatorIntegrityManifest(config.archiveDir, run.runId, existing.manifestSha256) - if (saved.sha256 !== existing.manifestSha256) throw new Error("evaluator integrity manifest bytes changed since the run was checkpointed") - const envelope = verifyEvaluatorIntegrity(trust, saved.envelope) - const payload = envelope.payload as Partial - if (payload.schemaVersion !== 1 || payload.runId !== run.runId || payload.inputSha256 !== currentInputSha256 || canonicalJson(payload.input) !== canonicalJson(input)) throw new Error("evaluator source, base tree, configuration, commands, or inputs changed since the signed RSI snapshot") - if (payload.archiveStateSha256 !== evaluatorArchiveStateDigest(run) || existing.archiveStateSha256 !== payload.archiveStateSha256) throw new Error("archived evaluator state changed since its last signed checkpoint") - if (canonicalJson(payload.outputs) !== canonicalJson(existing.outputs) || canonicalJson(payload.generations) !== canonicalJson(existing.generations) || payload.selectedCandidateId !== existing.selectedCandidateId) throw new Error("signed evaluator outputs or selection differ from the RSI archive checkpoint") - if (existing.keyId !== trust.keyId || existing.inputSha256 !== currentInputSha256) throw new Error("evaluator integrity trust or manifest identity differs from the frozen run identity") - verifyArchivedIntegrityOutputs(run, existing.outputs) - for (const generation of existing.generations) { - const generationCandidates = run.candidates.filter((candidate) => candidate.generation === generation.generation) - const accepted = generationCandidates.filter((candidate) => candidate.status === "accepted") - const expectedSelectedIds = paretoFront(accepted).map((candidate) => candidate.id) - const expectedSelected = [...accepted].sort((left, right) => compareFitness(right.fitness, left.fitness))[0]?.id - const reviews = (run.adversarialReviews ?? []).filter((review) => generationCandidates.some((candidate) => candidate.id === review.candidateId)) - if (canonicalJson(generation.selectedCandidateIds) !== canonicalJson(expectedSelectedIds) || generation.selectedCandidateId !== expectedSelected || - generation.decisionsSha256 !== evaluatorGenerationDecisionDigest(generationCandidates, reviews, expectedSelected)) { - throw new Error(`archived evaluator decisions differ from the signed selection for generation ${generation.generation}`) - } - } - const latestGeneration = existing.generations.at(-1) - if ((latestGeneration && canonicalJson(latestGeneration.selectedCandidateIds) !== canonicalJson(run.selectedCandidates ?? [])) || existing.selectedCandidateId !== run.selected) throw new Error("selected candidates differ from the signed evaluator selection") - return - } - if (trust) throw new Error("cannot retroactively seal an unsealed RSI run; start a new run with evaluator integrity trust configured") - if (attested) throw new Error("attested RSI resume requires an independently signed evaluator integrity manifest") -} - -function isTerminal(candidate: CandidateRecord): boolean { - return candidate.status === "accepted" || candidate.status === "rejected" || candidate.status === "failed" -} - -function proofCandidateSlot(generation: number, index: number): string { return `g${generation}-candidate-${index}` } -export const RSI_CONFIRMATION_RETENTION_TASK_IDS = ["redirect-escape", "qa-baseline-counts", "strict-tool-schema", "dashboard-git-owner-api"] as const - -export function proofCampaignTaskWallBoundMs(config: RsiConfig, plannedGenerations: number, developmentTasks: BenchmarkManifest["tasks"], confirmationTasks: BenchmarkManifest["tasks"], includeConfirmation = true): number { - const slotsPerArm = plannedGenerations * config.population - const candidateCountBothArms = 2 * slotsPerArm - const candidateTaskMs = config.commandTimeoutMs * (3 + config.evalCommands.length) - + 3 * developmentTasks.reduce((sum, task) => sum + task.ceilings.timeoutMs, 0) - const confirmationTaskMs = includeConfirmation ? 4 * confirmationTasks.reduce((sum, task) => sum + task.ceilings.timeoutMs, 0) : 0 - const bound = candidateCountBothArms * candidateTaskMs + confirmationTaskMs - if (!Number.isSafeInteger(bound) || bound <= 0) throw new Error("proof campaign task-time ceiling exceeds safe integer accounting") - return bound -} - -export interface ProofCampaignSpendReservationMicros { - mutation: number - benchmark: number - total: number -} - -export function proofCampaignSpendReservationMicros(args: { - mutationJobs: number - mutationMaxJobSpendUsd: number - mutationAllocationUsd: number - benchmarkJobs: number - benchmarkMaxJobSpendUsd: number - benchmarkAllocationUsd: number - totalAllocationUsd: number - benchmarkShortfallMessage: string -}): ProofCampaignSpendReservationMicros { - const floorMicros = (usd: number): number => { - const micros = Math.floor(usd * 1_000_000) - if (!Number.isSafeInteger(micros) || micros < 0) throw new Error("proof campaign spend allocation exceeds safe microUSD accounting") - return micros - } - const reservationMicros = (jobs: number, perJobUsd: number): number => { - const perJobMicros = Math.ceil(perJobUsd * 1_000_000) - const totalMicros = jobs * perJobMicros - if (!Number.isSafeInteger(jobs) || jobs < 0 || !Number.isSafeInteger(perJobMicros) || perJobMicros < 0 || !Number.isSafeInteger(totalMicros)) throw new Error("proof campaign job reservations exceed safe microUSD accounting") - return totalMicros - } - const mutation = reservationMicros(args.mutationJobs, args.mutationMaxJobSpendUsd) - const benchmark = reservationMicros(args.benchmarkJobs, args.benchmarkMaxJobSpendUsd) - if (floorMicros(args.mutationAllocationUsd) < mutation) throw new Error("mutation broker campaign allocation is below the sum of frozen mutation job reservations") - if (floorMicros(args.benchmarkAllocationUsd) < benchmark) throw new Error(args.benchmarkShortfallMessage) - const total = mutation + benchmark - if (!Number.isSafeInteger(total)) throw new Error("proof campaign job reservations exceed safe microUSD accounting") - if (floorMicros(args.totalAllocationUsd) < total) throw new Error("proof campaign total allocation is below the sum of frozen job reservations") - return { mutation, benchmark, total } -} - -export function createProofCampaignManifest(args: { - config: RsiConfig - campaignId: string - baseCommit: string - model: string - provider: string - openRouterProfileId?: string - runtime: NonNullable - developmentManifest: BenchmarkManifest - developmentManifestDigest: string - plannedGenerations: number - proofProtocol: NonNullable -}): ProofCampaignManifest { - const engineeringOnly = args.config.workerHarnessEngineeringOnly === true - if (engineeringOnly && args.config.workerHarnessPhase === "confirmation") throw new Error("engineering-only proof runs cannot enter confirmation") - if (args.provider !== "openshell-openrouter") throw new Error("restart-safe proof campaigns require supervisor-accounted OpenRouter inference") - if (args.config.computePolicy === "adaptive-independent") throw new Error("restart-safe proof campaigns require every frozen generation to run; adaptive early-stop is not admitted") - const benchmarkTasks = args.developmentManifest.tasks.filter((task) => task.split === "development") - const confirmationTasks = args.developmentManifest.tasks.filter((task) => task.split === "confirmation") - if (!benchmarkTasks.length || (!engineeringOnly && !confirmationTasks.length)) throw new Error("proof campaign requires nonempty development tasks and, unless engineering-only, confirmation tasks") - if (!engineeringOnly && RSI_CONFIRMATION_RETENTION_TASK_IDS.some((taskId) => !confirmationTasks.some((task) => task.id === taskId))) throw new Error("frozen benchmark manifest is missing a preregistered confirmation retention task") - const taskDigest = (tasks: typeof args.developmentManifest.tasks) => createHash("sha256").update(JSON.stringify(tasks)).digest("hex") - const mutationCampaignId = proofBrokerCampaignId(args.campaignId, "mutation") - const benchmarkCampaignId = proofBrokerCampaignId(args.campaignId, "benchmark") - const mutationLimits = openRouterBrokerLimitsFromEnvironment(openRouterAllocatedEnvironment(process.env, "mutation"), mutationCampaignId, { maxCalls: args.config.maxIterations, ...(args.config.maxTokens ? { maxOutputTokensPerCall: args.config.maxTokens } : {}), maxDurationMs: args.config.commandTimeoutMs }) - const benchmarkEnv = openRouterAllocatedEnvironment(process.env, "benchmark") - const benchmarkLimits = openRouterBrokerLimitsFromEnvironment(benchmarkEnv, benchmarkCampaignId, { maxDurationMs: Math.min(...benchmarkTasks.map((task) => task.ceilings.timeoutMs)) }) - const confirmationLimits = openRouterBrokerLimitsFromEnvironment(benchmarkEnv, benchmarkCampaignId, { maxDurationMs: Math.min(...confirmationTasks.map((task) => task.ceilings.timeoutMs)) }) - const jobCostCeiling = (limits: typeof mutationLimits, maxCalls: number, maxOutputTokensPerCall: number): number => { - const perCallTokens = Math.min(limits.maxTotalTokens, limits.maxInputTokensPerCall + maxOutputTokensPerCall) - const jobTokens = Math.min(limits.maxTotalTokens, maxCalls * (limits.maxInputTokensPerCall + maxOutputTokensPerCall)) - const unitPrice = Math.max(limits.maxInputPricePerMillionUsd, limits.maxOutputPricePerMillionUsd) - const micros = (tokens: number) => Math.ceil(tokens * unitPrice) - if (micros(perCallTokens) > limits.maxCallSpendUsd * 1_000_000) throw new Error("OpenRouter per-call spend cap is below the price ceiling for the admitted token ceiling") - return micros(jobTokens) / 1_000_000 - } - if (jobCostCeiling(mutationLimits, mutationLimits.maxCalls, mutationLimits.maxOutputTokensPerCall) > mutationLimits.maxJobSpendUsd) throw new Error("OpenRouter mutation job spend cap is below its admitted token and price ceilings") - for (const task of benchmarkTasks) if (jobCostCeiling(benchmarkLimits, task.ceilings.maxCalls, task.ceilings.maxOutputTokensPerCall) > benchmarkLimits.maxJobSpendUsd) throw new Error(`OpenRouter benchmark job spend cap is below the frozen token and price ceiling for ${task.id}`) - if (!engineeringOnly) for (const task of confirmationTasks) if (jobCostCeiling(confirmationLimits, task.ceilings.maxCalls, task.ceilings.maxOutputTokensPerCall) > confirmationLimits.maxJobSpendUsd) throw new Error(`OpenRouter confirmation job spend cap is below the frozen token and price ceiling for ${task.id}`) - const candidateCounts = Array.from({ length: args.plannedGenerations }, (_, generation) => args.config.computePolicy === "adaptive-independent" && generation > 0 ? 1 : args.config.population) - const schedule: ProofCampaignManifest["schedule"] = [] - for (const armId of ["fixed-control", "self-hosted"]) for (let generation = 0; generation < args.plannedGenerations; generation++) { - for (let index = 0; index < candidateCounts[generation]!; index++) { - const slot = proofCandidateSlot(generation, index) - schedule.push({ armId, generation, candidateId: slot, taskId: "mutation", seed: args.config.seed, attemptKind: "mutation", phase: "development" }) - schedule.push({ armId, generation, candidateId: slot, taskId: "regression", seed: args.config.seed, attemptKind: "verification", phase: "development" }) - schedule.push({ armId, generation, candidateId: slot, taskId: "typecheck", seed: args.config.seed, attemptKind: "verification", phase: "development" }) - for (const [taskIndex] of args.config.evalCommands.entries()) schedule.push({ armId, generation, candidateId: slot, taskId: `visible-${taskIndex}`, seed: args.config.seed, attemptKind: "verification", phase: "development" }) - for (const role of ["child", "parent", "base"]) for (const task of benchmarkTasks) schedule.push({ armId, generation, candidateId: `${slot}:${role}`, taskId: task.id, seed: String(task.seed), attemptKind: "development-proof", phase: "development" }) - } - } - const finalGeneration = args.plannedGenerations - 1 - if (!engineeringOnly) for (const armId of ["fixed-control", "self-hosted"]) { - for (let generation = 0; generation < args.plannedGenerations; generation++) { - for (let index = 0; index < candidateCounts[generation]!; index++) for (const task of confirmationTasks) schedule.push({ armId, generation, candidateId: proofCandidateSlot(generation, index), taskId: task.id, seed: String(task.seed), attemptKind: "confirmation", phase: "confirmation" }) - } - for (const task of confirmationTasks) schedule.push({ armId, generation: finalGeneration, candidateId: "root-control", taskId: task.id, seed: String(task.seed), attemptKind: "fixed-control", phase: "confirmation" }) - } - const developmentCallsPerProofRole = benchmarkTasks.reduce((sum, task) => sum + task.ceilings.maxCalls, 0) - const confirmationCalls = engineeringOnly ? 0 : 4 * confirmationTasks.reduce((sum, task) => sum + task.ceilings.maxCalls, 0) // selected final and root-control, for both arms - const rootCalls = 2 * candidateCounts.reduce((sum, count) => sum + count * (mutationLimits.maxCalls + 3 * developmentCallsPerProofRole), 0) + confirmationCalls - const developmentTokensPerProofRole = benchmarkTasks.reduce((sum, task) => sum + Math.min(benchmarkLimits.maxTotalTokens, task.ceilings.maxCalls * (benchmarkLimits.maxInputTokensPerCall + task.ceilings.maxOutputTokensPerCall)), 0) - const confirmationTokensPerHarness = confirmationTasks.reduce((sum, task) => sum + Math.min(confirmationLimits.maxTotalTokens, task.ceilings.maxCalls * (confirmationLimits.maxInputTokensPerCall + task.ceilings.maxOutputTokensPerCall)), 0) - const mutationTokens = Math.min(mutationLimits.maxTotalTokens, mutationLimits.maxCalls * (mutationLimits.maxInputTokensPerCall + mutationLimits.maxOutputTokensPerCall)) - const rootTokens = 2 * candidateCounts.reduce((sum, count) => sum + count * (mutationTokens + 3 * developmentTokensPerProofRole), 0) + (engineeringOnly ? 0 : 4 * confirmationTokensPerHarness) - if (!Number.isSafeInteger(rootCalls) || !Number.isSafeInteger(rootTokens)) throw new Error("proof campaign root call or token ceiling exceeds safe integer accounting") - const campaignBudget = openRouterCampaignBudgetAllocation(process.env) - const slotsPerArm = candidateCounts.reduce((sum, count) => sum + count, 0) - const mutationBudgetLimits = openRouterBrokerLimitsFromEnvironment(openRouterAllocatedEnvironment(process.env, "mutation"), mutationCampaignId) - const benchmarkJobCount = 2 * slotsPerArm * 3 * benchmarkTasks.length + (engineeringOnly ? 0 : 4 * confirmationTasks.length) - const reservations = proofCampaignSpendReservationMicros({ - mutationJobs: 2 * slotsPerArm, mutationMaxJobSpendUsd: mutationLimits.maxJobSpendUsd, mutationAllocationUsd: mutationBudgetLimits.maxCampaignSpendUsd, - benchmarkJobs: benchmarkJobCount, benchmarkMaxJobSpendUsd: benchmarkLimits.maxJobSpendUsd, benchmarkAllocationUsd: benchmarkLimits.maxCampaignSpendUsd, - totalAllocationUsd: campaignBudget.totalUsd, - benchmarkShortfallMessage: engineeringOnly ? "benchmark broker campaign allocation is below development proof job reservations" : "benchmark broker campaign allocation is below development plus selected-final/root confirmation job reservations", - }) - const taskWallBoundMs = proofCampaignTaskWallBoundMs(args.config, args.plannedGenerations, benchmarkTasks, confirmationTasks, !engineeringOnly) - const maxRuntimeMs = args.config.maxRuntimeMs ?? 60 * 60_000 - if (maxRuntimeMs < taskWallBoundMs) throw new Error(`proof campaign runtime ceiling ${maxRuntimeMs}ms is below the summed frozen task-time ceiling ${taskWallBoundMs}ms`) - return { - schemaVersion: 1, phaseMode: engineeringOnly ? "engineering-only" : "paired-confirmation", campaignId: args.campaignId, rootCommit: args.baseCommit, - benchmarkDigests: { manifest: args.developmentManifestDigest, development: taskDigest(benchmarkTasks), confirmation: taskDigest(engineeringOnly ? [] : confirmationTasks) }, - armAssignment: { fixedControl: "fixed-control", selfHosted: "self-hosted" }, - model: { provider: args.provider, id: args.model, ...(args.openRouterProfileId ? { profileId: args.openRouterProfileId } : {}), settings: { sampling: args.proofProtocol.model.sampling, roles: args.config.roles ?? {}, maxIterations: args.config.maxIterations, maxTokens: args.config.maxTokens ?? null } }, - seed: args.config.seed, taskOrder: ["mutation", "regression", "typecheck", ...args.config.evalCommands.map((_, index) => `visible-${index}`), ...benchmarkTasks.map((task) => task.id), ...(engineeringOnly ? [] : confirmationTasks.map((task) => task.id))], schedule, - budgets: { - attempt: { maxCalls: Math.max(mutationLimits.maxCalls, benchmarkLimits.maxCalls, ...(!engineeringOnly ? [confirmationLimits.maxCalls] : [])), maxTokens: Math.max(mutationLimits.maxTotalTokens, benchmarkLimits.maxTotalTokens, ...(!engineeringOnly ? [confirmationLimits.maxTotalTokens] : [])), maxSpendUsd: Math.max(mutationLimits.maxJobSpendUsd, benchmarkLimits.maxJobSpendUsd, ...(!engineeringOnly ? [confirmationLimits.maxJobSpendUsd] : [])), maxDurationMs: Math.max(args.config.commandTimeoutMs, ...benchmarkTasks.map((task) => task.ceilings.timeoutMs), ...(!engineeringOnly ? confirmationTasks.map((task) => task.ceilings.timeoutMs) : [])) }, - root: { maxCalls: rootCalls, maxTokens: rootTokens, maxSpendUsd: Math.min(campaignBudget.totalUsd, reservations.total / 1_000_000), maxRuntimeMs }, - }, - acceptanceRule: { ...args.proofProtocol.rule, confirmationRetentionTaskIds: engineeringOnly ? [] : [...RSI_CONFIRMATION_RETENTION_TASK_IDS], minimumSelfHostedCampaigns: 2, requiredSuccessiveTransitions: 2, clusterConfidence: 0.9 }, runtime: { imageDigest: args.runtime.imageDigest, harnessCommit: args.runtime.harnessCommit, dependencyLockDigest: args.runtime.dependencyLockDigest }, - analysisVersion: args.proofProtocol.schemaVersion.toString(), settings: { phaseMode: engineeringOnly ? "engineering-only" : "paired-confirmation", configDigest: workerHarnessConfigDigest(args.config, { baseCommit: args.baseCommit, model: args.model, provider: args.provider, proofProtocolDigest: args.proofProtocol.protocolDigest }), effectiveConfig: Object.fromEntries(Object.entries(args.config).filter(([key]) => !["workerHarnessMode", "workerHarnessPhase", "workerHarnessEngineeringOnly", "workerHarnessPairId", "workerHarnessRunId", "resumeRunId", "candidateIdNamespace", "workerHarnessParentCommit", "workerHarnessSnapshotTreeDigest", "hiddenSuitePath", "hiddenPublicKeyPath", "hiddenPrivateKeyPath", "hiddenNonceStorePath", "evaluatorSigningKeyPath", "evaluatorTrustedPublicKeyPath", "proofBenchmarkFixtures"].includes(key))), candidateCounts, taskWallBoundMs, evaluationCells: ["regression", "typecheck", ...args.config.evalCommands.map((_, index) => `visible-${index}`)], developmentTaskIdentities: benchmarkTasks.map((task) => ({ id: task.id, family: task.family, seed: task.seed, startDigest: task.startDigest, verifierDigest: task.verifierDigest, ceilings: task.ceilings })), confirmationTaskIdentities: engineeringOnly ? [] : confirmationTasks.map((task) => ({ id: task.id, family: task.family, seed: task.seed, startDigest: task.startDigest, verifierDigest: task.verifierDigest, ceilings: task.ceilings })), inferenceCeilings: { maxInputTokensPerCall: benchmarkLimits.maxInputTokensPerCall, maxOutputTokensPerCall: benchmarkLimits.maxOutputTokensPerCall, maxCallsPerAttempt: benchmarkLimits.maxCalls, maxTotalTokens: benchmarkLimits.maxTotalTokens, maxCallSpendUsd: benchmarkLimits.maxCallSpendUsd, maxJobSpendUsd: benchmarkLimits.maxJobSpendUsd, maxInputPricePerMillionUsd: benchmarkLimits.maxInputPricePerMillionUsd, maxOutputPricePerMillionUsd: benchmarkLimits.maxOutputPricePerMillionUsd }, inferenceCampaignIds: { mutation: mutationCampaignId, benchmark: benchmarkCampaignId }, inferenceAllocationsUsd: { mutation: mutationLimits.maxCampaignSpendUsd, benchmark: benchmarkLimits.maxCampaignSpendUsd } }, - } -} - -export function proofBrokerCampaignId(rootCampaignId: string, allocation: "mutation" | "benchmark"): string { - if (!rootCampaignId.trim()) throw new Error("proof campaign ID is required to derive broker campaign identity") - return `${rootCampaignId}:${allocation}` -} - -export async function reconcileProofCampaignJob(queue: PostgresRsiJobQueue, existing: FleetJob | undefined, expected: FleetJobIdentity, lease: ProofCampaignArmLease): Promise { - if (!existing || fleetJobMatches(existing, expected)) return existing - if (existing.status === "queued") { - const rebound = await queue.rebindQueuedProofJob(existing.jobId, expected, lease) - if (fleetJobMatches(rebound, expected)) return rebound - } - throw new Error("existing proof campaign job is not the same completed result or a safely rebindable queued job") -} - -export async function rebuildFinishedProofCampaignArchive(args: { - run: RsiRunRecord - config: RsiConfig - snapshot: Record - manifest: ProofCampaignManifest - armId: string - now: () => string -}): Promise { - const campaign = args.snapshot.campaign as Record | undefined - const arms = args.snapshot.arms as Array> | undefined - const arm = arms?.find((entry) => entry.arm_id === args.armId) - const expectedDigest = proofCampaignManifestDigest(args.manifest) - if (!campaign || !arm || arm.state !== "finished" || String(campaign.manifest_sha256).trim() !== expectedDigest) throw new Error("finished proof campaign snapshot does not match the frozen manifest and arm") - if (arm.run_id !== args.run.runId || args.run.candidates.some((candidate) => !isTerminal(candidate))) throw new Error("finished proof campaign arm cannot rebuild an incomplete or foreign run archive") - if (args.manifest.phaseMode === "engineering-only") { - const events = args.snapshot.events as Array> | undefined - if (!events?.some((event) => event.arm_id === args.armId && event.event_type === "engineering-development-finished") || args.run.proofConfirmation || args.run.confirmationAttempts?.length) throw new Error("engineering-only recovery must have a terminal development event and no confirmation evidence") - } else await verifyFinishedConfirmationCheckpoint(args.run, args.config.archiveDir, args.snapshot, args.manifest, args.armId) - args.run.proofCampaign = { manifest: args.manifest, manifestSha256: expectedDigest, campaignId: args.manifest.campaignId, armId: args.armId, epoch: Number(arm.epoch), state: "finished", ledgerSnapshot: args.snapshot } - const selections = (args.snapshot.events as Array>).filter((event) => event.arm_id === args.armId && event.event_type === "generation-selected").sort((left, right) => Number(left.sequence) - Number(right.sequence)) - const finalSelection = selections.at(-1)?.payload as { generation?: number; selectedCandidateIds?: string[] } | undefined - if (finalSelection && Number.isSafeInteger(finalSelection.generation)) { - args.run.selectedCandidates = (finalSelection.selectedCandidateIds ?? []).map((slot) => { - const generationCandidates = args.run.candidates.filter((candidate) => candidate.generation === finalSelection.generation) - const index = Number(/candidate-(\d+)$/.exec(slot)?.[1] ?? -1) - return generationCandidates[index]?.id ?? "" - }).filter(Boolean) - args.run.selected ??= [...args.run.candidates.filter((candidate) => candidate.status === "accepted")].sort((a, b) => compareFitness(b.fitness, a.fitness))[0]?.id - } - args.run.finishedAt ??= args.now() - const recoveredReportPath = `${args.config.archiveDir}/${args.run.runId}.md` - await fs.mkdir(args.config.archiveDir, { recursive: true }) - await fs.writeFile(recoveredReportPath, formatRunReport(args.run, args.config), "utf8") - args.run.reports = [...new Set([...args.run.reports, recoveredReportPath])] - await appendRun(args.config.archiveDir, args.run) - return args.run -} - -async function verifyFinishedConfirmationCheckpoint(run: RsiRunRecord, archiveDir: string, snapshot: Record, manifest: ProofCampaignManifest, armId: string): Promise { - const events = snapshot.events as Array> | undefined - const attempts = snapshot.attempts as Array> | undefined - const selection = events?.filter((event) => event.arm_id === armId && event.event_type === "final-harness-selected") - const payload = selection?.length === 1 ? selection[0]!.payload as { generation?: number; candidateId?: string; commit?: string } : undefined - const saved = run.proofConfirmation - if (!payload || !saved || saved.selectedHarness.slotId !== payload.candidateId || saved.selectedHarness.generation !== payload.generation || saved.selectedHarness.commit !== payload.commit || saved.rootHarness.commit !== manifest.rootCommit || run.confirmationEvidence !== "complete") throw new Error("finished campaign recovery requires a matching checkpointed final-harness selection and confirmation result") - const cells = manifest.schedule.filter((cell) => cell.armId === armId && cell.phase === "confirmation" && (cell.candidateId === "root-control" || (cell.candidateId === payload.candidateId && cell.generation === payload.generation))) - const byCandidateKind = (candidateId: string, kind: string) => cells.filter((cell) => cell.candidateId === candidateId && cell.attemptKind === kind) - const selectedRows = byCandidateKind(String(payload.candidateId), payload.candidateId === "root-control" ? "fixed-control" : "confirmation") - const rootRows = byCandidateKind("root-control", "fixed-control") - const rowFor = (cell: typeof cells[number]) => attempts?.filter((row) => row.arm_id === armId && Number(row.generation) === cell.generation && row.candidate_id === cell.candidateId && row.task_id === cell.taskId && row.seed === cell.seed && row.attempt_kind === cell.attemptKind && row.status === "completed") ?? [] - const expectedSelectedRows = selectedRows.flatMap((cell) => rowFor(cell)) - const expectedRootRows = rootRows.flatMap((cell) => rowFor(cell)) - const orderedIds = (rows: Array>) => rows.map((row) => String(row.broker_job_id)).sort() - if (expectedSelectedRows.length !== selectedRows.length || expectedRootRows.length !== rootRows.length || JSON.stringify(orderedIds(expectedSelectedRows)) !== JSON.stringify([...saved.selectedHarness.attemptIds].sort()) || JSON.stringify(orderedIds(expectedRootRows)) !== JSON.stringify([...saved.rootHarness.attemptIds].sort())) throw new Error("finished campaign recovery checkpoint does not name the completed selected and root confirmation cells") - const artifacts = new Map((run.confirmationAttempts ?? []).map((attempt) => [attempt.attemptId, attempt])) - for (const row of [...expectedSelectedRows, ...expectedRootRows]) { - const attemptId = String(row.broker_job_id) - const attempt = artifacts.get(attemptId) - const expectedProfile = attempt ? openRouterProfileForModel(attempt.model.id, run.proofCampaign?.manifest.model.profileId) : undefined - const profileIdMatches = expectedProfile?.id === RSI_DEFAULT_OPENROUTER_PROFILE_ID ? attempt?.inferenceReceipt?.profileId === undefined || attempt?.inferenceReceipt?.profileId === expectedProfile.id : attempt?.inferenceReceipt?.profileId === expectedProfile?.id - if (!attempt || attempt.validity !== "valid" || !attempt.proofQualified || attempt.resourceAccounting !== "supervisor-enforced" || !attempt.inferenceReceipt?.qualified || !profileIdMatches || attempt.inferenceReceipt.endpointProvider !== expectedProfile?.endpointProvider || attempt.inferenceReceipt.returnedProviders?.join(",") !== expectedProfile?.endpointProvider || createHash("sha256").update(JSON.stringify(attempt)).digest("hex") !== String(row.result_sha256).trim()) throw new Error(`finished campaign recovery attempt ${attemptId} does not match its verified durable result`) - const storedRefs = row.artifact_refs as Record | undefined - if (!storedRefs || Object.entries(attempt.artifactRefs).some(([name, digest]) => storedRefs[name]?.sha256 !== digest)) throw new Error(`finished campaign recovery attempt ${attemptId} has different artifact references`) - const candidateId = String(row.candidate_id) - const kind = String(row.attempt_kind) - const outputRoot = path.join(archiveDir, "confirmation", manifest.campaignId, armId, candidateId, kind, "harness", attempt.harnessCommit) - for (const digest of Object.values(attempt.artifactRefs)) { - if (!/^[a-f0-9]{64}$/.test(digest)) throw new Error(`finished campaign recovery attempt ${attemptId} has a malformed artifact digest`) - const artifact = path.join(outputRoot, "artifacts", "sha256", digest.slice(0, 2), digest) - const stat = await fs.lstat(artifact) - if (!stat.isFile() || stat.isSymbolicLink() || createHash("sha256").update(await fs.readFile(artifact)).digest("hex") !== digest) throw new Error(`finished campaign recovery artifact ${digest} is missing or changed`) - } - } -} - -/** Rebuild only the durable development-phase checkpoint after its DB commit. */ -export async function rebuildEvaluatedProofCampaignArchive(args: { - run: RsiRunRecord - config: RsiConfig - snapshot: Record - manifest: ProofCampaignManifest - armId: string - now: () => string -}): Promise { - const campaign = args.snapshot.campaign as Record | undefined - const arms = args.snapshot.arms as Array> | undefined - const events = args.snapshot.events as Array> | undefined - const arm = arms?.find((entry) => entry.arm_id === args.armId) - const expectedDigest = proofCampaignManifestDigest(args.manifest) - const finishEvent = events?.some((event) => event.arm_id === args.armId && event.event_type === "development-phase-finished" && (event.payload as { remainingPhase?: unknown })?.remainingPhase === "confirmation") - if (!campaign || !arm || !finishEvent || arm.phase !== "confirmation" || !["evaluated", "selected", "rejected"].includes(String(arm.state)) || String(campaign.manifest_sha256).trim() !== expectedDigest) throw new Error("evaluated proof campaign snapshot does not match the frozen manifest and development completion") - if (arm.run_id !== args.run.runId || args.run.candidates.some((candidate) => !isTerminal(candidate))) throw new Error("evaluated proof campaign arm cannot rebuild from an incomplete or foreign run checkpoint") - const expectedMode = args.armId === args.manifest.armAssignment.fixedControl ? "fixed-control" : "self-hosted" - if (args.run.workerHarnessPairId !== args.manifest.campaignId || args.run.workerHarnessMode !== expectedMode) throw new Error("evaluated proof campaign checkpoint has a foreign paired-run identity") - if (arm.lease_expires_at && new Date(String(arm.lease_expires_at)).getTime() > Date.now()) throw new Error("evaluated proof campaign arm has an active coordinator; refusing read-only recovery during confirmation work") - args.run.proofCampaign = { manifest: args.manifest, manifestSha256: expectedDigest, campaignId: args.manifest.campaignId, armId: args.armId, epoch: Number(arm.epoch), state: "evaluated", ledgerSnapshot: args.snapshot } - args.run.finishedAt ??= args.now() - const reportPath = `${args.config.archiveDir}/${args.run.runId}.md` - await fs.mkdir(args.config.archiveDir, { recursive: true }) - await fs.writeFile(reportPath, formatRunReport(args.run, args.config), "utf8") - args.run.reports = [...new Set([...args.run.reports, reportPath])] - await appendRun(args.config.archiveDir, args.run) - return args.run -} - -class CandidateLineageError extends Error {} - -export function validateCandidateParent(candidate: CandidateRecord, run: RsiRunRecord, config: RsiConfig, protocol: NonNullable): void { - if (candidate.parent === "baseline") { - if (candidate.baseCommit !== run.baseCommit || candidate.parentCommit !== run.baseCommit) throw new CandidateLineageError("baseline child does not use the frozen run base commit") - return - } - const parent = run.candidates.find((entry) => entry.id === candidate.parent) - if (!parent || parent.status !== "accepted" || parent.developmentProof?.accepted !== true || parent.developmentProof.runId !== run.runId || parent.developmentProof.protocolDigest !== protocol.protocolDigest || !parent.fitness || Object.values(parent.fitness.hardGates).some((passed) => passed === false)) { - throw new CandidateLineageError(`candidate parent ${candidate.parent} is missing, rejected, from another run/protocol, or fails hard gates`) - } - const parentCommit = parent.commits[0] - if (!parentCommit || candidate.baseCommit !== parentCommit || candidate.parentCommit !== parentCommit) throw new CandidateLineageError(`candidate parent ${candidate.parent} commit does not match its mutation base`) - try { - execFileSync("git", ["merge-base", "--is-ancestor", run.baseCommit, parentCommit], { cwd: config.repoRoot, stdio: "ignore" }) - } catch { throw new CandidateLineageError(`candidate parent ${candidate.parent} is outside the frozen run base lineage`) } - const inheritedPaths = execFileSync("git", ["diff", "--name-only", "-z", "--no-renames", run.baseCommit, parentCommit], { cwd: config.repoRoot, encoding: "buffer" }) as Buffer - const violations = mutationSnapshotProtectedPathViolations(inheritedPaths.toString("utf8").split("\0").filter(Boolean), config.protectedPaths) - if (violations.length > 0) throw new CandidateLineageError(`candidate parent ${candidate.parent} inherits protected path changes: ${violations.join(", ")}`) -} - -function replaceJob(run: RsiRunRecord, job: ExperimentJob): void { - run.jobs ??= [] - const index = run.jobs.findIndex((entry) => entry.id === job.id) - if (index >= 0) run.jobs[index] = job - else run.jobs.push(job) -} - -function combinedArchive(archive: RsiArchive, run: RsiRunRecord): RsiArchive { - const currentIds = new Set(run.candidates.map((candidate) => candidate.id)) - return { ...archive, candidates: [...archive.candidates.filter((candidate) => !currentIds.has(candidate.id)), ...run.candidates] } -} - -function validateDeterministicWorkerHarnessRunId(config: RsiConfig): void { - if (config.workerHarnessRunId) { - const suffix = config.workerHarnessMode === "fixed-control" ? "fixed" : config.workerHarnessMode === "self-hosted" ? "self" : undefined - if (!config.workerHarnessPairId || !suffix || config.workerHarnessRunId !== `${config.workerHarnessPairId}-${suffix}` || !/^rsi-proof-[a-f0-9]{20}-(fixed|self)$/.test(config.workerHarnessRunId)) throw new Error("deterministic worker harness run ID must match its paired campaign and arm") - } - } - -function initialRun(config: RsiConfig, baseCommit: string, now: string): RsiRunRecord { - const modelCandidateId = config.modelCandidateId ?? "model-base" - validateDeterministicWorkerHarnessRunId(config) - return { - runId: config.workerHarnessRunId ?? `rsi-${now.replace(/[^0-9]/g, "").slice(0, 14)}-${randomUUID().slice(0, 8)}`, - startedAt: now, - model: config.model, - baseRef: config.baseRef, - baseCommit, - generations: config.generations, - parentSelectionPolicy: config.parentSelectionPolicy ?? "champion-specialist-novelty", - selectedCandidates: [], - parentChoices: {}, - modelCandidates: [baseModelCandidate(config.model, now, modelCandidateId)], - combinations: [], - jobs: [], - trajectoryRefs: [], - curriculumTasks: [], - adversarialReviews: [], - candidates: [], - reports: [], - confirmationEvidence: "unmeasured", - } -} - -async function buildEvaluatorIntegrityInput(args: { - config: RsiConfig - run: RsiRunRecord - packageRoot: string - roles: RsiConfig["roles"] - workerModel: string - workerProvider: string - workerOpenRouterProfile?: string - proofProtocol: ReturnType - benchmarkManifestSha256: string - evaluatorRuntime?: EvaluatorRuntimeIdentity - hiddenVerifierIdentity?: RsiRunRecord["hiddenVerifierIdentity"] - syntheticRuntimeIdentity?: boolean -}): Promise> { - const source = await evaluatorSourceIdentity(args.packageRoot) - const baseTreeSha256 = evaluatorBaseTreeIdentity(args.config.repoRoot, args.run.baseCommit) - const config = args.config - return { - schemaVersion: 1, - runId: args.run.runId, - baseCommit: args.run.baseCommit, - baseTreeSha256, - evaluatorSource: source, - imageIdentity: { - requestedImage: args.evaluatorRuntime ? `headlesscode-openshell-rsi@${args.evaluatorRuntime.imageDigest}` : null, - pinnedImageDigest: args.evaluatorRuntime?.imageDigest ?? null, - identitySource: args.evaluatorRuntime?.identitySource ?? "not-executed-dry-run", - workerHarnessRuntime: args.run.workerHarnessRuntime ?? null, - gatewayAlias: process.env.OPENSHELL_GATEWAY ?? null, - }, - evaluatorIsolationPolicy: args.evaluatorRuntime ? { source: args.evaluatorRuntime.policySource, sha256: args.evaluatorRuntime.policySha256 } : null, - storageAndWorkspace: { - repository: path.resolve(config.repoRoot), - archive: path.resolve(config.archiveDir), - worktrees: path.resolve(config.worktreeDir), - trajectoryOutput: path.resolve(config.trajectoryDir ?? path.join(config.archiveDir, "trajectories")), - curriculumOutput: path.resolve(config.curriculumDir ?? path.join(config.archiveDir, "curriculum")), - }, - commands: { - regression: config.regressionCommand, - visible: [...config.evalCommands], - hidden: [...config.hiddenEvalCommands], - }, - effectiveArguments: { - baseRef: config.baseRef, - model: args.workerModel, - provider: args.workerProvider, - openRouterProfile: args.workerOpenRouterProfile ?? null, - roles: args.roles ?? null, - population: config.population, - generations: args.run.generations, - maxConcurrent: config.maxConcurrent, - maxIterations: config.maxIterations, - maxTokens: config.maxTokens ?? null, - commandTimeoutMs: config.commandTimeoutMs, - computePolicy: config.computePolicy ?? "single", - maxTrajectories: config.maxTrajectories ?? null, - maxTotalIterations: config.maxTotalIterations ?? null, - maxRuntimeMs: config.maxRuntimeMs ?? null, - seed: config.seed, - mutationTask: config.mutationTask, - mutationKind: config.mutationKind ?? "corrective", - hypothesis: config.hypothesis ?? null, - protectedPaths: [...config.protectedPaths].sort(), - parentSelectionPolicy: config.parentSelectionPolicy ?? "champion-specialist-novelty", - developmentProofRule: config.developmentProofRule ?? null, - developmentProofProtocolSha256: args.proofProtocol.protocolDigest, - workerHarness: { - mode: args.run.workerHarnessMode ?? "fixed-control", - pairId: args.run.workerHarnessPairId ?? null, - configDigest: args.run.workerHarnessConfigDigest ?? null, - plannedGenerations: args.run.workerHarnessPlannedGenerations ?? null, - phase: config.workerHarnessPhase ?? null, - engineeringOnly: config.workerHarnessEngineeringOnly ?? false, - }, - }, - inputArtifacts: [ - { id: "development-benchmark-manifest", sha256: args.benchmarkManifestSha256 }, - ...(args.hiddenVerifierIdentity ? [{ id: "hidden-verifier-suite", sha256: args.hiddenVerifierIdentity.suiteSha256 }, { id: "hidden-verifier-public-key", sha256: args.hiddenVerifierIdentity.publicKeySha256 }, { id: "hidden-verifier-image", sha256: args.hiddenVerifierIdentity.imageDigest }] : []), - ], - } -} - -async function waitForFleetJob(queue: PostgresRsiJobQueue, jobId: string, timeoutMs: number, runtimeDeadlineMs?: number): Promise { - const deadline = Math.min(Date.now() + timeoutMs, runtimeDeadlineMs ?? Number.POSITIVE_INFINITY) - for (;;) { - const job = await queue.getJob(jobId) - if (!job) throw new Error(`fleet job ${jobId} disappeared`) - if (job.status === "completed" || job.status === "failed" || job.status === "cancelled") return job - if (Date.now() >= deadline) break - await delay(500) - } - const reason = runtimeDeadlineMs !== undefined && Date.now() >= runtimeDeadlineMs ? "RSI run runtime ceiling" : "coordinator wait timeout" - await queue.cancel(jobId, reason) - const teardownDeadline = Date.now() + 60_000 - while (Date.now() < teardownDeadline) { - const job = await queue.getJob(jobId) - if (!job) throw new Error(`fleet job ${jobId} disappeared during cancellation`) - if (job.status === "completed" || job.status === "failed" || job.status === "cancelled") return job - await delay(200) - } - throw new Error(`fleet job ${jobId} exceeded ${reason}; cancellation is pending worker teardown and its lease remains capacity-reserved`) -} - -function fleetRuntimeDeadlineMs(config: RsiConfig, run: RsiRunRecord): number | undefined { - return config.maxRuntimeMs === undefined ? undefined : Date.parse(run.startedAt) + config.maxRuntimeMs -} - -export function fleetPayload(config: RsiConfig, candidate: CandidateRecord, snapshot: { content: Buffer; snapshotCommit: string; snapshotTreeDigest?: string }, options: { campaignId?: string; boundedInference?: boolean; includeWorkerHarness?: boolean; proofCampaign?: FleetJobPayload["proofCampaign"]; remainingCampaignMs?: number; evaluatorRuntime?: Pick } = {}): FleetJobPayload { - const provider = config.roles?.worker?.provider === "openrouter" ? "openrouter" : "ollama" - const openRouterProfile = provider === "openrouter" ? openRouterProfileForModel(candidate.model, config.roles?.worker?.openRouterProfile) : undefined - const inferenceLimits = provider === "openrouter" && options.boundedInference - ? openRouterBrokerLimitsFromEnvironment(openRouterAllocatedEnvironment(process.env, "mutation"), options.campaignId ?? "", { - maxCalls: config.maxIterations, - ...(config.maxTokens ? { maxOutputTokensPerCall: config.maxTokens } : {}), - maxDurationMs: Math.max(1, Math.min(config.commandTimeoutMs, options.remainingCampaignMs ?? config.commandTimeoutMs)), - }) - : undefined - const effectiveMode = effectiveWorkerHarnessMode(config.workerHarnessMode ?? "fixed-control", candidate.generation) - const runtime = config.workerHarnessRuntime - if (config.workerHarnessMode && options.includeWorkerHarness && (!runtime || !validWorkerHarnessRuntimeIdentity(runtime) || !snapshot.snapshotTreeDigest)) throw new Error("worker harness job lacks its frozen runtime and sanitized snapshot identity") - return { - schemaVersion: 1, - snapshotSha256: "", // replaced with the verified artifact digest before enqueue - snapshotBytes: snapshot.content.byteLength, - snapshotCommit: snapshot.snapshotCommit, - task: config.mutationTask, - model: candidate.model, - provider, - ...(openRouterProfile && openRouterProfile.id !== RSI_DEFAULT_OPENROUTER_PROFILE_ID ? { openRouterProfile: openRouterProfile.id } : {}), - ...(options.proofCampaign ? { proofCampaign: options.proofCampaign } : {}), - ...(inferenceLimits ? { inferenceLimits } : {}), - ...(options.evaluatorRuntime ? { evaluatorRuntime: { ...options.evaluatorRuntime } } : {}), - maxIterations: config.maxIterations, - timeoutMs: config.commandTimeoutMs, - protectedPaths: config.protectedPaths, - candidate: { - id: candidate.id, generation: candidate.generation, parent: candidate.parent, - ...(config.workerHarnessMode && options.includeWorkerHarness ? { baseCommit: candidate.baseCommit } : {}), - mutationKind: candidate.mutationKind, hypothesis: candidate.hypothesis, - modelCandidateId: candidate.modelCandidateId, computePolicy: config.computePolicy, - }, - regressionCommand: config.regressionCommand, - evalCommands: config.evalCommands, - ...(config.workerHarnessMode && options.includeWorkerHarness ? { workerHarness: { - runMode: config.workerHarnessMode, - mode: effectiveMode, - ...(config.workerHarnessPairId ? { pairId: config.workerHarnessPairId } : {}), - parentCommit: candidate.baseCommit, - snapshotTreeDigest: snapshot.snapshotTreeDigest!, - runtime: runtime!, - executedCliPath: workerHarnessCliPath(effectiveMode), - } } : {}), - } -} - -function recordFleetInferenceUsage(candidate: CandidateRecord, resultValue: unknown, payload: FleetJobPayload, jobId: string): void { - candidate.modelProvider = `openshell-${payload.provider ?? "ollama"}` - if ((payload.provider ?? "ollama") !== "openrouter") return - if (!resultValue || typeof resultValue !== "object" || !payload.inferenceLimits) throw new Error("OpenRouter fleet mutation result is missing its broker identity") - const result = resultValue as { provider?: unknown; model?: unknown; inferenceReceipt?: Record } - const receipt = result.inferenceReceipt - const profile = openRouterProfileForModel(payload.model, payload.openRouterProfile) - const profileIdMatches = profile.id === RSI_DEFAULT_OPENROUTER_PROFILE_ID ? receipt?.profileId === undefined || receipt.profileId === profile.id : receipt?.profileId === profile.id - const price = receipt?.endpointPrice as OpenRouterEndpointPriceSnapshot | undefined - const priceMatches = profile.id === RSI_DEFAULT_OPENROUTER_PROFILE_ID || Boolean(price && price.model === profile.model && price.provider === profile.endpointProvider && price.endpointTag === profile.endpointTag - && price.quantization === profile.quantization && profile.requiredParameters.every((parameter) => price.supportedParameters.includes(parameter)) - && Number.isFinite(Date.parse(price.observedAt)) && /^[a-f0-9]{64}$/.test(price.responseSha256) - && Number.isFinite(price.inputPricePerMillionUsd) && price.inputPricePerMillionUsd > 0 && price.inputPricePerMillionUsd <= payload.inferenceLimits.maxInputPricePerMillionUsd - && Number.isFinite(price.outputPricePerMillionUsd) && price.outputPricePerMillionUsd > 0 && price.outputPricePerMillionUsd <= payload.inferenceLimits.maxOutputPricePerMillionUsd) - if (result.provider !== "openrouter" || result.model !== payload.model || !receipt || receipt.provider !== "openrouter" || !profileIdMatches || !priceMatches || receipt.endpointProvider !== profile.endpointProvider || receipt.jobId !== jobId || receipt.campaignId !== payload.inferenceLimits.campaignId || receipt.model !== payload.model || receipt.accounting !== "supervisor-enforced" || receipt.qualified !== true || !Array.isArray(receipt.returnedModels) || receipt.returnedModels.length !== 1 || !profile.returnedModels.includes(String(receipt.returnedModels[0])) || !Array.isArray(receipt.returnedProviders) || receipt.returnedProviders.length !== 1 || receipt.returnedProviders[0] !== profile.endpointProvider) { - throw new Error("OpenRouter fleet mutation result lacks a qualified receipt bound to the admitted provider, model, job and campaign") - } - const values = [receipt.calls, receipt.promptTokens, receipt.completionTokens, receipt.totalTokens, receipt.costUsd] - if (!values.every((value) => typeof value === "number" && Number.isFinite(value) && value >= 0) || !Number.isSafeInteger(receipt.calls) || !Number.isSafeInteger(receipt.promptTokens) || !Number.isSafeInteger(receipt.completionTokens) || !Number.isSafeInteger(receipt.totalTokens)) throw new Error("OpenRouter fleet mutation receipt contains malformed resource totals") - if (!Array.isArray(receipt.denials) || !receipt.denials.every((entry) => entry && typeof entry === "object" && Number.isSafeInteger((entry as OpenRouterDenialSummary).statusCode) && (entry as OpenRouterDenialSummary).statusCode >= 400 && (entry as OpenRouterDenialSummary).statusCode <= 599 && Number.isSafeInteger((entry as OpenRouterDenialSummary).count) && (entry as OpenRouterDenialSummary).count >= 1 && /^[a-z-]+$/.test((entry as OpenRouterDenialSummary).reason))) throw new Error("OpenRouter fleet mutation receipt contains malformed denial summaries") - candidate.inferenceUsage = { - provider: "openrouter", endpointProvider: profile.endpointProvider, ...(profile.id !== RSI_DEFAULT_OPENROUTER_PROFILE_ID ? { profileId: profile.id } : {}), model: payload.model, returnedModels: receipt.returnedModels as string[], returnedProviders: receipt.returnedProviders as string[], calls: receipt.calls as number, - promptTokens: receipt.promptTokens as number, completionTokens: receipt.completionTokens as number, totalTokens: receipt.totalTokens as number, - costUsd: receipt.costUsd as number, accounting: "supervisor-enforced", qualified: true, jobId, - campaignId: payload.inferenceLimits.campaignId, - denials: receipt.denials as OpenRouterDenialSummary[], - } -} - -function recordFleetInferenceDenials(candidate: CandidateRecord, resultValue: unknown, payload: FleetJobPayload, jobId: string): void { - if (payload.provider !== "openrouter" || !resultValue || typeof resultValue !== "object") return - const result = resultValue as { provider?: unknown; model?: unknown; inferenceAudit?: { accounting?: unknown; jobId?: unknown; denials?: unknown } } - const audit = result.inferenceAudit - if (result.provider !== "openrouter" || result.model !== payload.model || audit?.accounting !== "incomplete" || audit.jobId !== jobId || !Array.isArray(audit.denials)) return - const denials = audit.denials as OpenRouterDenialSummary[] - if (!denials.every((entry) => entry && typeof entry === "object" && Number.isSafeInteger(entry.statusCode) && entry.statusCode >= 400 && entry.statusCode <= 599 && Number.isSafeInteger(entry.count) && entry.count >= 1 && /^[a-z-]+$/.test(entry.reason))) return - candidate.inferenceDenials = structuredClone(denials) -} - -function fleetEvaluationResult(value: unknown): { regression: TrialResult; typecheck: TrialResult; visible: TrialResult[] } { - if (!value || typeof value !== "object") throw new Error("fleet evaluation result is malformed") - const result = value as { - evaluation?: { regression?: unknown; typecheck?: unknown; visible?: unknown[] } - outputArtifact?: { sha256?: unknown; bytes?: unknown } - } - const output = result.outputArtifact - if (!output || typeof output.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(output.sha256) || !Number.isSafeInteger(output.bytes) || Number(output.bytes) < 1) { - throw new Error("fleet evaluation output artifact reference is missing or malformed") - } - const compactTrial = (trial: unknown): TrialResult => { - if (!trial || typeof trial !== "object") throw new Error("fleet evaluation trial is malformed") - const item = trial as TrialResult - if (typeof item.ok !== "boolean" || (item.exitCode !== null && !Number.isSafeInteger(item.exitCode)) || !Number.isFinite(item.durationMs) || item.durationMs < 0 || typeof item.command !== "string" || item.command.length > 512 || typeof item.stdout !== "string" || item.stdout.length > 2048 || typeof item.stderr !== "string" || item.stderr.length > 2048 || item.outputArtifactSha256 !== output.sha256 || item.outputArtifactBytes !== output.bytes) { - throw new Error("fleet evaluation trial exceeds bounded result fields or lacks its output artifact reference") - } - return item - } - const evaluation = result.evaluation - if (!evaluation || !Array.isArray(evaluation.visible)) throw new Error("fleet evaluation trial list is malformed") - return { regression: compactTrial(evaluation.regression), typecheck: compactTrial(evaluation.typecheck), visible: evaluation.visible.map(compactTrial) } -} - -function fleetWorkerHarnessExecution(value: unknown, expected: FleetJobPayload): WorkerHarnessExecution | undefined { - if (!expected.workerHarness) return undefined - if (!value || typeof value !== "object") throw new Error("fleet mutation result omitted its worker harness identity") - const execution = (value as { workerHarnessExecution?: WorkerHarnessExecution }).workerHarnessExecution - const plan = expected.workerHarness - if (!execution || execution.mode !== plan.mode || execution.parentCommit !== plan.parentCommit - || execution.snapshotTreeDigest !== plan.snapshotTreeDigest || execution.executedCliPath !== plan.executedCliPath - || JSON.stringify(execution.runtime) !== JSON.stringify(plan.runtime) - || !/^[a-f0-9]{40}$/.test(execution.snapshotCommit)) throw new Error("fleet mutation result does not match its frozen worker harness identity") - return execution -} - -function validateCandidateWorkerHarnessExecution(candidate: CandidateRecord, run: RsiRunRecord, config: RsiConfig, execution?: WorkerHarnessExecution): void { - if (!config.workerHarnessMode) return - const expectedMode = effectiveWorkerHarnessMode(config.workerHarnessMode, candidate.generation) - if (!execution || execution.mode !== expectedMode || execution.parentCommit !== candidate.baseCommit - || execution.executedCliPath !== workerHarnessCliPath(expectedMode) - || !/^[a-f0-9]{64}$/.test(execution.snapshotTreeDigest) - || !/^[a-f0-9]{40}$/.test(execution.snapshotCommit) - || JSON.stringify(execution.runtime) !== JSON.stringify(run.workerHarnessRuntime)) throw new Error("mutation result lacks the frozen worker harness execution identity") - candidate.workerHarnessExecution = execution -} +import { + adversarialOutputRecords, + evaluationOutputRecords, + mergeIntegrityOutputs, + verifyIntegrityArtifacts, +} from "./controller-integrity.js" +import { + buildEvaluatorIntegrityInput, + persistEvaluatorIntegrity, + verifyResumedEvaluatorIntegrity, +} from "./controller-integrity-seal.js" +import { + CandidateLineageError, + combinedArchive, + initialRun, + isTerminal, + replaceJob, + validateCandidateParent, + validateDeterministicWorkerHarnessRunId, +} from "./controller-candidates.js" +import { + createProofCampaignManifest, + proofBrokerCampaignId, + proofCandidateSlot, +} from "./controller-proof-manifest.js" +import { + rebuildEvaluatedProofCampaignArchive, + rebuildFinishedProofCampaignArchive, + reconcileProofCampaignJob, +} from "./controller-proof-archive.js" +import { + fleetEvaluationResult, + fleetPayload, + fleetRuntimeDeadlineMs, + fleetWorkerHarnessExecution, + recordFleetInferenceDenials, + recordFleetInferenceUsage, + validateCandidateWorkerHarnessExecution, + waitForFleetJob, +} from "./controller-fleet.js" +export type { IntegrityOutput } from "./controller-integrity.js" +export { + adversarialOutputRecords, + evaluationOutputRecords, + evaluatorArchiveStateDigest, + mergeIntegrityOutputs, + trialOutputRecords, + verifyArchivedIntegrityOutputs, + verifyIntegrityArtifacts, +} from "./controller-integrity.js" +export { + buildEvaluatorIntegrityInput, + persistEvaluatorIntegrity, + verifyResumedEvaluatorIntegrity, +} from "./controller-integrity-seal.js" +export { + CandidateLineageError, + combinedArchive, + initialRun, + isTerminal, + replaceJob, + validateCandidateParent, + validateDeterministicWorkerHarnessRunId, +} from "./controller-candidates.js" +export { + createProofCampaignManifest, + proofBrokerCampaignId, + proofCampaignSpendReservationMicros, + proofCampaignTaskWallBoundMs, + proofCandidateSlot, + RSI_CONFIRMATION_RETENTION_TASK_IDS, +} from "./controller-proof-manifest.js" +export type { ProofCampaignSpendReservationMicros } from "./controller-proof-manifest.js" +export { + rebuildEvaluatedProofCampaignArchive, + rebuildFinishedProofCampaignArchive, + reconcileProofCampaignJob, + verifyFinishedConfirmationCheckpoint, +} from "./controller-proof-archive.js" +export { + fleetEvaluationResult, + fleetPayload, + fleetRuntimeDeadlineMs, + fleetWorkerHarnessExecution, + recordFleetInferenceDenials, + recordFleetInferenceUsage, + validateCandidateWorkerHarnessExecution, + waitForFleetJob, +} from "./controller-fleet.js" async function validateCurriculumOnFleet( tasks: RsiRunRecord["curriculumTasks"], @@ -1033,7 +381,7 @@ async function evaluateBaselineFleet( } } -function recoverInterruptedCandidates(run: RsiRunRecord, now: string): void { +export function recoverInterruptedCandidates(run: RsiRunRecord, now: string): void { for (const candidate of run.candidates) { if (candidate.status === "mutating" || candidate.status === "evaluating") { candidate.status = "failed" @@ -2059,3 +1407,4 @@ export async function improveMain(argv: string[]): Promise { return 1 } } +