assertion-cli 0.5.9__tar.gz → 0.5.11__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.
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/PKG-INFO +1 -1
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/assertion_cli.egg-info/PKG-INFO +1 -1
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/git.py +52 -4
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/main.py +23 -11
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/pyproject.toml +1 -1
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/templates/SKILL.md +1 -1
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/tests/test_git.py +181 -1
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/tests/test_new.py +6 -3
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/tests/test_session_start.py +11 -7
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/README.md +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/api.py +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/assertion_cli.egg-info/SOURCES.txt +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/assertion_cli.egg-info/dependency_links.txt +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/assertion_cli.egg-info/entry_points.txt +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/assertion_cli.egg-info/requires.txt +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/assertion_cli.egg-info/top_level.txt +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/bundle.py +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/link.py +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/models.py +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/session.py +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/setup.cfg +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/templates/AGENTS.md +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/templates/__init__.py +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/tests/test_api.py +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/tests/test_bundle.py +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/tests/test_decision.py +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/tests/test_install.py +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/tests/test_link.py +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/tests/test_main.py +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/tests/test_prompt.py +0 -0
- {assertion_cli-0.5.9 → assertion_cli-0.5.11}/tests/test_session.py +0 -0
|
@@ -49,11 +49,14 @@ def get_head_sha(repo_root: Path) -> str:
|
|
|
49
49
|
def get_upstream_ref(repo_root: Path) -> str | None:
|
|
50
50
|
"""Return the upstream tracking ref (e.g. ``origin/main``).
|
|
51
51
|
|
|
52
|
-
Tries HEAD's configured upstream first
|
|
53
|
-
|
|
54
|
-
remote
|
|
55
|
-
|
|
52
|
+
Tries HEAD's configured upstream first, then falls through increasingly
|
|
53
|
+
general heuristics to find a remote ref to compute a merge-base against:
|
|
54
|
+
same-named remote branch, closest remote ancestor (via
|
|
55
|
+
``git branch -r --merged HEAD`` + ``git merge-base``), then well-known
|
|
56
|
+
default branch names. Returns ``None`` when none are available.
|
|
56
57
|
"""
|
|
58
|
+
|
|
59
|
+
# 1. Configured upstream (git branch -u).
|
|
57
60
|
try:
|
|
58
61
|
return run_git_command(
|
|
59
62
|
repo_root, ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{upstream}"]
|
|
@@ -61,6 +64,7 @@ def get_upstream_ref(repo_root: Path) -> str | None:
|
|
|
61
64
|
except RuntimeError:
|
|
62
65
|
pass
|
|
63
66
|
|
|
67
|
+
# 2. Same branch name on remote (has been pushed at least once).
|
|
64
68
|
head_branch = get_head_branch(repo_root)
|
|
65
69
|
if head_branch is not None:
|
|
66
70
|
fallback = f"origin/{head_branch}"
|
|
@@ -71,6 +75,50 @@ def get_upstream_ref(repo_root: Path) -> str | None:
|
|
|
71
75
|
else:
|
|
72
76
|
return fallback
|
|
73
77
|
|
|
78
|
+
# 3. Closest remote ancestor — handles branches based on other pushed
|
|
79
|
+
# branches (e.g. my-feature on my-feature-base). Uses --merged HEAD
|
|
80
|
+
# so only remote branches in HEAD's ancestry are considered; a branch
|
|
81
|
+
# whose tip moved forward after we branched off is correctly excluded.
|
|
82
|
+
try:
|
|
83
|
+
merged = run_git_command(
|
|
84
|
+
repo_root,
|
|
85
|
+
[
|
|
86
|
+
"branch", "-r", "--merged", "HEAD",
|
|
87
|
+
"--format=%(refname:short)",
|
|
88
|
+
],
|
|
89
|
+
)
|
|
90
|
+
ancestors = [r for r in merged.splitlines() if r]
|
|
91
|
+
if ancestors:
|
|
92
|
+
return run_git_command(
|
|
93
|
+
repo_root, ["merge-base", "HEAD", *ancestors]
|
|
94
|
+
)
|
|
95
|
+
except RuntimeError:
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
# 4. Remote's default branch (symbolic ref, usually origin/main).
|
|
99
|
+
try:
|
|
100
|
+
run_git_command(repo_root, ["rev-parse", "--verify", "origin/HEAD"])
|
|
101
|
+
except RuntimeError:
|
|
102
|
+
pass
|
|
103
|
+
else:
|
|
104
|
+
return "origin/HEAD"
|
|
105
|
+
|
|
106
|
+
# 5. Conventional default branch name.
|
|
107
|
+
try:
|
|
108
|
+
run_git_command(repo_root, ["rev-parse", "--verify", "origin/main"])
|
|
109
|
+
except RuntimeError:
|
|
110
|
+
pass
|
|
111
|
+
else:
|
|
112
|
+
return "origin/main"
|
|
113
|
+
|
|
114
|
+
# 6. Legacy default branch name.
|
|
115
|
+
try:
|
|
116
|
+
run_git_command(repo_root, ["rev-parse", "--verify", "origin/master"])
|
|
117
|
+
except RuntimeError:
|
|
118
|
+
pass
|
|
119
|
+
else:
|
|
120
|
+
return "origin/master"
|
|
121
|
+
|
|
74
122
|
return None
|
|
75
123
|
|
|
76
124
|
|
|
@@ -16,9 +16,11 @@ from git import (
|
|
|
16
16
|
find_git_root,
|
|
17
17
|
get_full_diff,
|
|
18
18
|
get_head_branch,
|
|
19
|
+
get_head_sha,
|
|
19
20
|
get_merge_base,
|
|
20
21
|
get_uncommitted_diff,
|
|
21
22
|
get_upstream_ref,
|
|
23
|
+
run_git_command,
|
|
22
24
|
)
|
|
23
25
|
from link import load_link, save_link
|
|
24
26
|
from models import CheckpointResponse, SessionStatus, render_stack_list
|
|
@@ -265,18 +267,27 @@ app.add_typer(session_app, name="session")
|
|
|
265
267
|
def _resolve_diff_base(repo_root: Path) -> str:
|
|
266
268
|
"""Compute the merge-base with the upstream tracking branch.
|
|
267
269
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
+
Fetches from origin first so the merge-base SHA is guaranteed to exist
|
|
271
|
+
on the remote when the backend checks it out — stale remote tracking
|
|
272
|
+
refs (from a force-pushed or rebased branch) would otherwise produce a
|
|
273
|
+
merge-base that's no longer reachable from any remote ref.
|
|
274
|
+
|
|
275
|
+
Falls back to HEAD when no upstream can be resolved (e.g. Codex cloud
|
|
276
|
+
sandboxes with no remote tracking refs) — the branch was cloned from the
|
|
277
|
+
remote so HEAD exists on the remote at session start time.
|
|
270
278
|
"""
|
|
279
|
+
try:
|
|
280
|
+
run_git_command(repo_root, ["fetch", "--no-tags", "origin"])
|
|
281
|
+
except RuntimeError:
|
|
282
|
+
pass # non-fatal: offline or unavailable; proceed with stale refs
|
|
283
|
+
|
|
271
284
|
upstream = get_upstream_ref(repo_root)
|
|
272
285
|
if upstream is None:
|
|
273
|
-
|
|
274
|
-
"
|
|
275
|
-
|
|
276
|
-
"with a tracking branch configured, e.g.:\n"
|
|
277
|
-
" git push -u origin <branch>\n"
|
|
278
|
-
"Or use an existing branch that tracks an upstream.",
|
|
286
|
+
typer.echo(
|
|
287
|
+
"Warning: no upstream tracking branch found, falling back to HEAD.",
|
|
288
|
+
err=True,
|
|
279
289
|
)
|
|
290
|
+
return get_head_sha(repo_root)
|
|
280
291
|
return get_merge_base(repo_root, upstream)
|
|
281
292
|
|
|
282
293
|
|
|
@@ -285,9 +296,10 @@ def session_start() -> None:
|
|
|
285
296
|
"""Record the merge-base with the upstream tracking branch as the diff base.
|
|
286
297
|
|
|
287
298
|
Unlike the old HEAD-based approach, the merge-base always exists on the
|
|
288
|
-
remote so the backend can check it out.
|
|
289
|
-
|
|
290
|
-
|
|
299
|
+
remote so the backend can check it out. When no upstream tracking branch
|
|
300
|
+
can be resolved (e.g. Codex cloud sandboxes), falls back to HEAD — the
|
|
301
|
+
branch was cloned from the remote so HEAD exists on the remote at session
|
|
302
|
+
start time.
|
|
291
303
|
|
|
292
304
|
`session start` does not write skill files — that is `asrt install`, the
|
|
293
305
|
one-time onboarding step. It also never touches user content in `CLAUDE.md` /
|
|
@@ -15,7 +15,7 @@ Before any `asrt` call:
|
|
|
15
15
|
|
|
16
16
|
- You are inside a git repository.
|
|
17
17
|
- `asrt install` has been run once in this repo. It installs the skill files you are reading right now (plus the `AGENTS.md` / `CLAUDE.md` pointer and `.gitignore` rules). This is an onboarding step, not a per-run one.
|
|
18
|
-
- `asrt session start` has been run for this work session. It records the
|
|
18
|
+
- `asrt session start` has been run for this work session. It records the merge-base with the upstream tracking branch (falling back to HEAD when no upstream is available) as the diff base that every checkpoint/verify diffs against. If `asrt checkpoint` or `asrt verify` errors with "No diff base recorded", run `asrt session start` and retry.
|
|
19
19
|
|
|
20
20
|
**First command sequence for a new work session:**
|
|
21
21
|
|
|
@@ -7,7 +7,6 @@ from click.exceptions import Exit as ClickExit
|
|
|
7
7
|
from git import (
|
|
8
8
|
find_git_root,
|
|
9
9
|
get_full_diff,
|
|
10
|
-
get_head_branch,
|
|
11
10
|
get_head_sha,
|
|
12
11
|
get_merge_base,
|
|
13
12
|
get_uncommitted_diff,
|
|
@@ -180,6 +179,187 @@ def test_get_upstream_ref_detached_head(tmp_path: Path) -> None:
|
|
|
180
179
|
assert ref is None
|
|
181
180
|
|
|
182
181
|
|
|
182
|
+
def test_get_upstream_ref_fallback_to_origin_head(tmp_path: Path) -> None:
|
|
183
|
+
"""HEAD on an orphan commit (no shared history with remote refs)
|
|
184
|
+
returns origin/HEAD."""
|
|
185
|
+
subprocess.run(["git", "init"], cwd=tmp_path, capture_output=True, check=True)
|
|
186
|
+
_make_commit(tmp_path)
|
|
187
|
+
subprocess.run(
|
|
188
|
+
["git", "remote", "add", "origin", "http://example.com/repo.git"],
|
|
189
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
190
|
+
)
|
|
191
|
+
subprocess.run(
|
|
192
|
+
["git", "symbolic-ref", "refs/remotes/origin/HEAD", "refs/remotes/origin/somebranch"],
|
|
193
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
194
|
+
)
|
|
195
|
+
subprocess.run(
|
|
196
|
+
["git", "update-ref", "refs/remotes/origin/somebranch", "HEAD"],
|
|
197
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
198
|
+
)
|
|
199
|
+
# Create an orphan commit so HEAD has no shared history with the
|
|
200
|
+
# remote refs — this makes step 3 (--merged HEAD) produce nothing.
|
|
201
|
+
subprocess.run(
|
|
202
|
+
["git", "checkout", "--orphan", "orphan-branch"],
|
|
203
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
204
|
+
)
|
|
205
|
+
(tmp_path / "orphan.txt").write_text("orphan\n")
|
|
206
|
+
subprocess.run(
|
|
207
|
+
["git", "add", "orphan.txt"], cwd=tmp_path, capture_output=True, check=True
|
|
208
|
+
)
|
|
209
|
+
subprocess.run(
|
|
210
|
+
["git", "commit", "-m", "orphan"],
|
|
211
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
212
|
+
env={**GIT_ENV, "HOME": str(tmp_path)},
|
|
213
|
+
)
|
|
214
|
+
ref = get_upstream_ref(tmp_path)
|
|
215
|
+
assert ref == "origin/HEAD"
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def test_get_upstream_ref_fallback_to_origin_main(tmp_path: Path) -> None:
|
|
219
|
+
"""HEAD on an orphan commit, no origin/HEAD, but origin/main exists."""
|
|
220
|
+
subprocess.run(["git", "init"], cwd=tmp_path, capture_output=True, check=True)
|
|
221
|
+
_make_commit(tmp_path)
|
|
222
|
+
subprocess.run(
|
|
223
|
+
["git", "remote", "add", "origin", "http://example.com/repo.git"],
|
|
224
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
225
|
+
)
|
|
226
|
+
subprocess.run(
|
|
227
|
+
["git", "update-ref", "refs/remotes/origin/main", "HEAD"],
|
|
228
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
229
|
+
)
|
|
230
|
+
subprocess.run(
|
|
231
|
+
["git", "checkout", "--orphan", "orphan-branch"],
|
|
232
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
233
|
+
)
|
|
234
|
+
(tmp_path / "orphan.txt").write_text("orphan\n")
|
|
235
|
+
subprocess.run(
|
|
236
|
+
["git", "add", "orphan.txt"], cwd=tmp_path, capture_output=True, check=True
|
|
237
|
+
)
|
|
238
|
+
subprocess.run(
|
|
239
|
+
["git", "commit", "-m", "orphan"],
|
|
240
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
241
|
+
env={**GIT_ENV, "HOME": str(tmp_path)},
|
|
242
|
+
)
|
|
243
|
+
ref = get_upstream_ref(tmp_path)
|
|
244
|
+
assert ref == "origin/main"
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def test_get_upstream_ref_fallback_to_origin_master(tmp_path: Path) -> None:
|
|
248
|
+
"""HEAD on an orphan commit, no origin/HEAD/main, origin/master exists."""
|
|
249
|
+
subprocess.run(["git", "init"], cwd=tmp_path, capture_output=True, check=True)
|
|
250
|
+
_make_commit(tmp_path)
|
|
251
|
+
subprocess.run(
|
|
252
|
+
["git", "remote", "add", "origin", "http://example.com/repo.git"],
|
|
253
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
254
|
+
)
|
|
255
|
+
subprocess.run(
|
|
256
|
+
["git", "update-ref", "refs/remotes/origin/master", "HEAD"],
|
|
257
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
258
|
+
)
|
|
259
|
+
subprocess.run(
|
|
260
|
+
["git", "checkout", "--orphan", "orphan-branch"],
|
|
261
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
262
|
+
)
|
|
263
|
+
(tmp_path / "orphan.txt").write_text("orphan\n")
|
|
264
|
+
subprocess.run(
|
|
265
|
+
["git", "add", "orphan.txt"], cwd=tmp_path, capture_output=True, check=True
|
|
266
|
+
)
|
|
267
|
+
subprocess.run(
|
|
268
|
+
["git", "commit", "-m", "orphan"],
|
|
269
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
270
|
+
env={**GIT_ENV, "HOME": str(tmp_path)},
|
|
271
|
+
)
|
|
272
|
+
ref = get_upstream_ref(tmp_path)
|
|
273
|
+
assert ref == "origin/master"
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def test_get_upstream_ref_fallback_to_merged_ancestors(tmp_path: Path) -> None:
|
|
277
|
+
"""Branch based on a pushed feature branch finds the closest remote
|
|
278
|
+
ancestor via --merged HEAD + merge-base."""
|
|
279
|
+
_init_repo_with_commit(tmp_path)
|
|
280
|
+
base_sha = run_git_command(tmp_path, ["rev-parse", "HEAD"])
|
|
281
|
+
subprocess.run(
|
|
282
|
+
["git", "remote", "add", "origin", "http://example.com/repo.git"],
|
|
283
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
# Simulate origin/main at the initial commit.
|
|
287
|
+
subprocess.run(
|
|
288
|
+
["git", "update-ref", "refs/remotes/origin/main", base_sha],
|
|
289
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
# Simulate a pushed feature branch (origin/my-feature-base) with a
|
|
293
|
+
# commit on top of main.
|
|
294
|
+
(tmp_path / "base.txt").write_text("base\n")
|
|
295
|
+
subprocess.run(
|
|
296
|
+
["git", "add", "base.txt"], cwd=tmp_path, capture_output=True, check=True
|
|
297
|
+
)
|
|
298
|
+
subprocess.run(
|
|
299
|
+
["git", "commit", "-m", "base work"],
|
|
300
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
301
|
+
env={**GIT_ENV, "HOME": str(tmp_path)},
|
|
302
|
+
)
|
|
303
|
+
feature_base_sha = run_git_command(tmp_path, ["rev-parse", "HEAD"])
|
|
304
|
+
subprocess.run(
|
|
305
|
+
["git", "update-ref", "refs/remotes/origin/my-feature-base", feature_base_sha],
|
|
306
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
# Create a new local branch on top of my-feature-base (simulates a
|
|
310
|
+
# branch-off that hasn't been pushed). Use a branch name that doesn't
|
|
311
|
+
# collide with any remote ref so step 2 is skipped.
|
|
312
|
+
subprocess.run(
|
|
313
|
+
["git", "checkout", "-b", "my-feature"],
|
|
314
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
315
|
+
)
|
|
316
|
+
(tmp_path / "new.txt").write_text("new\n")
|
|
317
|
+
subprocess.run(
|
|
318
|
+
["git", "add", "new.txt"], cwd=tmp_path, capture_output=True, check=True
|
|
319
|
+
)
|
|
320
|
+
subprocess.run(
|
|
321
|
+
["git", "commit", "-m", "new work"],
|
|
322
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
323
|
+
env={**GIT_ENV, "HOME": str(tmp_path)},
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
ref = get_upstream_ref(tmp_path)
|
|
327
|
+
assert ref is not None
|
|
328
|
+
# ref was returned by `git merge-base HEAD ...`, it should be the
|
|
329
|
+
# SHA of the feature base, not a named ref.
|
|
330
|
+
assert ref == feature_base_sha
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def test_get_upstream_ref_no_fallback(tmp_path: Path) -> None:
|
|
334
|
+
"""No upstream, no origin/HEAD, no known default branches returns None."""
|
|
335
|
+
subprocess.run(["git", "init"], cwd=tmp_path, capture_output=True, check=True)
|
|
336
|
+
_make_commit(tmp_path)
|
|
337
|
+
subprocess.run(
|
|
338
|
+
["git", "remote", "add", "origin", "http://example.com/repo.git"],
|
|
339
|
+
cwd=tmp_path, capture_output=True, check=True,
|
|
340
|
+
)
|
|
341
|
+
ref = get_upstream_ref(tmp_path)
|
|
342
|
+
assert ref is None
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def _make_commit(tmp_path: Path) -> None:
|
|
346
|
+
"""Helper: make an initial commit in a bare init."""
|
|
347
|
+
subprocess.run(
|
|
348
|
+
["git", "commit", "--allow-empty", "-m", "init"],
|
|
349
|
+
cwd=tmp_path,
|
|
350
|
+
capture_output=True,
|
|
351
|
+
check=True,
|
|
352
|
+
env={
|
|
353
|
+
"GIT_AUTHOR_NAME": "T",
|
|
354
|
+
"GIT_AUTHOR_EMAIL": "t@t",
|
|
355
|
+
"GIT_COMMITTER_NAME": "T",
|
|
356
|
+
"GIT_COMMITTER_EMAIL": "t@t",
|
|
357
|
+
"HOME": str(tmp_path),
|
|
358
|
+
"PATH": "/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin",
|
|
359
|
+
},
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
|
|
183
363
|
# --- get_merge_base ---
|
|
184
364
|
|
|
185
365
|
|
|
@@ -125,15 +125,18 @@ def test_new_leaves_next_checkpoint_requiring_stack(
|
|
|
125
125
|
assert not (assertion_dir / "metadata.json").exists()
|
|
126
126
|
|
|
127
127
|
|
|
128
|
-
def
|
|
128
|
+
def test_new_falls_back_to_head_when_no_upstream(
|
|
129
129
|
tmp_path: Path,
|
|
130
130
|
monkeypatch,
|
|
131
131
|
) -> None:
|
|
132
|
+
"""When no upstream tracking branch exists, fall back to HEAD instead of erroring."""
|
|
132
133
|
_run_git(tmp_path, "init")
|
|
133
134
|
_run_git(tmp_path, "commit", "--allow-empty", "-m", "init")
|
|
135
|
+
head_sha = _run_git(tmp_path, "rev-parse", "HEAD")
|
|
134
136
|
monkeypatch.chdir(tmp_path)
|
|
135
137
|
|
|
136
138
|
result = runner.invoke(main.app, ["new"])
|
|
137
139
|
|
|
138
|
-
assert result.exit_code ==
|
|
139
|
-
assert "
|
|
140
|
+
assert result.exit_code == 0
|
|
141
|
+
assert "falling back to HEAD" in result.stderr
|
|
142
|
+
assert (tmp_path / ".assertion" / "base_sha").read_text().strip() == head_sha
|
|
@@ -197,23 +197,27 @@ def test_session_start_requires_git_repo(tmp_path: Path, monkeypatch) -> None:
|
|
|
197
197
|
assert "git repository" in result.stderr
|
|
198
198
|
|
|
199
199
|
|
|
200
|
-
def
|
|
200
|
+
def test_session_start_falls_back_to_head_when_no_upstream(
|
|
201
201
|
tmp_path: Path, monkeypatch
|
|
202
202
|
) -> None:
|
|
203
|
+
"""When no upstream tracking branch exists, fall back to HEAD instead of erroring.
|
|
204
|
+
|
|
205
|
+
Codex cloud sandboxes may not have upstream tracking configured, but the
|
|
206
|
+
branch was cloned from the remote so HEAD exists on the remote.
|
|
207
|
+
"""
|
|
203
208
|
_run_git(tmp_path, "init")
|
|
204
209
|
_run_git(tmp_path, "commit", "--allow-empty", "-m", "init")
|
|
210
|
+
head_sha = _run_git(tmp_path, "rev-parse", "HEAD")
|
|
205
211
|
monkeypatch.chdir(tmp_path)
|
|
206
212
|
|
|
207
213
|
result = runner.invoke(main.app, ["session", "start"])
|
|
208
214
|
|
|
209
|
-
assert result.exit_code ==
|
|
210
|
-
assert "
|
|
211
|
-
assert
|
|
215
|
+
assert result.exit_code == 0
|
|
216
|
+
assert "falling back to HEAD" in result.stderr
|
|
217
|
+
assert (tmp_path / ".assertion" / "base_sha").read_text().strip() == head_sha
|
|
212
218
|
|
|
213
219
|
|
|
214
|
-
def test_session_start_records_merge_base_as_base(
|
|
215
|
-
tmp_path: Path, monkeypatch
|
|
216
|
-
) -> None:
|
|
220
|
+
def test_session_start_records_merge_base_as_base(tmp_path: Path, monkeypatch) -> None:
|
|
217
221
|
"""`asrt session start` pins the diff base to the branch merge-base.
|
|
218
222
|
|
|
219
223
|
Every subsequent checkpoint/verify diffs against this SHA, so backend can
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|