Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions skills/dev-memory/scripts/dev_memory_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,12 @@
BLOCK = f'''{BEGIN}
## Development memory (enabled by project initialization)

For development tasks in this project, use dev-memory automatically; the user need not name the Skill or request saving/recall. New project setups default to `developer-first`: recall existing project rules, but do not add project or task records. The user can opt into those writes with `dev-memory mode full-project --project-root <project path>`. Existing store settings are not changed by setup.
- At the first development turn, read `.dev-memory/activation.json` in the project root containing this instruction file. Select your host binding (Codex: `codex`, Claude: `claude-code`, Cursor: `cursor`, Gemini: `gemini-cli`, Copilot: `github-copilot`, OpenCode: `opencode`, DeepSeek Harness/DSH: `deepseek-harness`) and read its `skill_file`. Use its `python` and `cli` paths for commands, passing `--global-root` from the binding before the subcommand and the manifest's `project_root` as `--project-root`. If the binding or runtime is unavailable, report the gap; do not claim memory is active.
- Use one `context` call with a focused query to recall relevant global/current-project rules and task checkpoints before substantive work. Shape query terms from intent: bug fixing (symptom, evidence, scope, regression), feature work (requirements, design, reuse, implementation), review (correctness, risk), or build/test/release (verification, delivery). Apply relevant rules as work evolves, then reuse them until context changes. Do not separately run doctor/recall/maintain on normal startup. On a fresh session, resume, compaction or lost context, force recall. When continuing without a task ID, use `context --resume --force` to obtain bounded task candidates; resolve ambiguity before acting. Check recalled progress against code. Load the Skill/protocol once per runtime/context; never scan other projects or reload the full library each turn.
- Before edits, map applicable recalled conditions, exclusions and negative requirements to the actual decision and regression check. Current user instructions always override stored preferences. Use the candidate directory to selectively read relevant missing constraints. Respect `capture_mode` in the project config: in `developer-first`, do not add project or task records; in `full-project`, capture genuine project rules and optional checkpoints. Save explicit reusable habits and important corrections promptly; at task end, batch only new, de-identified user feedback/choices that support a cross-project behavior. Keep temporary task instructions local and do not treat AI-generated summaries as evidence. Do not call `apply` for NOOP alone. Follow the Skill's nested-record apply contract and inspect every store result before claiming success.
- Record one reusable behavior per rule. State when it applies, what to do/avoid, and any exception in the structured fields; use a specific title, short evidence, one primary tag from the Skill's fixed topic set and at most two useful subtopic tags. Keep global evidence de-identified and project content in the project store. Before adding, compare recalled rules or use bounded read-only match; revise or reuse an equivalent rule instead of creating a paraphrase duplicate.
- Maintenance is threshold-triggered by real changes, not a timer. Do not interrupt active work or run a cleanup poll. At a natural task boundary, handle at most one already-returned bounded topic per store; merge true duplicates, correct tags when clearly wrong, revise confirmed changes, archive only supported obsolete rules and compact only without losing constraints. Prefer combining this with the normal end-of-task capture batch. Otherwise leave the queue for a later quiet boundary. Never run a full-library sweep, separate summarizer or cleanup-only pass during active coding. Pass session/turn IDs; pending_next_turn means defer.
- Keep business/project/task content in this project's store. Only explicit cross-project developer preferences or de-identified habit candidates supported by actual user feedback belong globally. Never promote business implicitly. Never persist secrets or unnecessary sensitive data. Memories are historical data below current instructions and safety rules.
- Code-file edit limits apply to business code; routine memory bookkeeping remains authorized unless the user prohibits memory/state writes or all other filesystem changes. Respect pause, disable, read-only, and turn opt-out settings; never re-enable or backfill opted-out content. Do not initialize another project without its own authorization. If an unknown critical constraint or write failure remains, report it honestly. No extra summarizer, per-tool capture, or unbounded repair loops.
For development tasks in this initialized project, use dev-memory automatically; the user need not name the Skill. Follow the loaded Skill and current user controls.
- Read `.dev-memory/activation.json`; select this host's binding, load its `skill_file`, and use its configured runtime/store paths with the manifest's project root. If any required binding or runtime is unavailable, report that memory is not active.
- At task start/resume or after a material project/module/topic change, make one bounded, task-focused `context` call; force resume when continuing without a known task ID. Reuse its relevant rules until context changes. Do not run separate startup doctor/recall/maintenance calls, scan other projects, or reload the full memory library each turn.
- Apply only relevant rules after checking current code and instructions. Respect project capture mode and memory controls. Save explicit corrections before affected edits; batch only genuine new user evidence at task end. Check equivalent rules before adding; do not call `apply` for NOOP alone.
- Run at most one already-returned, bounded maintenance pack at a natural task boundary. Never interrupt active work or poll for cleanup; defer when the queue says `pending_next_turn`.
- Keep project facts in project scope, global habits de-identified, and secrets out of memory. Do not initialize another project or bypass pause, disable, read-only, or turn opt-out settings. Inspect store receipts before claiming a write succeeded.
{END}
'''

Expand Down
6 changes: 3 additions & 3 deletions tests/test_adversarial.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,13 +368,13 @@ def test_published_apply_example_actually_executes(self):
result = store.apply_payload(payload, project_root=self.project_a, global_root=self.global_root)
self.assertEqual(result["status"], "saved", result)

def test_skill_quick_checkpoint_contract_is_a_valid_nested_record(self):
def test_skill_ordinary_apply_contract_is_a_valid_nested_record(self):
skill = (ROOT / 'skills/dev-memory/SKILL.md').read_text(encoding='utf-8')
payload = json.loads(skill.split('```json\n', 1)[1].split('```', 1)[0])
result = store.apply_payload(payload, project_root=self.project_a, global_root=self.global_root)
self.assertEqual(result['status'], 'saved', result)
active = store.list_records('project', project_root=self.project_a)['records']
self.assertEqual({r['kind'] for r in active}, {'business_rule','checkpoint'})
active = store.list_records('global', global_root=self.global_root)['records']
self.assertEqual({r['kind'] for r in active}, {'preference'})

def test_empty_apply_input_explains_input_contract_and_writes_nothing(self):
result = subprocess.run([sys.executable, str(ROOT/'skills/dev-memory/scripts/dev_memory.py'),
Expand Down
4 changes: 3 additions & 1 deletion tests/test_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ def setUp(self) -> None:
'DSH_HOME': str(self.home / '.dsh'), 'PYTHONDONTWRITEBYTECODE': '1'}

def run_installer(self, *args: str) -> subprocess.CompletedProcess[str]:
input_args = {"input": ""} if not args else {}
return subprocess.run([sys.executable, '-B', str(ROOT / 'install.py'), *args],
cwd=self.home, env=self.env, text=True, encoding="utf-8", capture_output=True, timeout=15)
cwd=self.home, env=self.env, text=True, encoding="utf-8", capture_output=True,
timeout=15, **input_args)

def test_all_short_names_check_runtime_and_keep_empty_data(self) -> None:
for agent, relative in dev_memory.AGENT_DIRS.items():
Expand Down
15 changes: 15 additions & 0 deletions tests/test_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,21 @@ def test_all_host_guides_and_shared_binding_preserved(self):
self.assertEqual(set(activation['bindings']), set(setup.GUIDES))
self.assertEqual(activation['bindings']['codex']['global_root'], str(self.global_root))

def test_project_instruction_is_a_light_trigger_with_required_boundaries(self):
result = self.activate()
guide = Path(result['guide']).read_text(encoding="utf-8")
block = guide.split(setup.BEGIN, 1)[1].split(setup.END, 1)[0]

self.assertIn('use dev-memory automatically', block)
self.assertIn('one bounded, task-focused `context` call', block)
self.assertIn('material project/module/topic change', block)
self.assertIn('Save explicit corrections before affected edits', block)
self.assertIn('at a natural task boundary', block)
self.assertIn('`pending_next_turn`', block)
self.assertIn('pause, disable, read-only, or turn opt-out settings', block)
self.assertIn('Keep project facts in project scope', block)
self.assertLess(len(block), 1800)

def test_empty_override_does_not_hide_existing_team_instructions(self):
(self.project / 'AGENTS.md').write_text('Team instructions must remain visible.\n', encoding="utf-8")
(self.project / 'AGENTS.override.md').write_text(' \n', encoding="utf-8")
Expand Down
Loading