paircode 0.11.5__tar.gz → 0.11.7__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 (29) hide show
  1. {paircode-0.11.5 → paircode-0.11.7}/PKG-INFO +1 -1
  2. {paircode-0.11.5 → paircode-0.11.7}/pyproject.toml +1 -1
  3. {paircode-0.11.5 → paircode-0.11.7}/src/paircode/__init__.py +1 -1
  4. {paircode-0.11.5 → paircode-0.11.7}/src/paircode/cli.py +7 -1
  5. {paircode-0.11.5 → paircode-0.11.7}/src/paircode/templates/claude/commands/paircode.md +1 -0
  6. {paircode-0.11.5 → paircode-0.11.7}/tests/test_cli_smoke.py +37 -0
  7. {paircode-0.11.5 → paircode-0.11.7}/.gitignore +0 -0
  8. {paircode-0.11.5 → paircode-0.11.7}/LICENSE +0 -0
  9. {paircode-0.11.5 → paircode-0.11.7}/README.md +0 -0
  10. {paircode-0.11.5 → paircode-0.11.7}/diary/001-step-a-architecture.md +0 -0
  11. {paircode-0.11.5 → paircode-0.11.7}/diary/002-v0.10-release-pipeline.md +0 -0
  12. {paircode-0.11.5 → paircode-0.11.7}/diary/003-arch-b-pivot-grappling.md +0 -0
  13. {paircode-0.11.5 → paircode-0.11.7}/src/paircode/__main__.py +0 -0
  14. {paircode-0.11.5 → paircode-0.11.7}/src/paircode/converge.py +0 -0
  15. {paircode-0.11.5 → paircode-0.11.7}/src/paircode/detect.py +0 -0
  16. {paircode-0.11.5 → paircode-0.11.7}/src/paircode/handshake.py +0 -0
  17. {paircode-0.11.5 → paircode-0.11.7}/src/paircode/installer.py +0 -0
  18. {paircode-0.11.5 → paircode-0.11.7}/src/paircode/runner.py +0 -0
  19. {paircode-0.11.5 → paircode-0.11.7}/src/paircode/state.py +0 -0
  20. {paircode-0.11.5 → paircode-0.11.7}/src/paircode/templates/FOCUS.md +0 -0
  21. {paircode-0.11.5 → paircode-0.11.7}/src/paircode/templates/JOURNEY.md +0 -0
  22. {paircode-0.11.5 → paircode-0.11.7}/src/paircode/templates/codex/commands/paircode.md +0 -0
  23. {paircode-0.11.5 → paircode-0.11.7}/src/paircode/templates/gemini/commands/paircode.toml +0 -0
  24. {paircode-0.11.5 → paircode-0.11.7}/src/paircode/templates/peers.yaml +0 -0
  25. {paircode-0.11.5 → paircode-0.11.7}/src/paircode/util.py +0 -0
  26. {paircode-0.11.5 → paircode-0.11.7}/tests/__init__.py +0 -0
  27. {paircode-0.11.5 → paircode-0.11.7}/tests/test_converge.py +0 -0
  28. {paircode-0.11.5 → paircode-0.11.7}/tests/test_smoke.py +0 -0
  29. {paircode-0.11.5 → paircode-0.11.7}/tests/test_state.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: paircode
3
- Version: 0.11.5
3
+ Version: 0.11.7
4
4
  Summary: Adversarial journey framework — orchestrate multiple LLM peers through research, plan, and execute stages with file-trace peer review.
5
5
  Project-URL: Homepage, https://github.com/starshipagentic/paircode
6
6
  Project-URL: Repository, https://github.com/starshipagentic/paircode
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "paircode"
7
- version = "0.11.5"
7
+ version = "0.11.7"
8
8
  description = "Adversarial journey framework — orchestrate multiple LLM peers through research, plan, and execute stages with file-trace peer review."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -2,4 +2,4 @@
2
2
 
3
3
  See README.md and diary/001-step-a-architecture.md for the full design.
