gitvow 0.34.0__tar.gz → 0.34.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.
- {gitvow-0.34.0/src/gitvow.egg-info → gitvow-0.34.2}/PKG-INFO +1 -1
- {gitvow-0.34.0 → gitvow-0.34.2}/pyproject.toml +1 -1
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/hooks/__init__.py +25 -4
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/install.py +12 -3
- {gitvow-0.34.0 → gitvow-0.34.2/src/gitvow.egg-info}/PKG-INFO +1 -1
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_hooks_disabled.py +48 -3
- {gitvow-0.34.0 → gitvow-0.34.2}/LICENSE +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/README.md +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/setup.cfg +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/__init__.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/__main__.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/adapters.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/carry.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/claims.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/cli.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/collect.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/decisions.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/default_policy.json +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/digest.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/export.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/intent.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/notes.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/outcomes.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/pack.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/paths.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/policy.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/pricing.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/proposals.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/providers.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/reach.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/recall.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/redact.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/report.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/rules.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/safewrite.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/scan.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/selftest.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/serve.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/snapshots.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/state.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/status.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/sync.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/targets.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow/transcript.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow.egg-info/SOURCES.txt +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow.egg-info/dependency_links.txt +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow.egg-info/entry_points.txt +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow.egg-info/requires.txt +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/src/gitvow.egg-info/top_level.txt +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_adapters.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_answered_confirm_commit.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_carry.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_claims.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_collect.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_command_position.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_commit_window.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_cost.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_decisions.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_defects_0_32_1.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_digest.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_duplicate_hooks.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_e2e_globalopt.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_export.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_external_adapter.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_git_rules.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_hardening.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_hooks.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_install_cli.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_intent.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_native_question.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_opencode.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_outcomes.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_pack.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_packaging.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_policy.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_policy_refresh.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_proposals.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_providers.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_reach.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_recall.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_redact.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_report.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_rules.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_scan.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_serve.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_snapshots.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_status.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_status_environment.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_symlinked_paths.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_sync.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_sync_http.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_targets.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_tiers.py +0 -0
- {gitvow-0.34.0 → gitvow-0.34.2}/tests/test_transcript_formats.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitvow
|
|
3
|
-
Version: 0.34.
|
|
3
|
+
Version: 0.34.2
|
|
4
4
|
Summary: Provenance and policy gate for AI-agent coding sessions: session trailers on commits, redacted session notes in git, a tool-call gate, and a local ledger. No runtime dependencies.
|
|
5
5
|
Author-email: Nikhil Bora <nikhil@wirevow.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "gitvow"
|
|
7
|
-
version = "0.34.
|
|
7
|
+
version = "0.34.2"
|
|
8
8
|
description = "Provenance and policy gate for AI-agent coding sessions: session trailers on commits, redacted session notes in git, a tool-call gate, and a local ledger. No runtime dependencies."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "Apache-2.0"}
|
|
@@ -275,6 +275,13 @@ def pre_tool_use(h: dict[str, Any], home: str | None = None) -> tuple[int, str]:
|
|
|
275
275
|
"observed" if d.observed else "finding",
|
|
276
276
|
{"tool": tool, "reason": d.reason[:200], "new": new, "session_id": h.get("session_id")},
|
|
277
277
|
)
|
|
278
|
+
if tool == "Bash" and PUSH_RE.search(inp.get("command", "")) and not os.environ.get("GITVOW_PUSHING_NOTES"):
|
|
279
|
+
# The agent is about to push. The pre-push hook steps aside while this is fresh, and PostToolUse pushes
|
|
280
|
+
# the notes after the branch has landed, so the two pushes never overlap on the server.
|
|
281
|
+
st = load_state(cwd)
|
|
282
|
+
st["pending_push"] = time.time()
|
|
283
|
+
st["pending_ttl"] = int((pol.get("decisions") or {}).get("commit_window_seconds", 1800))
|
|
284
|
+
save_state(cwd, st)
|
|
278
285
|
if tool == "Bash" and COMMIT_RE.search(inp.get("command", "")):
|
|
279
286
|
pending = dec.undecided(cwd)
|
|
280
287
|
mode = (pol.get("decisions") or {}).get("mode", "open")
|
|
@@ -462,6 +469,21 @@ def post_tool_use(h: dict[str, Any], home: str | None = None) -> tuple[int, str]
|
|
|
462
469
|
cwd = target_cwd(tool, inp, session_cwd)
|
|
463
470
|
if _duplicate(h, cwd, "post"):
|
|
464
471
|
return 0, ""
|
|
472
|
+
code, msg = _post_tool_use(h, home, tool, inp, cwd)
|
|
473
|
+
if tool == "Bash" and PUSH_RE.search(inp.get("command", "")) and not os.environ.get("GITVOW_PUSHING_NOTES"):
|
|
474
|
+
# The notes travel after the branch, never alongside it: the pre-push hook stepped aside for this push
|
|
475
|
+
# (or never ran, as under Gemini). Done last, after any note for a `commit && push` chain was attached,
|
|
476
|
+
# so the note travels with the push it belongs to.
|
|
477
|
+
st = load_state(cwd)
|
|
478
|
+
if "pending_push" in st:
|
|
479
|
+
st.pop("pending_push", None)
|
|
480
|
+
save_state(cwd, st)
|
|
481
|
+
_push_notes_after(cwd, inp.get("command", ""))
|
|
482
|
+
return code, msg
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def _post_tool_use(h: dict[str, Any], home: str | None, tool: str, inp: dict[str, Any], cwd: str) -> tuple[int, str]:
|
|
486
|
+
session_cwd = h.get("cwd") or os.getcwd()
|
|
465
487
|
if tool in EDIT_TOOLS:
|
|
466
488
|
fp = str(inp.get("file_path") or inp.get("notebook_path") or "")
|
|
467
489
|
if fp:
|
|
@@ -482,9 +504,6 @@ def post_tool_use(h: dict[str, Any], home: str | None = None) -> tuple[int, str]
|
|
|
482
504
|
return 0, ""
|
|
483
505
|
if tool == "Bash" and not COMMIT_RE.search(inp.get("command", "")):
|
|
484
506
|
_record_prompt_approval(cwd, inp, home)
|
|
485
|
-
if hooks_disabled_by_env() and PUSH_RE.search(inp.get("command", "")):
|
|
486
|
-
# the pre-push hook that carries the notes never ran; carry them now, after the push it belongs to
|
|
487
|
-
_push_notes_after(cwd, inp.get("command", ""))
|
|
488
507
|
return 0, ""
|
|
489
508
|
if tool != "Bash":
|
|
490
509
|
return 0, ""
|
|
@@ -662,7 +681,9 @@ def _push_notes_after(cwd: str, command: str) -> None:
|
|
|
662
681
|
capture_output=True,
|
|
663
682
|
text=True,
|
|
664
683
|
)
|
|
665
|
-
log_event(
|
|
684
|
+
log_event(
|
|
685
|
+
cwd, "notes_pushed_after_push", {"remote": remote, "ok": r.returncode == 0, "error": (r.stderr or "")[:120]}
|
|
686
|
+
)
|
|
666
687
|
|
|
667
688
|
|
|
668
689
|
def stop(h: dict[str, Any], home: str | None = None) -> tuple[int, str]:
|
|
@@ -72,11 +72,20 @@ PRE_PUSH_HOOK = """#!/bin/sh
|
|
|
72
72
|
# gitvow: push session notes (refs/notes/gitvow/*) to the same remote whenever a branch is pushed.
|
|
73
73
|
# The inner push re-enters this hook; GITVOW_PUSHING_NOTES stops the recursion.
|
|
74
74
|
REMOTE="$1"
|
|
75
|
-
|
|
75
|
+
GD0="$(git rev-parse --git-dir)"; STATE0="$GD0/gitvow-session.json"
|
|
76
|
+
# An agent's push: the gate saw the command and PostToolUse pushes the notes *after* the branch lands, so this
|
|
77
|
+
# hook stays out of the way. Two pushes overlapping on the server made GitHub reject the branch with
|
|
78
|
+
# "fatal error in commit_refs" (seen four times in a day of releases); one after the other never does.
|
|
79
|
+
AGENT_PUSH=""
|
|
80
|
+
if [ -f "$STATE0" ]; then
|
|
81
|
+
AGENT_PUSH=$(python3 -c "import json,time;s=json.load(open('$STATE0'));p=s.get('pending_push') or 0;w=s.get('pending_ttl') or 300;print('1' if time.time()-p<w else '')" 2>/dev/null)
|
|
82
|
+
fi
|
|
83
|
+
if [ -z "$GITVOW_PUSHING_NOTES" ] && [ -z "$AGENT_PUSH" ] && [ -n "$REMOTE" ] && git for-each-ref --count=1 refs/notes/gitvow/ | grep -q .; then
|
|
76
84
|
# Errors used to be discarded, so a notes push that failed left a record nobody could see was missing.
|
|
77
85
|
# The failure is now said once, on the hook's stderr, and never stops the branch push.
|
|
78
|
-
|
|
79
|
-
|
|
86
|
+
if GITVOW_PUSHING_NOTES=1 git push --quiet "$REMOTE" 'refs/notes/gitvow/*:refs/notes/gitvow/*' 2>"$GD0/gitvow-notes-push.err" </dev/null; then
|
|
87
|
+
sleep 1 # let the server finish committing the notes refs before the branch push reaches it
|
|
88
|
+
else
|
|
80
89
|
echo "gitvow: pushing refs/notes/gitvow/* to $REMOTE failed ($(head -c 200 "$GD0/gitvow-notes-push.err" | tr '\\n' ' ')); run: gitvow push-notes $REMOTE" >&2
|
|
81
90
|
fi
|
|
82
91
|
fi
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitvow
|
|
3
|
-
Version: 0.34.
|
|
3
|
+
Version: 0.34.2
|
|
4
4
|
Summary: Provenance and policy gate for AI-agent coding sessions: session trailers on commits, redacted session notes in git, a tool-call gate, and a local ledger. No runtime dependencies.
|
|
5
5
|
Author-email: Nikhil Bora <nikhil@wirevow.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -113,14 +113,15 @@ def test_notes_are_pushed_after_the_agents_push_when_hooks_are_off(repo, home, p
|
|
|
113
113
|
check=True,
|
|
114
114
|
)
|
|
115
115
|
assert git(bare, "for-each-ref", "refs/notes/") == ""
|
|
116
|
-
#
|
|
116
|
+
# after any agent push the notes follow, sequentially (0.34.1): hooks on or off makes no difference here
|
|
117
117
|
post_tool_use(payload("PostToolUse", "Bash", {"command": "git push origin main"}), str(home))
|
|
118
|
-
assert git(bare, "
|
|
118
|
+
assert git(bare, "rev-parse", "refs/notes/gitvow/s1") == git(repo, "rev-parse", "refs/notes/gitvow/s1")
|
|
119
|
+
assert '"kind": "notes_pushed_after_push"' in (repo / ".git" / "gitvow-hooks.log").read_text()
|
|
119
120
|
for k, v in GEMINI_ENV.items():
|
|
120
121
|
monkeypatch.setenv(k, v)
|
|
122
|
+
git(repo, "notes", "--ref=gitvow/s1", "add", "-f", "-m", 'gitvow-session\n{"x": 1}', "HEAD")
|
|
121
123
|
post_tool_use(payload("PostToolUse", "Bash", {"command": "git push origin main"}), str(home))
|
|
122
124
|
assert git(bare, "rev-parse", "refs/notes/gitvow/s1") == git(repo, "rev-parse", "refs/notes/gitvow/s1")
|
|
123
|
-
assert '"kind": "notes_pushed_by_hook"' in (repo / ".git" / "gitvow-hooks.log").read_text()
|
|
124
125
|
|
|
125
126
|
|
|
126
127
|
def test_identity_falls_back_to_the_gitconfig_file_gemini_hides(repo, home, monkeypatch):
|
|
@@ -132,3 +133,47 @@ def test_identity_falls_back_to_the_gitconfig_file_gemini_hides(repo, home, monk
|
|
|
132
133
|
monkeypatch.delenv("GIT_CONFIG_GLOBAL")
|
|
133
134
|
monkeypatch.setenv("GIT_CONFIG_GLOBAL", str(home / ".gitconfig"))
|
|
134
135
|
assert dec.identity(str(repo))[0] == "priya" # the ordinary path still reads it
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def test_the_pre_push_hook_steps_aside_for_an_agents_push(repo, home, payload, tmp_path):
|
|
139
|
+
"""The gate marks the agent's push; the hook then leaves the notes to PostToolUse, so the two never overlap."""
|
|
140
|
+
bare = tmp_path / "remote.git"
|
|
141
|
+
subprocess.run(["git", "init", "-q", "--bare", str(bare)], check=True)
|
|
142
|
+
git(repo, "remote", "add", "origin", str(bare))
|
|
143
|
+
_write_git_hook(str(repo / ".gitvow" / "git-hooks"))
|
|
144
|
+
git(repo, "config", "core.hooksPath", ".gitvow/git-hooks")
|
|
145
|
+
git(repo, "notes", "--ref=gitvow/s1", "add", "-m", "gitvow-session\n{}", "HEAD")
|
|
146
|
+
session_start(payload("SessionStart"), str(home))
|
|
147
|
+
code, _ = pre_tool_use(payload("PreToolUse", "Bash", {"command": "git push origin feature"}), str(home))
|
|
148
|
+
assert code == 0
|
|
149
|
+
st = json.loads((repo / ".git" / "gitvow-session.json").read_text())
|
|
150
|
+
assert st["pending_push"] and st["pending_ttl"] == 1800
|
|
151
|
+
subprocess.run(["git", "push", "-q", "origin", "HEAD:refs/heads/feature"], cwd=repo, check=True)
|
|
152
|
+
assert git(bare, "for-each-ref", "refs/notes/") == "" # the hook stepped aside
|
|
153
|
+
post_tool_use(payload("PostToolUse", "Bash", {"command": "git push origin feature"}), str(home))
|
|
154
|
+
assert git(bare, "rev-parse", "refs/notes/gitvow/s1") == git(repo, "rev-parse", "refs/notes/gitvow/s1")
|
|
155
|
+
assert "pending_push" not in json.loads((repo / ".git" / "gitvow-session.json").read_text())
|
|
156
|
+
# a person's push, no gate involved: the hook carries the notes itself
|
|
157
|
+
git(repo, "notes", "--ref=gitvow/s1", "add", "-f", "-m", 'gitvow-session\n{"y": 2}', "HEAD")
|
|
158
|
+
subprocess.run(["git", "push", "-q", "origin", "HEAD:refs/heads/feature2"], cwd=repo, check=True)
|
|
159
|
+
assert git(bare, "rev-parse", "refs/notes/gitvow/s1") == git(repo, "rev-parse", "refs/notes/gitvow/s1")
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def test_a_commit_and_push_chain_sends_the_note_with_the_push(repo, home, payload, transcript, tmp_path):
|
|
163
|
+
"""`git commit … && git push` takes the commit path; the notes must still follow, after the note is attached."""
|
|
164
|
+
bare = tmp_path / "remote.git"
|
|
165
|
+
subprocess.run(["git", "init", "-q", "--bare", str(bare)], check=True)
|
|
166
|
+
git(repo, "remote", "add", "origin", str(bare))
|
|
167
|
+
_write_git_hook(str(repo / ".gitvow" / "git-hooks"))
|
|
168
|
+
git(repo, "config", "core.hooksPath", ".gitvow/git-hooks")
|
|
169
|
+
session_start(payload("SessionStart"), str(home))
|
|
170
|
+
cmd = "git commit -am x && git push origin HEAD:refs/heads/feature" # not protected: no question
|
|
171
|
+
assert pre_tool_use(payload("PreToolUse", "Bash", {"command": cmd}, transcript), str(home))[0] == 0
|
|
172
|
+
(repo / "a.txt").write_text("chain\n")
|
|
173
|
+
git(repo, "commit", "-qam", "chain")
|
|
174
|
+
subprocess.run(["git", "push", "-q", "origin", "HEAD:refs/heads/feature"], cwd=repo, check=True)
|
|
175
|
+
assert git(bare, "for-each-ref", "refs/notes/") == "" # the hook stepped aside for the agent's push
|
|
176
|
+
code, msg = post_tool_use(payload("PostToolUse", "Bash", {"command": cmd}, transcript), str(home))
|
|
177
|
+
assert code == 0 and "session note attached" in msg
|
|
178
|
+
local = git(repo, "rev-parse", "refs/notes/gitvow/sess-1")
|
|
179
|
+
assert git(bare, "rev-parse", "refs/notes/gitvow/sess-1") == local # the note just written is on the remote
|
|
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
|
|
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
|
|
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
|
|
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
|