agentic-dev-loop 0.15.0__tar.gz → 0.15.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. {agentic_dev_loop-0.15.0/src/agentic_dev_loop.egg-info → agentic_dev_loop-0.15.1}/PKG-INFO +1 -1
  2. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/pyproject.toml +1 -1
  3. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/viz/server.py +18 -5
  4. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1/src/agentic_dev_loop.egg-info}/PKG-INFO +1 -1
  5. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_viz.py +40 -0
  6. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/LICENSE +0 -0
  7. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/README.md +0 -0
  8. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/setup.cfg +0 -0
  9. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/__init__.py +0 -0
  10. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/core/__init__.py +0 -0
  11. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/core/config.py +0 -0
  12. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/core/failure.py +0 -0
  13. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/core/paths.py +0 -0
  14. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/core/profiles/__init__.py +0 -0
  15. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/core/profiles/data/dotnet-node.yaml +0 -0
  16. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/core/profiles/data/python.yaml +0 -0
  17. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/core/results.py +0 -0
  18. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/core/verdict.py +0 -0
  19. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/__init__.py +0 -0
  20. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/actions.py +0 -0
  21. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/agent_error_guide.md +0 -0
  22. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/agent_error_guide.py +0 -0
  23. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/agents.py +0 -0
  24. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/allowlist.py +0 -0
  25. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/brief_coverage.py +0 -0
  26. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/brief_prompt.md +0 -0
  27. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/brief_prompt.py +0 -0
  28. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/cli.py +0 -0
  29. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/completion.py +0 -0
  30. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/containers.py +0 -0
  31. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/copilot_auth.py +0 -0
  32. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/deps.py +0 -0
  33. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/dryrun.py +0 -0
  34. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/egress_policy.py +0 -0
  35. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/egress_proxy.py +0 -0
  36. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/extensions/__init__.py +0 -0
  37. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/extensions/aidlc.py +0 -0
  38. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/forbidden.py +0 -0
  39. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/graph.py +0 -0
  40. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/hostboundary.py +0 -0
  41. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/isolation.py +0 -0
  42. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/llm.py +0 -0
  43. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/loaders.py +0 -0
  44. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/mounts.py +0 -0
  45. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/onboarding/__init__.py +0 -0
  46. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/onboarding/checks.py +0 -0
  47. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/onboarding/doctor.py +0 -0
  48. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/onboarding/init.py +0 -0
  49. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/onboarding/recipes/dotnet-node.yaml +0 -0
  50. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/onboarding/recipes/python.yaml +0 -0
  51. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/onboarding/recipes.py +0 -0
  52. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/onboarding/templates/dev.agent.md +0 -0
  53. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/onboarding/templates/env.example +0 -0
  54. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/onboarding/templates/qa.agent.md +0 -0
  55. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/onboarding/templates/review.agent.md +0 -0
  56. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/onboarding/templates.py +0 -0
  57. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/onboarding/upgrade.py +0 -0
  58. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/outputs.py +0 -0
  59. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/presets.py +0 -0
  60. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/proc.py +0 -0
  61. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/provider_wire.py +0 -0
  62. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/registry.py +0 -0
  63. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/run_status.py +0 -0
  64. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/run_status_command.md +0 -0
  65. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/seccomp/__init__.py +0 -0
  66. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/seccomp/chromium-userns.json +0 -0
  67. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/secrets.py +0 -0
  68. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/stages.py +0 -0
  69. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/step_workspace.py +0 -0
  70. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/engine/sync_back.py +0 -0
  71. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/py.typed +0 -0
  72. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/reporting/__init__.py +0 -0
  73. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/reporting/metrics.py +0 -0
  74. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/reporting/render.py +0 -0
  75. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/reporting/sources.py +0 -0
  76. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/viz/__init__.py +0 -0
  77. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/viz/layout.py +0 -0
  78. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/viz/page.py +0 -0
  79. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop/viz/runs_view.py +0 -0
  80. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop.egg-info/SOURCES.txt +0 -0
  81. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop.egg-info/dependency_links.txt +0 -0
  82. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop.egg-info/entry_points.txt +0 -0
  83. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop.egg-info/requires.txt +0 -0
  84. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/src/agentic_dev_loop.egg-info/top_level.txt +0 -0
  85. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_029_merge_on_conflict.py +0 -0
  86. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_033_brief_step.py +0 -0
  87. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_035_finalize_nudge.py +0 -0
  88. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_036_banned_commands_and_lane_boundary.py +0 -0
  89. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_041_brief_coverage.py +0 -0
  90. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_046_brief_prompt.py +0 -0
  91. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_055_cli.py +0 -0
  92. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_055_doctor.py +0 -0
  93. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_055_init.py +0 -0
  94. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_055_profile_loading.py +0 -0
  95. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_055_profiles.py +0 -0
  96. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_055_recipes.py +0 -0
  97. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_055_schema_lock.py +0 -0
  98. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_055_templates.py +0 -0
  99. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_055_upgrade.py +0 -0
  100. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_actions.py +0 -0
  101. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_agent_error_guide.py +0 -0
  102. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_agents.py +0 -0
  103. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_aidlc.py +0 -0
  104. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_build_viz_page.py +0 -0
  105. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_cli.py +0 -0
  106. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_config.py +0 -0
  107. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_containers.py +0 -0
  108. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_copilot_auth.py +0 -0
  109. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_copilot_cli.py +0 -0
  110. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_deps.py +0 -0
  111. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_dry_run.py +0 -0
  112. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_e2e_fixtures.py +0 -0
  113. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_egress_proxy.py +0 -0
  114. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_graph.py +0 -0
  115. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_hostboundary.py +0 -0
  116. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_hostboundary_integration.py +0 -0
  117. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_isolation.py +0 -0
  118. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_isolation_integration.py +0 -0
  119. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_isolation_pbt.py +0 -0
  120. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_llm.py +0 -0
  121. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_loaders.py +0 -0
  122. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_module_contract.py +0 -0
  123. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_outputs.py +0 -0
  124. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_outputs_pbt.py +0 -0
  125. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_proc.py +0 -0
  126. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_provider_wire.py +0 -0
  127. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_publish.py +0 -0
  128. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_qa_prompt.py +0 -0
  129. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_registry.py +0 -0
  130. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_repo_ascii.py +0 -0
  131. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_results.py +0 -0
  132. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_run_status.py +0 -0
  133. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_runs_js_filters.py +0 -0
  134. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_runs_js_key_stats.py +0 -0
  135. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_runs_js_metrics.py +0 -0
  136. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_runs_layout.py +0 -0
  137. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_runs_layout_key_stats.py +0 -0
  138. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_sdk_compat.py +0 -0
  139. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_secrets.py +0 -0
  140. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_secrets_integration.py +0 -0
  141. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_secrets_pipeline.py +0 -0
  142. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_source_resolution.py +0 -0
  143. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_stages.py +0 -0
  144. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_step_workspace.py +0 -0
  145. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_sync_back.py +0 -0
  146. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_unit11.py +0 -0
  147. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_unit11_pbt.py +0 -0
  148. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_unit12_reporting.py +0 -0
  149. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_unit13_runs_viz.py +0 -0
  150. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_unit5_config.py +0 -0
  151. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_unit5_pbt.py +0 -0
  152. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_unit5_pure.py +0 -0
  153. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_verdict.py +0 -0
  154. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_viz_demo.py +0 -0
  155. {agentic_dev_loop-0.15.0 → agentic_dev_loop-0.15.1}/tests/test_viz_layout.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-dev-loop
3
- Version: 0.15.0
3
+ Version: 0.15.1
4
4
  Summary: Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase.
5
5
  Author: Luke Wilson
6
6
  License-Expression: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agentic-dev-loop"
7
- version = "0.15.0"
7
+ version = "0.15.1"
8
8
  description = "Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -168,13 +168,20 @@ class VizBus:
168
168
  (lifecycle) and console events merged in publish order, so a browser tab
169
169
  opened mid-run catches up without console volume evicting lifecycle
170
170
  events. Heartbeats are delivered live but never replayed.
171
+
172
+ Console retention is per node: ``console_maxlen`` bounds the most recent
173
+ console lines retained for each publishing node (and for one bucket
174
+ holding unattributed, node-less output), not the run as a whole, so every
175
+ step's log replays on connect instead of only the current step's.
176
+ Lifecycle retention remains a single global ``maxlen`` backlog.
171
177
  """
172
178
 
173
179
  def __init__(self, maxlen: int = 500, console_maxlen: int = 500) -> None:
174
180
  self._subscribers: set[queue.Queue] = set()
175
181
  self._lock = threading.Lock()
176
182
  self._backlog: deque = deque(maxlen=maxlen)
177
- self._console_backlog: deque = deque(maxlen=console_maxlen)
183
+ self._console_maxlen = console_maxlen
184
+ self._console_backlogs: dict[str | None, deque] = {}
178
185
  self._seq = 0
179
186
 
180
187
  def publish(self, event: dict) -> None:
@@ -183,7 +190,12 @@ class VizBus:
183
190
  seq = self._seq
184
191
  etype = event.get("type")
185
192
  if etype == "console":
186
- self._console_backlog.append((seq, event))
193
+ node = event.get("node")
194
+ bucket = self._console_backlogs.get(node)
195
+ if bucket is None:
196
+ bucket = deque(maxlen=self._console_maxlen)
197
+ self._console_backlogs[node] = bucket
198
+ bucket.append((seq, event))
187
199
  elif etype != "heartbeat":
188
200
  self._backlog.append((seq, event))
189
201
  subscribers = list(self._subscribers)
@@ -193,9 +205,10 @@ class VizBus:
193
205
  def subscribe(self) -> queue.Queue:
194
206
  sub: queue.Queue = queue.Queue()
195
207
  with self._lock:
196
- merged = sorted(
197
- list(self._backlog) + list(self._console_backlog),
198
- key=lambda item: item[0])
208
+ retained = list(self._backlog)
209
+ for bucket in self._console_backlogs.values():
210
+ retained.extend(bucket)
211
+ merged = sorted(retained, key=lambda item: item[0])
199
212
  for _seq, event in merged:
200
213
  sub.put(event)
201
214
  self._subscribers.add(sub)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-dev-loop
3
- Version: 0.15.0
3
+ Version: 0.15.1
4
4
  Summary: Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase.
5
5
  Author: Luke Wilson
6
6
  License-Expression: Apache-2.0
@@ -311,6 +311,46 @@ class TestVizBus:
311
311
  events.append(q.get_nowait())
312
312
  assert [e["text"] for e in events] == [3, 4]
313
313
 
314
+ def test_console_replay_retains_each_node_separately(self):
315
+ bus = VizBus()
316
+ bus.publish({"type": "run_started"})
317
+ for i in range(600):
318
+ bus.publish({"type": "console", "node": "dev", "text": i})
319
+ for i in range(600):
320
+ bus.publish({"type": "console", "node": "qa", "text": i})
321
+ bus.publish({"type": "node_started", "node": "qa"})
322
+ q = bus.subscribe()
323
+ events = []
324
+ while not q.empty():
325
+ events.append(q.get_nowait())
326
+ assert [e["type"] for e in events] == \
327
+ ["run_started"] + ["console"] * 1000 + ["node_started"]
328
+ dev_texts = [e["text"] for e in events
329
+ if e["type"] == "console" and e["node"] == "dev"]
330
+ qa_texts = [e["text"] for e in events
331
+ if e["type"] == "console" and e["node"] == "qa"]
332
+ assert dev_texts == list(range(100, 600))
333
+ assert qa_texts == list(range(100, 600))
334
+
335
+ def test_console_replay_merges_nodes_in_publish_order(self):
336
+ bus = VizBus()
337
+ published = [
338
+ {"type": "console", "node": "dev", "text": "a0"},
339
+ {"type": "node_started", "node": "dev"},
340
+ {"type": "console", "node": "qa", "text": "b0"},
341
+ {"type": "console", "node": "dev", "text": "a1"},
342
+ {"type": "console", "node": "qa", "text": "b1"},
343
+ {"type": "node_finished", "node": "dev"},
344
+ {"type": "console", "node": "dev", "text": "a2"},
345
+ ]
346
+ for event in published:
347
+ bus.publish(event)
348
+ q = bus.subscribe()
349
+ events = []
350
+ while not q.empty():
351
+ events.append(q.get_nowait())
352
+ assert events == published
353
+
314
354
  def test_heartbeat_live_only_not_replayed(self):
315
355
  bus = VizBus()
316
356
  bus.publish({"type": "heartbeat", "node": "dev", "elapsed_seconds": 1})