4
4
  """
5
- __version__ = "0.11.4"
5
+ __version__ = "0.11.6"
@@ -115,9 +115,15 @@ def ensure_scaffold() -> None:
115
115
  proposed = propose_roster()
116
116
  if proposed:
117
117
  write_peers(state, proposed_as_yaml_dicts(proposed))
118
- ensure_peer_dirs(state, proposed)
118
+ peers = proposed_as_yaml_dicts(proposed)
119
119
  proposed_count = len(proposed)
120
120
 
121
+ # Always reconcile sandbox dirs with the roster-of-record (peers.yaml).
122
+ # Idempotent via mkdir(exist_ok=True); recreates any missing peer sandbox.
123
+ # Without this, roster peers added after first init (or dropped by a rename)
124
+ # never get their sandbox dir back.
125
+ ensure_peer_dirs(state, peers)
126
+
121
127
  if did_init:
122
128
  click.echo(f"init {state.root}")
123
129
  if proposed_count:
@@ -165,6 +165,7 @@ Keep it under 200 words. Brief, honest, actionable.
165
165
  - **Never skip Step 1.** The focus and roster helpers do the work Python is better at.
166
166
  - **Never invent peer commands.** Always route through `paircode invoke <id> "..." --out <path>` — that's how cliworker's speed flags, MCP strip, and output tracing work.
167
167
  - **Never pass the interactive Claude session as a peer by mistake.** `paircode roster --alpha claude` handles that for you.
168
+ - **Fire every peer in `paircode roster`** unless the user's prompt explicitly passes `--peer`/`--peers`. don't quarantine on prior failures.
168
169
  - **Peer sandboxes are always available, never required.** Use them when it materially sharpens your answer, not by default.
169
170
  - **The user's prompt is implicit go-ahead for the whole `{flow}`.** Do not pause for permission between stages. If you must stop early, say so explicitly in the final report with the reason.
170
171
  - **No AI attribution** in any artifact you write (consensus.md, alpha-*.md). See the maintainer's CLAUDE.md.
@@ -126,6 +126,43 @@ def test_ensure_scaffold_is_idempotent(tmp_path, monkeypatch):
126
126
  assert second.output.strip() == ""
127
127
 
128
128
 
129
+ def test_ensure_scaffold_recreates_missing_peer_sandbox(tmp_path, monkeypatch):
130
+ """Roster-of-record wins. If peers.yaml has a peer but its sandbox dir
131
+ went missing (rename, manual rm, whatever), ensure-scaffold must recreate
132
+ it on next run — idempotently."""
133
+ import shutil
134
+
135
+ from paircode.handshake import ProposedPeer
136
+
137
+ monkeypatch.chdir(tmp_path)
138
+ peers = [
139
+ ProposedPeer(id="peer-a-codex", cli="codex", priority="high", notes=""),
140
+ ProposedPeer(id="peer-b-gemini", cli="gemini", priority="low", notes=""),
141
+ ]
142
+ monkeypatch.setattr("paircode.cli.propose_roster", lambda: peers)
143
+ runner = CliRunner()
144
+ runner.invoke(main, ["ensure-scaffold"])
145
+
146
+ sandbox = tmp_path / ".paircode" / "sandbox"
147
+ assert (sandbox / "peer-a-codex").is_dir()
148
+ assert (sandbox / "peer-b-gemini").is_dir()
149
+
150
+ # Simulate the real-world drift: gemini's sandbox gets deleted.
151
+ shutil.rmtree(sandbox / "peer-b-gemini")
152
+ assert not (sandbox / "peer-b-gemini").exists()
153
+
154
+ # Re-running ensure-scaffold must put it back. Roster (peers.yaml) is
155
+ # the source of truth — propose_roster() returning the same list should
156
+ # NOT be required for reconciliation.
157
+ monkeypatch.setattr("paircode.cli.propose_roster", lambda: [])
158
+ result = runner.invoke(main, ["ensure-scaffold"])
159
+ assert result.exit_code == 0, result.output
160
+ assert (sandbox / "peer-b-gemini").is_dir(), (
161
+ "ensure-scaffold should reconcile sandbox dirs against peers.yaml, "
162
+ "not just on fresh handshake"
163
+ )
164
+
165
+
129
166
  # ---------------------------------------------------------------------------
130
167
  # focus new / focus active
131
168
  # ---------------------------------------------------------------------------
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes