Orchestrata Ultimate / Specs / 023-goal-system
023-goal-system: Goal-Driven Session System
A persistent, measurable goal system that drives agent behavior across sessions, context compactions, and conversation restarts. The agent commits to a goal, decomposes it into tasks, continuously evaluates progress against boolean exit criteria, and does not stop until the goal is achieved or the agent is blocked. When either condition is reached, an audio alarm alerts the user.
At a glance
| Item | Value |
|---|---|
| Spec directory | specs/023-goal-system/ |
| Status line in the spec | Draft |
| Requirement IDs defined | 155 |
With a @traces citation in src/ |
17 (11.0%) |
With a @traces citation in tests/ |
0 (0.0%) |
| Tracked files | 3 |
| RTM page | 023-goal-system |
Reading this spec
The Goal-Driven Session System is a persistent, measurable goal mechanism that drives agent behavior across sessions, context compactions and conversation restarts. The agent commits to a goal, breaks it into tasks, evaluates progress against boolean exit criteria and does not stop until the goal is achieved or the agent is blocked; an audio alarm then alerts the user. The spec names three problems it solves: goal amnesia after compaction or restart, premature stopping, and silent completion.
The scope is seven user stories: setting a session goal, continuous progress evaluation, persistence across compaction and the Stop hook guard (priority 0), and the audio alarm, reflective re-planning and goal lifecycle commands (priority 1). The functional requirements add exit criterion validation and blocker detection and notification. Requirement IDs use the REQ-023- prefix.
The central design decision is a BDI (Belief-Desire-Intention) architecture mapped onto the session infrastructure of spec 006. Beliefs are the context.json state, desires are the goal statement with exit criteria, and intentions are the active goal with committed tasks. The goal persists in context.json under state.goal. Session hooks inject it at startup, and the Stop hook checks it before the agent may stop.
In the code, the largest tracers are orchestrata_ultimate/session/controller/_goals and orchestrata_ultimate/session/audio_alarm, with orchestrata_ultimate/session/hooks/session_auto_start_hook and orchestrata_ultimate/session/exceptions. The per-requirement view is on the RTM page.
The spec's status line reads Draft. It tracks 3 files and defines 155 requirement IDs. The full text follows below.
Measured coverage is 17 of 155 requirements cited by at least one @traces in src/ (11.0%), spread over 4 modules; the module citing the most of them is orchestrata_ultimate/session/controller/_goals (10 requirements). In tests/, 0 requirements are cited by a test-side @traces.
Coverage gaps: 138 requirements have no citing symbol. The largest untraced groups are REQ-023-* (138). Untraced means no @traces names the ID; the behaviour may still exist in code that was never annotated. Test-side citations are thinner than code-side ones (0 against 17).
Requirements
Checkbox is the state recorded in the requirements checklist; Traced by is the count of src/ symbols whose @traces names the ID.
| ID | Done | Requirement | Traced by |
|---|---|---|---|
REQ-023-007 |
yes | After every CI run, test execution, or code change, the agent evaluates all exit criteria — GoalMixin.goal_evaluate runs every verify_command; auto-triggered by the US-005 Stop gate and exposed as session goal evaluate. Tests: tests/session/test_goal_evaluate.py. |
2 |
REQ-023-008 |
yes | A criterion transitioning pending -> met is logged and announced — goal_evaluate logs a progress entry and prints the transition. Tests: tests/session/test_goal_evaluate.py. |
1 |
REQ-023-009 |
yes | A criterion regressing met -> failed is logged and announced — goal_evaluate re-runs verifiers on met criteria and logs regressions. Tests: tests/session/test_goal_evaluate.py. |
1 |
REQ-023-010 |
yes | All criteria met => goal transitions to achieved (achieved_at recorded). Tests: tests/session/test_goal_evaluate.py. |
1 |
REQ-023-011 |
yes | Evaluation is automatic — wired into SessionController.end()'s Stop gate; the user does not trigger it. Tests: tests/session/test_goal_stop_gate.py. |
1 |
REQ-023-012 |
yes | Each evaluation is logged as a progress entry with timestamp — GoalProgress entry per evaluation. Tests: tests/session/test_goal_evaluate.py. |
1 |
REQ-023-013 |
yes | The goal persists in context.json under state.goal — ContextFileManager store, untouched by compaction (pre-existing). |
none |
REQ-023-014 |
yes | A new session injects the goal into the conversation — session_auto_start_hook._format_goal_injection. Tests: tests/session/test_session_auto_start_hook.py. |
1 |
REQ-023-015 |
yes | The first hook output includes the goal statement and criteria status. Tests: tests/session/test_session_auto_start_hook.py. |
1 |
REQ-023-016 |
yes | The goal is always in context.json, never conversation memory alone (pre-existing). |
none |
REQ-023-017 |
yes | All criteria met => success alarm (ascending tones) — AudioAlarm.play_success, triggered by goal_evaluate and /goal achieved. Tests: tests/session/test_audio_alarm.py. |
1 |
REQ-023-018 |
yes | Blocked => attention alarm (low tones) — AudioAlarm.play_attention, triggered by /goal block and the Stop gate. Tests: tests/session/test_audio_alarm.py. |
1 |
REQ-023-019 |
yes | Backend chain ffplay -> paplay -> aplay -> mpv -> terminal bell — audio_alarm.select_backend. Tests: tests/session/test_audio_alarm.py. |
1 |
REQ-023-020 |
yes | Alarm repeats every 30 seconds until the user sends a message — AudioAlarm.start_repeat/stop (daemon thread). Tests: tests/session/test_audio_alarm.py. |
1 |
REQ-023-021 |
yes | Alarm is non-blocking — detached subprocesses + daemon thread. Tests: tests/session/test_audio_alarm.py. |
1 |
REQ-023-022 |
yes | On every Stop event the gate reads state.goal from context.json — _enforce_goal_stop_gate runs in SessionController.end(). Tests: tests/session/test_goal_stop_gate.py. |
2 |
REQ-023-023 |
yes | Active + incomplete + unblocked => inject "Goal not achieved. Remaining: [...]" and refuse the stop (GoalNotAchievedError). Tests: tests/session/test_goal_stop_gate.py. |
2 |
REQ-023-024 |
yes | Active + blocked => inject the blocker reason and trigger the attention alarm. Tests: tests/session/test_goal_stop_gate.py. |
1 |
REQ-023-025 |
yes | No active goal => the gate does nothing. Tests: tests/session/test_goal_stop_gate.py. |
1 |
REQ-023-026 |
yes | The agent reads the injected message and resumes — the CLI surfaces the injected text in the error (behavioral; no separate test). | none |
REQ-023-027 |
no | Task failure logs the failure and evaluates alternative approaches. | none |
REQ-023-028 |
no | Criterion regression creates a new investigation task. | none |
REQ-023-029 |
no | Re-planning never discards completed tasks. | none |
REQ-023-030 |
no | 3 failed attempts on the same task => goal blocked + alarm. | none |
REQ-023-031 |
no | Re-planning logged as a progress entry. | none |
REQ-023-032 |
yes | /goal set <statement> defines the goal, replacing any existing active goal (pre-existing). Tests: tests/session/test_session_cli.py. |
none |
REQ-023-033 |
yes | /goal status shows statement, criteria, tasks, progress, blockers (pre-existing). Tests: tests/session/test_session_cli.py. |
none |
REQ-023-034 |
yes | /goal achieved marks achieved (override) + success alarm. Tests: tests/session/test_session_cli.py. |
none |
REQ-023-035 |
yes | /goal abandon [reason] abandons and logs the reason (pre-existing). Tests: tests/session/test_session_cli.py. |
none |
REQ-023-036 |
yes | /goal block <reason> marks blocked + attention alarm. Tests: tests/session/test_session_cli.py. |
none |
REQ-023-037 |
yes | /goal unblock clears the blocked status and resumes. Tests: tests/session/test_session_cli.py. |
none |
REQ-023-038 |
yes | All commands update context.json atomically — every command writes through ContextFileManager (pre-existing). |
none |
REQ-023-001 |
- | The goal system SHALL NOT execute destructive actions autonomously — it SHALL evaluate criteria and propose tasks, and execution SHALL still require tool approval per the existing permission model (Article III — Harm Prevention). | none |
REQ-023-002 |
- | Given a new session with no active goal, when the session starts, then the agent SHALL prompt the user to define a goal before beginning work | none |
REQ-023-003 |
- | Given the command /goal set, when the user provides a statement, then the agent SHALL decompose it into exit criteria and tasks interactively |
none |
REQ-023-004 |
- | Given exit criteria, when each is defined, then it SHALL be stored as a boolean condition with status pending |
none |
REQ-023-005 |
- | Given a goal is set, when context.json is read, then state.goal SHALL contain the full goal structure |
none |
REQ-023-006 |
- | Given a goal is set, when a new session starts (including after compaction), then the goal SHALL be displayed with current criteria status | none |
REQ-023-039 |
- | The system SHALL play the sound pattern defined for each event: goal achieved — 3 ascending tones at 600, 800 and 1000 Hz for 0.2s, 0.2s and 0.3s; agent blocked — 2 low tones at 300 Hz repeated for 0.3s each; criterion met — a single 800 Hz tone for 0.2s; criterion regressed — a descending tone at 500 Hz then 300 Hz for 0.2s and 0.3s. | none |
REQ-023-040 |
- | On achievement the alarm SHALL play once and then every 30 seconds until the user sends a message; on blocked it SHALL play once and then every 60 seconds until the user sends a message. | none |
REQ-023-041 |
- | The alarm repeat SHALL be a background process that checks the flag file /tmp/orchestrata-alarm-active, and a user message received via the UserPromptSubmit hook SHALL remove the flag file and stop the alarm. |
none |
REQ-023-042 |
- | The system SHALL persist all goal state in context.json including statement, criteria, tasks, progress, and blockers. |
none |
REQ-023-043 |
- | The goal SHALL survive session restarts, context compaction, and conversation truncation. | none |
REQ-023-044 |
- | The SessionStart hook SHALL read the goal from context.json and inject it into the conversation. |
none |
REQ-023-045 |
- | If an active goal exists, the hook SHALL display the goal statement and all criteria with current status indicators ([x] for met, [ ] for pending). | none |
REQ-023-046 |
- | After every CI run, test execution, or code modification, the system SHALL automatically evaluate all goal criteria. The evaluation SHALL NOT require user intervention and SHALL be triggered by session events. | none |
REQ-023-047 |
- | Each exit criterion SHALL be validated by executing its verify_command and matching against its verify_pattern. |
none |
REQ-023-048 |
- | The system SHALL transition criterion status from pending to met when the pattern matches successfully. |
none |
REQ-023-049 |
- | The Stop hook SHALL prevent the agent from stopping when an active goal exists with unachieved criteria. | none |
REQ-023-050 |
- | The hook SHALL inject a continue message and signal continuation instead of allowing the stop. | none |
REQ-023-051 |
- | When all exit criteria are met, the system SHALL play a success audio alarm. | none |
REQ-023-052 |
- | When the goal becomes blocked, the system SHALL play an attention audio alarm. | none |
REQ-023-053 |
- | The alarm SHALL persist until the user sends a message. | none |
REQ-023-054 |
- | The system SHALL support /goal commands for set, status, achieved, abandon, block, and unblock operations. |
none |
REQ-023-055 |
- | All commands SHALL update context.json atomically and SHALL provide user feedback. |
none |
REQ-023-056 |
- | When a task fails or a criterion regresses, the system SHALL evaluate alternative approaches. | none |
REQ-023-057 |
- | After 3 consecutive failures on the same task, the system SHALL mark the goal as blocked with a reason. | none |
REQ-023-058 |
- | The system SHALL detect blockers that prevent goal progress. Blockers MAY be auto-detected (network failures, permission errors) or user-defined via /goal block. |
none |
REQ-023-059 |
- | When blocked, the system SHALL trigger the attention alarm. | none |
REQ-023-060 |
- | The system SHALL enforce goal state transitions according to the defined state machine: none → active → (achieved|blocked|abandoned). Invalid transitions SHALL be rejected. | none |
REQ-023-061 |
- | Every goal state change SHALL be logged as a progress entry with timestamp, action, notes, and criteria changes. | none |
REQ-023-062 |
- | Progress SHALL be stored in context.json and MAY be persisted to Neo4j. |
none |
REQ-023-063 |
- | When a previously met criterion regresses to failed status, the system SHALL log the regression, announce it to the user, and trigger re-planning to address the regression. |
none |
REQ-023-064 |
- | The system SHALL detect the first available audio backend at startup using the fallback chain: ffplay, paplay, aplay, mpv, terminal bell. | none |
REQ-023-065 |
- | The detected backend SHALL be cached for subsequent use. | none |
REQ-023-066 |
- | The goal field in context.json SHALL maintain referential integrity: task criteria references SHALL match existing criterion IDs. |
none |
REQ-023-067 |
- | Invalid references SHALL be rejected during goal creation. | none |
REQ-023-068 |
- | An active goal SHALL be visible across all sessions within the same project. | none |
REQ-023-069 |
- | New Claude Code sessions SHALL receive the goal via session hooks, not via conversation memory. | none |
REQ-023-070 |
- | Audio alarms SHALL NOT block agent output or tool execution. | none |
REQ-023-071 |
- | Alarms SHALL run as background processes with a flag file to coordinate state. | none |
REQ-023-072 |
- | When a goal is abandoned, the system SHALL log the abandonment reason, timestamp, and SHALL NOT auto-resume. | none |
REQ-023-073 |
- | Abandoned goals SHALL be displayable via /goal status. |
none |
REQ-023-074 |
- | ERR-001 — When a goal is set with a blank statement, the system SHALL reject it with the error "Goal statement cannot be blank" and SHALL maintain no active goal. | none |
REQ-023-075 |
- | ERR-002 — When a criterion has no verify_command or verify_pattern, the system SHALL allow its creation with a warning, and that criterion SHALL be transitionable only via /goal achieved. |
none |
REQ-023-076 |
- | ERR-003 — When a goal state transition targets an invalid state, the system SHALL reject the transition, log the error, and maintain the current state. | none |
REQ-023-077 |
- | ERR-004 — When no audio backend is available, the system SHALL log a warning, disable audio alarms, and continue with the terminal bell only. | none |
REQ-023-078 |
- | ERR-005 — When the goal field in context.json is corrupted JSON, the system SHALL fall back to no active goal and log the error to the system logs. |
none |
REQ-023-079 |
- | ERR-006 — When the Stop hook cannot read the goal from context.json, it SHALL allow the stop (existing behavior), log the error, and flag it for manual review. |
none |
REQ-023-080 |
- | ERR-007 — When all criteria remain pending after 100 task completions, the system SHALL log the warning "Goal may be mis-specified" and SHALL continue evaluation. | none |
REQ-023-081 |
- | ERR-008 — On a goal ID collision during goal creation, the system SHALL generate a new unique ID with a timestamp suffix and log a warning. | none |
REQ-023-082 |
- | ERR-009 — When a task references a non-existent criterion ID, the system SHALL reject the task creation and return an error carrying the valid criterion IDs. | none |
REQ-023-083 |
- | ERR-010 — When goal evaluation times out after 30 seconds, the system SHALL log the timeout error and continue to the next evaluation cycle. | none |
REQ-023-084 |
- | ERR-011 — When an alarm process cannot be spawned, the system SHALL log the error to the system logs and continue without audio. | none |
REQ-023-085 |
- | ERR-012 — When the flag file /tmp/orchestrata-alarm-active cannot be written, the system SHALL disable the alarm repeat and play the alarm once only. |
none |
REQ-023-086 |
- | ERR-013 — On a user prompt during an alarm repeat, the system SHALL attempt to kill the alarm process and SHALL log the success or failure. | none |
REQ-023-087 |
- | ERR-014 — When Neo4j persistence fails for goal state, the system SHALL continue with context.json only and log the error to the system logs. |
none |
REQ-023-088 |
- | ERR-015 — When re-planning generates no new tasks after 3 failures, the system SHALL mark the goal as blocked with the reason "No alternative approaches found". | none |
REQ-023-089 |
- | ERR-016 — On goal achievement with conflicting criterion statuses, the system SHALL log the error and require manual resolution via the /goal achieved override. |
none |
REQ-023-090 |
- | ERR-017 — On session start with corrupted goal progress entries, the system SHALL filter the invalid entries, continue with the valid entries, and log a warning. | none |
REQ-023-091 |
- | ERR-018 — When the alarm backend probe fails for all backends, the system SHALL fall back to the terminal bell (\a) and log all backend failures. |
none |
REQ-023-092 |
- | ERR-019 — When a goal criterion verify_command execution fails, the system SHALL log the failure, maintain the pending status, and continue with the next criterion. |
none |
REQ-023-093 |
- | ERR-020 — On multiple concurrent goal updates (race condition), last write SHALL win and the system SHALL log a warning about the concurrent modification. | none |
REQ-023-094 |
- | SessionController SHALL expose the goal API with the parameters, return values, side effects and exceptions defined in this section: goal_set(statement: str, criteria: list[dict]) -> Goal, goal_evaluate() -> dict[str, str], goal_status() -> dict, goal_achieve(reason: str) -> bool, goal_abandon(reason: str) -> bool, goal_block(reason: str) -> bool, and goal_unblock() -> bool. |
none |
REQ-023-095 |
- | AudioAlarm SHALL expose play_success_alarm() -> bool, play_blocked_alarm() -> bool and cancel_alarms() -> bool, and the hook layer SHALL expose session_start_goal_injection() -> str and stop_hook_goal_check() -> bool, with the return semantics defined in this section. |
none |
REQ-023-101 |
- | Goal evaluation SHALL complete within 30 seconds | none |
REQ-023-102 |
- | Audio alarm playback SHALL complete within 5 seconds | none |
REQ-023-103 |
- | context.json reads/writes SHALL complete within 1 second | none |
REQ-023-104 |
- | Session hook execution SHALL complete within 2 seconds | none |
REQ-023-105 |
- | Goal system SHALL maintain functionality if audio alarms fail | none |
REQ-023-106 |
- | Goal persistence SHALL work even if Neo4j is unavailable | none |
REQ-023-107 |
- | Session hooks SHALL gracefully degrade if context.json is temporarily unavailable | none |
REQ-023-108 |
- | Goal statements SHALL NOT execute arbitrary code | none |
REQ-023-109 |
- | verify_command values SHALL be validated against a whitelist of safe commands | none |
REQ-023-110 |
- | Goal status updates SHALL be logged for audit | none |
REQ-023-111 |
- | User SHALL be able to abandon goals at any time (user autonomy) | none |
REQ-023-112 |
- | System SHALL support up to 50 active criteria per goal | none |
REQ-023-113 |
- | System SHALL support up to 1000 progress entries per goal | none |
REQ-023-114 |
- | System SHALL support concurrent goal operations from multiple session instances | none |
REQ-023-115 |
- | Goal model SHALL be defined using Pydantic for validation | none |
REQ-023-116 |
- | Hook architecture SHALL be modular for easy testing | none |
REQ-023-117 |
- | Error conditions SHALL be logged with context for debugging | none |
REQ-023-118 |
- | Goal state SHALL be consistent across session restarts | none |
REQ-023-119 |
- | Audio alarms SHALL have fallback chain to ensure user notification | none |
REQ-023-120 |
- | Goal evaluation SHALL be idempotent (multiple evaluations yield same result) | none |
REQ-023-121 |
- | Goal status SHALL be human-readable at a glance | none |
REQ-023-122 |
- | Audio alarms SHALL be distinguishable by sound pattern | none |
REQ-023-123 |
- | Goal commands SHALL provide immediate feedback | none |
REQ-023-124 |
- | Goal statements SHALL be validated for maximum length (512 characters) | none |
REQ-023-125 |
- | Reason strings for abandon/block SHALL be sanitized to prevent injection | none |
REQ-023-126 |
- | User SHALL be able to inspect and override any automated goal operations | none |
REQ-023-127 |
- | All goal state changes SHALL be logged with timestamp, user, and reason | none |
REQ-023-128 |
- | Goal progress SHALL be stored in Neo4j for complete audit trail | none |
REQ-023-129 |
- | Authentication status SHALL be included in all goal-related logs | none |
REQ-023-130 |
- | Failed verification commands SHALL be logged with full error context | none |
REQ-023-131 |
- | Any user SHALL be able to view goal status | none |
REQ-023-132 |
- | Any user SHALL be able to abandon or block goals | none |
REQ-023-133 |
- | Goal modification SHALL require an active session (no background changes without user action) | none |
REQ-023-134 |
- | Critical operations (abandon, achieve) SHALL be confirmed before execution | none |
REQ-023-135 |
- | Goal data SHALL NOT contain sensitive information (passwords, tokens) | none |
REQ-023-136 |
- | context.json file SHALL have restricted permissions (owner read/write only) | none |
REQ-023-137 |
- | Audio alarms SHALL NOT expose system information in their execution | none |
REQ-023-096 |
- | The goal schema in context.json SHALL be versioned for backward compatibility |
none |
REQ-023-097 |
- | local-ci.sh results SHALL be parsed for criterion evaluation: test pass/fail counts extracted from JUnit XML, coverage reports parsed for threshold verification, and lint output checked for clean status. |
none |
REQ-023-098 |
- | Goal state persisted to Neo4j SHALL use Goal nodes with a CONTAINS relationship to criteria and a HAS_TASK relationship to task nodes, and SHALL support cross-session goal-history queries and goal-achievement-rate analytics. |
none |
REQ-023-099 |
- | When no goal exists or the goal is not active, the SessionStart hook SHALL output No active goal. Use /goal set <statement> to define one. |
none |
REQ-023-100 |
- | When the Stop hook evaluates all criteria and finds them all met, it SHALL mark the goal achieved, play the success alarm and allow the stop; when the goal is blocked, it SHALL play the blocked alarm and allow the stop. | none |
REQ-023-138 |
- | Primary Framework: pytest for Python test implementation | none |
REQ-023-139 |
- | Test Type: Behavior-Driven Development (BDD) approach emphasizing goal-oriented testing | none |
REQ-023-140 |
- | Test Organization: Tests grouped by functional requirement (FR-<num>) and user story (US-<num>) | none |
REQ-023-141 |
- | Mocking Policy: No mocking of core goal system components; real implementations required for authentic testing | none |
REQ-023-142 |
- | Assertion Style: Use explicit assertions with descriptive error messages | none |
REQ-023-143 |
- | Each functional requirement MUST have corresponding unit tests: | none |
REQ-023-144 |
- | Each user story MUST have end-to-end tests simulating real usage: | none |
REQ-023-145 |
- | Line-coverage minimums SHALL be met: goal system modules 95%, session controller 90%, audio alarm system 85% (hardware-dependent), hook architecture 90%, overall system 90%. | none |
REQ-023-146 |
- | Coverage measurement SHALL exclude audio backend probe failures, actual audio playback, and Neo4j unavailability. | none |
REQ-023-147 |
- | The pre-commit hook SHALL run the goal system unit tests, enforce the 90% coverage threshold, and validate JSON schema conformity. | none |
REQ-023-148 |
- | CI SHALL run python -m pytest tests/test_goal_system/ -v --cov=src/goal_system --cov-report=term-missing. |
none |
REQ-023-149 |
- | Test runs SHALL generate JUnit XML for CI integration, HTML coverage reports for developer review, and test execution timing reports for performance monitoring. | none |
REQ-023-150 |
- | Each error condition MUST have tests verifying proper handling: | none |
REQ-023-151 |
- | The goal system SHALL log all major operations with appropriate log levels, include correlation IDs for request tracing, log performance metrics for critical operations, and log errors with full context for debugging. | none |
REQ-023-152 |
- | The goal system SHALL expose performance metrics: operation duration histograms, request/error rate counters, resource utilization gauges, and business metrics relevant to the domain. | none |
REQ-023-153 |
- | The goal system SHALL support distributed tracing: trace propagation across component boundaries, span attributes for domain-specific context, and trace sampling based on operational needs. | none |
REQ-023-154 |
- | The goal system SHALL raise critical alerts on error rate spikes, performance degradation, resource exhaustion, and business metric anomalies. | none |
REQ-023-155 |
- | The goal system SHALL provide an operational dashboard covering real-time system health, performance trends, error analysis, and resource monitoring. | none |
Code that traces this spec
| Module | Symbols | Requirements |
|---|---|---|
orchestrata_ultimate/session/audio_alarm |
1 | 5 |
orchestrata_ultimate/session/controller/_goals |
3 | 10 |
orchestrata_ultimate/session/exceptions |
1 | 2 |
orchestrata_ultimate/session/hooks/session_auto_start_hook |
1 | 2 |
Checklists
checklists/requirements.md
The requirement list above comes from this file; 27 of 32 checkbox items are ticked.
Other tracked files
specs/023-goal-system/compliance-audit.md
Full spec text
Verbatim from specs/023-goal-system/spec.md; headings are pushed down two levels.
SPEC-023: Goal-Driven Session System
Metadata
- Spec ID: 023-goal-system
- Title: Goal-Driven Session System
- Author: Mohamed
- Status: Draft
- Created: 2026-04-03
- Last Updated: 2026-04-09
- Priority: P0
- Version: 1.0.0
- Extends: 006-session-management
- Category: Core Architecture
- Components: Session hooks, context.json, audio alerting, goal lifecycle
- Dependencies: Spec 006 (Session Management), Spec 001 (Kernel), Spec 007 (Governance)
- References:
- Rao & Georgeff, "BDI Agents: From Theory to Practice" (ICMAS 1995)
- "Thought Management System for long-horizon, goal-driven LLM agents" (ScienceDirect 2025)
- "InfiAgent: Infinite-Horizon Framework for Autonomous Agents" (2025)
- "A Subgoal-driven Framework for Improving Long-Horizon LLM Agents" (2025)
- BDI Agent Architectures: A Survey (IJCAI 2020)
Overview
What
A persistent, measurable goal system that drives agent behavior across sessions, context compactions, and conversation restarts. The agent commits to a goal, decomposes it into tasks, continuously evaluates progress against boolean exit criteria, and does not stop until the goal is achieved or the agent is blocked. When either condition is reached, an audio alarm alerts the user.
Why
Sessions without goals drift. Today, the session system (Spec 006) tracks what happened but not what was supposed to happen. The session purpose is a timestamp string, not a contract. When context compaction occurs, the agent loses track of intent. When the user steps away, the agent stops and waits instead of continuing toward the objective. The result: the user must repeatedly re-state intent, re-orient the agent, and manually track progress.
This spec solves three problems:
- Goal amnesia — the agent forgets what it's working toward after compaction or restart
- Premature stopping — the agent stops when it could continue autonomously
- Silent completion — the agent finishes or gets blocked with no way to notify the user
How
BDI (Belief-Desire-Intention) architecture mapped onto the existing Orchestrata session infrastructure:
- Beliefs =
context.jsonstate (test counts, branch, version, infra status) - Desires = goal statement with exit criteria (what the user wants achieved)
- Intentions = active goal with committed tasks (what the agent is doing about it)
The goal persists in context.json under state.goal — the same file that survives everything. Session hooks inject it at startup. The Stop hook checks it before allowing the agent to stop. An audio alarm bridges the attention gap when the user is away.
Visual Architecture
Class Diagram
Diagram source shown as text: Mermaid could not parse it (Error: Parse error on line 239:).
classDiagram
class MultiAgentEditResult {
+file_path: str
+old_string: str
+new_string: str
+agent_results: dict[Any]
+aggregated_result: dict[Any]
+timestamp: datetime = Field()
+success: bool = True
}
class GovernanceError {
}
class OrchestrataKernel {
+__init__(self): None
+_initialize_agents(self): None
+get_agents_by_type(self, agent_type): list[Any]
+get_agent_by_id(self, agent_id): Any
+async broadcast_to_type(self, agent_type, message): list[Any]
+async broadcast_to_all(self, message): list[Any]
+async send_to_agent(self, agent_id, message, sender): Any
}
class UltimateOrchestrator {
+__init__(self): None
+async execute_multi_agent_edit(self, file_path, old_string, new_string): MultiAgentEditResult
+_aggregate_agent_results(self, results): dict[Any]
+async create_project(self, name, path, template): dict[Any]
+async shutdown(self): None
}
class ThreadPoolTaskStatus {
+PENDING = 'pending'
+RUNNING = 'running'
+COMPLETED = 'completed'
+FAILED = 'failed'
+CANCELLED = 'cancelled'
+TIMEOUT = 'timeout'
}
class ThreadPoolTask {
+task_id: str = field()
+function: Any = None
+args: tuple = field()
+kwargs: dict[Any] = field()
+status: ThreadPoolTaskStatus = .PENDING
+created_at: datetime = field()
+started_at: Any = None
+completed_at: Any = None
+result: Any = None
+error: Any = None
+traceback: Any = None
+future: Any = None
+metadata: dict[Any] = field()
}
class ThreadPoolManager {
+__init__(self, max_workers, thread_name_prefix, initializer, initargs)
+start(self): None
+stop(self, wait, cancel_futures): None
+submit(self, fn, *args, **kwargs): str
+submit_many(self, functions, args_list, kwargs_list): list[str]
+async submit_async(self, fn, *args, **kwargs): T
+async submit_many_async(self, functions, args_list, kwargs_list): list[T]
+async wait_for_task(self, task_id, timeout): Any
+get_task_status(self, task_id): ThreadPoolTaskStatus
+get_task_result(self, task_id, wait): Any
+cancel_task(self, task_id): bool
+get_stats(self): dict[Any]
+_wrap_function(self, task_id, fn, *args, **kwargs): T
+_update_task_from_future(self, task_id, future): None
+task_context(self, task_id): Any
+async async_task_context(self, task_id): Any
}
class ParallelTestExecutor {
+__init__(self, thread_pool, max_workers)
+run_tests_parallel(self, test_paths, pytest_args, timeout_per_test): dict[Any]
+_run_single_test(self, test_path, pytest_args, timeout): dict[Any]
+async run_tests_parallel_async(self, test_paths, pytest_args, timeout_per_test): dict[Any]
}
class LogLevel {
+TRACE = 'TRACE'
+DEBUG = 'DEBUG'
+INFO = 'INFO'
+WARNING = 'WARNING'
+ERROR = 'ERROR'
+CRITICAL = 'CRITICAL'
}
class LogContext {
+__init__(self, logger, **context)
+__enter__(self)
+__exit__(self, exc_type, exc_val, exc_tb)
}
class LoggingContext {
+request_id: Any = None
+session_id: Any = None
+trace_id: Any = None
+span_id: Any = None
+user_id: Any = None
+user_email: Any = None
+user_role: Any = None
+component: Any = None
+operation: Any = None
+extra: dict[Any] = field()
+created_at: datetime = field()
---
+to_dict(self): dict[Any]
}
class ContextManager {
+get_current_context(): LoggingContext
+set_current_context(context): None
+clear_context(): None
+update_context(**kwargs): LoggingContext
+create_child_context(**updates): LoggingContext
}
class AsyncContext {
+__init__(self, **context_updates)
+async __aenter__(self): LoggingContext
+async __aexit__(self, exc_type, exc_val, exc_tb)
}
class Context {
+__init__(self, **context_updates)
+__enter__(self): LoggingContext
+__exit__(self, exc_type, exc_val, exc_tb)
}
class AuditAction {
+LOGIN = 'login'
+LOGOUT = 'logout'
+AUTH_FAILURE = 'auth_failure'
+PERMISSION_CHECK = 'permission_check'
+PERMISSION_DENIED = 'permission_denied'
+READ = 'read'
+CREATE = 'create'
+UPDATE = 'update'
+DELETE = 'delete'
+EXPORT = 'export'
+IMPORT = 'import'
+CONFIG_CHANGE = 'config_change'
+SERVICE_START = 'service_start'
+SERVICE_STOP = 'service_stop'
+BACKUP = 'backup'
+RESTORE = 'restore'
+USER_CREATE = 'user_create'
+USER_UPDATE = 'user_update'
+USER_DELETE = 'user_delete'
+ROLE_ASSIGN = 'role_assign'
+ROLE_REVOKE = 'role_revoke'
+EXTERNAL_CALL = 'external_call'
+WEBHOOK_RECEIVED = 'webhook_received'
+API_CALL = 'api_call'
+WORKFLOW_START = 'workflow_start'
+WORKFLOW_COMPLETE = 'workflow_complete'
+ESCALATION = 'escalation'
+APPROVAL = 'approval'
+REJECTION = 'rejection'
}
class AuditStatus {
+SUCCESS = 'success'
+FAILURE = 'failure'
+PARTIAL = 'partial'
+PENDING = 'pending'
}
class AuditEvent {
+action: AuditAction
+timestamp: datetime = field()
+status: AuditStatus = .SUCCESS
+actor_id: Any = None
+actor_type: str = 'user'
+actor_ip: Any = None
+actor_user_agent: Any = None
+resource_type: Any = None
+resource_id: Any = None
+resource_path: Any = None
+operation_id: Any = None
+operation_name: Any = None
+operation_duration_ms: Any = None
+before_state: Any = None
+after_state: Any = None
+changes: Any = None
+request_id: Any = None
+session_id: Any = None
+trace_id: Any = None
+span_id: Any = None
+component: Any = None
+severity: str = 'info'
+details: Any = None
+error_message: Any = None
+error_stack: Any = None
+event_hash: Any = None
+previous_hash: Any = None
---
+__post_init__(self)
+calculate_hash(self): str
+_json_serializer(self, obj)
+to_dict(self): dict[Any]
+verify_hash(self): bool
}
class AuditLogger {
+__init__(self, component)
+log_event(self, event): str
+log_action(self, action, status, resource_type, resource_id, before_state, after_state, actor_id, actor_type, details, error_message, operation_duration_ms, severity): str
}
class AuditOperation {
+action: Any
+resource_type: Any = None
+resource_id: Any = None
+before_state: Any = None
+actor_id: Any = None
+actor_type: str = 'user'
+component: Any = None
+details: Any = None
---
+__enter__(self)
+__exit__(self, exc_type, exc_val, exc_tb)
}
class DomainEntity {
+entity_id: str = Field()
+is_active: bool = Field()
---
+__init__(self, **data)
+mark_inactive(self)
+to_domain_dict(self): dict[Any]
+get_business_key(self): str
+__eq__(self, other): bool
+__hash__(self): int
+__repr__(self): str
}
class Node {
+node_id: str = Field()
+node_type: str = Field()
+name: str = Field()
+properties: dict[Any] = Field()
+created_at: datetime = Field()
+updated_at: datetime = Field()
+version: int = Field()
---
+to_graph_dict(self): dict[Any]
+from_graph_dict(cls, data): Node
+update_properties(self, **kwargs)
+__str__(self): str
}
class Config {
+arbitrary_types_allowed = True
+json_encoders = {}
}
class TracingLevel {
+OFF = 0
+BASIC = 1
+DETAILED = 2
+DEBUG = 3
---
+label(self): str
}
class TracingConfig {
+model_config = ConfigDict()
+enabled: bool = Field()
+service_name: str = Field()
+service_version: str = Field()
+endpoint: Any = Field()
+sample_rate: float = Field()
+console_export: bool = Field()
+tracing_level: TracingLevel = Field()
---
+clamp_sample_rate(cls, v): float
}
class MetricsConfig {
+__init__(self, enabled, port, host, include_system_metrics, system_metrics_interval)
}
class RetryStrategy {
+__init__(self, max_retries, initial_delay, max_delay, backoff_factor, jitter)
+calculate_delay(self, attempt): float
}
class CircuitState {
+CLOSED = 'closed'
+OPEN = 'open'
+HALF_OPEN = 'half_open'
}
class CircuitBreaker {
+__init__(self, failure_threshold, reset_timeout, half_open_max_requests, name)
+is_closed(self): bool
+is_open(self): bool
+is_half_open(self): bool
+record_success(self): None
+record_failure(self): None
+should_allow_request(self): bool
+async execute(self, operation): T
}
class Bulkhead {
+__init__(self, max_concurrent, max_queue_size, name)
+async execute(self, operation): T
}
Node <|-- DomainEntity
Dependencies
| Specification | Title | Relationship |
|---|---|---|
| 006 | Session Management | Extends session lifecycle with goal persistence, hooks for goal injection and stop guard |
| 001 | Kernel | Uses kernel infrastructure for session management and context persistence |
| 007 | Governance | Operates under governance framework, ensures human autonomy and transparency in goal operations |
Terminology
| Term | Definition |
|---|---|
| Goal | A single statement of intent with measurable exit criteria, persisted in context.json. Maps to BDI "intention" — a desire the agent has committed to pursuing. |
| Exit Criterion | A boolean condition that must be true for the goal to be achieved. Not subjective — measurable by executing a command or reading a value. |
| Task | A unit of work that moves one or more exit criteria from pending to met. Tasks are ordered by dependency. |
| Progress Entry | A timestamped log of what changed toward the goal. Stored in state.goal.progress. |
| Blocker | A condition that prevents the agent from continuing without user input. Triggers the blocked alarm. |
| Reflective Re-planning | After a task fails or a criterion regresses, the agent re-evaluates the task decomposition and proposes new tasks. From TMS (2025). |
| Commitment | The agent's contract with the goal. A committed goal persists until explicitly achieved, abandoned, or marked impossible. It does not drift, decay, or get forgotten. From BDI theory. |
Governance
This spec operates under the existing Prolog constitutional framework:
| Article | Alignment |
|---|---|
| I — Human Autonomy | The user sets, overrides, and abandons goals at will. The agent proposes; the user decides. Goal commands (/goal abandon, /goal block) are always available. |
| II — Transparency | All goal progress is logged in context.json. The agent states what it's doing and why at every evaluation checkpoint. No silent work. |
| III — Harm Prevention | The goal system does not execute destructive actions autonomously. It evaluates criteria and proposes tasks — execution still requires tool approval per existing permission model. |
| VI — Accountability | Every goal state transition (set, progress, achieved, blocked, abandoned) is recorded with timestamp and reason. Full audit trail in context.json and Neo4j. |
| VIII — Sustainability | The audio alarm has a fallback chain and does not consume resources when not alerting. Goal evaluation is lightweight (read JSON, check booleans). |
| XI — Continuous Improvement | The goal system itself is a mechanism for improvement — each completed goal advances the project. Reflective re-planning allows the agent to learn from failed tasks within a session. |
REQ-023-001: The goal system SHALL NOT execute destructive actions autonomously — it SHALL evaluate criteria and propose tasks, and execution SHALL still require tool approval per the existing permission model (Article III — Harm Prevention).
User Scenarios & Stories
US-001: Set a Session Goal (P0)
As a developer I want to define a measurable goal at session start So that the agent knows what to work toward and I can track progress
Acceptance Criteria:
- REQ-023-002 (AC-001-01): Given a new session with no active goal, when the session starts, then the agent SHALL prompt the user to define a goal before beginning work
- REQ-023-003 (AC-001-02): Given the command
/goal set, when the user provides a statement, then the agent SHALL decompose it into exit criteria and tasks interactively - REQ-023-004 (AC-001-03): Given exit criteria, when each is defined, then it SHALL be stored as a boolean condition with status
pending - REQ-023-005 (AC-001-04): Given a goal is set, when
context.jsonis read, thenstate.goalSHALL contain the full goal structure - REQ-023-006 (AC-001-05): Given a goal is set, when a new session starts (including after compaction), then the goal SHALL be displayed with current criteria status
US-002: Continuous Progress Evaluation (P0)
As a developer I want the agent to automatically check progress after every significant action So that it keeps working toward the goal without me having to ask
Acceptance Criteria:
- REQ-023-007 (AC-002-01): After every CI run, test execution, or code change, the agent SHALL evaluate all exit criteria
- REQ-023-008 (AC-002-02): If a criterion transitions from
pendingtomet, the agent SHALL log it and announce it - REQ-023-009 (AC-002-03): If a criterion regresses from
mettofailed, the agent SHALL log it, announce it, and trigger re-planning - REQ-023-010 (AC-002-04): If all criteria are
met, the goal SHALL transition toachieved - REQ-023-011 (AC-002-05): The evaluation SHALL be automatic — the user SHALL NOT need to trigger it
- REQ-023-012 (AC-002-06): Each evaluation SHALL be logged as a progress entry with timestamp
US-003: Goal Persistence Across Compaction (P0)
As a developer I want the goal to survive context compaction and session restarts So that the agent never forgets what we're working toward
Acceptance Criteria:
- REQ-023-013 (AC-003-01): Given an active goal, when context compaction occurs, then the goal SHALL still be present in subsequent messages (read from
context.json) - REQ-023-014 (AC-003-02): Given an active goal, when a new Claude Code session starts, then
session_auto_start.pySHALL inject the goal into the conversation - REQ-023-015 (AC-003-03): Given an active goal, when the conversation is restarted from scratch, then the first hook output SHALL include the goal statement and criteria status
- REQ-023-016 (AC-003-04): The goal SHALL NOT be stored in conversation memory alone — it SHALL always be in
context.json
US-004: Audio Alarm on Achievement or Block (P1)
As a developer who steps away from the terminal I want an audio alarm when the goal is achieved or the agent is blocked So that I know when to come back without watching the screen
Acceptance Criteria:
- REQ-023-017 (AC-004-01): When all exit criteria are
met, the system SHALL play a success alarm (ascending tones) - REQ-023-018 (AC-004-02): When the agent is blocked, the system SHALL play an attention alarm (low repeating tones)
- REQ-023-019 (AC-004-03): The alarm SHALL use the first available audio backend:
ffplaythenpaplaythenaplaythenmpvthen terminal bell - REQ-023-020 (AC-004-04): The alarm SHALL repeat every 30 seconds until the user sends a message
- REQ-023-021 (AC-004-05): The alarm SHALL be non-blocking — it SHALL NOT prevent the agent from outputting text
US-005: Stop Hook — Prevent Premature Stopping (P0)
As a developer I want the agent to check the goal before it stops responding So that it continues working instead of going silent
Acceptance Criteria:
- REQ-023-022 (AC-005-01): On every Stop event, the hook SHALL read
state.goalfromcontext.json - REQ-023-023 (AC-005-02): If goal is
activeand not all criteria aremetand no blockers exist, the hook SHALL inject: "Goal not achieved. Remaining: [criteria list]. Continue working." - REQ-023-024 (AC-005-03): If goal is
activeand the agent is blocked, the hook SHALL inject the blocker reason and trigger the blocked alarm - REQ-023-025 (AC-005-04): If no goal is active, the hook SHALL do nothing (existing behavior)
- REQ-023-026 (AC-005-05): The agent SHALL read the injected message and resume work toward the goal
US-006: Reflective Re-planning (P1)
As a developer I want the agent to re-plan when a task fails or a criterion regresses So that it adapts instead of repeating the same failed approach
Acceptance Criteria:
- REQ-023-027 (AC-006-01): When a task fails, the agent SHALL log the failure and evaluate alternative approaches
- REQ-023-028 (AC-006-02): When a criterion regresses, the agent SHALL create a new task to investigate the regression
- REQ-023-029 (AC-006-03): Re-planning SHALL NOT discard completed tasks — it SHALL add new tasks or modify pending ones
- REQ-023-030 (AC-006-04): After 3 failed attempts on the same task, the agent SHALL mark the goal as
blockedwith reason and trigger the alarm - REQ-023-031 (AC-006-05): Re-planning SHALL be logged as a progress entry
US-007: Goal Lifecycle Commands (P1)
As a developer I want commands to manage the goal lifecycle So that I have full control over what the agent is pursuing
Acceptance Criteria:
- REQ-023-032 (AC-007-01):
/goal set <statement>SHALL interactively define the goal with exit criteria and SHALL replace any existing active goal - REQ-023-033 (AC-007-02):
/goal statusSHALL display the goal statement, all criteria with status, all tasks with status, recent progress entries, and any blockers - REQ-023-034 (AC-007-03):
/goal achievedSHALL manually mark the goal as achieved (override) and SHALL trigger the success alarm - REQ-023-035 (AC-007-04):
/goal abandon [reason]SHALL abandon the goal without achieving it and SHALL log the reason - REQ-023-036 (AC-007-05):
/goal block <reason>SHALL manually mark the goal blocked and SHALL trigger the attention alarm - REQ-023-037 (AC-007-06):
/goal unblockSHALL clear the blocked status and resume work - REQ-023-038 (AC-007-07): All commands SHALL update
context.jsonatomically
Data Models
Goal Structure in context.json
{
"state": {
"active_session": 412,
"goal": {
"id": "goal-412-001",
"statement": "All CI tests pass at 100% pass rate, 90%+ coverage, zero hardcoded values",
"status": "active",
"set_at": "2026-04-03T16:00:00Z",
"achieved_at": null,
"abandoned_at": null,
"criteria": [
{
"id": "c1",
"description": "CI test pass rate = 100%",
"status": "pending",
"verify_command": "ci/local-ci.sh test",
"verify_pattern": "Tests passed:\\s+(\\d+)",
"met_at": null
},
{
"id": "c2",
"description": "Coverage >= 90%",
"status": "pending",
"verify_command": "ci/local-ci.sh coverage",
"verify_pattern": "TOTAL.*?(\\d+)%",
"met_at": null
},
{
"id": "c3",
"description": "Lint clean (black, isort, flake8)",
"status": "pending",
"verify_command": "ci/local-ci.sh lint",
"verify_pattern": null,
"met_at": null
}
],
"tasks": [
{
"id": "t1",
"description": "Fix Neo4j connection failures",
"criteria": ["c1"],
"status": "done",
"created_at": "2026-04-03T16:00:00Z",
"done_at": "2026-04-03T17:30:00Z"
}
],
"progress": [
{
"at": "2026-04-03T16:30:00Z",
"action": "CI run",
"note": "11554/11793 passing — Redis hardcoding fixed",
"criteria_changed": {"c3": "met"}
}
],
"blockers": [],
"retry_counts": {}
}
}
}
State Transitions
/goal set
[none] ──────────> [active]
|
+-----------+-----------+
| | |
all criteria /goal block /goal abandon
met | |
| v v
v [blocked] [abandoned]
[achieved] |
| /goal unblock
| |
| v
| [active]
v
(alarm)
Pydantic Models
class GoalCriterion(BaseModel):
id: str
description: str
status: Literal["pending", "met", "failed"] = "pending"
verify_command: str | None = None
verify_pattern: str | None = None
met_at: datetime | None = None
class GoalTask(BaseModel):
id: str
description: str
criteria: list[str]
status: Literal["pending", "in_progress", "done", "failed"] = "pending"
created_at: datetime = Field(default_factory=datetime.now)
done_at: datetime | None = None
class GoalProgress(BaseModel):
at: datetime = Field(default_factory=datetime.now)
action: str
note: str
criteria_changed: dict[str, str] = Field(default_factory=dict)
class Goal(BaseModel):
id: str
statement: str
status: Literal["active", "achieved", "blocked", "abandoned"] = "active"
set_at: datetime = Field(default_factory=datetime.now)
achieved_at: datetime | None = None
abandoned_at: datetime | None = None
criteria: list[GoalCriterion] = Field(default_factory=list)
tasks: list[GoalTask] = Field(default_factory=list)
progress: list[GoalProgress] = Field(default_factory=list)
blockers: list[str] = Field(default_factory=list)
retry_counts: dict[str, int] = Field(default_factory=dict)
Audio Alarm System
Sound Patterns
| Event | Pattern | Frequencies | Duration |
|---|---|---|---|
| Goal achieved | 3 ascending tones | 600, 800, 1000 Hz | 0.2s, 0.2s, 0.3s |
| Agent blocked | 2 low tones, repeated | 300, 300 Hz | 0.3s, 0.3s |
| Criterion met | Single tone | 800 Hz | 0.2s |
| Criterion regressed | Descending tone | 500, 300 Hz | 0.2s, 0.3s |
REQ-023-039: The system SHALL play the sound pattern defined for each event: goal achieved — 3 ascending tones at 600, 800 and 1000 Hz for 0.2s, 0.2s and 0.3s; agent blocked — 2 low tones at 300 Hz repeated for 0.3s each; criterion met — a single 800 Hz tone for 0.2s; criterion regressed — a descending tone at 500 Hz then 300 Hz for 0.2s and 0.3s.
Backend Fallback Chain
ffplay -nodisp -autoexit -f lavfi -i "sine=f={freq}:d={dur}"(ffmpeg)paplay /usr/share/sounds/freedesktop/stereo/complete.oga(PulseAudio)aplay <generated wav>(ALSA)mpv --no-video <generated wav>(mpv)python3 -c "import sys; sys.stdout.write('\a'); sys.stdout.flush()"(terminal bell)
Detection at startup: probe each backend, cache the first working one.
Repeat Logic
- On achievement: play once, then every 30s until user sends a message
- On blocked: play once, then every 60s until user sends a message
- The repeat is a background process that checks a flag file (
/tmp/orchestrata-alarm-active) - User message via UserPromptSubmit hook removes the flag file and stops the alarm
REQ-023-040: On achievement the alarm SHALL play once and then every 30 seconds until the user sends a message; on blocked it SHALL play once and then every 60 seconds until the user sends a message.
REQ-023-041: The alarm repeat SHALL be a background process that checks the flag file /tmp/orchestrata-alarm-active, and a user message received via the UserPromptSubmit hook SHALL remove the flag file and stop the alarm.
Functional Requirements
FR-001: Goal Persistence
REQ-023-042: The system SHALL persist all goal state in context.json including statement, criteria, tasks, progress, and blockers.
REQ-023-043: The goal SHALL survive session restarts, context compaction, and conversation truncation.
FR-002: Goal Injection at Session Start
REQ-023-044: The SessionStart hook SHALL read the goal from context.json and inject it into the conversation.
REQ-023-045: If an active goal exists, the hook SHALL display the goal statement and all criteria with current status indicators ([x] for met, [ ] for pending).
FR-003: Continuous Progress Evaluation
REQ-023-046: After every CI run, test execution, or code modification, the system SHALL automatically evaluate all goal criteria. The evaluation SHALL NOT require user intervention and SHALL be triggered by session events.
FR-004: Exit Criterion Validation
REQ-023-047: Each exit criterion SHALL be validated by executing its verify_command and matching against its verify_pattern.
REQ-023-048: The system SHALL transition criterion status from pending to met when the pattern matches successfully.
FR-005: Stop Hook Goal Guard
REQ-023-049: The Stop hook SHALL prevent the agent from stopping when an active goal exists with unachieved criteria.
REQ-023-050: The hook SHALL inject a continue message and signal continuation instead of allowing the stop.
FR-006: Audio Alarm on Completion
REQ-023-051: When all exit criteria are met, the system SHALL play a success audio alarm.
REQ-023-052: When the goal becomes blocked, the system SHALL play an attention audio alarm.
REQ-023-053: The alarm SHALL persist until the user sends a message.
FR-007: Goal Lifecycle Commands
REQ-023-054: The system SHALL support /goal commands for set, status, achieved, abandon, block, and unblock operations.
REQ-023-055: All commands SHALL update context.json atomically and SHALL provide user feedback.
FR-008: Reflective Re-planning
REQ-023-056: When a task fails or a criterion regresses, the system SHALL evaluate alternative approaches.
REQ-023-057: After 3 consecutive failures on the same task, the system SHALL mark the goal as blocked with a reason.
FR-009: Blocker Detection and Notification
REQ-023-058: The system SHALL detect blockers that prevent goal progress. Blockers MAY be auto-detected (network failures, permission errors) or user-defined via /goal block.
REQ-023-059: When blocked, the system SHALL trigger the attention alarm.
FR-010: Goal State Machine
REQ-023-060: The system SHALL enforce goal state transitions according to the defined state machine: none → active → (achieved|blocked|abandoned). Invalid transitions SHALL be rejected.
FR-011: Progress Logging
REQ-023-061: Every goal state change SHALL be logged as a progress entry with timestamp, action, notes, and criteria changes.
REQ-023-062: Progress SHALL be stored in context.json and MAY be persisted to Neo4j.
FR-012: Criteria Regression Handling
REQ-023-063: When a previously met criterion regresses to failed status, the system SHALL log the regression, announce it to the user, and trigger re-planning to address the regression.
FR-013: Audio Backend Detection
REQ-023-064: The system SHALL detect the first available audio backend at startup using the fallback chain: ffplay, paplay, aplay, mpv, terminal bell.
REQ-023-065: The detected backend SHALL be cached for subsequent use.
FR-014: Context JSON Integrity
REQ-023-066: The goal field in context.json SHALL maintain referential integrity: task criteria references SHALL match existing criterion IDs.
REQ-023-067: Invalid references SHALL be rejected during goal creation.
FR-015: Multi-session Goal Visibility
REQ-023-068: An active goal SHALL be visible across all sessions within the same project.
REQ-023-069: New Claude Code sessions SHALL receive the goal via session hooks, not via conversation memory.
FR-016: Alarm Non-Blocking
REQ-023-070: Audio alarms SHALL NOT block agent output or tool execution.
REQ-023-071: Alarms SHALL run as background processes with a flag file to coordinate state.
FR-017: Goal Abandonment Tracking
REQ-023-072: When a goal is abandoned, the system SHALL log the abandonment reason, timestamp, and SHALL NOT auto-resume.
REQ-023-073: Abandoned goals SHALL be displayable via /goal status.
Error Conditions
| ID | Condition | Handling |
|---|---|---|
| ERR-001 | Goal set with blank statement | Reject with error "Goal statement cannot be blank", maintain no active goal |
| ERR-002 | Criterion has no verify_command or verify_pattern | Allow creation with warning, criterion can only transition via /goal achieved |
| ERR-003 | Goal state transition to invalid state | Reject transition, log error, maintain current state |
| ERR-004 | No audio backend available | Log warning, disable audio alarms, continue with terminal bell only |
| ERR-005 | context.json goal field is corrupted JSON | Fallback to no active goal, log error to system logs |
| ERR-006 | Stop hook cannot read goal from context.json | Allow stop (existing behavior), log error, flag for manual review |
| ERR-007 | All criteria remain pending after 100 task completions | Log warning "Goal may be mis-specified", continue evaluation |
| ERR-008 | Goal ID collision during goal creation | Generate new unique ID with timestamp suffix, log warning |
| ERR-009 | Task references non-existent criterion ID | Reject task creation, return error with valid criterion IDs |
| ERR-010 | Goal evaluation timeout after 30 seconds | Log timeout error, continue to next evaluation cycle |
| ERR-011 | Alarm process cannot be spawned | Log error to system logs, continue without audio |
| ERR-012 | Flag file /tmp/orchestrata-alarm-active cannot be written |
Disable alarm repeat, play alarm once only |
| ERR-013 | User prompt during alarm repeat | Attempt to kill alarm process, log success/failure |
| ERR-014 | Neo4j persistence fails for goal state | Continue with context.json only, log error to system logs |
| ERR-015 | Re-planning generates no new tasks after 3 failures | Mark goal as blocked with reason "No alternative approaches found" |
| ERR-016 | Goal achievement with conflicting criterion statuses | Log error, require manual resolution via /goal achieved override |
| ERR-017 | Session start with corrupted goal progress entries | Filter invalid entries, continue with valid entries, log warning |
| ERR-018 | Alarm backend probe fails for all backends | Fall back to terminal bell (\a), log all backend failures |
| ERR-019 | Goal criterion verify_command execution fails | Log failure, maintain pending status, continue with next criterion |
| ERR-020 | Multiple concurrent goal updates (race condition) | Last write wins, log warning about concurrent modification |
REQ-023-074: ERR-001 — When a goal is set with a blank statement, the system SHALL reject it with the error "Goal statement cannot be blank" and SHALL maintain no active goal.
REQ-023-075: ERR-002 — When a criterion has no verify_command or verify_pattern, the system SHALL allow its creation with a warning, and that criterion SHALL be transitionable only via /goal achieved.
REQ-023-076: ERR-003 — When a goal state transition targets an invalid state, the system SHALL reject the transition, log the error, and maintain the current state.
REQ-023-077: ERR-004 — When no audio backend is available, the system SHALL log a warning, disable audio alarms, and continue with the terminal bell only.
REQ-023-078: ERR-005 — When the goal field in context.json is corrupted JSON, the system SHALL fall back to no active goal and log the error to the system logs.
REQ-023-079: ERR-006 — When the Stop hook cannot read the goal from context.json, it SHALL allow the stop (existing behavior), log the error, and flag it for manual review.
REQ-023-080: ERR-007 — When all criteria remain pending after 100 task completions, the system SHALL log the warning "Goal may be mis-specified" and SHALL continue evaluation.
REQ-023-081: ERR-008 — On a goal ID collision during goal creation, the system SHALL generate a new unique ID with a timestamp suffix and log a warning.
REQ-023-082: ERR-009 — When a task references a non-existent criterion ID, the system SHALL reject the task creation and return an error carrying the valid criterion IDs.
REQ-023-083: ERR-010 — When goal evaluation times out after 30 seconds, the system SHALL log the timeout error and continue to the next evaluation cycle.
REQ-023-084: ERR-011 — When an alarm process cannot be spawned, the system SHALL log the error to the system logs and continue without audio.
REQ-023-085: ERR-012 — When the flag file /tmp/orchestrata-alarm-active cannot be written, the system SHALL disable the alarm repeat and play the alarm once only.
REQ-023-086: ERR-013 — On a user prompt during an alarm repeat, the system SHALL attempt to kill the alarm process and SHALL log the success or failure.
REQ-023-087: ERR-014 — When Neo4j persistence fails for goal state, the system SHALL continue with context.json only and log the error to the system logs.
REQ-023-088: ERR-015 — When re-planning generates no new tasks after 3 failures, the system SHALL mark the goal as blocked with the reason "No alternative approaches found".
REQ-023-089: ERR-016 — On goal achievement with conflicting criterion statuses, the system SHALL log the error and require manual resolution via the /goal achieved override.
REQ-023-090: ERR-017 — On session start with corrupted goal progress entries, the system SHALL filter the invalid entries, continue with the valid entries, and log a warning.
REQ-023-091: ERR-018 — When the alarm backend probe fails for all backends, the system SHALL fall back to the terminal bell (\a) and log all backend failures.
REQ-023-092: ERR-019 — When a goal criterion verify_command execution fails, the system SHALL log the failure, maintain the pending status, and continue with the next criterion.
REQ-023-093: ERR-020 — On multiple concurrent goal updates (race condition), last write SHALL win and the system SHALL log a warning about the concurrent modification.
API Contracts
SessionController.goal_set(statement: str, criteria: list[dict]) -> Goal
Creates a new active goal with the provided statement and criteria.
Parameters:
statement: String describing the goal (must contain at least 1 character)criteria: List of dicts with keys: description, verify_command (optional), verify_pattern (optional)
Returns:
- Goal object with generated ID and initial state
Exceptions:
ValueError: If statement is blank or criteria are invalid
SessionController.goal_evaluate() -> dict[str, str]
Evaluates all active goal criteria against their verification commands and patterns.
Returns:
- Dictionary mapping criterion IDs to new status ('pending'|'met'|'failed')
Side Effects:
- Updates criterion statuses in context.json
- Creates progress entries for status changes
- Triggers alarms if goal achieves or blocks
SessionController.goal_status() -> dict
Returns complete goal status including statement, criteria with status, tasks, and progress.
Returns:
- Dictionary with goal structure and current state
SessionController.goal_achieve(reason: str = "Manually achieved") -> bool
Manually marks the goal as achieved and triggers success alarm.
Parameters:
reason: Optional reason for achievement
Returns:
- True if successful, False if no active goal
SessionController.goal_abandon(reason: str) -> bool
Abandons the active goal without achieving it.
Parameters:
reason: String explaining why goal is abandoned (must contain at least 1 character)
Returns:
- True if successful, False if no active goal
SessionController.goal_block(reason: str) -> bool
Marks the active goal as blocked and triggers attention alarm.
Parameters:
reason: String explaining the blocker (must contain at least 1 character)
Returns:
- True if successful, False if no active goal
SessionController.goal_unblock() -> bool
Clears blocked status from the active goal.
Returns:
- True if successful, False if no active goal or goal not blocked
AudioAlarm.play_success_alarm() -> bool
Plays the success alarm pattern (3 ascending tones).
Returns:
- True if alarm played, False if all backends failed
AudioAlarm.play_blocked_alarm() -> bool
Plays the blocked alarm pattern (2 low repeating tones).
Returns:
- True if alarm played, False if all backends failed
AudioAlarm.cancel_alarms() -> bool
Cancels all active alarm processes.
Returns:
- True if cancellation attempted, False if no active alarms
HookArchitecture.session_start_goal_injection() -> str
Injects goal information into session start output.
Returns:
- Markdown formatted goal display string, or blank string if no active goal
HookArchitecture.stop_hook_goal_check() -> bool
Checks goal status before allowing agent stop.
Returns:
- True if stop should be prevented, False if stop is allowed
REQ-023-094: SessionController SHALL expose the goal API with the parameters, return values, side effects and exceptions defined in this section: goal_set(statement: str, criteria: list[dict]) -> Goal, goal_evaluate() -> dict[str, str], goal_status() -> dict, goal_achieve(reason: str) -> bool, goal_abandon(reason: str) -> bool, goal_block(reason: str) -> bool, and goal_unblock() -> bool.
REQ-023-095: AudioAlarm SHALL expose play_success_alarm() -> bool, play_blocked_alarm() -> bool and cancel_alarms() -> bool, and the hook layer SHALL expose session_start_goal_injection() -> str and stop_hook_goal_check() -> bool, with the return semantics defined in this section.
Non-Functional Requirements
Performance
- REQ-023-101: Goal evaluation SHALL complete within 30 seconds
- REQ-023-102: Audio alarm playback SHALL complete within 5 seconds
- REQ-023-103: context.json reads/writes SHALL complete within 1 second
- REQ-023-104: Session hook execution SHALL complete within 2 seconds
Availability
- REQ-023-105: Goal system SHALL maintain functionality if audio alarms fail
- REQ-023-106: Goal persistence SHALL work even if Neo4j is unavailable
- REQ-023-107: Session hooks SHALL gracefully degrade if context.json is temporarily unavailable
Security
- REQ-023-108: Goal statements SHALL NOT execute arbitrary code
- REQ-023-109: verify_command values SHALL be validated against a whitelist of safe commands
- REQ-023-110: Goal status updates SHALL be logged for audit
- REQ-023-111: User SHALL be able to abandon goals at any time (user autonomy)
Scalability
- REQ-023-112: System SHALL support up to 50 active criteria per goal
- REQ-023-113: System SHALL support up to 1000 progress entries per goal
- REQ-023-114: System SHALL support concurrent goal operations from multiple session instances
Maintainability
- REQ-023-115: Goal model SHALL be defined using Pydantic for validation
- REQ-023-116: Hook architecture SHALL be modular for easy testing
- REQ-023-117: Error conditions SHALL be logged with context for debugging
Reliability
- REQ-023-118: Goal state SHALL be consistent across session restarts
- REQ-023-119: Audio alarms SHALL have fallback chain to ensure user notification
- REQ-023-120: Goal evaluation SHALL be idempotent (multiple evaluations yield same result)
Usability
- REQ-023-121: Goal status SHALL be human-readable at a glance
- REQ-023-122: Audio alarms SHALL be distinguishable by sound pattern
- REQ-023-123: Goal commands SHALL provide immediate feedback
Technical Design
Architecture Pattern
The goal system implements the BDI (Belief-Desire-Intention) agent architecture:
Belief Layer: context.json state containing current system status (test counts, branch, coverage)
Desire Layer: Goal statement with exit criteria defining what the user wants achieved
Intention Layer: Active goal with committed tasks defining what the agent will do about it
Data Flow
1. User sets goal → /goal set → SessionController.goal_set()
↓
2. Goal stored in context.json
↓
3. Session start → Hook reads context.json → Injects goal into conversation
↓
4. Work proceeds → Events trigger goal_evaluate()
↓
5. Evaluation checks criteria → Updates status → Creates progress entries
↓
6. If all criteria met → goal_achieve() → Play success alarm
↓
7. If blocked → goal_block() → Play blocked alarm
↓
8. If user sends message → Cancel alarms
Storage Design
- Primary Storage: context.json (survives all sessions and restarts)
- Secondary Storage: Neo4j (optional, for audit trail and queries)
- Runtime State: In-memory goal object for fast access
- Flag Files: /tmp/orchestrata-alarm-active for alarm coordination
Event Triggers
- SessionStart: Goal injection
- CIComplete: Criterion evaluation
- TaskComplete: Criterion evaluation
- Stop: Goal guard check
- UserPromptSubmit: Alarm cancellation
State Machine Implementation
- Goal states managed via enum: ACTIVE, ACHIEVED, BLOCKED, ABANDONED
- Transitions validated at application layer (no database constraints needed)
- All transitions logged for audit trail
Audio Alarm Architecture
Alarm Triggered
↓
Detect Backend (probe ffplay → paplay → aplay → mpv → bell)
↓
Write Flag File (/tmp/orchestrata-alarm-active)
↓
Spawn Background Process
↓
Play Pattern (repeat every 30s/60s)
↓
User Message Received → Cancel Alarm
↓
Remove Flag File
Hook Integration Points
- session_auto_start.py: Inject goal at session start
- goal_check_hook.py: New hook for Stop event
- session_close_hook.py: Enhanced with alarm cancellation
- ci_results_parser.py: Enhanced to trigger goal evaluation
Security
Input Validation
- REQ-023-124: Goal statements SHALL be validated for maximum length (512 characters)
- REQ-023-109 (restated from Non-Functional Requirements → Security): Criterion verify_command values SHALL be validated against whitelist of safe commands
- REQ-023-125: Reason strings for abandon/block SHALL be sanitized to prevent injection
- REQ-023-126: User SHALL be able to inspect and override any automated goal operations
Audit Logging
- REQ-023-127: All goal state changes SHALL be logged with timestamp, user, and reason
- REQ-023-128: Goal progress SHALL be stored in Neo4j for complete audit trail
- REQ-023-129: Authentication status SHALL be included in all goal-related logs
- REQ-023-130: Failed verification commands SHALL be logged with full error context
Access Control
- REQ-023-131: Any user SHALL be able to view goal status
- REQ-023-132: Any user SHALL be able to abandon or block goals
- REQ-023-133: Goal modification SHALL require an active session (no background changes without user action)
- REQ-023-134: Critical operations (abandon, achieve) SHALL be confirmed before execution
Data Protection
- REQ-023-135: Goal data SHALL NOT contain sensitive information (passwords, tokens)
- REQ-023-136: context.json file SHALL have restricted permissions (owner read/write only)
- REQ-023-137: Audio alarms SHALL NOT expose system information in their execution
Threat Model
- Tampering: context.json protected by filesystem permissions
- Injection: verify_command whitelist prevents command injection
- DoS: Alarm rate limiting prevents audio spam
- Privilege Escalation: Goal operations run with user permissions only
Integration Points
Session Management (Spec 006)
- Goal system extends ProjectState with goal field
- Session hooks inject goal at startup
- Stop hook prevents premature stopping when goals active
- Session lifecycle events trigger goal evaluation
Context System
- Goal persisted in context.json under state.goal
- Context compaction preserves goal (survives memory truncation)
- Context file operations use atomic writes for integrity
- REQ-023-096: The goal schema in
context.jsonSHALL be versioned for backward compatibility
Audio System
- Backend detection probes system audio capabilities
- Fallback chain ensures alarm delivery
- Alarm processes run non-blocking
- Terminal bell as universal fallback
CI/CD Integration
- local-ci.sh results parsed for criterion evaluation
- Test pass/fail counts extracted from JUnit XML
- Coverage reports parsed for threshold verification
- Lint output checked for clean status
REQ-023-097: local-ci.sh results SHALL be parsed for criterion evaluation: test pass/fail counts extracted from JUnit XML, coverage reports parsed for threshold verification, and lint output checked for clean status.
Claude Code Hooks
- SessionStart hook injects goal display
- Stop hook checks goal before allowing stop
- UserPromptSubmit hook cancels active alarms
- Custom
/goalcommands registered in hook system
Neo4j Database (Optional)
- Goal nodes with CONTAINS relationship to criteria
- HAS_TASK relationship to task nodes
- Cross-session goal history queries
- Analytics on goal achievement rates
REQ-023-098: Goal state persisted to Neo4j SHALL use Goal nodes with a CONTAINS relationship to criteria and a HAS_TASK relationship to task nodes, and SHALL support cross-session goal-history queries and goal-achievement-rate analytics.
Hook Architecture
SessionStart: Goal Injection
session_auto_start.py
+-- Read context.json
+-- If state.goal exists and status == "active":
| +-- Output: "## Active Goal"
| +-- Output: goal.statement
| +-- Output: criteria with [x]/[ ] status
| +-- Output: "Continue working toward this goal."
+-- If no goal or goal not active:
+-- Output: "No active goal. Use /goal set <statement> to define one."
REQ-023-099: When no goal exists or the goal is not active, the SessionStart hook SHALL output No active goal. Use /goal set <statement> to define one.
Stop: Goal Guard
goal_check_hook.py (new, registered on Stop event)
+-- Read context.json
+-- If no active goal: exit (allow stop)
+-- Evaluate all criteria:
+-- If all met: mark achieved, play success alarm, allow stop
+-- If blocked: play blocked alarm, allow stop
+-- If criteria remain and no blockers:
+-- Output: "Goal not achieved. Remaining criteria: [list]"
+-- Output: "Resuming work."
+-- Exit with continue signal
REQ-023-100: When the Stop hook evaluates all criteria and finds them all met, it SHALL mark the goal achieved, play the success alarm and allow the stop; when the goal is blocked, it SHALL play the blocked alarm and allow the stop.
UserPromptSubmit: Alarm Cancellation
session_close_hook.py (existing, add alarm check)
+-- If /tmp/orchestrata-alarm-active exists:
+-- Remove flag file
+-- Kill alarm background process
Examples
Example 1: Setting a Goal
User: /goal set "Complete CI pipeline with 100% test pass rate"
Agent: I'll help you define measurable criteria for this goal.
Exit Criterion 1: All CI tests pass
Exit Criterion 2: Coverage >= 90%
Exit Criterion 3: Code passes all linting
[Continue interactively...]
User: Accept these criteria
Agent: Goal set! Active: "Complete CI pipeline with 100% test pass rate"
Criteria:
[ ] All CI tests pass
[ ] Coverage >= 90%
[ ] Code passes all linting
Example 2: Progress Evaluation
Agent: Running CI tests... 11554/11793 tests passing
Coverage report: 87.5%
Goal Evaluation:
❌ Criterion 1: Tests pass (239 failures remain)
❌ Criterion 2: Coverage < 90%
✅ Criterion 3: Lint clean
Continuing work toward goal...
Example 3: Goal Achievement
Agent: Running CI tests... 11793/11793 tests passing
Coverage report: 92.3%
Goal Evaluation:
✅ All criteria met!
[SUCCESS ALARM PLAYS]
Goal achieved! "Complete CI pipeline with 100% test pass rate"
Achieved at: 2026-04-09T14:30:00Z
Example 4: Stop Hook Prevention
Agent: [User sends nothing, system prepares to stop]
Stop Hook: Active goal detected!
Goal not achieved. Remaining criteria:
- All CI tests pass (123 failures)
- Coverage >= 90% (currently 87.5%)
Resuming work toward goal...
Example 5: Reflective Re-planning
Agent: Task "Fix Neo4j connection" failed after 3 attempts
Re-planning triggered:
Alternative approach: Use SQLite fallback
New task: "Implement SQLite persistence layer"
Alternative approach: Mock Neo4j for development
New task: "Create test doubles for Neo4j"
[ALARM: Goal blocked - no viable alternatives after 3 failed tasks]
Success Criteria
The following criteria MUST be met for this spec to be considered complete:
Phase 1: Data Model and CRUD (Day 1)
- Add
Goal,GoalCriterion,GoalTask,GoalProgressmodels to session models - Add
goalfield toProjectStatemodel - Add goal operations to
SessionController - Add
/goalskill to Claude Code hooks - Tests: goal CRUD, persistence, state transitions
Phase 2: Session Injection (Day 1)
- Modify
session_auto_start.pyto read and display active goal - Goal prompt when no goal is set
- Tests: injection at session start, after compaction
Phase 3: Continuous Evaluation (Day 2)
- Add
goal_evaluate()method that checks criteria - Integrate with CI results parsing (read JUnit XML, extract pass/fail/coverage)
- Auto-evaluate after
ci/local-ci.shruns - Progress logging
- Tests: evaluation triggers, criterion transitions, progress entries
Phase 4: Stop Hook Guard (Day 2)
- Create
goal_check_hook.pyregistered on Stop event - Goal state check logic
- Continue signal when goal not achieved
- Tests: stop prevention, stop allowance when achieved/blocked
Phase 5: Audio Alarm (Day 2)
- Backend detection (probe ffplay, paplay, aplay, mpv)
- Tone generation for each pattern
- Repeat logic with flag file
- Alarm cancellation on user message
- Tests: backend detection, alarm trigger, cancellation
Phase 6: Reflective Re-planning (Day 3)
- Failure detection after task execution
- Re-planning logic (propose alternative tasks)
- Retry counting with 3-strike block
- Tests: re-planning triggers, retry limits
Phase 7: Neo4j Integration (Day 3)
- Persist goal lifecycle to Neo4j (Goal node, CONTAINS criteria, HAS_TASK relationships)
- Query goal history across sessions
- Tests: Neo4j persistence, cross-session queries
Testing Requirements
Test Framework and Approach
- REQ-023-138: Primary Framework: pytest for Python test implementation
- REQ-023-139: Test Type: Behavior-Driven Development (BDD) approach emphasizing goal-oriented testing
- REQ-023-140: Test Organization: Tests grouped by functional requirement (FR-
) and user story (US- ) - REQ-023-141: Mocking Policy: No mocking of core goal system components; real implementations required for authentic testing
- REQ-023-142: Assertion Style: Use explicit assertions with descriptive error messages
Unit Testing Requirements
FR-001 through FR-017 Coverage
REQ-023-143: Each functional requirement MUST have corresponding unit tests:
Goal Model Tests (FR-001, FR-014)
- Test goal creation with valid/invalid statement lengths
- Test criteria validation and ID reference integrity
- Test task-to-criteria relationship validation
- Test goal state transitions (active → achieved/blocked/abandoned)
Goal Persistence Tests (FR-001)
- Test goal serialization to context.json
- Test goal deserialization from context.json
- Test corrupted goal data handling (ERR-005)
- Test atomic goal updates
Evaluation Tests (FR-003, FR-004, FR-012)
- Test automatic criterion evaluation after CI runs
- Test verify_command execution and pattern matching
- Test criterion status transitions (pending → met → failed → pending)
- Test regression detection and logging
Audio Alarm Tests (FR-006, FR-013, FR-016)
- Test backend detection probe (ffplay, paplay, aplay, mpv, bell)
- Test success alarm pattern playback
- Test blocked alarm pattern playback
- Test alarm non-blocking behavior
- Test alarm cancellation on user message
Command Tests (FR-007)
- Test
/goal setcommand with interactive criteria definition - Test
/goal statusdisplays complete goal state - Test
/goal achievedmanual override - Test
/goal abandonwith reason logging - Test
/goal blockand/goal unblockoperations
Pydantic Model Validation Tests
def test_goal_criterion_validation():
"""Test GoalCriterion model validates fields correctly"""
def test_goal_task_validation():
"""Test GoalTask model enforces criteria references"""
def test_goal_progress_validation():
"""Test GoalProgress model requires timestamp and action"""
Integration Testing Requirements
Hook Integration Tests
-
SessionStart Hook (FR-002): Test goal injection at session start
- Test goal display when active goal exists
- Test prompt when no goal set
- Test criteria status indicators [x]/[ ]
-
Stop Hook (FR-005): Test goal guard preventing premature stop
- Test stop prevention when criteria remain
- Test stop allowance when goal achieved
- Test stop allowance when goal blocked
- Test stop allowance when no active goal
-
UserPromptSubmit Hook: Test alarm cancellation
- Test flag file removal
- Test alarm process termination
Session Management Integration (Spec 006)
- Test goal persistence across session lifecycle events
- Test context.json integration with session state
- Test goal surviving context compaction
- Test goal injection after conversation restart
CI/CD Integration Tests
- Test automatic evaluation trigger after
ci/local-ci.shruns - Test JUnit XML parsing for test pass/fail counts
- Test coverage report parsing for threshold verification
- Test lint output validation
End-to-End Testing Requirements
US-001 through US-007 Scenarios
REQ-023-144: Each user story MUST have end-to-end tests simulating real usage:
US-001: Set Session Goal (P0)
def test_e2e_set_session_goal():
"""End-to-end: User sets goal, agent decomposes into criteria"""
# Simulate: session start → /goal set → criteria definition → goal active
US-002: Continuous Progress Evaluation (P0)
def test_e2e_continuous_progress_evaluation():
"""End-to-end: Agent evaluates progress automatically after actions"""
# Simulate: task → CI run → criterion evaluation → progress log
US-003: Goal Persistence (P0)
def test_e2e_goal_persistence_across_compaction():
"""End-to-end: Goal survives context compaction"""
# Simulate: set goal → compact context → restart session → goal still active
US-004: Audio Alarm (P1)
def test_e2e_audio_alarm_on_achievement():
"""End-to-end: Success alarm plays when goal achieved"""
# Simulate: all criteria met → alarm plays → user message cancels
def test_e2e_audio_alarm_on_block():
"""End-to-end: Attention alarm plays when blocked"""
# Simulate: goal blocked → alarm plays → user message cancels
US-005: Stop Hook (P0)
def test_e2e_stop_hook_prevents_premature_stopping():
"""End-to-end: Stop hook prevents agent from stopping"""
# Simulate: active goal → prepare to stop → stop hook injects continue
US-006: Reflective Re-planning (P1)
def test_e2e_reflective_replanning_on_failure():
"""End-to-end: Agent re-plans after task failure"""
# Simulate: task fails → re-plan → new tasks → 3 strikes blocks goal
US-007: Goal Lifecycle Commands (P1)
def test_e2e_goal_lifecycle_commands():
"""End-to-end: All /goal commands work correctly"""
# Simulate: set → status → achieve/abandon/block → unblock
Performance Testing Requirements
FR-016 Performance Benchmarks
- Goal Evaluation: Must complete within 30 seconds (FR-016)
- Audio Alarm Playback: Must complete within 5 seconds (FR-016)
- Context.json Operations: Must complete within 1 second (FR-016)
- Session Hook Execution: Must complete within 2 seconds (FR-016)
Load Testing
def test_performance_goal_evaluation():
"""Test goal evaluation completes within 30 seconds with 50 criteria"""
def test_performance_context_json_write():
"""Test context.json goal update completes within 1 second"""
def test_performance_audio_backend_detection():
"""Test backend detection completes within 2 seconds"""
Test Data Management
Test Fixtures
- Create standardized goal test fixtures with varying complexity:
- Simple goal: 1-3 criteria
- Complex goal: 20+ criteria
- Goal with many progress entries: 1000+ entries
- Goal with regression scenarios
- Goal with blocker states
Context.json Test Files
- Provide clean, corrupted, and edge-case context.json files
- Version-specific test files for backward compatibility
- Multi-session context files for persistence testing
Audio Test Files
- Generate tone patterns for alarm testing
- Create silence detection for alarm cancellation
- Test file corruption handling
Test Environment Requirements
Audio Backend Testing
- Test all audio backends in isolation:
- Mock
ffmpegavailability - Mock
pulseaudioavailability - Mock
alsaavailability - Mock
mpvavailability - Fall back to terminal bell
- Mock
File System Requirements
- Write permissions for
/tmp/(alarm flag file) - Read/write permissions for
context.json - Temporary directory for audio file generation
Database Testing
- Neo4j integration tests (optional per FR-014)
- Graph persistence and query validation
- Audit trail completeness verification
Code Coverage Requirements
Minimum Coverage Thresholds
- Goal system modules: 95% line coverage
- Session controller: 90% line coverage
- Audio alarm system: 85% line coverage (hardware-dependent)
- Hook architecture: 90% line coverage
- Overall system: 90% line coverage
REQ-023-145: Line-coverage minimums SHALL be met: goal system modules 95%, session controller 90%, audio alarm system 85% (hardware-dependent), hook architecture 90%, overall system 90%.
Coverage Exclusions
- Audio backend probe failures (hardware-dependent)
- Actual audio playback (requires hardware)
- Neo4j unavailability (infrastructure-dependent)
REQ-023-146: Coverage measurement SHALL exclude audio backend probe failures, actual audio playback, and Neo4j unavailability.
Test Automation Requirements
Pre-commit Hook
- Run goal system unit tests before commit
- Enforce 90% coverage threshold
- Validate JSON schema conformity
REQ-023-147: The pre-commit hook SHALL run the goal system unit tests, enforce the 90% coverage threshold, and validate JSON schema conformity.
CI/CD Integration
# Required CI commands
python -m pytest tests/test_goal_system/ -v --cov=src/goal_system --cov-report=term-missing
REQ-023-148: CI SHALL run python -m pytest tests/test_goal_system/ -v --cov=src/goal_system --cov-report=term-missing.
Test Reporting
- Generate JUnit XML for CI integration
- HTML coverage reports for developer review
- Test execution timing reports for performance monitoring
REQ-023-149: Test runs SHALL generate JUnit XML for CI integration, HTML coverage reports for developer review, and test execution timing reports for performance monitoring.
Error Condition Testing
ERR-001 through ERR-020 Coverage
REQ-023-150: Each error condition MUST have tests verifying proper handling:
def test_err001_blank_goal_statement():
"""Test ERR-001: Reject blank goal statement"""
def test_err002_criterion_no_verify_command():
"""Test ERR-002: Allow criterion without verify command with warning"""
def test_err005_corrupted_context_json():
"""Test ERR-005: Handle corrupted goal field gracefully"""
def test_err019_verify_command_execution_fails():
"""Test ERR-019: Handle verification command failure"""
Regression Testing
Context Compaction Regression
- Simulate context compaction by truncating conversation
- Verify goal survives compaction
- Verify session hook re-injects goal correctly
Session Restart Regression
- Start session with active goal
- End session and start new session
- Verify goal persistence and injection
Multi-session Goal Visibility
- Set goal in session A
- Open session B for same project
- Verify goal visible in session B
Verification
Test Categories
- Unit: Goal model CRUD, state transitions, criterion evaluation
- Integration: Hook injection, stop guard, alarm trigger
- E2E: Full lifecycle — set goal, work toward it, achieve it, hear alarm
- Regression: Goal survives context compaction (simulate by truncating conversation)
Exit Criteria for This Spec
- [ ] Goal persists across session restart (verified by restarting Claude Code)
- [ ] Goal displayed at session start (verified by reading hook output)
- [ ] Stop hook prevents premature stopping (verified by triggering Stop with active goal)
- [ ] Audio alarm plays on achievement (verified by marking goal achieved)
- [ ] Audio alarm plays on block (verified by marking goal blocked)
- [ ]
/goalcommands work (verified by running each command) - [ ] CI passes with goal system tests (verified by
ci/local-ci.sh test)
Observability
Logging Requirements
- Component Logging
- Log all major operations with appropriate log levels
- Include correlation IDs for request tracing
- Log performance metrics for critical operations
- Log errors with full context for debugging
REQ-023-151: The goal system SHALL log all major operations with appropriate log levels, include correlation IDs for request tracing, log performance metrics for critical operations, and log errors with full context for debugging.
Metrics Requirements
- Performance Metrics
- Operation duration histograms
- Request/error rate counters
- Resource utilization gauges
- Business metrics relevant to domain
REQ-023-152: The goal system SHALL expose performance metrics: operation duration histograms, request/error rate counters, resource utilization gauges, and business metrics relevant to the domain.
Tracing Requirements
- Distributed Tracing
- Trace propagation across component boundaries
- Span attributes for domain-specific context
- Trace sampling based on operational needs
REQ-023-153: The goal system SHALL support distributed tracing: trace propagation across component boundaries, span attributes for domain-specific context, and trace sampling based on operational needs.
Alerting Requirements
- Critical Alerts
- Error rate spikes
- Performance degradation
- Resource exhaustion
- Business metric anomalies
REQ-023-154: The goal system SHALL raise critical alerts on error rate spikes, performance degradation, resource exhaustion, and business metric anomalies.
Dashboard Requirements
- Operational Dashboard
- Real-time system health
- Performance trends
- Error analysis
- Resource monitoring
REQ-023-155: The goal system SHALL provide an operational dashboard covering real-time system health, performance trends, error analysis, and resource monitoring.
Revision History
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0.0 | 2026-04-09 | Mohamed | Initial specification for Goal-Driven Session System implementing BDI architecture with audio alarms, continuous progress evaluation, and session persistence |