stacktrace-cli 0.5.1__tar.gz → 0.5.3__tar.gz
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.
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/PKG-INFO +30 -2
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/README.md +29 -1
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0031-one-host-daemon-observes-native-session-logs.md +1 -1
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0036-deliver-eligible-findings-to-slack-from-the-daemon.md +1 -1
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0045-scan-every-session-and-outlive-them.md +1 -1
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0046-the-finding-store-is-an-append-only-queue.md +1 -1
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0058-one-word-for-a-finding-columns-for-a-counter-and-a-push-that-does-not-wait.md +1 -1
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0059-the-daemon-does-not-decide-whether-it-should-be-running.md +2 -2
- stacktrace_cli-0.5.3/docs/adrs/0066-host-owned-daemon-supervision.md +197 -0
- stacktrace_cli-0.5.3/docs/adrs/0067-a-webhook-is-an-independent-queue-subscriber.md +100 -0
- stacktrace_cli-0.5.3/docs/adrs/0068-webhooks-retry-the-retained-backlog.md +80 -0
- stacktrace_cli-0.5.3/docs/adrs/0069-webhooks-deliver-what-retention-holds.md +86 -0
- stacktrace_cli-0.5.3/docs/adrs/0070-package-the-cloud-agent-environment-bootstrap.md +162 -0
- stacktrace_cli-0.5.3/docs/adrs/0071-render-the-allowlisted-payload-per-destination.md +89 -0
- stacktrace_cli-0.5.3/docs/adrs/0072-host-supervisors-own-daemon-restarts.md +95 -0
- stacktrace_cli-0.5.3/docs/adrs/0073-name-what-fired-and-which-component.md +114 -0
- stacktrace_cli-0.5.3/docs/adrs/0074-a-window-selects-findings-by-when-they-happened.md +115 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/INDEX.md +12 -1
- stacktrace_cli-0.5.3/docs/releases/v0.5.2.md +215 -0
- stacktrace_cli-0.5.3/docs/releases/v0.5.3.md +70 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/specs/daemon-scanning.md +22 -30
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/specs/notification-delivery.md +101 -59
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/pyproject.toml +2 -1
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/__init__.py +1 -1
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/analysis.py +68 -6
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/cli.py +2 -0
- stacktrace_cli-0.5.3/src/stacktrace_cli/cloud/__init__.py +39 -0
- stacktrace_cli-0.5.3/src/stacktrace_cli/cloud/setup.sh +274 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/activity_log.py +3 -3
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/cli.py +168 -19
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/client.py +134 -26
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/config.py +14 -5
- stacktrace_cli-0.5.3/src/stacktrace_cli/daemon/lifecycle.py +172 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/paths.py +3 -6
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/protocol.py +20 -1
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/runtime.py +136 -101
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/server.py +78 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/service.py +32 -17
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/store.py +36 -3
- stacktrace_cli-0.5.3/src/stacktrace_cli/daemon/webhook.py +508 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/render.py +8 -4
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/monitor/site/app.js +1 -1
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/monitor/site/index.html +3 -4
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/monitor/watch.py +37 -1
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/telemetry/events.py +3 -4
- stacktrace_cli-0.5.3/src/stacktrace_cli/webhook/__init__.py +1 -0
- stacktrace_cli-0.5.3/src/stacktrace_cli/webhook/cli.py +169 -0
- stacktrace_cli-0.5.3/src/stacktrace_cli/webhook/config.py +193 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/conftest.py +21 -6
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/test_activity_log.py +23 -3
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/test_cli.py +324 -32
- stacktrace_cli-0.5.3/tests/daemon/test_client.py +338 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/test_config.py +5 -4
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/test_fleet.py +3 -1
- stacktrace_cli-0.5.3/tests/daemon/test_lifecycle.py +115 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/test_runtime.py +493 -32
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/test_service.py +35 -18
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/test_socket.py +99 -3
- stacktrace_cli-0.5.3/tests/daemon/test_webhook.py +1072 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/test_wire_frames_are_bounded.py +2 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/monitor/test_reasoning_gates.py +3 -1
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/monitor/test_render.py +27 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/monitor/test_server.py +18 -6
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/monitor/test_watch.py +33 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_seam.py +12 -5
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_sync_detect.py +6 -1
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_analysis.py +133 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_no_live_document_states_the_old_stage_three_default.py +7 -1
- stacktrace_cli-0.5.3/tests/test_title_disclosure_invariant.py +159 -0
- stacktrace_cli-0.5.3/tests/webhook/test_cli.py +662 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/uv.lock +1 -1
- stacktrace_cli-0.5.1/src/stacktrace_cli/daemon/version.py +0 -49
- stacktrace_cli-0.5.1/tests/daemon/test_client.py +0 -130
- stacktrace_cli-0.5.1/tests/daemon/test_version.py +0 -48
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/.agents/skills/release-stacktrace/SKILL.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/.claude/settings.json +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/.claude/skills/release-stacktrace/SKILL.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/.codex/hooks.json +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/.github/managed-autofix.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/.github/workflows/autofix.yml +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/.github/workflows/ci.yml +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/.github/workflows/claude.yml +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/.github/workflows/publish-pypi.yml +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/.gitignore +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/AGENTS.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/CLAUDE.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0001-session-telemetry-as-input.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0002-session-collection-in-openaidr.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0003-runtime-edges.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0004-three-stage-detector.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0005-detection-family-and-report-assembly.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0006-trust-boundary-and-detection-upload.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0007-proprietary-package-on-open-dependencies.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0008-console-in-separate-demo-package.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0010-detection-severity-and-confidence-ladders.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0011-verdict-cache.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0012-observation-evidence-kinds-and-transport.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0013-rule-catalogue-triage-and-per-rule-context.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0014-declared-project-mapping.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0015-narrow-the-security-catalogue.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0016-stall-grouping-is-session-wide.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0017-a-declined-repeat-is-a-stall.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0018-remote-sync-config-and-facade-consumption.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0019-delegated-commands-are-openaca-objects.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0020-openaca-consumption-boundary.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0021-two-command-kinds.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0022-detection-scope-is-a-catalogue-column.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0023-sync-detect-collects-and-does-not-escalate.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0024-the-upload-carries-observations.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0025-a-denied-call-is-activity-never-an-invocation.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0026-the-console-becomes-a-product-surface.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0027-a-finding-may-name-what-it-could-not-place.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0028-the-console-shows-what-it-placed.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0029-stage-three-returns-to-opt-in.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0030-a-detection-is-dated-by-the-turns-it-cites.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0032-refresh-daemon-compositions-outside-detection.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0033-a-third-party-analyzer-only-by-a-flag-that-names-it.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0034-the-drift-grade-is-read-off-a-run-not-a-call.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0035-naming-an-analyzer-selects-stored-answers.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0037-reuse-openaidr-append-cursors-in-the-daemon.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0038-poll-normalized-sessions-for-supplemental-changes.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0039-send-usage-telemetry-by-default-with-an-opt-out.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0041-defer-native-windows-support.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0042-read-the-build-commit-from-the-install-record.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0043-name-the-session-agent-and-host-and-count-errors-by-class.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0044-send-the-build-as-version-prints-it.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0047-capture-activity-during-correlation-or-lose-it.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0048-the-daemon-keeps-a-leveled-content-free-activity-log.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0056-stop-a-resident-daemon-by-record-and-signal.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0057-grow-the-queue-schema-additively-and-share-only-the-document.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0060-two-upload-postures-not-one-command-with-two-sources.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0061-the-drain-sends-findings-only.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0062-hold-the-transition-lock-across-remote-side-effects.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0063-underbuilt-debt-does-not-hold-the-scan-watermark.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0064-a-redacted-value-has-one-path-span.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/0065-asset-external-id-resolves-by-a-ladder.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/HOOK-PROMPT.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/adrs/TEMPLATE.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/cutover-openaca-remote.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/plans/002-session-input.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/plans/003-correlation.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/plans/004-detector.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/plans/005-remote-sync.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/plans/006-cli-composition.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/plans/007-detection-upload.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/plans/008-monitor.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/plans/009-reasoning-jev.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/plans/010-session-daemon.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/plans/011-daemon-store.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/plans/012-daemon-scanning.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/plans/013-daemon-uploads.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/plans/014-asset-identity.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/releases/v0.0.1.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/releases/v0.1.0.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/releases/v0.2.0.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/releases/v0.2.1.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/releases/v0.2.2.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/releases/v0.2.3.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/releases/v0.3.0.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/releases/v0.3.1.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/releases/v0.4.0.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/releases/v0.4.1.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/releases/v0.5.0.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/releases/v0.5.1.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/research/001-intent-drift-metric.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/specs/aidr.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/specs/cli-composition.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/specs/correlation.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/specs/daemon-store.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/specs/daemon-uploads.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/specs/detection-upload.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/specs/detector.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/specs/event-time.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/specs/monitor.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/specs/reasoning-jev.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/specs/remote-sync.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/docs/specs/session-input.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/scripts/git-hooks/pre-push +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/scripts/install-hooks.sh +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/__main__.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/build.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/correlate/__init__.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/correlate/acquire.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/correlate/composition.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/correlate/join.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/correlate/observed.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/correlate/orchestrate.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/correlate/project_map.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/correlate/record.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/correlate/render.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/__init__.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/bom.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/composition.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/db.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/fleet.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/identity.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/jobs.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/observer.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/presentation.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/daemon/scan.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/__init__.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/analyzer.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/blocked.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/cache.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/deterministic.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/finding.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/history.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/jev.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/priors.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/prompts/__init__.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/prompts/jev-v1/stacktrace-deceptive-completion.json +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/prompts/jev-v2/stacktrace-deceptive-completion.json +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/prompts/v1/exclusions.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/prompts/v1/framing.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/prompts/v1/stacktrace-deceptive-completion.md +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/reasoning.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/rules.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/run.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/secrets.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/verdict.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/detector/windows.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/monitor/__init__.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/monitor/reasoning.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/monitor/render.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/monitor/server.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/monitor/site/fonts/OFL.txt +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/monitor/site/fonts/dm-mono-400-latin.woff2 +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/monitor/site/fonts/dm-mono-500-latin.woff2 +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/monitor/site/fonts/dm-sans-latin.woff2 +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/monitor/site/styles.css +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/monitor/state.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/monitor/verdicts.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/options.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/private_state.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/remote/__init__.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/remote/cli.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/remote/client.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/remote/config.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/remote/detect_payload.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/remote/identity.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/remote/payload.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/remote/policy.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/remote/redact.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/remote/spool.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/remote/sync.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/remote/sync_detect.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/remote/upload_contract.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/sessions/__init__.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/sessions/access.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/sessions/index.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/sessions/outcome.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/sessions/protocols.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/sessions/render.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/telemetry/__init__.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/telemetry/cli.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/telemetry/posthog.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/src/stacktrace_cli/telemetry/state.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/__init__.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/agent_bom_fixture.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/__init__.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/test_composition.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/test_db.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/test_detection_needs_no_subscriber.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/test_identity.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/test_jobs.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/test_observer.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/test_queue_reader.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/test_scan.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/test_store.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/test_the_drained_payload_is_the_cold_one.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/daemon/test_the_stored_row_is_sufficient.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/detector_session_fixture.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/monitor/__init__.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/monitor/feed_harness.mjs +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/monitor/test_feed_model.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/monitor/test_site_assets.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/monitor/test_state.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/monitor/test_verdicts.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/__init__.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/helpers.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_cli.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_client.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_config.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_detect_activity.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_detect_cli.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_detect_client.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_detect_contract_is_exhaustive.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_detect_gate_matches_the_cloud.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_detect_layers_compose.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_detect_payload_reaches_the_cloud_model.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_detect_redaction.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_detect_spool.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_detect_upload_contract.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_detect_wire_payload.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_facade_contract.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_identity.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_no_local_path_survives_redaction.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_payload.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_policy.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_redact_payload.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_spool.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_sync.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/remote/test_upload_contract.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/telemetry/__init__.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/telemetry/test_activity_log.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/telemetry/test_cli.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/telemetry/test_events.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/telemetry/test_hooks.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/telemetry/test_release_gate.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/telemetry/test_state.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/telemetry/test_uploader.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_acquire.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_adr_cross_references_are_reciprocal.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_agent_instance_id.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_bom_shape.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_build_commit.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_call_status_conventions.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_cli.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_composition.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_correlate_orchestrate.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_correlate_render.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_correlated_session.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detection_components.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detection_event_time.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detection_subject.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detector_analyzer_contract.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detector_analyzer_live.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detector_blocked.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detector_cache.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detector_deterministic.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detector_end_to_end.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detector_finding.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detector_history.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detector_jev.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detector_priors.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detector_prompts.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detector_reasoning.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detector_render.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detector_run.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detector_secrets.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detector_verdict.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detector_why_it_stopped.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_detector_windows.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_evidence_is_dated_not_numbered.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_fingerprint_covers_what_the_analyzer_reads.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_help_sections.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_join.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_monitor_cli.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_no_test_undoes_every_patch_at_once.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_observed.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_openaca_contract.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_openaidr_contract.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_option_arity_matches_the_binary.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_project_map.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_readme_examples_are_real_output.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_release_gate_coverage.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_release_readiness.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_seam_boundary.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_session_identity_is_the_pair.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_sessions_access.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_sessions_end_to_end.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_sessions_protocol_typing.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_sessions_protocols.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_sessions_render.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_shell_separators_match_the_shell.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_stored_verdicts_are_never_gated_on_reasoning.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_the_detector_spec_lists_the_shipped_catalogue.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_the_jev_spec_states_the_shipped_thresholds.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_the_locality_claim_is_qualified_everywhere.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_untrusted_content_never_travels.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_verification_subcommands_match_the_binary.py +0 -0
- {stacktrace_cli-0.5.1 → stacktrace_cli-0.5.3}/tests/test_what_jev_is_told_is_what_was_disclosed.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: stacktrace-cli
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.3
|
|
4
4
|
Summary: CLI for Stacktrace — Detection and Response platform for AI Agents.
|
|
5
5
|
Project-URL: Homepage, https://stacktrace.ai
|
|
6
6
|
Author-email: "Stacktrace AI, Inc" <founders@stacktrace.ai>
|
|
@@ -51,7 +51,7 @@ Requires Python 3.11 or newer.
|
|
|
51
51
|
|
|
52
52
|
```console
|
|
53
53
|
$ stacktrace --version
|
|
54
|
-
stacktrace 0.5.
|
|
54
|
+
stacktrace 0.5.3 (openaca 0.7.0)
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
## Quick start
|
|
@@ -70,6 +70,7 @@ stacktrace monitor # the same, live in a browser
|
|
|
70
70
|
| `detect` | Find security and reliability findings in what agents did. |
|
|
71
71
|
| `monitor` | Watch this machine's agents in a browser, live. |
|
|
72
72
|
| `remote` | Configure remote endpoint services and upload to Stacktrace Cloud. |
|
|
73
|
+
| `webhook` | Send eligible daemon findings to a host-level HTTPS endpoint. |
|
|
73
74
|
| `scan` | Scan a repository or endpoint for agent-composition findings. |
|
|
74
75
|
| `bom` | Generate an Agent BOM for a repository or endpoint. |
|
|
75
76
|
| `policy` | Validate and compile restrictive endpoint policies. |
|
|
@@ -78,6 +79,33 @@ The last three are composition analysis, supplied by
|
|
|
78
79
|
[`openaca`](https://pypi.org/project/openaca/) and available under either
|
|
79
80
|
name.
|
|
80
81
|
|
|
82
|
+
### Webhook notifications
|
|
83
|
+
|
|
84
|
+
Configure one host-level endpoint; the running daemon delivers retained eligible
|
|
85
|
+
findings and new findings automatically:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
stacktrace webhook configure --url https://alerts.example.com/stacktrace
|
|
89
|
+
stacktrace webhook status
|
|
90
|
+
stacktrace webhook disable
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`configure` prompts for an optional HMAC-SHA256 signing secret without echoing
|
|
94
|
+
it. The endpoint receives a narrow JSON document containing the event ID, rule,
|
|
95
|
+
severity, confidence and session identity—not transcript content, finding prose
|
|
96
|
+
or evidence. Delivery is at least once, so receivers should deduplicate on
|
|
97
|
+
`event_id`. The URL and secret are stored in a mode-0600 host configuration
|
|
98
|
+
file. HTTP is accepted only for loopback development endpoints.
|
|
99
|
+
|
|
100
|
+
Failed deliveries retry without a limit while enabled. Outages can grow the
|
|
101
|
+
local backlog, bounded by the local retention window—findings older than that
|
|
102
|
+
when the endpoint next accepts requests are not sent. Changing the endpoint
|
|
103
|
+
sends the remaining backlog to the new destination; rotating its signing
|
|
104
|
+
secret preserves progress. Configure and disable take effect from the next
|
|
105
|
+
request: one already in flight may still complete with the previous
|
|
106
|
+
configuration. Re-enabling resumes only pending findings still within the
|
|
107
|
+
retention window, not already-delivered findings.
|
|
108
|
+
|
|
81
109
|
## What it looks like
|
|
82
110
|
|
|
83
111
|
```console
|
|
@@ -24,7 +24,7 @@ Requires Python 3.11 or newer.
|
|
|
24
24
|
|
|
25
25
|
```console
|
|
26
26
|
$ stacktrace --version
|
|
27
|
-
stacktrace 0.5.
|
|
27
|
+
stacktrace 0.5.3 (openaca 0.7.0)
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
## Quick start
|
|
@@ -43,6 +43,7 @@ stacktrace monitor # the same, live in a browser
|
|
|
43
43
|
| `detect` | Find security and reliability findings in what agents did. |
|
|
44
44
|
| `monitor` | Watch this machine's agents in a browser, live. |
|
|
45
45
|
| `remote` | Configure remote endpoint services and upload to Stacktrace Cloud. |
|
|
46
|
+
| `webhook` | Send eligible daemon findings to a host-level HTTPS endpoint. |
|
|
46
47
|
| `scan` | Scan a repository or endpoint for agent-composition findings. |
|
|
47
48
|
| `bom` | Generate an Agent BOM for a repository or endpoint. |
|
|
48
49
|
| `policy` | Validate and compile restrictive endpoint policies. |
|
|
@@ -51,6 +52,33 @@ The last three are composition analysis, supplied by
|
|
|
51
52
|
[`openaca`](https://pypi.org/project/openaca/) and available under either
|
|
52
53
|
name.
|
|
53
54
|
|
|
55
|
+
### Webhook notifications
|
|
56
|
+
|
|
57
|
+
Configure one host-level endpoint; the running daemon delivers retained eligible
|
|
58
|
+
findings and new findings automatically:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
stacktrace webhook configure --url https://alerts.example.com/stacktrace
|
|
62
|
+
stacktrace webhook status
|
|
63
|
+
stacktrace webhook disable
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`configure` prompts for an optional HMAC-SHA256 signing secret without echoing
|
|
67
|
+
it. The endpoint receives a narrow JSON document containing the event ID, rule,
|
|
68
|
+
severity, confidence and session identity—not transcript content, finding prose
|
|
69
|
+
or evidence. Delivery is at least once, so receivers should deduplicate on
|
|
70
|
+
`event_id`. The URL and secret are stored in a mode-0600 host configuration
|
|
71
|
+
file. HTTP is accepted only for loopback development endpoints.
|
|
72
|
+
|
|
73
|
+
Failed deliveries retry without a limit while enabled. Outages can grow the
|
|
74
|
+
local backlog, bounded by the local retention window—findings older than that
|
|
75
|
+
when the endpoint next accepts requests are not sent. Changing the endpoint
|
|
76
|
+
sends the remaining backlog to the new destination; rotating its signing
|
|
77
|
+
secret preserves progress. Configure and disable take effect from the next
|
|
78
|
+
request: one already in flight may still complete with the previous
|
|
79
|
+
configuration. Re-enabling resumes only pending findings still within the
|
|
80
|
+
retention window, not already-delivered findings.
|
|
81
|
+
|
|
54
82
|
## What it looks like
|
|
55
83
|
|
|
56
84
|
```console
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 0066
|
|
3
|
+
title: Put daemon lifetime under host-owned supervision
|
|
4
|
+
status: superseded
|
|
5
|
+
date: 2026-09-23
|
|
6
|
+
supersedes: 0059
|
|
7
|
+
superseded-by: 0072
|
|
8
|
+
amends: 0031, 0045, 0058
|
|
9
|
+
amended-by: null
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Context
|
|
13
|
+
|
|
14
|
+
ADR-0031 tied daemon lifetime to subscribers, and ADR-0045 made the daemon
|
|
15
|
+
resident without changing who starts it. The plugin monitor still calls
|
|
16
|
+
`ensure_daemon()`, which forks `daemon run` as the monitor's child. Starting a
|
|
17
|
+
new session keeps terminal signals away from the child, but does not remove it
|
|
18
|
+
from the monitor's process tree.
|
|
19
|
+
|
|
20
|
+
That distinction caused an outage on 2026-09-22. Ending one Claude session
|
|
21
|
+
killed its monitor's entire descendant tree, including the daemon that other
|
|
22
|
+
sessions were using. Their monitors lost the socket and one eventually started
|
|
23
|
+
a replacement. The terminated daemon wrote no traceback, skipped shutdown
|
|
24
|
+
cleanup, left its socket behind and did not flush telemetry. A focused
|
|
25
|
+
reproduction showed why: `setsid` survives a signal to the parent or its
|
|
26
|
+
process group, but not a tree kill that follows parent identifiers.
|
|
27
|
+
|
|
28
|
+
The first subscriber therefore cannot own a machine-wide service. The hosts
|
|
29
|
+
also provide no single supervisor API. Laptops have launchd or systemd;
|
|
30
|
+
Niteshift has declared services; Claude Code and Codex cloud sandboxes have no
|
|
31
|
+
usable user systemd; Claude Managed Agents expose no startup hook. Except for
|
|
32
|
+
Managed Agents, each environment can still run owner-controlled setup or
|
|
33
|
+
session-start commands.
|
|
34
|
+
|
|
35
|
+
Cloud probes established the portable minimum. A process survives into the
|
|
36
|
+
agent phase when its launcher calls `setsid`, double-forks, redirects standard
|
|
37
|
+
streams and closes every inherited descriptor. On Codex, leaving even one
|
|
38
|
+
runner descriptor open prevents setup from finishing. Cached Codex containers
|
|
39
|
+
can also run setup and maintenance starters together, so concurrent starts are
|
|
40
|
+
normal rather than exceptional.
|
|
41
|
+
|
|
42
|
+
ADR-0059 removed every lifecycle command in favor of a future external
|
|
43
|
+
supervisor. That is sufficient on a host with launchd, systemd or a declared
|
|
44
|
+
service, but it leaves cloud sandboxes without a lifecycle owner even though
|
|
45
|
+
their setup hooks can safely launch a fully detached process. This decision
|
|
46
|
+
supersedes ADR-0059: the daemon still does not decide whether it should run,
|
|
47
|
+
but Stacktrace supplies a portable supervisor for hosts that do not have one.
|
|
48
|
+
|
|
49
|
+
## Decision
|
|
50
|
+
|
|
51
|
+
Daemon lifecycle belongs to host configuration. Agent adapters and monitors
|
|
52
|
+
only connect; they never create a daemon as a side effect of subscribing. A
|
|
53
|
+
host may invoke Stacktrace's detached supervisor from a setup or SessionStart
|
|
54
|
+
hook, but the resulting process is not owned by that agent session.
|
|
55
|
+
|
|
56
|
+
1. **`stacktrace daemon run` is the foreground service.** It remains attached
|
|
57
|
+
to its caller until it stops, so an external supervisor or container
|
|
58
|
+
entrypoint can observe it directly. SIGTERM and SIGINT stop the tick loop,
|
|
59
|
+
flush telemetry, close compositions, remove the socket and return the
|
|
60
|
+
deliberate shutdown status. If another daemon holds `daemon.lock`, `run`
|
|
61
|
+
returns without taking ownership. The running process continues to write
|
|
62
|
+
its pid, version and effective configuration to the run-state file, and
|
|
63
|
+
`daemon status` reads that record rather than re-deriving configuration from
|
|
64
|
+
its own environment.
|
|
65
|
+
2. **`stacktrace daemon start` launches a detached supervisor.** The launcher
|
|
66
|
+
calls `setsid`, double-forks, redirects stdin from `/dev/null`, sends stdout
|
|
67
|
+
and stderr to the daemon log and closes every caller-provided descriptor. A
|
|
68
|
+
private readiness channel may survive long enough to report that the
|
|
69
|
+
detached process acquired `supervisor.lock`, but it is closed before
|
|
70
|
+
`start` returns. Two concurrent starts therefore converge on one supervisor
|
|
71
|
+
and both commands succeed. The supervisor keeps only descriptors it owns.
|
|
72
|
+
3. **Every daemon child starts through a fresh exec of the installed CLI.** The
|
|
73
|
+
supervisor never forks an already-imported daemon or calls it in-process.
|
|
74
|
+
This is the boundary that makes a restart load newly installed code while
|
|
75
|
+
allowing the small supervisor loop itself to remain old.
|
|
76
|
+
4. **Exit status controls supervision.** Zero means the daemon chose to stay
|
|
77
|
+
down; non-zero means start it again. `daemon stop` and a recorded
|
|
78
|
+
`daemon disable` return zero. Lock contention and an already-live socket
|
|
79
|
+
return non-zero instead: they mean another process already holds the role,
|
|
80
|
+
not that the role is deliberately unfilled, and nothing here confirms that
|
|
81
|
+
process has a supervisor of its own. This is the case a host hits the first
|
|
82
|
+
time it layers this supervisor onto a daemon still running from the
|
|
83
|
+
pre-ADR-0066 `ensure_daemon()` path — treating that as "stay down" would
|
|
84
|
+
exit the only supervisor watching the machine and strand the daemon the
|
|
85
|
+
moment it later exits for its own `version_label()` restart. A stop meant
|
|
86
|
+
to hold through that retry window must be `daemon disable`: its marker is
|
|
87
|
+
checked before every fresh-exec'd child opens a socket, supervisor retries
|
|
88
|
+
included, while `daemon stop` reaches only the process it signals — the
|
|
89
|
+
incumbent, not a supervisor waiting behind it, so the supervisor still
|
|
90
|
+
takes over once the incumbent is gone. An unhandled crash also returns
|
|
91
|
+
non-zero. Version divergence from ADR-0045 returns a dedicated non-zero
|
|
92
|
+
restart code. Stop and disable take precedence if they race with an
|
|
93
|
+
upgrade. External supervisor recipes use restart-on-failure semantics.
|
|
94
|
+
5. **The detached supervisor restarts with bounded backoff.** It waits one
|
|
95
|
+
second after a non-zero exit, doubles to at most thirty seconds, and resets
|
|
96
|
+
the delay after the child has stayed up for sixty seconds. SIGTERM sent to
|
|
97
|
+
the supervisor is forwarded to the child, whose exit is awaited before the
|
|
98
|
+
supervisor exits. A zero child exit ends the supervisor.
|
|
99
|
+
6. **Subscribing has no lifecycle side effect.** `ensure_daemon()` is deleted.
|
|
100
|
+
`daemon subscribe` reports once that no daemon is running, keeps retrying and
|
|
101
|
+
reports when it reconnects. The socket handshake carries an integer protocol
|
|
102
|
+
version: a compatibility epoch, not a package or build version. Only an
|
|
103
|
+
exact match proceeds. On a mismatch the daemon returns
|
|
104
|
+
`unsupported_version` and closes that connection; the subscriber reports
|
|
105
|
+
that the agent session must be restarted and exits instead of retrying. The
|
|
106
|
+
daemon remains running because one incompatible subscriber must not disrupt
|
|
107
|
+
other sessions. Daemon code freshness remains the separate
|
|
108
|
+
`version_label()` exit-and-restart decision from ADR-0045.
|
|
109
|
+
7. **The host decides when to call `start`.** A person does so on a laptop. A
|
|
110
|
+
repository SessionStart hook may do so in Claude Code cloud, and Codex cloud
|
|
111
|
+
may do so from setup and maintenance scripts. Niteshift and containers run
|
|
112
|
+
`daemon run` under their native supervisor. These are deployment recipes,
|
|
113
|
+
not plugin behavior.
|
|
114
|
+
|
|
115
|
+
This replaces ADR-0031 decision 6 and ADR-0045's assumption that
|
|
116
|
+
`ensure_daemon()` remains the daemon's starter. ADR-0031's routing,
|
|
117
|
+
persistence and delivery decisions, and ADR-0045's scanning, stop and disable
|
|
118
|
+
decisions, remain in force. It supersedes ADR-0059's removal of lifecycle
|
|
119
|
+
commands: host configuration still owns the decision, while `daemon start`
|
|
120
|
+
provides the supervisor on hosts that lack a native one.
|
|
121
|
+
|
|
122
|
+
## Alternatives considered
|
|
123
|
+
|
|
124
|
+
- **Install launchd and systemd units.** Rejected for now. Neither helps in the
|
|
125
|
+
cloud sandboxes, and a daemon needed only while agents run does not yet
|
|
126
|
+
justify platform-specific install, detection and uninstall paths. Revisit
|
|
127
|
+
when laptops need pre-session or reboot persistence.
|
|
128
|
+
- **Expose only detached `daemon start`.** Rejected because native supervisors
|
|
129
|
+
need a foreground process whose lifetime and exit status they own. A forking
|
|
130
|
+
service conflicts with systemd control groups, launchd process groups,
|
|
131
|
+
declared sandbox services and container entrypoints.
|
|
132
|
+
- **Keep `ensure_daemon()` and make its child detach correctly.** Rejected
|
|
133
|
+
because it fixes ancestry but not ownership. Connection would still decide
|
|
134
|
+
whether the machine-wide service exists, and host configuration could not
|
|
135
|
+
choose its start boundary.
|
|
136
|
+
- **Start the daemon from the plugin's own SessionStart hook.** Rejected. A
|
|
137
|
+
host-owned hook may invoke `start`, but daemon availability is not a plugin
|
|
138
|
+
responsibility and other agent adapters must not copy that behavior.
|
|
139
|
+
- **Use launchd or systemd socket activation.** Rejected because the daemon
|
|
140
|
+
would have to adopt inherited sockets on both platforms, while the sandbox
|
|
141
|
+
hosts would still need another lifecycle mechanism.
|
|
142
|
+
- **Require `daemon start --restart` after every upgrade.** Rejected because it
|
|
143
|
+
is easy to omit and leaves every monitor reporting an obsolete daemon until
|
|
144
|
+
someone intervenes. The fresh-exec restart code makes the installed upgrade
|
|
145
|
+
take effect without adding a provisioning step.
|
|
146
|
+
- **Compare package or build versions during the socket handshake.** Rejected
|
|
147
|
+
because code freshness and wire compatibility are different facts. Commit
|
|
148
|
+
labels are not orderable, while compatible package releases should not end a
|
|
149
|
+
working subscription. The protocol epoch changes only when the wire becomes
|
|
150
|
+
incompatible.
|
|
151
|
+
- **Stop both processes on a protocol mismatch.** Rejected because the daemon
|
|
152
|
+
is shared across sessions. A stale subscriber may end its own connection and
|
|
153
|
+
process; it cannot take down delivery for compatible subscribers.
|
|
154
|
+
|
|
155
|
+
## Consequences
|
|
156
|
+
|
|
157
|
+
An agent session can end, crash or have its descendant tree killed without
|
|
158
|
+
taking down the daemon used by other sessions. Adapters become strict clients,
|
|
159
|
+
which gives later Codex and Cursor integrations the same lifecycle boundary as
|
|
160
|
+
Claude.
|
|
161
|
+
|
|
162
|
+
The portable supervisor is intentionally small, but nothing supervises that
|
|
163
|
+
supervisor. If it dies, its current child can continue, but no replacement is
|
|
164
|
+
launched after that child exits until the host calls `start` again. A laptop
|
|
165
|
+
also needs another `start` after logout or reboot.
|
|
166
|
+
|
|
167
|
+
Full descriptor closure is part of correctness. A test must hold a pipe on an
|
|
168
|
+
extra descriptor across `start` and observe EOF. A second test must replace the
|
|
169
|
+
installed version, cause version divergence and prove that the next child
|
|
170
|
+
executes the new code rather than the supervisor's imported code.
|
|
171
|
+
|
|
172
|
+
Protocol tests must prove that an exact epoch match subscribes, a mismatch
|
|
173
|
+
ends the subscriber with restart guidance, and the daemon remains available to
|
|
174
|
+
a subsequent compatible subscriber.
|
|
175
|
+
|
|
176
|
+
A migration test must start a daemon directly, the way the deleted
|
|
177
|
+
`ensure_daemon()` did, then start the supervisor against it and prove the
|
|
178
|
+
supervisor keeps retrying with backoff rather than exiting, and that it takes
|
|
179
|
+
over once the direct process exits. A second case must prove `daemon disable`
|
|
180
|
+
recorded during that retry window keeps the supervisor down, while a bare
|
|
181
|
+
`daemon stop` against the direct process only ends that process and does not
|
|
182
|
+
stop the waiting supervisor from taking over.
|
|
183
|
+
|
|
184
|
+
The host must run Stacktrace as the same user as the agents it observes. The
|
|
185
|
+
daemon reads that user's agent logs and binds below `/tmp/stacktrace-$UID/`.
|
|
186
|
+
Claude Managed Agents cannot run this daemon until they expose a startup or
|
|
187
|
+
supervised-service mechanism; self-hosted environments can use their image
|
|
188
|
+
entrypoint.
|
|
189
|
+
|
|
190
|
+
## When to revisit
|
|
191
|
+
|
|
192
|
+
- Add launchd or systemd installation when laptops need automatic startup or
|
|
193
|
+
reboot persistence.
|
|
194
|
+
- Prefer a sandbox's native service manager whenever it provides one.
|
|
195
|
+
- Reconsider the portable supervisor if its own failures become material.
|
|
196
|
+
- Revisit cloud deployment if an agent host removes owner-controlled startup
|
|
197
|
+
commands or supplies a first-class replacement.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 0067
|
|
3
|
+
title: A webhook is an independent queue subscriber with an allowlisted payload
|
|
4
|
+
status: accepted
|
|
5
|
+
date: 2026-09-24
|
|
6
|
+
supersedes: null
|
|
7
|
+
superseded-by: null
|
|
8
|
+
amends: 0036, 0046
|
|
9
|
+
amended-by: 0068, 0071, 0073
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Context
|
|
13
|
+
|
|
14
|
+
The daemon already delivers eligible findings to the active agent session and
|
|
15
|
+
to Fleet. A host owner also needs a generic integration point for destinations
|
|
16
|
+
such as an incident router or automation service, without installing an
|
|
17
|
+
agent-specific adapter or making Fleet availability a prerequisite.
|
|
18
|
+
|
|
19
|
+
The finding queue now has a monotonic sequence and a cursor per subscriber and
|
|
20
|
+
agent-kind partition. That makes the generic sink a queue reader, not another
|
|
21
|
+
branch on the scan path. The stored finding is intentionally richer than any
|
|
22
|
+
outbound payload, so forwarding it directly would also widen the data boundary
|
|
23
|
+
to detector prose, evidence descriptors, spans and component details.
|
|
24
|
+
|
|
25
|
+
## Decision
|
|
26
|
+
|
|
27
|
+
One optional host-level webhook is a subscriber named `webhook`.
|
|
28
|
+
|
|
29
|
+
- It reads every queue revision in order and advances its own cursor only after
|
|
30
|
+
an HTTP 2xx. A failed request holds that row for retry and cannot move or
|
|
31
|
+
block the Fleet cursor.
|
|
32
|
+
- Only the exact revision that first crossed the local notification gate is
|
|
33
|
+
delivered. Later revisions still advance the cursor but do not ring again.
|
|
34
|
+
- Configuration time is the lower delivery bound. Enabling a webhook does not
|
|
35
|
+
replay findings that predate the configuration.
|
|
36
|
+
- The JSON body is an allowlist: schema version, event type, event ID, rule ID,
|
|
37
|
+
severity, confidence, agent kind and native session ID. Stored titles,
|
|
38
|
+
remediation, evidence, spans, participants and unknown records never enter
|
|
39
|
+
the mapper.
|
|
40
|
+
- Delivery is at least once. A process can stop after the remote endpoint
|
|
41
|
+
accepts a request and before SQLite commits the cursor, so receivers use the
|
|
42
|
+
stable event ID as their idempotency key.
|
|
43
|
+
- The endpoint is HTTPS, except that HTTP is accepted on loopback for local
|
|
44
|
+
development. Userinfo and fragments are refused. An optional secret signs
|
|
45
|
+
the exact request bytes with HMAC-SHA256.
|
|
46
|
+
- The URL, optional signing secret and configuration time live in a private
|
|
47
|
+
host file. CLI output and daemon logs never print the URL path, query,
|
|
48
|
+
secret, response body or exception message.
|
|
49
|
+
- A scan commit wakes a single webhook worker thread; the daemon's scan and
|
|
50
|
+
scheduler thread never waits on HTTP. Wakes coalesce while a delivery is in
|
|
51
|
+
progress. A one-minute job is the retry fallback; setting its interval to
|
|
52
|
+
zero disables only that fallback.
|
|
53
|
+
|
|
54
|
+
This amends ADR-0036's proposed fan-out implementation. The independence and
|
|
55
|
+
per-destination acknowledgement rules remain, but SQLite cursors introduced by
|
|
56
|
+
ADR-0046 are the durable fan-out mechanism for resident subscribers; no shared
|
|
57
|
+
sink registry or per-event acknowledgement map is added.
|
|
58
|
+
|
|
59
|
+
## Alternatives considered
|
|
60
|
+
|
|
61
|
+
- **Send from the scanner callback.** Rejected: network latency and failure
|
|
62
|
+
would enter the detection hot path, and a crash after the scan would lose the
|
|
63
|
+
only delivery attempt.
|
|
64
|
+
- **Reuse Fleet's cursor.** Rejected: whichever destination acknowledged first
|
|
65
|
+
would skip work for the other, and a slow webhook would block Fleet.
|
|
66
|
+
- **Forward the stored finding.** Rejected: storage is not a wire contract and
|
|
67
|
+
contains fields this destination does not need. The allowlist is the data
|
|
68
|
+
boundary.
|
|
69
|
+
- **Add a delivery row for every `(event_id, sink)`.** Rejected: delivery is
|
|
70
|
+
ordered within each agent-kind partition, so the existing subscriber cursor
|
|
71
|
+
already represents every acknowledgement below it.
|
|
72
|
+
- **Replay the retained backlog when configured.** Rejected: configuration is
|
|
73
|
+
consent for future delivery, not consent to export findings retained before
|
|
74
|
+
that choice.
|
|
75
|
+
- **Exactly-once delivery.** Rejected: it requires the receiver to participate
|
|
76
|
+
in a transaction with the local database. Stable event IDs make receiver-side
|
|
77
|
+
deduplication possible without claiming an impossible transport guarantee.
|
|
78
|
+
|
|
79
|
+
## Consequences
|
|
80
|
+
|
|
81
|
+
A webhook outage accumulates work behind only its own cursor. Retention keeps
|
|
82
|
+
that backlog while the subscriber remains configured, so a permanently broken
|
|
83
|
+
endpoint can increase local database use until it is disabled.
|
|
84
|
+
|
|
85
|
+
Receivers get enough information to route and deduplicate an alert, but not a
|
|
86
|
+
human-readable explanation. They use the event ID and session identity to link
|
|
87
|
+
back to the local finding rather than asking the endpoint to export more data.
|
|
88
|
+
|
|
89
|
+
Changing the endpoint starts a new consent boundary at the new configuration
|
|
90
|
+
time. Re-entering the same endpoint preserves that time so rotating its signing
|
|
91
|
+
secret does not silently skip a retry already in progress.
|
|
92
|
+
|
|
93
|
+
## When to revisit
|
|
94
|
+
|
|
95
|
+
- If more than one generic webhook per host is required; each needs its own
|
|
96
|
+
stable subscriber identity and cursor.
|
|
97
|
+
- If receivers need more context; widening the payload requires a new data-
|
|
98
|
+
boundary decision and exhaustive non-leak tests.
|
|
99
|
+
- If queue partitions can be delivered concurrently; ordering and cursor
|
|
100
|
+
advancement would need a lease or another claim mechanism.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 0068
|
|
3
|
+
title: Webhooks retry the retained backlog without a configuration-time cutoff
|
|
4
|
+
status: accepted
|
|
5
|
+
date: 2026-09-25
|
|
6
|
+
supersedes: null
|
|
7
|
+
superseded-by: null
|
|
8
|
+
amends: 0067
|
|
9
|
+
amended-by: 0069
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Context
|
|
13
|
+
|
|
14
|
+
ADR-0067 already provides durable delivery through an independent subscriber
|
|
15
|
+
cursor. Its configuration-time lower bound adds timestamp validation and clock
|
|
16
|
+
ordering to a queue that already has an ordered sequence. It also discards
|
|
17
|
+
pending deliveries when the endpoint changes. These semantics are unnecessary
|
|
18
|
+
for a single host-level destination whose purpose is reliable alerting.
|
|
19
|
+
|
|
20
|
+
## Decision
|
|
21
|
+
|
|
22
|
+
Amend ADR-0067's configuration-time lower bound, configuration fields and
|
|
23
|
+
endpoint-change behavior. Its payload, eligibility, signing, independent cursor
|
|
24
|
+
and asynchronous worker decisions remain in force.
|
|
25
|
+
|
|
26
|
+
- First enable delivers eligible findings still retained in the queue. The CLI
|
|
27
|
+
explicitly states that configuration authorizes retained history as well as
|
|
28
|
+
new findings. There is no time cutoff; the private file contains only the URL
|
|
29
|
+
and optional signing secret. Legacy `configured_at` fields are ignored.
|
|
30
|
+
- One worker reads sequentially within each partition. A successful HTTP 2xx
|
|
31
|
+
advances that row's durable cursor. Non-notifiable revisions advance without
|
|
32
|
+
sending. Any delivery failure leaves the failed row pending for the next
|
|
33
|
+
attempt. There is no retry count, discard threshold or separate retry queue.
|
|
34
|
+
- A scan commit wakes the worker, and the existing default one-minute interval
|
|
35
|
+
retries while idle. Setting that interval to zero remains an explicit opt-out
|
|
36
|
+
from timed retries; commits and daemon restarts still wake delivery.
|
|
37
|
+
- Changing the endpoint transfers the remaining backlog to the new destination.
|
|
38
|
+
Changing the signing secret preserves progress. Previously acknowledged rows
|
|
39
|
+
are not replayed for a new destination.
|
|
40
|
+
- Configuration writes and each config-read/POST/cursor-advance span share one
|
|
41
|
+
cross-process file lock. The lock is released between rows. Configure/disable
|
|
42
|
+
waits for an in-flight request; after the command returns, subsequent requests
|
|
43
|
+
cannot use the old configuration. Direct file edits do not participate in this
|
|
44
|
+
coordination. HTTP requests use a ten-second timeout per HTTPX operation.
|
|
45
|
+
- While enabled, pending partitions have retention floors, including before the
|
|
46
|
+
first delivery attempt and every retention sweep. Disable stops delivery and
|
|
47
|
+
releases those floors at the next drain or sweep, even if timed retries are
|
|
48
|
+
disabled. Disabling explicitly ends the pending-delivery guarantee. Re-enable
|
|
49
|
+
resumes from the saved acknowledgement position for whatever remains retained.
|
|
50
|
+
- Invalid configuration stops delivery and preserves existing floors for repair.
|
|
51
|
+
Disable remains available to release them. No endpoint, secret, response body
|
|
52
|
+
or underlying HTTP error message is printed in delivery failures.
|
|
53
|
+
|
|
54
|
+
## Alternatives considered
|
|
55
|
+
|
|
56
|
+
- **Best effort with an in-memory queue.** Loses alerts during outages or daemon
|
|
57
|
+
restarts, weakening the purpose of a notification channel for unattended work.
|
|
58
|
+
- **Keep the timestamp or replace it with a configuration-time sequence.** Both
|
|
59
|
+
maintain a second delivery boundary. Replaying retained history is an explicit
|
|
60
|
+
product choice that removes that state entirely.
|
|
61
|
+
- **Discard after a retry limit or age limit while enabled.** Would silently
|
|
62
|
+
abandon undelivered alerts. Disable is the explicit way to release a backlog.
|
|
63
|
+
- **Read config immediately before POST without a lock.** A concurrent command
|
|
64
|
+
can return between the read and request. A comparison after sending cannot
|
|
65
|
+
undo delivery to the old destination.
|
|
66
|
+
- **Lock the entire backlog drain.** A large backlog would delay configuration
|
|
67
|
+
changes for many requests. Per-row locking preserves the same correctness
|
|
68
|
+
boundary without holding the lock across a whole drain.
|
|
69
|
+
|
|
70
|
+
## Consequences
|
|
71
|
+
|
|
72
|
+
First enable may send a burst of older findings. An unreachable or rejecting
|
|
73
|
+
endpoint can retain a growing backlog indefinitely. Delivery resumes only when
|
|
74
|
+
the endpoint accepts requests and the worker runs again; disable ends that
|
|
75
|
+
obligation. A crash after acceptance but before cursor persistence can resend a
|
|
76
|
+
finding, so receivers still deduplicate with the stable `event_id`. A transport
|
|
77
|
+
timeout can also leave the receiver's outcome unknown.
|
|
78
|
+
|
|
79
|
+
This introduces no database migration or second queue. The first notifiable
|
|
80
|
+
revision remains the only revision sent for an event.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 0069
|
|
3
|
+
title: Webhooks deliver what retention holds, and configuration takes no lock
|
|
4
|
+
status: accepted
|
|
5
|
+
date: 2026-09-25
|
|
6
|
+
supersedes: null
|
|
7
|
+
superseded-by: null
|
|
8
|
+
amends: 0068
|
|
9
|
+
amended-by: null
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Context
|
|
13
|
+
|
|
14
|
+
ADR-0068 gave a configured webhook retention floors, so a pending delivery
|
|
15
|
+
could outlive the retention window, and serialized configuration changes with
|
|
16
|
+
each POST through a cross-process file lock. Both made the daemon act on a
|
|
17
|
+
reading of `webhook.toml` in ways that could lose or corrupt state.
|
|
18
|
+
|
|
19
|
+
Clearing a floor was the daemon's inference from "no URL", so every way a read
|
|
20
|
+
could come back empty — a missing, empty or truncated file, a dangling symlink,
|
|
21
|
+
an unreadable directory, invalid UTF-8, a table without `url` — was a way to
|
|
22
|
+
release a backlog for retention to delete. The floor also had to be reconciled
|
|
23
|
+
inside the retention sweep, under the same lock as the POST, which put that lock
|
|
24
|
+
on the scan thread's prune path and made optional webhook storage a dependency
|
|
25
|
+
of base retention. Review of the implementation produced one finding after
|
|
26
|
+
another in these two areas, each a new route to the same two invariants.
|
|
27
|
+
|
|
28
|
+
## Decision
|
|
29
|
+
|
|
30
|
+
Amend three clauses of ADR-0068. Its payload, eligibility, no-cutoff replay of
|
|
31
|
+
retained history, sequential per-partition delivery, unbounded retry and
|
|
32
|
+
endpoint-change behavior remain in force.
|
|
33
|
+
|
|
34
|
+
- **No retention floors for the webhook.** Its cursor records delivery
|
|
35
|
+
progress only. Retention prunes by age, and by Fleet's cursors as before
|
|
36
|
+
ADR-0067; a finding older than the retention window when the endpoint next
|
|
37
|
+
accepts requests is not delivered. This replaces "while enabled, pending
|
|
38
|
+
partitions have retention floors", and "invalid configuration preserves
|
|
39
|
+
existing floors for repair" becomes "invalid configuration pauses delivery".
|
|
40
|
+
- **Configuration is read before each request, without a lock.** `configure`
|
|
41
|
+
and `disable` replace or remove the file atomically and return at once. The
|
|
42
|
+
change applies from the next request; the one already in flight completes
|
|
43
|
+
with the configuration it read. This replaces the per-row cross-process lock.
|
|
44
|
+
- **The daemon never removes a webhook cursor.** Nothing it reads can release
|
|
45
|
+
anything, so a configuration that cannot be read, or names no endpoint, only
|
|
46
|
+
pauses delivery. Disable deletes the file and keeps the cursor, so re-enable
|
|
47
|
+
resumes from the saved position for whatever is still retained, as ADR-0068
|
|
48
|
+
already states.
|
|
49
|
+
|
|
50
|
+
URLs are validated by parsing them with `httpx.URL`, the client that sends, so
|
|
51
|
+
`configure` cannot accept an endpoint delivery would refuse.
|
|
52
|
+
|
|
53
|
+
## Alternatives considered
|
|
54
|
+
|
|
55
|
+
- **Keep floors and make "disabled" explicit.** Have only `disable` clear the
|
|
56
|
+
cursor and never infer it from a read. Rejected: it keeps the reconcile in the
|
|
57
|
+
retention sweep and the lock on its path, the source of the other half of the
|
|
58
|
+
findings, to protect a backlog older than the window every other local
|
|
59
|
+
consumer already accepts losing.
|
|
60
|
+
- **Keep the lock so a change is a hard cutoff.** Rejected: what it prevents is
|
|
61
|
+
one request, already in flight, carrying the allowlisted metadata payload to
|
|
62
|
+
the previous endpoint. That does not justify a cross-process lock, its failure
|
|
63
|
+
modes, and a CLI that waits on HTTP.
|
|
64
|
+
- **Move the configuration into the daemon's SQLite store.** Would make
|
|
65
|
+
configure one transaction with cursor state. Rejected for now: with no floors
|
|
66
|
+
and no lock there is no cursor state for configure to keep consistent, and a
|
|
67
|
+
plain file keeps the CLI independent of the daemon's database.
|
|
68
|
+
- **Reintroduce a configuration-time boundary as the cursor.** Rejected, as in
|
|
69
|
+
ADR-0068: replaying retained history is the product choice.
|
|
70
|
+
|
|
71
|
+
## Consequences
|
|
72
|
+
|
|
73
|
+
An endpoint unavailable for longer than the retention window misses the
|
|
74
|
+
findings that aged out meanwhile; there is no longer an obligation that grows
|
|
75
|
+
without bound while an endpoint rejects requests. A receiver can get one request
|
|
76
|
+
after `disable` returns, or at the previous endpoint after `configure` returns.
|
|
77
|
+
|
|
78
|
+
Base retention no longer depends on the webhook's storage or configuration,
|
|
79
|
+
and the scan thread never takes a webhook lock.
|
|
80
|
+
|
|
81
|
+
## When to revisit
|
|
82
|
+
|
|
83
|
+
If a destination must never lose a finding to retention, give it its own
|
|
84
|
+
durable queue rather than floors on the shared one. If a receiver must treat
|
|
85
|
+
any request after `disable` as an incident, the hard cutoff needs a daemon-side
|
|
86
|
+
acknowledgement, not a file lock.
|