feat(decision): add decision persistence layer #131

Merged
hamza.khyari merged 2 commits from feature/m4-decision-persistence into master 2026-02-26 14:25:27 +00:00
Member

Metadata

  • Commit Message: feat(decision): add decision persistence layer
  • Branch: feature/m4-decision-persistence

Background

This PR implements the decision persistence layer for Milestone 3 (v3.2.0). It adds the ability to persist decision records to the SQLite database, enabling decision trees to be recorded during strategize and queried via CLI commands.

Related issue: #171

Expected Behavior

Decision records are persisted to SQLite with full context snapshots. The decision persistence layer supports CRUD operations and integrates with the plan execution lifecycle.

Acceptance Criteria

  • Decision model persists to SQLite via repository pattern
  • Decision records include context snapshots
  • CRUD operations work for decision lifecycle
  • Existing tests pass with no regressions
  • Coverage remains >=97%

Definition of Done

This PR is complete when:

  • All acceptance criteria above are met.
  • Code review is approved.
  • CI passes (nox full suite, coverage >=97%).
  • PR is merged to master.
## Metadata - **Commit Message**: `feat(decision): add decision persistence layer` - **Branch**: `feature/m4-decision-persistence` ## Background This PR implements the decision persistence layer for Milestone 3 (v3.2.0). It adds the ability to persist decision records to the SQLite database, enabling decision trees to be recorded during strategize and queried via CLI commands. Related issue: #171 ## Expected Behavior Decision records are persisted to SQLite with full context snapshots. The decision persistence layer supports CRUD operations and integrates with the plan execution lifecycle. ## Acceptance Criteria - [x] Decision model persists to SQLite via repository pattern - [x] Decision records include context snapshots - [x] CRUD operations work for decision lifecycle - [x] Existing tests pass with no regressions - [x] Coverage remains >=97% ## Definition of Done This PR is complete when: - All acceptance criteria above are met. - Code review is approved. - CI passes (nox full suite, coverage >=97%). - PR is merged to master.
hamza.khyari changed title from feat(db): add decision tables and repositories [H-19/H-20, D5.db/D5.repo] to WIP: feat(db): add decision tables and repositories [H-19/H-20, D5.db/D5.repo] 2026-02-20 15:34:18 +00:00
hamza.khyari changed title from WIP: feat(db): add decision tables and repositories [H-19/H-20, D5.db/D5.repo] to feat(decision): add decision persistence layer [H-19, M3.2] 2026-02-23 15:54:56 +00:00
freemo added this to the v3.2.0 milestone 2026-02-23 17:26:17 +00:00
Owner

Relates to #171

Relates to #171
hamza.khyari changed target branch from develop-hamza-2 to master 2026-02-23 22:56:04 +00:00
Owner

Note: This PR (feature/m4-decision-persistence) is a duplicate tracking path. The properly scoped issue for this work is #171 (feat(decision): add decision persistence). This PR's branch exists on the remote and appears to contain Hamza's active work. The PR should be retargeted and associated with issue #171 once ready for review.

Note: This PR (`feature/m4-decision-persistence`) is a duplicate tracking path. The properly scoped issue for this work is #171 (feat(decision): add decision persistence). This PR's branch exists on the remote and appears to contain Hamza's active work. The PR should be retargeted and associated with issue #171 once ready for review.
hamza.khyari force-pushed feature/m4-decision-persistence from 89a374a296
Some checks failed
CI / benchmark-publish (pull_request) Has been skipped
CI / quality (pull_request) Successful in 18s
CI / lint (pull_request) Successful in 20s
CI / build (pull_request) Successful in 23s
CI / typecheck (pull_request) Failing after 51s
CI / security (pull_request) Successful in 51s
CI / coverage (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Has been skipped
CI / integration_tests (pull_request) Successful in 4m7s
CI / unit_tests (pull_request) Successful in 6m36s
CI / docker (pull_request) Has been skipped
to d0feda432a
Some checks failed
CI / lint (pull_request) Successful in 23s
CI / benchmark-publish (pull_request) Has been skipped
CI / quality (pull_request) Successful in 32s
CI / build (pull_request) Successful in 25s
CI / typecheck (pull_request) Failing after 58s
CI / security (pull_request) Successful in 58s
CI / coverage (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Has been skipped
CI / integration_tests (pull_request) Successful in 4m5s
CI / unit_tests (pull_request) Has been cancelled
CI / docker (pull_request) Has been cancelled
2026-02-24 17:39:09 +00:00
Compare
hamza.khyari force-pushed feature/m4-decision-persistence from 9f36e8f848
Some checks failed
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Failing after 22s
CI / build (pull_request) Successful in 25s
CI / quality (pull_request) Successful in 32s
CI / security (pull_request) Successful in 50s
CI / typecheck (pull_request) Successful in 1m1s
CI / coverage (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Has been skipped
CI / unit_tests (pull_request) Has been cancelled
CI / integration_tests (pull_request) Has been cancelled
CI / docker (pull_request) Has been cancelled
to 78c3e5dc01
Some checks failed
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 17s
CI / build (pull_request) Successful in 19s
CI / quality (pull_request) Successful in 22s
CI / security (pull_request) Successful in 52s
CI / typecheck (pull_request) Successful in 59s
CI / integration_tests (pull_request) Successful in 4m13s
CI / unit_tests (pull_request) Failing after 6m46s
CI / docker (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Successful in 21m32s
CI / coverage (pull_request) Failing after 32m41s
2026-02-24 17:46:56 +00:00
Compare
hamza.khyari force-pushed feature/m4-decision-persistence from 78c3e5dc01
Some checks failed
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 17s
CI / build (pull_request) Successful in 19s
CI / quality (pull_request) Successful in 22s
CI / security (pull_request) Successful in 52s
CI / typecheck (pull_request) Successful in 59s
CI / integration_tests (pull_request) Successful in 4m13s
CI / unit_tests (pull_request) Failing after 6m46s
CI / docker (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Successful in 21m32s
CI / coverage (pull_request) Failing after 32m41s
to 42c9ee85d4
All checks were successful
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 15s
CI / build (pull_request) Successful in 22s
CI / quality (pull_request) Successful in 25s
CI / typecheck (pull_request) Successful in 35s
CI / security (pull_request) Successful in 37s
CI / integration_tests (pull_request) Successful in 5m1s
CI / unit_tests (pull_request) Successful in 13m0s
CI / docker (pull_request) Successful in 1m10s
CI / benchmark-regression (pull_request) Successful in 22m9s
CI / coverage (pull_request) Successful in 51m46s
2026-02-24 20:09:33 +00:00
Compare
freemo force-pushed feature/m4-decision-persistence from 42c9ee85d4
All checks were successful
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 15s
CI / build (pull_request) Successful in 22s
CI / quality (pull_request) Successful in 25s
CI / typecheck (pull_request) Successful in 35s
CI / security (pull_request) Successful in 37s
CI / integration_tests (pull_request) Successful in 5m1s
CI / unit_tests (pull_request) Successful in 13m0s
CI / docker (pull_request) Successful in 1m10s
CI / benchmark-regression (pull_request) Successful in 22m9s
CI / coverage (pull_request) Successful in 51m46s
to f88a4a1290
Some checks failed
CI / lint (pull_request) Successful in 25s
CI / quality (pull_request) Successful in 29s
CI / typecheck (pull_request) Successful in 1m4s
CI / security (pull_request) Successful in 52s
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 24s
CI / integration_tests (pull_request) Successful in 5m7s
CI / unit_tests (pull_request) Successful in 22m49s
CI / benchmark-regression (pull_request) Successful in 21m32s
CI / docker (pull_request) Successful in 1m1s
CI / coverage (pull_request) Failing after 59m49s
2026-02-24 20:50:24 +00:00
Compare
freemo requested changes 2026-02-24 22:27:54 +00:00
Dismissed
freemo left a comment

Code Review: PR #131feat(decision): add decision persistence layer [H-19, M3.2]

This PR implements a well-scoped decision persistence subsystem with proper repository pattern architecture. The branch name matches issue #171's metadata, and the overall structure (models, repository, migration, tests, docs, benchmarks) is thorough. However, several process and code quality issues need attention.

Process Issues

1. Commit message mismatch with issue metadata
Issue #171 specifies the commit message first line as: feat(decision): add decision persistence
The actual commit message is: feat(decision): add decision persistence layer
Per the issue's Definition of Done: "the first line of the commit message matches the Commit Message in Metadata exactly."

2. No issue references in commit footers (CONTRIBUTING.md §4)
Neither commit (feat(decision): add decision persistence layer nor fix(decision): use setattr for ORM attribute write to satisfy pyright) has a footer referencing issue #171. Per guidelines, every commit must reference its associated issue (e.g., ISSUES CLOSED: #171 or Refs: #171).

3. No CHANGELOG update (CONTRIBUTING.md §6)
A +2,349 line PR adding a new persistence subsystem requires a CHANGELOG entry describing the change from the user's perspective.

4. No CONTRIBUTORS.md update (CONTRIBUTING.md §8)
Hamza Khyari should be added to CONTRIBUTORS.md if not already listed.

5. Improper labels on PR (CONTRIBUTING.md §12)
This PR carries MoSCoW/Must have, Points/8, and enhancement labels. Per guidelines: "No other label scopes (State/, Priority/, MoSCoW/) are applied to Pull Requests." The PR should have only Type/Feature. Remove MoSCoW/Must have, Points/8, and enhancement.

Code Quality Issues

6. Overuse of Any types (CONTRIBUTING.md — Type Safety)
DecisionRepository uses Any extensively where the concrete Decision type should be specified:

  • create(self, decision: Any) -> Any
  • get(self, decision_id: str) -> Any | None
  • get_by_plan(self, plan_id: str) -> list[Any]
  • get_tree(self, root_decision_id: str) -> list[Any]
  • to_domain(self) -> Any
  • from_domain(cls, decision: Any) -> DecisionModel

Per CONTRIBUTING.md: "every function signature, variable declaration, and return type should be annotated with explicit types." If this is to avoid circular imports, use TYPE_CHECKING:

from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from cleveragents.domain.decisions import Decision

7. Missing type annotations in Robot helper
robot/helper_decision_persistence.py has 12 functions all lacking type annotations (parameter types and return types). While these are module-private, the project standard requires full annotations.

8. BFS uses list.pop(0) — O(n) per operation
In DecisionRepository.get_tree():

queue = [root_decision_id]
while queue:
    parent_id = queue.pop(0)  # O(n) operation

This should use collections.deque for O(1) popleft. For large decision trees this becomes a performance concern:

from collections import deque
queue = deque([root_decision_id])
while queue:
    parent_id = queue.popleft()  # O(1) operation

What's Good

  • Single feature scope (decision persistence only) — properly scoped PR
  • Correct branch name matching issue #171 metadata
  • Proper milestone assignment (v3.2.0)
  • Repository pattern with proper separation of concerns
  • Complete test coverage (Behave scenarios, Robot tests, ASV benchmarks)
  • Alembic migration included
  • Documentation updated (database_schema.md)
  • Existing maintainer comments acknowledged (#171 linkage)

Action Items

  1. Fix commit message to match issue metadata exactly.
  2. Add issue references to commit footers.
  3. Add CHANGELOG entry.
  4. Add CONTRIBUTORS.md entry.
  5. Replace Any types with concrete Decision type using TYPE_CHECKING.
  6. Add type annotations to Robot helper functions.
  7. Replace list.pop(0) with deque.popleft() in get_tree().
  8. Fix PR labels to only Type/Feature.
## Code Review: PR #131 — `feat(decision): add decision persistence layer [H-19, M3.2]` This PR implements a well-scoped decision persistence subsystem with proper repository pattern architecture. The branch name matches issue #171's metadata, and the overall structure (models, repository, migration, tests, docs, benchmarks) is thorough. However, several process and code quality issues need attention. ### Process Issues **1. Commit message mismatch with issue metadata** Issue #171 specifies the commit message first line as: `feat(decision): add decision persistence` The actual commit message is: `feat(decision): add decision persistence layer` Per the issue's Definition of Done: "the first line of the commit message matches the Commit Message in Metadata exactly." **2. No issue references in commit footers (CONTRIBUTING.md §4)** Neither commit (`feat(decision): add decision persistence layer` nor `fix(decision): use setattr for ORM attribute write to satisfy pyright`) has a footer referencing issue #171. Per guidelines, every commit must reference its associated issue (e.g., `ISSUES CLOSED: #171` or `Refs: #171`). **3. No CHANGELOG update (CONTRIBUTING.md §6)** A +2,349 line PR adding a new persistence subsystem requires a CHANGELOG entry describing the change from the user's perspective. **4. No CONTRIBUTORS.md update (CONTRIBUTING.md §8)** Hamza Khyari should be added to CONTRIBUTORS.md if not already listed. **5. Improper labels on PR (CONTRIBUTING.md §12)** This PR carries `MoSCoW/Must have`, `Points/8`, and `enhancement` labels. Per guidelines: "No other label scopes (`State/`, `Priority/`, `MoSCoW/`) are applied to Pull Requests." The PR should have only `Type/Feature`. Remove `MoSCoW/Must have`, `Points/8`, and `enhancement`. ### Code Quality Issues **6. Overuse of `Any` types (CONTRIBUTING.md — Type Safety)** `DecisionRepository` uses `Any` extensively where the concrete `Decision` type should be specified: - `create(self, decision: Any) -> Any` - `get(self, decision_id: str) -> Any | None` - `get_by_plan(self, plan_id: str) -> list[Any]` - `get_tree(self, root_decision_id: str) -> list[Any]` - `to_domain(self) -> Any` - `from_domain(cls, decision: Any) -> DecisionModel` Per CONTRIBUTING.md: "every function signature, variable declaration, and return type should be annotated with explicit types." If this is to avoid circular imports, use `TYPE_CHECKING`: ```python from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: from cleveragents.domain.decisions import Decision ``` **7. Missing type annotations in Robot helper** `robot/helper_decision_persistence.py` has 12 functions all lacking type annotations (parameter types and return types). While these are module-private, the project standard requires full annotations. **8. BFS uses `list.pop(0)` — O(n) per operation** In `DecisionRepository.get_tree()`: ```python queue = [root_decision_id] while queue: parent_id = queue.pop(0) # O(n) operation ``` This should use `collections.deque` for O(1) popleft. For large decision trees this becomes a performance concern: ```python from collections import deque queue = deque([root_decision_id]) while queue: parent_id = queue.popleft() # O(1) operation ``` ### What's Good - Single feature scope (decision persistence only) — properly scoped PR - Correct branch name matching issue #171 metadata - Proper milestone assignment (v3.2.0) - Repository pattern with proper separation of concerns - Complete test coverage (Behave scenarios, Robot tests, ASV benchmarks) - Alembic migration included - Documentation updated (database_schema.md) - Existing maintainer comments acknowledged (#171 linkage) ### Action Items 1. Fix commit message to match issue metadata exactly. 2. Add issue references to commit footers. 3. Add CHANGELOG entry. 4. Add CONTRIBUTORS.md entry. 5. Replace `Any` types with concrete `Decision` type using `TYPE_CHECKING`. 6. Add type annotations to Robot helper functions. 7. Replace `list.pop(0)` with `deque.popleft()` in `get_tree()`. 8. Fix PR labels to only `Type/Feature`.
freemo dismissed freemo's review 2026-02-24 22:44:50 +00:00
Reason:

dont want hard block

hamza.khyari force-pushed feature/m4-decision-persistence from f88a4a1290
Some checks failed
CI / lint (pull_request) Successful in 25s
CI / quality (pull_request) Successful in 29s
CI / typecheck (pull_request) Successful in 1m4s
CI / security (pull_request) Successful in 52s
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 24s
CI / integration_tests (pull_request) Successful in 5m7s
CI / unit_tests (pull_request) Successful in 22m49s
CI / benchmark-regression (pull_request) Successful in 21m32s
CI / docker (pull_request) Successful in 1m1s
CI / coverage (pull_request) Failing after 59m49s
to 8e56067dbb
All checks were successful
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 17s
CI / lint (pull_request) Successful in 20s
CI / quality (pull_request) Successful in 30s
CI / security (pull_request) Successful in 33s
CI / typecheck (pull_request) Successful in 55s
CI / integration_tests (pull_request) Successful in 4m7s
CI / unit_tests (pull_request) Successful in 23m1s
CI / docker (pull_request) Successful in 1m6s
CI / benchmark-regression (pull_request) Successful in 23m39s
CI / coverage (pull_request) Successful in 45m26s
2026-02-25 15:25:23 +00:00
Compare
brent.edwards approved these changes 2026-02-26 00:52:35 +00:00
Dismissed
brent.edwards left a comment

Looks good to me!

Looks good to me!
hamza.khyari force-pushed feature/m4-decision-persistence from 8e56067dbb
All checks were successful
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 17s
CI / lint (pull_request) Successful in 20s
CI / quality (pull_request) Successful in 30s
CI / security (pull_request) Successful in 33s
CI / typecheck (pull_request) Successful in 55s
CI / integration_tests (pull_request) Successful in 4m7s
CI / unit_tests (pull_request) Successful in 23m1s
CI / docker (pull_request) Successful in 1m6s
CI / benchmark-regression (pull_request) Successful in 23m39s
CI / coverage (pull_request) Successful in 45m26s
to 6083747cb8
Some checks failed
CI / lint (pull_request) Successful in 15s
CI / typecheck (pull_request) Successful in 59s
CI / security (pull_request) Successful in 55s
CI / quality (pull_request) Successful in 19s
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 25s
CI / unit_tests (pull_request) Failing after 13m9s
CI / docker (pull_request) Has been skipped
CI / coverage (pull_request) Has been cancelled
CI / benchmark-regression (pull_request) Has been cancelled
CI / integration_tests (pull_request) Waiting to run
2026-02-26 01:02:23 +00:00
Compare
hamza.khyari dismissed brent.edwards's review 2026-02-26 01:02:24 +00:00
Reason:

New commits pushed, approval review dismissed automatically according to repository settings

hamza.khyari changed title from feat(decision): add decision persistence layer [H-19, M3.2] to feat(decision): add decision persistence layer 2026-02-26 01:29:49 +00:00
hamza.khyari force-pushed feature/m4-decision-persistence from 6083747cb8
Some checks failed
CI / lint (pull_request) Successful in 15s
CI / typecheck (pull_request) Successful in 59s
CI / security (pull_request) Successful in 55s
CI / quality (pull_request) Successful in 19s
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 25s
CI / unit_tests (pull_request) Failing after 13m9s
CI / docker (pull_request) Has been skipped
CI / coverage (pull_request) Has been cancelled
CI / benchmark-regression (pull_request) Has been cancelled
CI / integration_tests (pull_request) Waiting to run
to f29e485f71
Some checks failed
CI / lint (pull_request) Successful in 23s
CI / typecheck (pull_request) Successful in 56s
CI / security (pull_request) Successful in 49s
CI / quality (pull_request) Successful in 29s
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 23s
CI / integration_tests (pull_request) Successful in 5m40s
CI / unit_tests (pull_request) Successful in 29m47s
CI / docker (pull_request) Successful in 1m10s
CI / benchmark-regression (pull_request) Successful in 26m10s
CI / coverage (pull_request) Failing after 1h42m56s
2026-02-26 01:38:38 +00:00
Compare
hamza.khyari scheduled this pull request to auto merge when all checks succeed 2026-02-26 02:22:00 +00:00
hamza.khyari force-pushed feature/m4-decision-persistence from f29e485f71
Some checks failed
CI / lint (pull_request) Successful in 23s
CI / typecheck (pull_request) Successful in 56s
CI / security (pull_request) Successful in 49s
CI / quality (pull_request) Successful in 29s
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 23s
CI / integration_tests (pull_request) Successful in 5m40s
CI / unit_tests (pull_request) Successful in 29m47s
CI / docker (pull_request) Successful in 1m10s
CI / benchmark-regression (pull_request) Successful in 26m10s
CI / coverage (pull_request) Failing after 1h42m56s
to 38fa95a70c
Some checks failed
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 15s
CI / build (pull_request) Successful in 17s
CI / quality (pull_request) Successful in 20s
CI / security (pull_request) Successful in 31s
CI / typecheck (pull_request) Successful in 32s
CI / integration_tests (pull_request) Failing after 3m13s
CI / unit_tests (pull_request) Failing after 10m8s
CI / docker (pull_request) Has been skipped
CI / coverage (pull_request) Has been cancelled
CI / benchmark-regression (pull_request) Has been cancelled
2026-02-26 12:35:27 +00:00
Compare
hamza.khyari force-pushed feature/m4-decision-persistence from 5d3a0f48ec
All checks were successful
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 15s
CI / build (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 19s
CI / security (pull_request) Successful in 32s
CI / typecheck (pull_request) Successful in 33s
CI / integration_tests (pull_request) Successful in 4m11s
CI / unit_tests (pull_request) Successful in 19m43s
CI / docker (pull_request) Successful in 39s
CI / benchmark-regression (pull_request) Successful in 24m47s
CI / coverage (pull_request) Successful in 37m38s
to 7f2b1c61fc
All checks were successful
CI / lint (pull_request) Successful in 15s
CI / benchmark-publish (pull_request) Has been skipped
CI / quality (pull_request) Successful in 28s
CI / typecheck (pull_request) Successful in 34s
CI / build (pull_request) Successful in 23s
CI / security (pull_request) Successful in 54s
CI / integration_tests (pull_request) Successful in 5m18s
CI / unit_tests (pull_request) Successful in 19m34s
CI / docker (pull_request) Successful in 1m4s
CI / benchmark-regression (pull_request) Successful in 24m59s
CI / coverage (pull_request) Successful in 44m0s
2026-02-26 13:40:47 +00:00
Compare
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Blocks
Reference
cleveragents/cleveragents-core!131
No description provided.