stacktrace-cli 0.5.3__tar.gz → 0.6.1__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 (393) hide show
  1. stacktrace_cli-0.6.1/.claude/skills/pr-review-loop/SKILL.md +120 -0
  2. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/PKG-INFO +3 -3
  3. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/README.md +1 -1
  4. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0006-trust-boundary-and-detection-upload.md +1 -1
  5. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0029-stage-three-returns-to-opt-in.md +2 -2
  6. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0033-a-third-party-analyzer-only-by-a-flag-that-names-it.md +2 -2
  7. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0035-naming-an-analyzer-selects-stored-answers.md +1 -1
  8. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0039-send-usage-telemetry-by-default-with-an-opt-out.md +1 -1
  9. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0060-two-upload-postures-not-one-command-with-two-sources.md +1 -1
  10. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0065-asset-external-id-resolves-by-a-ladder.md +1 -1
  11. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0067-a-webhook-is-an-independent-queue-subscriber.md +1 -1
  12. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0068-webhooks-retry-the-retained-backlog.md +1 -1
  13. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0073-name-what-fired-and-which-component.md +1 -1
  14. stacktrace_cli-0.6.1/docs/adrs/0076-two-phase-detection-pipeline.md +164 -0
  15. stacktrace_cli-0.6.1/docs/adrs/0077-the-bom-job-wakes-once-at-startup-staggered.md +208 -0
  16. stacktrace_cli-0.6.1/docs/adrs/0078-a-seeded-display-prefix-replaces-the-hostname-label.md +74 -0
  17. stacktrace_cli-0.6.1/docs/adrs/0079-the-monitor-page-renders-findings-not-pipeline-health.md +93 -0
  18. stacktrace_cli-0.6.1/docs/adrs/0080-enabling-reasoning-does-not-requeue-the-past.md +103 -0
  19. stacktrace_cli-0.6.1/docs/adrs/0081-reasoning-activation-returns-to-explicit-opt-in.md +136 -0
  20. stacktrace_cli-0.6.1/docs/adrs/0082-monitor-checks-every-installed-component-by-default.md +72 -0
  21. stacktrace_cli-0.6.1/docs/adrs/0083-each-block-reason-is-its-own-rule.md +127 -0
  22. stacktrace_cli-0.6.1/docs/adrs/0084-a-recursive-deletes-evidence-is-the-rm-that-ran.md +85 -0
  23. stacktrace_cli-0.6.1/docs/adrs/0085-show-when-and-which-finding-slack-delivered.md +88 -0
  24. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/INDEX.md +14 -5
  25. stacktrace_cli-0.6.1/docs/releases/v0.6.0.md +120 -0
  26. stacktrace_cli-0.6.1/docs/releases/v0.6.1.md +108 -0
  27. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/specs/daemon-uploads.md +14 -4
  28. stacktrace_cli-0.6.1/docs/specs/detection-policies.md +368 -0
  29. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/specs/detection-upload.md +11 -7
  30. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/specs/detector.md +85 -20
  31. stacktrace_cli-0.6.1/docs/specs/monitor-inventory.md +203 -0
  32. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/specs/monitor.md +169 -167
  33. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/specs/notification-delivery.md +21 -18
  34. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/pyproject.toml +2 -2
  35. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/__init__.py +1 -1
  36. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/analysis.py +33 -31
  37. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/cli.py +5 -0
  38. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/cloud/__init__.py +10 -0
  39. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/cloud/setup.sh +17 -4
  40. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/correlate/acquire.py +60 -67
  41. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/correlate/composition.py +4 -4
  42. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/correlate/observed.py +62 -6
  43. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/correlate/orchestrate.py +30 -38
  44. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/cli.py +93 -2
  45. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/config.py +85 -5
  46. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/identity.py +17 -1
  47. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/jobs.py +48 -1
  48. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/observer.py +31 -2
  49. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/presentation.py +34 -1
  50. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/runtime.py +25 -11
  51. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/store.py +60 -15
  52. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/webhook.py +34 -4
  53. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/blocked.py +12 -0
  54. stacktrace_cli-0.6.1/src/stacktrace_cli/detector/deterministic.py +2414 -0
  55. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/render.py +6 -9
  56. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/rules.py +74 -12
  57. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/run.py +11 -3
  58. stacktrace_cli-0.6.1/src/stacktrace_cli/monitor/inventory.py +495 -0
  59. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/monitor/reasoning.py +8 -6
  60. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/monitor/render.py +49 -1
  61. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/monitor/server.py +44 -20
  62. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/monitor/site/app.js +386 -369
  63. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/monitor/site/index.html +26 -14
  64. stacktrace_cli-0.6.1/src/stacktrace_cli/monitor/site/inventory.js +554 -0
  65. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/monitor/site/styles.css +139 -51
  66. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/monitor/state.py +45 -2
  67. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/monitor/watch.py +294 -129
  68. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/remote/identity.py +41 -12
  69. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/remote/payload.py +10 -5
  70. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/remote/redact.py +100 -2
  71. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/remote/sync.py +11 -0
  72. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/telemetry/events.py +12 -30
  73. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/webhook/cli.py +35 -4
  74. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/conftest.py +25 -1
  75. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_cli.py +201 -13
  76. stacktrace_cli-0.6.1/tests/daemon/test_config.py +195 -0
  77. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_jobs.py +116 -0
  78. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_observer.py +194 -0
  79. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_runtime.py +97 -0
  80. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_store.py +117 -7
  81. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_the_drained_payload_is_the_cold_one.py +124 -3
  82. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_webhook.py +99 -8
  83. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/detector_session_fixture.py +1 -1
  84. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/monitor/feed_harness.mjs +1078 -284
  85. stacktrace_cli-0.6.1/tests/monitor/inventory_harness.mjs +429 -0
  86. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/monitor/test_feed_model.py +470 -226
  87. stacktrace_cli-0.6.1/tests/monitor/test_inventory.py +866 -0
  88. stacktrace_cli-0.6.1/tests/monitor/test_inventory_page.py +241 -0
  89. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/monitor/test_render.py +49 -0
  90. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/monitor/test_server.py +11 -2
  91. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/monitor/test_site_assets.py +68 -52
  92. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/monitor/test_state.py +37 -0
  93. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/monitor/test_watch.py +821 -60
  94. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_detect_redaction.py +131 -0
  95. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_facade_contract.py +172 -1
  96. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_identity.py +62 -0
  97. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/telemetry/test_events.py +24 -34
  98. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/telemetry/test_hooks.py +21 -10
  99. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_acquire.py +94 -41
  100. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_adr_cross_references_are_reciprocal.py +15 -0
  101. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_analysis.py +4 -4
  102. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_correlate_orchestrate.py +49 -3
  103. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detection_components.py +4 -4
  104. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detection_event_time.py +2 -2
  105. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detection_subject.py +1 -1
  106. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detector_blocked.py +27 -0
  107. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detector_cache.py +6 -6
  108. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detector_end_to_end.py +8 -5
  109. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detector_finding.py +33 -7
  110. stacktrace_cli-0.6.1/tests/test_detector_new_deterministic.py +3803 -0
  111. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detector_priors.py +2 -2
  112. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detector_render.py +21 -4
  113. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detector_verdict.py +2 -2
  114. stacktrace_cli-0.6.1/tests/test_detector_vulnerable_dep_invoked.py +477 -0
  115. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detector_why_it_stopped.py +46 -14
  116. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_evidence_is_dated_not_numbered.py +6 -1
  117. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_fingerprint_covers_what_the_analyzer_reads.py +1 -1
  118. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_monitor_cli.py +10 -14
  119. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_readme_examples_are_real_output.py +1 -1
  120. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_seam_boundary.py +3 -0
  121. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_shell_separators_match_the_shell.py +51 -5
  122. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_the_detector_spec_lists_the_shipped_catalogue.py +63 -1
  123. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_the_locality_claim_is_qualified_everywhere.py +0 -39
  124. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_title_disclosure_invariant.py +4 -4
  125. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_untrusted_content_never_travels.py +104 -6
  126. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_what_jev_is_told_is_what_was_disclosed.py +2 -26
  127. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/webhook/test_cli.py +82 -0
  128. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/uv.lock +5 -5
  129. stacktrace_cli-0.5.3/src/stacktrace_cli/detector/deterministic.py +0 -506
  130. stacktrace_cli-0.5.3/tests/daemon/test_config.py +0 -92
  131. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/.agents/skills/release-stacktrace/SKILL.md +0 -0
  132. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/.claude/settings.json +0 -0
  133. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/.claude/skills/release-stacktrace/SKILL.md +0 -0
  134. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/.codex/hooks.json +0 -0
  135. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/.github/managed-autofix.md +0 -0
  136. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/.github/workflows/autofix.yml +0 -0
  137. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/.github/workflows/ci.yml +0 -0
  138. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/.github/workflows/claude.yml +0 -0
  139. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/.github/workflows/publish-pypi.yml +0 -0
  140. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/.gitignore +0 -0
  141. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/AGENTS.md +0 -0
  142. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/CLAUDE.md +0 -0
  143. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0001-session-telemetry-as-input.md +0 -0
  144. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0002-session-collection-in-openaidr.md +0 -0
  145. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0003-runtime-edges.md +0 -0
  146. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0004-three-stage-detector.md +0 -0
  147. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0005-detection-family-and-report-assembly.md +0 -0
  148. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0007-proprietary-package-on-open-dependencies.md +0 -0
  149. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0008-console-in-separate-demo-package.md +0 -0
  150. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0010-detection-severity-and-confidence-ladders.md +0 -0
  151. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0011-verdict-cache.md +0 -0
  152. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0012-observation-evidence-kinds-and-transport.md +0 -0
  153. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0013-rule-catalogue-triage-and-per-rule-context.md +0 -0
  154. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0014-declared-project-mapping.md +0 -0
  155. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0015-narrow-the-security-catalogue.md +0 -0
  156. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0016-stall-grouping-is-session-wide.md +0 -0
  157. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0017-a-declined-repeat-is-a-stall.md +0 -0
  158. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0018-remote-sync-config-and-facade-consumption.md +0 -0
  159. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0019-delegated-commands-are-openaca-objects.md +0 -0
  160. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0020-openaca-consumption-boundary.md +0 -0
  161. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0021-two-command-kinds.md +0 -0
  162. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0022-detection-scope-is-a-catalogue-column.md +0 -0
  163. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0023-sync-detect-collects-and-does-not-escalate.md +0 -0
  164. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0024-the-upload-carries-observations.md +0 -0
  165. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0025-a-denied-call-is-activity-never-an-invocation.md +0 -0
  166. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0026-the-console-becomes-a-product-surface.md +0 -0
  167. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0027-a-finding-may-name-what-it-could-not-place.md +0 -0
  168. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0028-the-console-shows-what-it-placed.md +0 -0
  169. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0030-a-detection-is-dated-by-the-turns-it-cites.md +0 -0
  170. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0031-one-host-daemon-observes-native-session-logs.md +0 -0
  171. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0032-refresh-daemon-compositions-outside-detection.md +0 -0
  172. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0034-the-drift-grade-is-read-off-a-run-not-a-call.md +0 -0
  173. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0036-deliver-eligible-findings-to-slack-from-the-daemon.md +0 -0
  174. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0037-reuse-openaidr-append-cursors-in-the-daemon.md +0 -0
  175. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0038-poll-normalized-sessions-for-supplemental-changes.md +0 -0
  176. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0041-defer-native-windows-support.md +0 -0
  177. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0042-read-the-build-commit-from-the-install-record.md +0 -0
  178. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0043-name-the-session-agent-and-host-and-count-errors-by-class.md +0 -0
  179. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0044-send-the-build-as-version-prints-it.md +0 -0
  180. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0045-scan-every-session-and-outlive-them.md +0 -0
  181. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0046-the-finding-store-is-an-append-only-queue.md +0 -0
  182. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0047-capture-activity-during-correlation-or-lose-it.md +0 -0
  183. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0048-the-daemon-keeps-a-leveled-content-free-activity-log.md +0 -0
  184. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0056-stop-a-resident-daemon-by-record-and-signal.md +0 -0
  185. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0057-grow-the-queue-schema-additively-and-share-only-the-document.md +0 -0
  186. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0058-one-word-for-a-finding-columns-for-a-counter-and-a-push-that-does-not-wait.md +0 -0
  187. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0059-the-daemon-does-not-decide-whether-it-should-be-running.md +0 -0
  188. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0061-the-drain-sends-findings-only.md +0 -0
  189. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0062-hold-the-transition-lock-across-remote-side-effects.md +0 -0
  190. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0063-underbuilt-debt-does-not-hold-the-scan-watermark.md +0 -0
  191. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0064-a-redacted-value-has-one-path-span.md +0 -0
  192. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0066-host-owned-daemon-supervision.md +0 -0
  193. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0069-webhooks-deliver-what-retention-holds.md +0 -0
  194. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0070-package-the-cloud-agent-environment-bootstrap.md +0 -0
  195. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0071-render-the-allowlisted-payload-per-destination.md +0 -0
  196. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0072-host-supervisors-own-daemon-restarts.md +0 -0
  197. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/0074-a-window-selects-findings-by-when-they-happened.md +0 -0
  198. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/HOOK-PROMPT.md +0 -0
  199. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/adrs/TEMPLATE.md +0 -0
  200. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/cutover-openaca-remote.md +0 -0
  201. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/plans/002-session-input.md +0 -0
  202. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/plans/003-correlation.md +0 -0
  203. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/plans/004-detector.md +0 -0
  204. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/plans/005-remote-sync.md +0 -0
  205. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/plans/006-cli-composition.md +0 -0
  206. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/plans/007-detection-upload.md +0 -0
  207. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/plans/008-monitor.md +0 -0
  208. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/plans/009-reasoning-jev.md +0 -0
  209. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/plans/010-session-daemon.md +0 -0
  210. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/plans/011-daemon-store.md +0 -0
  211. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/plans/012-daemon-scanning.md +0 -0
  212. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/plans/013-daemon-uploads.md +0 -0
  213. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/plans/014-asset-identity.md +0 -0
  214. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/releases/v0.0.1.md +0 -0
  215. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/releases/v0.1.0.md +0 -0
  216. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/releases/v0.2.0.md +0 -0
  217. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/releases/v0.2.1.md +0 -0
  218. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/releases/v0.2.2.md +0 -0
  219. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/releases/v0.2.3.md +0 -0
  220. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/releases/v0.3.0.md +0 -0
  221. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/releases/v0.3.1.md +0 -0
  222. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/releases/v0.4.0.md +0 -0
  223. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/releases/v0.4.1.md +0 -0
  224. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/releases/v0.5.0.md +0 -0
  225. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/releases/v0.5.1.md +0 -0
  226. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/releases/v0.5.2.md +0 -0
  227. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/releases/v0.5.3.md +0 -0
  228. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/research/001-intent-drift-metric.md +0 -0
  229. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/specs/aidr.md +0 -0
  230. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/specs/cli-composition.md +0 -0
  231. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/specs/correlation.md +0 -0
  232. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/specs/daemon-scanning.md +0 -0
  233. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/specs/daemon-store.md +0 -0
  234. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/specs/event-time.md +0 -0
  235. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/specs/reasoning-jev.md +0 -0
  236. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/specs/remote-sync.md +0 -0
  237. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/docs/specs/session-input.md +0 -0
  238. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/scripts/git-hooks/pre-push +0 -0
  239. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/scripts/install-hooks.sh +0 -0
  240. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/__main__.py +0 -0
  241. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/build.py +0 -0
  242. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/correlate/__init__.py +0 -0
  243. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/correlate/join.py +0 -0
  244. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/correlate/project_map.py +0 -0
  245. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/correlate/record.py +0 -0
  246. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/correlate/render.py +0 -0
  247. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/__init__.py +0 -0
  248. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/activity_log.py +0 -0
  249. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/bom.py +0 -0
  250. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/client.py +0 -0
  251. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/composition.py +0 -0
  252. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/db.py +0 -0
  253. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/fleet.py +0 -0
  254. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/lifecycle.py +0 -0
  255. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/paths.py +0 -0
  256. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/protocol.py +0 -0
  257. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/scan.py +0 -0
  258. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/server.py +0 -0
  259. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/daemon/service.py +0 -0
  260. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/__init__.py +0 -0
  261. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/analyzer.py +0 -0
  262. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/cache.py +0 -0
  263. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/finding.py +0 -0
  264. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/history.py +0 -0
  265. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/jev.py +0 -0
  266. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/priors.py +0 -0
  267. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/prompts/__init__.py +0 -0
  268. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/prompts/jev-v1/stacktrace-deceptive-completion.json +0 -0
  269. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/prompts/jev-v2/stacktrace-deceptive-completion.json +0 -0
  270. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/prompts/v1/exclusions.md +0 -0
  271. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/prompts/v1/framing.md +0 -0
  272. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/prompts/v1/stacktrace-deceptive-completion.md +0 -0
  273. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/reasoning.py +0 -0
  274. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/secrets.py +0 -0
  275. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/verdict.py +0 -0
  276. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/detector/windows.py +0 -0
  277. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/monitor/__init__.py +0 -0
  278. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/monitor/site/fonts/OFL.txt +0 -0
  279. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/monitor/site/fonts/dm-mono-400-latin.woff2 +0 -0
  280. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/monitor/site/fonts/dm-mono-500-latin.woff2 +0 -0
  281. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/monitor/site/fonts/dm-sans-latin.woff2 +0 -0
  282. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/monitor/verdicts.py +0 -0
  283. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/options.py +0 -0
  284. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/private_state.py +0 -0
  285. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/remote/__init__.py +0 -0
  286. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/remote/cli.py +0 -0
  287. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/remote/client.py +0 -0
  288. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/remote/config.py +0 -0
  289. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/remote/detect_payload.py +0 -0
  290. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/remote/policy.py +0 -0
  291. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/remote/spool.py +0 -0
  292. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/remote/sync_detect.py +0 -0
  293. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/remote/upload_contract.py +0 -0
  294. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/sessions/__init__.py +0 -0
  295. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/sessions/access.py +0 -0
  296. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/sessions/index.py +0 -0
  297. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/sessions/outcome.py +0 -0
  298. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/sessions/protocols.py +0 -0
  299. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/sessions/render.py +0 -0
  300. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/telemetry/__init__.py +0 -0
  301. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/telemetry/cli.py +0 -0
  302. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/telemetry/posthog.py +0 -0
  303. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/telemetry/state.py +0 -0
  304. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/webhook/__init__.py +0 -0
  305. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/src/stacktrace_cli/webhook/config.py +0 -0
  306. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/__init__.py +0 -0
  307. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/agent_bom_fixture.py +0 -0
  308. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/__init__.py +0 -0
  309. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_activity_log.py +0 -0
  310. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_client.py +0 -0
  311. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_composition.py +0 -0
  312. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_db.py +0 -0
  313. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_detection_needs_no_subscriber.py +0 -0
  314. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_fleet.py +0 -0
  315. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_identity.py +0 -0
  316. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_lifecycle.py +0 -0
  317. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_queue_reader.py +0 -0
  318. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_scan.py +0 -0
  319. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_service.py +0 -0
  320. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_socket.py +0 -0
  321. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_the_stored_row_is_sufficient.py +0 -0
  322. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/daemon/test_wire_frames_are_bounded.py +0 -0
  323. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/monitor/__init__.py +0 -0
  324. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/monitor/test_reasoning_gates.py +0 -0
  325. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/monitor/test_verdicts.py +0 -0
  326. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/__init__.py +0 -0
  327. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/helpers.py +0 -0
  328. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_cli.py +0 -0
  329. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_client.py +0 -0
  330. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_config.py +0 -0
  331. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_detect_activity.py +0 -0
  332. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_detect_cli.py +0 -0
  333. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_detect_client.py +0 -0
  334. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_detect_contract_is_exhaustive.py +0 -0
  335. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_detect_gate_matches_the_cloud.py +0 -0
  336. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_detect_layers_compose.py +0 -0
  337. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_detect_payload_reaches_the_cloud_model.py +0 -0
  338. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_detect_spool.py +0 -0
  339. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_detect_upload_contract.py +0 -0
  340. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_detect_wire_payload.py +0 -0
  341. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_no_local_path_survives_redaction.py +0 -0
  342. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_payload.py +0 -0
  343. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_policy.py +0 -0
  344. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_redact_payload.py +0 -0
  345. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_seam.py +0 -0
  346. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_spool.py +0 -0
  347. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_sync.py +0 -0
  348. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_sync_detect.py +0 -0
  349. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/remote/test_upload_contract.py +0 -0
  350. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/telemetry/__init__.py +0 -0
  351. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/telemetry/test_activity_log.py +0 -0
  352. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/telemetry/test_cli.py +0 -0
  353. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/telemetry/test_release_gate.py +0 -0
  354. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/telemetry/test_state.py +0 -0
  355. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/telemetry/test_uploader.py +0 -0
  356. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_agent_instance_id.py +0 -0
  357. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_bom_shape.py +0 -0
  358. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_build_commit.py +0 -0
  359. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_call_status_conventions.py +0 -0
  360. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_cli.py +0 -0
  361. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_composition.py +0 -0
  362. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_correlate_render.py +0 -0
  363. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_correlated_session.py +0 -0
  364. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detector_analyzer_contract.py +0 -0
  365. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detector_analyzer_live.py +0 -0
  366. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detector_deterministic.py +0 -0
  367. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detector_history.py +0 -0
  368. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detector_jev.py +0 -0
  369. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detector_prompts.py +0 -0
  370. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detector_reasoning.py +0 -0
  371. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detector_run.py +0 -0
  372. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detector_secrets.py +0 -0
  373. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_detector_windows.py +0 -0
  374. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_help_sections.py +0 -0
  375. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_join.py +0 -0
  376. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_no_live_document_states_the_old_stage_three_default.py +0 -0
  377. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_no_test_undoes_every_patch_at_once.py +0 -0
  378. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_observed.py +0 -0
  379. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_openaca_contract.py +0 -0
  380. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_openaidr_contract.py +0 -0
  381. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_option_arity_matches_the_binary.py +0 -0
  382. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_project_map.py +0 -0
  383. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_release_gate_coverage.py +0 -0
  384. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_release_readiness.py +0 -0
  385. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_session_identity_is_the_pair.py +0 -0
  386. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_sessions_access.py +0 -0
  387. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_sessions_end_to_end.py +0 -0
  388. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_sessions_protocol_typing.py +0 -0
  389. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_sessions_protocols.py +0 -0
  390. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_sessions_render.py +0 -0
  391. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_stored_verdicts_are_never_gated_on_reasoning.py +0 -0
  392. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_the_jev_spec_states_the_shipped_thresholds.py +0 -0
  393. {stacktrace_cli-0.5.3 → stacktrace_cli-0.6.1}/tests/test_verification_subcommands_match_the_binary.py +0 -0
@@ -0,0 +1,120 @@
1
+ ---
2
+ name: pr-review-loop
3
+ description: Autonomous code → commit → push → PR → review monitoring → fix → thumbsup loop. Use when the user wants end-to-end delivery of code changes through Codex review approval.
4
+ ---
5
+
6
+ # pr-review-loop
7
+
8
+ End-to-end autonomous loop: generate code, commit, push, create or update a PR,
9
+ monitor Codex review, fix findings, and iterate until the PR receives a thumbsup
10
+ or approval with no actionable findings remaining.
11
+
12
+ **Announce at start:** "I'm using the pr-review-loop skill to deliver this change
13
+ through review."
14
+
15
+ ## When to invoke
16
+
17
+ User says any of: "ship this", "get this to thumbsup", "run the review loop",
18
+ "deliver through review", or asks for code changes with instructions to
19
+ autonomously handle the PR review cycle.
20
+
21
+ ## Inputs
22
+
23
+ 1. **Branch name** — inferred from the current branch, or ask the user.
24
+ 2. **PR number** — if a PR already exists for the branch, use it. Otherwise
25
+ create a new one.
26
+ 3. **Base branch** — defaults to `main`.
27
+
28
+ ## Phase 1: Code generation
29
+
30
+ 1. Implement the requested changes following the repo's conventions (CLAUDE.md):
31
+ - TDD for business logic (red → green → refactor)
32
+ - Surgical changes — touch only what you must
33
+ - Simplicity first — no speculative abstractions
34
+ 2. Run all four gates before committing:
35
+ ```bash
36
+ uv run ruff check .
37
+ uv run ruff format --check .
38
+ uv run pyright
39
+ uv run pytest -q
40
+ ```
41
+ 3. Fix any failures. If a gate reveals a design decision, go with the
42
+ recommendation and log the decision.
43
+
44
+ ## Phase 2: Commit, push, PR
45
+
46
+ 1. Stage only the files your changes touch — never `git add -A`.
47
+ 2. Commit with a descriptive message explaining WHY, not WHAT.
48
+ 3. Push to remote. If rejected, stop and report it as a blocker — fetch and
49
+ look at what changed, but do not rebase or force-push to resolve it
50
+ automatically (see "Never force-push, rebase published commits" below).
51
+ 4. If no PR exists for this branch, create one with `gh pr create`. Include a
52
+ summary, list of changes, and a test plan.
53
+ 5. If a PR already exists, update its description if the scope changed.
54
+
55
+ ## Phase 3: Monitor review
56
+
57
+ Poll for review status using a dynamic loop (`/loop`). On each tick:
58
+
59
+ 1. Check CI status:
60
+ ```bash
61
+ gh pr checks <PR_NUMBER>
62
+ ```
63
+ 2. Check for new review comments:
64
+ ```bash
65
+ gh api repos/<OWNER>/<REPO>/pulls/<PR_NUMBER>/comments --paginate
66
+ gh api repos/<OWNER>/<REPO>/pulls/<PR_NUMBER>/reviews
67
+ ```
68
+ 3. If CI fails, read the logs and fix the issue. Push a new commit.
69
+ 4. If Codex or a human reviewer left comments:
70
+ - Read each comment thread.
71
+ - For P0/P1/P2 findings: fix them. Push a new commit.
72
+ - For P3 findings: defer unless the user approved them.
73
+ - If you disagree with a finding: reply on the PR explaining why,
74
+ with evidence. Do not silently skip it.
75
+ 5. If a reviewer requested changes, address them and re-request review.
76
+
77
+ ### Decisions during review
78
+
79
+ - **Make autonomous decisions** for: code style fixes, test additions,
80
+ lint/format fixes, obvious bug fixes, naming improvements.
81
+ - **Log but proceed** for: architecture choices between reasonable alternatives,
82
+ threshold values, which invariant test to extend.
83
+ - **Stop and ask** for: design-level questions flagged for the user, scope
84
+ changes that would significantly alter the PR, removing existing tests or
85
+ changing public interfaces.
86
+
87
+ ### Stopping conditions
88
+
89
+ - **Success:** CI passes, the latest head has been reviewed, and no actionable
90
+ P0/P1/P2 findings remain. Report the result.
91
+ - **Cap reached:** After 7 automated fix rounds without a human corrective
92
+ commit, stop and post a message in the PR (per CLAUDE.md review rules).
93
+ - **Blocked:** A finding requires human judgment (design decision, scope change).
94
+ Post a summary comment and notify the user.
95
+
96
+ ## Phase 4: Summary
97
+
98
+ When the loop exits, report:
99
+ 1. Total commits pushed.
100
+ 2. Review rounds completed.
101
+ 3. Decisions made autonomously (with brief justification for each).
102
+ 4. Any remaining P3 suggestions that were deferred.
103
+ 5. Final PR status (approved / changes requested / commented).
104
+
105
+ ## Conventions
106
+
107
+ - Never force-push, rebase published commits, or skip hooks.
108
+ - Every fix is a new commit, not an amend.
109
+ - Batch related fixes into one commit when possible.
110
+ - Run all four gates before every push.
111
+ - Count fix rounds per CLAUDE.md: 7 rounds without a human corrective commit
112
+ triggers the cap.
113
+
114
+ ## What this skill does NOT do
115
+
116
+ - **Doesn't merge the PR.** That's always a human action.
117
+ - **Doesn't approve its own PR.** It can only push fixes and respond to review.
118
+ - **Doesn't modify CI/CD, branch protection, or permissions.**
119
+ - **Doesn't override human reviewer decisions.** If a human says "don't fix
120
+ this", it stays unfixed.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: stacktrace-cli
3
- Version: 0.5.3
3
+ Version: 0.6.1
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>
@@ -21,7 +21,7 @@ Requires-Python: >=3.11
21
21
  Requires-Dist: click>=8.1
