stacktrace-cli 0.1.0__tar.gz → 0.2.0__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 (211) hide show
  1. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/PKG-INFO +1 -1
  2. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0006-trust-boundary-and-detection-upload.md +1 -1
  3. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0008-console-in-separate-demo-package.md +2 -2
  4. stacktrace_cli-0.2.0/docs/adrs/0026-the-console-becomes-a-product-surface.md +108 -0
  5. stacktrace_cli-0.2.0/docs/adrs/0027-a-finding-may-name-what-it-could-not-place.md +112 -0
  6. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/INDEX.md +3 -1
  7. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/plans/007-detection-upload.md +11 -6
  8. stacktrace_cli-0.2.0/docs/plans/008-monitor.md +630 -0
  9. stacktrace_cli-0.2.0/docs/releases/v0.2.0.md +121 -0
  10. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/specs/detection-upload.md +23 -1
  11. stacktrace_cli-0.2.0/docs/specs/monitor.md +459 -0
  12. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/pyproject.toml +1 -1
  13. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/__init__.py +1 -1
  14. stacktrace_cli-0.2.0/src/stacktrace_cli/analysis.py +375 -0
  15. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/cli.py +174 -18
  16. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/correlate/acquire.py +5 -9
  17. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/correlate/composition.py +59 -0
  18. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/correlate/join.py +48 -2
  19. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/correlate/orchestrate.py +37 -3
  20. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/correlate/record.py +8 -0
  21. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/detector/deterministic.py +95 -47
  22. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/detector/finding.py +111 -2
  23. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/detector/render.py +62 -2
  24. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/detector/run.py +385 -17
  25. stacktrace_cli-0.2.0/src/stacktrace_cli/monitor/__init__.py +12 -0
  26. stacktrace_cli-0.2.0/src/stacktrace_cli/monitor/escalate.py +172 -0
  27. stacktrace_cli-0.2.0/src/stacktrace_cli/monitor/render.py +252 -0
  28. stacktrace_cli-0.2.0/src/stacktrace_cli/monitor/server.py +477 -0
  29. stacktrace_cli-0.2.0/src/stacktrace_cli/monitor/site/app.js +1045 -0
  30. stacktrace_cli-0.2.0/src/stacktrace_cli/monitor/site/fonts/OFL.txt +210 -0
  31. stacktrace_cli-0.2.0/src/stacktrace_cli/monitor/site/fonts/dm-mono-400-latin.woff2 +0 -0
  32. stacktrace_cli-0.2.0/src/stacktrace_cli/monitor/site/fonts/dm-mono-500-latin.woff2 +0 -0
  33. stacktrace_cli-0.2.0/src/stacktrace_cli/monitor/site/fonts/dm-sans-latin.woff2 +0 -0
  34. stacktrace_cli-0.2.0/src/stacktrace_cli/monitor/site/index.html +104 -0
  35. stacktrace_cli-0.2.0/src/stacktrace_cli/monitor/site/styles.css +628 -0
  36. stacktrace_cli-0.2.0/src/stacktrace_cli/monitor/state.py +102 -0
  37. stacktrace_cli-0.2.0/src/stacktrace_cli/monitor/verdicts.py +52 -0
  38. stacktrace_cli-0.2.0/src/stacktrace_cli/monitor/watch.py +349 -0
  39. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/remote/detect_payload.py +99 -27
  40. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/remote/redact.py +13 -2
  41. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/remote/sync_detect.py +79 -42
  42. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/remote/upload_contract.py +21 -0
  43. stacktrace_cli-0.2.0/tests/monitor/feed_harness.mjs +803 -0
  44. stacktrace_cli-0.2.0/tests/monitor/test_escalation_gates.py +398 -0
  45. stacktrace_cli-0.2.0/tests/monitor/test_feed_model.py +484 -0
  46. stacktrace_cli-0.2.0/tests/monitor/test_render.py +344 -0
  47. stacktrace_cli-0.2.0/tests/monitor/test_server.py +413 -0
  48. stacktrace_cli-0.2.0/tests/monitor/test_site_assets.py +204 -0
  49. stacktrace_cli-0.2.0/tests/monitor/test_state.py +111 -0
  50. stacktrace_cli-0.2.0/tests/monitor/test_verdicts.py +137 -0
  51. stacktrace_cli-0.2.0/tests/monitor/test_watch.py +607 -0
  52. stacktrace_cli-0.2.0/tests/remote/__init__.py +0 -0
  53. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_detect_contract_is_exhaustive.py +79 -2
  54. stacktrace_cli-0.2.0/tests/remote/test_detect_payload_reaches_the_cloud_model.py +286 -0
  55. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_detect_redaction.py +49 -0
  56. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_sync_detect.py +2 -2
  57. stacktrace_cli-0.2.0/tests/test_agent_instance_id.py +207 -0
  58. stacktrace_cli-0.2.0/tests/test_analysis.py +362 -0
  59. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_cli.py +38 -0
  60. stacktrace_cli-0.2.0/tests/test_detection_components.py +194 -0
  61. stacktrace_cli-0.2.0/tests/test_detection_subject.py +671 -0
  62. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_detector_render.py +93 -1
  63. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_detector_run.py +43 -0
  64. stacktrace_cli-0.2.0/tests/test_monitor_cli.py +85 -0
  65. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_untrusted_content_never_travels.py +216 -6
  66. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/uv.lock +1 -1
  67. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/.agents/skills/release-stacktrace/SKILL.md +0 -0
  68. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/.claude/settings.json +0 -0
  69. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/.claude/skills/release-stacktrace/SKILL.md +0 -0
  70. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/.codex/hooks.json +0 -0
  71. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/.github/workflows/autofix.yml +0 -0
  72. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/.github/workflows/ci.yml +0 -0
  73. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/.github/workflows/claude.yml +0 -0
  74. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/.github/workflows/publish-pypi.yml +0 -0
  75. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/.gitignore +0 -0
  76. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/AGENTS.md +0 -0
  77. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/CLAUDE.md +0 -0
  78. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/README.md +0 -0
  79. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0001-session-telemetry-as-input.md +0 -0
  80. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0002-session-collection-in-openaidr.md +0 -0
  81. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0003-runtime-edges.md +0 -0
  82. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0004-three-stage-detector.md +0 -0
  83. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0005-detection-family-and-report-assembly.md +0 -0
  84. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0007-proprietary-package-on-open-dependencies.md +0 -0
  85. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0010-detection-severity-and-confidence-ladders.md +0 -0
  86. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0011-verdict-cache.md +0 -0
  87. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0012-observation-evidence-kinds-and-transport.md +0 -0
  88. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0013-rule-catalogue-triage-and-per-rule-context.md +0 -0
  89. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0014-declared-project-mapping.md +0 -0
  90. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0015-narrow-the-security-catalogue.md +0 -0
  91. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0016-stall-grouping-is-session-wide.md +0 -0
  92. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0017-a-declined-repeat-is-a-stall.md +0 -0
  93. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0018-remote-sync-config-and-facade-consumption.md +0 -0
  94. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0019-delegated-commands-are-openaca-objects.md +0 -0
  95. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0020-openaca-consumption-boundary.md +0 -0
  96. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0021-two-command-kinds.md +0 -0
  97. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0022-detection-scope-is-a-catalogue-column.md +0 -0
  98. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0023-sync-detect-collects-and-does-not-escalate.md +0 -0
  99. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0024-the-upload-carries-observations.md +0 -0
  100. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/0025-a-denied-call-is-activity-never-an-invocation.md +0 -0
  101. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/HOOK-PROMPT.md +0 -0
  102. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/adrs/TEMPLATE.md +0 -0
  103. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/plans/002-session-input.md +0 -0
  104. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/plans/003-correlation.md +0 -0
  105. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/plans/004-detector.md +0 -0
  106. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/plans/005-remote-sync.md +0 -0
  107. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/plans/006-cli-composition.md +0 -0
  108. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/releases/v0.0.1.md +0 -0
  109. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/releases/v0.1.0.md +0 -0
  110. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/specs/aidr.md +0 -0
  111. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/specs/cli-composition.md +0 -0
  112. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/specs/correlation.md +0 -0
  113. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/specs/detector.md +0 -0
  114. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/specs/remote-sync.md +0 -0
  115. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/docs/specs/session-input.md +0 -0
  116. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/scripts/git-hooks/pre-push +0 -0
  117. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/scripts/install-hooks.sh +0 -0
  118. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/__main__.py +0 -0
  119. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/correlate/__init__.py +0 -0
  120. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/correlate/observed.py +0 -0
  121. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/correlate/project_map.py +0 -0
  122. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/correlate/render.py +0 -0
  123. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/detector/__init__.py +0 -0
  124. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/detector/analyzer.py +0 -0
  125. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/detector/cache.py +0 -0
  126. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/detector/markers.py +0 -0
  127. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/detector/priors.py +0 -0
  128. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/detector/prompts/__init__.py +0 -0
  129. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/detector/prompts/v1/exclusions.md +0 -0
  130. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/detector/prompts/v1/framing.md +0 -0
  131. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/detector/prompts/v1/stacktrace-deceptive-completion.md +0 -0
  132. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/detector/prompts/v1/stacktrace-injected-instruction-followed.md +0 -0
  133. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/detector/prompts/v1/stacktrace-intent-drift.md +0 -0
  134. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/detector/reasoning.py +0 -0
  135. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/detector/rules.py +0 -0
  136. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/detector/secrets.py +0 -0
  137. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/detector/verdict.py +0 -0
  138. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/remote/__init__.py +0 -0
  139. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/remote/cli.py +0 -0
  140. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/remote/client.py +0 -0
  141. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/remote/config.py +0 -0
  142. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/remote/payload.py +0 -0
  143. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/remote/policy.py +0 -0
  144. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/remote/spool.py +0 -0
  145. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/remote/sync.py +0 -0
  146. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/sessions/__init__.py +0 -0
  147. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/sessions/access.py +0 -0
  148. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/sessions/outcome.py +0 -0
  149. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/sessions/protocols.py +0 -0
  150. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/src/stacktrace_cli/sessions/render.py +0 -0
  151. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/__init__.py +0 -0
  152. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/agent_bom_fixture.py +0 -0
  153. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/detector_session_fixture.py +0 -0
  154. {stacktrace_cli-0.1.0/tests/remote → stacktrace_cli-0.2.0/tests/monitor}/__init__.py +0 -0
  155. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/helpers.py +0 -0
  156. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_cli.py +0 -0
  157. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_client.py +0 -0
  158. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_config.py +0 -0
  159. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_detect_activity.py +0 -0
  160. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_detect_cli.py +0 -0
  161. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_detect_client.py +0 -0
  162. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_detect_gate_matches_the_cloud.py +0 -0
  163. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_detect_layers_compose.py +0 -0
  164. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_detect_spool.py +0 -0
  165. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_detect_upload_contract.py +0 -0
  166. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_detect_wire_payload.py +0 -0
  167. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_facade_contract.py +0 -0
  168. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_payload.py +0 -0
  169. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_policy.py +0 -0
  170. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_redact_payload.py +0 -0
  171. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_seam.py +0 -0
  172. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_spool.py +0 -0
  173. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_sync.py +0 -0
  174. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/remote/test_upload_contract.py +0 -0
  175. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_acquire.py +0 -0
  176. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_bom_shape.py +0 -0
  177. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_call_status_conventions.py +0 -0
  178. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_composition.py +0 -0
  179. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_correlate_orchestrate.py +0 -0
  180. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_correlate_render.py +0 -0
  181. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_correlated_session.py +0 -0
  182. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_detector_analyzer_contract.py +0 -0
  183. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_detector_analyzer_live.py +0 -0
  184. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_detector_cache.py +0 -0
  185. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_detector_deterministic.py +0 -0
  186. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_detector_end_to_end.py +0 -0
  187. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_detector_finding.py +0 -0
  188. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_detector_liveness.py +0 -0
  189. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_detector_markers.py +0 -0
  190. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_detector_priors.py +0 -0
  191. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_detector_reasoning.py +0 -0
  192. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_detector_secrets.py +0 -0
  193. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_detector_verdict.py +0 -0
  194. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_fingerprint_covers_what_the_analyzer_reads.py +0 -0
  195. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_help_sections.py +0 -0
  196. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_join.py +0 -0
  197. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_observed.py +0 -0
  198. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_openaca_contract.py +0 -0
  199. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_openaidr_contract.py +0 -0
  200. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_option_arity_matches_the_binary.py +0 -0
  201. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_project_map.py +0 -0
  202. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_readme_examples_are_real_output.py +0 -0
  203. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_release_readiness.py +0 -0
  204. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_seam_boundary.py +0 -0
  205. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_sessions_access.py +0 -0
  206. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_sessions_end_to_end.py +0 -0
  207. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_sessions_protocol_typing.py +0 -0
  208. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_sessions_protocols.py +0 -0
  209. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_sessions_render.py +0 -0
  210. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_shell_separators_match_the_shell.py +0 -0
  211. {stacktrace_cli-0.1.0 → stacktrace_cli-0.2.0}/tests/test_verification_subcommands_match_the_binary.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: stacktrace-cli
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Placeholder CLI for Stacktrace.ai — installs the `stacktrace` command.
5
5
  Project-URL: Homepage, https://stacktrace.ai
6
6
  Author-email: Prasanth G <prasanth@openaca.dev>
@@ -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
8
+ amended-by: 0011, 0027
9
9
  ---
10
10
 
11
11
  ## Context
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  id: 0008
3
3
  title: Ship the local trace console as a separate demo package
4
- status: accepted
4
+ status: superseded
5
5
  date: 2026-08-27
6
6
  supersedes: null
7
- superseded-by: null
7
+ superseded-by: 0026
8
8
  ---
9
9
 
10
10
  ## Context
@@ -0,0 +1,108 @@
1
+ ---
2
+ id: 0026
3
+ title: The console becomes a product surface, served by the engine
4
+ status: accepted
5
+ date: 2026-09-06
6
+ supersedes: 0008
7
+ superseded-by: null
8
+ amends: null
9
+ amended-by: null
10
+ ---
11
+
12
+ ## Context
13
+
14
+ ADR-0008 kept the trace console out of the engine and gave three reasons. Two
15
+ were about *kind*: a served page with taste-driven layout is different software
16
+ from a library, and their release cadences and review standards differ. One was
17
+ about *order*: making the console the product surface "before the engine's
18
+ claims are validated inverts the order — the engine comes first, and nothing yet
19
+ measures its central claim."
20
+
21
+ That last reason names its own expiry, and it has arrived. 0.1.0 ships
22
+ `sessions`, `detect` over nine rules and three stages, and both upload paths;
23
+ the detector's claims are measured by its own corpus and by the hosted side
24
+ receiving them. The engine came first, and did.
25
+
26
+ What forces the decision now is a product one rather than a technical one: the
27
+ data is only useful if a person can follow it, and a sequence reads better as a
28
+ timeline than as a findings list — 0008's own opening sentence. A developer who
29
+ has to run `detect`, read JSON, and re-run it to see what changed is being asked
30
+ to be the renderer. `stacktrace monitor` is that renderer, and it has to ship
31
+ where the data is.
32
+
33
+ ## Decision
34
+
35
+ The engine serves a local console. `stacktrace monitor` starts an HTTP server
36
+ bound to loopback, opens a browser, and renders live sessions and detections
37
+ from in-process calls into the same surfaces `detect` uses.
38
+
39
+ The three clauses of 0008 that this reverses, stated plainly:
40
+
41
+ 1. **The engine gains a serving surface and a browser-facing asset.** `site/`
42
+ ships as package data in the wheel.
43
+ 2. **The console performs no analysis of its own.** *This one survives.* Monitor
44
+ calls `collect_sessions`, `acquire_correlated_view` and `run_detector`; it
45
+ adds no rule, no grade and no heuristic, and it never writes the verdict
46
+ cache. A finding on the page is a finding `detect` would print.
47
+ 3. **The tenets govern the console now**, because it is a product surface rather
48
+ than a demonstration. Trust, quality, cost and security apply — which is why
49
+ the page computes only the two free stages by default, and why the one action
50
+ it offers is gated four ways.
51
+
52
+ ## Alternatives considered
53
+
54
+ - **Keep 0008 and ship the console as a separate optional distribution**
55
+ (`stacktrace-console`, or an extra) — rejected. It preserves the letter of
56
+ 0008 at the cost of the thing 0008 was protecting: a second distribution has
57
+ its own version, its own install step and its own drift against three read
58
+ contracts, and every user who wants to *see* their data pays an install to do
59
+ it. The cadence argument that justified separation for a demo does not survive
60
+ the surface becoming the product.
61
+
62
+ - **Amend 0008 to carve out one read-only local surface** — rejected as
63
+ dishonest bookkeeping. The carve-out is the whole of clause 1; an amendment
64
+ that removes a decision's first clause is a supersession wearing a smaller
65
+ word.
66
+
67
+ - **Leave rendering to the hosted console** — rejected. The hosted side is
68
+ agent-scoped and receives only what ADR-0006 authorizes: no prompt, no
69
+ argument, no result, no matched value. The local console is precisely where a
70
+ responder may see the matched string, which ADR-0006 calls out as deliberate
71
+ asymmetry. A fleet view cannot replace a machine view; it is a different
72
+ question.
73
+
74
+ - **Ship the console but compute nothing** (render the last `detect` run's
75
+ cache) — rejected. "Live sessions, stale findings" is the failure mode a
76
+ reader cannot see: the page looks current, and a leak that happened after the
77
+ last run is absent from it.
78
+
79
+ ## Consequences
80
+
81
+ **What this enables.** A developer watches an alias resolve into a real
82
+ component while a session is still running — 0008's own description of the
83
+ demonstration, now available without a demo. The three published read contracts
84
+ gain their first in-repo consumer, so a change to span identity breaks a test
85
+ here rather than silently landing enrichments on the wrong rows elsewhere.
86
+
87
+ **What it costs, and 0008 was right about both.**
88
+
89
+ - **Every consumer of the wheel carries a UI they did not ask for.** `site/` is
90
+ three files and no JavaScript dependency, so the cost is bytes rather than a
91
+ dependency tree — but a library consumer still ships them.
92
+ - **Visual iteration now happens inside the engine's release process.** A
93
+ layout change rides a release built for a detection engine. The mitigation is
94
+ that the console's own priorities stay 0008's — latency, then fidelity — and
95
+ that no page change can alter a finding, because monitor computes none.
96
+
97
+ **What to watch for.** A rule that fires only in the console, or a grade the
98
+ page adjusts for legibility. Either would make clause 2 false, and clause 2 is
99
+ the one keeping this from being a fork of the detector.
100
+
101
+ ## When to revisit
102
+
103
+ Revisit if the console acquires a reason to compute something the detector does
104
+ not — that is the point at which it stops being a renderer and needs its own
105
+ decision. Revisit if the wheel's browser-facing assets grow beyond static files,
106
+ because a build step is the cadence problem 0008 warned about actually arriving.
107
+ Do not revisit on the grounds that a library consumer carries the UI: that cost
108
+ is accepted here explicitly, not overlooked.
@@ -0,0 +1,112 @@
1
+ ---
2
+ id: 0027
3
+ title: A finding may name the component it could not place
4
+ status: accepted
5
+ date: 2026-09-07
6
+ supersedes: null
7
+ superseded-by: null
8
+ amends: 0006
9
+ ---
10
+
11
+ ## Context
12
+
13
+ ADR-0006 constraint 3 keeps session content off the wire *by construction*: a
14
+ detection carries a rule id and non-content descriptors, "never an excerpt of
15
+ an argument, result or prompt", and the family is uploadable whole so no
16
+ projection exists to be got wrong. That constraint has held every finding this
17
+ product has shipped and is not in question here.
18
+
19
+ One case has now met the condition ADR-0006 wrote for its own revision:
20
+
21
+ > Revisit if a detection type proves genuinely unactionable without an evidence
22
+ > excerpt — that would be a real argument against constraint 3, and the answer
23
+ > is more likely a better non-content descriptor than an excerpt.
24
+
25
+ A finding's **subject** is what the finding is about. Where correlation places
26
+ the call, the subject is a BOM component and constraint 3 was never in play.
27
+ Where correlation cannot place it, the only name that exists is the one the
28
+ session used — `call.mcp_server` for a server, `arguments["skill"]` for a
29
+ skill — and the second of those is, read literally, an excerpt of an argument.
30
+
31
+ Removing it leaves the highest-severity finding this detector produces reading:
32
+
33
+ high stacktrace-credential-egress
34
+ Credential-shaped material reached an outbound call
35
+ on a mcp_server (not in inventory)
36
+
37
+ A responder cannot act on that. They cannot rotate the right credential, revoke
38
+ the right access, or find the thing, because the document does not say what the
39
+ thing was. The unplaced case is also the one where naming matters *most*: a
40
+ credential reaching a component no Agent BOM declares is the least expected and
41
+ least explicable event this product reports.
42
+
43
+ Empirically it is not rare. On the machine this was written against, 156 calls
44
+ across 15 distinct names were unplaced, including the one `high`
45
+ credential-egress finding in the window.
46
+
47
+ ## Decision
48
+
49
+ **A detection may carry the name the session addressed a component by, when
50
+ correlation could not place it.** This narrows ADR-0006 constraint 3; every
51
+ other clause of 0006 stays in force, and constraint 3 continues to forbid
52
+ excerpts of prompts, results and arguments in every other position.
53
+
54
+ Four bounds make it a descriptor rather than an opening.
55
+
56
+ 1. **One field, one purpose.** The name appears only as
57
+ `Participant.name` on a finding's subject or a named party to it. No rule
58
+ may interpolate it into prose; `Evidence.detail` remains closed to it, which
59
+ is the drift ADR-0006 names as *"reached by writing a helpful sentence"*.
60
+
61
+ 2. **It is never an identity.** `Participant.identity` stays null for an
62
+ unplaced party, so the name can resolve no lookup, join to no inventory and
63
+ render as no link. A consumer must mark it as unverified. Two machines can
64
+ call unrelated servers `support`; this ADR does not make that name mean
65
+ anything beyond the session that said it.
66
+
67
+ 3. **Bounded and validated at the boundary.** Length-capped on the wire, and
68
+ walked by the same disclosure gate every other leaf passes, so a name
69
+ carrying a path or a credential shape is refused rather than trimmed.
70
+
71
+ 4. **The local surfaces are unchanged and remain the fuller answer.** `monitor`
72
+ and `stacktrace detect` read the session directly and may always say more
73
+ than the upload does. This ADR is only about what leaves the machine.
74
+
75
+ ## Consequences
76
+
77
+ The `credential-egress` above reads `on support (not in inventory)`, in Fleet
78
+ as well as locally, and a responder has somewhere to start.
79
+
80
+ The residual exposure is a short, model-influenceable string reaching a hosted
81
+ service. It is real and it is accepted here rather than argued away: an
82
+ attacker who can choose the `skill` argument already drives the agent's tool
83
+ calls, and has channels with far more bandwidth than a capped name field in a
84
+ detection upload. What this ADR must not become is precedent — the argument
85
+ above is about *the subject of a finding being unnameable otherwise*, and does
86
+ not extend to any other field.
87
+
88
+ `tests/test_untrusted_content_never_travels.py` keeps every other canary,
89
+ including the ones for prompts, results, arguments and the tool name an MCP
90
+ server chose. It gains the case this ADR permits, so the exemption is stated
91
+ where the invariant is enforced rather than only here.
92
+
93
+ ## Alternatives rejected
94
+
95
+ - **Drop the name, keep the type** (`a skill (not in inventory)`). This was
96
+ implemented and is what prompted the ADR. It is compliant and unactionable,
97
+ which makes it the wrong trade for a product whose output is meant to be
98
+ acted on. A rule that produces findings nobody can use protects nothing.
99
+
100
+ - **Carry the server alias, drop the skill argument.** Defensible on the letter
101
+ of constraint 3 — a server alias is not an argument excerpt — and rejected as
102
+ a distinction a reader cannot see. It would make one finding nameable and its
103
+ neighbour not, for a reason invisible on the page, and leaves the same
104
+ residual exposure for the case it permits.
105
+
106
+ - **Send a hash of the name.** Rejected. It is stable across machines, which is
107
+ the one property the name must not have, and it is unreadable, which is the
108
+ one property a responder needs.
109
+
110
+ - **Amend nothing and surface the name only in `monitor`.** Rejected as the
111
+ status quo restated: triage happens in Fleet, and a console on one laptop
112
+ does not answer for a fleet.
@@ -41,7 +41,7 @@ supersedes anything yet.
41
41
  - [0005](0005-detection-family-and-report-assembly.md) — **Detections are their own finding family, assembled here.** Why a detection is not an observation, and why `stacktrace` merges the four families rather than OpenACA emitting a fourth. `stacktrace-*` identifiers. Read before adding a finding family, touching output assembly, or contributing anything upstream.
