ja4plus 1.2.0__tar.gz → 1.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (502) hide show
  1. ja4plus-1.2.2/.claude/rules/agent-reports.md +157 -0
  2. {ja4plus-1.2.0 → ja4plus-1.2.2}/.claude/rules/batch-gate.md +78 -25
  3. {ja4plus-1.2.0 → ja4plus-1.2.2}/.claude/rules/conformance.md +236 -2
  4. {ja4plus-1.2.0 → ja4plus-1.2.2}/.claude/rules/external-apis.md +1 -1
  5. {ja4plus-1.2.0 → ja4plus-1.2.2}/.claude/rules/ste.md +66 -9
  6. {ja4plus-1.2.0 → ja4plus-1.2.2}/.github/workflows/docs.yml +1 -1
  7. {ja4plus-1.2.0 → ja4plus-1.2.2}/.github/workflows/test.yml +36 -1
  8. {ja4plus-1.2.0 → ja4plus-1.2.2}/.issue-flow.json +15 -0
  9. {ja4plus-1.2.0 → ja4plus-1.2.2}/CHANGELOG.md +822 -0
  10. {ja4plus-1.2.0 → ja4plus-1.2.2}/CLAUDE.md +49 -2
  11. {ja4plus-1.2.0/ja4plus.egg-info → ja4plus-1.2.2}/PKG-INFO +6 -6
  12. {ja4plus-1.2.0 → ja4plus-1.2.2}/README.md +1 -1
  13. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/api_reference.md +1 -1
  14. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/implementation_notes.md +12 -12
  15. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/methods/ja4l.md +1 -1
  16. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/methods/ja4ls.md +2 -2
  17. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/migration-0.6-to-1.0.md +7 -2
  18. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/output-schema.md +2 -2
  19. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/features/00-foundation.md +1 -1
  20. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/features/01-spec-conformance.md +3 -3
  21. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/features/03-concurrency-safety.md +1 -1
  22. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/features/07-db-enrichment.md +1 -1
  23. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/features/09-release.md +1 -1
  24. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/features/11-pre-release-validation.md +3 -3
  25. ja4plus-1.2.2/docs/specs/foxio/JA4.md +523 -0
  26. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/foxio/JA4D.md +3 -3
  27. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/foxio/JA4SSH.md +45 -20
  28. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/foxio/JA4T.md +1 -1
  29. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/foxio/JA4X.md +1 -1
  30. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/foxio/README.md +1 -0
  31. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/foxio/deleted-text-specifications.md +1 -1
  32. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/foxio/zeek.md +3 -3
  33. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/spec.html +3 -3
  34. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/spec.md +47 -15
  35. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/__init__.py +1 -1
  36. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/cli.py +67 -17
  37. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/fingerprinters/ja4l.py +162 -54
  38. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/processor.py +33 -17
  39. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/utils/quic_utils.py +9 -5
  40. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/utils/tcp_stream.py +20 -6
  41. {ja4plus-1.2.0 → ja4plus-1.2.2/ja4plus.egg-info}/PKG-INFO +6 -6
  42. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus.egg-info/SOURCES.txt +23 -0
  43. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus.egg-info/requires.txt +4 -4
  44. {ja4plus-1.2.0 → ja4plus-1.2.2}/pyproject.toml +4 -4
  45. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/build_quic_ja4l_captures.py +1 -1
  46. ja4plus-1.2.2/tests/citation_line_allowlist.json +140 -0
  47. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/download_test_vectors.py +35 -8
  48. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_citation_lines.py +83 -10
  49. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/skip_gate.py +1 -1
  50. ja4plus-1.2.2/tests/test_agent_report_absence_rule.py +307 -0
  51. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_alpn_ruling_register.py +58 -1
  52. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_batch_gate.py +2 -2
  53. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_batch_gate_protection_rule.py +1 -1
  54. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_breaking_change_record.py +10 -4
  55. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_changelog_round_agreement.py +19 -10
  56. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_changelog_sentence_exemption.py +27 -22
  57. ja4plus-1.2.2/tests/test_cli_closed_window_endpoints.py +215 -0
  58. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_cli_types_ja4ls.py +1 -1
  59. ja4plus-1.2.2/tests/test_conformance_frame_discrimination.py +255 -0
  60. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_db_item_access.py +1 -1
  61. ja4plus-1.2.2/tests/test_decryption_non_goal_ruling.py +288 -0
  62. ja4plus-1.2.2/tests/test_delegated_ruling_rule.py +348 -0
  63. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_dhcp_tunnel_inner_udp.py +1 -1
  64. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_documentation_fallback_rule.py +2 -2
  65. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_documentation_image_count.py +1 -1
  66. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_documentation_publish.py +1 -1
  67. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_documentation_site.py +33 -22
  68. ja4plus-1.2.2/tests/test_download_filter_fields.py +103 -0
  69. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_empty_list_sentinel_ruling.py +39 -3
  70. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_foxio_citation_lines.py +106 -6
  71. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_foxio_deviations.py +1 -1
  72. ja4plus-1.2.2/tests/test_foxio_ja4_transcription.py +212 -0
  73. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_future_dated_packet_eviction.py +2 -2
  74. ja4plus-1.2.2/tests/test_get_stream_byte_cap_truncation.py +107 -0
  75. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ipv6_support.py +1 -1
  76. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4d_decisions.py +1 -1
  77. ja4plus-1.2.2/tests/test_ja4h_emission_frame_ruling.py +254 -0
  78. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4h_reassembly.py +1 -1
  79. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4l_foxio.py +71 -9
  80. ja4plus-1.2.2/tests/test_ja4l_quic_eviction_ruling.py +308 -0
  81. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4l_quic_marker.py +41 -5
  82. ja4plus-1.2.2/tests/test_ja4l_quic_point_b_ruling.py +223 -0
  83. ja4plus-1.2.2/tests/test_ja4l_quic_point_d_emission.py +223 -0
  84. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4l_quic_server_hello.py +33 -7
  85. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4l_return_path.py +7 -7
  86. ja4plus-1.2.2/tests/test_ja4l_timing_fork_ruling.py +166 -0
  87. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4l_udp_direction.py +34 -7
  88. ja4plus-1.2.2/tests/test_ja4ssh_packet_selection_ruling.py +311 -0
  89. ja4plus-1.2.2/tests/test_ja4ssh_parity_record.py +98 -0
  90. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4ts_reset.py +1 -1
  91. ja4plus-1.2.2/tests/test_key_log_interface_ruling.py +335 -0
  92. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_memory_bounds.py +1 -1
  93. ja4plus-1.2.2/tests/test_ported_pattern_cost.py +193 -0
  94. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_processor_stats.py +1 -1
  95. ja4plus-1.2.2/tests/test_quic_coalesced_datagram_ruling.py +354 -0
  96. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_quic_crypto_buffer_bound.py +9 -2
  97. ja4plus-1.2.2/tests/test_quic_crypto_gap.py +132 -0
  98. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_round_entry_existence.py +274 -13
  99. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_settlement_procedure.py +2 -2
  100. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_tcp_byte_cap_admission_ruling.py +1 -1
  101. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_tcp_stream.py +1 -1
  102. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_thread_safety.py +2 -2
  103. ja4plus-1.2.2/tests/test_us_english_spelling.py +692 -0
  104. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_watch.py +1 -1
  105. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_watch_statistics.py +2 -2
  106. {ja4plus-1.2.0 → ja4plus-1.2.2}/.claude/settings.json +0 -0
  107. {ja4plus-1.2.0 → ja4plus-1.2.2}/.claude/skills/conformance/SKILL.md +0 -0
  108. {ja4plus-1.2.0 → ja4plus-1.2.2}/.claude/skills/release/SKILL.md +0 -0
  109. {ja4plus-1.2.0 → ja4plus-1.2.2}/.claude/skills/test/SKILL.md +0 -0
  110. {ja4plus-1.2.0 → ja4plus-1.2.2}/.git-blame-ignore-revs +0 -0
  111. {ja4plus-1.2.0 → ja4plus-1.2.2}/.github/dependabot.yml +0 -0
  112. {ja4plus-1.2.0 → ja4plus-1.2.2}/.github/workflows/docs-build.yml +0 -0
  113. {ja4plus-1.2.0 → ja4plus-1.2.2}/.github/workflows/publish.yml +0 -0
  114. {ja4plus-1.2.0 → ja4plus-1.2.2}/.github/workflows/update-db.yml +0 -0
  115. {ja4plus-1.2.0 → ja4plus-1.2.2}/.gitignore +0 -0
  116. {ja4plus-1.2.0 → ja4plus-1.2.2}/LICENSE +0 -0
  117. {ja4plus-1.2.0 → ja4plus-1.2.2}/assets/logo.png +0 -0
  118. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/README.md +0 -0
  119. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/concurrency.md +0 -0
  120. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/methods/index.md +0 -0
  121. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/methods/ja4.md +0 -0
  122. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/methods/ja4d.md +0 -0
  123. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/methods/ja4d6.md +0 -0
  124. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/methods/ja4h.md +0 -0
  125. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/methods/ja4s.md +0 -0
  126. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/methods/ja4ssh.md +0 -0
  127. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/methods/ja4t.md +0 -0
  128. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/methods/ja4ts.md +0 -0
  129. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/methods/ja4x.md +0 -0
  130. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/mutation_reports/412-utils.json +0 -0
  131. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/mutation_reports/413-fingerprinters.json +0 -0
  132. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/mutation_reports/414-interface.json +0 -0
  133. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/mutation_settlements/412-utils.json +0 -0
  134. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/mutation_settlements/413-fingerprinters.json +0 -0
  135. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/mutation_settlements/414-interface.json +0 -0
  136. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/mutation_sweep.md +0 -0
  137. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/performance.md +0 -0
  138. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/reference/fingerprinters.md +0 -0
  139. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/reference/index.md +0 -0
  140. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/reference/ja4db.md +0 -0
  141. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/reference/processor.md +0 -0
  142. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/reference/types.md +0 -0
  143. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/features/02-correctness-audit.md +0 -0
  144. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/features/04-typed-api.md +0 -0
  145. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/features/05-structured-output.md +0 -0
  146. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/features/06-live-capture.md +0 -0
  147. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/features/08-documentation.md +0 -0
  148. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/foxio/JA4H.md +0 -0
  149. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/foxio/JA4L.md +0 -0
  150. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/foxio/JA4S.md +0 -0
  151. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/specs/mockups/01-cli-output.html +0 -0
  152. {ja4plus-1.2.0 → ja4plus-1.2.2}/docs/usage.md +0 -0
  153. {ja4plus-1.2.0 → ja4plus-1.2.2}/examples/demo_fingerprints.py +0 -0
  154. {ja4plus-1.2.0 → ja4plus-1.2.2}/examples/live_traffic_fingerprinting.py +0 -0
  155. {ja4plus-1.2.0 → ja4plus-1.2.2}/examples/pcap_analysis.py +0 -0
  156. {ja4plus-1.2.0 → ja4plus-1.2.2}/examples/tcpdump_workflows.py +0 -0
  157. {ja4plus-1.2.0 → ja4plus-1.2.2}/examples/threat_detection.py +0 -0
  158. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/data/ja4plus-mapping.csv +0 -0
  159. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/fingerprinters/__init__.py +0 -0
  160. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/fingerprinters/base.py +0 -0
  161. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/fingerprinters/ja4.py +0 -0
  162. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/fingerprinters/ja4d.py +0 -0
  163. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/fingerprinters/ja4d6.py +0 -0
  164. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/fingerprinters/ja4h.py +0 -0
  165. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/fingerprinters/ja4s.py +0 -0
  166. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/fingerprinters/ja4ssh.py +0 -0
  167. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/fingerprinters/ja4t.py +0 -0
  168. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/fingerprinters/ja4ts.py +0 -0
  169. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/fingerprinters/ja4x.py +0 -0
  170. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/ja4db.py +0 -0
  171. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/output.py +0 -0
  172. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/py.typed +0 -0
  173. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/types.py +0 -0
  174. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/utils/__init__.py +0 -0
  175. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/utils/http_utils.py +0 -0
  176. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/utils/icmp_quoted.py +0 -0
  177. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/utils/loopback.py +0 -0
  178. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/utils/packet_utils.py +0 -0
  179. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/utils/ssh_utils.py +0 -0
  180. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/utils/state_table.py +0 -0
  181. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/utils/tcp_options.py +0 -0
  182. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/utils/tls_utils.py +0 -0
  183. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/utils/tunnels.py +0 -0
  184. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/utils/x509_utils.py +0 -0
  185. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus/watch.py +0 -0
  186. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus.egg-info/dependency_links.txt +0 -0
  187. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus.egg-info/entry_points.txt +0 -0
  188. {ja4plus-1.2.0 → ja4plus-1.2.2}/ja4plus.egg-info/top_level.txt +0 -0
  189. {ja4plus-1.2.0 → ja4plus-1.2.2}/mkdocs.yml +0 -0
  190. {ja4plus-1.2.0 → ja4plus-1.2.2}/setup.cfg +0 -0
  191. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/__init__.py +0 -0
  192. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/batch_gate.py +0 -0
  193. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/build_alpn_condition_capture.py +0 -0
  194. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/build_ja4ts_rst.py +0 -0
  195. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/build_ssh_retransmission.py +0 -0
  196. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/compare_zeek_baselines.py +0 -0
  197. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/conformance_index.py +0 -0
  198. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/conftest.py +0 -0
  199. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/data/dns.cap +0 -0
  200. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/data/example_cert.der +0 -0
  201. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/data/filedownload.dmp +0 -0
  202. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/data/http.cap +0 -0
  203. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/data/suspectemployeechat.dmp +0 -0
  204. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/data/test_cert.der +0 -0
  205. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/data/wrongdissector.dmp +0 -0
  206. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/dependency_entries.py +0 -0
  207. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/documentation_samples.py +0 -0
  208. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_deviation_owners.json +0 -0
  209. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_deviations.json +0 -0
  210. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_deviations.py +0 -0
  211. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_manifest.py +0 -0
  212. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vector_manifest.json +0 -0
  213. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/CVE-2018-6794.pcap +0 -0
  214. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/CVE-2018-6794.pcap.json +0 -0
  215. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/NOTICE +0 -0
  216. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/alpn-condition.pcap +0 -0
  217. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/alpn-condition.pcap.json +0 -0
  218. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/badcurveball.pcap +0 -0
  219. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/badcurveball.pcap.json +0 -0
  220. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/browsers-x509.pcapng +0 -0
  221. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/browsers-x509.pcapng.json +0 -0
  222. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/chrome-cloudflare-quic-with-secrets.pcapng +0 -0
  223. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/chrome-cloudflare-quic-with-secrets.pcapng.json +0 -0
  224. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/dhcp.pcapng +0 -0
  225. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/dhcp.pcapng.json +0 -0
  226. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/dhcpv6.pcap +0 -0
  227. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/dhcpv6.pcap.json +0 -0
  228. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/gre-erspan-vxlan.pcap +0 -0
  229. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/gre-erspan-vxlan.pcap.json +0 -0
  230. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/gre-sample.pcap +0 -0
  231. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/gre-sample.pcap.json +0 -0
  232. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/http-empty-useragent.pcap +0 -0
  233. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/http-empty-useragent.pcap.json +0 -0
  234. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/http1-with-cookies.pcapng +0 -0
  235. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/http1-with-cookies.pcapng.json +0 -0
  236. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/http1.pcapng +0 -0
  237. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/http1.pcapng.json +0 -0
  238. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/http2-with-cookies.pcapng +0 -0
  239. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/http2-with-cookies.pcapng.json +0 -0
  240. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/https-connect.pcap +0 -0
  241. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/https-connect.pcap.json +0 -0
  242. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/https3-301-get.pcap +0 -0
  243. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/https3-301-get.pcap.json +0 -0
  244. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/ipv6.pcapng +0 -0
  245. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/ipv6.pcapng.json +0 -0
  246. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/latest.pcapng +0 -0
  247. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/latest.pcapng.json +0 -0
  248. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/macos_tcp_flags.pcap +0 -0
  249. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/macos_tcp_flags.pcap.json +0 -0
  250. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/quic-tls-handshake.pcapng +0 -0
  251. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/quic-tls-handshake.pcapng.json +0 -0
  252. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/quic-with-several-tls-frames.pcapng +0 -0
  253. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/quic-with-several-tls-frames.pcapng.json +0 -0
  254. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/rust_expected/ja4__insta@browsers-x509.pcapng.snap +0 -0
  255. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/rust_expected/ja4__insta@chrome-cloudflare-quic-with-secrets.pcapng.snap +0 -0
  256. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/rust_expected/ja4__insta@gre-erspan-vxlan.pcap.snap +0 -0
  257. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/rust_expected/ja4__insta@https-connect.pcap.snap +0 -0
  258. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/rust_expected/ja4__insta@latest.pcapng.snap +0 -0
  259. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/rust_expected/ja4__insta@quic-tls-handshake.pcapng.snap +0 -0
  260. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/rust_expected/ja4__insta@quic-with-several-tls-frames.pcapng.snap +0 -0
  261. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/rust_expected/ja4__insta@ssh2.pcapng.snap +0 -0
  262. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/rust_expected/ja4__insta@tls-handshake.pcapng.snap +0 -0
  263. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/rust_expected/ja4__insta@tls-sni.pcapng.snap +0 -0
  264. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/rust_expected/ja4__insta@tls3.pcapng.snap +0 -0
  265. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/single-packets.pcap +0 -0
  266. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/single-packets.pcap.json +0 -0
  267. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/socks-https-example.pcap +0 -0
  268. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/socks-https-example.pcap.json +0 -0
  269. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/socks4-https.pcap +0 -0
  270. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/socks4-https.pcap.json +0 -0
  271. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/ssh-r.pcap +0 -0
  272. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/ssh-r.pcap.json +0 -0
  273. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/ssh-scp-1050.pcap +0 -0
  274. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/ssh-scp-1050.pcap.json +0 -0
  275. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/ssh.pcapng +0 -0
  276. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/ssh.pcapng.json +0 -0
  277. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/ssh2-malformed.pcap +0 -0
  278. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/ssh2-malformed.pcap.json +0 -0
  279. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/ssh2-moloch-crash.pcap +0 -0
  280. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/ssh2-moloch-crash.pcap.json +0 -0
  281. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/ssh2.pcapng +0 -0
  282. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/ssh2.pcapng.json +0 -0
  283. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/sshv1.pcap +0 -0
  284. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/sshv1.pcap.json +0 -0
  285. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/tcpdump-geneve.pcap +0 -0
  286. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/tcpdump-geneve.pcap.json +0 -0
  287. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/tls-alpn-h2.pcap +0 -0
  288. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/tls-alpn-h2.pcap.json +0 -0
  289. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/tls-handshake.pcapng +0 -0
  290. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/tls-handshake.pcapng.json +0 -0
  291. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/tls-non-ascii-alpn.pcapng +0 -0
  292. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/tls-non-ascii-alpn.pcapng.json +0 -0
  293. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/tls-sni.pcapng +0 -0
  294. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/tls-sni.pcapng.json +0 -0
  295. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/tls12.pcap +0 -0
  296. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/tls12.pcap.json +0 -0
  297. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/tls3.pcapng +0 -0
  298. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/tls3.pcapng.json +0 -0
  299. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/v6.pcap +0 -0
  300. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/v6.pcap.json +0 -0
  301. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/CVE-2018-6794.pcap.json +0 -0
  302. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/badcurveball.pcap.json +0 -0
  303. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/browsers-x509.pcapng.json +0 -0
  304. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/chrome-cloudflare-quic-with-secrets.pcapng.json +0 -0
  305. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/dhcp.pcapng.json +0 -0
  306. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/dhcpv6.pcap.json +0 -0
  307. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/gre-erspan-vxlan.pcap.json +0 -0
  308. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/http-empty-useragent.pcap.json +0 -0
  309. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/http1-with-cookies.pcapng.json +0 -0
  310. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/http2-with-cookies.pcapng.json +0 -0
  311. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/https-connect.pcap.json +0 -0
  312. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/https3-301-get.pcap.json +0 -0
  313. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/ipv6.pcapng.json +0 -0
  314. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/latest.pcapng.json +0 -0
  315. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/socks-https-example.pcap.json +0 -0
  316. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/socks4-https.pcap.json +0 -0
  317. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/ssh-r.pcap.json +0 -0
  318. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/ssh-scp-1050.pcap.json +0 -0
  319. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/ssh2-malformed.pcap.json +0 -0
  320. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/ssh2-moloch-crash.pcap.json +0 -0
  321. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/ssh2.pcapng.json +0 -0
  322. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/sshv1.pcap.json +0 -0
  323. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/tcpdump-geneve.pcap.json +0 -0
  324. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/tls-alpn-h2.pcap.json +0 -0
  325. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/tls3.pcapng.json +0 -0
  326. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/wireshark_expected/v6.pcap.json +0 -0
  327. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/zeek_expected/Scripts.ja4-conn/conn.log +0 -0
  328. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/zeek_expected/Scripts.ja4-conn-quic/conn.log +0 -0
  329. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/zeek_expected/Scripts.ja4-conn-tls3/conn.log +0 -0
  330. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/zeek_expected/Scripts.ja4-dhcp/ja4d.log +0 -0
  331. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/zeek_expected/Scripts.ja4-http1-with-cookies/http.log +0 -0
  332. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/zeek_expected/Scripts.ja4-ssh2/ja4ssh.log +0 -0
  333. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/foxio_vectors/zeek_expected/Scripts.ja4-tls-handshake/ssl.log +0 -0
  334. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/fuzz/README.md +0 -0
  335. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/fuzz/__init__.py +0 -0
  336. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/fuzz/conftest.py +0 -0
  337. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/fuzz/support.py +0 -0
  338. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/fuzz/test_icmp_quoted_header.py +0 -0
  339. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/fuzz/test_length_fields.py +0 -0
  340. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/fuzz/test_malformed_captures.py +0 -0
  341. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/fuzz/test_server_hello_extensions.py +0 -0
  342. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/fuzz/test_structural_validity.py +0 -0
  343. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/fuzz/test_synthetic_hostile_input.py +0 -0
  344. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/fuzz/test_truncated_captures.py +0 -0
  345. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/generate_foxio_baseline.py +0 -0
  346. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/measure_random_client_hello.py +0 -0
  347. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/memory_ceiling_run.py +0 -0
  348. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/mutation_census.py +0 -0
  349. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/mutation_cover.py +0 -0
  350. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/mutation_sweep.py +0 -0
  351. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/quic_builder.py +0 -0
  352. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/release_body.py +0 -0
  353. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/release_verification.py +0 -0
  354. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/state_readers.py +0 -0
  355. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_api_reference_interface_table.py +0 -0
  356. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_bare_exception_reading.py +0 -0
  357. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_bounded_state_table_adoption.py +0 -0
  358. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_cleanup_connection.py +0 -0
  359. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_cli.py +0 -0
  360. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_cli_lookup_opt_in.py +0 -0
  361. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_cli_output_option.py +0 -0
  362. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_cli_processor.py +0 -0
  363. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_compare_zeek_baselines.py +0 -0
  364. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_comprehensive.py +0 -0
  365. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_criterion_counts.py +0 -0
  366. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_db_cache_directory.py +0 -0
  367. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_db_lookup_many.py +0 -0
  368. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_db_offline.py +0 -0
  369. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_dependency_entries.py +0 -0
  370. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_documentation_behaviour_rule.py +0 -0
  371. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_documentation_samples.py +0 -0
  372. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_documentation_version_claims.py +0 -0
  373. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_documented_method_count.py +0 -0
  374. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_documented_state_table_count.py +0 -0
  375. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_edge_cases.py +0 -0
  376. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_examples.py +0 -0
  377. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_foxio_license_register.py +0 -0
  378. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_foxio_manifest.py +0 -0
  379. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_foxio_rust_parity.py +0 -0
  380. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_foxio_wireshark_ja4ts.py +0 -0
  381. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_foxio_zeek_ja4ts.py +0 -0
  382. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_generate_foxio_baseline.py +0 -0
  383. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_http_header_block_terminator.py +0 -0
  384. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_installed_wheel.py +0 -0
  385. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_installed_wheel_selection.py +0 -0
  386. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_integration.py +0 -0
  387. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_interpreter_set.py +0 -0
  388. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4.py +0 -0
  389. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4_alpn.py +0 -0
  390. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4_alpn_condition.py +0 -0
  391. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4_deep.py +0 -0
  392. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4_empty_ext.py +0 -0
  393. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4_hello_retry.py +0 -0
  394. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4_original_order.py +0 -0
  395. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4_raw_branches.py +0 -0
  396. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4_ssl2_version.py +0 -0
  397. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4d.py +0 -0
  398. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4d6_foxio.py +0 -0
  399. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4d_foxio.py +0 -0
  400. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4d_fqdn_name_ruling.py +0 -0
  401. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4db.py +0 -0
  402. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4db_empty_list_alias.py +0 -0
  403. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4h.py +0 -0
  404. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4h_cookie_list.py +0 -0
  405. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4h_deep.py +0 -0
  406. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4h_large_cookie_header.py +0 -0
  407. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4h_line_endings.py +0 -0
  408. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4h_part_a_readings.py +0 -0
  409. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4h_raw.py +0 -0
  410. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4h_raw_sorted.py +0 -0
  411. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4h_request_line_and_empty_header_list.py +0 -0
  412. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4h_retransmission.py +0 -0
  413. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4h_spec.py +0 -0
  414. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4l.py +0 -0
  415. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4l_connection_state.py +0 -0
  416. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4l_header_block_terminator.py +0 -0
  417. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4l_quic_repeated_connection.py +0 -0
  418. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4s.py +0 -0
  419. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4s_quic_direction.py +0 -0
  420. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4s_quic_split_server_hello.py +0 -0
  421. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4s_raw_order.py +0 -0
  422. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4ssh.py +0 -0
  423. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4ssh_close_connection_window.py +0 -0
  424. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4ssh_deep.py +0 -0
  425. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4ssh_direction.py +0 -0
  426. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4ssh_interpret.py +0 -0
  427. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4ssh_message_count.py +0 -0
  428. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4ssh_server_side.py +0 -0
  429. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4ssh_spec.py +0 -0
  430. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4ssh_window.py +0 -0
  431. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4ssh_windows.py +0 -0
  432. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4t.py +0 -0
  433. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4t_citation_lines.py +0 -0
  434. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4t_deep.py +0 -0
  435. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4t_emission_model_ruling.py +0 -0
  436. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4t_form.py +0 -0
  437. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4t_icmp_quoted.py +0 -0
  438. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4t_syn_selection_ruling.py +0 -0
  439. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4ts.py +0 -0
  440. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4ts_delay_rounding_ruling.py +0 -0
  441. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4ts_part_e.py +0 -0
  442. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4ts_prefix_bound.py +0 -0
  443. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4x.py +0 -0
  444. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4x_deep.py +0 -0
  445. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4x_empty_ext.py +0 -0
  446. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4x_named_exceptions.py +0 -0
  447. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4x_raw.py +0 -0
  448. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4x_reassembly.py +0 -0
  449. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ja4x_record_scan.py +0 -0
  450. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_lint_gate_pin.py +0 -0
  451. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_loopback_link_type.py +0 -0
  452. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_method_pages.py +0 -0
  453. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_mutation_census.py +0 -0
  454. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_mutation_cover.py +0 -0
  455. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_mutation_sweep_annotation_skip.py +0 -0
  456. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_mutation_sweep_commit.py +0 -0
  457. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_mutation_sweep_module_list.py +0 -0
  458. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_mutation_sweep_timeout.py +0 -0
  459. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_no_retained_packets.py +0 -0
  460. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_output_schema.py +0 -0
  461. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_packaging.py +0 -0
  462. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_packet_utils.py +0 -0
  463. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_parity.py +0 -0
  464. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_parser_description.py +0 -0
  465. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_precedence_exception.py +0 -0
  466. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_processor.py +0 -0
  467. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_processor_typed_results.py +0 -0
  468. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_public_interface.py +0 -0
  469. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_publish_workflow.py +0 -0
  470. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_quic_integration.py +0 -0
  471. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_quic_multipacket.py +0 -0
  472. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_quic_server_initial.py +0 -0
  473. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_quic_tunnel_inner_udp.py +0 -0
  474. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_quic_utils.py +0 -0
  475. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_readme_contracts.py +0 -0
  476. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_release_body.py +0 -0
  477. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_release_notes.py +0 -0
  478. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_release_skill.py +0 -0
  479. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_requirement_scope.py +0 -0
  480. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_ruling_vocabulary.py +0 -0
  481. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_skip_gate.py +0 -0
  482. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_spec_validation.py +0 -0
  483. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_specification_changelog.py +0 -0
  484. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_specification_terms.py +0 -0
  485. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_state_table.py +0 -0
  486. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_statistics_thread_term.py +0 -0
  487. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_tcp_segment_bound_ruling.py +0 -0
  488. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_throughput.py +0 -0
  489. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_tls_utils.py +0 -0
  490. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_tunnels.py +0 -0
  491. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_types.py +0 -0
  492. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_utils.py +0 -0
  493. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_version_gate.py +0 -0
  494. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_watch_capture.py +0 -0
  495. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_watch_drop_count.py +0 -0
  496. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_watch_stop.py +0 -0
  497. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_wireshark_expected_counts.py +0 -0
  498. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_workflow_action_pins.py +0 -0
  499. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/test_x509_certificate_reader.py +0 -0
  500. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/throughput_run.py +0 -0
  501. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/universal_skips.json +0 -0
  502. {ja4plus-1.2.0 → ja4plus-1.2.2}/tests/version_gate.py +0 -0
