From bc9d4e050caf6d74075ef41193931a416c12baae Mon Sep 17 00:00:00 2001 From: Michael Panchenko Date: Wed, 13 May 2026 16:10:18 +0200 Subject: [PATCH] Feature: added memory maintenance memory as part of onboarding Rephrased onboarding prompt to adjust to the recommended memory management --- .../generated/generated_prompt_factory.py | 42 ++++++------- src/serena/project.py | 37 +++++++++++ .../prompt_templates/simple_tool_outputs.yml | 61 +++++++++++-------- src/serena/resources/memory_maintenance.md | 40 ++++++++++++ src/serena/tools/workflow_tools.py | 5 +- 5 files changed, 134 insertions(+), 51 deletions(-) create mode 100644 src/serena/resources/memory_maintenance.md diff --git a/src/serena/generated/generated_prompt_factory.py b/src/serena/generated/generated_prompt_factory.py index a1ee2920..fedf4142 100644 --- a/src/serena/generated/generated_prompt_factory.py +++ b/src/serena/generated/generated_prompt_factory.py @@ -14,39 +14,31 @@ class PromptFactory(PromptFactoryBase): """ def get_info_jet_brains_debug_repl_template_string(self) -> str: - return self.get_prompt_template_string("info_jet_brains_debug_repl") - + return self.get_prompt_template_string('info_jet_brains_debug_repl') + def create_info_jet_brains_debug_repl(self) -> str: - return self._render_prompt("info_jet_brains_debug_repl", locals()) + return self._render_prompt('info_jet_brains_debug_repl', locals()) def get_onboarding_prompt_template_string(self) -> str: - return self.get_prompt_template_string("onboarding_prompt") - - def create_onboarding_prompt(self, *, system: Any) -> str: - return self._render_prompt("onboarding_prompt", locals()) + return self.get_prompt_template_string('onboarding_prompt') + + def create_onboarding_prompt(self, *, memory_maintenance_name: Any, system: Any) -> str: + return self._render_prompt('onboarding_prompt', locals()) def get_connection_prompt_template_string(self) -> str: - return self.get_prompt_template_string("connection_prompt") - + return self.get_prompt_template_string('connection_prompt') + def create_connection_prompt(self) -> str: - return self._render_prompt("connection_prompt", locals()) + return self._render_prompt('connection_prompt', locals()) def get_system_prompt_template_string(self) -> str: - return self.get_prompt_template_string("system_prompt") - - def create_system_prompt( - self, - *, - available_markers: Any, - available_tools: Any, - context_system_prompt: Any, - global_memories_list: Any, - mode_system_prompts: Any, - ) -> str: - return self._render_prompt("system_prompt", locals()) + return self.get_prompt_template_string('system_prompt') + + def create_system_prompt(self, *, available_markers: Any, available_tools: Any, context_system_prompt: Any, global_memories_list: Any, mode_system_prompts: Any) -> str: + return self._render_prompt('system_prompt', locals()) def get_cc_system_prompt_override_template_string(self) -> str: - return self.get_prompt_template_string("cc_system_prompt_override") - + return self.get_prompt_template_string('cc_system_prompt_override') + def create_cc_system_prompt_override(self) -> str: - return self._render_prompt("cc_system_prompt_override", locals()) + return self._render_prompt('cc_system_prompt_override', locals()) diff --git a/src/serena/project.py b/src/serena/project.py index dceb65a7..21c5b3e1 100644 --- a/src/serena/project.py +++ b/src/serena/project.py @@ -93,6 +93,43 @@ class MemoriesManager: name = cls._prepare_name(name) return cls._MEMORY_REF_PREFIX + name + MEMORY_MAINTENANCE_NAME: str = "memory_maintenance" + _MEMORY_MAINTENANCE_TEMPLATE_PATH: Path = Path(__file__).parent / "resources" / "memory_maintenance.md" + + def ensure_memory_maintenance_memory(self) -> str: + """ + Ensures a memory describing how memories should be maintained exists for this project, + and returns the name to reference it by. + + Precedence: + + 1. If a global copy exists at ``global/memory_maintenance``, return that name; no + project copy is created (the global version takes precedence). + 2. Else if a project copy already exists, return its name unchanged. + 3. Else seed a project copy from the package-shipped template and return that name. + + Existing memory files are never overwritten; users may have customized them. To + refresh from the shipped template, delete the existing memory first. + + :return: the bare name to reference the maintenance memory by (without the ``mem:`` + prefix); either ``"global/memory_maintenance"`` or ``"memory_maintenance"``. + :raises FileNotFoundError: if the shipped template is missing on disk. + :raises AssertionError: if this manager has no associated project directory. + """ + global_name = f"{self.GLOBAL_TOPIC}/{self.MEMORY_MAINTENANCE_NAME}" + if self.get_memory_file_path(global_name).exists(): + return global_name + if self.get_memory_file_path(self.MEMORY_MAINTENANCE_NAME).exists(): + return self.MEMORY_MAINTENANCE_NAME + + # seed a project copy from the shipped template + template_path = self._MEMORY_MAINTENANCE_TEMPLATE_PATH + if not template_path.exists(): + raise FileNotFoundError(f"Memory maintenance template not found at {template_path}") + content = template_path.read_text(encoding=self._encoding) + self.save_memory(self.MEMORY_MAINTENANCE_NAME, content, is_tool_context=False) + return self.MEMORY_MAINTENANCE_NAME + def rename_references_to_memory(self, content: str, old_name: str, new_name: str) -> tuple[str, int]: r""" Replaces all occurrences of a memory reference (e.g. ``mem:foo``) in ``content`` with diff --git a/src/serena/resources/config/prompt_templates/simple_tool_outputs.yml b/src/serena/resources/config/prompt_templates/simple_tool_outputs.yml index c6f2c619..168840ff 100644 --- a/src/serena/resources/config/prompt_templates/simple_tool_outputs.yml +++ b/src/serena/resources/config/prompt_templates/simple_tool_outputs.yml @@ -4,33 +4,44 @@ prompts: onboarding_prompt: | You are viewing the project for the first time. - Your task is to assemble relevant high-level information about the project which - will be saved to memory files in the following steps. - The information should be sufficient to understand what the project is about, - and the most important commands for developing code. + Your task is to assemble durable, non-obvious information about the project and write it + to memory files that future agents will consult. The project is being developed on the system: {{ system }}. - You need to identify at least the following information: - * the project's purpose - * the tech stack used - * the code style and conventions used (including naming, type hints, docstrings, etc.) - * which commands to run when a task is completed (linting, formatting, testing, etc.) - * the rough structure of the codebase - * the commands for testing, formatting, and linting - * the commands for running the entrypoints of the project - * the util commands for the system, like `git`, `ls`, `cd`, `grep`, `find`, etc. Keep in mind that the system is {{ system }}, - so the commands might be different than on a regular unix system. - * whether there are particular guidelines, styles, design patterns, etc. that one should know about + **Before writing anything, read `mem:{{ memory_maintenance_name }}`** using the `read_memory` tool. + It defines the style, naming, reference, and add/update threshold conventions that every + memory in this project must follow. Do not skip this step. - This list is not exhaustive, you can add more information if you think it is relevant. + Target memory layout — use the `write_memory` tool, one call per memory: - For doing that, you will need to acquire information about the project with the corresponding tools. - Read only the necessary files and directories to avoid loading too much data into memory. - If you cannot find everything you need from the project itself, you should ask the user for more information. + * `mem:core` — top-level routing/source map: a terse index pointing at the focused memories + below, plus only those project-wide invariants that don't belong in a focused memory. + * `mem:tech_stack` — language(s), framework(s), build tools, package manager, version + pins where they matter. + * `mem:suggested_commands` — project commands the user will actually run (dev, test, lint, + format, run entrypoints) and any system util commands (`git`, `ls`, `grep`, …) whose + form differs on {{ system }} from a standard unix shell. Skip generic commands that + behave identically across systems. + * `mem:conventions` — code style, naming, type hints, docstring conventions, design + patterns specific to this codebase. + * `mem:task_completion` — exact commands to run when a coding task is considered done + (linter, formatter, test runner, type checker, etc.). - After collecting all the information, you will use the `write_memory` tool (in multiple calls) to save it to various memory files. - A particularly important memory file will be the `suggested_commands.md` file, which should contain - a list of commands that the user should know about to develop code in this project. - Moreover, you should create memory files for the style and conventions and a dedicated memory file for - what should be done when a task is completed. - **Important**: after done with the onboarding task, remember to call the `write_memory` to save the collected information! + If the project has clearly distinct modules (e.g. frontend/backend), create per-module + `mem:/core` memories for module-specific routing instead of pushing everything + into `mem:core`. + + Acquire information using the available tools. Read only the files needed; do not load + entire directory trees. If essential information is missing, ask the user. + + Memory body style (full rules in `mem:{{ memory_maintenance_name }}`): dense agent notes — + terse bullets, invariants, paths, commands. No self-referential cues ("Use when…", + "Start here…", "Read before…"); routing belongs in `mem:core` upstream, not inside the + body it points to. References to other memories use `` `mem:NAME` `` (backticked, with + the `mem:` prefix). + + When all memories are written, sanity-check the references by mentioning that the user can run + `serena memories check` from the project root. + + **Important**: the onboarding is only complete once you have actually called `write_memory` + for each memory above — do not summarize the content in chat and skip writing. diff --git a/src/serena/resources/memory_maintenance.md b/src/serena/resources/memory_maintenance.md new file mode 100644 index 00000000..567d39c3 --- /dev/null +++ b/src/serena/resources/memory_maintenance.md @@ -0,0 +1,40 @@ +# Memory Maintenance + +## Discovery Model + +- Agents start with the full memory name list and should read `mem:core` first as the routing entry point. +- All other memory discovery comes from memory names plus routing already read. +- Routing is useful only before the target memory is selected. Once a memory is open, target-selection text inside it can no longer help decide to read it. +- Therefore, never put self-referential selection cues in a memory body: no "Start here", "Use when", "Use before", "Use after", or "read this when..." openers. + Such self-referential cues are an error mode: they are unreachable for selection, duplicate routing that belongs upstream, and drift independently from the actual routing graph. +- Add routing only when names are insufficient, ambiguous, or a non-obvious dependency/order matters. Do not add tautological routing such as "frontend debugging -> frontend/debugging". +- Routing can live outside core memories when needed, but core memories own most module-level routing because they are read before focused memories in that subfolder. +- Top-level/startup memory is `mem:core`; top-level memory in each memory subfolder is `/core` (for example `mem:frontend/core`). +- Memory references must use a mem: prefix inside backticks, e.g. `mem:frontend/core`. + +## Style + +Dense agent notes, not prose docs. Prefer invariants, paths, commands, terse bullets. Avoid obvious context, rationale, and examples unless they prevent likely mistakes. Keep guidance durable and generalizable, not task-local. + +## Add/update threshold + +Add or update memory only for stable, non-obvious project conventions that avoid complex rediscovery. + +Do not add: quick-read/rg facts; generic language/framework knowledge; one-off task notes; volatile line-level details; behavior likely to change soon. + +Operational constants (commands, paths, version pins): phrase as current behavior; before changing the value, update it in the owning memory and run `serena memories check` to catch references that need updating. + +## Graph Ownership + +- Project-level routing: `mem:core`. +- Module-level routing/source maps: module core memories. +- Focused memories: topic details only; assume relevant routing was read before selection. + +## Maintenance Actions + +- Renamed/removed/split/merged/superseded memory: update all references. Done automatically if handled via Serena's memory rename tool. +- Stale paths/namespaces/commands/behavior: update or remove in owning memory. If working with Serena, call `serena memories check` for a report. +- Split catch-all memory when sections are commonly needed independently or agents must read unrelated domains to reach needed topic. +- Retire/delete memory when owning module/subsystem is removed or replaced; remove all references. +- Added/renamed/split/moved/deleted memory: update affected routing only where names are insufficient. +- Before finishing: list memories for old names and key new names to catch stale/missing references. \ No newline at end of file diff --git a/src/serena/tools/workflow_tools.py b/src/serena/tools/workflow_tools.py index 461da9ea..7d5942d1 100644 --- a/src/serena/tools/workflow_tools.py +++ b/src/serena/tools/workflow_tools.py @@ -54,7 +54,10 @@ class OnboardingTool(Tool): if not write_memory_tool_available: return "Memory writing tool not activated, skipping onboarding." system = platform.system() - return self.prompt_factory.create_onboarding_prompt(system=system) + # seed the project-local memory-maintenance memory (or detect a global override) so + # the prompt can point the agent at the conventions before it writes anything + memory_maintenance_name = self.memories_manager.ensure_memory_maintenance_memory() + return self.prompt_factory.create_onboarding_prompt(system=system, memory_maintenance_name=memory_maintenance_name) class InitialInstructionsTool(Tool, ToolMarkerDoesNotRequireActiveProject):