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.
Files changed (385) hide show
  1. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/.gitignore +3 -0
  2. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/PKG-INFO +30 -2
  3. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/README.md +29 -1
  4. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0006-trust-boundary-and-detection-upload.md +1 -1
  5. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0031-one-host-daemon-observes-native-session-logs.md +1 -1
  6. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0036-deliver-eligible-findings-to-slack-from-the-daemon.md +1 -1
  7. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0037-reuse-openaidr-append-cursors-in-the-daemon.md +1 -1
  8. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0038-poll-normalized-sessions-for-supplemental-changes.md +1 -1
  9. stacktrace_cli-0.5.2/docs/adrs/0045-scan-every-session-and-outlive-them.md +137 -0
  10. stacktrace_cli-0.5.2/docs/adrs/0046-the-finding-store-is-an-append-only-queue.md +139 -0
  11. stacktrace_cli-0.5.2/docs/adrs/0047-capture-activity-during-correlation-or-lose-it.md +108 -0
  12. stacktrace_cli-0.5.2/docs/adrs/0048-the-daemon-keeps-a-leveled-content-free-activity-log.md +59 -0
  13. stacktrace_cli-0.5.2/docs/adrs/0056-stop-a-resident-daemon-by-record-and-signal.md +124 -0
  14. stacktrace_cli-0.5.2/docs/adrs/0057-grow-the-queue-schema-additively-and-share-only-the-document.md +117 -0
  15. 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
  16. stacktrace_cli-0.5.2/docs/adrs/0059-the-daemon-does-not-decide-whether-it-should-be-running.md +111 -0
  17. stacktrace_cli-0.5.2/docs/adrs/0060-two-upload-postures-not-one-command-with-two-sources.md +121 -0
  18. stacktrace_cli-0.5.2/docs/adrs/0061-the-drain-sends-findings-only.md +118 -0
  19. stacktrace_cli-0.5.2/docs/adrs/0062-hold-the-transition-lock-across-remote-side-effects.md +134 -0
  20. stacktrace_cli-0.5.2/docs/adrs/0063-underbuilt-debt-does-not-hold-the-scan-watermark.md +90 -0
  21. stacktrace_cli-0.5.2/docs/adrs/0064-a-redacted-value-has-one-path-span.md +86 -0
  22. stacktrace_cli-0.5.2/docs/adrs/0065-asset-external-id-resolves-by-a-ladder.md +119 -0
  23. stacktrace_cli-0.5.2/docs/adrs/0066-host-owned-daemon-supervision.md +197 -0
  24. stacktrace_cli-0.5.2/docs/adrs/0067-a-webhook-is-an-independent-queue-subscriber.md +100 -0
  25. stacktrace_cli-0.5.2/docs/adrs/0068-webhooks-retry-the-retained-backlog.md +80 -0
  26. stacktrace_cli-0.5.2/docs/adrs/0069-webhooks-deliver-what-retention-holds.md +86 -0
  27. stacktrace_cli-0.5.2/docs/adrs/0070-package-the-cloud-agent-environment-bootstrap.md +162 -0
  28. stacktrace_cli-0.5.2/docs/adrs/0071-render-the-allowlisted-payload-per-destination.md +89 -0
  29. stacktrace_cli-0.5.2/docs/adrs/0072-host-supervisors-own-daemon-restarts.md +95 -0
  30. stacktrace_cli-0.5.2/docs/adrs/0073-name-what-fired-and-which-component.md +114 -0
  31. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/INDEX.md +24 -0
  32. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/cutover-openaca-remote.md +8 -6
  33. stacktrace_cli-0.5.2/docs/plans/011-daemon-store.md +2689 -0
  34. stacktrace_cli-0.5.2/docs/plans/012-daemon-scanning.md +172 -0
  35. stacktrace_cli-0.5.2/docs/plans/013-daemon-uploads.md +130 -0
  36. stacktrace_cli-0.5.2/docs/plans/014-asset-identity.md +666 -0
  37. stacktrace_cli-0.5.2/docs/releases/v0.5.1.md +209 -0
  38. stacktrace_cli-0.5.2/docs/releases/v0.5.2.md +215 -0
  39. stacktrace_cli-0.5.2/docs/specs/daemon-scanning.md +243 -0
  40. stacktrace_cli-0.5.2/docs/specs/daemon-store.md +246 -0
  41. stacktrace_cli-0.5.2/docs/specs/daemon-uploads.md +367 -0
  42. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/notification-delivery.md +101 -59
  43. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/remote-sync.md +6 -5
  44. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/pyproject.toml +2 -1
  45. stacktrace_cli-0.5.2/src/stacktrace_cli/__init__.py +10 -0
  46. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/cli.py +2 -0
  47. stacktrace_cli-0.5.2/src/stacktrace_cli/cloud/__init__.py +39 -0
  48. stacktrace_cli-0.5.2/src/stacktrace_cli/cloud/setup.sh +247 -0
  49. stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/activity_log.py +94 -0
  50. stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/bom.py +56 -0
  51. stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/cli.py +538 -0
  52. stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/client.py +355 -0
  53. stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/composition.py +242 -0
  54. stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/config.py +191 -0
  55. stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/db.py +625 -0
  56. stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/fleet.py +660 -0
  57. stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/identity.py +83 -0
  58. stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/jobs.py +181 -0
  59. stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/lifecycle.py +172 -0
  60. stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/observer.py +626 -0
  61. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/daemon/paths.py +6 -1
  62. stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/protocol.py +146 -0
  63. stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/runtime.py +694 -0
  64. stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/scan.py +256 -0
  65. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/daemon/server.py +141 -2
  66. stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/service.py +131 -0
  67. stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/store.py +911 -0
  68. stacktrace_cli-0.5.2/src/stacktrace_cli/daemon/webhook.py +508 -0
  69. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/render.py +31 -4
  70. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/site/app.js +1 -1
  71. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/site/index.html +3 -4
  72. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/cli.py +28 -19
  73. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/client.py +29 -0
  74. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/detect_payload.py +147 -25
  75. stacktrace_cli-0.5.2/src/stacktrace_cli/remote/identity.py +305 -0
  76. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/payload.py +7 -4
  77. stacktrace_cli-0.5.2/src/stacktrace_cli/remote/redact.py +1328 -0
  78. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/sync.py +330 -98
  79. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/sync_detect.py +134 -64
  80. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/upload_contract.py +10 -1
  81. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/telemetry/events.py +12 -6
  82. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/telemetry/posthog.py +34 -5
  83. stacktrace_cli-0.5.2/src/stacktrace_cli/webhook/__init__.py +1 -0
  84. stacktrace_cli-0.5.2/src/stacktrace_cli/webhook/cli.py +169 -0
  85. stacktrace_cli-0.5.2/src/stacktrace_cli/webhook/config.py +193 -0
  86. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/conftest.py +32 -2
  87. stacktrace_cli-0.5.2/tests/daemon/test_activity_log.py +328 -0
  88. stacktrace_cli-0.5.2/tests/daemon/test_cli.py +844 -0
  89. stacktrace_cli-0.5.2/tests/daemon/test_client.py +338 -0
  90. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/daemon/test_composition.py +87 -0
  91. stacktrace_cli-0.5.2/tests/daemon/test_config.py +92 -0
  92. stacktrace_cli-0.5.2/tests/daemon/test_db.py +827 -0
  93. stacktrace_cli-0.5.2/tests/daemon/test_detection_needs_no_subscriber.py +115 -0
  94. stacktrace_cli-0.5.2/tests/daemon/test_fleet.py +737 -0
  95. stacktrace_cli-0.5.2/tests/daemon/test_identity.py +91 -0
  96. stacktrace_cli-0.5.2/tests/daemon/test_jobs.py +329 -0
  97. stacktrace_cli-0.5.2/tests/daemon/test_lifecycle.py +115 -0
  98. stacktrace_cli-0.5.2/tests/daemon/test_observer.py +861 -0
  99. stacktrace_cli-0.5.2/tests/daemon/test_queue_reader.py +200 -0
  100. stacktrace_cli-0.5.2/tests/daemon/test_runtime.py +1455 -0
  101. stacktrace_cli-0.5.2/tests/daemon/test_scan.py +412 -0
  102. stacktrace_cli-0.5.2/tests/daemon/test_service.py +198 -0
  103. stacktrace_cli-0.5.2/tests/daemon/test_socket.py +547 -0
  104. stacktrace_cli-0.5.2/tests/daemon/test_store.py +690 -0
  105. stacktrace_cli-0.5.2/tests/daemon/test_the_drained_payload_is_the_cold_one.py +166 -0
  106. stacktrace_cli-0.5.2/tests/daemon/test_the_stored_row_is_sufficient.py +247 -0
  107. stacktrace_cli-0.5.2/tests/daemon/test_webhook.py +1072 -0
  108. stacktrace_cli-0.5.2/tests/daemon/test_wire_frames_are_bounded.py +156 -0
  109. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/test_reasoning_gates.py +3 -1
  110. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/test_server.py +18 -6
  111. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/helpers.py +4 -3
  112. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_cli.py +33 -0
  113. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_client.py +23 -0
  114. stacktrace_cli-0.5.2/tests/remote/test_detect_redaction.py +692 -0
  115. stacktrace_cli-0.5.2/tests/remote/test_identity.py +347 -0
  116. stacktrace_cli-0.5.2/tests/remote/test_no_local_path_survives_redaction.py +376 -0
  117. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_payload.py +10 -7
  118. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_redact_payload.py +37 -0
  119. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_seam.py +12 -5
  120. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_sync.py +475 -5
  121. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_sync_detect.py +208 -1
  122. stacktrace_cli-0.5.2/tests/telemetry/test_activity_log.py +135 -0
  123. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/telemetry/test_hooks.py +30 -30
  124. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_no_live_document_states_the_old_stage_three_default.py +7 -1
  125. stacktrace_cli-0.5.2/tests/test_no_test_undoes_every_patch_at_once.py +51 -0
  126. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_session_identity_is_the_pair.py +20 -8
  127. stacktrace_cli-0.5.2/tests/test_title_disclosure_invariant.py +159 -0
  128. stacktrace_cli-0.5.2/tests/webhook/test_cli.py +662 -0
  129. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/uv.lock +1 -1
  130. stacktrace_cli-0.5.0/src/stacktrace_cli/__init__.py +0 -3
  131. stacktrace_cli-0.5.0/src/stacktrace_cli/daemon/cli.py +0 -104
  132. stacktrace_cli-0.5.0/src/stacktrace_cli/daemon/client.py +0 -134
  133. stacktrace_cli-0.5.0/src/stacktrace_cli/daemon/composition.py +0 -133
  134. stacktrace_cli-0.5.0/src/stacktrace_cli/daemon/observer.py +0 -173
  135. stacktrace_cli-0.5.0/src/stacktrace_cli/daemon/protocol.py +0 -29
  136. stacktrace_cli-0.5.0/src/stacktrace_cli/daemon/runtime.py +0 -222
  137. stacktrace_cli-0.5.0/src/stacktrace_cli/daemon/service.py +0 -154
  138. stacktrace_cli-0.5.0/src/stacktrace_cli/daemon/store.py +0 -380
  139. stacktrace_cli-0.5.0/src/stacktrace_cli/remote/redact.py +0 -660
  140. stacktrace_cli-0.5.0/tests/daemon/test_cli.py +0 -224
  141. stacktrace_cli-0.5.0/tests/daemon/test_client.py +0 -136
  142. stacktrace_cli-0.5.0/tests/daemon/test_observer.py +0 -407
  143. stacktrace_cli-0.5.0/tests/daemon/test_runtime.py +0 -411
  144. stacktrace_cli-0.5.0/tests/daemon/test_service.py +0 -227
  145. stacktrace_cli-0.5.0/tests/daemon/test_socket.py +0 -183
  146. stacktrace_cli-0.5.0/tests/daemon/test_store.py +0 -207
  147. stacktrace_cli-0.5.0/tests/remote/test_detect_redaction.py +0 -191
  148. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/.agents/skills/release-stacktrace/SKILL.md +0 -0
  149. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/.claude/settings.json +0 -0
  150. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/.claude/skills/release-stacktrace/SKILL.md +0 -0
  151. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/.codex/hooks.json +0 -0
  152. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/.github/managed-autofix.md +0 -0
  153. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/.github/workflows/autofix.yml +0 -0
  154. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/.github/workflows/ci.yml +0 -0
  155. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/.github/workflows/claude.yml +0 -0
  156. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/.github/workflows/publish-pypi.yml +0 -0
  157. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/AGENTS.md +0 -0
  158. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/CLAUDE.md +0 -0
  159. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0001-session-telemetry-as-input.md +0 -0
  160. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0002-session-collection-in-openaidr.md +0 -0
  161. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0003-runtime-edges.md +0 -0
  162. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0004-three-stage-detector.md +0 -0
  163. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0005-detection-family-and-report-assembly.md +0 -0
  164. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0007-proprietary-package-on-open-dependencies.md +0 -0
  165. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0008-console-in-separate-demo-package.md +0 -0
  166. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0010-detection-severity-and-confidence-ladders.md +0 -0
  167. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0011-verdict-cache.md +0 -0
  168. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0012-observation-evidence-kinds-and-transport.md +0 -0
  169. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0013-rule-catalogue-triage-and-per-rule-context.md +0 -0
  170. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0014-declared-project-mapping.md +0 -0
  171. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0015-narrow-the-security-catalogue.md +0 -0
  172. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0016-stall-grouping-is-session-wide.md +0 -0
  173. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0017-a-declined-repeat-is-a-stall.md +0 -0
  174. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0018-remote-sync-config-and-facade-consumption.md +0 -0
  175. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0019-delegated-commands-are-openaca-objects.md +0 -0
  176. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0020-openaca-consumption-boundary.md +0 -0
  177. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0021-two-command-kinds.md +0 -0
  178. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0022-detection-scope-is-a-catalogue-column.md +0 -0
  179. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0023-sync-detect-collects-and-does-not-escalate.md +0 -0
  180. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0024-the-upload-carries-observations.md +0 -0
  181. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0025-a-denied-call-is-activity-never-an-invocation.md +0 -0
  182. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0026-the-console-becomes-a-product-surface.md +0 -0
  183. {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
  184. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0028-the-console-shows-what-it-placed.md +0 -0
  185. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0029-stage-three-returns-to-opt-in.md +0 -0
  186. {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
  187. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0032-refresh-daemon-compositions-outside-detection.md +0 -0
  188. {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
  189. {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
  190. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0035-naming-an-analyzer-selects-stored-answers.md +0 -0
  191. {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
  192. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0041-defer-native-windows-support.md +0 -0
  193. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0042-read-the-build-commit-from-the-install-record.md +0 -0
  194. {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
  195. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/0044-send-the-build-as-version-prints-it.md +0 -0
  196. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/HOOK-PROMPT.md +0 -0
  197. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/adrs/TEMPLATE.md +0 -0
  198. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/plans/002-session-input.md +0 -0
  199. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/plans/003-correlation.md +0 -0
  200. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/plans/004-detector.md +0 -0
  201. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/plans/005-remote-sync.md +0 -0
  202. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/plans/006-cli-composition.md +0 -0
  203. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/plans/007-detection-upload.md +0 -0
  204. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/plans/008-monitor.md +0 -0
  205. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/plans/009-reasoning-jev.md +0 -0
  206. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/plans/010-session-daemon.md +0 -0
  207. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.0.1.md +0 -0
  208. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.1.0.md +0 -0
  209. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.2.0.md +0 -0
  210. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.2.1.md +0 -0
  211. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.2.2.md +0 -0
  212. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.2.3.md +0 -0
  213. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.3.0.md +0 -0
  214. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.3.1.md +0 -0
  215. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.4.0.md +0 -0
  216. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.4.1.md +0 -0
  217. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/releases/v0.5.0.md +0 -0
  218. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/research/001-intent-drift-metric.md +0 -0
  219. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/aidr.md +0 -0
  220. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/cli-composition.md +0 -0
  221. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/correlation.md +0 -0
  222. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/detection-upload.md +0 -0
  223. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/detector.md +0 -0
  224. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/event-time.md +0 -0
  225. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/monitor.md +0 -0
  226. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/reasoning-jev.md +0 -0
  227. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/docs/specs/session-input.md +0 -0
  228. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/scripts/git-hooks/pre-push +0 -0
  229. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/scripts/install-hooks.sh +0 -0
  230. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/__main__.py +0 -0
  231. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/analysis.py +0 -0
  232. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/build.py +0 -0
  233. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/correlate/__init__.py +0 -0
  234. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/correlate/acquire.py +0 -0
  235. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/correlate/composition.py +0 -0
  236. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/correlate/join.py +0 -0
  237. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/correlate/observed.py +0 -0
  238. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/correlate/orchestrate.py +0 -0
  239. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/correlate/project_map.py +0 -0
  240. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/correlate/record.py +0 -0
  241. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/correlate/render.py +0 -0
  242. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/daemon/__init__.py +0 -0
  243. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/daemon/presentation.py +0 -0
  244. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/__init__.py +0 -0
  245. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/analyzer.py +0 -0
  246. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/blocked.py +0 -0
  247. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/cache.py +0 -0
  248. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/deterministic.py +0 -0
  249. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/finding.py +0 -0
  250. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/history.py +0 -0
  251. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/jev.py +0 -0
  252. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/priors.py +0 -0
  253. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/prompts/__init__.py +0 -0
  254. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/prompts/jev-v1/stacktrace-deceptive-completion.json +0 -0
  255. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/prompts/jev-v2/stacktrace-deceptive-completion.json +0 -0
  256. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/prompts/v1/exclusions.md +0 -0
  257. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/prompts/v1/framing.md +0 -0
  258. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/prompts/v1/stacktrace-deceptive-completion.md +0 -0
  259. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/reasoning.py +0 -0
  260. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/rules.py +0 -0
  261. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/run.py +0 -0
  262. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/secrets.py +0 -0
  263. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/verdict.py +0 -0
  264. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/detector/windows.py +0 -0
  265. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/__init__.py +0 -0
  266. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/reasoning.py +0 -0
  267. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/render.py +0 -0
  268. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/server.py +0 -0
  269. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/site/fonts/OFL.txt +0 -0
  270. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/site/fonts/dm-mono-400-latin.woff2 +0 -0
  271. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/site/fonts/dm-mono-500-latin.woff2 +0 -0
  272. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/site/fonts/dm-sans-latin.woff2 +0 -0
  273. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/site/styles.css +0 -0
  274. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/state.py +0 -0
  275. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/verdicts.py +0 -0
  276. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/monitor/watch.py +0 -0
  277. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/options.py +0 -0
  278. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/private_state.py +0 -0
  279. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/__init__.py +0 -0
  280. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/config.py +0 -0
  281. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/policy.py +0 -0
  282. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/remote/spool.py +0 -0
  283. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/sessions/__init__.py +0 -0
  284. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/sessions/access.py +0 -0
  285. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/sessions/index.py +0 -0
  286. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/sessions/outcome.py +0 -0
  287. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/sessions/protocols.py +0 -0
  288. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/sessions/render.py +0 -0
  289. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/telemetry/__init__.py +0 -0
  290. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/telemetry/cli.py +0 -0
  291. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/src/stacktrace_cli/telemetry/state.py +0 -0
  292. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/__init__.py +0 -0
  293. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/agent_bom_fixture.py +0 -0
  294. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/daemon/__init__.py +0 -0
  295. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/detector_session_fixture.py +0 -0
  296. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/__init__.py +0 -0
  297. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/feed_harness.mjs +0 -0
  298. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/test_feed_model.py +0 -0
  299. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/test_render.py +0 -0
  300. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/test_site_assets.py +0 -0
  301. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/test_state.py +0 -0
  302. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/test_verdicts.py +0 -0
  303. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/monitor/test_watch.py +0 -0
  304. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/__init__.py +0 -0
  305. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_client.py +0 -0
  306. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_config.py +0 -0
  307. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_activity.py +0 -0
  308. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_cli.py +0 -0
  309. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_contract_is_exhaustive.py +0 -0
  310. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_gate_matches_the_cloud.py +0 -0
  311. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_layers_compose.py +0 -0
  312. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_payload_reaches_the_cloud_model.py +0 -0
  313. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_spool.py +0 -0
  314. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_upload_contract.py +0 -0
  315. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_detect_wire_payload.py +0 -0
  316. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_facade_contract.py +0 -0
  317. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_policy.py +0 -0
  318. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_spool.py +0 -0
  319. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/remote/test_upload_contract.py +0 -0
  320. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/telemetry/__init__.py +0 -0
  321. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/telemetry/test_cli.py +0 -0
  322. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/telemetry/test_events.py +0 -0
  323. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/telemetry/test_release_gate.py +0 -0
  324. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/telemetry/test_state.py +0 -0
  325. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/telemetry/test_uploader.py +0 -0
  326. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_acquire.py +0 -0
  327. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_adr_cross_references_are_reciprocal.py +0 -0
  328. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_agent_instance_id.py +0 -0
  329. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_analysis.py +0 -0
  330. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_bom_shape.py +0 -0
  331. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_build_commit.py +0 -0
  332. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_call_status_conventions.py +0 -0
  333. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_cli.py +0 -0
  334. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_composition.py +0 -0
  335. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_correlate_orchestrate.py +0 -0
  336. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_correlate_render.py +0 -0
  337. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_correlated_session.py +0 -0
  338. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detection_components.py +0 -0
  339. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detection_event_time.py +0 -0
  340. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detection_subject.py +0 -0
  341. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_analyzer_contract.py +0 -0
  342. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_analyzer_live.py +0 -0
  343. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_blocked.py +0 -0
  344. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_cache.py +0 -0
  345. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_deterministic.py +0 -0
  346. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_end_to_end.py +0 -0
  347. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_finding.py +0 -0
  348. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_history.py +0 -0
  349. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_jev.py +0 -0
  350. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_priors.py +0 -0
  351. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_prompts.py +0 -0
  352. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_reasoning.py +0 -0
  353. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_render.py +0 -0
  354. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_run.py +0 -0
  355. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_secrets.py +0 -0
  356. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_verdict.py +0 -0
  357. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_why_it_stopped.py +0 -0
  358. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_detector_windows.py +0 -0
  359. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_evidence_is_dated_not_numbered.py +0 -0
  360. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_fingerprint_covers_what_the_analyzer_reads.py +0 -0
  361. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_help_sections.py +0 -0
  362. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_join.py +0 -0
  363. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_monitor_cli.py +0 -0
  364. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_observed.py +0 -0
  365. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_openaca_contract.py +0 -0
  366. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_openaidr_contract.py +0 -0
  367. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_option_arity_matches_the_binary.py +0 -0
  368. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_project_map.py +0 -0
  369. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_readme_examples_are_real_output.py +0 -0
  370. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_release_gate_coverage.py +0 -0
  371. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_release_readiness.py +0 -0
  372. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_seam_boundary.py +0 -0
  373. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_sessions_access.py +0 -0
  374. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_sessions_end_to_end.py +0 -0
  375. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_sessions_protocol_typing.py +0 -0
  376. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_sessions_protocols.py +0 -0
  377. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_sessions_render.py +0 -0
  378. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_shell_separators_match_the_shell.py +0 -0
  379. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_stored_verdicts_are_never_gated_on_reasoning.py +0 -0
  380. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_the_detector_spec_lists_the_shipped_catalogue.py +0 -0
  381. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_the_jev_spec_states_the_shipped_thresholds.py +0 -0
  382. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_the_locality_claim_is_qualified_everywhere.py +0 -0
  383. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_untrusted_content_never_travels.py +0 -0
  384. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_verification_subcommands_match_the_binary.py +0 -0
  385. {stacktrace_cli-0.5.0 → stacktrace_cli-0.5.2}/tests/test_what_jev_is_told_is_what_was_disclosed.py +0 -0
@@ -6,3 +6,6 @@ __pycache__/
6
6
 
7
7
  # Local git worktrees
8
8
  .worktrees/
9
+
10
+ # Local research handoffs, kept on disk and out of the repo.
11
+ HANDOFF-*.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: stacktrace-cli
3
- Version: 0.5.0
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.0 (openaca 0.7.0)
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.0 (openaca 0.7.0)
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
@@ -5,7 +5,7 @@ status: accepted
5
5
  date: 2026-08-28
6
6
  supersedes: null
7
7
  superseded-by: null
8
- amended-by: 0011, 0027, 0031, 0039
8
+ amended-by: 0011, 0027, 0031, 0039, 0064
9
9
  ---
10
10
 
11
11
  ## Context
@@ -6,7 +6,7 @@ date: 2026-09-17
6
6
  supersedes: null
7
7
  superseded-by: null
8
8
  amends: 0006
9
- amended-by: 0032, 0036, 0037, 0038, 0039
9
+ amended-by: 0032, 0036, 0037, 0038, 0039, 0045, 0046, 0066, 0072
10
10
  ---
11
11
 
12
12
  ## Context
@@ -6,7 +6,7 @@ date: 2026-09-20
6
6
  supersedes: null
7
7
  superseded-by: null
8
8
  amends: 0031
9
- amended-by: null
9
+ amended-by: 0067
10
10
  ---
11
11
 
12
12
  Subsystem: the daemon and its delivery sinks. The integration view across the
@@ -6,7 +6,7 @@ date: 2026-09-18
6
6
  supersedes: null
7
7
  superseded-by: null
8
8
  amends: 0031
9
- amended-by: 0038
9
+ amended-by: 0038, 0045
10
10
  ---
11
11
 
12
12
  ## Context
@@ -6,7 +6,7 @@ date: 2026-09-21
6
6
  supersedes: null
7
7
  superseded-by: null
8
8
  amends: 0031, 0037
9
- amended-by: null
9
+ amended-by: 0045
10
10
  ---
11
11
 
12
12
  ## Context
@@ -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`.