42
42
  - [0006](0006-trust-boundary-and-detection-upload.md) — **Sessions never leave the machine; detections upload agent-keyed.** No upload path for anything session-derived; findings carry descriptors, never excerpts; place identity stays in the envelope. Read before adding a detection field, touching redaction, or describing AIDR as fully local or anonymous.
43
43
  - [0007](0007-proprietary-package-on-open-dependencies.md) — **Proprietary package on open dependencies.** Why this is not inside OpenACA or OpenAIDR, and why the dependency arrow never reverses. Read before moving code between repos.
44
- - [0008](0008-console-in-separate-demo-package.md) — **Trace console ships as a separate demo package.** Read before adding a served UI, transport, or web asset to this repo.
44
+ - [0008](0008-console-in-separate-demo-package.md) — **Trace console ships as a separate demo package.** Read before adding a served UI, transport, or web asset to this repo. **Superseded by [0026](0026-the-console-becomes-a-product-surface.md).**
45
45
  - [0010](0010-detection-severity-and-confidence-ladders.md) — **Detections define their own severity and confidence ladders.** Amends ADR-0005: `openaca.core` exports no ladder to reuse, and CVSS labels grade advisories a detection does not have. Read before changing a finding's grade, or before assuming the four families share one scale.
46
46
  - [0011](0011-verdict-cache.md) — **A bounded local verdict cache, and what it may hold.** Amends ADR-0006: a reasoning verdict is an expensive pure function of a session, so it may persist — carrying rule ids, grades, spans and analyzer identity, and never conversation. Read before persisting anything session-derived, or before letting an upload path read local state.
47
47
  - [0012](0012-observation-evidence-kinds-and-transport.md) — **Observation is graded by what could misread it; an MCP transport is positive-only evidence.** Severity and confidence are orthogonal and neither is read off the other; a precondition sets a finding's grade, corroboration takes the best; `stdio` is never evidence of local execution. Read before changing a rule's confidence, adding an evidence kind, or treating an absent transport as local.
@@ -57,4 +57,6 @@ supersedes anything yet.
57
57
  - [0022](0022-detection-scope-is-a-catalogue-column.md) — **Detection scope is a per-rule catalogue column, set by cardinality.** Fleet identifies a detection by rule plus anchor, so scope decides what counts as the same finding seen again; `advisory-reach` and `progress-stall` both read as session-shaped and fire more than once per session, and session scope would merge those firings into one row permanently. Deriving scope from `bool(evidence)` is rejected — every rule always cites something. Read before adding a rule, or before changing how one groups its findings.
58
58
  - [0023](0023-sync-detect-collects-and-does-not-escalate.md) — **`sync detect` re-collects in-process, and defaults `--escalate` off.** The inverse of `stacktrace detect`, because a scheduled unattended run spending provider quota and crossing a content boundary on a timer is a recurring charge nobody approved per-run; consuming `detect --format json` was rejected as a projection of a projection. Read before changing a sync default, or before aligning the two commands' flags.
59
59
  - [0024](0024-the-upload-carries-observations.md) — **The upload carries observations; the Cloud derives catalogue text and coverage.** `title`, `remediation`, `family`, `verdict.stage` and `coverage` are all dropped — the first four are catalogue facts the Cloud seeds, and coverage is summed from `activity[]`. Not a privacy projection (ADR-0006 settled that) but a non-duplication one. Read before adding a field to the upload payload, or before "fixing" a bare `rule_id` in the console.
60
+ - [0026](0026-the-console-becomes-a-product-surface.md) — **The console becomes a product surface, served by the engine.** Supersedes ADR-0008 on its own stated condition: the engine came first, 0.1.0 shipped, and the data is only useful if a person can follow it. `stacktrace monitor` serves a loopback page from in-process calls, so the wheel now carries a `site/` asset — the cost 0008 refused, accepted here explicitly. What survives from 0008 is the clause that matters: the console renders and never computes, so a finding on the page is one `detect` would print. Read before adding anything to the console that the detector does not produce.
60
61
  - [0025](0025-a-denied-call-is-activity-never-an-invocation.md) — **A denied call is activity, and never an invocation.** ADR-0017 makes a declined repeat a stall while `outcome.py` keeps a denied call out of every count, so a denied-only session produced a finding with no activity and the Cloud rejected the whole run; a separate `denied` counter resolves it without either rule bending, and coverage keeps summing `invocations` alone. Read before changing what `activity[]` counts, or before folding `denied` into a coverage-like ratio.
62
+ - [0027](0027-a-finding-may-name-what-it-could-not-place.md) — **A finding may name the component it could not place.** Narrows ADR-0006 constraint 3 for one field: an unplaced subject carries the name the session addressed it by, never as an identity. Read before removing a name from a finding on trust-boundary grounds, or before widening this to a second field.
@@ -48,7 +48,7 @@ not against the spec's prose. Four of them contradict the obvious reading.
48
48
  | The Cloud's bounds | read `stacktrace-fleet` `schemas.py` | `MAX_DETECTIONS=5000`, `MAX_UNKNOWNS=5000`, `MAX_ACTIVITY=20000`, `MAX_EVIDENCE_PER_DETECTION=100`, `MAX_COMPONENTS_PER_DETECTION=50`, `MAX_CAPABILITIES=16`, `MAX_SPANS_PER_UNKNOWN=100`, `MAX_SPAN_LEN=256`, `MAX_SESSION_ID_LEN=256`, `MAX_STRING_LEN=512`, `MAX_RULE_ID_LEN=128` |
49
49
  | The span grammar | read `stacktrace-fleet` `schemas.py` | `^[a-z0-9][a-z0-9._-]*(?::[A-Za-z0-9._-]+)+:[0-9]+$`, and separately: a span may carry no path separator at all |