22
22
  Requires-Dist: httpx<1.0.dev0,>=0.28.1
23
23
  Requires-Dist: openaca==0.7.0
24
- Requires-Dist: openaidr==0.3.0
24
+ Requires-Dist: openaidr==0.3.2
25
25
  Requires-Dist: posthog<8,>=7.0
26
26
  Description-Content-Type: text/markdown
27
27
 
@@ -51,7 +51,7 @@ Requires Python 3.11 or newer.
51
51
 
52
52
  ```console
53
53
  $ stacktrace --version
54
- stacktrace 0.5.3 (openaca 0.7.0)
54
+ stacktrace 0.6.1 (openaca 0.7.0)
55
55
  ```
56
56
 
57
57
  ## Quick start
@@ -24,7 +24,7 @@ Requires Python 3.11 or newer.
24
24
 
25
25
  ```console
26
26
  $ stacktrace --version
27
- stacktrace 0.5.3 (openaca 0.7.0)
27
+ stacktrace 0.6.1 (openaca 0.7.0)
28
28
  ```
29
29
 
30
30
  ## Quick start
@@ -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, 0064
8
+ amended-by: 0011, 0027, 0031, 0039, 0064, 0084
9
9
  ---
10
10
 
11
11
  ## Context
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  id: 0029
3
3
  title: Stage three returns to opt-in
4
- status: accepted
4
+ status: superseded
5
5
  date: 2026-09-09
6
6
  supersedes: null
7
- superseded-by: null
7
+ superseded-by: 0076
8
8
  amends: 0004
9
9
  amended-by: null
10
10
  ---
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  id: 0033
3
3
  title: A third-party analyzer is reached only by a flag that names it, and its output is numbers
4
- status: accepted
4
+ status: superseded
5
5
  date: 2026-09-17
6
6
  supersedes: null
7
- superseded-by: null
7
+ superseded-by: 0076
8
8
  amends: 0004, 0011
9
9
  amended-by: 0034, 0035
10
10
  ---
@@ -6,7 +6,7 @@ date: 2026-09-19
6
6
  supersedes: null
7
7
  superseded-by: null
8
8
  amends: 0033
9
- amended-by: null
9
+ amended-by: 0076
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: 0006, 0031
9
- amended-by: 0043, 0044
9
+ amended-by: 0043, 0044, 0083
10
10
  ---
11
11
 
12
12
  ## Context
@@ -6,7 +6,7 @@ date: 2026-09-23
6
6
  supersedes: null
7
7
  superseded-by: null
8
8
  amends: null
9
- amended-by: null
9
+ amended-by: 0077
10
10
  ---
11
11
 
12
12
  ## Context
@@ -6,7 +6,7 @@ date: 2026-09-24
6
6
  supersedes: null
7
7
  superseded-by: null
8
8
  amends: null
9
- amended-by: null
9
+ amended-by: 0078
10
10
  ---
11
11
 
12
12
  ## Context
@@ -6,7 +6,7 @@ date: 2026-09-24
6
6
  supersedes: null
7
7
  superseded-by: null
8
8
  amends: 0036, 0046
9
- amended-by: 0068, 0071, 0073
9
+ amended-by: 0068, 0071, 0073, 0085
10
10
  ---
11
11
 
12
12
  ## Context
@@ -6,7 +6,7 @@ date: 2026-09-25
6
6
  supersedes: null
7
7
  superseded-by: null
8
8
  amends: 0067
9
- amended-by: 0069
9
+ amended-by: 0069, 0085
10
10
  ---
11
11
 
12
12
  ## Context
@@ -6,7 +6,7 @@ date: 2026-09-26
6
6
  supersedes: null
7
7
  superseded-by: null
8
8
  amends: 0067, 0071
9
- amended-by: null
9
+ amended-by: 0084, 0085
10
10
  ---
11
11
 
12
12
  ## Context
@@ -0,0 +1,164 @@
1
+ ---
2
+ id: 0076
3
+ title: Collapse detector to two phases with conditional reasoning
4
+ status: accepted
5
+ date: 2026-09-27
6
+ supersedes: 0029, 0033
7
+ superseded-by: null
8
+ amends: 0035
9
+ amended-by: 0081
10
+ ---
11
+
12
+ ## Context
13
+
14
+ The detector has three stages: deterministic (2 rules), priors (0 rules — all
15
+ five withdrawn), and reasoning (cache-only — daemon hardcodes
16
+ `reasoning=False`). Stage 2 contains no detection logic, only routing and two
17
+ data-quality metrics. The daemon never activates new reasoning. Adding new
18
+ rules (10 total) requires cleaning up this structure.
19
+
20
+ Three problems force the change now:
21
+
22
+ 1. **Stage 2 is empty.** `run_priors` has no rules. What remains is
23
+ `low_coverage` (a data-quality metric), `_missing_mcp_transport` (same),
24
+ and routing logic that builds `ReasoningRequest`s. None of that is
25
+ detection.
26
+
27
+ 2. **The daemon never reasons.** `analyse_batch()` hardcodes
28
+ `reasoning=False`. New reasoning rules (`stacktrace-infra-unsafe-action`,
29
+ `stacktrace-unrequested-publication`) will never fire in the background.
30
+
31
+ 3. **Reasoning blocks the tick.** A 1–3s Jev call per session, run
32
+ synchronously in a 5s tick, blocks the daemon.
33
+
34
+ ## Decision
35
+
36
+ Collapse to two phases and one orchestrator. Phase 1 is deterministic (no
37
+ model, no vendor boundary — predominantly μs, with the exception of
38
+ `stacktrace-vulnerable-dep-invoked` which queries cached advisory data and degrades
39
+ to async enrichment on a cold cache). Phase 2 is reasoning (seconds,
40
+ requires `TYPESAFE_API_KEY`). The orchestrator sits between them and
41
+ builds the reasoning queue from gate trips, solo routes, and the
42
+ EVERY_SESSION route.
43
+
44
+ ### Phase collapse
45
+
46
+ Stage 2's remnants move:
47
+
48
+ | Function | Destination |
49
+ |---|---|
50
+ | `low_coverage` | Session-level metric (not a rule) |
51
+ | `_missing_mcp_transport` | Per-rule coverage metadata on the analysis result |
52
+ | Routing (`_solo_routes`, `ReasoningRequest`) | Orchestrator inside `run_detector` |
53
+
54
+ ### Conditional reasoning activation
55
+
56
+ `TYPESAFE_API_KEY` in the environment is the only switch. No `--reasoning`
57
+ flag, no `--analyzer jev`. If the key is absent, `reasoning=False` — no
58
+ model, no cost, no vendor boundary. If present, Phase 2 runs only when
59
+ Phase 1 produces a non-empty reasoning queue.
60
+
61
+ Budget: `DEFAULT_DAEMON_BUDGET = 50`. It caps *sessions* dispatched to
62
+ Phase 2 per tick, not Jev calls — `stacktrace-deceptive-completion` alone
63
+ issues one gate call plus one call per verification call with a covering
64
+ claim, so a session's actual cost scales with its checks. At $0.0006/session
65
+ (one gate call) and typical <20 sessions/tick, normal operation is nowhere
66
+ near the cap; $0.03/tick is that single-call estimate at the budget ceiling,
67
+ not a hard spend cap — a tick of sessions with many verification calls can
68
+ exceed it.
69
+
70
+ ### Async Phase 2
71
+
72
+ Phase 1 runs synchronously in the tick. Phase 2 dispatches to
73
+ `ThreadPoolExecutor(_WORKERS=4)` and returns immediately. The next tick
74
+ does not wait. When a verdict arrives, findings are recorded and
75
+ `on_commit` wakes the fleet/webhook jobs.
76
+
77
+ ```
78
+ tick()
79
+ ├─ Synchronous (μs–ms)
80
+ │ ├─ enumerate + collect + correlate
81
+ │ ├─ Phase 1: deterministic → findings recorded immediately
82
+ │ ├─ orchestrator → reasoning queue
83
+ │ └─ record(findings, unknowns, pending=queue)
84
+ │
85
+ ├─ Asynchronous (ThreadPoolExecutor, background)
86
+ │ └─ for each ReasoningRequest:
87
+ │ build windows → Jev call → evaluate → store.record_reasoning()
88
+ │
89
+ └─ Scheduler.tick()
90
+ ```
91
+
92
+ ### Rule placement
93
+
94
+ | Rule | Phase | Rationale |
95
+ |---|---|---|
96
+ | `stacktrace-credential-egress` | 1 | Regex on call arguments |
97
+ | `stacktrace-agent-blocked` | 1 | Refused-call channel match |
98
+ | `stacktrace-destructive-action` | 1 | Path resolution on delete commands |
99
+ | `stacktrace-credential-persisted` | 1 | Secret match on a Write/Edit call's own content |
100
+ | `stacktrace-mcp-transient-failure` | 1 | MCP call sequence pattern |
101
+ | `stacktrace-tool-retry-storm` | 1 | Counter with reset |
102
+ | `stacktrace-vulnerable-dep-invoked` | 1 | Package list × osv.dev |
103
+ | `stacktrace-deceptive-completion` | 2 | Gate (Jev) + per-call nouls. EVERY_SESSION route. |
104
+ | `stacktrace-infra-unsafe-action` | 1 gate → 2 | Gate: mutating command. Model: beyond_request + shared_target. |
105
+ | `stacktrace-unrequested-publication` | 1 gate → 2 | Gate: publication verb. Model: beyond_request. |
106
+
107
+ ## Alternatives considered
108
+
109
+ - **Keep three stages, add rules to stage 2.** Rejected because stage 2 has
110
+ no rules and its abstraction boundary is accidental — it exists because the
111
+ function signature existed, not because the domain has three kinds of work.
112
+
113
+ - **Separate `--reasoning` and `--analyzer` flags.** Rejected because the
114
+ consent decision is setting `TYPESAFE_API_KEY` — the operator named a
115
+ vendor by placing its key. Two flags that must be typed together, where one
116
+ is meaningless without the other, is ceremony that protects nothing.
117
+
118
+ - **Synchronous reasoning in the tick with a short budget.** Rejected because
119
+ even a budget of 3 blocks the tick for 3–9 seconds. The daemon goes deaf,
120
+ and the tick interval (5s) is shorter than a single Jev call.
121
+
122
+ ## Consequences
123
+
124
+ - 7 of 10 rules fire on every tick with no model dependency.
125
+ - Reasoning activates automatically when a key is present and a gate trips.
126
+ - The tick never blocks on inference.
127
+ - `ThreadPoolExecutor(4)` caps Jev API pressure at 4 concurrent calls.
128
+
129
+ **Costs:**
130
+
131
+ - Async Phase 2 requires a daemon-owned executor with these invariants:
132
+ (1) bounded in-flight work — cap queued futures, not just the budget;
133
+ (2) in-flight deduplication — a session already pending reasoning is not
134
+ re-queued; (3) revision-aware commits — a reasoning result is committed
135
+ only if its session fingerprint matches the current analysis, so a stale
136
+ result from a slow Jev call never overwrites a newer deterministic pass;
137
+ (4) retry on transient Jev failure, with backoff, capped at 3 attempts;
138
+ (5) on daemon restart, pending reasoning is re-derived from Phase 1 on
139
+ the next tick rather than persisted across restarts;
140
+ (6) on daemon stop or upgrade, cancel queued futures and observe
141
+ cancellation between per-window requests in active workers — the executor
142
+ must not outlive the daemon's stop deadline (600s), so shutdown joins only
143
+ the work already in flight, never the full queue.
144
+ - New rules send data the original consent did not envision (shell commands,
145
+ user requests). Documentation should describe what each rule sends. This
146
+ ADR amends the consent model established in ADR-0033; key-as-consent is a
147
+ product decision that should be documented as such. Cached verdicts from
148
+ prior runs remain readable when the key is present, independent of whether
149
+ new reasoning is commissioned — this preserves the `--analyzer jev`
150
+ read-only behavior. Cached Jev verdicts remain readable without a key:
151
+ the cache is keyed by analyzer identity and prompt version, not by the
152
+ presence of a runtime credential, so removing or rotating
153
+ `TYPESAFE_API_KEY` stops commissioning new work but does not discard
154
+ already-paid findings.
155
+
156
+ ## When to revisit
157
+
158
+ - If a second reasoning provider is added (Jev is the only one today), the
159
+ key-as-consent model needs per-provider consent.
160
+ - If Phase 2 latency exceeds 10s per session, the ThreadPoolExecutor design
161
+ may need a persistent queue with retry.
162
+ - If cross-rule incident grouping becomes urgent (credentials written,
163
+ committed, and pushed = three findings for one mistake), the orchestrator
164
+ needs a post-Phase-2 deduplication pass.
@@ -0,0 +1,208 @@
1
+ ---
2
+ id: 0077
3
+ title: The BOM job wakes once at startup, staggered
4
+ status: accepted
5
+ date: 2026-09-30
6
+ supersedes: null
7
+ superseded-by: null
8
+ amends: 0060
9
+ amended-by: null
10
+ ---
11
+
12
+ ## Context
13
+
14
+ ADR-0060 clause 2 justifies `stacktrace daemon sync-bom` this way: "the drain
15
+ at least has a wake path, and the BOM job has none." That was true when
16
+ ADR-0060 was accepted. This PR gives the BOM job the same one-time startup
17
+ wake `fleet`/`webhook` already had — otherwise a freshly started daemon sits on
18
+ a machine's inventory for up to `--bom-interval` (6h default) plus a
19
+ hostname-derived stagger before its first upload, unlike the other two jobs.
20
+ ADR-0060's sentence is now the one thing in this repository's design contract
21
+ that says the opposite of what the code does.
22
+
23
+ The first attempt at the wake reset `Job._due_at` to `now + interval`
24
+ unconditionally on every run. Codex review caught that this loses the
25
+ hostname stagger `stagger()` computes not just on the first run but forever
26
+ after: every host provisioned from one script converges onto the same
27
+ recurring upload instant, which is the exact fleet-wide burst `stagger()`
28
+ exists to prevent (`docs/specs/daemon-uploads.md`'s "Stagger machines" row).
29
+
30
+ The second attempt preserved the stagger only when `_due_at > now` at the
31
+ moment `run_if_due` executed — reasoning that a run "arrived early" only if
32
+ its deadline hadn't passed yet. A further Codex pass caught that this breaks
33
+ too: `DaemonService.tick()` runs `scanner.run_once()` before `jobs.tick()`, so
34
+ a slow initial scan can push `now` past a low-offset host's `_due_at` before
35
+ the startup-woken `bom.run_if_due()` ever executes, even though that run is
36
+ still the job's first. Comparing `_due_at` to `now` cannot distinguish "ran
37
+ early" from "ran late" — it can only be answered by whether this is the job's
38
+ first run at all.
39
+
40
+ A third Codex pass caught that a single `+ interval` step still isn't enough:
41
+ with a short `--bom-interval`, a delay of more than one interval left the
42
+ preserved deadline in the past, so the job went due again immediately and
43
+ that second run — no longer the first — re-anchored on `now` via the normal
44
+ path anyway. A fourth caught that even advancing by whole intervals wasn't
45
+ enough on its own, because the deadline was computed from the clock read
46
+ *before* `self.run()` — the BOM upload itself can eat into whatever margin
47
+ that pre-run reading assumed was safe, on the same short-interval hosts the
48
+ third pass was about. A fifth caught the last boundary case: rounding the
49
+ delay up with `ceil` leaves the deadline exactly equal to the post-run clock
50
+ when the delay lands on an exact interval multiple, which `_is_due` still
51
+ reads as due.
52
+
53
+ A sixth and seventh Codex pass moved the goalposts again, but this time both
54
+ findings were instances of the same thing: `--bom-interval` (and every other
55
+ daemon interval — `parse_duration`, `config.py`) validated only a maximum,
56
+ never a minimum. A sixth pass constructed an interval small enough (the
57
+ smallest positive `float` at all) that the phase arithmetic's division
58
+ overflowed to infinity, raising `OverflowError` out of `run_if_due` and
59
+ crashing the scheduler loop. Patched locally at first — a `try`/`except`
60
+ around the division, falling back to the plain `after + interval` deadline —
61
+ a seventh pass immediately found the sibling case one `try`/`except` does not
62
+ cover: an interval too small relative to a long-uptime host's `monotonic()`
63
+ reading to survive floating-point addition at all (`now + interval == now`),
64
+ which does not raise, so the `except` never fires, and the job re-runs on
65
+ every tick forever. Two Codex passes finding two failure modes of the same
66
+ missing input check, back to back, is the shape CLAUDE.md names directly:
67
+ "a second finding of the same class means the first fix was at the wrong
68
+ level." The fix moved to the boundary instead of the arithmetic:
69
+ `parse_duration` now refuses any positive duration under one second (closing
70
+ both repro cases directly, `5e-324s` and `1e-20s` alike), for every daemon
71
+ interval it parses, not only the BOM job's. `Job`'s phase arithmetic no
72
+ longer defends against a value that should never have reached it, and the
73
+ `try`/`except` this replaced is removed along with the test that pinned it —
74
+ CLAUDE.md's own bar for removing a test that protected a behavior which was
75
+ "intentionally changed or removed": the behavior here (surviving a
76
+ sub-one-second interval inside `Job`) is removed, not merely relocated, since
77
+ no caller can construct one through the validated path anymore.
78
+
79
+ ## Decision
80
+
81
+ The BOM job is woken once, unconditionally, right after it's constructed —
82
+ the same as `fleet`/`webhook`. `Job` gains a `preserve_stagger` flag, set only
83
+ for the BOM job: on that job's first-ever run (`_ran_at is None`, checked
84
+ before this run sets it), the pre-run `_due_at` assignment (`now + interval`)
85
+ is skipped, and the next due time is instead computed once `run()` returns
86
+ (success or failure alike, from a `finally` block) as the original staggered
87
+ slot advanced by as many whole intervals as the delay — measured against the
88
+ clock *at that point*, not before the run — needs to land at or after it:
89
+ `_due_at + interval * max(1, floor((after - _due_at) / interval) + 1)` —
90
+ `floor(x) + 1`, not `ceil(x)`, since `ceil` leaves the result exactly equal to
91
+ `after` when the delay lands on an exact interval multiple, and `floor(x) + 1`
92
+ is strictly greater than `x` for every real `x` with no boundary case left
93
+ over. That division assumes `interval` is representable and well away from
94
+ zero; `parse_duration` (`config.py`) now enforces a one-second floor on every
95
+ positive daemon interval it parses (`0` still means disabled), which is the
96
+ only thing standing between this arithmetic and the two failure modes below,
97
+ placed at the boundary where the value entered the system rather than as a
98
+ defensive check inside `Job` itself. Every run
99
+ after the first behaves exactly as before: `_ran_at` is no longer `None`, so
100
+ `run_if_due` falls back to `now + interval`, computed before the run same as
101
+ any job without `preserve_stagger` set. `fleet`/`webhook` do not set the
102
+ flag: they are woken repeatedly by real events (a scan commit, a flush) for
103
+ the daemon's whole life, where re-anchoring on `now` at each of those wakes is
104
+ the correct behavior, not just tolerable on the first one.
105
+
106
+ This amends ADR-0060, not supersedes it: the two-posture decision, `sync-bom`
107
+ existing in `flush`'s shape, blocking one pass, and serializing with the
108
+ scheduled job are all unchanged. Only the one clause explaining *why*
109
+ `sync-bom` is needed — "the BOM job has none [wake path]" — no longer holds,
110
+ and only for the reason given there: `sync-bom` is still the only way to run
111
+ `sync_endpoint` on demand once the daemon is past its own single startup kick.
112
+
113
+ ## Alternatives considered
114
+
115
+ - **Leave ADR-0060 accepted as written, fix only the spec.** Rejected: a
116
+ future reader of ADR-0060 alone would still be told the BOM job has no wake
117
+ at all, with no pointer to what changed or why — the durable-decision
118
+ problem CLAUDE.md's ADR discipline exists to prevent.
119
+ - **Fully supersede ADR-0060.** Rejected: every other clause in it — the two
120
+ postures, `flush`/`sync-bom`'s shape, one blocking pass, serialization with
121
+ the scheduled job — is unaffected by this change. Retiring the whole
122
+ decision over one now-stale sentence would erase context that still binds,
123
+ against the amend/supersede test itself ("does a numbered decision in the
124
+ old ADR still bind — if one does, it's an amendment").
125
+ - **Reset `_due_at` to `now + interval` unconditionally.** Rejected (P1,
126
+ first Codex pass): defeats the fleet-wide stagger on every run after the
127
+ first, not only the first, which was the whole point of `stagger()`.
128
+ - **Preserve the stagger when `_due_at > now` at execution time.** Rejected
129
+ (P2, second Codex pass): a scan slower than a low-offset host's remaining
130
+ stagger makes `_due_at` already `<= now` by the time this job's
131
+ `run_if_due` runs, so exactly the hosts stagger matters most for would fall
132
+ through to the unstaggered path anyway.
133
+ - **Advance the preserved deadline by exactly one interval.** Rejected (P2,
134
+ third Codex pass): with a short `--bom-interval`, a delay of more than one
135
+ interval left the deadline in the past, so the job ran again immediately —
136
+ no longer the first run — and re-anchored on `now` via the normal path,
137
+ losing the stagger the fix was meant to keep.
138
+ - **Compute the advanced deadline from the clock read before `run()`.**
139
+ Rejected (P2, fourth Codex pass): the run itself (an actual upload) can
140
+ outrun the margin that pre-run reading assumed was safe, on the same
141
+ short-interval hosts the third pass was about. Measuring from the clock
142
+ after `run()` returns keeps the deadline honest about time the run itself
143
+ spent.
144
+ - **Round the delay up to whole intervals with `ceil`.** Rejected (P2, fifth
145
+ Codex pass): `ceil` leaves the deadline exactly equal to the post-run clock
146
+ when the delay lands on an exact interval multiple, which `_is_due` still
147
+ reads as due, running the job again immediately on the normal (no longer
148
+ preserved) path. `floor(x) + 1` is strictly greater than `x` always, with
149
+ no boundary left to land on.
150
+ - **Give the BOM job a real, repeating wake** (e.g. on scan commit, like
151
+ `fleet`). Rejected as out of scope: nothing in the queue is BOM input (a
152
+ BOM upload is a full inventory snapshot, not a drain of anything a scan
153
+ writes), so there is no event that should retrigger it before its interval
154
+ — only the one-time startup case this ADR covers.
155
+ - **Catch the division's `OverflowError` inside `Job` and fall back to
156
+ `after + interval` on that run.** Tried first (P2, sixth Codex pass; this
157
+ is what commit `37b1da1` shipped), then rejected once a seventh pass found
158
+ the case it does not cover: an interval too small to move a large
159
+ `monotonic()` reading by floating-point addition at all does not raise —
160
+ it silently drops the deadline back to `now`, so nothing catches it. A
161
+ `try`/`except` guards one arithmetic failure mode of an unvalidated input,
162
+ not the input; a sibling failure mode with no exception to catch was only
163
+ ever one interval choice away. Rejected in favor of refusing the value at
164
+ `parse_duration`, the boundary every daemon interval already passes
165
+ through, which closes both failure modes and any other the same missing
166
+ floor would eventually surface.
167
+ - **Guard the tiny-interval case inside every `Job` caller in `runtime.py`
168
+ instead of in `parse_duration`.** Rejected: `runtime.py` has four call
169
+ sites building a `Job` from a parsed setting; `parse_duration` is the one
170
+ place all four already funnel through, and it is also where `daemon
171
+ status`'s "duration is too small" message reaches an operator's terminal
172
+ at `daemon run` time (`test_a_mistyped_interval_fails_at_resolution_not_at_
173
+ the_first_tick`), rather than a forked process's log file after the fact.
174
+
175
+ ## Consequences
176
+
177
+ A freshly started daemon uploads its BOM promptly instead of waiting up to
178
+ `--bom-interval` plus a stagger, on every daemon, without collapsing a
179
+ fleet's recurring upload cadence onto one instant. `sync-bom` remains
180
+ necessary for an on-demand run after startup — the BOM job still has no
181
+ recurring wake of its own. `preserve_stagger`'s "first run ever" key assumes
182
+ the flag is only ever set on a job that is woken exactly once, at
183
+ construction; a job that combines `preserve_stagger` with a real recurring
184
+ wake would need the mechanism revisited. Every daemon interval — not only
185
+ `--bom-interval` — now has a one-second floor: `--scan-interval`,
186
+ `--fleet-interval`, `--webhook-interval`, and `--scan-since` all parse
187
+ through the same `parse_duration`, so a value that was always nonsensical
188
+ (a webhook retry cadence measured in nanoseconds) is now refused at
189
+ resolution for those too, not only for the job whose arithmetic happened to
190
+ expose it.
191
+
192
+ ## When to revisit
193
+
194
+ - If the BOM job ever gains an event-driven wake beyond the one-time startup
195
+ kick (mirroring `fleet`'s scan-commit wake), `preserve_stagger`'s
196
+ first-run-only key stops being sufficient, and ADR-0060 clause 2 needs
197
+ another look alongside it.
198
+ - If `DaemonService.tick()`'s ordering changes so scanning no longer runs
199
+ ahead of the jobs it wakes, the delayed-first-tick case this ADR's second
200
+ alternative was rejected for may no longer be reachable — worth confirming
201
+ before simplifying `preserve_stagger` back to a deadline comparison.
202
+
203
+ ## References
204
+
205
+ - ADR-0060 — two upload postures, not one command with two sources
206
+ - `docs/specs/daemon-uploads.md` — the BOM job, `sync-bom`, and the stagger
207
+ - `src/stacktrace_cli/daemon/jobs.py` — `Job.preserve_stagger`, `stagger()`
208
+ - `src/stacktrace_cli/daemon/config.py` — `parse_duration`'s one-second floor