Skip to content

fix: auto-export AGENTS.md after graduation#249

Merged
Gradata merged 1 commit into
mainfrom
gra-27-agents-export
Jun 3, 2026
Merged

fix: auto-export AGENTS.md after graduation#249
Gradata merged 1 commit into
mainfrom
gra-27-agents-export

Conversation

@Gradata

@Gradata Gradata commented Jun 3, 2026

Copy link
Copy Markdown
Owner

Summary

  • Decision: AGENTS.md auto-export is ON by default after graduation; no manual gradata export step required.
  • Wire brain_end_session to rewrite AGENTS.md from canonical RULE-tier lessons after successful graduation persistence.
  • Wire the session-close hook graduation path to preserve graduated RULE lessons and export AGENTS.md too.
  • Add unit/hook smoke coverage proving synthetic graduation populates AGENTS.md idempotently.

Verification

  • python3 -m pytest tests/test_graduation_notification.py::test_rule_graduation_auto_exports_agents_md tests/test_session_close_write_through_gate.py::test_run_graduation_exports_agents_md_for_new_rule tests/test_rule_export.py -q
    • 23 passed in 3.24s
  • /home/olive/.local/bin/uvx ruff check src/gradata/_core.py src/gradata/hooks/session_close.py tests/test_graduation_notification.py tests/test_session_close_write_through_gate.py
    • All checks passed!
  • git diff --check
    • clean

Closes GRA-27

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary

  • Auto-export AGENTS.md: brain_end_session() now automatically exports AGENTS.md when lessons transition to RULE tier, eliminating the need for manual gradata export step
  • Core implementation: Added logic to call export_rules(target="agents") and write output to configured agents path in brain directory
  • Error handling: Export failures are caught and logged at debug level without aborting graduation
  • Return type change: brain_end_session() return value now includes agents_exported: bool flag indicating export success
  • Session-close hook: Updated to preserve both active and newly graduated lessons before writing back to lessons.md
  • Graduated rule export: Hook now auto-exports rule artifacts when lessons graduate, with failures silently logged
  • Idempotent export: Tests confirm AGENTS.md is rewritten canonically (not appended) to ensure idempotency
  • Test coverage: Added 2 new test functions covering synthetic graduation and agents export scenarios; all 23 tests passing

Walkthrough

When lessons graduate from PATTERN to RULE during session end, the brain now automatically exports an AGENTS.md file containing the promoted rules. The graduation writer persists both active and graduated lessons, triggers the export with error suppression, and the session end returns an agents_exported flag indicating success.

Changes

Auto-export AGENTS.md on lesson graduation

Layer / File(s) Summary
Session end auto-export coordination and return value
src/gradata/_core.py
When transitions exist, brain_end_session() calls export_rules(..., target="agents"), writes the result to the canonical AGENTS.md path with debug-level error handling, and extends the result dict with an agents_exported bool flag.
Graduation writer: lesson persistence and auto-export trigger
src/gradata/hooks/session_close.py
Graduation writer persists both active and graduated lessons to lessons.md, and conditionally invokes rule_export() to auto-export rules when any lessons graduate, with export failures suppressed at debug level.
Auto-export verification tests
tests/test_graduation_notification.py, tests/test_session_close_write_through_gate.py
Integration test verifies AGENTS.md export and idempotency during brain.end_session() on promoted PATTERN→RULE lessons; unit test checks direct graduation export behavior and canonical rewrite without entry duplication.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Gradata/gradata#95: Both PRs extend brain_end_session() with non-blocking post-session export behavior (main: auto-export AGENTS.md with agents_exported flag; referenced: best-effort cloud telemetry sync).

Suggested labels

bug, breaking-change

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: auto-export AGENTS.md after graduation' directly and clearly describes the main change: implementing automatic export of AGENTS.md following lesson graduation.
Description check ✅ Passed The description provides clear context about the auto-export feature being enabled by default, implementation details across multiple modules, and comprehensive verification steps.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch gra-27-agents-export

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 OpenGrep (1.22.0)

OpenGrep fatal error (exit code 2):
┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

�[1m Loading rules from local config...�[0m
[00.45][ERROR]: Error: exception Glob.Lexer.Syntax_error("malformed glob pattern: missing ']'")
Raised at Glob__Lexer.syntax_error in file "libs/glob/Lexer.mll", line 8, characters 2-26
Called from Glob__Lexer.__ocaml_lex_token_rec in file "libs/glob/Lexer.mll", line 29, characters 26-53
Cal


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot added bug Something isn't working breaking-change labels Jun 3, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Gradata/src/gradata/hooks/session_close.py`:
- Line 110: The hook currently writes lessons.md directly with
lessons_path.write_text(format_lessons(all_lessons), ...) which risks partial
files; replace this direct write with the atomic helper
gradata._db.write_lessons_safe and pass the target path and the formatted
lessons content (i.e., call write_lessons_safe(lessons_path,
format_lessons(all_lessons))) so lessons are written atomically; ensure you
import or reference gradata._db.write_lessons_safe where the hook uses
lessons_path and format_lessons.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9574e91f-5d0c-4588-bde0-3303b1e63a73

📥 Commits

Reviewing files that changed from the base of the PR and between 9daf9e5 and 8906be8.

📒 Files selected for processing (4)
  • Gradata/src/gradata/_core.py
  • Gradata/src/gradata/hooks/session_close.py
  • Gradata/tests/test_graduation_notification.py
  • Gradata/tests/test_session_close_write_through_gate.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: pytest ubuntu-latest / py3.11
  • GitHub Check: pytest windows-latest / py3.12
  • GitHub Check: pytest macos-latest / py3.12
  • GitHub Check: pytest macos-latest / py3.11
  • GitHub Check: pytest ubuntu-latest / py3.12
  • GitHub Check: pytest windows-latest / py3.11
  • GitHub Check: pytest (py3.11)
  • GitHub Check: pytest (py3.12)
🧰 Additional context used
📓 Path-based instructions (2)
Gradata/tests/**/*.py

📄 CodeRabbit inference engine (Gradata/AGENTS.md)

Gradata/tests/**/*.py: Set BRAIN_DIR environment variable via tmp_path in conftest.py for test isolation — ensure _paths.py module cache refreshes when calling Brain.init() directly inside tests
Add unit tests in tests/test_*.py for every CI push without LLM calls (deterministic); mark integration tests with @pytest.mark.integration and skip them by default (they hit real LLM APIs)

Files:

  • Gradata/tests/test_graduation_notification.py
  • Gradata/tests/test_session_close_write_through_gate.py
Gradata/src/**/*.py

📄 CodeRabbit inference engine (Gradata/AGENTS.md)

Gradata/src/**/*.py: Prefer sentence-transformers for local embeddings, google-genai for Gemini embeddings, cryptography for AES-GCM encrypted system.db, bm25s for BM25 rule ranking, and mem0ai for external memory adapters — guard all optional dependency imports with try / except ImportError at the call site, never at module level
Maintain strict layering: Layer 0 (Primitives: _types.py, _db.py, _events.py, _paths.py, _file_lock.py; Patterns: contrib/patterns/) must never import from Layer 1 (Enhancements: enhancements/, rules/) or Layer 2 (Public API: brain.py, cli.py, daemon.py, mcp_server.py)
Never use bare except: pass — use typed exceptions or at minimum logger.warning(...) with exc_info=True to avoid silent failure in a memory product
Never import from out-of-scope sibling directories ../Sprites/ or ../Hausgem/ within gradata/* code — that is a layering bug
Never leak private-sibling paths into public docs/code — no references to ../Sprites/, ../Hausgem/, email addresses, OneDrive paths, or Sprites-specific examples from inside gradata/*
Use atomic-write helper when writing JSON files to prevent corruption from mid-write crashes

Files:

  • Gradata/src/gradata/_core.py
  • Gradata/src/gradata/hooks/session_close.py
🧠 Learnings (3)
📓 Common learnings
Learnt from: Gradata
Repo: Gradata/gradata PR: 0
File: :0-0
Timestamp: 2026-04-17T17:18:07.439Z
Learning: In PR `#102` (gradata/gradata), Round 2 addressed: cli.py env-first brain resolution (GRADATA_BRAIN > --brain-dir > cwd), _tenant.py corrupt .tenant_id overwrite, _env_int default clamping to minimum, and _events.py tenant-scoped fallback SELECT for dedup. All ruff and 99 tests green after these fixes.
📚 Learning: 2026-05-01T15:50:32.772Z
Learnt from: CR
Repo: Gradata/gradata PR: 0
File: Gradata/AGENTS.md:0-0
Timestamp: 2026-05-01T15:50:32.772Z
Learning: Applies to Gradata/tests/**/*.py : Add unit tests in `tests/test_*.py` for every CI push without LLM calls (deterministic); mark integration tests with `pytest.mark.integration` and skip them by default (they hit real LLM APIs)

Applied to files:

  • Gradata/tests/test_graduation_notification.py
📚 Learning: 2026-05-01T15:50:32.772Z
Learnt from: CR
Repo: Gradata/gradata PR: 0
File: Gradata/AGENTS.md:0-0
Timestamp: 2026-05-01T15:50:32.772Z
Learning: Applies to Gradata/tests/**/*.py : Set `BRAIN_DIR` environment variable via `tmp_path` in conftest.py for test isolation — ensure `_paths.py` module cache refreshes when calling `Brain.init()` directly inside tests

Applied to files:

  • Gradata/tests/test_session_close_write_through_gate.py
🔇 Additional comments (5)
Gradata/src/gradata/_core.py (2)

1017-1033: LGTM!


1146-1146: LGTM!

Gradata/src/gradata/hooks/session_close.py (1)

111-121: LGTM!

Gradata/tests/test_session_close_write_through_gate.py (1)

10-10: LGTM!

Also applies to: 46-66

Gradata/tests/test_graduation_notification.py (1)

158-185: LGTM!

lessons_path.write_text(format_lessons(active), encoding="utf-8")
active, graduated = graduate(lessons)
all_lessons = active + graduated
lessons_path.write_text(format_lessons(all_lessons), encoding="utf-8")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use atomic-write helper for lessons.md to prevent corruption.

The codebase provides gradata._db.write_lessons_safe() specifically to prevent mid-write corruption of lessons.md (used in _core.py at lines 630, 1015). This hook writes lessons using direct write_text(), which can leave a partial file if the process crashes mid-write.

🛡️ Proposed fix
+from gradata._db import write_lessons_safe
+
 def _run_graduation(brain_dir: str) -> None:
     try:
         from gradata.enhancements.self_improvement import format_lessons, graduate, parse_lessons
 
         lessons_path = Path(brain_dir) / "lessons.md"
         if not lessons_path.is_file():
             return
         text = lessons_path.read_text(encoding="utf-8")
         lessons = parse_lessons(text)
         if not lessons:
             return
         active, graduated = graduate(lessons)
         all_lessons = active + graduated
-        lessons_path.write_text(format_lessons(all_lessons), encoding="utf-8")
+        write_lessons_safe(lessons_path, format_lessons(all_lessons))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Gradata/src/gradata/hooks/session_close.py` at line 110, The hook currently
writes lessons.md directly with
lessons_path.write_text(format_lessons(all_lessons), ...) which risks partial
files; replace this direct write with the atomic helper
gradata._db.write_lessons_safe and pass the target path and the formatted
lessons content (i.e., call write_lessons_safe(lessons_path,
format_lessons(all_lessons))) so lessons are written atomically; ensure you
import or reference gradata._db.write_lessons_safe where the hook uses
lessons_path and format_lessons.

@Gradata Gradata merged commit 160a05f into main Jun 3, 2026
9 checks passed
@Gradata Gradata deleted the gra-27-agents-export branch June 3, 2026 06:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant