Skip to content

tier2: verify in-workspace repos against the session-start snapshot (r3 M1, delta LOW-1) - #6

Open
Vibherpunk wants to merge 1 commit into
mainfrom
fix/tier2-inworkspace-snapshot-baseline
Open

Vibherpunk wants to merge 1 commit into
mainfrom
fix/tier2-inworkspace-snapshot-baseline

Conversation

@Vibherpunk

Copy link
Copy Markdown
Owner

Follow-up to #3: round-3 review r3 M1 and delta LOW-1.

Problem (r3 M1)

Repositories inside the workspace still used get_or_set_session_baseline. That function is lazy: it records HEAD after whatever tool ran first, and handle_post_tool_use calls it for every tool cwd. It falls back to refs/hardtruth/baseline/<conv_hash> and a halt-dir cache, both of which the agent can write. A conftest tamper committed through the shell before the first edit therefore became the baseline, and it passed Phase 5, Phase 8 and the Tier 2 tamper check.

Change (client/hardtruth_hook.py)

If SessionStart registered a snapshot for the workspace repo, or for an in-workspace target's git top-level (trusted_repo_baseline):

  • Phase 5: lineage is checked against the snapshot. The stash is compared by value with the session-start refs/stash, not by its %ct, which can be backdated. The code is factored out as check_repo_lineage_and_stash.
  • Phases 6 and 8: changed lines and assertion counts are computed against the snapshot. git show <sha>:./<rel> is used so that a workspace below the top-level works.
  • Phase 9: check_outside_repo_integrity now also runs for in-workspace targets. It checks lineage, stash, manifest/conftest tamper with the explicit baseline, assertion deletion and tautologies, anchored at the target's toplevel.
  • Snapshots under id-less session ids (unknown, default) are never trusted, because every such session shares one daemon key.

No snapshot for an in-workspace repo (the decision)

Today's lazy behaviour is kept by default. A snapshot can be missing because:

Failing closed by default would halt every one of those sessions. HARDTRUTH_REQUIRE_SESSION_SNAPSHOT=1 opts into failing closed, with a non-remediable halt. Repositories outside the workspace still always fail closed without a snapshot. The policy is documented in the "Repositories inside the workspace" comment block.

delta LOW-1

The snapshot_repos_at_session_start docstring now gives the overrun bound as up to 3s. A worker already running makes two sequential 1.5s calls, and the process exits only after in-flight workers finish. Previously it said one call (1.5s). A new test measures this: the last call ends more than budget + 1.5s and less than budget + 3s (+0.5s slack) after the start.

Tests

New file tests/test_tier2_in_workspace_snapshot.py (13 tests). They run end to end through the real handle_session_start and handle_stop with the fake first-write-wins daemon; the class subclasses TestStopHookOutsideRepos with the workspace being the repo. Cases:

  • a conftest tamper shell-committed before the first edit halts. The lazy baseline is asserted to be the tampered HEAD;
  • a planted refs/hardtruth/baseline/* ref plus halt-dir cache files are ignored;
  • a backdated tamper commit halts;
  • assertion deletion committed before the first edit halts;
  • a backdated stash halts;
  • resetting below the snapshot gives E4;
  • a subproject target inside the workspace repo halts;
  • with no snapshot: allowed by default (the documented gap), and fails closed with HARDTRUTH_REQUIRE_SESSION_SNAPSHOT=1;
  • strict mode with a snapshot still allows a clean edit;
  • snapshots stored under unknown or default are ignored;
  • the LOW-1 bound.

With hardtruth_hook.py from main, 6 of the halt tests fail (they allow the Stop).

Full suite: python3 -m pytest tests/ -q -p no:cacheprovider

  • main a77df23 (clean worktree): 6 failed, 218 passed, 9 skipped. The failures are the 3 known ones (reachy test_04, TestMacosNativeGateMessage, v21 test_14) plus 3 in test_concurrency_load (load-flaky).
  • This branch: 4 failed, 236 passed, 6 skipped. The failures are the same 3 known ones plus 1 in test_concurrency_load (test_concurrent_secret_isolation_and_no_data_leakage, connection resets; it also failed on main).

Not changed / notes

  • get_git_modified_source_files still lists modified files against the lazy baseline. Committed test weakening outside modified_paths is now caught by the Phase 9 whole-repo diff for snapshotted repos.
  • The daemon-side Tier 2 tamper check (check_manifest_tampering without an explicit baseline) is unchanged. The hook now runs the snapshot-anchored check before the handoff.
  • Conflicts with tier2: per-worktree session baselines; one-call session-start snapshot #5, which replaces the per-repo daemon keys with a digest-registered manifest and removes the two-call worker. If tier2: per-worktree session baselines; one-call session-start snapshot #5 lands first, this branch only needs trusted_repo_baseline and the stash lookup routed through the manifest, and the LOW-1 docstring/test reworked for the one-call design. The in-workspace wiring (workspace_snapshot_baseline, Phases 5/6/8/9) stays as is.

Not installed into ~/.hardtruth.

🤖 Generated with Claude Code

Follow-up r3 M1 from the PR #3 review. Repositories inside the workspace
still used get_or_set_session_baseline, which is lazy (HEAD after whatever
tool ran first; PostToolUse calls it for every tool cwd) and falls back to a
git ref and a halt-dir cache the agent can write. A conftest tamper committed
through the shell before the first edit therefore became the baseline and
passed Phase 5, Phase 8 and the Tier 2 tamper check.

When SessionStart snapshotted the repository (trusted_repo_baseline):
- Phase 5: lineage against the snapshot; stash compared by value with the
  session-start refs/stash instead of by its agent-settable %ct.
- Phases 6 and 8: changed lines and assertion counts against the snapshot
  (git show <sha>:./<rel>, so a workspace below the top-level works).
- Phase 9: check_outside_repo_integrity (lineage, stash, manifest/conftest
  tamper with the explicit baseline, assertion deletion, tautologies) now
  runs for in-workspace targets too, anchored at the target's git top-level.

No snapshot for an in-workspace repository (another harness, hook installed
mid-session, id-less session, SessionStart budget exhausted): today's lazy
behaviour is kept, because failing closed would halt every such session.
HARDTRUTH_REQUIRE_SESSION_SNAPSHOT=1 opts into failing closed (non-remediable).
Repositories outside the workspace still always fail closed without one.

Snapshots under id-less session ids ("unknown", "default") are never trusted:
every such session shares one daemon key.

delta LOW-1: the snapshot_repos_at_session_start docstring now says the hook
can outlive its budget by up to 3s (a running worker makes two sequential
1.5s calls), not one call; a test measures it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant