@quickchatai/quickreview 0.0.0-stage → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +385 -2
- package/THIRD-PARTY-NOTICES.md +134 -0
- package/agents/pass-abstraction.md +64 -0
- package/agents/pass-ask-human.md +134 -0
- package/agents/pass-backcompat.md +67 -0
- package/agents/pass-baseline.md +61 -0
- package/agents/pass-behaviour-delta.md +64 -0
- package/agents/pass-bounds.md +36 -0
- package/agents/pass-claims.md +31 -0
- package/agents/pass-concurrency.md +168 -0
- package/agents/pass-consistency.md +57 -0
- package/agents/pass-contracts.md +30 -0
- package/agents/pass-data-safety.md +54 -0
- package/agents/pass-deferred.md +35 -0
- package/agents/pass-delivery.md +37 -0
- package/agents/pass-duplication.md +60 -0
- package/agents/pass-intent.md +63 -0
- package/agents/pass-lint-conformance.md +46 -0
- package/agents/pass-maintainability.md +67 -0
- package/agents/pass-placement.md +68 -0
- package/agents/pass-prod-risk.md +24 -0
- package/agents/pass-rollout.md +32 -0
- package/agents/pass-roundtrip.md +46 -0
- package/agents/pass-security.md +31 -0
- package/agents/pass-simplicity.md +68 -0
- package/agents/pass-statelessness.md +27 -0
- package/agents/pass-style.md +37 -0
- package/agents/pass-sweep.md +57 -0
- package/agents/pass-tests.md +58 -0
- package/agents/pass-ui.md +23 -0
- package/agents/pass-validate.md +50 -0
- package/agents/pass-widened.md +46 -0
- package/agents/scan/pass-scan.md +86 -0
- package/bin/qcr.js +11 -0
- package/config.example.toml +1067 -0
- package/deploy/ansible/README.md +356 -0
- package/deploy/ansible/group_vars/all.yml +212 -0
- package/deploy/ansible/inventory.example.ini +25 -0
- package/deploy/ansible/playbook.yml +360 -0
- package/deploy/collect-secrets.sh +289 -0
- package/deploy/collect-slack-secrets.mjs +1190 -0
- package/deploy/github-app-manifest.json +31 -0
- package/deploy/manifest.schema.json +75 -0
- package/deploy/qcr-update.sh +803 -0
- package/deploy/reviewer/AGENTS.override.md +12 -0
- package/dist/extensions/cli/bash-guard-ambient.js +42 -0
- package/dist/extensions/cli/bash-guard-ambient.js.map +1 -0
- package/dist/extensions/cli/bash-guard.js +5 -0
- package/dist/extensions/cli/bash-guard.js.map +1 -0
- package/dist/extensions/cli/companion-guard.js +5 -0
- package/dist/extensions/cli/companion-guard.js.map +1 -0
- package/dist/extensions/cli/debug-round.js +8 -0
- package/dist/extensions/cli/debug-round.js.map +1 -0
- package/dist/extensions/cli/inject-bridge.js +5 -0
- package/dist/extensions/cli/inject-bridge.js.map +1 -0
- package/dist/extensions/cli/ledger-pin.js +5 -0
- package/dist/extensions/cli/ledger-pin.js.map +1 -0
- package/dist/extensions/cli/lens.js +13 -0
- package/dist/extensions/cli/lens.js.map +1 -0
- package/dist/extensions/cli/muted-theme.js +5 -0
- package/dist/extensions/cli/muted-theme.js.map +1 -0
- package/dist/extensions/cli/onboarding.js +5 -0
- package/dist/extensions/cli/onboarding.js.map +1 -0
- package/dist/extensions/cli/setup.js +5 -0
- package/dist/extensions/cli/setup.js.map +1 -0
- package/dist/extensions/cli/tui-view.js +5 -0
- package/dist/extensions/cli/tui-view.js.map +1 -0
- package/dist/extensions/companion-guard.js +361 -0
- package/dist/extensions/companion-guard.js.map +1 -0
- package/dist/extensions/debug-round.js +204 -0
- package/dist/extensions/debug-round.js.map +1 -0
- package/dist/extensions/inject-bridge.js +603 -0
- package/dist/extensions/inject-bridge.js.map +1 -0
- package/dist/extensions/muted-theme.js +12 -0
- package/dist/extensions/muted-theme.js.map +1 -0
- package/dist/extensions/onboarding.js +175 -0
- package/dist/extensions/onboarding.js.map +1 -0
- package/dist/extensions/setup-replay.js +269 -0
- package/dist/extensions/setup-replay.js.map +1 -0
- package/dist/extensions/setup-scan.js +998 -0
- package/dist/extensions/setup-scan.js.map +1 -0
- package/dist/extensions/setup-triage.js +175 -0
- package/dist/extensions/setup-triage.js.map +1 -0
- package/dist/extensions/setup.js +1160 -0
- package/dist/extensions/setup.js.map +1 -0
- package/dist/extensions/tui-view.js +389 -0
- package/dist/extensions/tui-view.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/client.js +376 -0
- package/dist/extensions/vendor/openviking-pi/client.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/config.js +189 -0
- package/dist/extensions/vendor/openviking-pi/config.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/index.js +421 -0
- package/dist/extensions/vendor/openviking-pi/index.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/lib/capture-adapter.mjs +86 -0
- package/dist/extensions/vendor/openviking-pi/lib/capture-adapter.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/lib/takeover-core.mjs +408 -0
- package/dist/extensions/vendor/openviking-pi/lib/takeover-core.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs +41 -0
- package/dist/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-hint.js +290 -0
- package/dist/extensions/vendor/openviking-pi/qcr-hint.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-honest.js +148 -0
- package/dist/extensions/vendor/openviking-pi/qcr-honest.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-hooks.js +68 -0
- package/dist/extensions/vendor/openviking-pi/qcr-hooks.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-leaf.js +77 -0
- package/dist/extensions/vendor/openviking-pi/qcr-leaf.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-lesson.js +216 -0
- package/dist/extensions/vendor/openviking-pi/qcr-lesson.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-namespaces.js +154 -0
- package/dist/extensions/vendor/openviking-pi/qcr-namespaces.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-recall.js +254 -0
- package/dist/extensions/vendor/openviking-pi/qcr-recall.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-shim.js +94 -0
- package/dist/extensions/vendor/openviking-pi/qcr-shim.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-write.js +333 -0
- package/dist/extensions/vendor/openviking-pi/qcr-write.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/recall.js +108 -0
- package/dist/extensions/vendor/openviking-pi/recall.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/capture-utils.mjs +529 -0
- package/dist/extensions/vendor/openviking-pi/shared/capture-utils.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/credentials.mjs +205 -0
- package/dist/extensions/vendor/openviking-pi/shared/credentials.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/pending-queue.mjs +442 -0
- package/dist/extensions/vendor/openviking-pi/shared/pending-queue.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/profile-inject.mjs +286 -0
- package/dist/extensions/vendor/openviking-pi/shared/profile-inject.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs +189 -0
- package/dist/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/recall-core.mjs +580 -0
- package/dist/extensions/vendor/openviking-pi/shared/recall-core.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/retryable.mjs +11 -0
- package/dist/extensions/vendor/openviking-pi/shared/retryable.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/session-model.mjs +63 -0
- package/dist/extensions/vendor/openviking-pi/shared/session-model.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/uri-guard.mjs +58 -0
- package/dist/extensions/vendor/openviking-pi/shared/uri-guard.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/workspace-peer.mjs +16 -0
- package/dist/extensions/vendor/openviking-pi/shared/workspace-peer.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/sync.js +127 -0
- package/dist/extensions/vendor/openviking-pi/sync.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/takeover.js +25 -0
- package/dist/extensions/vendor/openviking-pi/takeover.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/tools.js +417 -0
- package/dist/extensions/vendor/openviking-pi/tools.js.map +1 -0
- package/dist/src/anomalies.js +483 -0
- package/dist/src/anomalies.js.map +1 -0
- package/dist/src/answerworker.js +180 -0
- package/dist/src/answerworker.js.map +1 -0
- package/dist/src/appstamp.js +68 -0
- package/dist/src/appstamp.js.map +1 -0
- package/dist/src/archive/admission.js +3 -0
- package/dist/src/archive/admission.js.map +1 -0
- package/dist/src/archive/archivedb.js +474 -0
- package/dist/src/archive/archivedb.js.map +1 -0
- package/dist/src/archive/archiver.js +1770 -0
- package/dist/src/archive/archiver.js.map +1 -0
- package/dist/src/archive/archivewrite.js +198 -0
- package/dist/src/archive/archivewrite.js.map +1 -0
- package/dist/src/archive/bundle.js +266 -0
- package/dist/src/archive/bundle.js.map +1 -0
- package/dist/src/archive/dbmove.js +638 -0
- package/dist/src/archive/dbmove.js.map +1 -0
- package/dist/src/archive/duplicatecounts.js +80 -0
- package/dist/src/archive/duplicatecounts.js.map +1 -0
- package/dist/src/archive/eligibility.js +113 -0
- package/dist/src/archive/eligibility.js.map +1 -0
- package/dist/src/archive/extract.js +288 -0
- package/dist/src/archive/extract.js.map +1 -0
- package/dist/src/archive/fsutil.js +330 -0
- package/dist/src/archive/fsutil.js.map +1 -0
- package/dist/src/archive/gzfile.js +277 -0
- package/dist/src/archive/gzfile.js.map +1 -0
- package/dist/src/archive/kinds.js +22 -0
- package/dist/src/archive/kinds.js.map +1 -0
- package/dist/src/archive/livecheckpoint.js +178 -0
- package/dist/src/archive/livecheckpoint.js.map +1 -0
- package/dist/src/archive/liveretention.js +145 -0
- package/dist/src/archive/liveretention.js.map +1 -0
- package/dist/src/archive/manifest.js +284 -0
- package/dist/src/archive/manifest.js.map +1 -0
- package/dist/src/archive/overseer.js +283 -0
- package/dist/src/archive/overseer.js.map +1 -0
- package/dist/src/archive/piglobal.js +129 -0
- package/dist/src/archive/piglobal.js.map +1 -0
- package/dist/src/archive/plan.js +398 -0
- package/dist/src/archive/plan.js.map +1 -0
- package/dist/src/archive/prarchive.js +831 -0
- package/dist/src/archive/prarchive.js.map +1 -0
- package/dist/src/archive/prfiles.js +303 -0
- package/dist/src/archive/prfiles.js.map +1 -0
- package/dist/src/archive/report.js +169 -0
- package/dist/src/archive/report.js.map +1 -0
- package/dist/src/archive/restore.js +276 -0
- package/dist/src/archive/restore.js.map +1 -0
- package/dist/src/archive/rows.js +106 -0
- package/dist/src/archive/rows.js.map +1 -0
- package/dist/src/archive/seal.js +480 -0
- package/dist/src/archive/seal.js.map +1 -0
- package/dist/src/archive/status.js +528 -0
- package/dist/src/archive/status.js.map +1 -0
- package/dist/src/archive/storepass.js +409 -0
- package/dist/src/archive/storepass.js.map +1 -0
- package/dist/src/backfill.js +217 -0
- package/dist/src/backfill.js.map +1 -0
- package/dist/src/bench/cost.js +87 -0
- package/dist/src/bench/exec.js +76 -0
- package/dist/src/bench/values.js +14 -0
- package/dist/src/botcontrol.js +127 -0
- package/dist/src/botcontrol.js.map +1 -0
- package/dist/src/botworker.js +805 -0
- package/dist/src/botworker.js.map +1 -0
- package/dist/src/capture.js +234 -0
- package/dist/src/capture.js.map +1 -0
- package/dist/src/cases.js +1491 -0
- package/dist/src/cases.js.map +1 -0
- package/dist/src/channelfs.js +27 -0
- package/dist/src/channelfs.js.map +1 -0
- package/dist/src/checklist.js +312 -0
- package/dist/src/checklist.js.map +1 -0
- package/dist/src/ciretry.js +91 -0
- package/dist/src/ciretry.js.map +1 -0
- package/dist/src/cli.js +1916 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/cliops.js +333 -0
- package/dist/src/cliops.js.map +1 -0
- package/dist/src/clonedir.js +15 -0
- package/dist/src/clonedir.js.map +1 -0
- package/dist/src/commandeffects.js +43 -0
- package/dist/src/commandeffects.js.map +1 -0
- package/dist/src/commands.js +1312 -0
- package/dist/src/commands.js.map +1 -0
- package/dist/src/companioninbox.js +203 -0
- package/dist/src/companioninbox.js.map +1 -0
- package/dist/src/compare.js +673 -0
- package/dist/src/compare.js.map +1 -0
- package/dist/src/config.js +1027 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/configcheck.js +111 -0
- package/dist/src/configcheck.js.map +1 -0
- package/dist/src/coreext.js +43 -0
- package/dist/src/coreext.js.map +1 -0
- package/dist/src/daemon.js +929 -0
- package/dist/src/daemon.js.map +1 -0
- package/dist/src/debuground.js +731 -0
- package/dist/src/debuground.js.map +1 -0
- package/dist/src/debugroundpi.js +219 -0
- package/dist/src/debugroundpi.js.map +1 -0
- package/dist/src/diagnostics.js +14 -0
- package/dist/src/diagnostics.js.map +1 -0
- package/dist/src/digest.js +139 -0
- package/dist/src/digest.js.map +1 -0
- package/dist/src/dispatch.js +1726 -0
- package/dist/src/dispatch.js.map +1 -0
- package/dist/src/engine/answerguard.js +18 -0
- package/dist/src/engine/answerguard.js.map +1 -0
- package/dist/src/engine/botsdk.js +262 -0
- package/dist/src/engine/botsdk.js.map +1 -0
- package/dist/src/engine/caserunner-sdk.js +332 -0
- package/dist/src/engine/caserunner-sdk.js.map +1 -0
- package/dist/src/engine/debugpi.js +194 -0
- package/dist/src/engine/debugpi.js.map +1 -0
- package/dist/src/eod.js +473 -0
- package/dist/src/eod.js.map +1 -0
- package/dist/src/eodpinwatch.js +391 -0
- package/dist/src/eodpinwatch.js.map +1 -0
- package/dist/src/escalation.js +1009 -0
- package/dist/src/escalation.js.map +1 -0
- package/dist/src/eventloop.js +779 -0
- package/dist/src/eventloop.js.map +1 -0
- package/dist/src/execboundary.js +430 -0
- package/dist/src/execboundary.js.map +1 -0
- package/dist/src/feedback.js +915 -0
- package/dist/src/feedback.js.map +1 -0
- package/dist/src/findingscheck.js +200 -0
- package/dist/src/findingscheck.js.map +1 -0
- package/dist/src/fsdenied.js +26 -0
- package/dist/src/fsdenied.js.map +1 -0
- package/dist/src/ghread.js +181 -0
- package/dist/src/ghread.js.map +1 -0
- package/dist/src/github.js +1063 -0
- package/dist/src/github.js.map +1 -0
- package/dist/src/homes.js +103 -0
- package/dist/src/homes.js.map +1 -0
- package/dist/src/hostroute.js +24 -0
- package/dist/src/hostroute.js.map +1 -0
- package/dist/src/improvements.js +625 -0
- package/dist/src/improvements.js.map +1 -0
- package/dist/src/install.js +946 -0
- package/dist/src/install.js.map +1 -0
- package/dist/src/installkind.js +40 -0
- package/dist/src/installkind.js.map +1 -0
- package/dist/src/ipc/capability.js +286 -0
- package/dist/src/ipc/capability.js.map +1 -0
- package/dist/src/ipc/client.js +226 -0
- package/dist/src/ipc/client.js.map +1 -0
- package/dist/src/ipc/control.js +70 -0
- package/dist/src/ipc/control.js.map +1 -0
- package/dist/src/ipc/ghreadshim.js +77 -0
- package/dist/src/ipc/ghreadshim.js.map +1 -0
- package/dist/src/ipc/launcher.js +65 -0
- package/dist/src/ipc/launcher.js.map +1 -0
- package/dist/src/ipc/permits.js +293 -0
- package/dist/src/ipc/permits.js.map +1 -0
- package/dist/src/ipc/postshim.js +137 -0
- package/dist/src/ipc/postshim.js.map +1 -0
- package/dist/src/ipc/protocol.js +239 -0
- package/dist/src/ipc/protocol.js.map +1 -0
- package/dist/src/ipc/server.js +312 -0
- package/dist/src/ipc/server.js.map +1 -0
- package/dist/src/ipc/workerspec.js +148 -0
- package/dist/src/ipc/workerspec.js.map +1 -0
- package/dist/src/learner/learner.js +1272 -0
- package/dist/src/learner/learner.js.map +1 -0
- package/dist/src/learner/miner.js +948 -0
- package/dist/src/learner/miner.js.map +1 -0
- package/dist/src/learner/packet.js +216 -0
- package/dist/src/learner/packet.js.map +1 -0
- package/dist/src/learner/receipt.js +135 -0
- package/dist/src/learner/receipt.js.map +1 -0
- package/dist/src/learner/status.js +38 -0
- package/dist/src/learner/status.js.map +1 -0
- package/dist/src/ledgercheck.js +120 -0
- package/dist/src/ledgercheck.js.map +1 -0
- package/dist/src/lessonledger.js +451 -0
- package/dist/src/lessonledger.js.map +1 -0
- package/dist/src/lessonselect.js +639 -0
- package/dist/src/lessonselect.js.map +1 -0
- package/dist/src/lessonshadow.js +540 -0
- package/dist/src/lessonshadow.js.map +1 -0
- package/dist/src/levers.js +302 -0
- package/dist/src/levers.js.map +1 -0
- package/dist/src/live/projection.js +450 -0
- package/dist/src/live/projection.js.map +1 -0
- package/dist/src/live/publish.js +83 -0
- package/dist/src/live/publish.js.map +1 -0
- package/dist/src/live/read.js +60 -0
- package/dist/src/live/read.js.map +1 -0
- package/dist/src/livecli.js +288 -0
- package/dist/src/livecli.js.map +1 -0
- package/dist/src/loginhint.js +42 -0
- package/dist/src/loginhint.js.map +1 -0
- package/dist/src/loginrefresh/atomic.js +66 -0
- package/dist/src/loginrefresh/atomic.js.map +1 -0
- package/dist/src/loginrefresh/lock.js +93 -0
- package/dist/src/loginrefresh/lock.js.map +1 -0
- package/dist/src/loginrefresh/main.js +58 -0
- package/dist/src/loginrefresh/main.js.map +1 -0
- package/dist/src/loginrefresh/pirefresh.js +49 -0
- package/dist/src/loginrefresh/pirefresh.js.map +1 -0
- package/dist/src/loginrefresh/refresher.js +286 -0
- package/dist/src/loginrefresh/refresher.js.map +1 -0
- package/dist/src/loginrefresh/supervisor.js +253 -0
- package/dist/src/loginrefresh/supervisor.js.map +1 -0
- package/dist/src/loginrefresh/types.js +4 -0
- package/dist/src/loginrefresh/types.js.map +1 -0
- package/dist/src/memory/context.js +251 -0
- package/dist/src/memory/context.js.map +1 -0
- package/dist/src/memory/datafence.js +5 -0
- package/dist/src/memory/datafence.js.map +1 -0
- package/dist/src/memory/delivery.js +131 -0
- package/dist/src/memory/delivery.js.map +1 -0
- package/dist/src/memory/eventtags.js +81 -0
- package/dist/src/memory/eventtags.js.map +1 -0
- package/dist/src/memory/health.js +713 -0
- package/dist/src/memory/health.js.map +1 -0
- package/dist/src/memory/import.js +504 -0
- package/dist/src/memory/import.js.map +1 -0
- package/dist/src/memory/layout.js +252 -0
- package/dist/src/memory/layout.js.map +1 -0
- package/dist/src/memory/legacy-file.js +100 -0
- package/dist/src/memory/legacy-file.js.map +1 -0
- package/dist/src/memory/lessons.js +370 -0
- package/dist/src/memory/lessons.js.map +1 -0
- package/dist/src/memory/ovsession.js +449 -0
- package/dist/src/memory/ovsession.js.map +1 -0
- package/dist/src/memory/relayout.js +259 -0
- package/dist/src/memory/relayout.js.map +1 -0
- package/dist/src/memory/roundactivity.js +180 -0
- package/dist/src/memory/roundactivity.js.map +1 -0
- package/dist/src/memory/selection.js +690 -0
- package/dist/src/memory/selection.js.map +1 -0
- package/dist/src/memory/shadowstatus.js +69 -0
- package/dist/src/memory/shadowstatus.js.map +1 -0
- package/dist/src/memory/store.js +83 -0
- package/dist/src/memory/store.js.map +1 -0
- package/dist/src/memory/textidentity.js +8 -0
- package/dist/src/memory/textidentity.js.map +1 -0
- package/dist/src/metrics.js +773 -0
- package/dist/src/metrics.js.map +1 -0
- package/dist/src/missingconfig.js +8 -0
- package/dist/src/missingconfig.js.map +1 -0
- package/dist/src/npmverbs.js +226 -0
- package/dist/src/npmverbs.js.map +1 -0
- package/dist/src/onboard/choices.js +191 -0
- package/dist/src/onboard/choices.js.map +1 -0
- package/dist/src/onboard/engine.js +344 -0
- package/dist/src/onboard/engine.js.map +1 -0
- package/dist/src/onboard/launch.js +83 -0
- package/dist/src/onboard/launch.js.map +1 -0
- package/dist/src/onboard/steps/appslug.js +211 -0
- package/dist/src/onboard/steps/appslug.js.map +1 -0
- package/dist/src/onboard/steps/bringup.js +408 -0
- package/dist/src/onboard/steps/bringup.js.map +1 -0
- package/dist/src/onboard/steps/compose.js +343 -0
- package/dist/src/onboard/steps/compose.js.map +1 -0
- package/dist/src/onboard/steps/configdelivery.js +50 -0
- package/dist/src/onboard/steps/configdelivery.js.map +1 -0
- package/dist/src/onboard/steps/configfill.js +1148 -0
- package/dist/src/onboard/steps/configfill.js.map +1 -0
- package/dist/src/onboard/steps/deploysh.js +80 -0
- package/dist/src/onboard/steps/deploysh.js.map +1 -0
- package/dist/src/onboard/steps/embeddings.js +472 -0
- package/dist/src/onboard/steps/embeddings.js.map +1 -0
- package/dist/src/onboard/steps/escalation.js +571 -0
- package/dist/src/onboard/steps/escalation.js.map +1 -0
- package/dist/src/onboard/steps/githubapp.js +1092 -0
- package/dist/src/onboard/steps/githubapp.js.map +1 -0
- package/dist/src/onboard/steps/golive.js +506 -0
- package/dist/src/onboard/steps/golive.js.map +1 -0
- package/dist/src/onboard/steps/index.js +67 -0
- package/dist/src/onboard/steps/index.js.map +1 -0
- package/dist/src/onboard/steps/ingress.js +762 -0
- package/dist/src/onboard/steps/ingress.js.map +1 -0
- package/dist/src/onboard/steps/inventory.js +420 -0
- package/dist/src/onboard/steps/inventory.js.map +1 -0
- package/dist/src/onboard/steps/llmauth.js +893 -0
- package/dist/src/onboard/steps/llmauth.js.map +1 -0
- package/dist/src/onboard/steps/memory.js +1060 -0
- package/dist/src/onboard/steps/memory.js.map +1 -0
- package/dist/src/onboard/steps/memorykeys.js +43 -0
- package/dist/src/onboard/steps/memorykeys.js.map +1 -0
- package/dist/src/onboard/steps/preflight.js +172 -0
- package/dist/src/onboard/steps/preflight.js.map +1 -0
- package/dist/src/onboard/steps/proof.js +502 -0
- package/dist/src/onboard/steps/proof.js.map +1 -0
- package/dist/src/onboard/steps/ssh.js +300 -0
- package/dist/src/onboard/steps/ssh.js.map +1 -0
- package/dist/src/onboard/steps/topology.js +291 -0
- package/dist/src/onboard/steps/topology.js.map +1 -0
- package/dist/src/onboard/steps/transport.js +511 -0
- package/dist/src/onboard/steps/transport.js.map +1 -0
- package/dist/src/onboard/steps/verifyup.js +362 -0
- package/dist/src/onboard/steps/verifyup.js.map +1 -0
- package/dist/src/onboard/steps/vmdeploy.js +368 -0
- package/dist/src/onboard/steps/vmdeploy.js.map +1 -0
- package/dist/src/onboard/toml.js +53 -0
- package/dist/src/onboard/toml.js.map +1 -0
- package/dist/src/opencli.js +508 -0
- package/dist/src/opencli.js.map +1 -0
- package/dist/src/openpr.js +457 -0
- package/dist/src/openpr.js.map +1 -0
- package/dist/src/openviking/client.js +821 -0
- package/dist/src/openviking/client.js.map +1 -0
- package/dist/src/openviking/context.js +218 -0
- package/dist/src/openviking/context.js.map +1 -0
- package/dist/src/openviking/embedder.js +457 -0
- package/dist/src/openviking/embedder.js.map +1 -0
- package/dist/src/openviking/gapsink.js +38 -0
- package/dist/src/openviking/gapsink.js.map +1 -0
- package/dist/src/openviking/health.js +69 -0
- package/dist/src/openviking/health.js.map +1 -0
- package/dist/src/openviking/index.js +24 -0
- package/dist/src/openviking/index.js.map +1 -0
- package/dist/src/openviking/lessonfind.js +54 -0
- package/dist/src/openviking/lessonfind.js.map +1 -0
- package/dist/src/openviking/probe.js +89 -0
- package/dist/src/openviking/probe.js.map +1 -0
- package/dist/src/openviking/proc.js +95 -0
- package/dist/src/openviking/proc.js.map +1 -0
- package/dist/src/openviking/provisionreport.js +144 -0
- package/dist/src/openviking/provisionreport.js.map +1 -0
- package/dist/src/openviking/reembed.js +317 -0
- package/dist/src/openviking/reembed.js.map +1 -0
- package/dist/src/openviking/relayout.js +153 -0
- package/dist/src/openviking/relayout.js.map +1 -0
- package/dist/src/openviking/sidecar-main.js +39 -0
- package/dist/src/openviking/sidecar-main.js.map +1 -0
- package/dist/src/openviking/sidecar.js +79 -0
- package/dist/src/openviking/sidecar.js.map +1 -0
- package/dist/src/openviking/store.js +1758 -0
- package/dist/src/openviking/store.js.map +1 -0
- package/dist/src/openviking/supervisor.js +422 -0
- package/dist/src/openviking/supervisor.js.map +1 -0
- package/dist/src/openviking/types.js +5 -0
- package/dist/src/openviking/types.js.map +1 -0
- package/dist/src/operator-recovery.js +172 -0
- package/dist/src/operator-recovery.js.map +1 -0
- package/dist/src/operatormessage.js +44 -0
- package/dist/src/operatormessage.js.map +1 -0
- package/dist/src/outcomes.js +1361 -0
- package/dist/src/outcomes.js.map +1 -0
- package/dist/src/piauthcopy.js +5 -0
- package/dist/src/piauthcopy.js.map +1 -0
- package/dist/src/picli.js +45 -0
- package/dist/src/picli.js.map +1 -0
- package/dist/src/picwd.js +25 -0
- package/dist/src/picwd.js.map +1 -0
- package/dist/src/pilogin.js +35 -0
- package/dist/src/pilogin.js.map +1 -0
- package/dist/src/pingpong.js +1028 -0
- package/dist/src/pingpong.js.map +1 -0
- package/dist/src/posting-journal.js +441 -0
- package/dist/src/posting-journal.js.map +1 -0
- package/dist/src/posting-outbox.js +662 -0
- package/dist/src/posting-outbox.js.map +1 -0
- package/dist/src/posting-receipt.js +203 -0
- package/dist/src/posting-receipt.js.map +1 -0
- package/dist/src/posting.js +348 -0
- package/dist/src/posting.js.map +1 -0
- package/dist/src/postops.js +637 -0
- package/dist/src/postops.js.map +1 -0
- package/dist/src/pr-activity.js +11 -0
- package/dist/src/pr-activity.js.map +1 -0
- package/dist/src/pr-author.js +5 -0
- package/dist/src/pr-author.js.map +1 -0
- package/dist/src/pr-conversation.js +355 -0
- package/dist/src/pr-conversation.js.map +1 -0
- package/dist/src/pr-session-lifecycle.js +542 -0
- package/dist/src/pr-session-lifecycle.js.map +1 -0
- package/dist/src/preflight.js +341 -0
- package/dist/src/preflight.js.map +1 -0
- package/dist/src/priorresolve.js +275 -0
- package/dist/src/priorresolve.js.map +1 -0
- package/dist/src/qcrbin.js +180 -0
- package/dist/src/qcrbin.js.map +1 -0
- package/dist/src/recheck.js +1398 -0
- package/dist/src/recheck.js.map +1 -0
- package/dist/src/reconcile.js +820 -0
- package/dist/src/reconcile.js.map +1 -0
- package/dist/src/recordedfindings.js +15 -0
- package/dist/src/recordedfindings.js.map +1 -0
- package/dist/src/regression.js +546 -0
- package/dist/src/regression.js.map +1 -0
- package/dist/src/replay.js +598 -0
- package/dist/src/replay.js.map +1 -0
- package/dist/src/requiredpasses.js +113 -0
- package/dist/src/requiredpasses.js.map +1 -0
- package/dist/src/resumecheck.js +168 -0
- package/dist/src/resumecheck.js.map +1 -0
- package/dist/src/reviewcli.js +671 -0
- package/dist/src/reviewcli.js.map +1 -0
- package/dist/src/reviewcontext.js +22 -0
- package/dist/src/reviewcontext.js.map +1 -0
- package/dist/src/reviewdetach.js +574 -0
- package/dist/src/reviewdetach.js.map +1 -0
- package/dist/src/reviewhistory.js +284 -0
- package/dist/src/reviewhistory.js.map +1 -0
- package/dist/src/reviewhome.js +26 -0
- package/dist/src/reviewhome.js.map +1 -0
- package/dist/src/reviewmemory.js +110 -0
- package/dist/src/reviewmemory.js.map +1 -0
- package/dist/src/reviewrendercli.js +104 -0
- package/dist/src/reviewrendercli.js.map +1 -0
- package/dist/src/round-work.js +20 -0
- package/dist/src/round-work.js.map +1 -0
- package/dist/src/runhost.js +72 -0
- package/dist/src/runhost.js.map +1 -0
- package/dist/src/scanauth.js +25 -0
- package/dist/src/scanauth.js.map +1 -0
- package/dist/src/scanbox.js +846 -0
- package/dist/src/scanbox.js.map +1 -0
- package/dist/src/scancli.js +1074 -0
- package/dist/src/scancli.js.map +1 -0
- package/dist/src/scandriverlog.js +30 -0
- package/dist/src/scandriverlog.js.map +1 -0
- package/dist/src/session-bridge.js +75 -0
- package/dist/src/session-bridge.js.map +1 -0
- package/dist/src/session-command-payload.js +29 -0
- package/dist/src/session-command-payload.js.map +1 -0
- package/dist/src/session-command-supervisor.js +939 -0
- package/dist/src/session-command-supervisor.js.map +1 -0
- package/dist/src/sessionjsonl.js +85 -0
- package/dist/src/sessionjsonl.js.map +1 -0
- package/dist/src/sessions.js +541 -0
- package/dist/src/sessions.js.map +1 -0
- package/dist/src/setup/adapters.js +330 -0
- package/dist/src/setup/adapters.js.map +1 -0
- package/dist/src/setup/baselines.js +64 -0
- package/dist/src/setup/baselines.js.map +1 -0
- package/dist/src/setup/caserunner.js +554 -0
- package/dist/src/setup/caserunner.js.map +1 -0
- package/dist/src/setup/configcommit.js +358 -0
- package/dist/src/setup/configcommit.js.map +1 -0
- package/dist/src/setup/engine.js +672 -0
- package/dist/src/setup/engine.js.map +1 -0
- package/dist/src/setup/facts.js +593 -0
- package/dist/src/setup/facts.js.map +1 -0
- package/dist/src/setup/findings.js +948 -0
- package/dist/src/setup/findings.js.map +1 -0
- package/dist/src/setup/history.js +338 -0
- package/dist/src/setup/history.js.map +1 -0
- package/dist/src/setup/interview.js +185 -0
- package/dist/src/setup/interview.js.map +1 -0
- package/dist/src/setup/ledger.js +207 -0
- package/dist/src/setup/ledger.js.map +1 -0
- package/dist/src/setup/pins.js +43 -0
- package/dist/src/setup/pins.js.map +1 -0
- package/dist/src/setup/portrait.js +432 -0
- package/dist/src/setup/portrait.js.map +1 -0
- package/dist/src/setup/presentation.js +9 -0
- package/dist/src/setup/presentation.js.map +1 -0
- package/dist/src/setup/profile.js +417 -0
- package/dist/src/setup/profile.js.map +1 -0
- package/dist/src/setup/replay.js +240 -0
- package/dist/src/setup/replay.js.map +1 -0
- package/dist/src/setup/replaybuild.js +311 -0
- package/dist/src/setup/replaybuild.js.map +1 -0
- package/dist/src/setup/replayrun.js +806 -0
- package/dist/src/setup/replayrun.js.map +1 -0
- package/dist/src/setup/replaysource.js +169 -0
- package/dist/src/setup/replaysource.js.map +1 -0
- package/dist/src/setup/retune.js +333 -0
- package/dist/src/setup/retune.js.map +1 -0
- package/dist/src/setup/retunelog.js +51 -0
- package/dist/src/setup/retunelog.js.map +1 -0
- package/dist/src/setup/scan.js +725 -0
- package/dist/src/setup/scan.js.map +1 -0
- package/dist/src/setup/scanbuild.js +133 -0
- package/dist/src/setup/scanbuild.js.map +1 -0
- package/dist/src/setup/scanfacts.js +96 -0
- package/dist/src/setup/scanfacts.js.map +1 -0
- package/dist/src/setup/scanlease.js +251 -0
- package/dist/src/setup/scanlease.js.map +1 -0
- package/dist/src/setup/scanreport.js +289 -0
- package/dist/src/setup/scanreport.js.map +1 -0
- package/dist/src/setup/scanrun.js +1733 -0
- package/dist/src/setup/scanrun.js.map +1 -0
- package/dist/src/setup/scantree.js +257 -0
- package/dist/src/setup/scantree.js.map +1 -0
- package/dist/src/setup/stage2.js +617 -0
- package/dist/src/setup/stage2.js.map +1 -0
- package/dist/src/setup/stage2request.js +259 -0
- package/dist/src/setup/stage2request.js.map +1 -0
- package/dist/src/setup/survey.js +307 -0
- package/dist/src/setup/survey.js.map +1 -0
- package/dist/src/setup/surveyrun.js +549 -0
- package/dist/src/setup/surveyrun.js.map +1 -0
- package/dist/src/setup/textsniff.js +89 -0
- package/dist/src/setup/textsniff.js.map +1 -0
- package/dist/src/setup/trackersink.js +395 -0
- package/dist/src/setup/trackersink.js.map +1 -0
- package/dist/src/setup/triage.js +636 -0
- package/dist/src/setup/triage.js.map +1 -0
- package/dist/src/setup/tuning.js +585 -0
- package/dist/src/setup/tuning.js.map +1 -0
- package/dist/src/setup/unitcut.js +437 -0
- package/dist/src/setup/unitcut.js.map +1 -0
- package/dist/src/singleflight.js +72 -0
- package/dist/src/singleflight.js.map +1 -0
- package/dist/src/slack/announce.js +725 -0
- package/dist/src/slack/announce.js.map +1 -0
- package/dist/src/slack/api.js +227 -0
- package/dist/src/slack/api.js.map +1 -0
- package/dist/src/slack/messages.js +158 -0
- package/dist/src/slack/messages.js.map +1 -0
- package/dist/src/slack/origin.js +550 -0
- package/dist/src/slack/origin.js.map +1 -0
- package/dist/src/slack/policy.js +225 -0
- package/dist/src/slack/policy.js.map +1 -0
- package/dist/src/slack/reply.js +153 -0
- package/dist/src/slack/reply.js.map +1 -0
- package/dist/src/slack/router.js +189 -0
- package/dist/src/slack/router.js.map +1 -0
- package/dist/src/slack/socket.js +323 -0
- package/dist/src/slack/socket.js.map +1 -0
- package/dist/src/slack/types.js +11 -0
- package/dist/src/slack/types.js.map +1 -0
- package/dist/src/sqlitehint.js +30 -0
- package/dist/src/sqlitehint.js.map +1 -0
- package/dist/src/ssh-observation.js +25 -0
- package/dist/src/ssh-observation.js.map +1 -0
- package/dist/src/state.js +5869 -0
- package/dist/src/state.js.map +1 -0
- package/dist/src/statemachine.js +220 -0
- package/dist/src/statemachine.js.map +1 -0
- package/dist/src/stubs.js +52 -0
- package/dist/src/stubs.js.map +1 -0
- package/dist/src/takeover.js +506 -0
- package/dist/src/takeover.js.map +1 -0
- package/dist/src/takeoverpi.js +249 -0
- package/dist/src/takeoverpi.js.map +1 -0
- package/dist/src/terminal-failure.js +26 -0
- package/dist/src/terminal-failure.js.map +1 -0
- package/dist/src/thread-resolution-refusal.js +4 -0
- package/dist/src/thread-resolution-refusal.js.map +1 -0
- package/dist/src/tmux.js +126 -0
- package/dist/src/tmux.js.map +1 -0
- package/dist/src/trackerid.js +43 -0
- package/dist/src/trackerid.js.map +1 -0
- package/dist/src/transcriptindex.js +512 -0
- package/dist/src/transcriptindex.js.map +1 -0
- package/dist/src/tui/actions.js +404 -0
- package/dist/src/tui/actions.js.map +1 -0
- package/dist/src/tui/alerts.js +523 -0
- package/dist/src/tui/alerts.js.map +1 -0
- package/dist/src/tui/app.js +1835 -0
- package/dist/src/tui/app.js.map +1 -0
- package/dist/src/tui/archiveops.js +164 -0
- package/dist/src/tui/archiveops.js.map +1 -0
- package/dist/src/tui/autofile.js +191 -0
- package/dist/src/tui/autofile.js.map +1 -0
- package/dist/src/tui/board.js +1143 -0
- package/dist/src/tui/board.js.map +1 -0
- package/dist/src/tui/candidates.js +699 -0
- package/dist/src/tui/candidates.js.map +1 -0
- package/dist/src/tui/cellwidth.js +151 -0
- package/dist/src/tui/cellwidth.js.map +1 -0
- package/dist/src/tui/changelog-store.js +404 -0
- package/dist/src/tui/changelog-store.js.map +1 -0
- package/dist/src/tui/chord.js +26 -0
- package/dist/src/tui/chord.js.map +1 -0
- package/dist/src/tui/clipboard.js +81 -0
- package/dist/src/tui/clipboard.js.map +1 -0
- package/dist/src/tui/cmdbar.js +253 -0
- package/dist/src/tui/cmdbar.js.map +1 -0
- package/dist/src/tui/companion.js +1275 -0
- package/dist/src/tui/companion.js.map +1 -0
- package/dist/src/tui/companionplan.js +200 -0
- package/dist/src/tui/companionplan.js.map +1 -0
- package/dist/src/tui/companiontab.js +61 -0
- package/dist/src/tui/companiontab.js.map +1 -0
- package/dist/src/tui/confighome.js +105 -0
- package/dist/src/tui/confighome.js.map +1 -0
- package/dist/src/tui/connect.js +44 -0
- package/dist/src/tui/connect.js.map +1 -0
- package/dist/src/tui/deploy-ceremony.js +75 -0
- package/dist/src/tui/deploy-ceremony.js.map +1 -0
- package/dist/src/tui/deploy-drain.js +93 -0
- package/dist/src/tui/deploy-drain.js.map +1 -0
- package/dist/src/tui/deploy-process.js +234 -0
- package/dist/src/tui/deploy-process.js.map +1 -0
- package/dist/src/tui/deploytab.js +1263 -0
- package/dist/src/tui/deploytab.js.map +1 -0
- package/dist/src/tui/design.js +111 -0
- package/dist/src/tui/design.js.map +1 -0
- package/dist/src/tui/digesttab.js +148 -0
- package/dist/src/tui/digesttab.js.map +1 -0
- package/dist/src/tui/emptystate.js +32 -0
- package/dist/src/tui/emptystate.js.map +1 -0
- package/dist/src/tui/evidence.js +790 -0
- package/dist/src/tui/evidence.js.map +1 -0
- package/dist/src/tui/feed.js +415 -0
- package/dist/src/tui/feed.js.map +1 -0
- package/dist/src/tui/feedbacktab.js +907 -0
- package/dist/src/tui/feedbacktab.js.map +1 -0
- package/dist/src/tui/filters.js +72 -0
- package/dist/src/tui/filters.js.map +1 -0
- package/dist/src/tui/ghread.js +272 -0
- package/dist/src/tui/ghread.js.map +1 -0
- package/dist/src/tui/historypull.js +61 -0
- package/dist/src/tui/historypull.js.map +1 -0
- package/dist/src/tui/inboxpull.js +197 -0
- package/dist/src/tui/inboxpull.js.map +1 -0
- package/dist/src/tui/index.js +1425 -0
- package/dist/src/tui/index.js.map +1 -0
- package/dist/src/tui/issuesink.js +206 -0
- package/dist/src/tui/issuesink.js.map +1 -0
- package/dist/src/tui/jsonfile.js +32 -0
- package/dist/src/tui/jsonfile.js.map +1 -0
- package/dist/src/tui/keys.js +97 -0
- package/dist/src/tui/keys.js.map +1 -0
- package/dist/src/tui/layout.js +70 -0
- package/dist/src/tui/layout.js.map +1 -0
- package/dist/src/tui/learning.js +469 -0
- package/dist/src/tui/learning.js.map +1 -0
- package/dist/src/tui/lessonswitch.js +103 -0
- package/dist/src/tui/lessonswitch.js.map +1 -0
- package/dist/src/tui/livecontrol.js +167 -0
- package/dist/src/tui/livecontrol.js.map +1 -0
- package/dist/src/tui/memforward.js +310 -0
- package/dist/src/tui/memforward.js.map +1 -0
- package/dist/src/tui/memincident.js +287 -0
- package/dist/src/tui/memincident.js.map +1 -0
- package/dist/src/tui/memory.js +485 -0
- package/dist/src/tui/memory.js.map +1 -0
- package/dist/src/tui/memtab.js +513 -0
- package/dist/src/tui/memtab.js.map +1 -0
- package/dist/src/tui/metricstab.js +979 -0
- package/dist/src/tui/metricstab.js.map +1 -0
- package/dist/src/tui/model.js +104 -0
- package/dist/src/tui/model.js.map +1 -0
- package/dist/src/tui/mouse.js +83 -0
- package/dist/src/tui/mouse.js.map +1 -0
- package/dist/src/tui/msg.js +196 -0
- package/dist/src/tui/msg.js.map +1 -0
- package/dist/src/tui/nativecompanion.js +321 -0
- package/dist/src/tui/nativecompanion.js.map +1 -0
- package/dist/src/tui/openurl.js +17 -0
- package/dist/src/tui/openurl.js.map +1 -0
- package/dist/src/tui/paint.js +501 -0
- package/dist/src/tui/paint.js.map +1 -0
- package/dist/src/tui/panes.js +74 -0
- package/dist/src/tui/panes.js.map +1 -0
- package/dist/src/tui/pipane.js +166 -0
- package/dist/src/tui/pipane.js.map +1 -0
- package/dist/src/tui/poll-hooks.js +11 -0
- package/dist/src/tui/poll-hooks.js.map +1 -0
- package/dist/src/tui/poll.js +113 -0
- package/dist/src/tui/poll.js.map +1 -0
- package/dist/src/tui/proposals.js +715 -0
- package/dist/src/tui/proposals.js.map +1 -0
- package/dist/src/tui/prpause.js +132 -0
- package/dist/src/tui/prpause.js.map +1 -0
- package/dist/src/tui/reload.js +351 -0
- package/dist/src/tui/reload.js.map +1 -0
- package/dist/src/tui/remote.js +312 -0
- package/dist/src/tui/remote.js.map +1 -0
- package/dist/src/tui/rollover.js +767 -0
- package/dist/src/tui/rollover.js.map +1 -0
- package/dist/src/tui/scantab.js +1053 -0
- package/dist/src/tui/scantab.js.map +1 -0
- package/dist/src/tui/setupentry.js +81 -0
- package/dist/src/tui/setupentry.js.map +1 -0
- package/dist/src/tui/sidebar.js +602 -0
- package/dist/src/tui/sidebar.js.map +1 -0
- package/dist/src/tui/sshtriage.js +393 -0
- package/dist/src/tui/sshtriage.js.map +1 -0
- package/dist/src/tui/startup.js +172 -0
- package/dist/src/tui/startup.js.map +1 -0
- package/dist/src/tui/tabs.js +41 -0
- package/dist/src/tui/tabs.js.map +1 -0
- package/dist/src/tui/termmirror.js +217 -0
- package/dist/src/tui/termmirror.js.map +1 -0
- package/dist/src/tui/theme.js +95 -0
- package/dist/src/tui/theme.js.map +1 -0
- package/dist/src/tui/ticketstab.js +224 -0
- package/dist/src/tui/ticketstab.js.map +1 -0
- package/dist/src/tui/tmuxsocket.js +101 -0
- package/dist/src/tui/tmuxsocket.js.map +1 -0
- package/dist/src/tui/trackertickets.js +77 -0
- package/dist/src/tui/trackertickets.js.map +1 -0
- package/dist/src/tui/unwedge.js +245 -0
- package/dist/src/tui/unwedge.js.map +1 -0
- package/dist/src/tui/viewer.js +479 -0
- package/dist/src/tui/viewer.js.map +1 -0
- package/dist/src/tui/viewerinput.js +125 -0
- package/dist/src/tui/viewerinput.js.map +1 -0
- package/dist/src/updatenotice.js +143 -0
- package/dist/src/updatenotice.js.map +1 -0
- package/dist/src/usage.js +1007 -0
- package/dist/src/usage.js.map +1 -0
- package/dist/src/verdicts.js +3848 -0
- package/dist/src/verdicts.js.map +1 -0
- package/dist/src/version.js +58 -0
- package/dist/src/version.js.map +1 -0
- package/dist/src/wakes.js +411 -0
- package/dist/src/wakes.js.map +1 -0
- package/dist/src/webhook.js +725 -0
- package/dist/src/webhook.js.map +1 -0
- package/dist/src/webhookheal.js +238 -0
- package/dist/src/webhookheal.js.map +1 -0
- package/dist/src/wire.js +1801 -0
- package/dist/src/wire.js.map +1 -0
- package/dist/src/worker-session-host.js +531 -0
- package/dist/src/worker-session-host.js.map +1 -0
- package/dist/src/workerbreaker.js +112 -0
- package/dist/src/workerbreaker.js.map +1 -0
- package/dist/src/workercontrol.js +49 -0
- package/dist/src/workercontrol.js.map +1 -0
- package/dist/src/workerlease.js +351 -0
- package/dist/src/workerlease.js.map +1 -0
- package/dist/src/workerliveness.js +84 -0
- package/dist/src/workerliveness.js.map +1 -0
- package/dist/src/workerlog.js +65 -0
- package/dist/src/workerlog.js.map +1 -0
- package/dist/src/workerruntime.js +10 -0
- package/dist/src/workerruntime.js.map +1 -0
- package/dist/src/workersupervisor.js +1206 -0
- package/dist/src/workersupervisor.js.map +1 -0
- package/dist/src/workerusage.js +23 -0
- package/dist/src/workerusage.js.map +1 -0
- package/dist/src/workerworld.js +332 -0
- package/dist/src/workerworld.js.map +1 -0
- package/dist/src/worktrees.js +984 -0
- package/dist/src/worktrees.js.map +1 -0
- package/docker/Dockerfile +284 -0
- package/docker/Dockerfile.dockerignore +35 -0
- package/docker/README.md +221 -0
- package/docker/companion/Dockerfile +139 -0
- package/docker/companion/agents/evidence.md +34 -0
- package/docker/companion/agents/explore.md +26 -0
- package/docker/companion/agents/pass-survey.md +49 -0
- package/docker/companion/agents/study.md +26 -0
- package/docker/companion/entrypoint.sh +343 -0
- package/docker/companion/memory-health.mjs +269 -0
- package/docker/entrypoint.sh +782 -0
- package/docker/memory/Dockerfile +191 -0
- package/docker/memory/Dockerfile.dockerignore +20 -0
- package/docker/memory/README.md +60 -0
- package/docker/memory/entrypoint.sh +163 -0
- package/docker/setid/qcr-setid.c +109 -0
- package/docker/tunnel/Dockerfile.cloudflared +51 -0
- package/docker/tunnel/Dockerfile.ngrok +40 -0
- package/docker/tunnel/sidecar.mjs +411 -0
- package/docker/vm/tmux.conf +26 -0
- package/docker-compose.yml +348 -0
- package/extensions/cli/bash-guard-ambient.ts +71 -0
- package/extensions/cli/bash-guard.ts +12 -0
- package/extensions/cli/companion-guard.ts +8 -0
- package/extensions/cli/debug-round.ts +9 -0
- package/extensions/cli/inject-bridge.ts +8 -0
- package/extensions/cli/ledger-pin.ts +13 -0
- package/extensions/cli/lens.ts +22 -0
- package/extensions/cli/muted-theme.ts +9 -0
- package/extensions/cli/onboarding.ts +8 -0
- package/extensions/cli/setup.ts +8 -0
- package/extensions/cli/tui-view.ts +8 -0
- package/extensions/companion-guard.ts +411 -0
- package/extensions/debug-round.ts +281 -0
- package/extensions/inject-bridge.ts +630 -0
- package/extensions/inline-bash.ts +102 -0
- package/extensions/muted-theme.json +68 -0
- package/extensions/muted-theme.ts +23 -0
- package/extensions/onboarding.ts +233 -0
- package/extensions/setup-replay.ts +336 -0
- package/extensions/setup-scan.ts +1236 -0
- package/extensions/setup-triage.ts +190 -0
- package/extensions/setup.ts +1428 -0
- package/extensions/tui-view.ts +489 -0
- package/extensions/vendor/openviking-pi/LICENSE +201 -0
- package/extensions/vendor/openviking-pi/NOTICE +68 -0
- package/extensions/vendor/openviking-pi/README.md +550 -0
- package/extensions/vendor/openviking-pi/client.ts +588 -0
- package/extensions/vendor/openviking-pi/config.json +28 -0
- package/extensions/vendor/openviking-pi/config.ts +271 -0
- package/extensions/vendor/openviking-pi/index.ts +523 -0
- package/extensions/vendor/openviking-pi/lib/capture-adapter.d.mts +10 -0
- package/extensions/vendor/openviking-pi/lib/capture-adapter.mjs +88 -0
- package/extensions/vendor/openviking-pi/lib/takeover-core.d.mts +80 -0
- package/extensions/vendor/openviking-pi/lib/takeover-core.mjs +406 -0
- package/extensions/vendor/openviking-pi/lib/uri-guard-adapter.d.mts +1 -0
- package/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs +42 -0
- package/extensions/vendor/openviking-pi/patches/0000-vendoring-and-build-conformance.patch +726 -0
- package/extensions/vendor/openviking-pi/patches/0001-remove-session-before-compact-handler.patch +101 -0
- package/extensions/vendor/openviking-pi/patches/0002-no-ambient-work-in-subagent-leaves.patch +202 -0
- package/extensions/vendor/openviking-pi/patches/0003-never-auto-create-openviking-sessions.patch +120 -0
- package/extensions/vendor/openviking-pi/patches/0004-viking-write-durable-batch-write.patch +521 -0
- package/extensions/vendor/openviking-pi/patches/0005-second-memory-namespace.patch +786 -0
- package/extensions/vendor/openviking-pi/patches/0006-writes-that-stick-and-honest-errors.patch +996 -0
- package/extensions/vendor/openviking-pi/patches/0007-bounded-recall-no-commit.patch +794 -0
- package/extensions/vendor/openviking-pi/patches/0008-pr-derived-recall.patch +379 -0
- package/extensions/vendor/openviking-pi/patches/0009-pr-session-tools-in-round-areas.patch +375 -0
- package/extensions/vendor/openviking-pi/patches/0010-typed-lessons.patch +448 -0
- package/extensions/vendor/openviking-pi/patches/0011-review-session-memory-boundary.patch +382 -0
- package/extensions/vendor/openviking-pi/patches/0012-typecheck-against-the-real-pi.patch +129 -0
- package/extensions/vendor/openviking-pi/patches/0013-createopenviking-factory.patch +1168 -0
- package/extensions/vendor/openviking-pi/qcr-hint.ts +313 -0
- package/extensions/vendor/openviking-pi/qcr-honest.ts +183 -0
- package/extensions/vendor/openviking-pi/qcr-hooks.ts +87 -0
- package/extensions/vendor/openviking-pi/qcr-leaf.ts +78 -0
- package/extensions/vendor/openviking-pi/qcr-lesson.ts +231 -0
- package/extensions/vendor/openviking-pi/qcr-namespaces.ts +198 -0
- package/extensions/vendor/openviking-pi/qcr-recall.ts +288 -0
- package/extensions/vendor/openviking-pi/qcr-shim.ts +142 -0
- package/extensions/vendor/openviking-pi/qcr-write.ts +427 -0
- package/extensions/vendor/openviking-pi/recall.ts +149 -0
- package/extensions/vendor/openviking-pi/shared/capture-utils.d.mts +9 -0
- package/extensions/vendor/openviking-pi/shared/capture-utils.mjs +515 -0
- package/extensions/vendor/openviking-pi/shared/credentials.d.mts +14 -0
- package/extensions/vendor/openviking-pi/shared/credentials.mjs +228 -0
- package/extensions/vendor/openviking-pi/shared/pending-queue.d.mts +19 -0
- package/extensions/vendor/openviking-pi/shared/pending-queue.mjs +458 -0
- package/extensions/vendor/openviking-pi/shared/profile-inject.d.mts +17 -0
- package/extensions/vendor/openviking-pi/shared/profile-inject.mjs +294 -0
- package/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs +195 -0
- package/extensions/vendor/openviking-pi/shared/recall-core.d.mts +13 -0
- package/extensions/vendor/openviking-pi/shared/recall-core.mjs +609 -0
- package/extensions/vendor/openviking-pi/shared/retryable.mjs +9 -0
- package/extensions/vendor/openviking-pi/shared/session-model.d.mts +3 -0
- package/extensions/vendor/openviking-pi/shared/session-model.mjs +57 -0
- package/extensions/vendor/openviking-pi/shared/uri-guard.mjs +56 -0
- package/extensions/vendor/openviking-pi/shared/workspace-peer.d.mts +5 -0
- package/extensions/vendor/openviking-pi/shared/workspace-peer.mjs +16 -0
- package/extensions/vendor/openviking-pi/sync.ts +163 -0
- package/extensions/vendor/openviking-pi/takeover.ts +36 -0
- package/extensions/vendor/openviking-pi/tools.ts +479 -0
- package/extensions/vendor/openviking-pi/upstream.json +48 -0
- package/package.json +138 -4
- package/profiles/default/README.md +21 -0
- package/profiles/default/addenda/.gitkeep +0 -0
- package/profiles/default/profile.toml +17 -0
- package/profiles/example-django/README.md +21 -0
- package/profiles/example-django/addenda/10-migration-discipline.md +13 -0
- package/profiles/example-django/addenda/20-new-column-nullability.md +12 -0
- package/profiles/example-django/addenda/30-test-doctrine.md +12 -0
- package/profiles/example-django/addenda/40-orm-usage.md +19 -0
- package/profiles/example-django/addenda/50-declared-shape.md +10 -0
- package/profiles/example-django/addenda/60-config-placement.md +14 -0
- package/profiles/example-django/heuristics.toml +24 -0
- package/profiles/example-django/profile.toml +9 -0
- package/profiles/fast/README.md +24 -0
- package/profiles/fast/profile.toml +59 -0
- package/profiles/minimal/README.md +17 -0
- package/profiles/minimal/addenda/.gitkeep +0 -0
- package/profiles/minimal/profile.toml +8 -0
- package/profiles/snappy/README.md +24 -0
- package/profiles/snappy/addenda/10-race-as-question.md +6 -0
- package/profiles/snappy/profile.toml +46 -0
- package/prompts/companion-day-open.md +46 -0
- package/prompts/companion-first-run.md +104 -0
- package/prompts/companion.md +821 -0
- package/prompts/conversation-system.md +117 -0
- package/prompts/eod-sweep.md +120 -0
- package/prompts/onboarding.md +74 -0
- package/prompts/pass-common.md +52 -0
- package/prompts/pass-quality-common.md +22 -0
- package/prompts/round-first.md +874 -0
- package/prompts/round-follow-up.md +991 -0
- package/prompts/round-re-review.md +889 -0
- package/prompts/round-recheck.md +196 -0
- package/prompts/round-staleness.md +856 -0
- package/schemas/review-result.v1.json +391 -0
- package/schemas/scan-result.v1.json +421 -0
- package/schemas/version.v1.json +57 -0
- package/scripts/approve-pr.sh +16 -0
- package/scripts/build-stamp.mjs +67 -0
- package/scripts/commit-verdict.sh +155 -0
- package/scripts/companion-e2e.sh +250 -0
- package/scripts/companion-memory-forward.mjs +1966 -0
- package/scripts/config-fingerprint.sh +28 -0
- package/scripts/deploy-manifest.mjs +141 -0
- package/scripts/deploy.sh +1435 -0
- package/scripts/directory-lock.mjs +193 -0
- package/scripts/heartbeat-fresh.mjs +54 -0
- package/scripts/install-locator.sh +328 -0
- package/scripts/install-wrapper.sh +76 -0
- package/scripts/memory-search-check.mjs +98 -0
- package/scripts/mint-token.sh +212 -0
- package/scripts/openviking-embedder.py +392 -0
- package/scripts/openviking-ov-conf.mjs +161 -0
- package/scripts/openviking-provision.mjs +1122 -0
- package/scripts/openviking-reembed.mjs +1154 -0
- package/scripts/post-review.sh +16 -0
- package/scripts/pr-checks.sh +256 -0
- package/scripts/pr-context.py +174 -0
- package/scripts/pr-context.sh +63 -0
- package/scripts/pr-self.sh +144 -0
- package/scripts/pr-threads.sh +145 -0
- package/scripts/qcr +15 -0
- package/scripts/qcr-host.sh +769 -0
- package/scripts/render-review.sh +24 -0
- package/scripts/reply-thread.sh +16 -0
- package/scripts/required-passes.py +112 -0
- package/scripts/smoke-companion.sh +211 -0
- package/scripts/tui-deploy-runner.mjs +61 -0
- package/scripts/verify-dist.mjs +201 -0
- package/scripts/vm-ghcr-login.sh +188 -0
- package/seeds/agents/pass-baseline.md +61 -0
- package/seeds/agents/pass-contracts.md +30 -0
- package/seeds/agents/pass-data-safety.md +54 -0
- package/seeds/agents/pass-delivery.md +37 -0
- package/seeds/agents/pass-prod-risk.md +24 -0
- package/seeds/agents/pass-security.md +31 -0
- package/seeds/agents/pass-statelessness.md +27 -0
- package/seeds/agents/pass-ui.md +23 -0
- package/seeds/agents/pass-validate.md +50 -0
- package/seeds/prompts/conversation-system.md +117 -0
- package/seeds/prompts/eod-sweep.md +120 -0
- package/seeds/prompts/pass-common.md +52 -0
- package/skills/companion/adjudicate/SKILL.md +247 -0
- package/skills/companion/evidence/SKILL.md +150 -0
- package/skills/companion/land/SKILL.md +200 -0
- package/skills/companion/replay/SKILL.md +138 -0
- package/skills/companion/setup/SKILL.md +167 -0
- package/skills/companion/survey/SKILL.md +160 -0
- package/skills/selfimprove/README.md +34 -0
- package/skills/selfimprove/SKILL.md +127 -0
- package/skills/selfimprove/agents/openai.yaml +2 -0
- package/skills/selfimprove/commands/selfimprove.md +6 -0
- package/skills/selfimprove/references/fix-loop.md +49 -0
- package/skills/selfimprove/references/ledger.md +20 -0
- package/skills/selfimprove/references/setup.md +40 -0
- package/src/anomalies.ts +566 -0
- package/src/answerworker.ts +251 -0
- package/src/appstamp.ts +65 -0
- package/src/archive/admission.ts +3 -0
- package/src/archive/archivedb.ts +548 -0
- package/src/archive/archiver.ts +1963 -0
- package/src/archive/archivewrite.ts +279 -0
- package/src/archive/bundle.ts +333 -0
- package/src/archive/dbmove.ts +911 -0
- package/src/archive/duplicatecounts.ts +98 -0
- package/src/archive/eligibility.ts +181 -0
- package/src/archive/extract.ts +342 -0
- package/src/archive/fsutil.ts +415 -0
- package/src/archive/gzfile.ts +370 -0
- package/src/archive/kinds.ts +21 -0
- package/src/archive/livecheckpoint.ts +217 -0
- package/src/archive/liveretention.ts +180 -0
- package/src/archive/manifest.ts +519 -0
- package/src/archive/overseer.ts +336 -0
- package/src/archive/piglobal.ts +174 -0
- package/src/archive/plan.ts +612 -0
- package/src/archive/prarchive.ts +984 -0
- package/src/archive/prfiles.ts +406 -0
- package/src/archive/report.ts +252 -0
- package/src/archive/restore.ts +330 -0
- package/src/archive/rows.ts +187 -0
- package/src/archive/seal.ts +630 -0
- package/src/archive/status.ts +719 -0
- package/src/archive/storepass.ts +581 -0
- package/src/backfill.ts +362 -0
- package/src/bench/cost.ts +113 -0
- package/src/bench/exec.ts +90 -0
- package/src/bench/values.ts +13 -0
- package/src/botcontrol.ts +176 -0
- package/src/botworker.ts +953 -0
- package/src/capture.ts +321 -0
- package/src/cases.ts +1789 -0
- package/src/channelfs.ts +26 -0
- package/src/checklist.ts +506 -0
- package/src/ciretry.ts +141 -0
- package/src/cli.ts +2271 -0
- package/src/cliops.ts +384 -0
- package/src/clonedir.ts +15 -0
- package/src/commandeffects.ts +57 -0
- package/src/commands.ts +1678 -0
- package/src/companioninbox.ts +239 -0
- package/src/compare.ts +957 -0
- package/src/config.ts +2050 -0
- package/src/configcheck.ts +133 -0
- package/src/coreext.ts +47 -0
- package/src/daemon.ts +1302 -0
- package/src/debuground.ts +927 -0
- package/src/debugroundpi.ts +262 -0
- package/src/diagnostics.ts +14 -0
- package/src/digest.ts +183 -0
- package/src/dispatch.ts +2250 -0
- package/src/engine/answerguard.ts +27 -0
- package/src/engine/botsdk.ts +336 -0
- package/src/engine/caserunner-sdk.ts +423 -0
- package/src/engine/debugpi.ts +235 -0
- package/src/eod.ts +669 -0
- package/src/eodpinwatch.ts +556 -0
- package/src/escalation.ts +1253 -0
- package/src/eventloop.ts +1045 -0
- package/src/execboundary.ts +633 -0
- package/src/feedback.ts +1162 -0
- package/src/findingscheck.ts +298 -0
- package/src/fsdenied.ts +24 -0
- package/src/ghread.ts +242 -0
- package/src/github.ts +1497 -0
- package/src/homes.ts +175 -0
- package/src/hostroute.ts +53 -0
- package/src/improvements.ts +787 -0
- package/src/install.ts +1103 -0
- package/src/installkind.ts +50 -0
- package/src/ipc/capability.ts +333 -0
- package/src/ipc/client.ts +269 -0
- package/src/ipc/control.ts +90 -0
- package/src/ipc/ghreadshim.ts +94 -0
- package/src/ipc/launcher.ts +65 -0
- package/src/ipc/permits.ts +362 -0
- package/src/ipc/postshim.ts +164 -0
- package/src/ipc/protocol.ts +342 -0
- package/src/ipc/server.ts +371 -0
- package/src/ipc/workerspec.ts +218 -0
- package/src/learner/learner.ts +1655 -0
- package/src/learner/miner.ts +1290 -0
- package/src/learner/packet.ts +340 -0
- package/src/learner/receipt.ts +168 -0
- package/src/learner/status.ts +107 -0
- package/src/ledgercheck.ts +157 -0
- package/src/lessonledger.ts +703 -0
- package/src/lessonselect.ts +888 -0
- package/src/lessonshadow.ts +810 -0
- package/src/levers.ts +393 -0
- package/src/live/projection.ts +491 -0
- package/src/live/publish.ts +92 -0
- package/src/live/read.ts +86 -0
- package/src/livecli.ts +336 -0
- package/src/loginhint.ts +56 -0
- package/src/loginrefresh/atomic.ts +76 -0
- package/src/loginrefresh/lock.ts +104 -0
- package/src/loginrefresh/main.ts +59 -0
- package/src/loginrefresh/pirefresh.ts +57 -0
- package/src/loginrefresh/refresher.ts +326 -0
- package/src/loginrefresh/supervisor.ts +273 -0
- package/src/loginrefresh/types.ts +35 -0
- package/src/memory/context.ts +385 -0
- package/src/memory/datafence.ts +9 -0
- package/src/memory/delivery.ts +171 -0
- package/src/memory/eventtags.ts +80 -0
- package/src/memory/health.ts +953 -0
- package/src/memory/import.ts +664 -0
- package/src/memory/layout.ts +261 -0
- package/src/memory/legacy-file.ts +104 -0
- package/src/memory/lessons.ts +447 -0
- package/src/memory/ovsession.ts +601 -0
- package/src/memory/relayout.ts +335 -0
- package/src/memory/roundactivity.ts +209 -0
- package/src/memory/selection.ts +989 -0
- package/src/memory/shadowstatus.ts +125 -0
- package/src/memory/store.ts +275 -0
- package/src/memory/textidentity.ts +8 -0
- package/src/metrics.ts +1056 -0
- package/src/missingconfig.ts +7 -0
- package/src/npmverbs.ts +264 -0
- package/src/onboard/choices.ts +222 -0
- package/src/onboard/engine.ts +518 -0
- package/src/onboard/launch.ts +115 -0
- package/src/onboard/steps/appslug.ts +265 -0
- package/src/onboard/steps/bringup.ts +482 -0
- package/src/onboard/steps/compose.ts +459 -0
- package/src/onboard/steps/configdelivery.ts +58 -0
- package/src/onboard/steps/configfill.ts +1352 -0
- package/src/onboard/steps/deploysh.ts +105 -0
- package/src/onboard/steps/embeddings.ts +550 -0
- package/src/onboard/steps/escalation.ts +640 -0
- package/src/onboard/steps/githubapp.ts +1414 -0
- package/src/onboard/steps/golive.ts +577 -0
- package/src/onboard/steps/index.ts +71 -0
- package/src/onboard/steps/ingress.ts +907 -0
- package/src/onboard/steps/inventory.ts +526 -0
- package/src/onboard/steps/llmauth.ts +1095 -0
- package/src/onboard/steps/memory.ts +1290 -0
- package/src/onboard/steps/memorykeys.ts +85 -0
- package/src/onboard/steps/preflight.ts +191 -0
- package/src/onboard/steps/proof.ts +624 -0
- package/src/onboard/steps/ssh.ts +377 -0
- package/src/onboard/steps/topology.ts +337 -0
- package/src/onboard/steps/transport.ts +665 -0
- package/src/onboard/steps/verifyup.ts +448 -0
- package/src/onboard/steps/vmdeploy.ts +442 -0
- package/src/onboard/toml.ts +50 -0
- package/src/opencli.ts +591 -0
- package/src/openpr.ts +568 -0
- package/src/openviking/client.ts +1131 -0
- package/src/openviking/context.ts +263 -0
- package/src/openviking/embedder.ts +516 -0
- package/src/openviking/gapsink.ts +63 -0
- package/src/openviking/health.ts +83 -0
- package/src/openviking/index.ts +119 -0
- package/src/openviking/lessonfind.ts +92 -0
- package/src/openviking/probe.ts +129 -0
- package/src/openviking/proc.ts +110 -0
- package/src/openviking/provisionreport.ts +160 -0
- package/src/openviking/reembed.ts +412 -0
- package/src/openviking/relayout.ts +177 -0
- package/src/openviking/sidecar-main.ts +38 -0
- package/src/openviking/sidecar.ts +106 -0
- package/src/openviking/store.ts +2025 -0
- package/src/openviking/supervisor.ts +474 -0
- package/src/openviking/types.ts +174 -0
- package/src/operator-recovery.ts +237 -0
- package/src/operatormessage.ts +52 -0
- package/src/outcomes.ts +1670 -0
- package/src/piauthcopy.ts +12 -0
- package/src/picli.ts +49 -0
- package/src/picwd.ts +25 -0
- package/src/pilogin.ts +37 -0
- package/src/pingpong.ts +1339 -0
- package/src/posting-journal.ts +549 -0
- package/src/posting-outbox.ts +802 -0
- package/src/posting-receipt.ts +324 -0
- package/src/posting.ts +603 -0
- package/src/postops.ts +832 -0
- package/src/pr-activity.ts +20 -0
- package/src/pr-author.ts +6 -0
- package/src/pr-conversation.ts +432 -0
- package/src/pr-session-lifecycle.ts +631 -0
- package/src/preflight.ts +391 -0
- package/src/priorresolve.ts +343 -0
- package/src/qcrbin.ts +186 -0
- package/src/recheck.ts +1775 -0
- package/src/reconcile.ts +1030 -0
- package/src/recordedfindings.ts +27 -0
- package/src/regression.ts +833 -0
- package/src/replay.ts +816 -0
- package/src/requiredpasses.ts +148 -0
- package/src/resumecheck.ts +246 -0
- package/src/reviewcli.ts +787 -0
- package/src/reviewcontext.ts +35 -0
- package/src/reviewdetach.ts +734 -0
- package/src/reviewhistory.ts +427 -0
- package/src/reviewhome.ts +36 -0
- package/src/reviewmemory.ts +133 -0
- package/src/reviewrendercli.ts +139 -0
- package/src/round-work.ts +42 -0
- package/src/runhost.ts +108 -0
- package/src/scanauth.ts +36 -0
- package/src/scanbox.ts +1081 -0
- package/src/scancli.ts +1296 -0
- package/src/scandriverlog.ts +32 -0
- package/src/session-bridge.ts +139 -0
- package/src/session-command-payload.ts +35 -0
- package/src/session-command-supervisor.ts +1142 -0
- package/src/sessionjsonl.ts +88 -0
- package/src/sessions.ts +659 -0
- package/src/setup/adapters.ts +335 -0
- package/src/setup/baselines.ts +110 -0
- package/src/setup/caserunner.ts +611 -0
- package/src/setup/configcommit.ts +443 -0
- package/src/setup/engine.ts +1001 -0
- package/src/setup/facts.ts +627 -0
- package/src/setup/findings.ts +1264 -0
- package/src/setup/history.ts +443 -0
- package/src/setup/interview.ts +243 -0
- package/src/setup/ledger.ts +324 -0
- package/src/setup/pins.ts +66 -0
- package/src/setup/portrait.ts +635 -0
- package/src/setup/presentation.ts +18 -0
- package/src/setup/profile.ts +508 -0
- package/src/setup/replay.ts +370 -0
- package/src/setup/replaybuild.ts +392 -0
- package/src/setup/replayrun.ts +1096 -0
- package/src/setup/replaysource.ts +198 -0
- package/src/setup/retune.ts +415 -0
- package/src/setup/retunelog.ts +95 -0
- package/src/setup/scan.ts +1033 -0
- package/src/setup/scanbuild.ts +168 -0
- package/src/setup/scanfacts.ts +107 -0
- package/src/setup/scanlease.ts +297 -0
- package/src/setup/scanreport.ts +373 -0
- package/src/setup/scanrun.ts +2268 -0
- package/src/setup/scantree.ts +259 -0
- package/src/setup/stage2.ts +926 -0
- package/src/setup/stage2request.ts +347 -0
- package/src/setup/survey.ts +458 -0
- package/src/setup/surveyrun.ts +758 -0
- package/src/setup/textsniff.ts +95 -0
- package/src/setup/trackersink.ts +433 -0
- package/src/setup/triage.ts +768 -0
- package/src/setup/tuning.ts +766 -0
- package/src/setup/unitcut.ts +523 -0
- package/src/singleflight.ts +86 -0
- package/src/slack/announce.ts +911 -0
- package/src/slack/api.ts +303 -0
- package/src/slack/messages.ts +288 -0
- package/src/slack/origin.ts +599 -0
- package/src/slack/policy.ts +354 -0
- package/src/slack/reply.ts +201 -0
- package/src/slack/router.ts +285 -0
- package/src/slack/socket.ts +384 -0
- package/src/slack/types.ts +108 -0
- package/src/sqlitehint.ts +32 -0
- package/src/ssh-observation.ts +30 -0
- package/src/state.ts +8243 -0
- package/src/statemachine.ts +369 -0
- package/src/stubs.ts +68 -0
- package/src/takeover.ts +634 -0
- package/src/takeoverpi.ts +293 -0
- package/src/terminal-failure.ts +41 -0
- package/src/thread-resolution-refusal.ts +2 -0
- package/src/tmux.ts +153 -0
- package/src/trackerid.ts +41 -0
- package/src/transcriptindex.ts +665 -0
- package/src/tui/actions.ts +484 -0
- package/src/tui/alerts.ts +666 -0
- package/src/tui/app.ts +1979 -0
- package/src/tui/archiveops.ts +192 -0
- package/src/tui/autofile.ts +233 -0
- package/src/tui/board.ts +1432 -0
- package/src/tui/candidates.ts +889 -0
- package/src/tui/cellwidth.ts +148 -0
- package/src/tui/changelog-store.ts +503 -0
- package/src/tui/chord.ts +25 -0
- package/src/tui/clipboard.ts +92 -0
- package/src/tui/cmdbar.ts +296 -0
- package/src/tui/companion.ts +1427 -0
- package/src/tui/companionplan.ts +247 -0
- package/src/tui/companiontab.ts +83 -0
- package/src/tui/confighome.ts +155 -0
- package/src/tui/connect.ts +68 -0
- package/src/tui/deploy-ceremony.ts +104 -0
- package/src/tui/deploy-drain.ts +129 -0
- package/src/tui/deploy-process.ts +265 -0
- package/src/tui/deploytab.ts +1523 -0
- package/src/tui/design.ts +125 -0
- package/src/tui/digesttab.ts +214 -0
- package/src/tui/emptystate.ts +39 -0
- package/src/tui/evidence.ts +999 -0
- package/src/tui/feed.ts +604 -0
- package/src/tui/feedbacktab.ts +1077 -0
- package/src/tui/filters.ts +90 -0
- package/src/tui/ghread.ts +321 -0
- package/src/tui/historypull.ts +81 -0
- package/src/tui/inboxpull.ts +236 -0
- package/src/tui/index.ts +1565 -0
- package/src/tui/issuesink.ts +238 -0
- package/src/tui/jsonfile.ts +42 -0
- package/src/tui/keys.ts +109 -0
- package/src/tui/layout.ts +108 -0
- package/src/tui/learning.ts +627 -0
- package/src/tui/lessonswitch.ts +118 -0
- package/src/tui/livecontrol.ts +228 -0
- package/src/tui/memforward.ts +443 -0
- package/src/tui/memincident.ts +363 -0
- package/src/tui/memory.ts +620 -0
- package/src/tui/memtab.ts +606 -0
- package/src/tui/metricstab.ts +1177 -0
- package/src/tui/model.ts +131 -0
- package/src/tui/mouse.ts +98 -0
- package/src/tui/msg.ts +268 -0
- package/src/tui/nativecompanion.ts +417 -0
- package/src/tui/openurl.ts +21 -0
- package/src/tui/paint.ts +511 -0
- package/src/tui/panes.ts +98 -0
- package/src/tui/pipane.ts +219 -0
- package/src/tui/poll-hooks.ts +12 -0
- package/src/tui/poll.ts +147 -0
- package/src/tui/proposals.ts +838 -0
- package/src/tui/prpause.ts +150 -0
- package/src/tui/reload.ts +436 -0
- package/src/tui/remote.ts +413 -0
- package/src/tui/rollover.ts +969 -0
- package/src/tui/scantab.ts +1290 -0
- package/src/tui/setupentry.ts +93 -0
- package/src/tui/sidebar.ts +749 -0
- package/src/tui/sshtriage.ts +510 -0
- package/src/tui/startup.ts +231 -0
- package/src/tui/tabs.ts +45 -0
- package/src/tui/termmirror.ts +224 -0
- package/src/tui/theme.ts +95 -0
- package/src/tui/ticketstab.ts +310 -0
- package/src/tui/tmuxsocket.ts +129 -0
- package/src/tui/trackertickets.ts +84 -0
- package/src/tui/unwedge.ts +286 -0
- package/src/tui/viewer.ts +575 -0
- package/src/tui/viewerinput.ts +173 -0
- package/src/updatenotice.ts +178 -0
- package/src/usage.ts +1154 -0
- package/src/verdicts.ts +4689 -0
- package/src/version.ts +80 -0
- package/src/wakes.ts +476 -0
- package/src/webhook.ts +942 -0
- package/src/webhookheal.ts +276 -0
- package/src/wire.ts +2153 -0
- package/src/worker-session-host.ts +602 -0
- package/src/workerbreaker.ts +156 -0
- package/src/workercontrol.ts +104 -0
- package/src/workerlease.ts +441 -0
- package/src/workerliveness.ts +103 -0
- package/src/workerlog.ts +79 -0
- package/src/workerruntime.ts +30 -0
- package/src/workersupervisor.ts +1538 -0
- package/src/workerusage.ts +42 -0
- package/src/workerworld.ts +427 -0
- package/src/worktrees.ts +1138 -0
- package/tests/fixtures/regression-cases.json +795 -0
- package/tests/fixtures/seed-manifest.json +19 -0
|
@@ -0,0 +1,1361 @@
|
|
|
1
|
+
import { prActivity } from "./pr-activity.js";
|
|
2
|
+
// Outcome capture: what became of every finding the reviewer posted.
|
|
3
|
+
//
|
|
4
|
+
// Why. `round_findings` records why each candidate was raised or killed; this
|
|
5
|
+
// module records what happened after a finding reached the PR — did the
|
|
6
|
+
// author fix it, argue it away, or leave it standing? — and links each posted
|
|
7
|
+
// finding to the GitHub comment that carried it, so a human reply joins back
|
|
8
|
+
// to the lens that raised the finding. The learner (finding cases, lessons)
|
|
9
|
+
// needs exactly that join. This module builds it, daemon-side and deterministic — no model in the
|
|
10
|
+
// loop, no GitHub write, nothing a round or a bench prompt can see.
|
|
11
|
+
//
|
|
12
|
+
// Three tables, owned here (on the state database, like the ping-pong
|
|
13
|
+
// ledger's):
|
|
14
|
+
//
|
|
15
|
+
// - `finding_comments` — one row per ROOT inline comment the bot posted, with
|
|
16
|
+
// the facts needed to link it (path, original line, review id, the marker,
|
|
17
|
+
// the title line) and the link itself: `(round_no, finding_key)` and how it
|
|
18
|
+
// was made (`via`). Linking, most trusted first:
|
|
19
|
+
// 1. `marker` — the hidden `<!-- qcr:f=<round>:<key> -->` the live render
|
|
20
|
+
// route stamps into every comment (packages/pi-plugin/src/findingkey.ts). Exact.
|
|
21
|
+
// 2. `thread_id` — the record's own `thread_id` names the comment.
|
|
22
|
+
// 3. `path_line` — the one posted finding at that path and line in the
|
|
23
|
+
// comment's round (the round its review id names, else the registry's
|
|
24
|
+
// attribution); ties broken by the title line (`path_line_title`), then
|
|
25
|
+
// the tag (`path_line_tag`). A comment none of these settles stays
|
|
26
|
+
// UNMATCHED (null) — never a guess.
|
|
27
|
+
// Facts arrive from the reconcile comment sweep's existing read (the bot's
|
|
28
|
+
// own inline comments), from the human replies
|
|
29
|
+
// already in `pr_comments` (a reply names its thread's root, path and line —
|
|
30
|
+
// GitHub's CURRENT line, which drifts as the branch moves, so it is the
|
|
31
|
+
// weakest fact), and from the record's `thread_id`. A fallback link is kept
|
|
32
|
+
// while its facts stand; when the bot's own comment supplies different ones
|
|
33
|
+
// (the line it was posted on, its review id), the link is re-opened and
|
|
34
|
+
// resolved again. No new GitHub read, and no gate: this only ever reads.
|
|
35
|
+
// - `finding_outcomes` — per posted finding, a FATE from the closed set
|
|
36
|
+
// below, with the rule that decided it (`basis`), the evidence and when.
|
|
37
|
+
// - `review_outcomes` — the WHOLE-REVIEW signals (a review a human set aside,
|
|
38
|
+
// a merge over a standing block), recorded per review and never smeared over
|
|
39
|
+
// the review's findings: nothing says which finding made a human set a
|
|
40
|
+
// review aside.
|
|
41
|
+
//
|
|
42
|
+
// The fates, and what decides them. Every piece of evidence is timestamped;
|
|
43
|
+
// the LATEST decisive one wins, so a finding upheld after a reply and fixed by
|
|
44
|
+
// a later push reads `fixed`.
|
|
45
|
+
// fixed — the session's thread decision said `fixed`; a human
|
|
46
|
+
// reply in the thread cites a commit made after the
|
|
47
|
+
// review (checked in git: a sha-shaped token that is
|
|
48
|
+
// no such commit — a UUID segment, a colour, the
|
|
49
|
+
// commit that introduced the behaviour — is no fix);
|
|
50
|
+
// the thread was resolved after a push that changed
|
|
51
|
+
// the anchored lines; or the finding is gone at the
|
|
52
|
+
// next round and the code at its anchor changed.
|
|
53
|
+
// refuted_conceded — the session's decision said `refuted`; a later round
|
|
54
|
+
// recorded it `conceded` with the code at its anchor
|
|
55
|
+
// unchanged and no sha cited; or the session resolved
|
|
56
|
+
// the thread with no push since the review and no
|
|
57
|
+
// unobserved head. The last two are INFERRED from the session's own act, so a
|
|
58
|
+
// human reply that accepts the point (`concedesPoint`:
|
|
59
|
+
// "fair catch", "fixed", "updated the description")
|
|
60
|
+
// turns them into `unknown`, basis `conflict:…`.
|
|
61
|
+
// upheld_after_reply — a human replied and the bot held: the decision said
|
|
62
|
+
// `held` or kept the thread open, or the next round
|
|
63
|
+
// posted the finding again. A thread kept open right
|
|
64
|
+
// after a reply citing a commit is no hold: a fix git
|
|
65
|
+
// verified stands, one it cannot check is `unknown`.
|
|
66
|
+
// open_at_close — none of the above, and the PR is closed.
|
|
67
|
+
// unknown — none of the above, and the PR is open.
|
|
68
|
+
// A `conceded` record is not read as a refutation on its own: most of them
|
|
69
|
+
// are fixes ("Fixed in <sha>"). It counts as `fixed` when a
|
|
70
|
+
// sha was cited or the anchored code changed, as `refuted_conceded` only when
|
|
71
|
+
// the code provably did not, and as no evidence when that cannot be told — a
|
|
72
|
+
// cited sha git cannot check included.
|
|
73
|
+
//
|
|
74
|
+
// A resolve is the session's: the event exists only when its thread decision
|
|
75
|
+
// said `resolve`. GitHub answering "already resolved" (a human clicked first)
|
|
76
|
+
// changes who clicked, not what the session judged, so it reads as the
|
|
77
|
+
// session's resolve; only the human-only refusals (`[check this]`,
|
|
78
|
+
// `[deferred]`) are no resolve at all. A decision that names its own
|
|
79
|
+
// `outcome` overrides the resolve it made.
|
|
80
|
+
//
|
|
81
|
+
// A later round's entry is this finding only when that is provable: it names
|
|
82
|
+
// this finding's thread or `settled:` it, or it names this finding's round in
|
|
83
|
+
// `raised_in_round` and either carries this finding's key or — same path,
|
|
84
|
+
// shared lens, within `CONTINUATION_LINES` — is nearer to this finding than
|
|
85
|
+
// to any other entry of that round. Only such a STRONG match carries
|
|
86
|
+
// evidence. A WEAK one (the next round, same path and lens, nearby, any
|
|
87
|
+
// round named or none) may be a different finding, so it only keeps this one
|
|
88
|
+
// from reading "gone", erring to `unknown`.
|
|
89
|
+
//
|
|
90
|
+
// A finding the next round posted again is CARRIED: the earlier row inherits
|
|
91
|
+
// the later one's evidence, so one finding raised in three rounds reads one
|
|
92
|
+
// fate three times rather than three different ones.
|
|
93
|
+
//
|
|
94
|
+
// Bounded. The reconcile pass runs `sweep()`: open PRs and PRs closed in the
|
|
95
|
+
// last day, at most `OUTCOME_SWEEP_MAX_PRS`, and a PR whose inputs have not
|
|
96
|
+
// moved since its last capture is skipped. Git reads (`AnchorGit`) are local,
|
|
97
|
+
// read-only and memoised; a commit the clone does not hold is UNKNOWN, never a
|
|
98
|
+
// change and never no-change. Every failure is the sweep's own — reconcile's
|
|
99
|
+
// health (which lifts a deploy drain) never waits on it.
|
|
100
|
+
import { carriedToRef, isPostedRow, predecessorOf, sameSite, } from "@quickchatai/pi-quickreview/src/finding-chain.js";
|
|
101
|
+
import { parseFindingMarker } from "@quickchatai/pi-quickreview/src/findingkey.js";
|
|
102
|
+
import { storedLensIds } from "@quickchatai/pi-quickreview/src/lensids.js";
|
|
103
|
+
import { bareTag } from "@quickchatai/pi-quickreview/src/severity.js";
|
|
104
|
+
import { yieldToLoop } from "@quickchatai/pi-quickreview/src/yieldloop.js";
|
|
105
|
+
export const FINDING_FATES = [
|
|
106
|
+
"fixed",
|
|
107
|
+
"refuted_conceded",
|
|
108
|
+
"upheld_after_reply",
|
|
109
|
+
"open_at_close",
|
|
110
|
+
"unknown",
|
|
111
|
+
];
|
|
112
|
+
/** Bump when a rule changes, so every PR is re-captured once. */
|
|
113
|
+
export const OUTCOME_RULES_VERSION = 5;
|
|
114
|
+
/** PRs captured per reconcile pass. */
|
|
115
|
+
export const OUTCOME_SWEEP_MAX_PRS = 25;
|
|
116
|
+
/** How long after a close a PR is still captured — the close is evidence. */
|
|
117
|
+
export const OUTCOME_CLOSED_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
118
|
+
/**
|
|
119
|
+
* Does the bounded pass (`OutcomeCapture.sweep`) still visit this PR? Open
|
|
120
|
+
* PRs, and PRs closed within `OUTCOME_CLOSED_WINDOW_MS`; a PR with no
|
|
121
|
+
* `pr_state` row never. The ONE spelling of the window: the case writer
|
|
122
|
+
* (src/cases.ts) catches up the PRs outside it itself.
|
|
123
|
+
*/
|
|
124
|
+
export function inCaptureWindow(p, nowMs) {
|
|
125
|
+
if (!p)
|
|
126
|
+
return false;
|
|
127
|
+
return p.state !== "closed" || nowMs - (ms(p.updated_at) ?? 0) <= OUTCOME_CLOSED_WINDOW_MS;
|
|
128
|
+
}
|
|
129
|
+
/** A PR whose inputs have not moved is still re-captured this often: a git
|
|
130
|
+
* answer that was unknown (a commit not yet fetched) may since be known. */
|
|
131
|
+
export const OUTCOME_RECAPTURE_MS = 6 * 60 * 60 * 1000;
|
|
132
|
+
/** Wall time one pass may spend before it stops STARTING PRs: the pass runs
|
|
133
|
+
* inside reconcile, ahead of the dispatch pump, so the first capture after a
|
|
134
|
+
* deploy (every open PR, cold git) must not hold the pump for long. The PRs
|
|
135
|
+
* left over come first next pass. */
|
|
136
|
+
export const OUTCOME_SWEEP_BUDGET_MS = 10_000;
|
|
137
|
+
const SCHEMA = `
|
|
138
|
+
CREATE TABLE IF NOT EXISTS finding_comments (
|
|
139
|
+
comment_id INTEGER PRIMARY KEY,
|
|
140
|
+
pr INTEGER NOT NULL,
|
|
141
|
+
review_id INTEGER,
|
|
142
|
+
path TEXT,
|
|
143
|
+
line INTEGER,
|
|
144
|
+
head TEXT,
|
|
145
|
+
marker_round INTEGER,
|
|
146
|
+
marker_key TEXT,
|
|
147
|
+
created_at TEXT,
|
|
148
|
+
-- 'github' (the bot's own comment, read) | 'reply' (a human reply named it)
|
|
149
|
+
-- | 'record' (a findings record's thread_id named it)
|
|
150
|
+
source TEXT NOT NULL,
|
|
151
|
+
round_no INTEGER,
|
|
152
|
+
finding_key TEXT,
|
|
153
|
+
via TEXT,
|
|
154
|
+
linked_at TEXT,
|
|
155
|
+
seen_at TEXT NOT NULL
|
|
156
|
+
);
|
|
157
|
+
CREATE INDEX IF NOT EXISTS finding_comments_finding
|
|
158
|
+
ON finding_comments (pr, round_no, finding_key);
|
|
159
|
+
CREATE TABLE IF NOT EXISTS finding_outcomes (
|
|
160
|
+
pr INTEGER NOT NULL,
|
|
161
|
+
round_no INTEGER NOT NULL,
|
|
162
|
+
finding_key TEXT NOT NULL,
|
|
163
|
+
comment_id INTEGER,
|
|
164
|
+
fate TEXT NOT NULL,
|
|
165
|
+
basis TEXT,
|
|
166
|
+
evidence TEXT,
|
|
167
|
+
decided_at TEXT,
|
|
168
|
+
human_replies INTEGER NOT NULL DEFAULT 0,
|
|
169
|
+
carried_to TEXT,
|
|
170
|
+
fate_changed_at TEXT NOT NULL,
|
|
171
|
+
updated_at TEXT NOT NULL,
|
|
172
|
+
PRIMARY KEY (pr, round_no, finding_key)
|
|
173
|
+
);
|
|
174
|
+
CREATE TABLE IF NOT EXISTS review_outcomes (
|
|
175
|
+
signal_id INTEGER PRIMARY KEY,
|
|
176
|
+
pr INTEGER NOT NULL,
|
|
177
|
+
kind TEXT NOT NULL,
|
|
178
|
+
round_no INTEGER,
|
|
179
|
+
gh_review_id INTEGER,
|
|
180
|
+
at TEXT NOT NULL,
|
|
181
|
+
recorded_at TEXT NOT NULL
|
|
182
|
+
);
|
|
183
|
+
CREATE TABLE IF NOT EXISTS outcome_capture_activity (
|
|
184
|
+
pr INTEGER PRIMARY KEY,
|
|
185
|
+
captured_at TEXT NOT NULL,
|
|
186
|
+
comments_max INTEGER NOT NULL,
|
|
187
|
+
signals_max INTEGER NOT NULL
|
|
188
|
+
);
|
|
189
|
+
CREATE TABLE IF NOT EXISTS outcome_capture_marks (
|
|
190
|
+
pr INTEGER PRIMARY KEY,
|
|
191
|
+
signature TEXT NOT NULL,
|
|
192
|
+
captured_at TEXT NOT NULL
|
|
193
|
+
);
|
|
194
|
+
`;
|
|
195
|
+
/**
|
|
196
|
+
* A retried posting can stamp one finding's marker on two comments: the
|
|
197
|
+
* earliest keeps the link (`via = marker`), every later one stays unlinked
|
|
198
|
+
* with the marker. Per linked comment, its twins' ids, ascending — the other
|
|
199
|
+
* threads that are the same finding's. One rule for the outcome capture and
|
|
200
|
+
* the finding cases.
|
|
201
|
+
*/
|
|
202
|
+
export function markerTwins(rows) {
|
|
203
|
+
const linked = new Map();
|
|
204
|
+
for (const c of rows)
|
|
205
|
+
if (c.round_no !== null && c.finding_key !== null)
|
|
206
|
+
linked.set(fkey(c.round_no, c.finding_key), c);
|
|
207
|
+
const out = new Map();
|
|
208
|
+
for (const c of [...rows].sort((a, b) => a.comment_id - b.comment_id)) {
|
|
209
|
+
if (c.finding_key !== null || c.marker_round === null || c.marker_key === null)
|
|
210
|
+
continue;
|
|
211
|
+
const root = linked.get(fkey(c.marker_round, c.marker_key));
|
|
212
|
+
if (root?.via !== "marker")
|
|
213
|
+
continue;
|
|
214
|
+
out.set(root.comment_id, [...(out.get(root.comment_id) ?? []), c.comment_id]);
|
|
215
|
+
}
|
|
216
|
+
return out;
|
|
217
|
+
}
|
|
218
|
+
/** A `finding_outcomes.carried_to` address: `<round>:<key>`. */
|
|
219
|
+
export { carriedToRef } from "@quickchatai/pi-quickreview/src/finding-chain.js";
|
|
220
|
+
/** Which rows reached the PR, and which earlier entry a later one continues:
|
|
221
|
+
* the review core reads them too (packages/pi-plugin/src/priorfindings.ts). */
|
|
222
|
+
export { CONTINUATION_LINES, isPostedRow, predecessorOf, } from "@quickchatai/pi-quickreview/src/finding-chain.js";
|
|
223
|
+
/** The finding a `carried_to` address names — split at the FIRST ':', since
|
|
224
|
+
* the key itself may hold ':' — or null when the round part is not an
|
|
225
|
+
* integer. */
|
|
226
|
+
export function parseCarriedTo(ref) {
|
|
227
|
+
const cut = ref.indexOf(":");
|
|
228
|
+
const round = Number(ref.slice(0, cut));
|
|
229
|
+
if (cut <= 0 || !Number.isInteger(round))
|
|
230
|
+
return null;
|
|
231
|
+
return { round_no: round, finding_key: ref.slice(cut + 1) };
|
|
232
|
+
}
|
|
233
|
+
// ---- small pure helpers ---------------------------------------------------
|
|
234
|
+
/** How many sha-shaped tokens of one reply are checked in git. */
|
|
235
|
+
const MAX_CITED_SHAS = 5;
|
|
236
|
+
/** How many later heads a cited sha is checked against. */
|
|
237
|
+
const MAX_LATER_HEADS = 8;
|
|
238
|
+
/** The sha-shaped tokens a human reply carries (7–40 hex characters with at
|
|
239
|
+
* least one digit and one letter, standing alone — "Fixed in 3f2a9c1", a
|
|
240
|
+
* commit URL; never `#`-prefixed like a colour, never a dash-joined run like
|
|
241
|
+
* a UUID's segments), distinct, in order, at most `MAX_CITED_SHAS`. A token
|
|
242
|
+
* is only a CANDIDATE: whether it is a fix is git's question
|
|
243
|
+
* (`AnchorGit.introducedBetween`). */
|
|
244
|
+
export function citedShas(text) {
|
|
245
|
+
if (typeof text !== "string")
|
|
246
|
+
return [];
|
|
247
|
+
const out = [];
|
|
248
|
+
for (const m of text.matchAll(/(?<![0-9a-z#-])[0-9a-f]{7,40}(?![0-9a-z-])/gi)) {
|
|
249
|
+
const t = m[0].toLowerCase();
|
|
250
|
+
if (/[0-9]/.test(t) && /[a-f]/.test(t) && !out.includes(t))
|
|
251
|
+
out.push(t);
|
|
252
|
+
if (out.length >= MAX_CITED_SHAS)
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
return out;
|
|
256
|
+
}
|
|
257
|
+
/** The first candidate `citedShas` finds, or null. */
|
|
258
|
+
export function citedSha(text) {
|
|
259
|
+
return citedShas(text)[0] ?? null;
|
|
260
|
+
}
|
|
261
|
+
/** The tags and title a comment opens with: `[blocker] title`,
|
|
262
|
+
* `**[check this]** title`, `[ask your human] [question] title`. */
|
|
263
|
+
export function commentHead(body) {
|
|
264
|
+
const first = (typeof body === "string" ? body : "").split("\n").find((l) => l.trim() !== "") ?? "";
|
|
265
|
+
let rest = first.trim();
|
|
266
|
+
const tags = [];
|
|
267
|
+
for (;;) {
|
|
268
|
+
rest = rest.replace(/^[*_`~>\s]+/, "");
|
|
269
|
+
const m = /^\[([^\]]{1,40})\][*_`~\s]*/.exec(rest);
|
|
270
|
+
if (!m)
|
|
271
|
+
break;
|
|
272
|
+
tags.push(m[1].trim().toLowerCase());
|
|
273
|
+
rest = rest.slice(m[0].length);
|
|
274
|
+
}
|
|
275
|
+
return { tags, title: rest.replace(/[*_~\s]+$/, "").trim() };
|
|
276
|
+
}
|
|
277
|
+
function normTitle(t) {
|
|
278
|
+
return (t ?? "")
|
|
279
|
+
.toLowerCase()
|
|
280
|
+
.replace(/[`*_~]/g, "")
|
|
281
|
+
.replace(/\s+/g, " ")
|
|
282
|
+
.replace(/[.!?:;\s]+$/, "")
|
|
283
|
+
.trim();
|
|
284
|
+
}
|
|
285
|
+
function ms(iso) {
|
|
286
|
+
if (!iso)
|
|
287
|
+
return null;
|
|
288
|
+
const t = Date.parse(iso);
|
|
289
|
+
return Number.isFinite(t) ? t : null;
|
|
290
|
+
}
|
|
291
|
+
const fkey = (round, key) => `${round}\u0000${key}`;
|
|
292
|
+
const FATE_RANK = {
|
|
293
|
+
fixed: 3,
|
|
294
|
+
refuted_conceded: 2,
|
|
295
|
+
upheld_after_reply: 1,
|
|
296
|
+
unknown: 0,
|
|
297
|
+
};
|
|
298
|
+
/**
|
|
299
|
+
* Does a human reply ACCEPT the finding — agree it was right, or say it was
|
|
300
|
+
* fixed or the description corrected? A deliberately small lexicon, read
|
|
301
|
+
* only to hold back a refutation the record would otherwise infer from the
|
|
302
|
+
* session's own act (it resolved the thread, or a later round conceded) with
|
|
303
|
+
* the anchored code unchanged. Many such "refutations" are agreements: "fair
|
|
304
|
+
* catch", "You are right, … I have corrected it", "Updated: the description
|
|
305
|
+
* now says …", a fix made away from the anchor. It over-matches on purpose
|
|
306
|
+
* — a hit only turns a refutation into `unknown`, never into a fix.
|
|
307
|
+
*/
|
|
308
|
+
const CONCESSION_RE = /\b(?:you(?:'|\u2019)?re (?:right|correct)|you are (?:right|correct)|(?:good|fair|great|nice) (?:catch|point|spot)|fixed|addressed|corrected|updated|amended|reworded|clarified|agreed|accepted|acknowledged|my (?:omission|mistake|bad)|will fix|made (?:it )?part of this pr)\b|^\s*done\b/im;
|
|
309
|
+
export function concedesPoint(text) {
|
|
310
|
+
return typeof text === "string" && CONCESSION_RE.test(text);
|
|
311
|
+
}
|
|
312
|
+
/** The latest evidence; at one instant, fixed > refuted > upheld > unknown. */
|
|
313
|
+
export function decideFate(evidence) {
|
|
314
|
+
let best = null;
|
|
315
|
+
for (const e of evidence)
|
|
316
|
+
if (!best || e.at > best.at || (e.at === best.at && FATE_RANK[e.fate] > FATE_RANK[best.fate]))
|
|
317
|
+
best = e;
|
|
318
|
+
return best;
|
|
319
|
+
}
|
|
320
|
+
/** When a human reply was made: GitHub's time, else when it was captured. */
|
|
321
|
+
function replyMs(r) {
|
|
322
|
+
return ms(r.created_at) ?? ms(r.captured_at) ?? 0;
|
|
323
|
+
}
|
|
324
|
+
/** A resolve that counts: the bot's own, or one GitHub answered "already
|
|
325
|
+
* resolved" (a human clicked first; the session still decided `resolve`). The
|
|
326
|
+
* human-only refusals are no resolve at all. */
|
|
327
|
+
function resolveCounts(skipped) {
|
|
328
|
+
return skipped === null || skipped === "already-resolved";
|
|
329
|
+
}
|
|
330
|
+
function intOf(v) {
|
|
331
|
+
if (typeof v === "number" && Number.isInteger(v))
|
|
332
|
+
return v;
|
|
333
|
+
if (typeof v === "string" && /^\d+$/.test(v))
|
|
334
|
+
return Number(v);
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
export class OutcomeCapture {
|
|
338
|
+
deps;
|
|
339
|
+
store;
|
|
340
|
+
now;
|
|
341
|
+
constructor(deps) {
|
|
342
|
+
this.deps = deps;
|
|
343
|
+
this.store = deps.store;
|
|
344
|
+
this.now = deps.now ?? (() => new Date());
|
|
345
|
+
this.store.db.exec(SCHEMA);
|
|
346
|
+
}
|
|
347
|
+
ts() {
|
|
348
|
+
return this.now().toISOString();
|
|
349
|
+
}
|
|
350
|
+
// ---- facts in ---------------------------------------------------------------
|
|
351
|
+
/**
|
|
352
|
+
* Keep the link facts of the bot's own ROOT inline comments from a read the
|
|
353
|
+
* caller already made (the reconcile comment sweep's `listReviewCommentsFull`).
|
|
354
|
+
* Replies, other authors' comments and the body beyond its first line are
|
|
355
|
+
* not kept. Returns how many rows were new or gained facts.
|
|
356
|
+
*
|
|
357
|
+
* Facts that change re-open the row's link: a fallback link made from a
|
|
358
|
+
* human reply's drifted line, or before the review id was known, was a
|
|
359
|
+
* resolution of the OLD facts, so it goes back to unmatched and the next
|
|
360
|
+
* `linkComments` resolves it from the new ones. Only a marker link whose
|
|
361
|
+
* marker is unchanged stands.
|
|
362
|
+
*/
|
|
363
|
+
observeBotComments(pr, comments) {
|
|
364
|
+
const keep = "(finding_comments.via = 'marker'" +
|
|
365
|
+
" AND finding_comments.marker_round IS excluded.marker_round" +
|
|
366
|
+
" AND finding_comments.marker_key IS excluded.marker_key)";
|
|
367
|
+
const stmt = this.store.db.prepare(`INSERT INTO finding_comments
|
|
368
|
+
(comment_id, pr, review_id, path, line, head, marker_round, marker_key, created_at,
|
|
369
|
+
source, seen_at)
|
|
370
|
+
VALUES (@comment_id, @pr, @review_id, @path, @line, @head, @marker_round, @marker_key,
|
|
371
|
+
@created_at, 'github', @seen_at)
|
|
372
|
+
ON CONFLICT(comment_id) DO UPDATE SET
|
|
373
|
+
review_id = excluded.review_id, path = excluded.path, line = excluded.line,
|
|
374
|
+
head = excluded.head, marker_round = excluded.marker_round,
|
|
375
|
+
marker_key = excluded.marker_key, created_at = excluded.created_at, source = 'github',
|
|
376
|
+
seen_at = excluded.seen_at,
|
|
377
|
+
round_no = CASE WHEN ${keep} THEN finding_comments.round_no ELSE NULL END,
|
|
378
|
+
finding_key = CASE WHEN ${keep} THEN finding_comments.finding_key ELSE NULL END,
|
|
379
|
+
via = CASE WHEN ${keep} THEN finding_comments.via ELSE NULL END,
|
|
380
|
+
linked_at = CASE WHEN ${keep} THEN finding_comments.linked_at ELSE NULL END
|
|
381
|
+
WHERE finding_comments.source != 'github'
|
|
382
|
+
OR finding_comments.review_id IS NOT excluded.review_id
|
|
383
|
+
OR finding_comments.path IS NOT excluded.path
|
|
384
|
+
OR finding_comments.line IS NOT excluded.line
|
|
385
|
+
OR finding_comments.head IS NOT excluded.head
|
|
386
|
+
OR finding_comments.marker_round IS NOT excluded.marker_round
|
|
387
|
+
OR finding_comments.marker_key IS NOT excluded.marker_key`);
|
|
388
|
+
let changed = 0;
|
|
389
|
+
const seen = this.ts();
|
|
390
|
+
this.store.db.transaction(() => {
|
|
391
|
+
for (const c of comments) {
|
|
392
|
+
if (c.user !== this.deps.botLogin || c.in_reply_to_id !== null)
|
|
393
|
+
continue;
|
|
394
|
+
const marker = parseFindingMarker(c.body);
|
|
395
|
+
const head = commentHead(c.body);
|
|
396
|
+
const info = stmt.run({
|
|
397
|
+
comment_id: c.id,
|
|
398
|
+
pr,
|
|
399
|
+
review_id: c.pull_request_review_id ?? null,
|
|
400
|
+
path: c.path || null,
|
|
401
|
+
// The line it was POSTED on — `line` drifts as the branch moves.
|
|
402
|
+
line: c.original_line ?? c.line ?? null,
|
|
403
|
+
head: [head.tags.map((t) => `[${t}]`).join(" "), head.title]
|
|
404
|
+
.filter(Boolean)
|
|
405
|
+
.join(" ")
|
|
406
|
+
.slice(0, 300),
|
|
407
|
+
marker_round: marker?.round_no ?? null,
|
|
408
|
+
marker_key: marker?.key ?? null,
|
|
409
|
+
created_at: c.created_at ?? null,
|
|
410
|
+
seen_at: seen,
|
|
411
|
+
});
|
|
412
|
+
changed += info.changes;
|
|
413
|
+
}
|
|
414
|
+
})();
|
|
415
|
+
return changed;
|
|
416
|
+
}
|
|
417
|
+
/** Link facts the state database already holds: the root a human reply
|
|
418
|
+
* names (when the registry says the bot posted it — its path, and its line
|
|
419
|
+
* as GitHub reported it then, which may have drifted from the posted one)
|
|
420
|
+
* and a record's own `thread_id`. Never overwrites a row: the bot's own
|
|
421
|
+
* comment, when read, replaces these facts (and re-opens their link). */
|
|
422
|
+
ingestLocalFacts(pr, posted) {
|
|
423
|
+
const insert = this.store.db.prepare(`INSERT OR IGNORE INTO finding_comments (comment_id, pr, path, line, created_at, source, seen_at)
|
|
424
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`);
|
|
425
|
+
// The registry knows when the bot posted the comment. Without it a
|
|
426
|
+
// reply- or record-sourced row has no posting time, and a fate rule
|
|
427
|
+
// falls back to the round's record time — a later round's, on a link
|
|
428
|
+
// the registry contradicts.
|
|
429
|
+
const backfill = this.store.db.prepare("UPDATE finding_comments SET created_at = ? WHERE comment_id = ? AND created_at IS NULL");
|
|
430
|
+
const seen = this.ts();
|
|
431
|
+
const add = (commentId, path, line, source) => {
|
|
432
|
+
const postedAt = this.store.botCommentPostedAt(commentId, "review_comment");
|
|
433
|
+
insert.run(commentId, pr, path, line, postedAt, source, seen);
|
|
434
|
+
if (postedAt !== null)
|
|
435
|
+
backfill.run(postedAt, commentId);
|
|
436
|
+
};
|
|
437
|
+
this.store.db.transaction(() => {
|
|
438
|
+
for (const c of this.store.prComments(pr)) {
|
|
439
|
+
if (c.source !== "review_comment" || c.in_reply_to_id === null)
|
|
440
|
+
continue;
|
|
441
|
+
if (!this.store.isBotComment(c.in_reply_to_id, "review_comment"))
|
|
442
|
+
continue;
|
|
443
|
+
add(c.in_reply_to_id, c.path, c.line, "reply");
|
|
444
|
+
}
|
|
445
|
+
for (const f of posted) {
|
|
446
|
+
if (f.thread_id === null || !this.store.isBotComment(f.thread_id, "review_comment"))
|
|
447
|
+
continue;
|
|
448
|
+
add(f.thread_id, f.path, f.line, "record");
|
|
449
|
+
}
|
|
450
|
+
})();
|
|
451
|
+
}
|
|
452
|
+
// ---- linking ------------------------------------------------------------------
|
|
453
|
+
/** The rounds of a PR that have a parsed findings record. */
|
|
454
|
+
recordedRounds(pr, rows) {
|
|
455
|
+
const out = new Map();
|
|
456
|
+
for (const r of rows) {
|
|
457
|
+
const at = ms(r.recorded_at) ?? 0;
|
|
458
|
+
const prev = out.get(r.round_no);
|
|
459
|
+
out.set(r.round_no, {
|
|
460
|
+
at: prev ? Math.min(prev.at, at) : at,
|
|
461
|
+
head: prev?.head || r.head_sha || null,
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
// A record with no entries leaves no rows — its event is the evidence
|
|
465
|
+
// the round was recorded at all.
|
|
466
|
+
for (const e of this.store.prEvents(pr, ["findings_recorded"])) {
|
|
467
|
+
const round = intOf(e.payload.round_no);
|
|
468
|
+
if (round === null || out.has(round))
|
|
469
|
+
continue;
|
|
470
|
+
out.set(round, { at: ms(e.ts) ?? 0, head: null });
|
|
471
|
+
}
|
|
472
|
+
// A head the rows do not carry: the round's posted review's commit.
|
|
473
|
+
for (const r of this.store.roundsFor(pr)) {
|
|
474
|
+
const rec = out.get(r.round_no);
|
|
475
|
+
if (rec && !rec.head && r.gh_commit_id)
|
|
476
|
+
rec.head = r.gh_commit_id;
|
|
477
|
+
}
|
|
478
|
+
return out;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Link every bot comment of a PR to its finding (see the module header for
|
|
482
|
+
* the order). A marker link is final. Every other comment is resolved
|
|
483
|
+
* AGAIN on every capture, from its facts alone, so the result never
|
|
484
|
+
* depends on the order comments are visited or on what an earlier pass
|
|
485
|
+
* linked.
|
|
486
|
+
*
|
|
487
|
+
* * The round: the one the comment's review id names, else the one the
|
|
488
|
+
* registry (`bot_comments.round_no`) attributes it to. When either is
|
|
489
|
+
* known, ONLY that round's findings are candidates — a comment whose
|
|
490
|
+
* round holds no posted finding at its path and line is unmatched,
|
|
491
|
+
* never linked to another round's finding. Only a comment
|
|
492
|
+
* neither names falls back to the first round recorded after it was
|
|
493
|
+
* posted.
|
|
494
|
+
* * A finding two comments could claim by fallback is NEITHER's, and one
|
|
495
|
+
* a marker claims is the marker's.
|
|
496
|
+
*
|
|
497
|
+
* Returns the counts after.
|
|
498
|
+
*/
|
|
499
|
+
linkComments(pr) {
|
|
500
|
+
const rows = this.store.roundFindings(pr);
|
|
501
|
+
const posted = rows.filter((r) => r.disposition === "posted");
|
|
502
|
+
this.ingestLocalFacts(pr, posted);
|
|
503
|
+
const comments = this.store.db
|
|
504
|
+
.prepare("SELECT * FROM finding_comments WHERE pr = ? ORDER BY comment_id")
|
|
505
|
+
.all(pr);
|
|
506
|
+
const byKey = new Map(rows.map((r) => [fkey(r.round_no, r.finding_key), r]));
|
|
507
|
+
const recorded = this.recordedRounds(pr, rows);
|
|
508
|
+
const reviewRound = new Map();
|
|
509
|
+
for (const r of this.store.roundsFor(pr))
|
|
510
|
+
if (r.gh_review_id !== null)
|
|
511
|
+
reviewRound.set(r.gh_review_id, r.round_no);
|
|
512
|
+
const update = this.store.db.prepare("UPDATE finding_comments SET round_no = ?, finding_key = ?, via = ?, linked_at = ? WHERE comment_id = ?");
|
|
513
|
+
const unlink = this.store.db.prepare("UPDATE finding_comments SET round_no = NULL, finding_key = NULL, via = NULL, linked_at = NULL WHERE comment_id = ?");
|
|
514
|
+
const at = this.ts();
|
|
515
|
+
const resolve = (c) => {
|
|
516
|
+
if (c.marker_round !== null && c.marker_key !== null) {
|
|
517
|
+
const f = byKey.get(fkey(c.marker_round, c.marker_key));
|
|
518
|
+
if (f)
|
|
519
|
+
return { f, via: "marker" };
|
|
520
|
+
// Its round's record is not parsed yet: wait for it rather than guess.
|
|
521
|
+
if (!recorded.has(c.marker_round))
|
|
522
|
+
return null;
|
|
523
|
+
// Parsed, and the key is not in it (the record was rewritten after
|
|
524
|
+
// rendering): the fallback below may still place it.
|
|
525
|
+
}
|
|
526
|
+
const byThread = posted.filter((f) => f.thread_id === c.comment_id);
|
|
527
|
+
if (byThread.length === 1)
|
|
528
|
+
return { f: byThread[0], via: "thread_id" };
|
|
529
|
+
if (c.path === null || c.line === null)
|
|
530
|
+
return null;
|
|
531
|
+
const here = posted.filter((f) => f.path === c.path && f.line === c.line);
|
|
532
|
+
if (here.length === 0)
|
|
533
|
+
return null;
|
|
534
|
+
const exactRound = c.review_id !== null ? reviewRound.get(c.review_id) : undefined;
|
|
535
|
+
const named = exactRound ?? this.store.botCommentRound(c.comment_id, "review_comment") ?? undefined;
|
|
536
|
+
let pool;
|
|
537
|
+
if (named !== undefined)
|
|
538
|
+
pool = here.filter((f) => f.round_no === named);
|
|
539
|
+
else {
|
|
540
|
+
// The comment's round is the first whose record was parsed after the
|
|
541
|
+
// comment was posted (a record is written after posting).
|
|
542
|
+
const rounds = [...new Set(here.map((f) => f.round_no))].sort((a, b) => a - b);
|
|
543
|
+
const created = ms(c.created_at);
|
|
544
|
+
const round = created === null
|
|
545
|
+
? rounds.length === 1
|
|
546
|
+
? rounds[0]
|
|
547
|
+
: undefined
|
|
548
|
+
: rounds.find((r) => (recorded.get(r)?.at ?? -Infinity) >= created);
|
|
549
|
+
pool = round === undefined ? [] : here.filter((f) => f.round_no === round);
|
|
550
|
+
}
|
|
551
|
+
if (pool.length === 1)
|
|
552
|
+
return { f: pool[0], via: "path_line" };
|
|
553
|
+
if (pool.length === 0 || c.head === null)
|
|
554
|
+
return null;
|
|
555
|
+
const head = commentHead(c.head);
|
|
556
|
+
const byTitle = pool.filter((f) => f.title && normTitle(f.title) === normTitle(head.title));
|
|
557
|
+
if (byTitle.length === 1)
|
|
558
|
+
return { f: byTitle[0], via: "path_line_title" };
|
|
559
|
+
const tag = head.tags.find((t) => t !== "ask your human" && t !== "deferred") ?? head.tags[0];
|
|
560
|
+
const byTag = pool.filter((f) => f.posted_as && bareTag(f.posted_as) === tag);
|
|
561
|
+
if (byTag.length === 1)
|
|
562
|
+
return { f: byTag[0], via: "path_line_tag" };
|
|
563
|
+
return null;
|
|
564
|
+
};
|
|
565
|
+
// Pass 1: every comment's own answer, from its facts alone.
|
|
566
|
+
const hits = new Map();
|
|
567
|
+
const claims = new Map();
|
|
568
|
+
for (const c of comments) {
|
|
569
|
+
const hit = c.via === "marker" && c.round_no !== null && c.finding_key !== null
|
|
570
|
+
? (() => {
|
|
571
|
+
const f = byKey.get(fkey(c.round_no, c.finding_key));
|
|
572
|
+
return f ? { f, via: "marker" } : resolve(c);
|
|
573
|
+
})()
|
|
574
|
+
: resolve(c);
|
|
575
|
+
hits.set(c.comment_id, hit);
|
|
576
|
+
if (!hit)
|
|
577
|
+
continue;
|
|
578
|
+
const k = fkey(hit.f.round_no, hit.f.finding_key);
|
|
579
|
+
const claim = claims.get(k) ?? { marker: [], fallback: [] };
|
|
580
|
+
(hit.via === "marker" ? claim.marker : claim.fallback).push(c.comment_id);
|
|
581
|
+
claims.set(k, claim);
|
|
582
|
+
}
|
|
583
|
+
// Pass 2: a finding goes to its marker (the earliest comment, when a
|
|
584
|
+
// retried posting stamped one marker twice), else to its one fallback
|
|
585
|
+
// claimant; contested by fallbacks alone, it goes to nobody.
|
|
586
|
+
this.store.db.transaction(() => {
|
|
587
|
+
for (const c of comments) {
|
|
588
|
+
const hit = hits.get(c.comment_id) ?? null;
|
|
589
|
+
let keep = null;
|
|
590
|
+
if (hit) {
|
|
591
|
+
const claim = claims.get(fkey(hit.f.round_no, hit.f.finding_key));
|
|
592
|
+
if (hit.via === "marker")
|
|
593
|
+
keep = Math.min(...claim.marker) === c.comment_id ? hit : null;
|
|
594
|
+
else
|
|
595
|
+
keep = claim.marker.length === 0 && claim.fallback.length === 1 ? hit : null;
|
|
596
|
+
}
|
|
597
|
+
if (!keep) {
|
|
598
|
+
if (c.finding_key !== null)
|
|
599
|
+
unlink.run(c.comment_id);
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
if (c.finding_key === keep.f.finding_key &&
|
|
603
|
+
c.round_no === keep.f.round_no &&
|
|
604
|
+
c.via === keep.via)
|
|
605
|
+
continue;
|
|
606
|
+
update.run(keep.f.round_no, keep.f.finding_key, keep.via, at, c.comment_id);
|
|
607
|
+
}
|
|
608
|
+
})();
|
|
609
|
+
const counts = this.store.db
|
|
610
|
+
.prepare("SELECT COUNT(finding_key) AS linked, COUNT(*) - COUNT(finding_key) AS unmatched FROM finding_comments WHERE pr = ?")
|
|
611
|
+
.get(pr);
|
|
612
|
+
return counts;
|
|
613
|
+
}
|
|
614
|
+
// ---- fates ----------------------------------------------------------------
|
|
615
|
+
loadFacts(pr) {
|
|
616
|
+
const rows = this.store.roundFindings(pr);
|
|
617
|
+
const posted = rows.filter((r) => r.disposition === "posted");
|
|
618
|
+
const byRound = new Map();
|
|
619
|
+
for (const r of rows)
|
|
620
|
+
byRound.set(r.round_no, [...(byRound.get(r.round_no) ?? []), r]);
|
|
621
|
+
const comments = this.commentsFor(pr);
|
|
622
|
+
const links = new Map();
|
|
623
|
+
for (const c of comments)
|
|
624
|
+
if (c.round_no !== null && c.finding_key !== null)
|
|
625
|
+
links.set(fkey(c.round_no, c.finding_key), c);
|
|
626
|
+
const twins = markerTwins(comments);
|
|
627
|
+
const replies = new Map();
|
|
628
|
+
for (const c of this.store.prComments(pr)) {
|
|
629
|
+
if (c.source !== "review_comment" || c.in_reply_to_id === null)
|
|
630
|
+
continue;
|
|
631
|
+
if (c.author === this.deps.botLogin)
|
|
632
|
+
continue;
|
|
633
|
+
replies.set(c.in_reply_to_id, [...(replies.get(c.in_reply_to_id) ?? []), c]);
|
|
634
|
+
}
|
|
635
|
+
const events = this.store.prEvents(pr, [
|
|
636
|
+
"pingpong_decision_applied",
|
|
637
|
+
"pingpong_thread_resolved",
|
|
638
|
+
"head_push",
|
|
639
|
+
"pr_closed",
|
|
640
|
+
]);
|
|
641
|
+
const facts = {
|
|
642
|
+
rows,
|
|
643
|
+
priorReports: this.store.priorFindingChecks(pr),
|
|
644
|
+
posted,
|
|
645
|
+
byRound,
|
|
646
|
+
recorded: this.recordedRounds(pr, rows),
|
|
647
|
+
rounds: this.store.roundsFor(pr),
|
|
648
|
+
links,
|
|
649
|
+
twins,
|
|
650
|
+
replies,
|
|
651
|
+
decisions: [],
|
|
652
|
+
resolves: [],
|
|
653
|
+
pushes: [],
|
|
654
|
+
currentHead: null,
|
|
655
|
+
closedAt: null,
|
|
656
|
+
};
|
|
657
|
+
for (const e of events) {
|
|
658
|
+
const at = ms(e.ts) ?? 0;
|
|
659
|
+
const thread = intOf(e.payload.thread_id);
|
|
660
|
+
const comment = intOf(e.payload.comment_id);
|
|
661
|
+
if (e.kind === "pingpong_decision_applied" && thread !== null && comment !== null)
|
|
662
|
+
facts.decisions.push({
|
|
663
|
+
at,
|
|
664
|
+
thread_id: thread,
|
|
665
|
+
comment_id: comment,
|
|
666
|
+
resolution: typeof e.payload.resolution === "string" ? e.payload.resolution : "",
|
|
667
|
+
outcome: typeof e.payload.outcome === "string" ? e.payload.outcome : null,
|
|
668
|
+
});
|
|
669
|
+
else if (e.kind === "pingpong_thread_resolved" && thread !== null && comment !== null)
|
|
670
|
+
facts.resolves.push({
|
|
671
|
+
at,
|
|
672
|
+
thread_id: thread,
|
|
673
|
+
comment_id: comment,
|
|
674
|
+
// A value that is neither null nor a known string is no resolve.
|
|
675
|
+
skipped: e.payload.skipped === undefined || e.payload.skipped === null
|
|
676
|
+
? null
|
|
677
|
+
: typeof e.payload.skipped === "string"
|
|
678
|
+
? e.payload.skipped
|
|
679
|
+
: "unreadable",
|
|
680
|
+
});
|
|
681
|
+
else if (e.kind === "head_push" && typeof e.payload.head_sha === "string")
|
|
682
|
+
facts.pushes.push({ at, head_sha: e.payload.head_sha });
|
|
683
|
+
else if (e.kind === "pr_closed")
|
|
684
|
+
facts.closedAt = at;
|
|
685
|
+
}
|
|
686
|
+
// Closed is the PR's state NOW: a PR reopened since its close is open.
|
|
687
|
+
const row = this.store.getPr(pr);
|
|
688
|
+
facts.currentHead = row?.head_sha || null;
|
|
689
|
+
if (row && row.state !== "closed")
|
|
690
|
+
facts.closedAt = null;
|
|
691
|
+
else if (row && facts.closedAt === null)
|
|
692
|
+
facts.closedAt = ms(row.updated_at) ?? 0;
|
|
693
|
+
return facts;
|
|
694
|
+
}
|
|
695
|
+
/** Later-round entries that are this finding again (see the module
|
|
696
|
+
* header). Strong: the entry names its thread or its `settled:` thread, or
|
|
697
|
+
* names its round in `raised_in_round` with the same path and is THIS
|
|
698
|
+
* finding's by key, else by being the one nearest same-lens entry of that
|
|
699
|
+
* round. Weak — the NEXT round only — the same path and lens near the same
|
|
700
|
+
* line, whoever it belongs to. */
|
|
701
|
+
continuations(f, facts, threads) {
|
|
702
|
+
const ownRound = (facts.byRound.get(f.round_no) ?? []).filter(isPostedRow);
|
|
703
|
+
const strong = [];
|
|
704
|
+
for (const e of facts.rows) {
|
|
705
|
+
if (e.round_no <= f.round_no)
|
|
706
|
+
continue;
|
|
707
|
+
const word = (e.word ?? "").trim().toLowerCase();
|
|
708
|
+
if (threads.some((t) => e.thread_id === t || word === `settled:${t}`)) {
|
|
709
|
+
strong.push(e);
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
if (e.raised_in_round !== f.round_no || !f.path || e.path !== f.path)
|
|
713
|
+
continue;
|
|
714
|
+
if (predecessorOf(ownRound, e) === f.finding_key)
|
|
715
|
+
strong.push(e);
|
|
716
|
+
}
|
|
717
|
+
const nextRound = [...facts.recorded.keys()]
|
|
718
|
+
.filter((r) => r > f.round_no)
|
|
719
|
+
.sort((a, b) => a - b)[0];
|
|
720
|
+
if (nextRound === undefined)
|
|
721
|
+
return { strong, next: null };
|
|
722
|
+
const weak = (facts.byRound.get(nextRound) ?? []).filter((e) => sameSite(f, e));
|
|
723
|
+
return { strong, next: { round: nextRound, weak } };
|
|
724
|
+
}
|
|
725
|
+
async touched(f, to) {
|
|
726
|
+
if (!this.deps.git || !f.path || !f.head_sha || !to)
|
|
727
|
+
return null;
|
|
728
|
+
try {
|
|
729
|
+
return await this.deps.git.anchorTouched(f.path, f.line, f.head_sha, to);
|
|
730
|
+
}
|
|
731
|
+
catch {
|
|
732
|
+
return null;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
/** The heads a fix of `f` can live in: the PR's head now, the heads of the
|
|
736
|
+
* rounds after `f`'s, and every pushed head, newest first — never `f`'s own. */
|
|
737
|
+
laterHeads(f, facts) {
|
|
738
|
+
const out = [];
|
|
739
|
+
const add = (h) => {
|
|
740
|
+
if (h && h !== f.head_sha && !out.includes(h))
|
|
741
|
+
out.push(h);
|
|
742
|
+
};
|
|
743
|
+
add(facts.currentHead);
|
|
744
|
+
for (const r of [...facts.recorded.keys()].sort((a, b) => b - a))
|
|
745
|
+
if (r > f.round_no)
|
|
746
|
+
add(facts.recorded.get(r)?.head);
|
|
747
|
+
for (const p of [...facts.pushes].reverse())
|
|
748
|
+
add(p.head_sha);
|
|
749
|
+
return out.slice(0, MAX_LATER_HEADS);
|
|
750
|
+
}
|
|
751
|
+
/** Does a human reply cite a commit made after `f`'s head (see `CitedFix`)?
|
|
752
|
+
* Every reply is read, each on its own. */
|
|
753
|
+
async citedFix(f, facts, replies) {
|
|
754
|
+
const heads = this.laterHeads(f, facts);
|
|
755
|
+
const byReply = new Map();
|
|
756
|
+
let latest = null;
|
|
757
|
+
let unverified = false;
|
|
758
|
+
for (const r of replies) {
|
|
759
|
+
const own = await this.replyCitesFix(f, heads, r);
|
|
760
|
+
byReply.set(r.comment_id, own.state);
|
|
761
|
+
if (own.state === "verified") {
|
|
762
|
+
if (latest === null || replyMs(r) >= replyMs(latest.reply))
|
|
763
|
+
latest = own;
|
|
764
|
+
}
|
|
765
|
+
else if (own.state === "unverified")
|
|
766
|
+
unverified = true;
|
|
767
|
+
}
|
|
768
|
+
const overall = latest ?? (unverified ? { state: "unverified" } : { state: "none" });
|
|
769
|
+
return { overall, byReply };
|
|
770
|
+
}
|
|
771
|
+
/** One reply's citation: the first of its shas git confirms is a commit
|
|
772
|
+
* after `f`'s head, else unverified when git could not answer for one. */
|
|
773
|
+
async replyCitesFix(f, heads, r) {
|
|
774
|
+
let unverified = false;
|
|
775
|
+
for (const sha of citedShas(r.body)) {
|
|
776
|
+
if (!this.deps.git || !f.head_sha || heads.length === 0) {
|
|
777
|
+
unverified = true;
|
|
778
|
+
continue;
|
|
779
|
+
}
|
|
780
|
+
for (const to of heads) {
|
|
781
|
+
let answer;
|
|
782
|
+
try {
|
|
783
|
+
answer = await this.deps.git.introducedBetween(sha, f.head_sha, to);
|
|
784
|
+
}
|
|
785
|
+
catch {
|
|
786
|
+
answer = null;
|
|
787
|
+
}
|
|
788
|
+
if (answer === true)
|
|
789
|
+
return { state: "verified", sha, reply: r };
|
|
790
|
+
if (answer === null)
|
|
791
|
+
unverified = true;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
return unverified ? { state: "unverified" } : { state: "none" };
|
|
795
|
+
}
|
|
796
|
+
async evidenceFor(f, facts, memo) {
|
|
797
|
+
const k = fkey(f.round_no, f.finding_key);
|
|
798
|
+
let p = memo.get(k);
|
|
799
|
+
if (!p) {
|
|
800
|
+
p = this.computeEvidence(f, facts, memo);
|
|
801
|
+
memo.set(k, p);
|
|
802
|
+
}
|
|
803
|
+
return p;
|
|
804
|
+
}
|
|
805
|
+
async computeEvidence(f, facts, memo) {
|
|
806
|
+
const evidence = [];
|
|
807
|
+
const link = facts.links.get(fkey(f.round_no, f.finding_key)) ?? null;
|
|
808
|
+
const thread = link?.comment_id ?? null;
|
|
809
|
+
/** Every thread of this finding: its linked comment's, and its twins'. */
|
|
810
|
+
const threads = thread === null ? [] : [thread, ...(facts.twins.get(thread) ?? [])];
|
|
811
|
+
const postedAt = ms(link?.created_at) ?? facts.recorded.get(f.round_no)?.at ?? ms(f.recorded_at) ?? 0;
|
|
812
|
+
// Backfilled replies and marker twins interleave by when they were written.
|
|
813
|
+
const replies = threads
|
|
814
|
+
.flatMap((t) => facts.replies.get(t) ?? [])
|
|
815
|
+
.sort((a, b) => replyMs(a) - replyMs(b) || a.comment_id - b.comment_id);
|
|
816
|
+
/** A human reply in the thread, no later than `at`, that accepts the
|
|
817
|
+
* point (`concedesPoint`) — the latest one, or null. */
|
|
818
|
+
const humanConceded = (at) => [...replies].reverse().find((r) => replyMs(r) <= at && concedesPoint(r.body)) ?? null;
|
|
819
|
+
// 1. A human reply that cites a commit made after the review — "Fixed in
|
|
820
|
+
// 3f2a9c1", checked in git. One git cannot check is no evidence, and no
|
|
821
|
+
// "nothing was cited" either.
|
|
822
|
+
const cites = await this.citedFix(f, facts, replies);
|
|
823
|
+
const cited = cites.overall;
|
|
824
|
+
const fixCited = cited.state === "verified";
|
|
825
|
+
if (cited.state === "verified")
|
|
826
|
+
evidence.push({
|
|
827
|
+
fate: "fixed",
|
|
828
|
+
basis: "sha_cited",
|
|
829
|
+
at: replyMs(cited.reply),
|
|
830
|
+
detail: `reply ${cited.reply.comment_id} cites ${cited.sha}, a commit after the review`,
|
|
831
|
+
});
|
|
832
|
+
// 2. The session's thread decisions.
|
|
833
|
+
const decided = new Set();
|
|
834
|
+
if (thread !== null) {
|
|
835
|
+
for (const d of facts.decisions.filter((x) => threads.includes(x.thread_id))) {
|
|
836
|
+
const fate = d.outcome === "fixed"
|
|
837
|
+
? "fixed"
|
|
838
|
+
: d.outcome === "refuted"
|
|
839
|
+
? "refuted_conceded"
|
|
840
|
+
: d.outcome === "held"
|
|
841
|
+
? "upheld_after_reply"
|
|
842
|
+
: null;
|
|
843
|
+
if (fate) {
|
|
844
|
+
decided.add(d.comment_id);
|
|
845
|
+
evidence.push({
|
|
846
|
+
fate,
|
|
847
|
+
basis: "pingpong_outcome",
|
|
848
|
+
at: d.at,
|
|
849
|
+
detail: `thread decision on reply ${d.comment_id}: ${d.outcome}`,
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
else if (d.resolution === "keep_open") {
|
|
853
|
+
// Keeping a thread open right after "Fixed in <sha>" is the
|
|
854
|
+
// session not having re-verified the new head yet — not the bot
|
|
855
|
+
// holding against a human. When the human reply it answered (the
|
|
856
|
+
// decision's own comment id) cites a commit: a fix git verified
|
|
857
|
+
// stands (nothing is added — `sha_cited` is the evidence); one git
|
|
858
|
+
// cannot check yet leaves the finding undecided. Another reply's
|
|
859
|
+
// citation decides nothing here.
|
|
860
|
+
const answered = replies.find((r) => r.comment_id === d.comment_id) ?? null;
|
|
861
|
+
const citesSha = answered !== null && citedShas(answered.body).length > 0;
|
|
862
|
+
const answeredCite = answered
|
|
863
|
+
? (cites.byReply.get(answered.comment_id) ?? "none")
|
|
864
|
+
: "none";
|
|
865
|
+
if (citesSha && answeredCite === "verified")
|
|
866
|
+
continue;
|
|
867
|
+
evidence.push(citesSha && answeredCite === "unverified"
|
|
868
|
+
? {
|
|
869
|
+
fate: "unknown",
|
|
870
|
+
basis: "conflict:kept_open_after_unverified_sha",
|
|
871
|
+
at: d.at,
|
|
872
|
+
detail: `kept open after reply ${answered.comment_id} cited a commit git could not check`,
|
|
873
|
+
}
|
|
874
|
+
: {
|
|
875
|
+
fate: "upheld_after_reply",
|
|
876
|
+
basis: "kept_open",
|
|
877
|
+
at: d.at,
|
|
878
|
+
detail: `thread decision on reply ${d.comment_id}: keep_open`,
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
// 3. The session's resolves, where its decision did not say why. A
|
|
883
|
+
// thread a human had already resolved is still the session's resolve
|
|
884
|
+
// (it decided `resolve`), at the latest at the event's time.
|
|
885
|
+
for (const r of facts.resolves.filter((x) => threads.includes(x.thread_id) && resolveCounts(x.skipped))) {
|
|
886
|
+
if (decided.has(r.comment_id))
|
|
887
|
+
continue;
|
|
888
|
+
const who = r.skipped === "already-resolved" ? " (a human had resolved it first)" : "";
|
|
889
|
+
if (fixCited) {
|
|
890
|
+
evidence.push({
|
|
891
|
+
fate: "fixed",
|
|
892
|
+
basis: "resolved_after_cited_fix",
|
|
893
|
+
at: r.at,
|
|
894
|
+
detail: `resolved after a reply cited a commit${who}`,
|
|
895
|
+
});
|
|
896
|
+
continue;
|
|
897
|
+
}
|
|
898
|
+
const since = facts.pushes.filter((p) => p.at > postedAt && p.at <= r.at);
|
|
899
|
+
const head = since[since.length - 1]?.head_sha ?? null;
|
|
900
|
+
if (head === null) {
|
|
901
|
+
if (cited.state === "unverified")
|
|
902
|
+
continue;
|
|
903
|
+
// Push events carry observation times. A head that differs now may
|
|
904
|
+
// have landed before this resolve and only been captured afterwards.
|
|
905
|
+
const observedHead = facts.pushes.filter((p) => p.at <= postedAt).at(-1)?.head_sha ?? f.head_sha;
|
|
906
|
+
if (!facts.currentHead ||
|
|
907
|
+
!observedHead ||
|
|
908
|
+
facts.currentHead.toLowerCase() !== observedHead.toLowerCase()) {
|
|
909
|
+
evidence.push({
|
|
910
|
+
fate: "unknown",
|
|
911
|
+
basis: "conflict:resolved_unobserved_head",
|
|
912
|
+
at: r.at,
|
|
913
|
+
detail: `resolved without a push observed before it${who}, but the current head does not confirm the reviewed head`,
|
|
914
|
+
});
|
|
915
|
+
continue;
|
|
916
|
+
}
|
|
917
|
+
const accepted = humanConceded(r.at);
|
|
918
|
+
evidence.push(accepted
|
|
919
|
+
? {
|
|
920
|
+
fate: "unknown",
|
|
921
|
+
basis: "conflict:resolved_no_change",
|
|
922
|
+
at: r.at,
|
|
923
|
+
detail: `resolved with no push since the review${who}, but reply ${accepted.comment_id} accepts the point`,
|
|
924
|
+
}
|
|
925
|
+
: {
|
|
926
|
+
fate: "refuted_conceded",
|
|
927
|
+
basis: "resolved_no_change",
|
|
928
|
+
at: r.at,
|
|
929
|
+
detail: `resolved with no push since the review${who}`,
|
|
930
|
+
});
|
|
931
|
+
continue;
|
|
932
|
+
}
|
|
933
|
+
if ((await this.touched(f, head)) === true)
|
|
934
|
+
evidence.push({
|
|
935
|
+
fate: "fixed",
|
|
936
|
+
basis: "resolved_after_push",
|
|
937
|
+
at: r.at,
|
|
938
|
+
detail: `resolved after ${head.slice(0, 12)} changed the anchored lines${who}`,
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
// 4. Later rounds. Only a strong match is this finding's evidence.
|
|
943
|
+
const { strong, next } = this.continuations(f, facts, threads);
|
|
944
|
+
const conceded = strong.filter((e) => e.disposition === "conceded");
|
|
945
|
+
for (const e of conceded) {
|
|
946
|
+
const head = facts.recorded.get(e.round_no)?.head ?? e.head_sha ?? null;
|
|
947
|
+
const changed = await this.touched(f, head);
|
|
948
|
+
const at = facts.recorded.get(e.round_no)?.at ?? ms(e.recorded_at) ?? 0;
|
|
949
|
+
if (fixCited || changed === true)
|
|
950
|
+
evidence.push({
|
|
951
|
+
fate: "fixed",
|
|
952
|
+
basis: "conceded_after_fix",
|
|
953
|
+
at,
|
|
954
|
+
detail: `round ${e.round_no} recorded it conceded after ${fixCited ? "a cited commit" : "the anchored lines changed"}`,
|
|
955
|
+
});
|
|
956
|
+
else if (changed === false && cited.state === "none") {
|
|
957
|
+
const accepted = humanConceded(at);
|
|
958
|
+
evidence.push(accepted
|
|
959
|
+
? {
|
|
960
|
+
fate: "unknown",
|
|
961
|
+
basis: "conflict:conceded_unchanged",
|
|
962
|
+
at,
|
|
963
|
+
detail: `round ${e.round_no} recorded it conceded with the anchored lines unchanged, but reply ${accepted.comment_id} accepts the point (a fix may sit away from the anchor)`,
|
|
964
|
+
}
|
|
965
|
+
: {
|
|
966
|
+
fate: "refuted_conceded",
|
|
967
|
+
basis: "conceded_unchanged",
|
|
968
|
+
at,
|
|
969
|
+
detail: `round ${e.round_no} recorded it conceded with the anchored lines unchanged`,
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
// Posted again, provably the same finding: CARRIED — this row inherits the
|
|
974
|
+
// later one's evidence. Only a strong match carries: a weak one (same
|
|
975
|
+
// path and lens, nearby) may be a different finding, so it only keeps
|
|
976
|
+
// this one from reading "gone".
|
|
977
|
+
const reposted = strong.filter((e) => e.disposition === "posted").sort((a, b) => a.round_no - b.round_no)[0] ??
|
|
978
|
+
null;
|
|
979
|
+
let carriedTo = null;
|
|
980
|
+
if (reposted) {
|
|
981
|
+
carriedTo = reposted;
|
|
982
|
+
const againAt = facts.recorded.get(reposted.round_no)?.at ?? ms(reposted.recorded_at) ?? 0;
|
|
983
|
+
if (replies.some((r) => replyMs(r) < againAt))
|
|
984
|
+
evidence.push({
|
|
985
|
+
fate: "upheld_after_reply",
|
|
986
|
+
basis: "reposted_after_reply",
|
|
987
|
+
at: againAt,
|
|
988
|
+
detail: `round ${reposted.round_no} posted it again after a human reply`,
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
else if (next &&
|
|
992
|
+
conceded.length === 0 &&
|
|
993
|
+
next.weak.length === 0 &&
|
|
994
|
+
!strong.some((e) => e.round_no === next.round) &&
|
|
995
|
+
!facts.priorReports.some((report) => report.round_no === f.round_no &&
|
|
996
|
+
report.finding_key === f.finding_key &&
|
|
997
|
+
report.checked_round === next.round &&
|
|
998
|
+
(report.status === "open" || report.status === "unverifiable"))) {
|
|
999
|
+
// Gone at the next round. Fixed only if the code at its anchor changed.
|
|
1000
|
+
const head = facts.recorded.get(next.round)?.head ?? null;
|
|
1001
|
+
if ((await this.touched(f, head)) === true)
|
|
1002
|
+
evidence.push({
|
|
1003
|
+
fate: "fixed",
|
|
1004
|
+
basis: "gone_after_change",
|
|
1005
|
+
at: facts.recorded.get(next.round)?.at ?? 0,
|
|
1006
|
+
detail: `gone at round ${next.round}, whose head changed the anchored lines`,
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
if (carriedTo) {
|
|
1010
|
+
const successor = carriedTo;
|
|
1011
|
+
const later = await this.evidenceFor(successor, facts, memo);
|
|
1012
|
+
evidence.push(...later.evidence.map((e) => ({
|
|
1013
|
+
...e,
|
|
1014
|
+
basis: e.basis.startsWith("carried:") ? e.basis : `carried:${e.basis}`,
|
|
1015
|
+
detail: `round ${successor.round_no} ${successor.finding_key}: ${e.detail}`,
|
|
1016
|
+
})));
|
|
1017
|
+
}
|
|
1018
|
+
return {
|
|
1019
|
+
evidence,
|
|
1020
|
+
comment: thread,
|
|
1021
|
+
replies: replies.length,
|
|
1022
|
+
carriedTo: carriedTo ? carriedToRef(carriedTo.round_no, carriedTo.finding_key) : null,
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
/** Record the whole-review signals of a PR, one row per signal. */
|
|
1026
|
+
recordReviewOutcomes(pr, rounds) {
|
|
1027
|
+
const upsert = this.store.db.prepare(`INSERT INTO review_outcomes (signal_id, pr, kind, round_no, gh_review_id, at, recorded_at)
|
|
1028
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1029
|
+
ON CONFLICT(signal_id) DO UPDATE SET round_no = excluded.round_no,
|
|
1030
|
+
gh_review_id = excluded.gh_review_id`);
|
|
1031
|
+
const at = this.ts();
|
|
1032
|
+
for (const s of this.store.feedbackSignalsFor(pr)) {
|
|
1033
|
+
if (s.kind === "review_set_aside" && s.review_id !== null) {
|
|
1034
|
+
const round = rounds.find((r) => r.gh_review_id === s.review_id) ?? null;
|
|
1035
|
+
upsert.run(s.id, pr, "set_aside", round?.round_no ?? null, s.review_id, s.created_at, at);
|
|
1036
|
+
}
|
|
1037
|
+
else if (s.kind === "merged_over_block") {
|
|
1038
|
+
// The review that stood: the latest decided round before the merge,
|
|
1039
|
+
// and only if it decided REQUEST_CHANGES — otherwise unknown.
|
|
1040
|
+
const signalAt = ms(s.created_at) ?? Infinity;
|
|
1041
|
+
const decided = rounds
|
|
1042
|
+
.filter((r) => (r.verdict === "APPROVE" || r.verdict === "REQUEST_CHANGES") &&
|
|
1043
|
+
(ms(r.finished_at ?? r.started_at) ?? Infinity) <= signalAt)
|
|
1044
|
+
.sort((a, b) => a.round_no - b.round_no);
|
|
1045
|
+
const last = decided[decided.length - 1];
|
|
1046
|
+
const stood = last && last.verdict === "REQUEST_CHANGES" ? last : null;
|
|
1047
|
+
upsert.run(s.id, pr, "merged_over_block", stood?.round_no ?? null, stood?.gh_review_id ?? null, s.created_at, at);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
/** Link, then decide and record the fate of every posted finding of a PR. */
|
|
1052
|
+
async capturePr(pr) {
|
|
1053
|
+
const link = this.linkComments(pr);
|
|
1054
|
+
const facts = this.loadFacts(pr);
|
|
1055
|
+
this.recordReviewOutcomes(pr, facts.rounds);
|
|
1056
|
+
// The fingerprint of exactly the inputs read above, taken BEFORE the first
|
|
1057
|
+
// await: a reply, push, decision or record that lands while the git reads
|
|
1058
|
+
// below are in flight was not read, so it must move the fingerprint away
|
|
1059
|
+
// from this mark and bring the PR back next pass.
|
|
1060
|
+
const signature = this.signature(pr);
|
|
1061
|
+
const activity = prActivity(this.store, pr);
|
|
1062
|
+
const memo = new Map();
|
|
1063
|
+
const decided = [];
|
|
1064
|
+
for (const f of facts.posted)
|
|
1065
|
+
decided.push({ f, chain: await this.evidenceFor(f, facts, memo) });
|
|
1066
|
+
const fates = Object.fromEntries(FINDING_FATES.map((x) => [x, 0]));
|
|
1067
|
+
const at = this.ts();
|
|
1068
|
+
const existing = new Map(this.store.db
|
|
1069
|
+
.prepare("SELECT round_no, finding_key, fate FROM finding_outcomes WHERE pr = ?")
|
|
1070
|
+
.all(pr).map((r) => [fkey(r.round_no, r.finding_key), r.fate]));
|
|
1071
|
+
const upsert = this.store.db.prepare(`INSERT INTO finding_outcomes
|
|
1072
|
+
(pr, round_no, finding_key, comment_id, fate, basis, evidence, decided_at, human_replies,
|
|
1073
|
+
carried_to, fate_changed_at, updated_at)
|
|
1074
|
+
VALUES (@pr, @round_no, @finding_key, @comment_id, @fate, @basis, @evidence, @decided_at,
|
|
1075
|
+
@human_replies, @carried_to, @at, @at)
|
|
1076
|
+
ON CONFLICT(pr, round_no, finding_key) DO UPDATE SET
|
|
1077
|
+
comment_id = excluded.comment_id, fate = excluded.fate, basis = excluded.basis,
|
|
1078
|
+
evidence = excluded.evidence, decided_at = excluded.decided_at,
|
|
1079
|
+
human_replies = excluded.human_replies, carried_to = excluded.carried_to,
|
|
1080
|
+
fate_changed_at = CASE WHEN finding_outcomes.fate = excluded.fate
|
|
1081
|
+
THEN finding_outcomes.fate_changed_at ELSE excluded.fate_changed_at END,
|
|
1082
|
+
updated_at = excluded.updated_at`);
|
|
1083
|
+
let changed = 0;
|
|
1084
|
+
this.store.db.transaction(() => {
|
|
1085
|
+
for (const { f, chain } of decided) {
|
|
1086
|
+
const best = decideFate(chain.evidence);
|
|
1087
|
+
// A latest piece of evidence that is a CONFLICT reads `unknown`, PR
|
|
1088
|
+
// open or closed — its basis says why — and is decided by nobody.
|
|
1089
|
+
const fate = best
|
|
1090
|
+
? best.fate
|
|
1091
|
+
: facts.closedAt !== null
|
|
1092
|
+
? "open_at_close"
|
|
1093
|
+
: "unknown";
|
|
1094
|
+
const decidedAt = best && best.fate !== "unknown"
|
|
1095
|
+
? new Date(best.at).toISOString()
|
|
1096
|
+
: !best && facts.closedAt !== null
|
|
1097
|
+
? new Date(facts.closedAt).toISOString()
|
|
1098
|
+
: null;
|
|
1099
|
+
fates[fate]++;
|
|
1100
|
+
if (existing.get(fkey(f.round_no, f.finding_key)) !== fate)
|
|
1101
|
+
changed++;
|
|
1102
|
+
upsert.run({
|
|
1103
|
+
pr,
|
|
1104
|
+
round_no: f.round_no,
|
|
1105
|
+
finding_key: f.finding_key,
|
|
1106
|
+
comment_id: chain.comment,
|
|
1107
|
+
fate,
|
|
1108
|
+
basis: best ? best.basis : fate === "open_at_close" ? "closed" : null,
|
|
1109
|
+
evidence: best ? best.detail.slice(0, 500) : null,
|
|
1110
|
+
decided_at: decidedAt,
|
|
1111
|
+
human_replies: chain.replies,
|
|
1112
|
+
carried_to: chain.carriedTo,
|
|
1113
|
+
at,
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
// A row whose finding is no longer posted (a re-recorded round) goes.
|
|
1117
|
+
const live = new Set(decided.map(({ f }) => fkey(f.round_no, f.finding_key)));
|
|
1118
|
+
const drop = this.store.db.prepare("DELETE FROM finding_outcomes WHERE pr = ? AND round_no = ? AND finding_key = ?");
|
|
1119
|
+
for (const k of existing.keys())
|
|
1120
|
+
if (!live.has(k)) {
|
|
1121
|
+
const [round, key] = k.split("\u0000");
|
|
1122
|
+
drop.run(pr, Number(round), key);
|
|
1123
|
+
}
|
|
1124
|
+
})();
|
|
1125
|
+
this.markCaptured(pr, signature, activity);
|
|
1126
|
+
return {
|
|
1127
|
+
posted: facts.posted.length,
|
|
1128
|
+
linked: link.linked,
|
|
1129
|
+
unmatched: link.unmatched,
|
|
1130
|
+
changed,
|
|
1131
|
+
fates,
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
// ---- the bounded pass -----------------------------------------------------
|
|
1135
|
+
/** A fingerprint of everything a capture reads, so an unmoved PR is skipped. */
|
|
1136
|
+
signature(pr) {
|
|
1137
|
+
const get = (sql) => this.store.db.prepare(sql).get(pr);
|
|
1138
|
+
const row = this.store.getPr(pr);
|
|
1139
|
+
return JSON.stringify([
|
|
1140
|
+
OUTCOME_RULES_VERSION,
|
|
1141
|
+
this.deps.git ? 1 : 0,
|
|
1142
|
+
get("SELECT COUNT(*) c, MAX(id) m, MAX(recorded_at) r FROM round_findings WHERE pr = ?"),
|
|
1143
|
+
get("SELECT COUNT(*) c, MAX(id) m FROM pr_comments WHERE pr = ?"),
|
|
1144
|
+
get("SELECT MAX(id) m FROM events WHERE pr = ? AND kind IN " +
|
|
1145
|
+
"('pingpong_decision_applied','pingpong_thread_resolved','head_push','pr_closed','findings_recorded')"),
|
|
1146
|
+
get("SELECT COUNT(*) c, MAX(id) m FROM feedback_signals WHERE pr = ?"),
|
|
1147
|
+
this.store.priorFindingChecks(pr),
|
|
1148
|
+
get(
|
|
1149
|
+
// `seen_at` moves when a comment's facts change (its anchor or head
|
|
1150
|
+
// corrected), which can re-link it with every count unchanged.
|
|
1151
|
+
"SELECT COUNT(*) c, COUNT(finding_key) l, COUNT(marker_key) k, COUNT(review_id) v, MAX(seen_at) s FROM finding_comments WHERE pr = ?"),
|
|
1152
|
+
get("SELECT COUNT(*) c FROM bot_comments WHERE pr = ?"),
|
|
1153
|
+
// a posting receipt landing later (gh_review_id) can settle a link
|
|
1154
|
+
get("SELECT COUNT(*) c, COUNT(gh_review_id) v, COUNT(gh_commit_id) h, MAX(finished_at) f FROM rounds WHERE pr = ?"),
|
|
1155
|
+
row?.state ?? null,
|
|
1156
|
+
row?.head_sha ?? null,
|
|
1157
|
+
]);
|
|
1158
|
+
}
|
|
1159
|
+
markCaptured(pr, signature, activity) {
|
|
1160
|
+
const at = this.ts();
|
|
1161
|
+
this.store.db.transaction(() => {
|
|
1162
|
+
this.store.db
|
|
1163
|
+
.prepare(`INSERT INTO outcome_capture_marks (pr, signature, captured_at) VALUES (?, ?, ?)
|
|
1164
|
+
ON CONFLICT(pr) DO UPDATE SET signature=excluded.signature, captured_at=excluded.captured_at`)
|
|
1165
|
+
.run(pr, signature, at);
|
|
1166
|
+
this.store.db
|
|
1167
|
+
.prepare(`INSERT INTO outcome_capture_activity (pr, captured_at, comments_max, signals_max) VALUES (?, ?, ?, ?)
|
|
1168
|
+
ON CONFLICT(pr) DO UPDATE SET captured_at=excluded.captured_at, comments_max=excluded.comments_max, signals_max=excluded.signals_max`)
|
|
1169
|
+
.run(pr, at, activity.comments, activity.signals);
|
|
1170
|
+
})();
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* One bounded pass: open PRs and PRs closed within the window, the ones
|
|
1174
|
+
* captured longest ago first, at most `maxPrs` and no new PR once
|
|
1175
|
+
* `budgetMs` of wall time is spent, skipping a PR whose inputs have not
|
|
1176
|
+
* moved (unless its capture is older than the recapture period). Never
|
|
1177
|
+
* throws; a PR that fails is named in `errors` and retried next pass.
|
|
1178
|
+
*/
|
|
1179
|
+
async sweep(opts = {}) {
|
|
1180
|
+
const report = {
|
|
1181
|
+
considered: 0,
|
|
1182
|
+
captured: 0,
|
|
1183
|
+
skipped: 0,
|
|
1184
|
+
fates_changed: 0,
|
|
1185
|
+
errors: [],
|
|
1186
|
+
};
|
|
1187
|
+
const nowMs = this.now().getTime();
|
|
1188
|
+
const max = opts.maxPrs ?? OUTCOME_SWEEP_MAX_PRS;
|
|
1189
|
+
const started = performance.now();
|
|
1190
|
+
const budget = opts.budgetMs ?? OUTCOME_SWEEP_BUDGET_MS;
|
|
1191
|
+
try {
|
|
1192
|
+
const marks = new Map(this.store.db
|
|
1193
|
+
.prepare("SELECT pr, signature, captured_at FROM outcome_capture_marks")
|
|
1194
|
+
.all().map((m) => [m.pr, m]));
|
|
1195
|
+
const candidates = this.store
|
|
1196
|
+
.allPrs()
|
|
1197
|
+
.filter((p) => inCaptureWindow(p, nowMs))
|
|
1198
|
+
.sort((a, b) => {
|
|
1199
|
+
const ma = marks.get(a.number)?.captured_at ?? "";
|
|
1200
|
+
const mb = marks.get(b.number)?.captured_at ?? "";
|
|
1201
|
+
return ma < mb ? -1 : ma > mb ? 1 : a.number - b.number;
|
|
1202
|
+
});
|
|
1203
|
+
// Attempts, not successes: a capture that throws still spent its slot
|
|
1204
|
+
// and its time, so failures cannot run the pass past either limit.
|
|
1205
|
+
let attempted = 0;
|
|
1206
|
+
for (const p of candidates) {
|
|
1207
|
+
if (attempted >= max)
|
|
1208
|
+
break;
|
|
1209
|
+
if (attempted > 0 && performance.now() - started > budget)
|
|
1210
|
+
break;
|
|
1211
|
+
// A real event-loop turn per PR: a capture whose git answers are all
|
|
1212
|
+
// memoized (or with no git at all) never awaits real I/O, so a run of
|
|
1213
|
+
// them would otherwise block the loop as one long synchronous stretch.
|
|
1214
|
+
await yieldToLoop();
|
|
1215
|
+
report.considered++;
|
|
1216
|
+
try {
|
|
1217
|
+
const mark = marks.get(p.number);
|
|
1218
|
+
if (!opts.force &&
|
|
1219
|
+
mark &&
|
|
1220
|
+
nowMs - (ms(mark.captured_at) ?? 0) < OUTCOME_RECAPTURE_MS &&
|
|
1221
|
+
mark.signature === this.signature(p.number)) {
|
|
1222
|
+
report.skipped++;
|
|
1223
|
+
continue;
|
|
1224
|
+
}
|
|
1225
|
+
attempted++;
|
|
1226
|
+
const r = await this.capturePr(p.number);
|
|
1227
|
+
report.captured++;
|
|
1228
|
+
report.fates_changed += r.changed;
|
|
1229
|
+
}
|
|
1230
|
+
catch (err) {
|
|
1231
|
+
report.errors.push(`outcomes #${p.number}: ${String(err)}`);
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
catch (err) {
|
|
1236
|
+
report.errors.push(`outcomes: ${String(err)}`);
|
|
1237
|
+
}
|
|
1238
|
+
if (report.fates_changed > 0 || report.errors.length > 0)
|
|
1239
|
+
this.store.logEvent("outcomes_captured", null, report);
|
|
1240
|
+
return report;
|
|
1241
|
+
}
|
|
1242
|
+
// ---- reading back ---------------------------------------------------------
|
|
1243
|
+
outcomesFor(pr) {
|
|
1244
|
+
return this.store.db
|
|
1245
|
+
.prepare("SELECT * FROM finding_outcomes WHERE pr = ? ORDER BY round_no, finding_key")
|
|
1246
|
+
.all(pr);
|
|
1247
|
+
}
|
|
1248
|
+
commentsFor(pr) {
|
|
1249
|
+
return this.store.db
|
|
1250
|
+
.prepare("SELECT * FROM finding_comments WHERE pr = ? ORDER BY comment_id")
|
|
1251
|
+
.all(pr);
|
|
1252
|
+
}
|
|
1253
|
+
reviewOutcomesFor(pr) {
|
|
1254
|
+
return this.store.db
|
|
1255
|
+
.prepare("SELECT * FROM review_outcomes WHERE pr = ? ORDER BY signal_id")
|
|
1256
|
+
.all(pr);
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* The per-lens fate table over findings posted since `sinceIso`. A finding
|
|
1260
|
+
* several lenses raised counts once under each of them; one raised outside
|
|
1261
|
+
* any known lens counts under `(no lens)`. Whole-review signals are counted
|
|
1262
|
+
* per review, never per lens.
|
|
1263
|
+
*/
|
|
1264
|
+
summary(sinceIso) {
|
|
1265
|
+
const rows = this.store.db
|
|
1266
|
+
.prepare(`SELECT f.lens_ids, f.lens, o.fate, o.human_replies,
|
|
1267
|
+
EXISTS (SELECT 1 FROM finding_comments c WHERE c.pr = f.pr AND c.round_no = f.round_no
|
|
1268
|
+
AND c.finding_key = f.finding_key) AS linked
|
|
1269
|
+
FROM round_findings f
|
|
1270
|
+
LEFT JOIN finding_outcomes o
|
|
1271
|
+
ON o.pr = f.pr AND o.round_no = f.round_no AND o.finding_key = f.finding_key
|
|
1272
|
+
WHERE f.disposition = 'posted' AND f.recorded_at >= ?`)
|
|
1273
|
+
.all(sinceIso);
|
|
1274
|
+
const empty = () => ({
|
|
1275
|
+
posted: 0,
|
|
1276
|
+
linked: 0,
|
|
1277
|
+
uncaptured: 0,
|
|
1278
|
+
...Object.fromEntries(FINDING_FATES.map((x) => [x, 0])),
|
|
1279
|
+
});
|
|
1280
|
+
const byLens = new Map();
|
|
1281
|
+
const total = empty();
|
|
1282
|
+
let replied = 0;
|
|
1283
|
+
let repliedKnown = 0;
|
|
1284
|
+
for (const r of rows) {
|
|
1285
|
+
const ids = storedLensIds(r.lens_ids) ?? [];
|
|
1286
|
+
const count = (c) => {
|
|
1287
|
+
c.posted++;
|
|
1288
|
+
if (r.linked)
|
|
1289
|
+
c.linked++;
|
|
1290
|
+
if (r.fate)
|
|
1291
|
+
c[r.fate]++;
|
|
1292
|
+
else
|
|
1293
|
+
c.uncaptured++;
|
|
1294
|
+
};
|
|
1295
|
+
for (const id of ids.length ? ids : ["(no lens)"]) {
|
|
1296
|
+
const c = byLens.get(id) ?? empty();
|
|
1297
|
+
count(c);
|
|
1298
|
+
byLens.set(id, c);
|
|
1299
|
+
}
|
|
1300
|
+
count(total);
|
|
1301
|
+
if ((r.human_replies ?? 0) > 0) {
|
|
1302
|
+
replied++;
|
|
1303
|
+
if (r.fate && r.fate !== "unknown" && r.fate !== "open_at_close")
|
|
1304
|
+
repliedKnown++;
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
const reviews = this.store.db
|
|
1308
|
+
.prepare("SELECT kind, COUNT(*) AS n FROM review_outcomes WHERE at >= ? GROUP BY kind ORDER BY kind")
|
|
1309
|
+
.all(sinceIso);
|
|
1310
|
+
return {
|
|
1311
|
+
since: sinceIso,
|
|
1312
|
+
total,
|
|
1313
|
+
lenses: [...byLens.entries()]
|
|
1314
|
+
.sort(([a, x], [b, y]) => y.posted - x.posted || a.localeCompare(b))
|
|
1315
|
+
.map(([lens, counts]) => ({ lens, ...counts })),
|
|
1316
|
+
replied_threads: replied,
|
|
1317
|
+
replied_known: repliedKnown,
|
|
1318
|
+
reviews: Object.fromEntries(reviews.map((r) => [r.kind, r.n])),
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
/** The summary as the operator reads it (`qcr outcomes`). */
|
|
1323
|
+
export function renderOutcomeSummary(s) {
|
|
1324
|
+
const pct = (n, d) => (d === 0 ? "—" : `${Math.round((100 * n) / d)}%`);
|
|
1325
|
+
const cols = [
|
|
1326
|
+
"posted",
|
|
1327
|
+
"linked",
|
|
1328
|
+
"fixed",
|
|
1329
|
+
"refuted",
|
|
1330
|
+
"upheld",
|
|
1331
|
+
"open@close",
|
|
1332
|
+
"unknown",
|
|
1333
|
+
"uncaptured",
|
|
1334
|
+
];
|
|
1335
|
+
const line = (name, c) => [
|
|
1336
|
+
name.padEnd(24),
|
|
1337
|
+
...[
|
|
1338
|
+
c.posted,
|
|
1339
|
+
c.linked,
|
|
1340
|
+
c.fixed,
|
|
1341
|
+
c.refuted_conceded,
|
|
1342
|
+
c.upheld_after_reply,
|
|
1343
|
+
c.open_at_close,
|
|
1344
|
+
c.unknown,
|
|
1345
|
+
c.uncaptured,
|
|
1346
|
+
].map((n, i) => String(n).padStart(cols[i].length + 1)),
|
|
1347
|
+
].join("");
|
|
1348
|
+
const out = [
|
|
1349
|
+
`Posted findings since ${s.since} — fate per lens (a finding several lenses raised counts under each).`,
|
|
1350
|
+
"",
|
|
1351
|
+
["lens".padEnd(24), ...cols.map((c) => ` ${c}`)].join(""),
|
|
1352
|
+
...s.lenses.map((l) => line(l.lens, l)),
|
|
1353
|
+
line("all findings", s.total),
|
|
1354
|
+
"",
|
|
1355
|
+
`linked to their comment: ${s.total.linked}/${s.total.posted} (${pct(s.total.linked, s.total.posted)})`,
|
|
1356
|
+
`human-replied threads with a known fate: ${s.replied_known}/${s.replied_threads} (${pct(s.replied_known, s.replied_threads)})`,
|
|
1357
|
+
`whole-review signals (per review, never per lens): set aside ${s.reviews.set_aside ?? 0}, merged over a block ${s.reviews.merged_over_block ?? 0}`,
|
|
1358
|
+
];
|
|
1359
|
+
return out.join("\n");
|
|
1360
|
+
}
|
|
1361
|
+
//# sourceMappingURL=outcomes.js.map
|