sase-github 0.2.6__tar.gz → 0.2.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 (44) hide show
  1. sase_github-0.2.7/.release-please-manifest.json +3 -0
  2. {sase_github-0.2.6 → sase_github-0.2.7}/CHANGELOG.md +7 -0
  3. {sase_github-0.2.6 → sase_github-0.2.7}/PKG-INFO +1 -1
  4. {sase_github-0.2.6 → sase_github-0.2.7}/pyproject.toml +1 -1
  5. {sase_github-0.2.6 → sase_github-0.2.7}/src/sase_github/scripts/gh_setup.py +28 -5
  6. {sase_github-0.2.6 → sase_github-0.2.7}/tests/test_gh_workspace_claims.py +179 -0
  7. sase_github-0.2.6/.release-please-manifest.json +0 -3
  8. {sase_github-0.2.6 → sase_github-0.2.7}/.github/workflows/ci.yml +0 -0
  9. {sase_github-0.2.6 → sase_github-0.2.7}/.github/workflows/pr-title.yml +0 -0
  10. {sase_github-0.2.6 → sase_github-0.2.7}/.github/workflows/publish.yml +0 -0
  11. {sase_github-0.2.6 → sase_github-0.2.7}/.sase_beads/beads.db +0 -0
  12. {sase_github-0.2.6 → sase_github-0.2.7}/.sase_beads/config.json +0 -0
  13. {sase_github-0.2.6 → sase_github-0.2.7}/.sase_beads/issues.jsonl +0 -0
  14. {sase_github-0.2.6 → sase_github-0.2.7}/CLAUDE.md +0 -0
  15. {sase_github-0.2.6 → sase_github-0.2.7}/Justfile +0 -0
  16. {sase_github-0.2.6 → sase_github-0.2.7}/LICENSE +0 -0
  17. {sase_github-0.2.6 → sase_github-0.2.7}/README.md +0 -0
  18. {sase_github-0.2.6 → sase_github-0.2.7}/docs/architecture.md +0 -0
  19. {sase_github-0.2.6 → sase_github-0.2.7}/docs/configuration.md +0 -0
  20. {sase_github-0.2.6 → sase_github-0.2.7}/docs/xprompts.md +0 -0
  21. {sase_github-0.2.6 → sase_github-0.2.7}/release-please-config.json +0 -0
  22. {sase_github-0.2.6 → sase_github-0.2.7}/src/sase_github/__init__.py +0 -0
  23. {sase_github-0.2.6 → sase_github-0.2.7}/src/sase_github/config.py +0 -0
  24. {sase_github-0.2.6 → sase_github-0.2.7}/src/sase_github/default_config.yml +0 -0
  25. {sase_github-0.2.6 → sase_github-0.2.7}/src/sase_github/errors.py +0 -0
  26. {sase_github-0.2.6 → sase_github-0.2.7}/src/sase_github/plugin.py +0 -0
  27. {sase_github-0.2.6 → sase_github-0.2.7}/src/sase_github/scripts/__init__.py +0 -0
  28. {sase_github-0.2.6 → sase_github-0.2.7}/src/sase_github/scripts/new_pr_desc_get_context.py +0 -0
  29. {sase_github-0.2.6 → sase_github-0.2.7}/src/sase_github/task_types.py +0 -0
  30. {sase_github-0.2.6 → sase_github-0.2.7}/src/sase_github/workspace_plugin.py +0 -0
  31. {sase_github-0.2.6 → sase_github-0.2.7}/src/sase_github/xprompts/gh.yml +0 -0
  32. {sase_github-0.2.6 → sase_github-0.2.7}/src/sase_github/xprompts/new_pr_desc.yml +0 -0
  33. {sase_github-0.2.6 → sase_github-0.2.7}/src/sase_github/xprompts/pr_diff.yml +0 -0
  34. {sase_github-0.2.6 → sase_github-0.2.7}/src/sase_github/xprompts/prdd.yml +0 -0
  35. {sase_github-0.2.6 → sase_github-0.2.7}/tests/__init__.py +0 -0
  36. {sase_github-0.2.6 → sase_github-0.2.7}/tests/test_ci_install_contract.py +0 -0
  37. {sase_github-0.2.6 → sase_github-0.2.7}/tests/test_config.py +0 -0
  38. {sase_github-0.2.6 → sase_github-0.2.7}/tests/test_github_issues.py +0 -0
  39. {sase_github-0.2.6 → sase_github-0.2.7}/tests/test_github_plugin.py +0 -0
  40. {sase_github-0.2.6 → sase_github-0.2.7}/tests/test_github_pull_requests.py +0 -0
  41. {sase_github-0.2.6 → sase_github-0.2.7}/tests/test_submit_with_recorded_pr.py +0 -0
  42. {sase_github-0.2.6 → sase_github-0.2.7}/tests/test_task_types.py +0 -0
  43. {sase_github-0.2.6 → sase_github-0.2.7}/tests/test_workspace_plugin.py +0 -0
  44. {sase_github-0.2.6 → sase_github-0.2.7}/uv.lock +0 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.2.7"
3
+ }
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.7](https://github.com/sase-org/sase-github/compare/v0.2.6...v0.2.7) (2026-08-28)
4
+
5
+
6
+ ### Features
7
+
8
+ * **gh-setup:** adopt the runner's numbered workspace claim ([5e8e9ea](https://github.com/sase-org/sase-github/commit/5e8e9ea6a48b5b285a65d9cb1fa087f74d52b6b0))
9
+
3
10
  ## [0.2.6](https://github.com/sase-org/sase-github/compare/v0.2.5...v0.2.6) (2026-08-18)
4
11
 
5
12
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sase-github
3
- Version: 0.2.6
3
+ Version: 0.2.7
4
4
  Summary: GitHub VCS plugin for sase
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sase-github"
7
- version = "0.2.6"
7
+ version = "0.2.7"
8
8
  description = "GitHub VCS plugin for sase"
9
9
  requires-python = ">=3.12"
10
10
  license = "MIT"
@@ -8,6 +8,7 @@ from sase.running_field import (
8
8
  WorkspaceClaim,
9
9
  claim_next_axe_workspace,
10
10
  claim_workspace,
11
+ find_runner_numbered_workspace,
11
12
  get_claimed_workspaces,
12
13
  release_workspace,
13
14
  )
@@ -61,14 +62,22 @@ def main(
61
62
  pinned=not release,
62
63
  )
63
64
  else:
64
- workspace_num, workspace_dir = _claim_next_workspace(
65
+ adopted = _adopt_runner_workspace(
65
66
  project_file=project_file,
66
67
  primary_workspace_dir=resolved.primary_workspace_dir,
67
- workflow_name=workflow_name,
68
- pid=pid,
69
- cl_name=cl_name,
70
- pinned=not release,
71
68
  )
69
+ if adopted is not None:
70
+ workspace_num, workspace_dir = adopted
71
+ pre_allocated = True
72
+ else:
73
+ workspace_num, workspace_dir = _claim_next_workspace(
74
+ project_file=project_file,
75
+ primary_workspace_dir=resolved.primary_workspace_dir,
76
+ workflow_name=workflow_name,
77
+ pid=pid,
78
+ cl_name=cl_name,
79
+ pinned=not release,
80
+ )
72
81
 
73
82
  # Refuse to hand this checkout to `prepare`/`checkout` if another live
74
83
  # agent still occupies it. Runs on the pre_allocated branch too: the
@@ -129,6 +138,20 @@ def main(
129
138
  print(f"workflow_name={workflow_name}")
130
139
 
131
140
 
141
+ def _adopt_runner_workspace(
142
+ *,
143
+ project_file: str,
144
+ primary_workspace_dir: str,
145
+ ) -> tuple[int, str] | None:
146
+ """Reuse the calling runner's numbered pool claim, if it holds one."""
147
+ workspace_num = find_runner_numbered_workspace(project_file)
148
+ if workspace_num is None:
149
+ return None
150
+ workspace_dir = ensure_workspace_checkout(primary_workspace_dir, workspace_num)
151
+ materialize_sdd_store(workspace_dir, workspace_num)
152
+ return workspace_num, workspace_dir
153
+
154
+
132
155
  def _claim_next_workspace(
133
156
  *,
134
157
  project_file: str,
@@ -567,6 +567,185 @@ class TestGhSetupOccupancyGuard:
567
567
  ] == [10]
568
568
 
569
569
 
570
+ class TestGhSetupAdoptsRunnerWorkspace:
571
+ def test_adopts_parent_numbered_claim_without_second_claim(
572
+ self, tmp_path: Path, capsys: pytest.CaptureFixture[str]
573
+ ) -> None:
574
+ parent_claim = WorkspaceClaim(
575
+ 12, "ace(run)-launcher", "feature", pid=os.getppid()
576
+ )
577
+ project_file = _write_project_file(tmp_path, running_claims=[parent_claim])
578
+ workspace_dir = tmp_path / "widget_12"
579
+ workspace_dir.mkdir()
580
+
581
+ with (
582
+ _isolate_ledger(tmp_path),
583
+ patch(
584
+ "sase_github.scripts.gh_setup.resolve_ref",
585
+ return_value=_resolved(project_file),
586
+ ),
587
+ patch.dict(os.environ, {"SASE_GH_PRE_ALLOCATED": "0"}),
588
+ patch(
589
+ "sase_github.scripts.gh_setup.ensure_workspace_checkout",
590
+ return_value=str(workspace_dir),
591
+ ),
592
+ patch("sase_github.scripts.gh_setup.materialize_sdd_store") as materialize,
593
+ patch(
594
+ "sase_github.scripts.gh_setup.claim_next_axe_workspace"
595
+ ) as claim_next,
596
+ patch("sase_github.scripts.gh_setup.claim_workspace") as claim,
597
+ ):
598
+ gh_setup.main(gh_ref="acme/widget", n=None, release=True)
599
+
600
+ claim_next.assert_not_called()
601
+ claim.assert_not_called()
602
+ materialize.assert_called_once_with(str(workspace_dir), 12)
603
+ out = capsys.readouterr().out
604
+ assert "workspace_num=12" in out
605
+ assert "should_release=false" in out
606
+ claims = get_claimed_workspaces(project_file)
607
+ assert [claim.workspace_num for claim in claims] == [12]
608
+ assert claims[0].pid == os.getppid()
609
+ assert read_occupant_record(str(workspace_dir)) is None
610
+
611
+ def test_does_not_adopt_when_parent_only_holds_placeholder(
612
+ self, tmp_path: Path, capsys: pytest.CaptureFixture[str]
613
+ ) -> None:
614
+ placeholder = WorkspaceClaim(
615
+ 0, "ace(run)-launcher", "feature", pid=os.getppid()
616
+ )
617
+ project_file = _write_project_file(tmp_path, running_claims=[placeholder])
618
+
619
+ with (
620
+ _isolate_ledger(tmp_path),
621
+ patch(
622
+ "sase_github.scripts.gh_setup.resolve_ref",
623
+ return_value=_resolved(project_file),
624
+ ),
625
+ patch.dict(os.environ, {"SASE_GH_PRE_ALLOCATED": "0"}),
626
+ patch(
627
+ "sase_github.scripts.gh_setup.ensure_workspace_checkout",
628
+ return_value="/work/widget_10/",
629
+ ),
630
+ patch("sase_github.scripts.gh_setup.materialize_sdd_store"),
631
+ ):
632
+ gh_setup.main(gh_ref="acme/widget", n=None, release=True)
633
+
634
+ claimed_nums = {
635
+ claim.workspace_num for claim in get_claimed_workspaces(project_file)
636
+ }
637
+ assert claimed_nums == {0, 10}
638
+ out = capsys.readouterr().out
639
+ assert "workspace_num=10" in out
640
+ assert "should_release=true" in out
641
+
642
+ def test_pinned_n_does_not_adopt_parent_claim(
643
+ self, tmp_path: Path, capsys: pytest.CaptureFixture[str]
644
+ ) -> None:
645
+ parent_claim = WorkspaceClaim(
646
+ 12, "ace(run)-launcher", "feature", pid=os.getppid()
647
+ )
648
+ project_file = _write_project_file(tmp_path, running_claims=[parent_claim])
649
+
650
+ with (
651
+ _isolate_ledger(tmp_path),
652
+ patch(
653
+ "sase_github.scripts.gh_setup.resolve_ref",
654
+ return_value=_resolved(project_file),
655
+ ),
656
+ patch.dict(os.environ, {"SASE_GH_PRE_ALLOCATED": "0"}),
657
+ patch(
658
+ "sase_github.scripts.gh_setup.ensure_workspace_checkout",
659
+ return_value="/work/widget_17/",
660
+ ),
661
+ patch("sase_github.scripts.gh_setup.materialize_sdd_store"),
662
+ ):
663
+ gh_setup.main(gh_ref="acme/widget", n=17, release=True)
664
+
665
+ claimed_nums = {
666
+ claim.workspace_num for claim in get_claimed_workspaces(project_file)
667
+ }
668
+ assert claimed_nums == {12, 17}
669
+ out = capsys.readouterr().out
670
+ assert "workspace_num=17" in out
671
+ assert "should_release=true" in out
672
+
673
+ def test_adopted_run_does_not_rewrite_occupant_record(self, tmp_path: Path) -> None:
674
+ parent_claim = WorkspaceClaim(
675
+ 12, "ace(run)-launcher", "feature", pid=os.getppid()
676
+ )
677
+ project_file = _write_project_file(tmp_path, running_claims=[parent_claim])
678
+ workspace_dir = tmp_path / "widget_12"
679
+ workspace_dir.mkdir()
680
+ original = new_occupant_record(
681
+ pid=os.getppid(),
682
+ workflow="ace(run)-launcher",
683
+ project="gh_acme__widget",
684
+ workspace_num=12,
685
+ agent_name="launcher-agent",
686
+ )
687
+ write_occupant_record(str(workspace_dir), original)
688
+
689
+ with (
690
+ _isolate_ledger(tmp_path),
691
+ patch(
692
+ "sase_github.scripts.gh_setup.resolve_ref",
693
+ return_value=_resolved(project_file),
694
+ ),
695
+ patch.dict(os.environ, {"SASE_GH_PRE_ALLOCATED": "0"}),
696
+ patch(
697
+ "sase_github.scripts.gh_setup.ensure_workspace_checkout",
698
+ return_value=str(workspace_dir),
699
+ ),
700
+ patch("sase_github.scripts.gh_setup.materialize_sdd_store"),
701
+ ):
702
+ gh_setup.main(gh_ref="acme/widget", n=None, release=True)
703
+
704
+ record = read_occupant_record(str(workspace_dir))
705
+ assert record is not None
706
+ assert record.pid == original.pid
707
+ assert record.workflow == "ace(run)-launcher"
708
+ assert record.agent_name == "launcher-agent"
709
+
710
+ def test_adopted_guard_refusal_releases_nothing(self, tmp_path: Path) -> None:
711
+ parent_claim = WorkspaceClaim(
712
+ 12, "ace(run)-launcher", "feature", pid=os.getppid()
713
+ )
714
+ project_file = _write_project_file(tmp_path, running_claims=[parent_claim])
715
+ workspace_dir = tmp_path / "widget_12"
716
+ workspace_dir.mkdir()
717
+ write_occupant_record(
718
+ str(workspace_dir),
719
+ new_occupant_record(
720
+ pid=os.getpid(),
721
+ workflow="ace(run)-rival",
722
+ project="gh_acme__widget",
723
+ workspace_num=12,
724
+ agent_name="rival-agent",
725
+ ),
726
+ )
727
+
728
+ with (
729
+ _isolate_ledger(tmp_path),
730
+ patch(
731
+ "sase_github.scripts.gh_setup.resolve_ref",
732
+ return_value=_resolved(project_file),
733
+ ),
734
+ patch.dict(os.environ, {"SASE_GH_PRE_ALLOCATED": "0"}),
735
+ patch(
736
+ "sase_github.scripts.gh_setup.ensure_workspace_checkout",
737
+ return_value=str(workspace_dir),
738
+ ),
739
+ patch("sase_github.scripts.gh_setup.materialize_sdd_store"),
740
+ pytest.raises(WorkspaceOccupiedError, match="rival-agent"),
741
+ ):
742
+ gh_setup.main(gh_ref="acme/widget", n=None, release=True)
743
+
744
+ assert [
745
+ claim.workspace_num for claim in get_claimed_workspaces(project_file)
746
+ ] == [12]
747
+
748
+
570
749
  class TestWsSubmitAtomicClaim:
571
750
  def test_submit_acquires_through_claim_next_dir_and_releases(
572
751
  self, tmp_path: Path
@@ -1,3 +0,0 @@
1
- {
2
- ".": "0.2.6"
3
- }
File without changes
File without changes
File without changes
File without changes
File without changes