@@ -0,0 +1,157 @@
1
+ # What a worker reports about the agents it spawns
2
+
3
+ **A worker spawns review agents, and an agent that never returns is a normal event.** The
4
+ worker then writes one comment that stands as the record of the review. This file states
5
+ what that comment says about an agent that returned nothing.
6
+
7
+ **Warning: read this file before you write a self-review comment.** #720 records the defect
8
+ that earned it. A worker on #613 spawned two review agents. One reported and one never did,
9
+ and the worker wrote up the agent that never reported as though it had.
10
+
11
+ ## The rule
12
+
13
+ 1. A report an agent did not return is reported as absent.
14
+ 2. A worker names the agent that reported, and it names the agent that did not.
15
+ 3. An absent report is a normal outcome and it refuses nothing.
16
+
17
+ **A worker that ran the review itself in place of the absent agent says so.** That sentence
18
+ is the whole repair, and it costs one line.
19
+
20
+ **Warning: rule 3 binds the reader of the comment as well.** An absent report carries these
21
+ three consequences.
22
+
23
+ - It refuses no merge.
24
+ - It fails no gate.
25
+ - It earns no tracker issue.
26
+
27
+ A reader who treats it as a failure gives the next worker a reason to fill the gap.
28
+
29
+ ## Why the rule names the absence as correct
30
+
31
+ **The worker on #613 reported its own fabrication in the verdict it returned.** It named the
32
+ comment and it named the exact sentences. The project manager corrected the comment at
33
+ https://github.com/Crank-Git/ja4plus/pull/718#issuecomment-5305142104, and it quoted the
34
+ superseded text rather than deleting it.
35
+
36
+ **A rule that punishes the report rather than the shape removes the one signal that found
37
+ this defect.** No gate of this project reads whether a sentence about a review is true. The
38
+ worker's own report is therefore the one detection this defect class has. The rule names
39
+ the absence as the correct outcome, and it leaves a worker one cheap true thing to say.
40
+
41
+ **The census of 2026-08-16 measured that the absence is common and that workers report it
42
+ plainly.** The corpus holds 17 comments that state a review agent returned nothing, and 10
43
+ of them use the word `stall`. Each one states what the worker did instead. One record of
44
+ the same corpus counts nine stalls in one session. The rule writes down a practice this
45
+ project already holds.
46
+
47
+ ## Where the repair belongs
48
+
49
+ **The repair belongs in two places, and this change makes one of them.**
50
+
51
+ | Home | What it holds | This change |
52
+ |---|---|---|
53
+ | `.claude/rules/agent-reports.md` | The rule, the reason, the census, and the limit | Written here |
54
+ | `agents/issue-worker.md` of the `issue-flow` plugin | The same rule beside the step that spawns the review agents | Named, and no line changed |
55
+
56
+ **The plugin is outside this repository, so #720 names the file and it edits no line of
57
+ it.** Step 3 of the runbook spawns the review agents. It opens `**Self-review**`, and it
58
+ directs the worker to review by specialist lens through Sonnet children. That step names no
59
+ outcome for a review agent that never returns. A second copy of the file stands at
60
+ `skills/issue-flow/references/issue-worker.md` of the same plugin.
61
+
62
+ **Warning: the rule reaches a worker of another repository only through the plugin.** This
63
+ file binds a worker of this repository alone, because a worker reads the rules of the
64
+ repository it works in. The maintainer owns the plugin change.
65
+
66
+ ## The census of 2026-08-16
67
+
68
+ **#720 asks whether any earlier record of this repository carries the same shape.** This
69
+ section reports the census that answers it.
70
+
71
+ **Count.** The census read 1842 records. The provider returned each count on 2026-08-16.
72
+
73
+ - 1101 issue comments. That endpoint holds every pull-request conversation comment.
74
+ - 741 issue and pull-request bodies.
75
+ - 0 pull-request review comments.
76
+
77
+ **Method.** The census ran in two passes. The first pass matched every record against a
78
+ pattern of two parts. The first part matches one of the literals `reviewer`, `lens`,
79
+ `lenses`, `review agent`, `review child`, `children` and `Sonnet`. The second part matches a
80
+ verb of finding. It selected 88 comments and 7 bodies.
81
+
82
+ The second pass read each selected record in full, and it looked for four signals.
83
+
84
+ - A quotation of an external document that the record attributes to a review agent.
85
+ - A count that the record attributes to a review agent.
86
+ - A record that reports findings from more agents than it states ran.
87
+ - A plain statement that an agent returned nothing.
88
+
89
+ Two Sonnet children read 44 comments each. **Both children reported.** The worker read the
90
+ 7 bodies itself.
91
+
92
+ **Bound.** The census reads the provider records of this repository alone. It reads no
93
+ session transcript, so it cannot compare a write-up against the reports a worker received.
94
+ It therefore finds the shape in three cases alone.
95
+
96
+ - The record contradicts itself.
97
+ - The record quotes a source that the source does not hold.
98
+ - The worker reported the fabrication.
99
+
100
+ **A fabrication that stays internally consistent and quotes nothing checkable reaches no
101
+ reader of this corpus.** The census reports a clean corpus over that class, and it measures
102
+ none of it.
103
+
104
+ **Finding.** One record of 1842 carries the shape, and that record earned #720. It is the
105
+ comment at
106
+ https://github.com/Crank-Git/ja4plus/pull/718#issuecomment-5305142104. It attributed these
107
+ four things to an agent that never reported.
108
+
109
+ - A count of 26 re-measured claims.
110
+ - A finding of no wrong claim.
111
+ - One claim the agent declined to reproduce.
112
+ - A quotation of RFC 9000 Section 12.2.
113
+
114
+ **Every other record is a clean negative, and the clean negative is the result this section
115
+ reports.**
116
+
117
+ **Two records sit next to the shape and neither one is it.**
118
+
119
+ - The comment at https://github.com/Crank-Git/ja4plus/pull/561#issuecomment-5246016733
120
+ states that a read by lens returned six findings, and it then itemizes five. All three
121
+ lenses reported, and two of them reported nothing. The mismatch is a wrong count and it
122
+ is no fabricated report.
123
+ - The comment at https://github.com/Crank-Git/ja4plus/pull/222#issuecomment-5224677084
124
+ states that two child agents never reported, and it then reports a review the worker ran
125
+ again itself. It carries the date 2026-08-08, so it holds rule 2 and rule 3 eight days
126
+ before this file states them.
127
+
128
+ ## No case of this repository refuses this shape
129
+
130
+ **No case of this repository refuses a fabricated review report, and no case here can.** A
131
+ guard that guards nothing costs a reader more than a stated absence, so this section states
132
+ the absence.
133
+
134
+ **A case would need the record of which agents returned, and that record is the session
135
+ transcript.** The transcript lives outside this repository, git tracks no copy of it, and a
136
+ pull-request comment states no such record either. A case therefore compares the write-up
137
+ against nothing.
138
+
139
+ **A fabricated write-up is well-formed text.** It names lenses, it states counts, and it
140
+ reads like a report the worker received. No property of the text parts it from a true
141
+ report, so a pattern that matched it would match every honest self-review beside it.
142
+
143
+ **`tests/test_agent_report_absence_rule.py` holds every claim of this file that a case can
144
+ read.** These are the claims it reads.
145
+
146
+ - The file exists, and it states the three rules of `## The rule`.
147
+ - The file states the reason that `## Why the rule names the absence as correct` holds.
148
+ - The file names the plugin file, and this repository tracks no copy of that file.
149
+ - The census states a count, a method, a bound and a finding.
150
+ - The file repeats no heading of `.claude/rules/conformance.md`.
151
+
152
+ **That module reads no comment of the provider**, because the suite reads the working tree.
153
+
154
+ **`.claude/rules/conformance.md` states the same discipline for the conformance suite**,
155
+ under `## What a green conformance run does not measure`. #736 wrote that section on
156
+ 2026-08-16 and this file repeats no sentence of it. The two readings share one rule: where a
157
+ case cannot read the shape, record what stays unmeasured.
@@ -31,13 +31,20 @@ the promotion, and it accepted no member pull request, so a pull request into an
31
31
  integration branch created no run at all. Pull request #519 carried a keyword-free head
32
32
  into `batch/510-dry-run-and-gates` and the provider held no run for it.
33
33
 
34
- **A check that reaches the batch pull request alone can pass by construction.**
35
- `tests/test_round_entry_existence.py` fails a change set that edits a tracked file and
36
- records no round. A batch pull request reads its change set against the tip of `dev`, and
37
- every member of a batch has already recorded a round, so that check finds an entry whatever
38
- one member did. **The member pull request is the change set the check exists to refuse, and
39
- the older filter kept it away from the job.** Read a new check against this shape before
40
- you trust it.
34
+ **A check that reaches the batch pull request alone can pass by construction.** Read a new
35
+ check against that shape before you trust it. #438 measured it on 2026-08-10, and the
36
+ paragraph below records that reading. The sentences are quoted rather than rewritten.
37
+
38
+ > `tests/test_round_entry_existence.py` fails a change set that edits a tracked file and
39
+ > records no round. A batch pull request reads its change set against the tip of `dev`, and
40
+ > every member of a batch has already recorded a round, so that check finds an entry whatever
41
+ > one member did. **The member pull request is the change set the check exists to refuse, and
42
+ > the older filter kept it away from the job.**
43
+
44
+ **#727 superseded the example on 2026-08-15, and it left the shape standing.** That issue
45
+ moved the round mandate from the change set to the batch pull request, so a member pull
46
+ request records no round and this check refuses none.
47
+ `## A round is a batch, and the batch pull request records it` below states the model.
41
48
 
42
49
  **The widened filter costs no run that the batch model saved.** A member commit ends with
43
50
  the keyword, so a member pull request still creates no run. Under the widened filter, a
@@ -242,27 +249,72 @@ every pull request into `dev` and it filters no path. `.github/workflows/docs-bu
242
249
  filters four paths, so the gate requires no run of it and a red run of it still refuses
243
250
  the merge.
244
251
 
245
- ## Assign every round number at the batch gate
252
+ ## A round is a batch, and the batch pull request records it
246
253
 
247
- **Warning: assign a round number at the batch gate, and never at a sub-merge.** A
248
- sub-merge is the merge of one member branch into the integration branch. The project
249
- manager assigns the numbers of a whole batch in one pass, immediately before the batch
250
- pull request. `dev` holds a fixed row count at that moment, so the pass reads one
251
- sequence that no other writer moves.
254
+ **Warning: record one round on the batch pull request, and none on a member pull
255
+ request.** #727 moved the mandate on 2026-08-15. The project manager writes the entry and
256
+ the row at the batch gate, immediately before the batch pull request, and it assigns the
257
+ number in the same pass. `dev` holds a fixed row count at that moment, so the pass reads
258
+ one sequence that no other writer moves.
252
259
 
253
- A member writes the literal `TBD` in place of its round number. The `TBD` reaches two
254
- records, and it stays in both through every sub-merge.
260
+ The round reaches two records, and one number covers both.
255
261
 
256
- 1. The entry of `CHANGELOG.md`, as `Round TBD.`.
257
- 2. The Changelog row of `docs/specs/spec.md`, as `| TBD | 2026-08-10 | ... |`.
262
+ 1. The entry of `CHANGELOG.md`, as `Round 262.`.
263
+ 2. The Changelog row of `docs/specs/spec.md`, as `| 262 | 2026-08-15 | ... |`.
258
264
 
259
265
  `tests/test_changelog_round_agreement.py` holds the two records against each other, so an
260
- assignment that covers one file alone fails a case. An integration branch carries one
261
- `TBD` for each member that has not reached the gate, and every one of them is correct.
266
+ assignment that covers one file alone fails a case.
267
+
268
+ **#727 measured what the older model cost.** One issue recorded one round, so
269
+ `tests/test_round_entry_existence.py` failed every change set that edited a tracked file
270
+ and recorded no round. Each member therefore wrote the two most contended files of the
271
+ repository, and each member then conflicted with every other member on them. A read of
272
+ 2026-08-15 covered the last 300 commits of `master`. 192 of them touch
273
+ `docs/specs/spec.md` and 186 touch `CHANGELOG.md`, against 56 that touch a file under
274
+ `ja4plus/`.
275
+
276
+ **Warning: the 261 rounds already recorded stay exactly as they are.** They record past
277
+ measurements and `.claude/rules/ste.md` bars a rewrite of such a record. The new model
278
+ begins at the next round this project assigns.
279
+
280
+ ### Which change set the mandate reads
281
+
282
+ `tests/test_round_entry_existence.py` reads the branch the change set belongs to, and
283
+ `records_a_round` holds the condition. A branch whose name opens with `batch/` or `epic/`
284
+ records a round. Every other branch records none, and the reading refuses no round it
285
+ finds on one.
286
+
287
+ **The two prefixes match the two patterns of the base-branch filter above.** One model
288
+ therefore names an integration branch in the filter and in the mandate.
289
+
290
+ **The `test` job names that branch in `ROUND_ENTRY_BRANCH`, because a run of a pull request
291
+ checks out a detached merge commit.** `git rev-parse --abbrev-ref HEAD` then answers the
292
+ literal `HEAD` and names no branch. The case would then skip on every job, and `skip-gate`
293
+ would fail the run. The step `Name the branch the change set belongs to` reads
294
+ `github.head_ref` first and `github.ref_name` second. The two together name the head branch
295
+ on every event the workflow accepts.
296
+
297
+ ### One guard still takes a record from a member
298
+
299
+ **Warning: the routine `TBD` sweep goes out with the older model, and one exception keeps
300
+ the form.** A member that writes no round record leaves nothing to assign later, so the
301
+ sweep covers no member of a routine batch.
302
+
303
+ **`tests/test_breaking_change_record.py` requires a record from the member that trips it.**
304
+ It requires that `CHANGELOG.md` and `docs/specs/spec.md` each record the Python floor the
305
+ package states. #575 moved that floor and met the result: a reader that demanded a number
306
+ could not pass on the branch that made the move. Such a member writes `Round TBD.` and the
307
+ matching row, and the project manager assigns the number at the batch gate.
308
+
309
+ **#395 records why that guard stands.** A comparison between two records finds no change
310
+ that is absent from both of them. The move of the floor from 3.8 to 3.9 was such a
311
+ change. #727 therefore kept the guard, and it kept the `TBD` form the guard reads.
312
+
313
+ ### The race the older model measured
262
314
 
263
315
  **The round sequence is global to the repository, and a sub-merge is an event of one
264
- batch.** Two live integration branches therefore assign from one sequence at once, and
265
- neither branch reads the rows of the other until that other branch merges. #482 measured
316
+ batch.** Two live integration branches therefore assigned from one sequence at once, and
317
+ neither branch read the rows of the other until that other branch merged. #482 measured
266
318
  the result on 2026-08-10, at the sub-merge gate of #456.
267
319
 
268
320
  ```
