sbxloop 0.7.0__tar.gz → 0.7.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 (64) hide show
  1. {sbxloop-0.7.0 → sbxloop-0.7.2}/PKG-INFO +1 -1
  2. sbxloop-0.7.0/src/sbxloop/_vendor/sbxloop_worker-0.7.0-py3-none-any.whl → sbxloop-0.7.2/src/sbxloop/_vendor/sbxloop_worker-0.7.2-py3-none-any.whl +0 -0
  3. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/_version.py +2 -2
  4. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/cli/app.py +2 -2
  5. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/engine/prompts/plan.md +8 -1
  6. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/verifylint.py +23 -1
  7. {sbxloop-0.7.0 → sbxloop-0.7.2}/.gitignore +0 -0
  8. {sbxloop-0.7.0 → sbxloop-0.7.2}/README.md +0 -0
  9. {sbxloop-0.7.0 → sbxloop-0.7.2}/hatch_build.py +0 -0
  10. {sbxloop-0.7.0 → sbxloop-0.7.2}/pyproject.toml +0 -0
  11. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/__init__.py +0 -0
  12. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/cli/__init__.py +0 -0
  13. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/cli/doctor.py +0 -0
  14. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/cli/models.py +0 -0
  15. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/cli/tui.py +0 -0
  16. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/config.py +0 -0
  17. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/daemon/__init__.py +0 -0
  18. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/daemon/backlog.py +0 -0
  19. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/daemon/control.py +0 -0
  20. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/daemon/discord.py +0 -0
  21. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/daemon/discord_format.py +0 -0
  22. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/daemon/github.py +0 -0
  23. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/daemon/loop.py +0 -0
  24. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/daemon/model.py +0 -0
  25. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/daemon/paths.py +0 -0
  26. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/daemon/sources.py +0 -0
  27. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/daemon/store.py +0 -0
  28. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/deliver.py +0 -0
  29. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/engine/__init__.py +0 -0
  30. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/engine/engine.py +0 -0
  31. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/engine/model.py +0 -0
  32. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/engine/phases.py +0 -0
  33. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/engine/prompts/__init__.py +0 -0
  34. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/engine/prompts/decompose.md +0 -0
  35. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/engine/prompts/execute.md +0 -0
  36. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/engine/prompts/scrutinize.md +0 -0
  37. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/engine/prompts/steer.md +0 -0
  38. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/engine/prompts/validate.md +0 -0
  39. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/engine/store.py +0 -0
  40. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/errors.py +0 -0
  41. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/events.py +0 -0
  42. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/gc.py +0 -0
  43. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/gh/__init__.py +0 -0
  44. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/gh/ops.py +0 -0
  45. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/gh/reporter.py +0 -0
  46. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/hostgit.py +0 -0
  47. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/ids.py +0 -0
  48. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/policy.py +0 -0
  49. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/py.typed +0 -0
  50. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/sbx/__init__.py +0 -0
  51. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/sbx/bake.py +0 -0
  52. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/sbx/cli.py +0 -0
  53. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/sbx/conformance.py +0 -0
  54. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/sbx/models.py +0 -0
  55. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/sbx/pair.py +0 -0
  56. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/sbx/parse.py +0 -0
  57. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/sbx/provision.py +0 -0
  58. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/sbx/prune.py +0 -0
  59. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/sbx/sandbox.py +0 -0
  60. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/sbx/secretstate.py +0 -0
  61. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/toolchains.py +0 -0
  62. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/worker/__init__.py +0 -0
  63. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/worker/client.py +0 -0
  64. {sbxloop-0.7.0 → sbxloop-0.7.2}/src/sbxloop/worker/wheel.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sbxloop
3
- Version: 0.7.0
3
+ Version: 0.7.2
4
4
  Summary: Agentic loop orchestration on Docker Sandboxes (sbx) with isolated credential domains
5
5
  Project-URL: Homepage, https://github.com/brettbergin/sbxloop
6
6
  Project-URL: Repository, https://github.com/brettbergin/sbxloop
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.7.0'
22
- __version_tuple__ = version_tuple = (0, 7, 0)
21
+ __version__ = version = '0.7.2'
22
+ __version_tuple__ = version_tuple = (0, 7, 2)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -2003,8 +2003,8 @@ mem_abort = 0.0
2003
2003
  # open a per-run tracking issue. For a design/discussion tracker set both
2004
2004
  # false: the source issue gets the summary + delivered_label and stays open
2005
2005
  # until a human merges the PR, and no extra tracking issue is opened.
2006
- # close_on_success = true
2007
- # tracking_issue = true
2006
+ # close_on_success = true # false leaves the issue open for a human to close on merge
2007
+ # tracking_issue = true # false skips the per-run tracking issue
2008
2008
  # max_runs_per_day = 12 # rolling 24h window, persisted across restarts
2009
2009
  # max_attempts_per_item = 2
2010
2010
  # max_resumes_per_item = 2 # interrupted runs resumed at most this often per item
@@ -105,7 +105,14 @@ literal text (a grep for an escape code never matches, and nothing says
105
105
  why), while `[[ ]]`, `source`, `declare`/`local`, `pushd`/`popd` fail as
106
106
  unknown commands and here-strings are a syntax error. Write portable shell:
107
107
  `[ ]` not `[[ ]]`, `printf` for escape sequences, pipes instead of
108
- here-strings, `.` instead of `source`. Bashisms are rejected mechanically. And prefer to verify *behavior* through the project's test
108
+ here-strings, `.` instead of `source`. Bashisms are rejected mechanically.
109
+ Never wrap a check in its own `sh -c "..."` / `bash -c` — each command is
110
+ already run that way, and any dollar expansion (`$$?`, an awk field) inside
111
+ the wrapper's double quotes is consumed by the outer shell before the inner
112
+ one ever sees it — a wrapped `git status | awk` field-print guard printed
113
+ whole lines and failed every revision of a correct change. Write the
114
+ pipeline directly. And
115
+ prefer to verify *behavior* through the project's test
109
116
  runner rather than shell pipelines: asserting on bytes, escape sequences,
110
117
  exact whitespace, or exit codes is trivial and exact inside a test file
111
118
  (`assert "\x1b[31m" in captured.out`) and fragile as a `grep`/`od`
@@ -177,6 +177,14 @@ def _strip_quoted(command: str, *, keep_ansi_c: bool = False) -> str:
177
177
  return "".join(out)
178
178
 
179
179
 
180
+ # `sh -c "..."` / `bash -c '...'` at command position: a shell inside the
181
+ # shell the runner already provides. Matched on the raw command so quoting
182
+ # does not hide it.
183
+ _NESTED_SHELL = re.compile(
184
+ r"(?:^|[|&;(]\s*)(?:/bin/|/usr/bin/)?(?:sh|bash|dash|zsh)\s+-[a-zA-Z]*c\b"
185
+ )
186
+
187
+
180
188
  def bashisms(command: str) -> list[str]:
181
189
  """Portable-shell violations in one verify command (empty = clean)."""
182
190
  problems: list[str] = []
@@ -191,12 +199,26 @@ def bashisms(command: str) -> list[str]:
191
199
  for token, what, rewrite in _BASHISM_OPERATORS[1:]:
192
200
  if token in unquoted:
193
201
  problems.append(f"uses {what} — verify commands run under `sh -c`; {rewrite}")
194
- for head in command_heads(command):
202
+ heads = command_heads(command)
203
+ for head in heads:
195
204
  if head in _BASHISM_COMMANDS:
196
205
  what, rewrite = _BASHISM_COMMANDS[head]
197
206
  problems.append(
198
207
  f"invokes {what}, which fails as an unknown command under `sh -c`; {rewrite}"
199
208
  )
209
+ if _NESTED_SHELL.search(command):
210
+ # Field failure r7ef26eht (first sbxloop-on-sbxloop run): the plan
211
+ # wrapped a pipeline as `sh -c "... awk '{print $2}' ..."`. The
212
+ # runner already executes each verify command under `sh -c`, so the
213
+ # OUTER shell expanded the double-quoted `$2` to nothing, awk printed
214
+ # whole lines, the allowlist grep matched none of them, and a correct
215
+ # change failed verification three revisions and a replan in a row.
216
+ problems.append(
217
+ "wraps the check in a nested `sh -c`/`bash -c` string — verify commands "
218
+ "already run under `sh -c`, and `$` expansions inside the wrapper's double "
219
+ "quotes are consumed by the outer shell; write the pipeline directly, "
220
+ "unwrapped"
221
+ )
200
222
  return problems
201
223
 
202
224
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes