Skip to content

Commit 4978f76

Browse files
calesthioclaude
andauthored
Harden CLAUDE.md template for agent compliance (#1)
* Harden CLAUDE.md template to improve agent compliance The generated CLAUDE.md instructions were being read but not followed by agents in practice. This rewrites the template to be a behavioral contract rather than a reference document: - Mark session start and save sections as MANDATORY in headers - Add install/init fallback instructions (pip install, python -m) - Add self-check rule: "if 3+ tasks done without a save, stop and save" - Make save method platform-adaptive (background agent OR inline) - Separate reference material (tiers, querying) from action items - Add PATH warning to init when CLI is installed but not discoverable Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Address PR review feedback - Restore save complete as primary action for finished tasks (P1): The previous version directed agents to log completed work as session_log entries, leaving action_items permanently active in boot briefings. save complete is now the first command shown for task completion. - Use module fallback consistently for init and boot (P2): The recovery flow now mentions python -m sessionanchor as a drop-in replacement for all commands, not just boot. - Reword PATH warning for uvx/pipx users (P2): The warning no longer claims the package is installed when it may have been run via uvx. It now says "not found on PATH" and suggests the runner prefix or python -m fallback. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add scope guard to prevent cross-project context pollution When a user asks the agent to work on an unrelated repo mid-session, the agent should not save that work into the current project's memory. Adds an explicit instruction with a concrete example. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix launcher fallback guidance * Add CI workflow for branch protection * Make CI test path assertion portable --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent dd5141b commit 4978f76

5 files changed

Lines changed: 152 additions & 36 deletions

File tree

.github/workflows/ci.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
- "codex/**"
8+
- "harden-*"
9+
pull_request:
10+
workflow_dispatch:
11+
12+
jobs:
13+
ci:
14+
name: CI
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Check out repository
19+
uses: actions/checkout@v4
20+
21+
- name: Set up Python
22+
uses: actions/setup-python@v5
23+
with:
24+
python-version: "3.11"
25+
26+
- name: Install test and build tooling
27+
run: |
28+
python -m pip install --upgrade pip
29+
python -m pip install pytest build twine
30+
31+
- name: Run test suite
32+
run: python -m pytest -q
33+
34+
- name: Build distributions
35+
run: python -m build
36+
37+
- name: Check distribution metadata
38+
run: python -m twine check dist/*
39+
40+
- name: Smoke test installed package
41+
run: |
42+
python -m venv .venv-ci
43+
.venv-ci/bin/python -m pip install dist/*.whl
44+
.venv-ci/bin/sessionanchor --help
45+
.venv-ci/bin/python -m sessionanchor --help

src/sessionanchor/init.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,25 +77,39 @@ def run_init(repo_root: str | None = None, skip_index: bool = False) -> None:
7777

7878
conn.close()
7979

80+
# Verify CLI is discoverable on PATH
81+
cli_on_path = shutil.which("sessionanchor") is not None
82+
quick_start_prefix = "sessionanchor"
83+
8084
print()
8185
print("=" * 60)
8286
print(" SessionAnchor initialized!")
8387
print("=" * 60)
88+
89+
if not cli_on_path:
90+
quick_start_prefix = "<launcher> sessionanchor"
91+
print()
92+
print(" [!] NOTE: 'sessionanchor' was not found on PATH.")
93+
print(" Use the same launcher for future commands, e.g.")
94+
print(" 'uvx sessionanchor ...', 'pipx run sessionanchor ...',")
95+
print(" or 'python -m sessionanchor ...'.")
96+
print(" Replace <launcher> in the examples below with the one you use.")
97+
8498
print()
8599
print(" Quick start:")
86100
print()
87101
print(" # At session start - get your briefing")
88-
print(" sessionanchor boot")
102+
print(f" {quick_start_prefix} boot")
89103
print()
90104
print(" # Save a decision")
91-
print(' sessionanchor save add --tier L1 --category decision \\')
105+
print(f' {quick_start_prefix} save add --tier L1 --category decision \\')
92106
print(' --title "Chose SQLite" --content "Zero deps, local-only"')
93107
print()
94108
print(" # Search memory")
95-
print(' sessionanchor query "deployment"')
109+
print(f' {quick_start_prefix} query "deployment"')
96110
print()
97111
print(" # End of session")
98-
print(' sessionanchor save session-end --summary "Built auth flow"')
112+
print(f' {quick_start_prefix} save session-end --summary "Built auth flow"')
99113
print()
100114
print(" CLAUDE.md has been configured to instruct Claude to use")
101115
print(" this memory system automatically.")

src/sessionanchor/templates/claude_md_section.txt

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,53 +2,78 @@
22
## Context Management
33

44
This project uses [SessionAnchor](https://github.com/calesthio/SessionAnchor) for session continuity.
5-
**Do NOT read large context files at session start.** Use the memory system instead.
65

7-
### Session Start Protocol
6+
### MANDATORY: Session Start (do this FIRST, before any other work)
87

8+
Run this before doing anything else:
99
```bash
10-
# 1. Get your briefing (~2000 tokens instead of reading all project files)
1110
sessionanchor boot
11+
```
12+
- If `command not found`: use the same launcher you used for SessionAnchor for all commands below:
13+
`uvx sessionanchor ...`, `pipx run sessionanchor ...`, or `python -m sessionanchor ...`.
14+
If SessionAnchor is not installed yet, run `pip install sessionanchor`, then retry.
15+
- If `No memory database found`: run the matching init command with the same launcher
16+
(`sessionanchor init`, `uvx sessionanchor init`, `pipx run sessionanchor init`,
17+
or `python -m sessionanchor init`), then boot the same way.
18+
- Read the output — it contains prior decisions, active work, and blockers from previous sessions.
19+
- Do NOT skip this. Do NOT read large project files as a substitute.
1220

13-
# 2. If you need deeper context on any topic during the session:
14-
sessionanchor query "search terms"
21+
### MANDATORY: Save After Every Substantive Action
1522

16-
# 3. If you need to understand code structure:
17-
sessionanchor index find "topic"
18-
sessionanchor index map
19-
```
23+
You MUST save context after each of these — do NOT batch, do NOT wait for session end:
24+
- A feature, fix, or deliverable is completed
25+
- A decision is made (technical choice, architecture, tradeoff)
26+
- A blocker is discovered
27+
- You learn something non-obvious about the codebase
28+
29+
**How to save:**
30+
- If your platform supports background agents: spawn one with the save command
31+
- Otherwise: run the save command inline before continuing
2032

21-
### Context Saving (MANDATORY - do not skip)
33+
```bash
34+
# Decision made
35+
sessionanchor save add --tier L1 --category decision --title "..." --content "..."
2236

23-
**Save context continuously, not just at session end.** After completing any substantive work
24-
(a feature, a fix, a decision, a research task), immediately write the relevant context to memory.
25-
Do NOT wait for the user to ask. Do NOT wait for "end of session." The user may close the
26-
window at any time.
37+
# Task/feature completed (marks the action item as completed)
38+
sessionanchor save complete --title "..." --category action_item
2739

28-
**How to save: use a subagent.** Do NOT block the main conversation. Spawn a background agent
29-
with the save commands and continue working.
40+
# Significant event or milestone worth logging
41+
sessionanchor save add --tier L1 --category session_log --title "..." --content "..."
3042

31-
**When to save (trigger immediately after any of these):**
32-
- A decision is made -> `sessionanchor save add --tier L1 --category decision --title "..." --content "..."`
33-
- A task is completed -> `sessionanchor save complete --title "..." --category action_item`
34-
- A new blocker is discovered -> `sessionanchor save add --tier L1 --category blocker --title "..." --content "..."`
35-
- New technical knowledge -> `sessionanchor save add --tier L2 --category technical_note --title "..." --content "..."`
36-
- Significant code changes -> `sessionanchor save add --tier L1 --category session_log --title "..." --content "..."`
43+
# Blocker found
44+
sessionanchor save add --tier L1 --category blocker --title "..." --content "..."
3745

38-
**Session summary (run when work naturally winds down):**
39-
```bash
40-
sessionanchor save session-end --summary "What was accomplished"
46+
# Technical knowledge learned
47+
sessionanchor save add --tier L2 --category technical_note --title "..." --content "..."
4148
```
4249

43-
### Memory Tiers
50+
**Self-check: if you have completed 3+ tasks without running a single save command, stop and save now.**
51+
52+
**Scope guard: only save context that is relevant to THIS project.** If the user asks you to
53+
work on an unrelated repo, fix an external tool, or do research that does not affect this
54+
codebase, do NOT save that work here — it is noise. Example: if this project is a web app and
55+
the user asks you to debug a CLI tool in a different repo mid-session, that debugging context
56+
does not belong in this project's memory.
4457

45-
- **L0** (~500 tokens): Identity, current phase, top priorities. Always loaded.
46-
- **L1** (~1500 tokens): Recent decisions, active action items, blockers. Loaded at boot.
47-
- **L2** (on-demand): Full history, completed items, deep technical notes. Query when needed.
58+
### Session End
4859

49-
### Re-indexing
60+
When work winds down:
61+
```bash
62+
sessionanchor save session-end --summary "What was accomplished"
63+
```
64+
65+
### Reference: Querying Deep Context
5066

51-
Run after major structural changes:
5267
```bash
68+
# Search memory for a topic
69+
sessionanchor query "search terms"
70+
71+
# Re-index after major structural changes (new directories, renamed modules)
5372
sessionanchor index
5473
```
74+
75+
### Reference: Memory Tiers
76+
77+
- **L0** (~500 tokens): Identity, current phase, top priorities. Always loaded at boot.
78+
- **L1** (~1500 tokens): Recent decisions, active items, blockers. Loaded at boot.
79+
- **L2** (on-demand): Full history, completed items, deep technical notes. Use `sessionanchor query` to retrieve.

tests/test_e2e_cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ def test_index_find_and_map_cover_repo_workflow(repo: Path):
339339
conn.close()
340340

341341
assert "Indexed" in index_result.stdout
342-
assert "src\\auth.py" in find_result.stdout
342+
assert str(Path("src") / "auth.py") in find_result.stdout
343343
assert ".env" not in find_result.stdout
344344
assert "## Modules" in map_result.stdout
345345
assert "## File Tree" in map_result.stdout

tests/test_init.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,38 @@ def test_init_creates_claude_md(temp_repo):
4848
assert "sessionanchor boot" in content
4949

5050

51+
def test_claude_md_has_mandatory_sections(temp_repo):
52+
run_init(repo_root=temp_repo, skip_index=True)
53+
claude_md = os.path.join(temp_repo, "CLAUDE.md")
54+
with open(claude_md) as f:
55+
content = f.read()
56+
# Mandatory start protocol
57+
assert "MANDATORY: Session Start" in content
58+
# Mandatory save protocol
59+
assert "MANDATORY: Save After Every Substantive Action" in content
60+
# Self-check instruction
61+
assert "Self-check" in content
62+
# Install fallback
63+
assert "pip install sessionanchor" in content
64+
# python -m fallback
65+
assert "python -m sessionanchor" in content
66+
# runner fallback for ephemeral installs
67+
assert "uvx sessionanchor" in content
68+
# completion wording should stay accurate
69+
assert "marks the action item as completed" in content
70+
assert "leaves the boot briefing" not in content
71+
72+
73+
def test_init_warns_when_cli_not_on_path(temp_repo, capsys, monkeypatch):
74+
import sessionanchor.init as init_module
75+
monkeypatch.setattr(init_module.shutil, "which", lambda _name: None)
76+
run_init(repo_root=temp_repo, skip_index=True)
77+
captured = capsys.readouterr()
78+
assert "not found on PATH" in captured.out
79+
assert "python -m sessionanchor" in captured.out
80+
assert "<launcher> sessionanchor" in captured.out
81+
82+
5183
def test_init_patches_existing_claude_md(temp_repo):
5284
claude_md = os.path.join(temp_repo, "CLAUDE.md")
5385
with open(claude_md, "w") as f:

0 commit comments

Comments
 (0)