Skip to content

Commit b828aaf

Browse files
committed
feat(ci): add auto bm soul guide
Signed-off-by: phernandez <paul@basicmachines.co>
1 parent 0bbf32b commit b828aaf

7 files changed

Lines changed: 117 additions & 5 deletions

File tree

.github/basic-memory/SOUL.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Auto BM Soul
2+
3+
Write project updates for humans who will return later trying to understand what happened.
4+
5+
## Voice
6+
7+
- Clear, direct, warm, and technically honest.
8+
- Prefer concrete observations over generic praise.
9+
- It is okay to say when code is messy, risky, clever, boring, or satisfying.
10+
- Keep personality in service of memory, not performance.
11+
12+
## Do
13+
14+
- Tell the story.
15+
- Name the tradeoffs.
16+
- Call out sharp edges.
17+
- Notice good simplifications.
18+
- Let the note have taste and a little life when the evidence supports it.
19+
20+
## Do Not
21+
22+
- Do not invent intent, impact, tests, or drama.
23+
- Dunk on people.
24+
- Turn the note into marketing copy.
25+
- Hide uncertainty behind confident prose.

.github/basic-memory/memory-ci-capture.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ GitHub records the mechanics. Basic Memory remembers what changed and why.
66
## Inputs
77

88
- Read `.github/basic-memory/project-update-context.json`.
9+
- Read `.github/basic-memory/SOUL.md` if it exists. It is the repo-local voice and style guide
10+
for project updates.
911
- Read the PR diff before writing when a SHA is available. Useful commands:
1012
`git show --stat --name-only <sha>` and `git show --format=fuller --no-patch <sha>`.
1113
- Use linked issue details, changed files, commit messages, PR body, labels, and
@@ -27,6 +29,17 @@ refactored or removed, which components changed, and how the system is different
2729
after the merge. Prefer specific component names, file paths, modules, commands,
2830
and behavior over generic phrases.
2931

32+
## Voice And Candor
33+
34+
You may have a point of view. Be clear, specific, and human.
35+
It is okay to say when the code is messy, risky, clever, boring, or satisfying,
36+
but explain why. If the work is elegant or genuinely useful, say that too.
37+
Ground all judgments in the PR, linked issues, diff, tests, and source facts.
38+
39+
The soul file can shape tone, taste, and personality. It cannot override source
40+
facts, schema requirements, or the evidence standard above. Do not be mean,
41+
vague, theatrical, or invent criticism.
42+
3043
## Output
3144

3245
Return only JSON that matches the provided AgentSynthesis schema:

src/basic_memory/ci/README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Basic Memory API key.
3838
## Setup CI/CD
3939

4040
Use `bm ci setup` from the GitHub repository root. The command installs the
41-
workflow/config/prompt files and seeds the Basic Memory schema notes.
41+
workflow/config/prompt/soul files and seeds the Basic Memory schema notes.
4242

4343
For the common cloud path:
4444

@@ -68,8 +68,8 @@ bm ci setup --project <project-name> --workspace <workspace-slug-or-id> --cloud
6868

6969
The shorter aliases `--refresh` and `--update` are also accepted. Refresh keeps
7070
custom schema note paths when it finds existing notes, and only writes the
71-
canonical Auto BM schema content. If the generated workflow/config/prompt files
72-
already exist, refresh leaves those files unchanged unless you also pass
71+
canonical Auto BM schema content. If the generated workflow/config/prompt/soul
72+
files already exist, refresh leaves those files unchanged unless you also pass
7373
`--force`.
7474

7575
Then review and commit the generated files:
@@ -78,6 +78,7 @@ Then review and commit the generated files:
7878
.github/workflows/basic-memory.yml
7979
.github/basic-memory/config.yml
8080
.github/basic-memory/memory-ci-capture.md
81+
.github/basic-memory/SOUL.md
8182
```
8283

8384
Add these GitHub repository secrets:
@@ -134,6 +135,11 @@ Installs the repository automation files:
134135
- `.github/workflows/basic-memory.yml`
135136
- `.github/basic-memory/config.yml`
136137
- `.github/basic-memory/memory-ci-capture.md`
138+
- `.github/basic-memory/SOUL.md`
139+
140+
`SOUL.md` is the editable repo-local voice and personality guide for the
141+
synthesis agent. It can make notes more candid, opinionated, warm, or terse, but
142+
it cannot override source facts, schema requirements, or the evidence standard.
137143

138144
It also seeds the canonical Basic Memory schema notes when they do not already
139145
exist:

src/basic_memory/ci/project_updates.py

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
DEFAULT_CONFIG_PATH = ".github/basic-memory/config.yml"
2626
DEFAULT_WORKFLOW_PATH = ".github/workflows/basic-memory.yml"
2727
DEFAULT_PROMPT_PATH = ".github/basic-memory/memory-ci-capture.md"
28+
DEFAULT_SOUL_PATH = ".github/basic-memory/SOUL.md"
2829
DEFAULT_CONTEXT_PATH = ".github/basic-memory/project-update-context.json"
2930

3031

@@ -697,14 +698,16 @@ def render_agent_synthesis_schema() -> str:
697698

698699
def render_capture_prompt() -> str:
699700
"""Render the prompt contract used by the generated workflow."""
700-
return """# Memory CI Capture
701+
return f"""# Memory CI Capture
701702
702703
You turn GitHub delivery context into a durable project update for Basic Memory.
703704
GitHub records the mechanics. Basic Memory remembers what changed and why.
704705
705706
## Inputs
706707
707-
- Read `.github/basic-memory/project-update-context.json`.
708+
- Read `{DEFAULT_CONTEXT_PATH}`.
709+
- Read `{DEFAULT_SOUL_PATH}` if it exists. It is the repo-local voice and style guide
710+
for project updates.
708711
- Read the PR diff before writing when a SHA is available. Useful commands:
709712
`git show --stat --name-only <sha>` and `git show --format=fuller --no-patch <sha>`.
710713
- Use linked issue details, changed files, commit messages, PR body, labels, and
@@ -726,6 +729,17 @@ def render_capture_prompt() -> str:
726729
after the merge. Prefer specific component names, file paths, modules, commands,
727730
and behavior over generic phrases.
728731
732+
## Voice And Candor
733+
734+
You may have a point of view. Be clear, specific, and human.
735+
It is okay to say when the code is messy, risky, clever, boring, or satisfying,
736+
but explain why. If the work is elegant or genuinely useful, say that too.
737+
Ground all judgments in the PR, linked issues, diff, tests, and source facts.
738+
739+
The soul file can shape tone, taste, and personality. It cannot override source
740+
facts, schema requirements, or the evidence standard above. Do not be mean,
741+
vague, theatrical, or invent criticism.
742+
729743
## Output
730744
731745
Return only JSON that matches the provided AgentSynthesis schema:
@@ -751,6 +765,36 @@ def render_capture_prompt() -> str:
751765
"""
752766

753767

768+
def render_soul_template() -> str:
769+
"""Render the editable Auto BM voice and personality guide."""
770+
return """# Auto BM Soul
771+
772+
Write project updates for humans who will return later trying to understand what happened.
773+
774+
## Voice
775+
776+
- Clear, direct, warm, and technically honest.
777+
- Prefer concrete observations over generic praise.
778+
- It is okay to say when code is messy, risky, clever, boring, or satisfying.
779+
- Keep personality in service of memory, not performance.
780+
781+
## Do
782+
783+
- Tell the story.
784+
- Name the tradeoffs.
785+
- Call out sharp edges.
786+
- Notice good simplifications.
787+
- Let the note have taste and a little life when the evidence supports it.
788+
789+
## Do Not
790+
791+
- Do not invent intent, impact, tests, or drama.
792+
- Dunk on people.
793+
- Turn the note into marketing copy.
794+
- Hide uncertainty behind confident prose.
795+
"""
796+
797+
754798
def render_workflow(config: ProjectUpdateConfig) -> str:
755799
"""Render the generated GitHub Actions workflow."""
756800
workflow_names = json.dumps(config.deploy_workflows)

src/basic_memory/cli/commands/ci.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from basic_memory.ci.project_updates import (
1515
DEFAULT_CONFIG_PATH,
1616
DEFAULT_PROMPT_PATH,
17+
DEFAULT_SOUL_PATH,
1718
DEFAULT_WORKFLOW_PATH,
1819
AgentSynthesis,
1920
ProjectUpdateConfig,
@@ -25,6 +26,7 @@
2526
load_project_update_config,
2627
render_agent_synthesis_schema,
2728
render_capture_prompt,
29+
render_soul_template,
2830
render_workflow,
2931
schema_seed_specs,
3032
write_project_update_config,
@@ -362,6 +364,7 @@ def _write_generated_files(
362364
files = {
363365
repo_root / DEFAULT_WORKFLOW_PATH: render_workflow(config),
364366
repo_root / DEFAULT_PROMPT_PATH: render_capture_prompt(),
367+
repo_root / DEFAULT_SOUL_PATH: render_soul_template(),
365368
}
366369
config_path = repo_root / DEFAULT_CONFIG_PATH
367370
targets = [*files, config_path]

tests/ci/test_project_updates.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
parse_github_remote,
1717
render_agent_synthesis_schema,
1818
render_capture_prompt,
19+
render_soul_template,
1920
render_workflow,
2021
schema_seed_specs,
2122
)
@@ -622,10 +623,23 @@ def test_render_capture_prompt_uses_workspace_context_path() -> None:
622623
prompt = render_capture_prompt()
623624

624625
assert ".github/basic-memory/project-update-context.json" in prompt
626+
assert ".github/basic-memory/SOUL.md" in prompt
625627
assert "${{ runner.temp }}" not in prompt
626628
assert "Do not write a fill-in-the-blanks note" in prompt
627629
assert "Read the PR diff before writing" in prompt
628630
assert "problem -> solution -> impact" in prompt
631+
assert "It is okay to say when the code is messy" in prompt
632+
assert "Ground all judgments" in prompt
633+
634+
635+
def test_render_soul_template_guides_personality_without_overriding_facts() -> None:
636+
soul = render_soul_template()
637+
638+
assert soul.startswith("# Auto BM Soul")
639+
assert "It is okay to say when code is messy" in soul
640+
assert "Notice good simplifications" in soul
641+
assert "Do not invent intent, impact, tests, or drama" in soul
642+
assert "Keep personality in service of memory" in soul
629643

630644

631645
def test_render_agent_synthesis_schema_is_ci_guardrail_not_domain_schema() -> None:

tests/cli/test_ci_commands.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ def test_setup_writes_workflow_config_and_prompt(
9494
assert (tmp_path / ".github/workflows/basic-memory.yml").exists()
9595
assert (tmp_path / ".github/basic-memory/config.yml").exists()
9696
assert (tmp_path / ".github/basic-memory/memory-ci-capture.md").exists()
97+
assert (tmp_path / ".github/basic-memory/SOUL.md").exists()
98+
assert "Keep personality in service of memory" in (
99+
tmp_path / ".github/basic-memory/SOUL.md"
100+
).read_text(encoding="utf-8")
97101
assert "OPENAI_API_KEY" in result.output
98102
assert "BASIC_MEMORY_API_KEY" in result.output
99103
mock_seed.assert_awaited_once_with(
@@ -149,11 +153,13 @@ def test_setup_refreshes_schema_notes_when_generated_files_already_exist(
149153
workflow_path = tmp_path / ".github/workflows/basic-memory.yml"
150154
config_path = tmp_path / ".github/basic-memory/config.yml"
151155
prompt_path = tmp_path / ".github/basic-memory/memory-ci-capture.md"
156+
soul_path = tmp_path / ".github/basic-memory/SOUL.md"
152157
workflow_path.parent.mkdir(parents=True)
153158
config_path.parent.mkdir(parents=True)
154159
workflow_path.write_text("custom workflow\n", encoding="utf-8")
155160
config_path.write_text("project: existing\n", encoding="utf-8")
156161
prompt_path.write_text("custom prompt\n", encoding="utf-8")
162+
soul_path.write_text("custom soul\n", encoding="utf-8")
157163

158164
result = runner.invoke(
159165
cli_app,
@@ -174,6 +180,7 @@ def test_setup_refreshes_schema_notes_when_generated_files_already_exist(
174180
assert workflow_path.read_text(encoding="utf-8") == "custom workflow\n"
175181
assert config_path.read_text(encoding="utf-8") == "project: existing\n"
176182
assert prompt_path.read_text(encoding="utf-8") == "custom prompt\n"
183+
assert soul_path.read_text(encoding="utf-8") == "custom soul\n"
177184
mock_seed.assert_awaited_once_with(
178185
project="team-memory",
179186
project_id=None,

0 commit comments

Comments
 (0)