@@ -270,9 +322,9 @@ AssertionError: the Changelog holds 169 rows and its highest round is 173
270
322
  assert 169 == 173
271
323
  ```
272
324
 
273
- **A project manager who assigns 168 and 169 on the second branch writes two rows numbered
274
- 168 and two numbered 169.** That state merges cleanly, and the rule above removes it
275
- rather than reports it.
325
+ **#727 removes that race for every routine member.** One batch writes one round at its own
326
+ gate. The project manager runs one gate at a time, so no second writer moves the sequence
327
+ between the read and the write.
276
328
 
277
329
  **Warning: a wrong pairing survives the row-count rule, so read the contiguity case
278
330
  instead.** `row count == highest round` reads a table that carries 168 twice and 170
@@ -281,7 +333,8 @@ pairing.
281
333
  `tests/test_specification_changelog.py::test_the_changelog_assigns_every_round_from_one_to_the_row_count`
282
334
  requires the rounds 1 to the row count, each on one row, and
283
335
  `test_the_row_count_rule_passes_on_a_table_that_repeats_a_round` holds the measurement of
284
- the older rule.
336
+ the older rule. **That case stays.** It caught the duplicate rounds of #482, and it is
337
+ cheap under the new model.
285
338
 
286
339
  **One integration branch at a time also removes the race, and this project declines that
287
340
  order.** It costs the concurrency the batch model exists to provide. #482 records the
@@ -62,9 +62,106 @@ method name plus an occurrence counter.
62
62
  method that emits more fingerprints than the reference is a defect, and so is one that
63
63
  emits fewer.
64
64
 
65
+ ## What a green conformance run does not measure
66
+
67
+ **Warning: a green conformance run states nothing about the frame that carries a value.**
68
+ The suite compares one value for each stream, and it never compares the packet that
69
+ produced it. #736 measured the limit on 2026-08-16 and this section records it.
70
+
71
+ **#606 moved seven values from one frame to another, and the suite discriminated none of
72
+ them.** That issue moved the QUIC `JA4L-S` emission from the packet that fills point `B`
73
+ to the packet that fills point `D`. #736 restored the point `B` emission and ran the suite
74
+ again. The three counts held at 1676 passed, 142 skipped and 138 xfailed under the change
75
+ and under the restored defect. These are the seven moves.
76
+
77
+ | Capture | The move |
78
+ |---|---|
79
+ | `chrome-cloudflare-quic-with-secrets.pcapng` | 49 to 52 |
80
+ | `ssh2.pcapng` | 1140 to 1147 |
81
+ | `tls3.pcapng` | 144 to 147 |
82
+ | `tls3.pcapng` | 149 to 153 |
83
+ | `tls3.pcapng` | 155 to 162 |
84
+ | `tls3.pcapng` | 159 to 167 |
85
+ | `tls3.pcapng` | 303 to 312 |
86
+
87
+ ### One of the four reference sources states a frame
88
+
89
+ **The conformance value comparison reads the one source that frames nothing.** A per-frame
90
+ comparison is possible only where a source states the frame, and #736 read all four.
91
+
92
+ | Source | States a frame | Values |
93
+ |---|---|---|
94
+ | FoxIO Python expected output, `tests/foxio_vectors/*.json` | No | 1203 |
95
+ | FoxIO Rust snapshots, `tests/foxio_vectors/rust_expected/*.snap` | No | 460 |
96
+ | Zeek baselines, `tests/foxio_vectors/zeek_expected/` | No | 7 baselines |
97
+ | FoxIO Wireshark dissector, `tests/foxio_vectors/wireshark_expected/*.json` | Yes, `frame.number` | 724 |
98
+
99
+ **The first three key a value on the stream or on the connection.** A Zeek baseline names
100
+ `orig_pkts` and `resp_pkts`, and each one counts the packets of a connection rather than
101
+ names one of them. **The dissector frames every value it writes**, and #736 measured 724
102
+ framed values and none unframed across 26 files.
103
+
104
+ **The conformance suite builds all 1203 of its value cases from the first source.** No case
105
+ of the suite can read a frame, because the source it reads states none.
106
+
107
+ ### Why the suite compares no frame
108
+
109
+ **The suite compares the FoxIO Python reference, and the dissector holds a different value
110
+ set.** A per-frame comparison reads a new source. It therefore adds no discrimination to
111
+ any case the suite runs today.
112
+
113
+ **#736 measured what that comparison would report on 2026-08-16.** The measurement ran the
114
+ `Processor` over each of the 26 captures this project holds, it numbered each packet, and
115
+ it read the 445 dissector values of a hashed form against the result. **Warning: the three
116
+ counts below are one measurement, and no case holds them.** Take them again before you
117
+ build on them.
118
+
119
+ | Reading | Values |
120
+ |---|---|
121
+ | The value agrees and the frame agrees | 206 |
122
+ | The value agrees and the frame differs | 136 |
123
+ | This project produces no such value | 103 |
124
+
125
+ **The 136 frame disagreements are values this project already produces correctly.** Each
126
+ one would enter `tests/foxio_deviations.json` as a new entry. Each entry would record a
127
+ difference this project has already ruled.
128
+
129
+ **The TCP `JA4L` and `JA4LS` values hold 70 of the 136.** The dissector writes each one on
130
+ a later frame than this project, and none of the 70 is a QUIC value. **A comparison whose
131
+ largest class is a recorded divergence measures the register and not the code.**
132
+
133
+ **The frame is an artifact of one dissector, and the value is what the standard defines.**
134
+ Two FoxIO implementations produce the same value on different packets, and neither one is
135
+ wrong. This project therefore declines the per-frame comparison in the conformance suite.
136
+
137
+ ### Where the frame is measured instead
138
+
139
+ **A frame this project rules on gets a case of its own.**
140
+ `tests/test_ja4l_quic_point_d_emission.py` holds the frame of every QUIC `JA4L` value
141
+ against `frame.number` of the dissector, and it runs in the unit suite. #736 restored the
142
+ point `B` emission and measured 8 failures in that module, against 0 in the conformance
143
+ suite.
144
+
145
+ **Warning: leave that module in the unit suite, and never move it to the conformance
146
+ suite.** The `test` job runs the unit suite on five environments. `.github/workflows/test.yml:86-91`
147
+ holds that matrix: `ubuntu-latest` on Python 3.10, 3.11, 3.12 and 3.13, and `macos-latest`
148
+ on Python 3.12. The `conformance` job runs the conformance suite on one environment. A move
149
+ would therefore take four readings away from the one frame guard this project holds. #736
150
+ declined the move on that reading.
151
+
152
+ **Warning: the count of five is the matrix after #575, and an older record states six.**
153
+ That issue dropped Python 3.9 on 2026-08-10. `.claude/rules/batch-gate.md` records a
154
+ measurement of that date over six jobs, and that record stays exactly as it is. Read the
155
+ workflow for the present count, and never a record of a past measurement.
156
+
157
+ **Warning: read a frame ruling against the unit suite, and never against the conformance
158
+ counts.** `tests/test_conformance_frame_discrimination.py` holds this whole reading. A
159
+ source that starts to state a frame therefore fails a case there, and it leaves no reader
160
+ with a stale rule.
161
+
65
162
  ## When the FoxIO reference holds a defect
66
163
 
67
- The FoxIO reference decides behaviour. It does not decide behaviour that the reference
164
+ The FoxIO reference decides behavior. It does not decide behavior that the reference
68
165
  itself produces by accident.
69
166
 
70
167
  **A defect is proven, never asserted.** Instrument the FoxIO implementation at the pinned
@@ -92,6 +189,116 @@ by analogy.
92
189
 
93
190
  Decided on 2026-08-07. #96, #97 and #105 are the first three.
94
191
 
192
+ ## What a delegated session may rule
193
+
194
+ **The user delegates a session to a project manager, and this section states what that
195
+ delegation permits.** The user granted two delegations, and both hold today. A question
196
+ that fails both belongs to the user.
197
+
198
+ | Delegation | Date | What it reaches | Where the delegation lives |
199
+ |---|---|---|---|
200
+ | The narrow delegation | 2026-08-12 | A schema violation. | `Crank-Git/ja4plus-go#246`, and #597 here |
201
+ | The recording delegation | 2026-08-15 | A reading this project already holds. | The ruling comments of #595, #607, #608 and #613 |
202
+
203
+ **A ruling lands in this repository and in the port together, or in neither.**
204
+ `.claude/rules/parity.md` of `Crank-Git/ja4plus-go` states that rule, and
205
+ `.claude/rules/rulings.md` of that repository holds the port half of the narrow
206
+ delegation.
207
+
208
+ ### The narrow delegation of 2026-08-12
209
+
210
+ **A schema violation has one right answer, and a reference split has none.** That sentence
211
+ is the boundary. The project manager rules a schema violation under this delegation. It
212
+ rules no reference split, and it rules no other question.
213
+
214
+ **A delegated ruling is a ruling, and never a reading.** A reading concludes what one
215
+ source states. A delegated ruling settles what this project does where a published FoxIO
216
+ value contradicts a published FoxIO rule. No source settles that question, because FoxIO
217
+ publishes both of them.
218
+
219
+ A project manager makes a delegated ruling only where every one of these is true.
220
+
221
+ 1. A published FoxIO rule states the answer. The rule reaches the material under
222
+ `technical_details/`, the transcription of it under `docs/specs/foxio/<METHOD>.md`, or
223
+ a FoxIO reference implementation.
224
+ 2. Every FoxIO implementation enforces that rule. One implementation that departs makes
225
+ the question a reference split, and this delegation bars a reference split.
226
+ 3. A recorded measurement proves the violation, and each citation names a file and a line.
227
+ `## When the FoxIO reference holds a defect` above states that a defect is proven and
228
+ never asserted.
229
+ 4. The record of the ruling carries a provisional marker, and it names the issue.
230
+ 5. The record names a reversal path, so the user reverses the ruling with one action.
231
+
232
+ **A question that fails one condition leaves this delegation.** The project manager reads
233
+ it against the recording delegation below.
234
+
235
+ ### The recording delegation of 2026-08-15
236
+
237
+ **The user granted a second delegation on 2026-08-15.** Four ruling comments each state it
238
+ in these words: #595, #607, #608 and #613. This file quotes them, and it rewrites no word
239
+ of the delegation.
240
+
241
+ **Warning: never open a line of this file with an issue number.** The section reader of
242
+ `tests/test_ported_pattern_cost.py` reads a line that opens with `#` as a heading, and it
243
+ then stops at that line. A paragraph below such a line reaches no case.
244
+
245
+ > The maintainer granted a delegated session this carve-out on 2026-08-15: a delegated
246
+ > session may rule where the decision preserves the present behaviour, moves no
247
+ > fingerprint, and records an existing reading as a divergence register row. A decision
248
+ > that moves a value, that forks from `ja4plus-go`, or that changes the scope of the
249
+ > project stays with the maintainer.
250
+
251
+ **The delegation states three limits, and a permitted question meets every one of them.**
252
+
253
+ - The ruling keeps the present behavior of this project.
254
+ - The ruling moves no fingerprint value.
255
+ - The ruling writes one row of the divergence register.
256
+
257
+ **A ruling that moves a value stays with the user.** A ruling that parts this project from
258
+ the port stays with the user. A ruling that changes the scope of this project stays with
259
+ the user.
260
+
261
+ **The project manager ruled each of the four issues under this delegation on 2026-08-15,
262
+ and no line of a fingerprinter moved.** #595 records the JA4L eviction of the QUIC path, #607 records the
263
+ JA4H emission frame, #608 records the JA4SSH packet selection, and #613 records the one
264
+ QUIC packet each datagram fills point C from.
265
+
266
+ ### How the two delegations relate
267
+
268
+ **The two delegations reach different questions, and neither one repeals the other.**
269
+
270
+ - The narrow delegation settles what this project does, and it can move a fingerprint
271
+ value. It therefore bars a reference split: a wrong answer there makes this project
272
+ answer differently from a FoxIO implementation on the same bytes.
273
+ - The recording delegation records what this project already does, and it moves no
274
+ fingerprint value. A reference split therefore reaches it. #595 and #613 are each such a
275
+ question, and each ruling of 2026-08-15 left every line of the fingerprinter as it
276
+ stood.
277
+ - **A reference split is never delegable as a schema violation.** Condition 2 of the
278
+ narrow delegation bars it, and no reading of the evidence removes that bar.
279
+ - **Every delegated ruling is provisional under both delegations**, and each one names a
280
+ reversal path.
281
+
282
+ **A question that fails both delegations belongs to the user.** The project manager labels
283
+ that issue `status:needs-feedback`. It records the reading it holds. It builds nothing that
284
+ depends on the answer.
285
+
286
+ **The user confirms a delegated ruling, or reverses it.** A delegated ruling that the user
287
+ has not confirmed stays provisional. A later reader reads a provisional ruling as
288
+ unconfirmed, and never as settled.
289
+
290
+ ### The worked example both repositories share
291
+
292
+ `Crank-Git/ja4plus-go#223` is the first delegated ruling, and the user confirmed it on
293
+ 2026-08-12. A published FoxIO value contradicts a rule that four implementations enforce.
294
+ `zeek/ja4ssh/main.zeek:63`, `wireshark/source/packet-ja4.c:400`, `rust/ja4/src/ssh.rs:284`
295
+ and `python/ja4ssh.py:51` each state that the JA4SSH mode is `0` when the side sent no SSH
296
+ packet. `python/test/testdata/ssh-scp-1050.pcap.json` holds `c112s1460_c0s200_c36s0`,
297
+ which pairs a client mode of `112` with a client packet count of `0`.
298
+
299
+ **This project already reads the mode of the window alone**, and #96 holds that rule. The
300
+ narrow delegation therefore moved no fingerprint here, and it moved no exported name.
301
+
95
302
  ## When the FoxIO material is ambiguous
96
303
 
97
304
  Eleven of the twelve methods are published as an image. Only JA4 holds a complete text
@@ -350,6 +557,33 @@ Where FoxIO specifies nothing — a field name, a default, a subcommand — the
350
557
  `Crank-Git/ja4plus-go` has already shipped a choice. Adopt it. Where FoxIO does specify,
351
558
  FoxIO wins, even against the port. `docs/specs/spec.md` holds the divergence register.
352
559
 
560
+ ### A pattern the port ships carries no statement of its cost
561
+
562
+ **Parity rule 2 adopts the interface the port ships. It adopts no statement of cost.** Go
563
+ runs a finite automaton, and it backtracks nowhere. Python `re` backtracks. The two
564
+ languages therefore read one pattern at two costs. The rule that carries the shape carries
565
+ no proof that the shape is safe against hostile input.
566
+
567
+ **Warning: a pattern that is safe in the port is a pattern this project has not
568
+ measured.** Measure a regular expression this project takes from the port against hostile
569
+ input before it lands. State the measurement where the change records its evidence.
570
+
571
+ **#612 followed parity rule 2 literally, and it shipped a backtracking defect to a
572
+ branch.** Its own self-review found the defect and measured it. `GET a` plus 32000 spaces
573
+ plus `HTTPX` cost **3017.9 milliseconds** under the ported form. The repaired form reads
574
+ the same payload at **0.630 milliseconds**. `REQUEST_LINE_LIMIT` of
575
+ `ja4plus/utils/http_utils.py` reads 8192. `is_http_request` reads that many bytes of every
576
+ TCP payload, so one crafted packet bought about 200 milliseconds of processor time.
577
+
578
+ **The rule reaches every construct whose cost differs between a finite automaton and a
579
+ backtracking engine.** A regular expression is the case this project has met.
580
+
581
+ **Warning: an atomic group states the bound directly, and this project cannot write
582
+ one.** Python accepts `(?>...)` from release 3.11, and `pyproject.toml:24` reads
583
+ `requires-python = ">=3.10"`. A repair that raises the floor of the package is a ruling
584
+ for the user. Verified against https://docs.python.org/3/library/re.html (retrieved
585
+ 2026-08-15).
586
+
353
587
  ## Measuring coverage
354
588
 
355
589
  Use the **directory** form, `--cov=ja4plus`. For one file, use the **path** form,
@@ -359,6 +593,6 @@ Use the **directory** form, `--cov=ja4plus`. For one file, use the **path** form
359
593
  QUIC cases fail, and the cause is not in this project. `coverage` resolves a dotted source
360
594
  name inside a `sys_modules_saved()` block, which imports `ja4plus`, scapy and
361
595
  `cryptography` and then deletes them. The second `cryptography` import builds Python
362
- classes the loaded Rust extension does not recognise, so every AES cipher raises
596
+ classes the loaded Rust extension does not recognize, so every AES cipher raises
363
597
  `UnsupportedAlgorithm`, and the second scapy import changes the dissection of a committed
364
598
  capture. #177 holds the measurement, and Changelog round 47 records it.
@@ -42,7 +42,7 @@ Code inside this repository is different. Read the code.
42
42
 
43
43
  ## Rules specific to this project
44
44
 
45
- **FoxIO is the authority on behaviour.** The specification decides intent and schema. The
45
+ **FoxIO is the authority on behavior.** The specification decides intent and schema. The
46
46
  vectors decide the exact bytes where intent runs out. A provable reference defect is
47
47
  declined and recorded.
48
48