assertion-cli 0.5.10__tar.gz → 0.5.12__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.10 → assertion_cli-0.5.12}/PKG-INFO +1 -1
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/assertion_cli.egg-info/PKG-INFO +1 -1
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/main.py +12 -11
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/pyproject.toml +1 -1
- assertion_cli-0.5.12/templates/AGENTS.md +26 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/templates/SKILL.md +1 -1
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/tests/test_git.py +0 -1
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/tests/test_new.py +6 -3
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/tests/test_session_start.py +11 -7
- assertion_cli-0.5.10/templates/AGENTS.md +0 -14
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/README.md +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/api.py +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/assertion_cli.egg-info/SOURCES.txt +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/assertion_cli.egg-info/dependency_links.txt +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/assertion_cli.egg-info/entry_points.txt +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/assertion_cli.egg-info/requires.txt +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/assertion_cli.egg-info/top_level.txt +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/bundle.py +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/git.py +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/link.py +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/models.py +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/session.py +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/setup.cfg +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/templates/__init__.py +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/tests/test_api.py +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/tests/test_bundle.py +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/tests/test_decision.py +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/tests/test_install.py +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/tests/test_link.py +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/tests/test_main.py +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/tests/test_prompt.py +0 -0
- {assertion_cli-0.5.10 → assertion_cli-0.5.12}/tests/test_session.py +0 -0
|
@@ -16,6 +16,7 @@ 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,
|
|
@@ -271,8 +272,9 @@ def _resolve_diff_base(repo_root: Path) -> str:
|
|
|
271
272
|
refs (from a force-pushed or rebased branch) would otherwise produce a
|
|
272
273
|
merge-base that's no longer reachable from any remote ref.
|
|
273
274
|
|
|
274
|
-
|
|
275
|
-
|
|
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.
|
|
276
278
|
"""
|
|
277
279
|
try:
|
|
278
280
|
run_git_command(repo_root, ["fetch", "--no-tags", "origin"])
|
|
@@ -281,13 +283,11 @@ def _resolve_diff_base(repo_root: Path) -> str:
|
|
|
281
283
|
|
|
282
284
|
upstream = get_upstream_ref(repo_root)
|
|
283
285
|
if upstream is None:
|
|
284
|
-
|
|
285
|
-
"
|
|
286
|
-
|
|
287
|
-
"with a tracking branch configured, e.g.:\n"
|
|
288
|
-
" git push -u origin <branch>\n"
|
|
289
|
-
"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,
|
|
290
289
|
)
|
|
290
|
+
return get_head_sha(repo_root)
|
|
291
291
|
return get_merge_base(repo_root, upstream)
|
|
292
292
|
|
|
293
293
|
|
|
@@ -296,9 +296,10 @@ def session_start() -> None:
|
|
|
296
296
|
"""Record the merge-base with the upstream tracking branch as the diff base.
|
|
297
297
|
|
|
298
298
|
Unlike the old HEAD-based approach, the merge-base always exists on the
|
|
299
|
-
remote so the backend can check it out.
|
|
300
|
-
|
|
301
|
-
|
|
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.
|
|
302
303
|
|
|
303
304
|
`session start` does not write skill files — that is `asrt install`, the
|
|
304
305
|
one-time onboarding step. It also never touches user content in `CLAUDE.md` /
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Assertion verification
|
|
2
|
+
|
|
3
|
+
This repo uses the Assertion CLI (`asrt`) to track user intent and verify your work. The full workflow lives in the `assertion-cli` skill:
|
|
4
|
+
|
|
5
|
+
- **Claude Code** — [`.claude/skills/assertion-cli/SKILL.md`](.claude/skills/assertion-cli/SKILL.md)
|
|
6
|
+
- **Codex / Cursor** — [`.agents/skills/assertion-cli/SKILL.md`](.agents/skills/assertion-cli/SKILL.md)
|
|
7
|
+
|
|
8
|
+
Both files are byte-identical — read whichever your agent loads (or either one directly). In brief, follow these three rules for every conversation:
|
|
9
|
+
|
|
10
|
+
1. **Every user message:** call `asrt prompt "<exact text of the message>"` before doing any other work for that turn. Apply to every user turn, not just the first.
|
|
11
|
+
2. **Checkpoint at trajectory-feedback moments** with `asrt checkpoint` — a handful across a feature build, not one per file.
|
|
12
|
+
3. **Verify, then PR with the session marker.** At completion, run `asrt verify` and poll `asrt verify-status` until clean. Then open the PR and embed the hidden session marker — `$(asrt get-link)`, an HTML comment — in its body.
|
|
13
|
+
|
|
14
|
+
**The session marker is mandatory, not optional.** It's an HTML comment carrying the session URL — `asrt get-link` prints exactly this form:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
<!-- assertion-session: https://app.tryassertion.com/sessions/<id> -->
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Every PR opened from an Assertion session **must** carry it: the Assertion GitHub bot links the PR to the verification session by branch name first, and falls back to this marker when the branch doesn't match — without it, that PR can't be linked. So:
|
|
21
|
+
|
|
22
|
+
- Put `$(asrt get-link)` in the PR body via `gh pr create`/`gh pr edit` — never paste the bare URL into the visible description.
|
|
23
|
+
- After creating or editing the PR, **confirm the marker actually landed in the raw body** (`gh pr view --json body --jq .body | grep -q 'assertion-session:'`). It won't show in the rendered description — that's expected.
|
|
24
|
+
- A missing marker is a **blocking error, not a warning**: do not consider the task done, and do not open a PR at all, until the marker is verified present. Re-run `gh pr edit` with `$(asrt get-link)` and re-check until it passes.
|
|
25
|
+
|
|
26
|
+
See the skill file for the full rules, edge cases, and the exact PR command (section: *Embed the hidden session marker in the PR*).
|
|
@@ -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
|
|
|
@@ -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
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
# Assertion verification
|
|
2
|
-
|
|
3
|
-
This repo uses the Assertion CLI (`asrt`) to track user intent and verify your work. The full workflow lives in the `assertion-cli` skill:
|
|
4
|
-
|
|
5
|
-
- **Claude Code** — [`.claude/skills/assertion-cli/SKILL.md`](.claude/skills/assertion-cli/SKILL.md)
|
|
6
|
-
- **Codex / Cursor** — [`.agents/skills/assertion-cli/SKILL.md`](.agents/skills/assertion-cli/SKILL.md)
|
|
7
|
-
|
|
8
|
-
Both files are byte-identical — read whichever your agent loads (or either one directly). In brief, follow these three rules for every conversation:
|
|
9
|
-
|
|
10
|
-
1. **Every user message:** call `asrt prompt "<exact text of the message>"` before doing any other work for that turn. Apply to every user turn, not just the first.
|
|
11
|
-
2. **Checkpoint at trajectory-feedback moments** with `asrt checkpoint` — a handful across a feature build, not one per file.
|
|
12
|
-
3. **Verify at completion** with `asrt verify`. Once it is clean, open a PR whose body embeds the hidden session marker from `asrt get-link`.
|
|
13
|
-
|
|
14
|
-
See the skill file for the full rules, edge cases, and the exact PR command.
|
|
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
|