stacktrace-cli 0.5.0__tar.gz → 0.5.2__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.0 → stacktrace_cli-0.5.2}/.gitignore +3 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/PKG-INFO +30 -2
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/README.md +29 -1
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0006-trust-boundary-and-detection-upload.md +1 -1
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0031-one-host-daemon-observes-native-session-logs.md +1 -1
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0036-deliver-eligible-findings-to-slack-from-the-daemon.md +1 -1
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0037-reuse-openaidr-append-cursors-in-the-daemon.md +1 -1
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0038-poll-normalized-sessions-for-supplemental-changes.md +1 -1
- stacktrace_cli-0.5.2/docs/adrs/0045-scan-every-session-and-outlive-them.md +137 -0
- stacktrace_cli-0.5.2/docs/adrs/0046-the-finding-store-is-an-append-only-queue.md +139 -0
- stacktrace_cli-0.5.2/docs/adrs/0047-capture-activity-during-correlation-or-lose-it.md +108 -0
- stacktrace_cli-0.5.2/docs/adrs/0048-the-daemon-keeps-a-leveled-content-free-activity-log.md +59 -0
- stacktrace_cli-0.5.2/docs/adrs/0056-stop-a-resident-daemon-by-record-and-signal.md +124 -0
- stacktrace_cli-0.5.2/docs/adrs/0057-grow-the-queue-schema-additively-and-share-only-the-document.md +117 -0
- stacktrace_cli-0.5.2/docs/adrs/0058-one-word-for-a-finding-columns-for-a-counter-and-a-push-that-does-not-wait.md +128 -0
- stacktrace_cli-0.5.2/docs/adrs/0059-the-daemon-does-not-decide-whether-it-should-be-running.md +111 -0
- stacktrace_cli-0.5.2/docs/adrs/0060-two-upload-postures-not-one-command-with-two-sources.md +121 -0
- stacktrace_cli-0.5.2/docs/adrs/0061-the-drain-sends-findings-only.md +118 -0
- stacktrace_cli-0.5.2/docs/adrs/0062-hold-the-transition-lock-across-remote-side-effects.md +134 -0
- stacktrace_cli-0.5.2/docs/adrs/0063-underbuilt-debt-does-not-hold-the-scan-watermark.md +90 -0
- stacktrace_cli-0.5.2/docs/adrs/0064-a-redacted-value-has-one-path-span.md +86 -0
- stacktrace_cli-0.5.2/docs/adrs/0065-asset-external-id-resolves-by-a-ladder.md +119 -0
- stacktrace_cli-0.5.2/docs/adrs/0066-host-owned-daemon-supervision.md +197 -0
- stacktrace_cli-0.5.2/docs/adrs/0067-a-webhook-is-an-independent-queue-subscriber.md +100 -0
- stacktrace_cli-0.5.2/docs/adrs/0068-webhooks-retry-the-retained-backlog.md +80 -0
- stacktrace_cli-0.5.2/docs/adrs/0069-webhooks-deliver-what-retention-holds.md +86 -0
- stacktrace_cli-0.5.2/docs/adrs/0070-package-the-cloud-agent-environment-bootstrap.md +162 -0
- stacktrace_cli-0.5.2/docs/adrs/0071-render-the-allowlisted-payload-per-destination.md +89 -0
- stacktrace_cli-0.5.2/docs/adrs/0072-host-supervisors-own-daemon-restarts.md +95 -0
- stacktrace_cli-0.5.2/docs/adrs/0073-name-what-fired-and-which-component.md +114 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/INDEX.md +24 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/cutover-openaca-remote.md +8 -6
- stacktrace_cli-0.5.2/docs/plans/011-daemon-store.md +2689 -0
- stacktrace_cli-0.5.2/docs/plans/012-daemon-scanning.md +172 -0
- stacktrace_cli-0.5.2/docs/plans/013-daemon-uploads.md +130 -0
- stacktrace_cli-0.5.2/docs/plans/014-asset-identity.md +666 -0
- stacktrace_cli-0.5.2/docs/releases/v0.5.1.md +209 -0
- stacktrace_cli-0.5.2/docs/releases/v0.5.2.md +215 -0
- stacktrace_cli-0.5.2/docs/specs/daemon-scanning.md +243 -0
- stacktrace_cli-0.5.2/docs/specs/daemon-store.md +246 -0
- stacktrace_cli-0.5.2/docs/specs/daemon-uploads.md +367 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/notification-delivery.md +101 -59
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/remote-sync.md +6 -5
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/pyproject.toml +2 -1
- stacktrace_cli-0.5.2/src/stacktrace_cli/__init__.py +10 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/cli.py +2 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/cloud/__init__.py +39 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/cloud/setup.sh +247 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/activity_log.py +94 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/bom.py +56 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/cli.py +538 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/client.py +355 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/composition.py +242 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/config.py +191 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/db.py +625 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/fleet.py +660 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/identity.py +83 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/jobs.py +181 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/lifecycle.py +172 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/observer.py +626 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/daemon/paths.py +6 -1
- stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/protocol.py +146 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/runtime.py +694 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/scan.py +256 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/daemon/server.py +141 -2
- stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/service.py +131 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/store.py +911 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/webhook.py +508 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/render.py +31 -4
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/site/app.js +1 -1
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/site/index.html +3 -4
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/cli.py +28 -19
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/client.py +29 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/detect_payload.py +147 -25
- stacktrace_cli-0.5.2/src/stacktrace_cli/remote/identity.py +305 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/payload.py +7 -4
- stacktrace_cli-0.5.2/src/stacktrace_cli/remote/redact.py +1328 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/sync.py +330 -98
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/sync_detect.py +134 -64
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/upload_contract.py +10 -1
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/telemetry/events.py +12 -6
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/telemetry/posthog.py +34 -5
- stacktrace_cli-0.5.2/src/stacktrace_cli/webhook/__init__.py +1 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/webhook/cli.py +169 -0
- stacktrace_cli-0.5.2/src/stacktrace_cli/webhook/config.py +193 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/conftest.py +32 -2
- stacktrace_cli-0.5.2/tests/daemon/test_activity_log.py +328 -0
- stacktrace_cli-0.5.2/tests/daemon/test_cli.py +844 -0
- stacktrace_cli-0.5.2/tests/daemon/test_client.py +338 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/daemon/test_composition.py +87 -0
- stacktrace_cli-0.5.2/tests/daemon/test_config.py +92 -0
- stacktrace_cli-0.5.2/tests/daemon/test_db.py +827 -0
- stacktrace_cli-0.5.2/tests/daemon/test_detection_needs_no_subscriber.py +115 -0
- stacktrace_cli-0.5.2/tests/daemon/test_fleet.py +737 -0
- stacktrace_cli-0.5.2/tests/daemon/test_identity.py +91 -0
- stacktrace_cli-0.5.2/tests/daemon/test_jobs.py +329 -0
- stacktrace_cli-0.5.2/tests/daemon/test_lifecycle.py +115 -0
- stacktrace_cli-0.5.2/tests/daemon/test_observer.py +861 -0
- stacktrace_cli-0.5.2/tests/daemon/test_queue_reader.py +200 -0
- stacktrace_cli-0.5.2/tests/daemon/test_runtime.py +1455 -0
- stacktrace_cli-0.5.2/tests/daemon/test_scan.py +412 -0
- stacktrace_cli-0.5.2/tests/daemon/test_service.py +198 -0
- stacktrace_cli-0.5.2/tests/daemon/test_socket.py +547 -0
- stacktrace_cli-0.5.2/tests/daemon/test_store.py +690 -0
- stacktrace_cli-0.5.2/tests/daemon/test_the_drained_payload_is_the_cold_one.py +166 -0
- stacktrace_cli-0.5.2/tests/daemon/test_the_stored_row_is_sufficient.py +247 -0
- stacktrace_cli-0.5.2/tests/daemon/test_webhook.py +1072 -0
- stacktrace_cli-0.5.2/tests/daemon/test_wire_frames_are_bounded.py +156 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/test_reasoning_gates.py +3 -1
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/test_server.py +18 -6
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/helpers.py +4 -3
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_cli.py +33 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_client.py +23 -0
- stacktrace_cli-0.5.2/tests/remote/test_detect_redaction.py +692 -0
- stacktrace_cli-0.5.2/tests/remote/test_identity.py +347 -0
- stacktrace_cli-0.5.2/tests/remote/test_no_local_path_survives_redaction.py +376 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_payload.py +10 -7
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_redact_payload.py +37 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_seam.py +12 -5
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_sync.py +475 -5
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_sync_detect.py +208 -1
- stacktrace_cli-0.5.2/tests/telemetry/test_activity_log.py +135 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/telemetry/test_hooks.py +30 -30
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_no_live_document_states_the_old_stage_three_default.py +7 -1
- stacktrace_cli-0.5.2/tests/test_no_test_undoes_every_patch_at_once.py +51 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_session_identity_is_the_pair.py +20 -8
- stacktrace_cli-0.5.2/tests/test_title_disclosure_invariant.py +159 -0
- stacktrace_cli-0.5.2/tests/webhook/test_cli.py +662 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/uv.lock +1 -1
- stacktrace_cli-0.5.0/src/stacktrace_cli/__init__.py +0 -3
- stacktrace_cli-0.5.0/src/stacktrace_cli/daemon/cli.py +0 -104
- stacktrace_cli-0.5.0/src/stacktrace_cli/daemon/client.py +0 -134
- stacktrace_cli-0.5.0/src/stacktrace_cli/daemon/composition.py +0 -133
- stacktrace_cli-0.5.0/src/stacktrace_cli/daemon/observer.py +0 -173
- stacktrace_cli-0.5.0/src/stacktrace_cli/daemon/protocol.py +0 -29
- stacktrace_cli-0.5.0/src/stacktrace_cli/daemon/runtime.py +0 -222
- stacktrace_cli-0.5.0/src/stacktrace_cli/daemon/service.py +0 -154
- stacktrace_cli-0.5.0/src/stacktrace_cli/daemon/store.py +0 -380
- stacktrace_cli-0.5.0/src/stacktrace_cli/remote/redact.py +0 -660
- stacktrace_cli-0.5.0/tests/daemon/test_cli.py +0 -224
- stacktrace_cli-0.5.0/tests/daemon/test_client.py +0 -136
- stacktrace_cli-0.5.0/tests/daemon/test_observer.py +0 -407
- stacktrace_cli-0.5.0/tests/daemon/test_runtime.py +0 -411
- stacktrace_cli-0.5.0/tests/daemon/test_service.py +0 -227
- stacktrace_cli-0.5.0/tests/daemon/test_socket.py +0 -183
- stacktrace_cli-0.5.0/tests/daemon/test_store.py +0 -207
- stacktrace_cli-0.5.0/tests/remote/test_detect_redaction.py +0 -191
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/.agents/skills/release-stacktrace/SKILL.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/.claude/settings.json +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/.claude/skills/release-stacktrace/SKILL.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/.codex/hooks.json +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/.github/managed-autofix.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/.github/workflows/autofix.yml +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/.github/workflows/ci.yml +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/.github/workflows/claude.yml +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/.github/workflows/publish-pypi.yml +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/AGENTS.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/CLAUDE.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0001-session-telemetry-as-input.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0002-session-collection-in-openaidr.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0003-runtime-edges.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0004-three-stage-detector.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0005-detection-family-and-report-assembly.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0007-proprietary-package-on-open-dependencies.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0008-console-in-separate-demo-package.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0010-detection-severity-and-confidence-ladders.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0011-verdict-cache.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0012-observation-evidence-kinds-and-transport.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0013-rule-catalogue-triage-and-per-rule-context.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0014-declared-project-mapping.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0015-narrow-the-security-catalogue.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0016-stall-grouping-is-session-wide.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0017-a-declined-repeat-is-a-stall.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0018-remote-sync-config-and-facade-consumption.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0019-delegated-commands-are-openaca-objects.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0020-openaca-consumption-boundary.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0021-two-command-kinds.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0022-detection-scope-is-a-catalogue-column.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0023-sync-detect-collects-and-does-not-escalate.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0024-the-upload-carries-observations.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0025-a-denied-call-is-activity-never-an-invocation.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0026-the-console-becomes-a-product-surface.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0027-a-finding-may-name-what-it-could-not-place.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0028-the-console-shows-what-it-placed.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0029-stage-three-returns-to-opt-in.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0030-a-detection-is-dated-by-the-turns-it-cites.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0032-refresh-daemon-compositions-outside-detection.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0033-a-third-party-analyzer-only-by-a-flag-that-names-it.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0034-the-drift-grade-is-read-off-a-run-not-a-call.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0035-naming-an-analyzer-selects-stored-answers.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0039-send-usage-telemetry-by-default-with-an-opt-out.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0041-defer-native-windows-support.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0042-read-the-build-commit-from-the-install-record.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0043-name-the-session-agent-and-host-and-count-errors-by-class.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0044-send-the-build-as-version-prints-it.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/HOOK-PROMPT.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/TEMPLATE.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/plans/002-session-input.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/plans/003-correlation.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/plans/004-detector.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/plans/005-remote-sync.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/plans/006-cli-composition.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/plans/007-detection-upload.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/plans/008-monitor.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/plans/009-reasoning-jev.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/plans/010-session-daemon.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.0.1.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.1.0.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.2.0.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.2.1.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.2.2.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.2.3.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.3.0.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.3.1.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.4.0.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.4.1.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.5.0.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/research/001-intent-drift-metric.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/aidr.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/cli-composition.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/correlation.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/detection-upload.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/detector.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/event-time.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/monitor.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/reasoning-jev.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/session-input.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/scripts/git-hooks/pre-push +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/scripts/install-hooks.sh +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/__main__.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/analysis.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/build.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/correlate/__init__.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/correlate/acquire.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/correlate/composition.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/correlate/join.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/correlate/observed.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/correlate/orchestrate.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/correlate/project_map.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/correlate/record.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/correlate/render.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/daemon/__init__.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/daemon/presentation.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/__init__.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/analyzer.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/blocked.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/cache.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/deterministic.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/finding.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/history.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/jev.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/priors.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/prompts/__init__.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/prompts/jev-v1/stacktrace-deceptive-completion.json +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/prompts/jev-v2/stacktrace-deceptive-completion.json +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/prompts/v1/exclusions.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/prompts/v1/framing.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/prompts/v1/stacktrace-deceptive-completion.md +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/reasoning.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/rules.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/run.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/secrets.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/verdict.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/windows.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/__init__.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/reasoning.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/render.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/server.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/site/fonts/OFL.txt +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/site/fonts/dm-mono-400-latin.woff2 +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/site/fonts/dm-mono-500-latin.woff2 +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/site/fonts/dm-sans-latin.woff2 +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/site/styles.css +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/state.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/verdicts.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/watch.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/options.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/private_state.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/__init__.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/config.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/policy.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/spool.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/sessions/__init__.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/sessions/access.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/sessions/index.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/sessions/outcome.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/sessions/protocols.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/sessions/render.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/telemetry/__init__.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/telemetry/cli.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/telemetry/state.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/__init__.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/agent_bom_fixture.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/daemon/__init__.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/detector_session_fixture.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/__init__.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/feed_harness.mjs +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/test_feed_model.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/test_render.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/test_site_assets.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/test_state.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/test_verdicts.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/test_watch.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/__init__.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_client.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_config.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_activity.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_cli.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_contract_is_exhaustive.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_gate_matches_the_cloud.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_layers_compose.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_payload_reaches_the_cloud_model.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_spool.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_upload_contract.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_wire_payload.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_facade_contract.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_policy.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_spool.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_upload_contract.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/telemetry/__init__.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/telemetry/test_cli.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/telemetry/test_events.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/telemetry/test_release_gate.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/telemetry/test_state.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/telemetry/test_uploader.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_acquire.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_adr_cross_references_are_reciprocal.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_agent_instance_id.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_analysis.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_bom_shape.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_build_commit.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_call_status_conventions.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_cli.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_composition.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_correlate_orchestrate.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_correlate_render.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_correlated_session.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detection_components.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detection_event_time.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detection_subject.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_analyzer_contract.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_analyzer_live.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_blocked.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_cache.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_deterministic.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_end_to_end.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_finding.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_history.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_jev.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_priors.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_prompts.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_reasoning.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_render.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_run.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_secrets.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_verdict.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_why_it_stopped.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_windows.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_evidence_is_dated_not_numbered.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_fingerprint_covers_what_the_analyzer_reads.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_help_sections.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_join.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_monitor_cli.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_observed.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_openaca_contract.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_openaidr_contract.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_option_arity_matches_the_binary.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_project_map.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_readme_examples_are_real_output.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_release_gate_coverage.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_release_readiness.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_seam_boundary.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_sessions_access.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_sessions_end_to_end.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_sessions_protocol_typing.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_sessions_protocols.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_sessions_render.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_shell_separators_match_the_shell.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_stored_verdicts_are_never_gated_on_reasoning.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_the_detector_spec_lists_the_shipped_catalogue.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_the_jev_spec_states_the_shipped_thresholds.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_the_locality_claim_is_qualified_everywhere.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_untrusted_content_never_travels.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_verification_subcommands_match_the_binary.py +0 -0
- {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/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.2
|
|
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.2 (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.2 (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,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 0045
|
|
3
|
+
title: Scan every session on a watermark, and outlive all of them
|
|
4
|
+
status: accepted
|
|
5
|
+
date: 2026-09-22
|
|
6
|
+
supersedes: null
|
|
7
|
+
superseded-by: null
|
|
8
|
+
amends: 0031, 0037, 0038
|
|
9
|
+
amended-by: 0056, 0058, 0066, 0072
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Context
|
|
13
|
+
|
|
14
|
+
ADR-0031 gave the daemon a subscriber-shaped life. `DaemonService.tick()` takes
|
|
15
|
+
its work list from `server.active_sessions()`, which returns
|
|
16
|
+
`tuple(self._subscriptions)` — literally the set of connected adapters — and
|
|
17
|
+
`should_exit()` ends the process once that set has been non-empty and is now
|
|
18
|
+
empty. Both follow from how the daemon starts: `ensure_daemon()` is called from
|
|
19
|
+
the plugin monitor's reconnect loop, so a session forks it and the process must
|
|
20
|
+
not outlive the session that did.
|
|
21
|
+
|
|
22
|
+
Two things make that shape wrong now. The daemon is becoming the machine's
|
|
23
|
+
detection engine rather than one session's helper, so a finding for a session
|
|
24
|
+
nobody subscribed to is still a finding worth having. And it is acquiring
|
|
25
|
+
scheduled work — uploads on a cadence — which has no session to keep it alive
|
|
26
|
+
and stops entirely on a quiet machine.
|
|
27
|
+
|
|
28
|
+
The subscription set is also doing two unrelated jobs at once: deciding *what
|
|
29
|
+
gets analysed* and deciding *where a notification goes*. Only the second is
|
|
30
|
+
what ADR-0031 argued for.
|
|
31
|
+
|
|
32
|
+
## Decision
|
|
33
|
+
|
|
34
|
+
The daemon has no concept of an active session. Work comes from the filesystem
|
|
35
|
+
on a cadence, and the process's lifetime is independent of any session.
|
|
36
|
+
|
|
37
|
+
1. `active_sessions()` is removed from the work path. Every `--scan-interval`
|
|
38
|
+
the daemon enumerates transcripts changed since a **persisted watermark**,
|
|
39
|
+
together with recently-live sessions due ADR-0038's supplemental poll, and
|
|
40
|
+
analyses those.
|
|
41
|
+
2. The first scan, with no watermark on disk, is bounded by `--scan-since`
|
|
42
|
+
(default 3 days). Every scan after that is bounded by the watermark, which
|
|
43
|
+
advances to *the scan's start time* — not the newest mtime seen, or a file
|
|
44
|
+
written during the scan is jumped over. A few seconds of overlap absorbs
|
|
45
|
+
mtime granularity and clock skew.
|
|
46
|
+
3. `should_exit()`, `_connected_once` and `startup_grace` are **deleted**, not
|
|
47
|
+
flagged. So are `_last_seen`, `_absent_since` and `disconnect_grace`: they
|
|
48
|
+
exist to track subscribers, and the scan window already keeps a
|
|
49
|
+
just-ended session in view for days rather than five seconds.
|
|
50
|
+
4. The daemon instead exits when told to (`daemon stop`, a recorded
|
|
51
|
+
`daemon disable`), when the machine goes away, or when its running
|
|
52
|
+
`version_label()` diverges from the installed distribution.
|
|
53
|
+
5. One `analyse_sessions` call per tick for the whole batch, not one per
|
|
54
|
+
session. `collect()` stays per transcript so a malformed file loses only its
|
|
55
|
+
own session.
|
|
56
|
+
6. `NotificationServer._subscriptions` is untouched and remains the delivery
|
|
57
|
+
routing table. **Subscriptions decide where a finding goes, never whether it
|
|
58
|
+
is found.**
|
|
59
|
+
|
|
60
|
+
This amends ADR-0031's implicit use of subscription as a work source and its
|
|
61
|
+
process-lifetime rule; its routing, persistence and delivery decisions stay in
|
|
62
|
+
force. ADR-0037's cursor ownership is untouched — the append cursors remain
|
|
63
|
+
OpenAIDR's, in-memory, for the collector's lifetime. ADR-0038's supplemental
|
|
64
|
+
poll survives as the second input to the work list.
|
|
65
|
+
|
|
66
|
+
## Alternatives considered
|
|
67
|
+
|
|
68
|
+
- **Keep subscription as the work source and union it with a scan.** Rejected:
|
|
69
|
+
two sources for one list, and the union can only ever equal the scan, since
|
|
70
|
+
any subscribed session's transcript is by definition recent. It preserves the
|
|
71
|
+
coupling while adding a second mechanism.
|
|
72
|
+
- **A `--resident` flag that pins `should_exit()` to `False`.** Rejected, and
|
|
73
|
+
this was the first design. A flag keeps the coupling and hides it behind a
|
|
74
|
+
default. Removal is clean instead: `daemon run` is `hidden=True`, an internal
|
|
75
|
+
command `ensure_daemon()` invokes rather than something a user runs expecting
|
|
76
|
+
it to return, and `should_exit` has no production caller outside `run()`. The
|
|
77
|
+
only fallout is assertions in `tests/daemon/test_service.py` that test the
|
|
78
|
+
behaviour being removed.
|
|
79
|
+
- **Keep `disconnect_grace`.** Rejected because it is subsumed, not merely
|
|
80
|
+
unused. Its window was carefully anchored to the first absent poll rather than
|
|
81
|
+
the last-seen time, because a slow `tick()` could otherwise consume the whole
|
|
82
|
+
budget. With a scan window measured in days, a session that ended a minute ago
|
|
83
|
+
is still enumerated; the intricacy and the bug class it guarded both stop
|
|
84
|
+
existing.
|
|
85
|
+
- **`--scan-since` as a standing window rather than a bootstrap horizon.**
|
|
86
|
+
Rejected: it re-reads the same days forever, and it forces a "has this daemon
|
|
87
|
+
run before" special case to avoid doing so. A watermark removes the special
|
|
88
|
+
case and closes downtime gaps for free — a daemon down for a week resumes and
|
|
89
|
+
enumerates a week.
|
|
90
|
+
- **Enumerate by mtime only.** Rejected: it silently regresses ADR-0038. A
|
|
91
|
+
session whose transcript is stable but whose MCP logs moved would never be
|
|
92
|
+
looked at again.
|
|
93
|
+
- **Per-session `analyse_sessions` calls, as today.** Rejected: correlation and
|
|
94
|
+
advisory lookups are per-run work, so N calls repeat them N times.
|
|
95
|
+
`analyse_sessions` already takes tuples and already shares
|
|
96
|
+
`_analyse_sessions` with the cold path.
|
|
97
|
+
- **Keep the per-transcript `mtime - 1 minute` cutoff as `since`.** Rejected as
|
|
98
|
+
unnecessary: `_analyse_sessions` does not filter on `since`; it stamps it as
|
|
99
|
+
`window_start` metadata and analyses the view whole. The watermark is a
|
|
100
|
+
simpler value with the same effect, and batching therefore cannot starve a
|
|
101
|
+
session-wide rule (ADR-0016) of turns.
|
|
102
|
+
|
|
103
|
+
## Consequences
|
|
104
|
+
|
|
105
|
+
Enables detection on a machine where no adapter ever connects, and gives
|
|
106
|
+
scheduled work a process that is still alive to do it. Removes more code than it
|
|
107
|
+
adds.
|
|
108
|
+
|
|
109
|
+
Costs, honestly:
|
|
110
|
+
|
|
111
|
+
- **Nothing restarts the daemon.** With supervision out of scope, a crash means
|
|
112
|
+
the endpoint is down until the next `ensure_daemon()` fork. The
|
|
113
|
+
version-divergence exit is what stops a long-lived process holding stale code,
|
|
114
|
+
replacing the garbage-collection that exit-on-idle was accidentally providing.
|
|
115
|
+
- **Collector memory now tracks transcripts seen since start**, not sessions
|
|
116
|
+
open now. ADR-0037's collector holds one cursor per file for the process
|
|
117
|
+
lifetime, and the process is now long-lived. If it bites, evict by last-touch
|
|
118
|
+
inside the seam — not by persisting cursors, which ADR-0037 places upstream.
|
|
119
|
+
- **A wedge lasts all day.** No idle exit and no supervisor means a hang
|
|
120
|
+
persists until logout. A maximum lifetime is worth having as a safety valve.
|
|
121
|
+
- **Telemetry's `session_started` changes character.** The bootstrap fires it
|
|
122
|
+
once per session in the horizon. It means "the daemon first saw this session",
|
|
123
|
+
not "a user started one" (ADR-0039).
|
|
124
|
+
- **Scan cost is a new standing cost.** Bounded in steady state by the
|
|
125
|
+
watermark; the bootstrap is the expensive pass.
|
|
126
|
+
|
|
127
|
+
## When to revisit
|
|
128
|
+
|
|
129
|
+
- When external supervision lands: the version-divergence exit becomes one of
|
|
130
|
+
several restart triggers rather than the only one, and exit codes need
|
|
131
|
+
defining before `KeepAlive` is wired — `run_daemon()` currently returns exit
|
|
132
|
+
`0` on lock contention and on a live socket, which a supervisor would read as
|
|
133
|
+
"do not restart".
|
|
134
|
+
- If enumeration cost on a large `~/.claude` proves material, which would make
|
|
135
|
+
a filesystem-event source worth its complexity.
|
|
136
|
+
- If a second agent kind lands, since the observer map and the scan root are
|
|
137
|
+
both still Claude-shaped.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 0046
|
|
3
|
+
title: Make the finding store an append-only queue of detection revisions
|
|
4
|
+
status: accepted
|
|
5
|
+
date: 2026-09-22
|
|
6
|
+
supersedes: null
|
|
7
|
+
superseded-by: null
|
|
8
|
+
amends: 0031
|
|
9
|
+
amended-by: 0057, 0058, 0067
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Context
|
|
13
|
+
|
|
14
|
+
ADR-0031 gave the daemon durable, content-free findings and delivery state, and
|
|
15
|
+
`FindingStore` implements that as one JSON document rewritten whole on every
|
|
16
|
+
`record()`. That is adequate for the sessions a human happened to subscribe to.
|
|
17
|
+
ADR-0045 makes the daemon analyse every session on the machine, and a later
|
|
18
|
+
deliverable will drain findings to Fleet on a cadence — which needs something
|
|
19
|
+
the document cannot express: *what is new since I last looked?*
|
|
20
|
+
|
|
21
|
+
Two properties of the existing store block that.
|
|
22
|
+
|
|
23
|
+
`_durable_finding()` is lossy. `observer.py` produces
|
|
24
|
+
`detection_json(detection)` — the whole detection — and `record()` keeps
|
|
25
|
+
`rule_id`, `severity`, `confidence`, `session_id` and evidence spans, discarding
|
|
26
|
+
the rest. Everything an upload mapper needs (`scope`, `subject`, `involved`,
|
|
27
|
+
component identities, occurrence range) is gone before it reaches disk.
|
|
28
|
+
|
|
29
|
+
And a finding's identity is stable while its content is not. `_event_id()`
|
|
30
|
+
hashes `(agent_kind, session_id, rule_id, anchor)`; for a session-scoped rule
|
|
31
|
+
the anchor *is* the session id, so one identity covers the whole session however
|
|
32
|
+
many times the rule fires. A session-wide rule such as stall grouping (ADR-0016)
|
|
33
|
+
legitimately accumulates cited turns and widens its occurrence range across
|
|
34
|
+
scans. Today that is `stored[event_id] = …`, last-write-wins in place, which is
|
|
35
|
+
invisible because nothing reads by position.
|
|
36
|
+
|
|
37
|
+
## Decision
|
|
38
|
+
|
|
39
|
+
The finding store becomes a SQLite **queue**: an append-only log of detection
|
|
40
|
+
revisions on a monotonic axis, with a cursor per reader. The interface
|
|
41
|
+
`FindingStore` presents to its callers does not change.
|
|
42
|
+
|
|
43
|
+
1. `findings(seq INTEGER PRIMARY KEY AUTOINCREMENT, event_id, content_hash,
|
|
44
|
+
agent_kind, session_id, detection_json, delivered_at, acked_at, created_at)`
|
|
45
|
+
with `UNIQUE (event_id, content_hash)` and an index on `(session_id, seq)`.
|
|
46
|
+
`seq` is the cursor axis. `event_id` is **not** unique.
|
|
47
|
+
2. A row is **one detection revision**. When a scan produces content differing
|
|
48
|
+
from the last stored revision of that `event_id`, a new row is appended with
|
|
49
|
+
a new `seq`. `content_hash` — over canonically serialised `detection_json` —
|
|
50
|
+
is what distinguishes "this finding again, unchanged" from "this finding, now
|
|
51
|
+
different", so an unchanged re-derivation writes nothing.
|
|
52
|
+
3. The row stores the **detection**, not any consumer's rendering of it. Each
|
|
53
|
+
reader renders at send time.
|
|
54
|
+
4. Exactly one writer, so insert order is commit order and a reader taking
|
|
55
|
+
`max(seq)` cannot skip a row committed later with a lower value. WAL, so
|
|
56
|
+
`stacktrace findings` and `daemon status` read without blocking it.
|
|
57
|
+
5. `cursors(subscriber, partition, last_seq)` exists with no rows. A reader that
|
|
58
|
+
is not configured holds **no cursor row at all** — retention prunes below the
|
|
59
|
+
cursor floor, and a row pinned at zero would make a machine accumulate
|
|
60
|
+
findings forever.
|
|
61
|
+
6. `daemon-v1.json` is **deleted, not imported**. Nothing is carried across:
|
|
62
|
+
the product is new, the file holds a few days of content-free findings, and
|
|
63
|
+
no code outside `daemon/` reads it. A newer `schema_meta` version fails
|
|
64
|
+
closed rather than reading a newer schema partially.
|
|
65
|
+
|
|
66
|
+
This amends ADR-0031's persistence clause. Its routing, delivery and lifecycle
|
|
67
|
+
decisions stay in force, and the socket path is untouched: `record()` still
|
|
68
|
+
publishes inline and `pending()` still drains on subscribe.
|
|
69
|
+
|
|
70
|
+
## Alternatives considered
|
|
71
|
+
|
|
72
|
+
- **`event_id UNIQUE` with `INSERT OR IGNORE`.** Rejected, and it is the
|
|
73
|
+
obvious-looking choice: `event_id` *is* the stable identity, and it is exactly
|
|
74
|
+
what makes redelivery idempotent. It only breaks once a cursor exists — the
|
|
75
|
+
first thin revision is kept and every later, richer one is lost. The failure
|
|
76
|
+
is silent and presents as "Fleet has the thin version of some findings".
|
|
77
|
+
- **Update the row in place.** Rejected for the mirror-image reason: the content
|
|
78
|
+
changes but `seq` does not move, so a reader that has already passed the row
|
|
79
|
+
never sees the update. Both write strategies lose data; only appending does
|
|
80
|
+
not.
|
|
81
|
+
- **Store a pre-rendered `upload_json`.** Rejected. `detection_json()` and
|
|
82
|
+
`_detection_for_upload()` are two renderings of the same `Detection`, each
|
|
83
|
+
keeping what its own audience needs — the local one adds `stage`, `label` and
|
|
84
|
+
`title`; the Cloud one adds `scope` and participants and deliberately omits
|
|
85
|
+
what the Cloud computes. Neither derives from the other, so a rendered column
|
|
86
|
+
freezes one consumer's wire format into the queue and starves the next one.
|
|
87
|
+
The defect was that `_durable_finding()` is lossy, not that rendering happens
|
|
88
|
+
late. Cost accepted: `_detection_for_upload()` takes a `Detection`, not a
|
|
89
|
+
dict, so either the stored record rehydrates or both renderers learn to read
|
|
90
|
+
the canonical dict.
|
|
91
|
+
- **A `sessions` table.** Rejected. A session is an `(agent_kind, session_id)`
|
|
92
|
+
pair carried on each row — enough to partition a cursor and route a frame.
|
|
93
|
+
More would be a model of the machine, which is what the queue is not. The
|
|
94
|
+
scan watermark (ADR-0045) is separate and no reader touches it.
|
|
95
|
+
- **A per-`(event_id, sink)` delivery table.** Rejected as redundant with a
|
|
96
|
+
watermark: within a partition the single writer produces in `seq` order and
|
|
97
|
+
delivery is sequential, so "below is done, above is pending" is complete.
|
|
98
|
+
Retry needs no column — a failed delivery simply does not advance the cursor.
|
|
99
|
+
- **Import the JSON document's contents.** Rejected as unearned: an importer,
|
|
100
|
+
a dedupe path and a crash-window argument, to preserve state nothing reads
|
|
101
|
+
except `stacktrace findings` on a session that is still open. The cost is a
|
|
102
|
+
notification lost for a session live at the moment of upgrade.
|
|
103
|
+
- **Keep the JSON document and add a sidecar index.** Rejected: the whole-file
|
|
104
|
+
rewrite is the cost that does not survive scanning every session, and two
|
|
105
|
+
stores disagree the first time one write succeeds and the other does not.
|
|
106
|
+
|
|
107
|
+
## Consequences
|
|
108
|
+
|
|
109
|
+
Enables a reader to ask "what is new", which is the whole point, and stops the
|
|
110
|
+
store discarding fields nobody can recover later.
|
|
111
|
+
|
|
112
|
+
Costs and cautions:
|
|
113
|
+
|
|
114
|
+
- **`seq` has gaps.** Verified against SQLite: an `INSERT OR IGNORE` that hits
|
|
115
|
+
the constraint still consumes the `AUTOINCREMENT` value. Harmless to a
|
|
116
|
+
`seq > last_seq` cursor, but nothing may assume contiguity. Reading the last
|
|
117
|
+
revision before inserting avoids churning the sequence on quiet ticks.
|
|
118
|
+
- **Canonical serialisation is load-bearing.** Key ordering alone would produce
|
|
119
|
+
phantom revisions at scan cadence.
|
|
120
|
+
- **One finding can occupy several rows**, so a batch holding two revisions of
|
|
121
|
+
one `event_id` should send only the later, and pruning must keep the newest
|
|
122
|
+
revision rather than the oldest.
|
|
123
|
+
- **The database grows with no reader to prune behind it**, until the
|
|
124
|
+
deliverable that adds one.
|
|
125
|
+
- Storing more per row widens what sits on disk for a retention window. ADR-0006
|
|
126
|
+
is unchanged — the added fields are catalogue text, identities, counts and
|
|
127
|
+
timestamps — but the invariant now deserves an exhaustive test in the manner
|
|
128
|
+
of `test_untrusted_content_never_travels.py`.
|
|
129
|
+
|
|
130
|
+
## When to revisit
|
|
131
|
+
|
|
132
|
+
- If a second writer becomes necessary — for instance if `sync detect` becomes
|
|
133
|
+
"drain the cursor now" and is implemented by opening the database rather than
|
|
134
|
+
asking the daemon over its socket. That would break the ordering invariant the
|
|
135
|
+
cursor rests on.
|
|
136
|
+
- If revision churn proves costly on long sessions, which would argue for
|
|
137
|
+
collapsing superseded revisions at write time rather than at prune time.
|
|
138
|
+
- If a reader ever needs a finding's full history rather than its latest state,
|
|
139
|
+
which the append-only shape happens to preserve but nothing currently uses.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 0047
|
|
3
|
+
title: Capture activity counters during correlation, or lose them
|
|
4
|
+
status: superseded
|
|
5
|
+
date: 2026-09-22
|
|
6
|
+
supersedes: null
|
|
7
|
+
superseded-by: 0061
|
|
8
|
+
amends: null
|
|
9
|
+
amended-by: 0058
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Context
|
|
13
|
+
|
|
14
|
+
A detect run uploads three things per agent kind: `detections`, `unknowns` and
|
|
15
|
+
`activity` (`docs/specs/detection-upload.md`). Activity is per-session
|
|
16
|
+
invocation counters — how many times each bucket of tooling actually ran —
|
|
17
|
+
and the Cloud sums composition coverage from it.
|
|
18
|
+
|
|
19
|
+
ADR-0046 makes the daemon's store a queue of detection revisions, and a later
|
|
20
|
+
deliverable drains that queue to Fleet. Detections fit the queue naturally: they
|
|
21
|
+
are events, appended, read by a monotonic cursor. Activity does not. It is a
|
|
22
|
+
**rollup** — counters that change — and a `seq` cursor cannot express "send me
|
|
23
|
+
the counters that moved".
|
|
24
|
+
|
|
25
|
+
The tempting simplification is to leave activity out: keep one mechanism, carry
|
|
26
|
+
findings only, and revisit if the console misses it. That was the working
|
|
27
|
+
recommendation until `_collect_detect_run` was read closely:
|
|
28
|
+
|
|
29
|
+
> Every activity row is built here, while the correlated record is in hand: a
|
|
30
|
+
> `Detection` carries no counters, and the record is the only place the
|
|
31
|
+
> `(call, resolution)` pairs exist.
|
|
32
|
+
|
|
33
|
+
## Decision
|
|
34
|
+
|
|
35
|
+
Activity counters are extracted during analysis, in the same transaction as
|
|
36
|
+
that session's findings, and persisted as an upserted table keyed
|
|
37
|
+
`(agent_kind, session_id, bucket)` carrying `updated_at`. A reader sends rows
|
|
38
|
+
touched since its last drain — a selection rule alongside, not on, the `seq`
|
|
39
|
+
axis.
|
|
40
|
+
|
|
41
|
+
`agent_kind` is part of the key because `build_detect_payloads` groups activity
|
|
42
|
+
by kind while an individual row carries none: the grouping exists only as a dict
|
|
43
|
+
key at build time, so a reader rebuilding the payload from storage has no other
|
|
44
|
+
way to recover it.
|
|
45
|
+
|
|
46
|
+
The daemon's observer must stop discarding the analysis result after taking
|
|
47
|
+
detections from it.
|
|
48
|
+
|
|
49
|
+
**It lands with the store, not with the reader.** "Cannot be backfilled" is a
|
|
50
|
+
statement about a *scan*, not a release — capture started at any time yields
|
|
51
|
+
counters from then on — so deferring it to the consuming deliverable would be
|
|
52
|
+
defensible. It is not done, for two reasons: the store is meant to hold
|
|
53
|
+
everything the upload needs, so splitting the schema across deliverables buys a
|
|
54
|
+
migration later; and the first upload would otherwise ship against an empty
|
|
55
|
+
counter table, reporting zero coverage until enough scans had run. `_scan_claude_session` currently keeps only
|
|
56
|
+
`tuple(detection_json(d) for d in result.run.detections)`; the correlated record
|
|
57
|
+
that `activity_for_upload()` needs is in hand at that moment and thrown away.
|
|
58
|
+
|
|
59
|
+
## Alternatives considered
|
|
60
|
+
|
|
61
|
+
- **Leave activity out of the queue and revisit later.** Rejected on a fact,
|
|
62
|
+
having first been the recommendation. The counters exist only while the
|
|
63
|
+
correlated record is live, so omitting them is a **per-scan discard, not a
|
|
64
|
+
deferral**. Recovering them later means re-analysing transcripts that
|
|
65
|
+
retention may have outlived. "We can add it when someone asks" is false here.
|
|
66
|
+
- **Rebuild activity from stored detections at send time.** Rejected because it
|
|
67
|
+
is not possible: a `Detection` carries no counters, and the `(call,
|
|
68
|
+
resolution)` pairs live only in the correlated record.
|
|
69
|
+
- **Send detections with an empty `activity[]`.** Rejected as a silent product
|
|
70
|
+
regression. The Cloud sums coverage from `activity[]`, so every daemon-driven
|
|
71
|
+
run would report zero coverage — indistinguishable, to a reader of the
|
|
72
|
+
console, from an endpoint that genuinely ran nothing.
|
|
73
|
+
- **Force activity onto the `seq` axis as append-only rows.** Rejected: a
|
|
74
|
+
counter that changes would append a row per bucket per scan, and a reader
|
|
75
|
+
would have to reduce the log to get the current value. The rollup is the
|
|
76
|
+
natural shape; pretending otherwise to preserve one mechanism costs more than
|
|
77
|
+
the second selection rule does.
|
|
78
|
+
- **Re-collect sessions at upload time to recompute activity.** Rejected: it
|
|
79
|
+
reintroduces exactly the collection cost the cursor exists to remove, and
|
|
80
|
+
makes the upload path depend on transcripts still being present.
|
|
81
|
+
|
|
82
|
+
## Consequences
|
|
83
|
+
|
|
84
|
+
Coverage keeps working on a daemon-driven endpoint, and the counters survive
|
|
85
|
+
transcript pruning because they are captured when they exist.
|
|
86
|
+
|
|
87
|
+
Costs:
|
|
88
|
+
|
|
89
|
+
- **A second selection rule.** A reader drains findings by `seq` and activity by
|
|
90
|
+
`updated_at`. The queue is no longer one mechanism, and that is accepted
|
|
91
|
+
deliberately rather than discovered.
|
|
92
|
+
- **The store deliverable is not pure substitution.** It changes the observer,
|
|
93
|
+
not only the persistence layer, because the capture point is upstream of the
|
|
94
|
+
store.
|
|
95
|
+
- Activity rows are not versioned. A drain sends the current value of a counter,
|
|
96
|
+
so a reader that misses a drain sees a jump rather than a series. That matches
|
|
97
|
+
what a rollup is and what the Cloud does with it, but it means activity has no
|
|
98
|
+
equivalent of ADR-0046's revision history.
|
|
99
|
+
|
|
100
|
+
## When to revisit
|
|
101
|
+
|
|
102
|
+
- If the Cloud ever distinguishes "no activity reported" from "no activity", the
|
|
103
|
+
simplification rejected here becomes available again.
|
|
104
|
+
- If activity acquires a per-drain delta shape rather than a running total, at
|
|
105
|
+
which point it becomes an event and belongs on the `seq` axis after all.
|
|
106
|
+
- If correlation is ever restructured so counters survive on the `Detection`
|
|
107
|
+
itself, which would remove the capture-point constraint this ADR exists to
|
|
108
|
+
record.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: 0048
|
|
3
|
+
title: The daemon keeps a leveled, content-free, size-capped activity log
|
|
4
|
+
status: accepted
|
|
5
|
+
date: 2026-09-23
|
|
6
|
+
supersedes: null
|
|
7
|
+
superseded-by: null
|
|
8
|
+
amends: null
|
|
9
|
+
amended-by: null
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Context
|
|
13
|
+
|
|
14
|
+
`daemon.log` held only failures. On 2026-09-22 a daemon that had been healthy
|
|
15
|
+
for hours, tracking 14 sessions, had a 90-byte log. There was no way to tell it
|
|
16
|
+
from a stuck one. Telemetry was worse: `emit` drops events silently (opted out,
|
|
17
|
+
lock busy, inadmissible, no key, refused by the SDK), and the SDK's `on_error`
|
|
18
|
+
was a no-op.
|
|
19
|
+
|
|
20
|
+
## Decision
|
|
21
|
+
|
|
22
|
+
The daemon logs through stdlib `logging` to `daemon.log`, at DEBUG unless
|
|
23
|
+
`STACKTRACE_DAEMON_LOG_LEVEL` says otherwise.
|
|
24
|
+
|
|
25
|
+
1. **INFO:** start, stop, subscriber connect and disconnect, notification
|
|
26
|
+
delivered and acknowledged, composition refreshed.
|
|
27
|
+
**DEBUG:** each scan, with its reason and counts.
|
|
28
|
+
**WARNING:** analysis and refresh failures, and failed uploads.
|
|
29
|
+
2. **Every telemetry event is `queued` or `dropped` with a reason.**
|
|
30
|
+
`queued` means the SDK returned a uuid. The reasons are a closed set:
|
|
31
|
+
`inadmissible`, `underivable`, `opted_out`, `lock_busy`,
|
|
32
|
+
`state_unwritable`, `no_project_key`, `sdk_refused` and `sdk_error`.
|
|
33
|
+
3. **No content.** A line holds kinds, counts, class names and event ids. It
|
|
34
|
+
never holds a finding's title or body, an exception message, or transcript
|
|
35
|
+
text. Peer-supplied session ids are written with `%r`, so a newline in one
|
|
36
|
+
cannot forge a line.
|
|
37
|
+
4. **Capped and private.** A 0600 `RotatingFileHandler`, 5 MiB plus one
|
|
38
|
+
backup. Only the daemon holding the lock opens it. After a rollover the
|
|
39
|
+
daemon moves its stdout and stderr onto the new file, so a crash traceback
|
|
40
|
+
is not lost.
|
|
41
|
+
5. **Silent elsewhere.** The `stacktrace_cli` logger has a `NullHandler`, so
|
|
42
|
+
CLI processes print nothing new.
|
|
43
|
+
|
|
44
|
+
## Alternatives considered
|
|
45
|
+
|
|
46
|
+
- **Log "sent" on delivery:** not possible. posthog 7.59 reports failed
|
|
47
|
+
uploads through `on_error` and has no success hook, so `queued` is the
|
|
48
|
+
strongest true claim.
|
|
49
|
+
- **Default to INFO:** rejected. The log exists to debug a daemon that looks
|
|
50
|
+
quiet, and the cap bounds what DEBUG costs.
|
|
51
|
+
- **Log finding titles:** rejected. The untrusted-content test polices
|
|
52
|
+
findings, not log files, and the `event_id` is enough to look a finding up.
|
|
53
|
+
- **Log per-session tracking in `DaemonService`:** deferred. ADR-0045 (PR #63)
|
|
54
|
+
removes subscriptions from the work path.
|
|
55
|
+
|
|
56
|
+
## When to revisit
|
|
57
|
+
|
|
58
|
+
If anything starts parsing `daemon.log`, move it to versioned JSONL. If the
|
|
59
|
+
SDK gains a delivery hook, log `sent`.
|