50
50
  | The response shape | read `DetectRunResponse` | `detect_run_id`, `agent_id`, `detections_ingested`, `detections_new`, `activity_rows`, `dashboard_url` |
51
- | `Composition` has no instance id | read `composition.py`, `acquire.py` | `acquire` reads only `openaca:agent_kind`; there is no field to carry `openaca:agent_id` |
51
+ | `Composition` has no instance id | read `composition.py`, `acquire.py` | `acquire` read only `openaca:agent_kind` and there was no field to carry `openaca:agent_id`. Closed by [#12](https://github.com/stacktrace-ai/stacktrace/issues/12): `Composition.agent_instance_id`, read by the shared `root_property` walk |
52
52
  | The spool is single-kind in four places | read `spool.py` | `pending-bom-` is a literal at four sites and `replay_pending_uploads` calls `upload_bom` directly |
53
53
 
54
54
  **ADRs:** the decisions this plan rests on are accepted and indexed —
@@ -274,16 +274,21 @@ rewrite*.
274
274
 
275
275
  ## Task 9: The cross-repo gate
276
276
 
277
- **Files:** none in this repository.
277
+ **Files:** `tests/remote/test_detect_payload_reaches_the_cloud_model.py`.
278
278
 
279
279
  - [ ] **Step 1: `denied` needs the Cloud's half.** The producer emits it per the
280
280
  spec; `RuntimePayload` is `extra="forbid"` (ground truth row 7), so until
281
281
  `stacktrace-fleet` ships the counter split, an upload is a 422 for the whole
282
282
  agent's run. `--dry-run` is unaffected. Specified in
283
283
  `stacktrace-fleet/docs/specs/2026-09-05-denied-attempts-and-agent-runtime.md`.
284
- - [ ] **Step 2: Add a contract test that fails when the gate clears**, in the
285
- shape `test_openaca_contract.py` already uses, so the coupling is visible
286
- here rather than discovered by a 422 in the field.
284
+ The sibling checkout now accepts the whole shape; the *deployed* service has
285
+ not been checked, so this stays open until it has.
286
+ - [x] **Step 2: Add a contract test over the real payload**, in the shape
287
+ `test_openaca_contract.py` already uses, so the coupling is visible here
288
+ rather than discovered by a 422 in the field. It runs the payload
289
+ `build_detect_payloads` and `activity_for_upload` actually produce — `mcp`,
290
+ `denied`, `subject`, `involved` and both `agent_instance_id` cases — through
291
+ the sibling's own `DetectRunUpload`, and skips when that checkout is absent.
287
292
 
288
293
  ## Deferred
289
294
 
@@ -292,7 +297,7 @@ should be re-raised in review as a finding.
292
297
 
293
298
  | Deferred | Cost of skipping |
294
299
  |---|---|
295
- | `agent_instance_id` is always `null` ([#12](https://github.com/stacktrace-ai/stacktrace/issues/12)) | The wire field exists and is never populated. `Composition` has no instance field and `acquire.py` reads only `openaca:agent_kind`, so carrying it means widening both — a change to the correlation layer for a field the Cloud currently treats as optional. Worse than coarse grouping: `sync endpoint` already sends a real id read off the BOM root, and the id is part of the Cloud's agent key, so the two commands resolve to two different agent rows — inventory on one, runtime findings on the other. Fixing it later strands the null-keyed row's history. |
300
+ | ~~`agent_instance_id` is always `null`~~ — **done**, [#12](https://github.com/stacktrace-ai/stacktrace/issues/12) | Was: the wire field existed and nothing populated it, so `sync endpoint` sent a real id off the BOM root while `sync detect` sent `null`, and the id is part of the Cloud's agent key — two agent rows for one agent, inventory on one and runtime findings on the other. Now read into `Composition.agent_instance_id` and mapped per kind in `sync_detect._instance_ids`. Note this does **not** enable several agents of one kind: `acquire` refuses two BOMs for one kind, and the map holds one id per kind. |
296
301
  | `verdict.analyzer`, `.model`, `.prompt_version` | *Which analyzer graded this, fleet-wide* is unanswerable. Cheap to add later. |
297
302
  | `Unknown.stage`, `.dispatched` | Cannot separate *the model never ran* from *the model refused*. `reason` carries most of it, but not `dispatched`, which is what keeps a session from being counted twice. |
298
303
  | Per-call timestamps | `last_invoked_at` is null on every row until `openaidr`'s `ToolCall` grows a timestamp; it carries `duration_ms`, a delta, and `Turn` carries none. |