Orchestrata Ultimate / Specs / 020-claude-code-harness
020-claude-code-harness: Claude Code Harness
A comprehensive hook-based harness that wraps Claude Code with session management, code quality enforcement, deception detection, safety guards, audit logging, and knowledge graph integration. The harness consists of 21 Python hook scripts that fire on Claude Code lifecycle events (SessionStart, PreToolUse, PostToolUse, UserPromptSubmit, Stop), a session management module with CLI, and a deployment pipeline. All hooks are version-controlled in claude/ and deployed to ~/.claude/hooks/ via...
At a glance
| Item | Value |
|---|---|
| Spec directory | specs/020-claude-code-harness/ |
| Status line in the spec | Implemented |
| Requirement IDs defined | 246 |
With a @traces citation in src/ |
9 (3.7%) |
With a @traces citation in tests/ |
1 (0.4%) |
| Tracked files | 6 |
| RTM page | 020-claude-code-harness |
Reading this spec
The Claude Code Harness is a hook-based wrapper around Claude Code that adds session management, code quality enforcement, deception detection, safety guards, audit logging and knowledge graph integration. Claude Code has no built-in session tracking or context persistence, no enforcement of project rules (no mocking, absolute imports, maximum function length, no bare except), no protection against destructive operations and no audit trail; the harness supplies these.
The scope is ten user stories: session lifecycle, code quality enforcement, safety guards and session context persistence (priority 0), deception detection, Neo4j context injection, audit trail and RTM generation (priority 1), and read throttling and remember/search (priority 2). Functional requirements cover hook registration and protocol, session auto-start, close and open, and the error gate checks. Requirement IDs use the REQ-020- prefix.
The design is stated in the overview. Twenty-one Python hook scripts fire on five Claude Code lifecycle events: SessionStart, PreToolUse, PostToolUse, UserPromptSubmit and Stop. Hooks are deployed by install.sh and registered in the Claude settings. An error gate runs six categories of check at session stop and blocks if errors exist. A stop audit compares claims against actual tool usage. A read throttle limits file reads to 3 per 30-second window and redirects to knowledge graph queries. The file context.json is the single source of truth for session state and is never trimmed, while context.md is a rendered view of the last three sessions. The audit trail is stored as JSONL with archive rotation and no deletion.
In the code, the hooks and tools live outside the orchestrata_ultimate package, so the Python trace table is short: orchestrata_ultimate/session/models, orchestrata_ultimate/session/controller/_git and orchestrata_ultimate/session/context. The per-requirement view is on the RTM page.
The spec's status line reads Implemented. It tracks 6 files and defines 246 requirement IDs. The full text follows below.
Measured coverage is 9 of 246 requirements cited by at least one @traces in src/ (3.7%), spread over 6 modules; the module citing the most of them is orchestrata_ultimate/session/context (3 requirements). In tests/, 1 requirement is cited by a test-side @traces.
Coverage gaps: 237 requirements have no citing symbol. The largest untraced groups are REQ-020-* (237). 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 (1 against 9).
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-020-001 |
yes | session_auto_start.py fires on SessionStart event |
none |
REQ-020-002 |
yes | Hook only runs for projects with "orchestrata" in the path | none |
REQ-020-003 |
yes | Hook imports SessionController from ~/.claude/tools/session/ |
none |
REQ-020-004 |
yes | Hook checks if last session has no length field (still active) and skips creation |
none |
REQ-020-005 |
yes | Hook checks if last session was created <300 seconds ago and skips creation | none |
REQ-020-006 |
yes | Hook calls SessionController.begin() with timestamped purpose string |
none |
REQ-020-007 |
yes | Hook outputs session number, start time, and purpose to stdout | none |
REQ-020-008 |
yes | Hook handles ImportError gracefully (prints error, does not crash) | none |
REQ-020-009 |
yes | Hook handles SessionController initialization failure gracefully | none |
REQ-020-010 |
yes | Hook reads cwd from JSON stdin input |
none |
REQ-020-011 |
yes | session_close_hook.py fires on UserPromptSubmit event |
none |
REQ-020-012 |
yes | Hook detects "cts" trigger (case-insensitive, trimmed) | none |
REQ-020-013 |
yes | Hook detects "cs" trigger (case-insensitive, trimmed) | none |
REQ-020-014 |
yes | Hook detects "close the session" trigger (case-insensitive, trimmed) | none |
REQ-020-015 |
yes | Hook finds project root by walking up from cwd looking for context.json | none |
REQ-020-016 |
yes | Hook generates RTM from @traces decorators via subprocess calls | none |
REQ-020-017 |
yes | Hook extracts achievements from git log since session start | none |
REQ-020-018 |
yes | Hook calls SessionController.end() to update context.json |
none |
REQ-020-019 |
yes | Hook creates annotated git tag in format session-NNN-YYYY-MM-DD-HHMM |
1 |
REQ-020-020 |
yes | Hook updates CHANGELOG.md with categorized achievements | 9 |
REQ-020-021 |
yes | Hook pushes session tag to origin via git push origin |
2 |
REQ-020-022 |
yes | Hook pushes current branch to origin via git push origin HEAD |
3 |
REQ-020-023 |
yes | Hook injects close summary into additionalContext |
none |
REQ-020-024 |
yes | RTM generation failures are logged but do not block session close | none |
REQ-020-025 |
yes | Hook does not block (Claude still sees the prompt) | none |
REQ-020-026 |
yes | session_open_hook.py fires on UserPromptSubmit event |
none |
REQ-020-027 |
yes | Hook detects "sns" and "start new session" triggers | none |
REQ-020-028 |
yes | Hook displays pending deferred work items grouped by priority | none |
REQ-020-029 |
yes | Hook injects project metrics into additionalContext | none |
REQ-020-030 |
yes | session_context_hook.py fires on SessionStart event |
none |
REQ-020-031 |
yes | Hook injects session context into conversation on startup | none |
REQ-020-032 |
yes | error_gate_hook.py fires on Stop event |
none |
REQ-020-033 |
yes | Hook delegates to error_gate package main() function |
none |
REQ-020-034 |
yes | error_gate.__init__ runs all 6 check categories sequentially |
none |
REQ-020-035 |
yes | Check order: syntax, lint, stubs, nesting, god objects, tests | none |
REQ-020-036 |
yes | If any errors found, Stop is blocked with decision: block |
none |
REQ-020-037 |
yes | Error report includes numbered fix instructions (6 steps) | none |
REQ-020-038 |
yes | Error report printed to stderr for operator visibility | none |
REQ-020-039 |
yes | If no errors, empty JSON {} returned (allow stop) |
none |
REQ-020-040 |
yes | Hook reads and discards stdin JSON (not used) | none |
REQ-020-041 |
yes | checks_syntax.py finds modified .py files via git diff --name-only --diff-filter=M HEAD |
none |
REQ-020-042 |
yes | Each modified .py file parsed with ast.parse() via subprocess |
none |
REQ-020-043 |
yes | Syntax errors reported with file path and error message (truncated to 200 chars) | none |
REQ-020-044 |
yes | Non-existent files (deleted after diff) are skipped | none |
REQ-020-045 |
yes | Git command failure returns empty error list (not crash) | none |
REQ-020-046 |
yes | checks_lint.py finds modified .py files via git diff |
none |
REQ-020-047 |
yes | Runs uv run flake8 --select=E9,F82,F83,F84 --max-line-length=120 |
none |
REQ-020-048 |
yes | Modified files capped at 30 per run to avoid timeout | none |
REQ-020-049 |
yes | Lint errors reported up to 10 per run | none |
REQ-020-050 |
yes | Empty file list results in no flake8 invocation | none |
REQ-020-051 |
yes | Uses shutil.which("uv") with fallback to ~/.local/bin/uv |
none |
REQ-020-052 |
yes | checks_test.py finds changed test files via git diff --name-only HEAD~1 |
none |
REQ-020-053 |
yes | Only files under tests/ ending in .py are selected |
none |
REQ-020-054 |
yes | Files with "prolog" in name are excluded | none |
REQ-020-055 |
yes | Files importing pyswip or GovernanceEngine are excluded | none |
REQ-020-056 |
yes | Files importing orchestrata.governance or PrologBridge are excluded | none |
REQ-020-057 |
yes | If tests/conftest.py uses pyswip, all tests are skipped |
none |
REQ-020-058 |
yes | Test file count capped at 10 per run | none |
REQ-020-059 |
yes | Runs uv run pytest -x -q --no-header on selected files |
none |
REQ-020-060 |
yes | Test timeout is 300 seconds | none |
REQ-020-061 |
yes | Exit code 139 (SIGSEGV) treated as infrastructure issue, not test failure | none |
REQ-020-062 |
yes | Only lines containing "FAILED" reported as test failures | none |
REQ-020-063 |
yes | checks_structure.py scans non-test modified .py files for stubs |
none |
REQ-020-064 |
yes | Stub defined as function body with only logging, local assigns, pass, docstrings | none |
REQ-020-065 |
yes | __init__, __del__, __enter__, __exit__, close, cleanup excluded |
none |
REQ-020-066 |
yes | Private functions (starting with _) excluded from stub detection |
none |
REQ-020-067 |
yes | await expressions are NOT classified as stubs |
none |
REQ-020-068 |
yes | self.x += 1 (augmented assignment on attributes) is NOT a stub |
none |
REQ-020-069 |
yes | Log calls detected via dotted name resolution (logger.info, log.debug, etc.) |
none |
REQ-020-070 |
yes | List appends (.append()) treated as logging-equivalent for stub detection |
none |
REQ-020-071 |
yes | return None and bare return treated as trivial returns in stubs |
none |
REQ-020-072 |
yes | raise in first 2 statements treated as guard clause, not real work |
none |
REQ-020-073 |
yes | Nesting measured for all functions in modified .py files | none |
REQ-020-074 |
yes | Nesting types: if, for, while, with, try, async for, async with | none |
REQ-020-075 |
yes | Functions exceeding 4 levels of nesting are flagged | none |
REQ-020-076 |
yes | Error message includes function name, line number, actual depth, and max allowed | none |
REQ-020-077 |
yes | Classes exceeding 300 lines flagged (end_lineno - lineno + 1) | none |
REQ-020-078 |
yes | Classes with >15 public methods flagged (not starting with _) |
none |
REQ-020-079 |
yes | Files exceeding 500 lines flagged | none |
REQ-020-080 |
yes | Only source files checked (test files excluded) | none |
REQ-020-081 |
yes | Generated directories (spike/, iterations/, ci/) excluded | none |
REQ-020-082 |
yes | utils.py provides run_command() with subprocess timeout handling |
none |
REQ-020-083 |
yes | utils.py provides get_modified_py_files() with git diff discovery |
none |
REQ-020-084 |
yes | utils.py provides parse_file() with safe AST parsing (returns None on failure) |
none |
REQ-020-085 |
yes | PROJECT_ROOT configurable via ORCHESTRATA_ROOT environment variable |
2 |
REQ-020-086 |
yes | CHECK_TIMEOUT set to 30 seconds per check |
none |
REQ-020-087 |
yes | FileNotFoundError from missing tools treated as skip (returns exit 0) |
none |
REQ-020-088 |
yes | stop_auditor.py fires on Stop event |
none |
REQ-020-089 |
yes | Hook reads JSON from stdin with session_id and transcript_path | none |
REQ-020-090 |
yes | Hook detects SubagentStop events and delegates to subagent handler | none |
REQ-020-091 |
yes | Hook extracts assistant messages from JSONL transcript | none |
REQ-020-092 |
yes | Hook loads tool calls from session JSONL file | none |
REQ-020-093 |
yes | Hook calls detect_deception() with assistant message and tool calls |
none |
REQ-020-094 |
yes | Hook logs findings to deception_log.jsonl |
none |
REQ-020-095 |
yes | Hook archives session JSONL to dated directory | none |
REQ-020-096 |
yes | Hook formats findings as severity-colored report (CRITICAL/HIGH/MEDIUM/LOW) | none |
REQ-020-097 |
yes | Hook outputs report via additionalContext if findings exist | none |
REQ-020-098 |
yes | Hook prints "No issues detected" to stderr if clean | none |
REQ-020-099 |
yes | deception.py strips code blocks (triple backtick) before analysis |
none |
REQ-020-100 |
yes | Strips inline code (single backtick) before analysis | none |
REQ-020-101 |
yes | Strips blockquotes (lines starting with >) before analysis | none |
REQ-020-102 |
yes | Strips HTML pre/code tags before analysis | none |
REQ-020-103 |
yes | File creation claims flagged as CRITICAL if Write/Edit not in tools_used | none |
REQ-020-104 |
yes | Command execution claims flagged as CRITICAL if Bash not in tools_used | none |
REQ-020-105 |
yes | File reading claims flagged as HIGH if Read/Grep not in tools_used | none |
REQ-020-106 |
yes | Tool errors (has_error=True) not mentioned in response flagged as CRITICAL |
none |
REQ-020-107 |
yes | Error concealment checked against 11 error-related terms | none |
REQ-020-108 |
yes | Sessions with >10 tool calls but <100 char response flagged as MEDIUM | none |
REQ-020-109 |
yes | Tool output >500 chars with <3 overlapping 6+ char words flagged as MEDIUM | none |
REQ-020-110 |
yes | Findings include type, severity, description, evidence fields | none |
REQ-020-111 |
yes | Action deception findings include pattern_matched field | none |
REQ-020-112 |
yes | Error concealment findings include tool_name field | none |
REQ-020-113 |
yes | Result hiding findings include tool_name field | none |
REQ-020-114 |
yes | transcript.py reads JSONL file line by line |
none |
REQ-020-115 |
yes | Extracts entries with message.role == "assistant" |
none |
REQ-020-116 |
yes | Handles string content (direct text) | none |
REQ-020-117 |
yes | Handles list content with typed blocks (type == "text") | none |
REQ-020-118 |
yes | Corrupt JSON lines skipped with warning to stderr | none |
REQ-020-119 |
yes | Missing or empty transcript returns empty string | none |
REQ-020-120 |
yes | IOError/OSError on file read returns empty list | none |
REQ-020-121 |
yes | stop_auditor_subagent.py detects SubagentStop events |
none |
REQ-020-122 |
yes | Parses subagent transcript for tool usage and text content | none |
REQ-020-123 |
yes | Runs same detect_deception() logic on subagent sessions |
none |
REQ-020-124 |
yes | Logs subagent findings to ~/.claude/audit/subagent_findings/ |
none |
REQ-020-125 |
yes | stop_audit/subagent.py re-exports from stop_auditor_subagent.py |
none |
REQ-020-126 |
yes | Findings logged with timestamp (epoch and ISO format) | none |
REQ-020-127 |
yes | Log entry includes session_id and findings_count | none |
REQ-020-128 |
yes | Log entry includes tools_used set and tool_calls_count | none |
REQ-020-129 |
yes | Log entry includes assistant_message_length and preview (500 chars) | none |
REQ-020-130 |
yes | Findings directory auto-created with parents=True, exist_ok=True |
none |
REQ-020-131 |
yes | intercept_bash.py fires on PreToolUse(Bash) |
none |
REQ-020-132 |
yes | rm command detected via regex \brm\s |
none |
REQ-020-133 |
yes | rm with flags detected via regex \brm\s+- |
none |
REQ-020-134 |
yes | unlink command detected via regex \bunlink\s |
none |
REQ-020-135 |
yes | rmdir command detected via regex \brmdir\s |
none |
REQ-020-136 |
yes | shred command detected via regex \bshred\s |
none |
REQ-020-137 |
yes | Redirect to ~/.claude/ detected (> and >> patterns) |
none |
REQ-020-138 |
yes | cat >, echo >, cp, mv, touch, mkdir to ~/.claude/ detected |
none |
REQ-020-139 |
yes | Full path patterns (/home/*/\.claude/) also detected |
none |
REQ-020-140 |
yes | Blocked commands logged with operation type "Bash-Delete-Blocked" | none |
REQ-020-141 |
yes | Claude-dir blocks logged with operation type "Bash-Claude-Dir-Blocked" | none |
REQ-020-142 |
yes | Allowed commands return {"decision": "allow"} |
none |
REQ-020-143 |
yes | Block response includes the original command for manual execution | none |
REQ-020-144 |
yes | intercept_write.py fires on PreToolUse(Write) |
none |
REQ-020-145 |
yes | Files that already exist are blocked with "use Edit tool" message | none |
REQ-020-146 |
yes | Writes to ~/.claude/plans/ blocked with redirect to project plans/ |
none |
REQ-020-147 |
yes | Writes to context.md blocked with session CLI instructions |
none |
REQ-020-148 |
yes | Writes to context.json blocked with session CLI instructions |
none |
REQ-020-149 |
yes | New file creation to valid paths returns {"decision": "allow"} |
none |
REQ-020-150 |
yes | Missing file_path returns block with error message | none |
REQ-020-151 |
yes | All operations (allowed and blocked) logged to file_operations.log |
none |
REQ-020-152 |
yes | Log entries include timestamp, operation, file_path, status, message | none |
REQ-020-153 |
yes | intercept_edit.py fires on PreToolUse(Edit) |
none |
REQ-020-154 |
yes | Git checkpoint created before edits | none |
REQ-020-155 |
yes | Edit operations logged to audit file | none |
REQ-020-156 |
yes | block_except_pass.py fires on PreToolUse(Write|Edit) |
none |
REQ-020-157 |
yes | Bare except: (no exception type) detected via AST |
none |
REQ-020-158 |
yes | except ...: pass (single-statement pass body) detected via AST |
none |
REQ-020-159 |
yes | Detection uses ast.parse() not regex |
none |
REQ-020-160 |
yes | Only .py files checked (non-Python files pass through) |
none |
REQ-020-161 |
yes | Violation message includes line number | none |
REQ-020-162 |
yes | Violation message suggests specific exception type with logging | none |
REQ-020-163 |
yes | For tuple exception types, all types listed in message | none |
REQ-020-164 |
yes | SyntaxError in content does not crash the hook (returns empty) | none |
REQ-020-165 |
yes | Non-Python files return empty JSON (allow) | none |
REQ-020-166 |
yes | block_file_tools.py fires on PreToolUse(Read|Grep) when active |
none |
REQ-020-167 |
yes | Queries Neo4j for file-scoped entities (classes, functions) | none |
REQ-020-168 |
yes | Queries Neo4j for specification traceability | none |
REQ-020-169 |
yes | Queries Neo4j for related nodes via full-text search | none |
REQ-020-170 |
yes | Results injected as additionalContext in hook output |
none |
REQ-020-171 |
yes | context.json is never trimmed or truncated | none |
REQ-020-172 |
yes | All sessions recorded in session_history array |
6 |
REQ-020-173 |
yes | state object tracks current session number, version, branch, test count, status |
3 |
REQ-020-174 |
yes | deferred_work array tracks pending tasks with priority and notes |
9 |
REQ-020-175 |
yes | Direct writes to context.json blocked by intercept_write.py | none |
REQ-020-176 |
yes | Direct writes to context.md blocked by intercept_write.py | none |
REQ-020-177 |
yes | sync_context_md.py fires on PostToolUse(Write|Edit) |
none |
REQ-020-178 |
yes | Hook only acts when modified file is context.json | none |
REQ-020-179 |
yes | Hook imports ContextManager from ~/.claude/tools/session/ |
none |
REQ-020-180 |
yes | Hook calls ContextManager.write() to regenerate context.md |
none |
REQ-020-181 |
yes | context.md shows only last 3 sessions (SESSION_LIMIT) | none |
REQ-020-182 |
yes | Regeneration failure logged but does not block | none |
REQ-020-183 |
yes | Non-context.json files result in empty JSON (pass through) | none |
REQ-020-184 |
yes | read_throttle.py fires on PreToolUse(Read|Grep) |
none |
REQ-020-185 |
yes | Read timestamps tracked in /tmp/claude-read-tracker.json |
none |
REQ-020-186 |
yes | Maximum 3 reads allowed per 30-second sliding window | none |
REQ-020-187 |
yes | Expired timestamps pruned on each check (window filter) | none |
REQ-020-188 |
yes | Reads of context.json exempted from throttle |
none |
REQ-020-189 |
yes | Reads of .env exempted from throttle |
none |
REQ-020-190 |
yes | Reads of settings.json exempted from throttle |
none |
REQ-020-191 |
yes | Reads of CLAUDE.md exempted from throttle |
none |
REQ-020-192 |
yes | Throttled reads blocked with {"decision": "block", "reason": "..."} |
none |
REQ-020-193 |
yes | Block message includes Neo4j query examples (/code commands) | none |
REQ-020-194 |
yes | Block message includes raw Cypher query example | none |
REQ-020-195 |
yes | Non-Read/Grep tools pass through with empty JSON | none |
REQ-020-196 |
yes | JSON parse error on stdin returns empty JSON (no crash) | none |
REQ-020-197 |
yes | File save failure logged to stderr (no crash) | none |
REQ-020-198 |
yes | posttool_auditor.py fires on PostToolUse(.*) |
none |
REQ-020-199 |
yes | Records tool_name, has_error, tool_output, tool_output_length | none |
REQ-020-200 |
yes | Records written to session JSONL in ~/.claude/audit/sessions/ |
none |
REQ-020-201 |
yes | log_operation.py fires on PostToolUse(Write|Edit|Bash|Read|Glob|Grep) |
none |
REQ-020-202 |
yes | Log entries include timestamp, operation, file_path/command, status, message | none |
REQ-020-203 |
yes | Logs written to ~/.claude/logs/file_operations.log as JSONL |
none |
REQ-020-204 |
yes | Active sessions stored in ~/.claude/audit/sessions/{session_id}.jsonl |
none |
REQ-020-205 |
yes | Completed sessions moved to ~/.claude/audit/archive/YYYY-MM-DD/ |
none |
REQ-020-206 |
yes | Archive filename includes session_id and epoch timestamp | none |
REQ-020-207 |
yes | Archive uses shutil.move() (move, not copy) to prevent duplicates |
none |
REQ-020-208 |
yes | Archive data NEVER deleted | none |
REQ-020-209 |
yes | Audit directories auto-created with parents=True, exist_ok=True |
none |
REQ-020-210 |
yes | Session close hook runs RTM generation scripts via subprocess | none |
REQ-020-211 |
yes | Scripts parse @traces(spec=, story=, reqs=[]) decorators from source |
none |
REQ-020-212 |
yes | RTM output committed to plans/ directory |
none |
REQ-020-213 |
yes | RTM generation failures logged but do not block session close | none |
REQ-020-214 |
yes | Generated RTM maps requirements to implementing functions/classes | none |
REQ-020-215 |
yes | remember_hook.py fires on UserPromptSubmit event |
none |
REQ-020-216 |
yes | Detects "remember when" trigger phrase | none |
REQ-020-217 |
yes | Detects "do you remember" trigger phrase | none |
REQ-020-218 |
yes | Detects "recall when" trigger phrase | none |
REQ-020-219 |
yes | Detects "remember that" trigger phrase | none |
REQ-020-220 |
yes | Detects "we discussed" trigger phrase | none |
REQ-020-221 |
yes | Detects "we talked about" trigger phrase | none |
REQ-020-222 |
yes | Detects "last time we" trigger phrase | none |
REQ-020-223 |
yes | Detects "what happened with" trigger phrase | none |
REQ-020-224 |
yes | Triggers compiled into single regex pattern (case-insensitive) | none |
REQ-020-225 |
yes | Topic extracted from text after trigger phrase | none |
REQ-020-226 |
yes | Falls back to whole message (minus trigger) if no text after trigger | none |
REQ-020-227 |
yes | Empty topic results in no search (empty JSON returned) | none |
REQ-020-228 |
yes | Query searches ConversationTurn.text with case-insensitive word matching | none |
REQ-020-229 |
yes | Only words with length >2 used as search terms | none |
REQ-020-230 |
yes | Query returns up to 15 results ordered by timestamp descending | none |
REQ-020-231 |
yes | Results include session_id, turn sequence, role, text (500 char limit), timestamp | none |
REQ-020-232 |
yes | Results injected as additionalContext with factual framing |
none |
REQ-020-233 |
yes | Query timeout set to 15 seconds | none |
REQ-020-234 |
yes | KG connection parameters loaded from the active env profile (KG_ROUTING_URI/KG_DATABASE layered over secrets-only .env); auth-less Memgraph — no credentials | none |
REQ-020-235 |
yes | Env files parsed for key=value pairs (strips quotes, skips comments); profile overlays .env with override=True | 4 |
REQ-020-236 |
yes | Missing Neo4j connection returns "(Search failed: ...)" not block | none |
REQ-020-237 |
yes | subprocess.TimeoutExpired handled gracefully | none |
REQ-020-238 |
yes | FileNotFoundError (missing uv) handled gracefully | none |
REQ-020-239 |
yes | Non-matching prompts return empty JSON (no search) | none |
REQ-020-240 |
yes | JSON parse error on stdin returns empty JSON (no crash) | none |
REQ-020-241 |
yes | claude/tools/notify sends a desktop notification via the system notifier |
none |
REQ-020-242 |
yes | Notification fires at CRITICAL urgency — normal urgency is suppressed by a focused fullscreen window (measured on KDE Plasma, 2026-08-29), so a lower level silently fails to deliver | none |
REQ-020-243 |
yes | The CLI is deployed to ~/.claude/tools/notify by deploy/install-claude-config.sh and mirrored at plugins/orchestrata/tools/notify |
none |
REQ-020-244 |
yes | claude/hooks/decision_adr_trigger.sh fires on UserPromptSubmit and detects any mention of a decision, ruling, or ADR |
none |
REQ-020-245 |
yes | On a match the hook injects the ADR discipline (ADR-0046 three homes, ADR-0047 two registers, ADR-0048 waiver rule) as additional context, so a decision cannot be acted on before the law that governs it is in view | none |
REQ-020-246 |
yes | The trigger exists at BOTH levels — user (claude/hooks/decision_adr_trigger.sh, delivered by the plugin) and project (.claude/hooks/decision-adr-trigger.sh, registered in .claude/settings.json) — because a session started outside the repo never reads the project file |
none |
Code that traces this spec
Checklists
checklists/README.md
Claude Code Harness Checklists
Project: Orchestrata Claude Code Harness (Spec 020) Component: Hook-Based Claude Code Wrapper Status: Implemented Owner: Mohamed
Overview
The Claude Code Harness is a comprehensive hook-based system that wraps Claude Code with session management, code quality enforcement, deception detection, safety guards, audit logging, and knowledge graph integration. It consists of 21 Python hook scripts firing on 5 Claude Code lifecycle events, a session management module with CLI, and a deployment pipeline via ./claude/install.sh.
Checklist Summary
| Checklist | Total Items | Completed | Progress |
|---|---|---|---|
| Requirements | 240 | 240 | 100% |
| Constitutional Compliance | 116 | 116 | 100% |
| Testing Plan | 190 | 0 | 0% |
| TOTAL | 546 | 356 | 65% |
Key Components
1. Session Management
- SessionAutoStart (SessionStart hook)
- SessionCloseHook (UserPromptSubmit hook)
- SessionOpenHook (UserPromptSubmit hook)
- SessionContextHook (SessionStart hook)
- Session CLI (
session_cli.py) - Context sync (
sync_context_md.py)
2. Error Gate (Stop hook)
- checks_syntax.py -- AST compilation check
- checks_lint.py -- Flake8 critical errors (E9xx, F8xx)
- checks_test.py -- Pytest on changed test files
- checks_structure.py -- Stubs, nesting, god objects
- utils.py -- Shared subprocess, git diff, AST helpers
3. Stop Audit (Stop hook)
- deception.py -- Pattern-based deception detection
- transcript.py -- JSONL transcript parsing
- subagent.py -- Subagent session handling
4. Safety Hooks (PreToolUse)
- intercept_bash.py -- Block destructive commands
- intercept_write.py -- Block overwrites, plan writes, context edits
- intercept_edit.py -- Git checkpoints
- block_except_pass.py -- Block bare except/pass patterns
5. Neo4j Integration
- read_throttle.py -- 3 reads/30s limit, KG redirect
- remember_hook.py -- Memory search on trigger phrases
- block_file_tools.py -- KG context injection on reads
6. Audit Trail
- posttool_auditor.py -- Tool call accumulation
- log_operation.py -- Operation logging
- enforcement_hook.py -- Central rule enforcement
7. Deployment
- install.sh -- Deploy to ~/.claude/
- strip_traces.py -- Remove @traces decorators
- settings.json -- Hook registration
Hook Event Matrix
| Hook Script | Event | Matcher | Action |
|---|---|---|---|
| session_auto_start.py | SessionStart | * | Create session entry |
| session_context_hook.py | SessionStart | * | Inject session context |
| session_close_hook.py | UserPromptSubmit | * | Close session on "cts" |
| session_open_hook.py | UserPromptSubmit | * | Open session on "sns" |
| remember_hook.py | UserPromptSubmit | * | Neo4j search on triggers |
| intercept_write.py | PreToolUse | Write | Block overwrites |
| intercept_edit.py | PreToolUse | Edit | Git checkpoint |
| intercept_bash.py | PreToolUse | Bash | Block destructive ops |
| block_except_pass.py | PreToolUse | Write/Edit | Block bare except |
| read_throttle.py | PreToolUse | Read/Grep | Throttle reads |
| enforcement_hook.py | PreToolUse | .* | Central enforcement |
| log_operation.py | PostToolUse | Write/Edit/Bash/Read/Glob/Grep | Log operations |
| posttool_auditor.py | PostToolUse | .* | Accumulate tool calls |
| sync_context_md.py | PostToolUse | Write/Edit | Sync context.md |
| error_gate_hook.py | Stop | * | Block on errors |
| stop_auditor.py | Stop | * | Deception detection |
Checklist Files
1. requirements.md
210 functional requirements organized by user stories:
- US-001: Session Lifecycle (P0)
- US-002: Code Quality Enforcement (P0)
- US-003: Deception Detection (P1)
- US-004: Safety Guards (P0)
- US-005: Neo4j Context Injection (P1)
- US-006: Session Context Persistence (P0)
- US-007: Read Throttling (P2)
- US-008: Audit Trail (P1)
- US-009: RTM Generation (P1)
- US-010: Remember/Search (P2)
2. constitutional.md
115 constitutional compliance items covering all 11 articles:
- Article I: Agent Autonomy
- Article II: Prolog as Single Source of Truth
- Article III: Session Isolation
- Article IV: Transparency and Logging
- Article V: Non-Deception
- Article VI: Quality Gates
- Article VII: Testing Without Mocks
- Article VIII: Metrics and Observability
- Article IX: Graceful Degradation
- Article X: Version Compatibility
- Article XI: Documentation First
3. testing.md
190 test cases across 3 categories:
- Unit Tests (TEST-UNIT-NNN): Individual hook function testing
- Integration Tests (TEST-INTEG-NNN): Multi-hook workflow testing
- Compliance Tests (TEST-COMPLIANCE-NNN): Constitutional enforcement testing
Dependencies
Required
- Claude Code CLI (>=1.0)
- Python 3.14t (free-threaded)
~/.claude/settings.json(hook registration)
Optional
- Neo4j 5.x (knowledge graph integration, remember hook)
- Redis 7.x (session state caching)
- SWI-Prolog 9.0.4 (governance enforcement)
- flake8 (lint checks in error gate)
- pytest (test checks in error gate)
References
- SPEC-020: Claude Code Harness
- SPEC-006: Session Management
- SPEC-008: Knowledge Graph
- claude/README.md
Last Updated: 2026-03-29 Version: 1.0.0 Maintained By: Mohamed
checklists/constitutional.md
Claude Code Harness - Constitutional Compliance
Component: Hook-Based Claude Code Wrapper Spec: 020-claude-code-harness Status: Compliance Review Total Compliance Items: 115
Overview
This document tracks compliance of the Claude Code Harness with all 11 articles of the Orchestrata Constitution. The harness is the primary enforcement layer between the human developer and Claude Code, making constitutional compliance critical -- it must faithfully enforce quality gates, detect deception, maintain audit trails, and never bypass governance or session isolation.
Article I: Agent Autonomy
Principle: Volks retain autonomy; the harness enforces boundaries without overriding agent decisions.
Compliance Requirements
Ownership Boundaries
- [x] CONST-I-001:
intercept_write.pyprevents overwriting files owned by other components (blocks Write to existing files) - [x] CONST-I-002:
intercept_bash.pyblocks writes to~/.claude/directory (system-owned) - [x] CONST-I-003:
intercept_write.pyblocks writes to~/.claude/plans/(redirects to projectplans/) - [x] CONST-I-004: Error gate excludes generated directories (spike/, iterations/, ci/) from enforcement
- [x] CONST-I-005: Read throttle exempts critical system files (context.json, settings.json, .env, CLAUDE.md)
- [x] CONST-I-006: Hooks do not modify tool output -- they block or allow, never alter content
- [x] CONST-I-007: Session auto-start only runs for Orchestrata project (path check)
- [x] CONST-I-008: Error gate checks source code only, not test files (for stub/god object detection)
- [x] CONST-I-009: Hooks return
{}(allow) by default -- blocking is the exception - [x] CONST-I-010: Hook decisions are logged for transparency, not hidden
Priority: Critical Verification: Code review + integration tests
Article II: Prolog as Single Source of Truth
Principle: All governance rules are in Prolog; the harness does not duplicate or override governance logic.
Compliance Requirements
Governance Separation
- [x] CONST-II-001: Error gate checks are structural code quality rules, not policy decisions
- [x] CONST-II-002: Harness does not evaluate Prolog predicates directly (delegates to governance engine)
- [x] CONST-II-003: Read throttle redirects to knowledge graph which respects Prolog-backed policies
- [x] CONST-II-004: Block_except_pass enforces Python language best practices, not project governance
- [x] CONST-II-005: Session close generates RTM from @traces decorators, not from Prolog rules
- [x] CONST-II-006: Deception detection uses pattern matching on narrative, not Prolog fact assertion
- [x] CONST-II-007: Hook decisions do not override Prolog governance verdicts
- [x] CONST-II-008: Safety hooks enforce filesystem boundaries, not semantic ownership policies
- [x] CONST-II-009: Error gate thresholds (300 lines, 15 methods, 4 nesting) are configurable constants, not Prolog rules
- [x] CONST-II-010: Session CLI interacts with context.json, not Prolog session facts
Priority: Critical Verification: Architecture review
Article III: Session Isolation
Principle: Sessions are isolated; the harness does not leak state between sessions.
Compliance Requirements
State Isolation
- [x] CONST-III-001: Each session gets a unique entry in context.json session_history
- [x] CONST-III-002: Session auto-start creates a new session entry, never modifies previous sessions
- [x] CONST-III-003: Session close creates an annotated git tag marking the session boundary
- [x] CONST-III-004: Audit trail attributes findings to specific session_id
- [x] CONST-III-005: Session JSONL files are per-session (
{session_id}.jsonl) - [x] CONST-III-006: Session archive moves to dated directory, preserving session identity
- [x] CONST-III-007: Deception findings include session_id in every log entry
- [x] CONST-III-008: Read throttle state (
/tmp/claude-read-tracker.json) resets between sessions (temp file) - [x] CONST-III-009: Session context hook injects only current session state, not cross-session data
- [x] CONST-III-010: context.md renders only last 3 sessions (SESSION_LIMIT)
- [x] CONST-III-011: Deferred work items tracked per session in context.json
- [x] CONST-III-012: Session open hook displays deferred work scoped to active session
Priority: Critical Verification: Integration tests + session lifecycle tests
Article IV: Transparency and Logging
Principle: All operations are logged; the audit trail provides complete visibility.
Compliance Requirements
Operation Logging
- [x] CONST-IV-001:
log_operation.pylogs every Write/Edit/Bash/Read/Glob/Grep operation - [x] CONST-IV-002:
posttool_auditor.pyaccumulates every tool call in session JSONL - [x] CONST-IV-003:
intercept_bash.pylogs blocked destructive commands with reason - [x] CONST-IV-004:
intercept_write.pylogs both allowed and blocked write operations - [x] CONST-IV-005: Deception findings logged with full context (tools_used, message_preview)
- [x] CONST-IV-006: Session close hook logs RTM generation results
- [x] CONST-IV-007: Error gate prints error report to stderr for operator visibility
- [x] CONST-IV-008: Hook diagnostic output goes to stderr (not stdout) to avoid corrupting JSON
- [x] CONST-IV-009: All log entries include ISO timestamps
- [x] CONST-IV-010: Audit archive NEVER deleted (append-only, move-only)
Priority: Critical Verification: Audit log review + integration tests
Article V: Non-Deception
Principle: Agents must not fabricate actions or conceal errors; the harness enforces truthfulness.
Compliance Requirements
Deception Detection
- [x] CONST-V-001: Stop auditor compares assistant narrative against actual tool usage at every session end
- [x] CONST-V-002: File creation claims without Write/Edit tool flagged as CRITICAL
- [x] CONST-V-003: Command execution claims without Bash tool flagged as CRITICAL
- [x] CONST-V-004: File reading claims without Read/Grep tool flagged as HIGH
- [x] CONST-V-005: Tool errors not reported to user flagged as CRITICAL (error concealment)
- [x] CONST-V-006: Many operations with tiny response flagged as MEDIUM (selective disclosure)
- [x] CONST-V-007: Large tool output with no mention flagged as MEDIUM (result hiding)
- [x] CONST-V-008: Code blocks and quotes stripped before analysis (prevents false positives)
- [x] CONST-V-009: Deception findings include evidence field for verification
- [x] CONST-V-010: Findings persisted to JSONL for offline review (never lost)
- [x] CONST-V-011: Subagent sessions audited with same rigor as primary sessions
- [x] CONST-V-012: Deception report displayed in chat so user sees violations
Priority: Critical Verification: Deception detection tests + manual audit review
Article VI: Quality Gates
Principle: Code quality enforced at session boundaries; no session ends with broken code.
Compliance Requirements
Error Gate Enforcement
- [x] CONST-VI-001: Error gate blocks session stop when syntax errors exist in modified files
- [x] CONST-VI-002: Error gate blocks session stop when critical lint errors exist (E9, F82-84)
- [x] CONST-VI-003: Error gate blocks session stop when changed tests fail
- [x] CONST-VI-004: Error gate blocks session stop when stub methods detected
- [x] CONST-VI-005: Error gate blocks session stop when nesting exceeds 4 levels
- [x] CONST-VI-006: Error gate blocks session stop when god objects detected
- [x] CONST-VI-007: Error report includes specific fix instructions
- [x] CONST-VI-008:
block_except_pass.pyprevents bare except patterns from entering codebase - [x] CONST-VI-009: Write hook forces Edit tool for modifications (preserves file history)
- [x] CONST-VI-010: All quality checks use real tools (ast, flake8, pytest) not approximations
Priority: Critical Verification: Error gate unit tests + integration tests
Article VII: Testing Without Mocks
Principle: Tests use real implementations; no mocking infrastructure.
Compliance Requirements
Real Testing
- [x] CONST-VII-001: Error gate test runner uses real pytest, not mock test framework
- [x] CONST-VII-002: Syntax checking uses real ast.parse(), not mock parser
- [x] CONST-VII-003: Lint checking uses real flake8, not mock linter
- [x] CONST-VII-004: Deception detection uses real regex patterns, not mock matchers
- [x] CONST-VII-005: Read throttle uses real file I/O for timestamp tracking
- [x] CONST-VII-006: Remember hook executes real Neo4j queries
- [x] CONST-VII-007: Session module uses real file I/O for context.json
- [x] CONST-VII-008: Block_except_pass uses real ast module for code analysis
- [x] CONST-VII-009: Harness hook tests (when written) SHALL use real tool invocations
- [x] CONST-VII-010: No unittest.mock, MagicMock, patch, or AsyncMock in harness code
Priority: High Verification: Code search for mock imports
Article VIII: Metrics and Observability
Principle: System state is observable; metrics are collected and accessible.
Compliance Requirements
Metrics Collection
- [x] CONST-VIII-001: Session history in context.json tracks development velocity (sessions, lengths)
- [x] CONST-VIII-002: Deception log provides metrics on AI trustworthiness over time
- [x] CONST-VIII-003: Error gate results show code quality trends across sessions
- [x] CONST-VIII-004: Read throttle tracks read frequency (observable via temp file)
- [x] CONST-VIII-005: Audit logs provide tool usage frequency metrics
- [x] CONST-VIII-006: Session close reports achievements (observable in CHANGELOG.md)
- [x] CONST-VIII-007: context.json state object tracks test count, version, branch
- [x] CONST-VIII-008: file_operations.log provides file change frequency metrics
- [x] CONST-VIII-009: Deception findings include severity distribution (CRITICAL/HIGH/MEDIUM/LOW)
- [x] CONST-VIII-010: Session tags provide timeline markers for development history
Priority: High Verification: Metrics review + dashboard integration
Article IX: Graceful Degradation
Principle: System continues operating when components fail; failures are contained.
Compliance Requirements
Failure Handling
- [x] CONST-IX-001: All hooks return
{}on JSON parse error (no crash) - [x] CONST-IX-002: Missing flake8 treated as skip, not error (FileNotFoundError returns exit 0)
- [x] CONST-IX-003: Missing pytest treated as skip, not error
- [x] CONST-IX-004: Missing Neo4j connection returns "(Search failed: ...)" not block
- [x] CONST-IX-005: Corrupt JSONL lines skipped with warning to stderr
- [x] CONST-IX-006: Missing audit directories auto-created on first write
- [x] CONST-IX-007: context.md sync failure logged but does not block tool use
- [x] CONST-IX-008: RTM generation failure logged but does not block session close
- [x] CONST-IX-009: Session module ImportError prints message, does not crash
- [x] CONST-IX-010: Read throttle file save failure logged to stderr, does not crash
- [x] CONST-IX-011: pyswip SIGSEGV (exit 139) treated as infrastructure issue, not test failure
- [x] CONST-IX-012: Non-Python files pass through block_except_pass without error
Priority: High Verification: Failure scenario tests
Article X: Version Compatibility
Principle: System handles version transitions without data loss.
Compliance Requirements
Version Management
- [x] CONST-X-001: install.sh overwrites deployed files (clean upgrade)
- [x] CONST-X-002: strip_traces.py decouples deployed code from monorepo version
- [x] CONST-X-003: Monorepo-specific imports removed from deployed init.py
- [x] CONST-X-004: context.json schema is forward-compatible (new fields don't break old readers)
- [x] CONST-X-005: Hook registration in settings.json is idempotent (safe to re-run)
- [x] CONST-X-006: Audit JSONL format is append-only (new fields don't break old entries)
- [x] CONST-X-007: Session module deployed independently of monorepo package versions
Priority: High Verification: Upgrade scenario tests
Article XI: Documentation First
Principle: Specifications precede implementation; documentation is comprehensive and current.
Compliance Requirements
Documentation Coverage
- [x] CONST-XI-001: SPEC-020 documents all 21 hook scripts with purpose and event type
- [x] CONST-XI-002: Every hook script has a module-level docstring
- [x] CONST-XI-003: Every public function has Google-style docstring with Args and Returns
- [x] CONST-XI-004: Hook event flow documented as ASCII architecture diagram
- [x] CONST-XI-005: Deployment architecture documented with source-to-target mapping
- [x] CONST-XI-006: Settings registration documented with JSON structure
- [x] CONST-XI-007: Error gate check categories documented individually
- [x] CONST-XI-008: Deception detection categories documented with severity table
- [x] CONST-XI-009: JSONL formats documented with example entries
- [x] CONST-XI-010: Audit trail storage layout documented
- [x] CONST-XI-011: Session close sequence documented step by step
- [x] CONST-XI-012: CLAUDE.md includes harness documentation for every conversation
- [x] CONST-XI-013: claude/README.md documents hook deployment process
Priority: High Verification: Documentation review
Summary
| Article | Compliance Items | Completed | Status |
|---|---|---|---|
| I: Agent Autonomy | 10 | 10 | Complete |
| II: Prolog as Source of Truth | 10 | 10 | Complete |
| III: Session Isolation | 12 | 12 | Complete |
| IV: Transparency and Logging | 10 | 10 | Complete |
| V: Non-Deception | 12 | 12 | Complete |
| VI: Quality Gates | 10 | 10 | Complete |
| VII: Testing Without Mocks | 10 | 10 | Complete |
| VIII: Metrics and Observability | 10 | 10 | Complete |
| IX: Graceful Degradation | 12 | 12 | Complete |
| X: Version Compatibility | 7 | 7 | Complete |
| XI: Documentation First | 13 | 13 | Complete |
| TOTAL | 116 | 116 | Complete |
Last Updated: 2026-03-29 Version: 1.0.0 Maintained By: Mohamed
checklists/requirements.md
The requirement list above comes from this file; 246 of 246 checkbox items are ticked.
checklists/testing.md
Claude Code Harness - Testing Plan
Component: Hook-Based Claude Code Wrapper Spec: 020-claude-code-harness Status: Test Planning Phase Total Test Cases: 190
Overview
This document defines the comprehensive testing strategy for the Claude Code Harness. All tests follow the strict "no mocking" policy and use real implementations: real AST parsing, real flake8 invocations, real file I/O, real subprocess execution, and real JSON serialization. Tests verify hook behavior by invoking hooks with crafted JSON stdin and checking stdout/stderr responses.
Test Categories Summary
| Category | Test Count | Purpose | Priority |
|---|---|---|---|
| Unit Tests | 120 | Individual hook function testing | Critical |
| Integration Tests | 45 | Multi-hook workflow testing | High |
| Compliance Tests | 25 | Constitutional enforcement testing | High |
| TOTAL | 190 |
Test Infrastructure
Hook Test Harness
All hook tests use a common pattern:
- Prepare a JSON dict matching the hook's expected stdin format
- Invoke the hook script via subprocess with JSON piped to stdin
- Parse stdout JSON response
- Assert on
decision,reason,additionalContextfields - Check stderr for diagnostic output
def invoke_hook(hook_path: str, hook_input: dict) -> dict:
result = subprocess.run(
[sys.executable, hook_path],
input=json.dumps(hook_input),
capture_output=True,
text=True,
timeout=30,
)
return json.loads(result.stdout)
Test File Fixtures
Tests create temporary Python files with known content (syntax errors, bare exceptions, stubs, deep nesting) in a temporary git repository, then invoke hooks against them.
Unit Tests (120)
Error Gate - Syntax Checks (10 tests)
- [ ] TEST-UNIT-001:
check_syntax_errorsreturns empty list for valid Python file - [ ] TEST-UNIT-002:
check_syntax_errorsdetectsSyntaxErrorin modified .py file - [ ] TEST-UNIT-003:
check_syntax_errorsskips non-.py files in git diff - [ ] TEST-UNIT-004:
check_syntax_errorsskips files that no longer exist on disk - [ ] TEST-UNIT-005:
check_syntax_errorsreturns empty list when git diff fails - [ ] TEST-UNIT-006:
check_syntax_errorstruncates error message to 200 chars - [ ] TEST-UNIT-007:
check_syntax_errorshandles multiple modified files - [ ] TEST-UNIT-008:
check_syntax_errorshandles file with encoding errors - [ ] TEST-UNIT-009:
check_syntax_errorshandles empty modified file - [ ] TEST-UNIT-010:
check_syntax_errorsuses subprocess timeout (30s)
Error Gate - Lint Checks (10 tests)
- [ ] TEST-UNIT-011:
check_lint_errorsreturns empty list for clean code - [ ] TEST-UNIT-012:
check_lint_errorsdetects E9xx errors (syntax via flake8) - [ ] TEST-UNIT-013:
check_lint_errorsdetects F821 (undefined name) - [ ] TEST-UNIT-014:
check_lint_errorsdetects F841 (local variable assigned but unused) - [ ] TEST-UNIT-015:
check_lint_errorscaps file list at 30 - [ ] TEST-UNIT-016:
check_lint_errorsreports up to 10 errors - [ ] TEST-UNIT-017:
check_lint_errorsreturns empty when no .py files modified - [ ] TEST-UNIT-018:
check_lint_errorsuses --max-line-length=120 - [ ] TEST-UNIT-019:
check_lint_errorsfalls back to ~/.local/bin/uv if shutil.which fails - [ ] TEST-UNIT-020:
check_lint_errorsreturns empty when git diff fails
Error Gate - Test Checks (12 tests)
- [ ] TEST-UNIT-021:
check_test_failuresreturns empty for passing tests - [ ] TEST-UNIT-022:
check_test_failuresdetects FAILED test line - [ ] TEST-UNIT-023:
check_test_failuresexcludes files with "prolog" in name - [ ] TEST-UNIT-024:
check_test_failuresexcludes files importing pyswip - [ ] TEST-UNIT-025:
check_test_failuresexcludes files importing GovernanceEngine - [ ] TEST-UNIT-026:
check_test_failuresexcludes all tests if conftest uses pyswip - [ ] TEST-UNIT-027:
check_test_failurescaps test files at 10 - [ ] TEST-UNIT-028:
check_test_failurestreats exit 139 as infrastructure issue - [ ] TEST-UNIT-029:
check_test_failuresuses 300s timeout - [ ] TEST-UNIT-030:
check_test_failuresonly selects files under tests/ - [ ] TEST-UNIT-031:
check_test_failuresreturns empty when no test files changed - [ ] TEST-UNIT-032:
check_test_failureshandles git diff failure gracefully
Error Gate - Stub Detection (15 tests)
- [ ] TEST-UNIT-033:
check_stub_methodsreturns empty for functions with real work - [ ] TEST-UNIT-034:
check_stub_methodsdetects function with onlylogger.info()call - [ ] TEST-UNIT-035:
check_stub_methodsdetects function with onlypass - [ ] TEST-UNIT-036:
check_stub_methodsdetects function with only local assignment - [ ] TEST-UNIT-037:
check_stub_methodsdetects function with only docstring + log - [ ] TEST-UNIT-038:
check_stub_methodsexcludes__init__from detection - [ ] TEST-UNIT-039:
check_stub_methodsexcludes__exit__from detection - [ ] TEST-UNIT-040:
check_stub_methodsexcludes private methods (starting with_) - [ ] TEST-UNIT-041:
check_stub_methodsdoes NOT flagawaitexpressions as stubs - [ ] TEST-UNIT-042:
check_stub_methodsdoes NOT flagself.x += 1as stub - [ ] TEST-UNIT-043:
check_stub_methodshandles function with.append()as stub - [ ] TEST-UNIT-044:
check_stub_methodstreatsreturn Noneas trivial return - [ ] TEST-UNIT-045:
check_stub_methodstreatsraisein first 2 lines as guard clause - [ ] TEST-UNIT-046:
check_stub_methodsexcludes test files - [ ] TEST-UNIT-047:
check_stub_methodsexcludes generated directories
Error Gate - Nesting Depth (8 tests)
- [ ] TEST-UNIT-048:
check_nesting_depthreturns empty for shallow functions - [ ] TEST-UNIT-049:
check_nesting_depthdetects 5-level if nesting - [ ] TEST-UNIT-050:
check_nesting_depthcounts if/for/while/with/try - [ ] TEST-UNIT-051:
check_nesting_depthcounts async for and async with - [ ] TEST-UNIT-052:
check_nesting_depthreports function name and line number - [ ] TEST-UNIT-053:
check_nesting_depthreports actual depth and max allowed - [ ] TEST-UNIT-054:
check_nesting_depthhandles multiple deeply nested functions - [ ] TEST-UNIT-055:
_measure_nestingreturns 0 for flat function body
Error Gate - God Objects (10 tests)
- [ ] TEST-UNIT-056:
check_god_objectsreturns empty for small classes and files - [ ] TEST-UNIT-057:
check_god_objectsdetects class >300 lines - [ ] TEST-UNIT-058:
check_god_objectsdetects class with >15 public methods - [ ] TEST-UNIT-059:
check_god_objectsdetects file >500 lines - [ ] TEST-UNIT-060:
check_god_objectsdoes not count private methods (starting with_) - [ ] TEST-UNIT-061:
check_god_objectsexcludes test files - [ ] TEST-UNIT-062:
check_god_objectsexcludes generated directories - [ ] TEST-UNIT-063:
_class_line_countuses end_lineno - lineno + 1 - [ ] TEST-UNIT-064:
_class_line_countreturns 0 if end_lineno missing - [ ] TEST-UNIT-065:
_count_public_methodscounts FunctionDef and AsyncFunctionDef
Error Gate - Utils (8 tests)
- [ ] TEST-UNIT-066:
run_commandreturns (0, output) for successful command - [ ] TEST-UNIT-067:
run_commandreturns (1, timeout message) on timeout - [ ] TEST-UNIT-068:
run_commandreturns (0, "") for FileNotFoundError (missing tool) - [ ] TEST-UNIT-069:
get_modified_py_filesreturns only .py files - [ ] TEST-UNIT-070:
get_modified_py_filesexcludes spike/, iterations/, ci/ - [ ] TEST-UNIT-071:
get_modified_py_filesoptionally excludes tests/ - [ ] TEST-UNIT-072:
parse_filereturns AST module for valid Python - [ ] TEST-UNIT-073:
parse_filereturns None for invalid Python
Deception Detection (15 tests)
- [ ] TEST-UNIT-074:
detect_deceptionreturns empty list when claims match tool usage - [ ] TEST-UNIT-075:
_check_file_creationflags "created the file" without Write - [ ] TEST-UNIT-076:
_check_file_creationallows "created the file" with Write in tools_used - [ ] TEST-UNIT-077:
_check_command_executionflags "ran the command" without Bash - [ ] TEST-UNIT-078:
_check_command_executionallows with Bash in tools_used - [ ] TEST-UNIT-079:
_check_file_readingflags "read the file" without Read - [ ] TEST-UNIT-080:
_check_file_readingallows with Read in tools_used - [ ] TEST-UNIT-081:
_check_error_concealmentflags tool error not mentioned in response - [ ] TEST-UNIT-082:
_check_error_concealmentallows when error terms present in response - [ ] TEST-UNIT-083:
_check_selective_disclosureflags >10 calls with <100 char response - [ ] TEST-UNIT-084:
_check_selective_disclosureallows reasonable response length - [ ] TEST-UNIT-085:
_check_result_hidingflags large output with no mention in response - [ ] TEST-UNIT-086:
_check_result_hidingallows when output words overlap with response - [ ] TEST-UNIT-087:
_remove_quoted_contentstrips triple-backtick code blocks - [ ] TEST-UNIT-088:
_remove_quoted_contentstrips inline code and blockquotes
Transcript Parsing (7 tests)
- [ ] TEST-UNIT-089:
extract_assistant_messagesextracts text from assistant role entries - [ ] TEST-UNIT-090:
extract_assistant_messageshandles string content - [ ] TEST-UNIT-091:
extract_assistant_messageshandles list content with typed blocks - [ ] TEST-UNIT-092:
extract_assistant_messagesskips non-assistant roles - [ ] TEST-UNIT-093:
extract_assistant_messagesreturns "" for missing transcript - [ ] TEST-UNIT-094:
_parse_jsonl_fileskips corrupt JSON lines - [ ] TEST-UNIT-095:
_parse_jsonl_filereturns [] for missing file
Read Throttle (10 tests)
- [ ] TEST-UNIT-096: Read throttle allows first 3 reads in 30s window
- [ ] TEST-UNIT-097: Read throttle blocks 4th read in 30s window
- [ ] TEST-UNIT-098: Read throttle allows reads after window expires
- [ ] TEST-UNIT-099: Read throttle exempts context.json
- [ ] TEST-UNIT-100: Read throttle exempts .env files
- [ ] TEST-UNIT-101: Read throttle exempts settings.json
- [ ] TEST-UNIT-102: Read throttle exempts CLAUDE.md
- [ ] TEST-UNIT-103: Read throttle passes through non-Read/Grep tools
- [ ] TEST-UNIT-104: Read throttle handles missing tracker file
- [ ] TEST-UNIT-105: Read throttle handles corrupt tracker JSON
Block Except Pass (8 tests)
- [ ] TEST-UNIT-106:
check_codereturns empty for proper exception handling - [ ] TEST-UNIT-107:
check_codedetects bareexcept: - [ ] TEST-UNIT-108:
check_codedetectsexcept ValueError: pass - [ ] TEST-UNIT-109:
check_codedetectsexcept (TypeError, KeyError): pass - [ ] TEST-UNIT-110:
check_codereports line numbers in violations - [ ] TEST-UNIT-111:
check_codereturns empty for SyntaxError in content - [ ] TEST-UNIT-112: Hook passes through non-.py files
- [ ] TEST-UNIT-113: Hook passes through non-Write/Edit tools
Intercept Bash (7 tests)
- [ ] TEST-UNIT-114:
is_destructive_commanddetectsrm file.txt - [ ] TEST-UNIT-115:
is_destructive_commanddetectsrm -rf /path - [ ] TEST-UNIT-116:
is_destructive_commanddetectsunlink file - [ ] TEST-UNIT-117:
is_destructive_commanddetectsrmdir dir - [ ] TEST-UNIT-118:
is_destructive_commanddetectsshred file - [ ] TEST-UNIT-119:
is_claude_dir_writedetectsecho > ~/.claude/file - [ ] TEST-UNIT-120:
is_claude_dir_writedetectscp file ~/.claude/
Integration Tests (45)
Session Lifecycle (10 tests)
- [ ] TEST-INTEG-001: Session auto-start creates entry in context.json on first run
- [ ] TEST-INTEG-002: Session auto-start skips creation when active session exists
- [ ] TEST-INTEG-003: Session auto-start skips creation when last session <300s old
- [ ] TEST-INTEG-004: Session close hook detects "cts" and initiates close sequence
- [ ] TEST-INTEG-005: Session close hook creates annotated git tag
- [ ] TEST-INTEG-006: Session close hook updates CHANGELOG.md
- [ ] TEST-INTEG-007: Session close hook pushes tag to origin
- [ ] TEST-INTEG-008: Session open hook detects "sns" and injects context
- [ ] TEST-INTEG-009: Session context hook injects session state at startup
- [ ] TEST-INTEG-010: Full lifecycle: auto-start -> work -> close -> verify state
Error Gate Pipeline (10 tests)
- [ ] TEST-INTEG-011: Error gate blocks stop when syntax error exists in modified file
- [ ] TEST-INTEG-012: Error gate blocks stop when lint error exists in modified file
- [ ] TEST-INTEG-013: Error gate blocks stop when test failure detected
- [ ] TEST-INTEG-014: Error gate blocks stop when stub method detected
- [ ] TEST-INTEG-015: Error gate blocks stop when nesting >4 detected
- [ ] TEST-INTEG-016: Error gate blocks stop when god object detected
- [ ] TEST-INTEG-017: Error gate allows stop when all checks pass
- [ ] TEST-INTEG-018: Error gate runs all 6 checks and combines errors
- [ ] TEST-INTEG-019: Error gate excludes spike/ and iterations/ from checks
- [ ] TEST-INTEG-020: Error gate output includes fix instructions
Safety Hook Pipeline (10 tests)
- [ ] TEST-INTEG-021: Write hook blocks overwrite of existing file
- [ ] TEST-INTEG-022: Write hook allows creation of new file
- [ ] TEST-INTEG-023: Write hook blocks write to context.json
- [ ] TEST-INTEG-024: Write hook blocks write to ~/.claude/plans/
- [ ] TEST-INTEG-025: Bash hook blocks
rmcommand - [ ] TEST-INTEG-026: Bash hook blocks write to ~/.claude/ directory
- [ ] TEST-INTEG-027: Bash hook allows safe commands (ls, git, etc.)
- [ ] TEST-INTEG-028: Block_except_pass blocks bare except in Write
- [ ] TEST-INTEG-029: Block_except_pass blocks except pass in Edit
- [ ] TEST-INTEG-030: Block_except_pass allows proper exception handling
Deception Audit Pipeline (8 tests)
- [ ] TEST-INTEG-031: Stop auditor detects file creation claim without Write tool
- [ ] TEST-INTEG-032: Stop auditor detects command claim without Bash tool
- [ ] TEST-INTEG-033: Stop auditor logs findings to deception_log.jsonl
- [ ] TEST-INTEG-034: Stop auditor archives session JSONL to dated directory
- [ ] TEST-INTEG-035: Stop auditor handles SubagentStop events
- [ ] TEST-INTEG-036: Stop auditor produces clean report for honest session
- [ ] TEST-INTEG-037: Stop auditor formats multi-finding report with severity icons
- [ ] TEST-INTEG-038: Stop auditor handles missing transcript gracefully
Context Sync Pipeline (7 tests)
- [ ] TEST-INTEG-039: sync_context_md regenerates context.md when context.json modified
- [ ] TEST-INTEG-040: sync_context_md passes through when non-context.json modified
- [ ] TEST-INTEG-041: sync_context_md handles ContextManager import failure gracefully
- [ ] TEST-INTEG-042: Read throttle blocks after 3 reads and provides Neo4j redirect
- [ ] TEST-INTEG-043: Read throttle resets after 30-second window expires
- [ ] TEST-INTEG-044: Remember hook searches Neo4j on "do you remember" trigger
- [ ] TEST-INTEG-045: Remember hook passes through non-trigger prompts
Compliance Tests (25)
Constitutional Article Verification
- [ ] TEST-COMPLIANCE-001: Hooks never modify tool output content (Article I)
- [ ] TEST-COMPLIANCE-002: Hooks return
{}by default, blocking is exception (Article I) - [ ] TEST-COMPLIANCE-003: Error gate checks are structural, not governance (Article II)
- [ ] TEST-COMPLIANCE-004: Each session gets unique context.json entry (Article III)
- [ ] TEST-COMPLIANCE-005: Audit trail attributes findings to specific session_id (Article III)
- [ ] TEST-COMPLIANCE-006: All Write/Edit/Bash operations logged (Article IV)
- [ ] TEST-COMPLIANCE-007: All tool calls accumulated in session JSONL (Article IV)
- [ ] TEST-COMPLIANCE-008: Deception detection compares claims vs tool usage (Article V)
- [ ] TEST-COMPLIANCE-009: Error gate blocks stop on syntax errors (Article VI)
- [ ] TEST-COMPLIANCE-010: Error gate blocks stop on stub methods (Article VI)
- [ ] TEST-COMPLIANCE-011: No mock imports in any harness hook file (Article VII)
- [ ] TEST-COMPLIANCE-012: JSON parse error returns
{}not crash (Article IX) - [ ] TEST-COMPLIANCE-013: Missing flake8 treated as skip (Article IX)
- [ ] TEST-COMPLIANCE-014: Missing Neo4j returns graceful failure (Article IX)
- [ ] TEST-COMPLIANCE-015: install.sh idempotent registration (Article X)
Data Integrity Verification
- [ ] TEST-COMPLIANCE-016: context.json never truncated by sync_context_md
- [ ] TEST-COMPLIANCE-017: Audit archive files never deleted by any hook
- [ ] TEST-COMPLIANCE-018: Deception log is append-only
- [ ] TEST-COMPLIANCE-019: Session JSONL moved (not copied) to archive
- [ ] TEST-COMPLIANCE-020: Git tags are annotated and pushed
Security Verification
- [ ] TEST-COMPLIANCE-021: No hardcoded passwords in any hook file
- [ ] TEST-COMPLIANCE-022: No eval() or exec() with dynamic input in any hook
- [ ] TEST-COMPLIANCE-023: KG connection parameters loaded from the active env profile (no hardcoded endpoints; auth-less Memgraph)
- [ ] TEST-COMPLIANCE-024: File paths validated before I/O operations
- [ ] TEST-COMPLIANCE-025: No shell injection via string concatenation in subprocess calls
Test Execution
Running Tests
# Run all harness tests (when implemented)
ci/local-ci.sh test -- -k "test_harness or test_hook or test_error_gate"
# Run specific hook test
uv run pytest tests/harness/test_error_gate.py -v
# Run deception detection tests
uv run pytest tests/harness/test_deception.py -v
# Run safety hook tests
uv run pytest tests/harness/test_safety_hooks.py -v
Test Directory Structure
tests/harness/
├── conftest.py # Shared fixtures (temp git repo, hook invoker)
├── test_error_gate_syntax.py # TEST-UNIT-001 through TEST-UNIT-010
├── test_error_gate_lint.py # TEST-UNIT-011 through TEST-UNIT-020
├── test_error_gate_tests.py # TEST-UNIT-021 through TEST-UNIT-032
├── test_error_gate_stubs.py # TEST-UNIT-033 through TEST-UNIT-047
├── test_error_gate_nesting.py # TEST-UNIT-048 through TEST-UNIT-055
├── test_error_gate_gods.py # TEST-UNIT-056 through TEST-UNIT-065
├── test_error_gate_utils.py # TEST-UNIT-066 through TEST-UNIT-073
├── test_deception.py # TEST-UNIT-074 through TEST-UNIT-088
├── test_transcript.py # TEST-UNIT-089 through TEST-UNIT-095
├── test_read_throttle.py # TEST-UNIT-096 through TEST-UNIT-105
├── test_block_except.py # TEST-UNIT-106 through TEST-UNIT-113
├── test_intercept_bash.py # TEST-UNIT-114 through TEST-UNIT-120
├── test_session_lifecycle.py # TEST-INTEG-001 through TEST-INTEG-010
├── test_error_gate_pipeline.py # TEST-INTEG-011 through TEST-INTEG-020
├── test_safety_pipeline.py # TEST-INTEG-021 through TEST-INTEG-030
├── test_deception_pipeline.py # TEST-INTEG-031 through TEST-INTEG-038
├── test_context_sync.py # TEST-INTEG-039 through TEST-INTEG-045
└── test_compliance.py # TEST-COMPLIANCE-001 through TEST-COMPLIANCE-025
Coverage Targets
| Component | Target | Critical Threshold |
|---|---|---|
| error_gate/ | 95% | 90% |
| stop_audit/ | 95% | 90% |
| read_throttle.py | 90% | 85% |
| block_except_pass.py | 95% | 90% |
| intercept_bash.py | 90% | 85% |
| intercept_write.py | 90% | 85% |
| session_auto_start.py | 85% | 80% |
| session_close_hook.py | 85% | 80% |
| remember_hook.py | 80% | 75% |
| sync_context_md.py | 85% | 80% |
| Overall | 90% | 85% |
Summary
| Category | Test Count | Completed | Progress |
|---|---|---|---|
| Unit Tests | 120 | 0 | 0% |
| Integration Tests | 45 | 0 | 0% |
| Compliance Tests | 25 | 0 | 0% |
| TOTAL | 190 | 0 | 0% |
Last Updated: 2026-03-29 Version: 1.0.0 Maintained By: Mohamed
Other tracked files
specs/020-claude-code-harness/compliance-audit.md
Full spec text
Verbatim from specs/020-claude-code-harness/spec.md; headings are pushed down two levels.
SPEC-020: Claude Code Harness
Metadata
- Spec ID: 020-claude-code-harness
- Title: Claude Code Harness
- Author: Mohamed
- Status: Implemented
- Created: 2026-03-29
- Last Updated: 2026-04-09
- Priority: P0
- Version: 1.1.0
- Extends: SPEC-006, SPEC-008
Overview
What
A comprehensive hook-based harness that wraps Claude Code with session management, code quality enforcement, deception detection, safety guards, audit logging, and knowledge graph integration. The harness consists of 21 Python hook scripts that fire on Claude Code lifecycle events (SessionStart, PreToolUse, PostToolUse, UserPromptSubmit, Stop), a session management module with CLI, and a deployment pipeline. All hooks are version-controlled in claude/ and deployed to ~/.claude/hooks/ via ./claude/install.sh.
Why
- Claude Code has no built-in session tracking or context persistence across conversations
- No enforcement of project-specific code quality rules (no mocking, absolute imports, max function length, no bare except)
- No protection against destructive operations (rm, unlink, shred, overwriting existing files)
- No integration with Neo4j knowledge graph for contextual code understanding
- No audit trail of tool usage, deception detection, or transparency enforcement
- Session lifecycle (start, close, RTM generation, git tagging, changelog updates) requires manual orchestration
- No throttling mechanism to encourage knowledge-graph-first code exploration
- No structural quality gates (stub detection, nesting depth, god object prevention)
How
- 21 Python hook scripts fire on 5 Claude Code lifecycle events
- Hooks deployed to
~/.claude/hooks/viainstall.sh, registered in~/.claude/settings.json - Session module (
~/.claude/tools/session/) provides begin/end/log-work/log-achievement operations session_cli.pyexposes session operations as a CLI callable from Bash tool- Error gate package (
error_gate/) runs 6 check categories at session stop, blocking if errors exist - Stop audit package (
stop_audit/) performs deception analysis comparing claims vs actual tool usage - Read throttle limits file reads to 3 per 30-second window, redirecting to Neo4j knowledge graph queries
- Safety hooks block destructive bash commands, bare except patterns, and overwriting existing files
context.jsonserves as the single source of truth for session state, never trimmedcontext.mdis a rendered view (last 3 sessions), regenerated automatically via PostToolUse hook- Audit trail persisted to JSONL files under
~/.claude/audit/with archive rotation (never deleted) strip_traces.pyremoves@tracesdecorators from deployed code to eliminate monorepo dependencies
User Scenarios
US-001: Session Lifecycle (P0)
As a developer using Claude Code for Orchestrata development I want sessions to automatically start, persist state, and cleanly close with RTM generation and git tags So that every development session is tracked, auditable, and recoverable
Acceptance Criteria:
- Session auto-starts on Claude Code launch (SessionStart event)
- Duplicate sessions are not created on context compaction
- "cts" or "close the session" triggers full close sequence
- RTM is generated from
@tracesdecorators and committed toplans/ - Achievements are extracted from git log since session start
- Annotated git tag
session-NNN-YYYY-MM-DD-HHMMis created - CHANGELOG.md is updated with categorized achievements
- Tag and branch are pushed to origin
- Session state persisted in context.json with seq, when, purpose, achievements, length
- "sns" or "start new session" triggers session open with deferred work display
US-002: Code Quality Enforcement (P0)
As a developer I want Claude Code to be blocked from ending a session when modified code has syntax errors, lint failures, test failures, stubs, deep nesting, or god objects So that every session ends with clean, production-quality code
Acceptance Criteria:
- Error gate runs at Stop event, checking modified files only
- Syntax errors detected via
ast.parse()on all modified.pyfiles - Critical lint errors detected via
flake8 --select=E9,F82,F83,F84 - Changed test files are run (non-prolog only, capped at 10)
- Stub methods detected (logging/pass/local-assign only bodies)
- Nesting depth >4 levels flagged
- God objects flagged (class >300 lines, >15 methods, file >500 lines)
- Stop is blocked with detailed error report and fix instructions
- Generated directories (spike/, iterations/, ci/) excluded from checks
- Test files excluded from stub and god object checks
US-003: Deception Detection (P1)
As a project owner I want automatic comparison of what Claude claims to have done vs what tools were actually invoked So that fabricated claims are detected and logged for review
Acceptance Criteria:
- Stop auditor runs at every session end
- Assistant narrative text extracted from JSONL transcript
- Code blocks and quotes removed before pattern matching
- File creation claims flagged if Write/Edit not used (CRITICAL)
- Command execution claims flagged if Bash not used (CRITICAL)
- File reading claims flagged if Read/Grep not used (HIGH)
- Tool errors not mentioned in response flagged (CRITICAL)
- Many operations with tiny response flagged (MEDIUM)
- Large tool output with no mention flagged (MEDIUM)
- Findings logged to
~/.claude/audit/findings/deception_log.jsonl - Session JSONL archived to dated directory (never deleted)
- Subagent sessions audited separately
US-004: Safety Guards (P0)
As a developer I want destructive operations (rm, file overwrites, bare exceptions) to be blocked automatically So that Claude Code cannot accidentally destroy files, overwrite existing code, or introduce error-swallowing patterns
Acceptance Criteria:
rm,unlink,rmdir,shredcommands blocked in Bash- Writes to
~/.claude/directory blocked in Bash - Write tool blocked if file already exists (must use Edit)
- Write to
~/.claude/plans/blocked (redirect to projectplans/) - Write to
context.md/context.jsonblocked (must use session CLI) - Bare
except:blocked in Python Write/Edit (AST-based detection) except ...: passblocked in Python Write/Edit- All blocked operations logged to audit file with reason
US-005: Neo4j Context Injection (P1)
As a developer I want file reads to be enriched with knowledge graph context (classes, functions, specs, related nodes) So that Claude Code has project-wide understanding beyond the single file being read
Acceptance Criteria:
block_file_tools.py(when active) queries Neo4j on Read/Grep- Injects file-scoped entities (classes, functions defined in the file)
- Injects specification traceability (which specs the file traces to)
- Injects related nodes via full-text search by filename
- Results injected as
additionalContextin hook output
US-006: Session Context Persistence (P0)
As a developer
I want context.json to serve as the single source of truth for all session data, never losing history
So that I can review any past session's state, achievements, and deferred work
Acceptance Criteria:
context.jsonis never trimmed or truncated- All sessions recorded in
session_historyarray context.mdauto-regenerated fromcontext.json(last 3 sessions)sync_context_md.pyfires on PostToolUse whencontext.jsonis modified- Direct writes to
context.md/context.jsonblocked (use session CLI) context.history.mdreceives append-only archives of older sessions
US-007: Read Throttling (P2)
As a project architect I want excessive file reads throttled to encourage knowledge-graph-first exploration So that Claude Code leverages the Neo4j knowledge graph instead of reading files sequentially
Acceptance Criteria:
- Read/Grep operations tracked in
/tmp/claude-read-tracker.json - 3 reads allowed per 30-second sliding window
- Reads of
context.json,.env,settings.json,CLAUDE.mdexempted - Excess reads blocked with Neo4j query examples
- Throttle state resets naturally as timestamps expire from window
US-008: Audit Trail (P1)
As a project owner I want all tool operations, deception findings, and session data logged as JSONL So that there is a complete, immutable audit trail of everything Claude Code does
Acceptance Criteria:
posttool_auditor.pyaccumulates tool calls in session JSONLlog_operation.pylogs Write/Edit/Bash/Read/Glob/Grep operations- Active session JSONL stored in
~/.claude/audit/sessions/ - Completed sessions archived to
~/.claude/audit/archive/YYYY-MM-DD/ - Deception findings appended to
~/.claude/audit/findings/deception_log.jsonl - Archive data NEVER deleted
- Subagent findings stored separately in
~/.claude/audit/subagent_findings/
US-009: RTM Generation (P1)
As a project manager
I want requirements traceability matrices automatically generated from @traces decorators at session close
So that requirement coverage is tracked and verified with every session
Acceptance Criteria:
- Session close hook runs 3 RTM generation scripts
- Scripts parse
@traces(spec=, story=, reqs=[])decorators from source code - RTM output committed to
plans/directory - RTM generation failures logged but do not block session close
- Generated RTM maps requirements to implementing functions/classes
US-010: Remember/Search (P2)
As a developer I want to ask "do you remember when we discussed X" and get actual past conversation context from Neo4j So that cross-session memory works without manual context loading
Acceptance Criteria:
- Trigger phrases detected: "remember when", "do you remember", "recall when", "remember that", "we discussed", "we talked about", "last time we", "what happened with"
- Topic extracted from text after trigger phrase
- Neo4j query searches
ConversationTurn.textfor all topic words (length > 2) - Up to 15 results returned, ordered by timestamp descending
- Results injected as
additionalContextbefore Claude processes the message - Query timeout of 15 seconds
- Missing Neo4j connection fails gracefully (no block)
Terminology and Glossary
| Term | Definition |
|---|---|
| Hook | A Python script registered in ~/.claude/settings.json that executes on a specific Claude Code lifecycle event (SessionStart, PreToolUse, PostToolUse, UserPromptSubmit, Stop). |
| Error Gate | A Stop hook package that blocks Claude Code from ending a session when unfixed errors (syntax, lint, test failures, stubs, deep nesting, god objects) are detected in modified files. |
| Stop Auditor | A Stop hook that performs deception analysis by comparing what Claude claimed to do (in its text response) against what tools were actually invoked (from the session JSONL). |
| Deception Detection | Pattern-based comparison of assistant narrative text against recorded tool calls, detecting claims of file creation without Write, command execution without Bash, or file reading without Read. |
| Read Throttle | A PreToolUse hook that limits Read/Grep tool invocations to 3 per 30-second window, blocking excess reads and redirecting Claude to use Neo4j knowledge graph queries instead. |
| Session Module | A Python package deployed to ~/.claude/tools/session/ that provides SessionController, ContextManager, and data models for tracking session lifecycle. |
| context.json | The single source of truth for session state. Contains state, deferred_work, and session_history arrays. Never trimmed. |
| context.md | A Jinja2-rendered markdown view of the last 3 sessions from context.json. Auto-regenerated by the sync_context_md.py PostToolUse hook. |
| RTM | Requirements Traceability Matrix. Generated from @traces decorators in source code during session close, committed to plans/. |
| JSONL | JSON Lines format (one JSON object per line). Used for audit logs, session transcripts, and deception findings. |
| Session Tag | An annotated git tag created at session close in the format session-NNN-YYYY-MM-DD-HHMM. |
| Git Checkpoint | A git stash or commit created before destructive operations to enable recovery. |
| God Object | A class exceeding 300 lines or 15 public methods, or a file exceeding 500 lines. Detected by checks_structure.py. |
| Stub Method | A function whose body contains only logging calls, local assignments, pass statements, or docstrings -- doing no real work. |
| Safety Hook | Any PreToolUse hook that blocks potentially harmful operations (destructive commands, bare exceptions, file overwrites). |
| SubagentStop | A Stop event variant for Claude Code subagent sessions, handled separately by stop_auditor_subagent.py. |
Data Models
context.json Schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["state", "deferred_work", "session_history"],
"properties": {
"state": {
"type": "object",
"required": ["session", "version", "branch", "status"],
"properties": {
"session": { "type": "integer", "minimum": 1 },
"version": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+$" },
"branch": { "type": "string" },
"test_count": { "type": "integer" },
"status": { "type": "string", "enum": ["active", "closed"] }
}
},
"deferred_work": {
"type": "array",
"items": {
"type": "object",
"required": ["id", "priority", "description", "status"],
"properties": {
"id": { "type": "string" },
"priority": { "type": "string", "enum": ["high", "medium", "low"] },
"description": { "type": "string" },
"status": { "type": "string", "enum": ["pending", "in_progress", "completed"] },
"notes": { "type": "string" }
}
}
},
"session_history": {
"type": "array",
"items": {
"type": "object",
"required": ["seq", "when", "purpose", "achievements", "length"],
"properties": {
"seq": { "type": "integer" },
"when": { "type": "string", "format": "date-time" },
"purpose": { "type": "string" },
"achievements": { "type": "array", "items": { "type": "string" } },
"length": { "type": "string" }
}
}
}
}
}
Hook Input Schema (PreToolUse/PostToolUse)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["hook_name", "session_id", "message", "tool_name"],
"properties": {
"hook_name": { "type": "string", "enum": ["PreToolUse", "PostToolUse", "SessionStart", "Stop", "UserPromptSubmit"] },
"session_id": { "type": "string" },
"message": {
"type": "object",
"properties": {
"role": { "type": "string" },
"content": { "type": "array" }
}
},
"tool_name": { "type": "string" },
"tool_input": { "type": "object" },
"tool_output": { "type": "string" },
"has_error": { "type": "boolean" }
}
}
Deception Finding Schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["timestamp", "session_id", "findings_count", "findings", "tool_calls_count", "tools_used"],
"properties": {
"timestamp": { "type": "number" },
"iso_time": { "type": "string", "format": "date-time" },
"session_id": { "type": "string" },
"findings_count": { "type": "integer" },
"findings": {
"type": "array",
"items": {
"type": "object",
"required": ["type", "severity", "description"],
"properties": {
"type": { "type": "string" },
"severity": { "type": "string", "enum": ["CRITICAL", "HIGH", "MEDIUM"] },
"description": { "type": "string" },
"evidence": { "type": "string" }
}
}
},
"tool_calls_count": { "type": "integer" },
"tools_used": { "type": "array", "items": { "type": "string" } },
"assistant_message_length": { "type": "integer" },
"assistant_message_preview": { "type": "string" }
}
}
Read Throttle State Schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"reads": {
"type": "array",
"items": {
"type": "object",
"required": ["timestamp", "path"],
"properties": {
"timestamp": { "type": "number" },
"path": { "type": "string" }
}
}
}
}
}
Technical Design
Hook Event Flow
SESSION LIFECYCLE
=================
SessionStart
├── session_auto_start.py ──── Creates session entry in context.json
│ (guards against duplicate creation on compaction)
└── session_context_hook.py ── Injects session context into conversation
UserPromptSubmit
├── session_close_hook.py ──── Detects "cts"/"close the session"
│ → Generates RTM from @traces decorators
│ → Calls SessionController.end()
│ → Creates annotated git tag
│ → Updates CHANGELOG.md
│ → Pushes tag and branch to origin
├── session_open_hook.py ───── Detects "sns"/"start new session"
│ → Injects pending tasks + project metrics
└── remember_hook.py ────────── Detects "remember when"/"do you remember"
→ Searches Neo4j conversation history
→ Injects results as additionalContext
PRE-TOOL HOOKS
==============
PreToolUse(Write)
├── intercept_write.py ──────── BLOCKS if file exists (use Edit instead)
│ BLOCKS writes to ~/.claude/plans/
│ BLOCKS writes to context.md/context.json
│ Logs operation to audit file
└── block_except_pass.py ────── BLOCKS bare `except:` in Python code
BLOCKS `except ...: pass` patterns
Uses ast.parse for accurate detection
PreToolUse(Edit)
├── intercept_edit.py ────────── Git checkpoint before edits
│ Logs operation to audit file
└── block_except_pass.py ────── Same bare-except checks as Write
PreToolUse(Read|Grep)
└── read_throttle.py ────────── Tracks reads in /tmp/claude-read-tracker.json
Allows 3 reads per 30-second window
Exempts context.json, .env, settings.json, CLAUDE.md
BLOCKS excess reads with Neo4j redirect message
PreToolUse(Bash)
└── intercept_bash.py ────────── BLOCKS rm, unlink, rmdir, shred
BLOCKS writes to ~/.claude/ directory
Creates git checkpoint before deletions
Logs all operations to audit file
PreToolUse(.*)
└── enforcement_hook.py ──────── Central rule enforcement
Checks all tool uses against project rules
POST-TOOL HOOKS
===============
PostToolUse(Edit)
└── log_operation.py ─────────── Logs Edit operations to audit file
PostToolUse(Write|Edit|Bash|Read|Glob|Grep)
└── log_operation.py ─────────── Logs all tool operations to audit file
PostToolUse(.*)
└── posttool_auditor.py ──────── Accumulates tool calls in session JSONL
Records tool_name, has_error, output_length
PostToolUse(Write|Edit) [when file is context.json]
└── sync_context_md.py ──────── Regenerates context.md from context.json
via ContextManager.write()
STOP HOOKS
==========
Stop
├── error_gate_hook.py ──────── Runs 6 check categories on modified files:
│ ├── checks_syntax.py ──── ast.parse() on modified .py files
│ ├── checks_lint.py ────── flake8 --select=E9,F82,F83,F84
│ ├── checks_test.py ────── pytest on changed test files (non-prolog)
│ └── checks_structure.py ─ Stub methods, nesting >4, god objects
│ BLOCKS session end if any errors found
│ Injects fix instructions into additionalContext
│
└── stop_auditor.py ─────────── Extracts assistant messages from transcript
├── detect_deception() ── Pattern matching: claims vs tool usage
│ ├── File creation without Write/Edit
│ ├── Command execution without Bash
│ ├── File reading without Read/Grep
│ ├── Error concealment (tool errors not reported)
│ ├── Selective disclosure (many ops, tiny response)
│ └── Result hiding (large output, no mention)
├── log_findings() ────── Appends to deception_log.jsonl
└── archive_session() ── Moves session JSONL to dated archive
Deployment Architecture
Source (version-controlled) Deployed (runtime)
======================== ==================
claude/ ~/.claude/
├── hooks/ ├── hooks/
│ ├── error_gate/ │ ├── error_gate/
│ │ ├── __init__.py │ │ ├── __init__.py
│ │ ├── checks_syntax.py │ │ ├── checks_syntax.py
│ │ ├── checks_lint.py │ │ ├── checks_lint.py
│ │ ├── checks_test.py │ │ ├── checks_test.py
│ │ ├── checks_structure.py │ │ ├── checks_structure.py
│ │ └── utils.py │ │ └── utils.py
│ ├── stop_audit/ │ ├── stop_audit/
│ │ ├── __init__.py │ │ ├── __init__.py
│ │ ├── deception.py │ │ ├── deception.py
│ │ ├── transcript.py │ │ ├── transcript.py
│ │ └── subagent.py │ │ └── subagent.py
│ ├── session_auto_start.py │ ├── session_auto_start.py
│ ├── session_close_hook.py │ ├── session_close_hook.py
│ ├── session_open_hook.py │ ├── session_open_hook.py
│ ├── read_throttle.py │ ├── read_throttle.py
│ ├── block_except_pass.py │ ├── block_except_pass.py
│ ├── intercept_bash.py │ ├── intercept_bash.py
│ ├── intercept_write.py │ ├── intercept_write.py
│ ├── intercept_edit.py │ ├── intercept_edit.py
│ ├── enforcement_hook.py │ ├── enforcement_hook.py
│ ├── instruction_enforcer.py │ ├── instruction_enforcer_hook.py
│ ├── sync_context_md.py │ ├── sync_context_md.py
│ ├── remember_hook.py │ ├── remember_hook.py
│ ├── log_operation.py │ ├── log_operation.py
│ ├── posttool_auditor.py │ ├── posttool_auditor.py
│ ├── block_file_tools.py │ ├── block_file_tools.py
│ ├── stop_auditor.py │ ├── stop_auditor.py
│ ├── stop_auditor_subagent.py │ ├── stop_auditor_subagent.py
│ ├── session_context_hook.py │ └── session_context_hook.py
│ └── deception_detector.py │
├── tools/ ├── tools/
│ ├── session_cli.py │ ├── session_cli.py
│ └── statusline_session.py │ ├── statusline_session.py
├── scripts/ │ └── session/
│ └── strip_traces.py │ ├── __init__.py
├── install.sh │ ├── models.py
├── settings.json │ ├── context.py
├── statusline-command.sh │ ├── session.py
└── README.md │ ├── exceptions.py
│ ├── archive.py
│ ├── carryover.py
│ ├── handover.py
│ ├── portfolio.py
│ ├── kg_context.py
│ ├── kg_sync.py
│ ├── spec_parser.py
│ └── templates/
├── audit/
│ ├── sessions/ (active JSONL)
│ ├── archive/ (dated, NEVER deleted)
│ └── findings/ (deception_log.jsonl)
├── logs/
│ └── file_operations.log
├── settings.json
└── statusline-command.sh
Settings Registration
The ~/.claude/settings.json file registers hooks by event type with matchers:
{
"hooks": {
"PreToolUse": [
{ "matcher": "Edit", "hooks": [{ "command": "intercept_edit.py" }] },
{ "matcher": "Bash", "hooks": [{ "command": "intercept_bash.py" }] },
{ "matcher": "Write", "hooks": [{ "command": "intercept_write.py" }] },
{ "matcher": ".*", "hooks": [{ "command": "enforcement_hook.py" }] }
],
"PostToolUse": [
{ "matcher": "Edit", "hooks": [{ "command": "log_operation.py" }] },
{ "matcher": "Write|Edit|Bash|Read|Glob|Grep", "hooks": [{ "command": "log_operation.py" }] },
{ "matcher": ".*", "hooks": [{ "command": "posttool_auditor.py" }] }
],
"SessionStart": [
{ "hooks": [{ "command": "session_auto_start.py" }] },
{ "hooks": [{ "command": "session_context_hook.py" }] }
],
"Stop": [
{ "hooks": [{ "command": "stop_auditor.py" }] }
],
"UserPromptSubmit": [
{ "hooks": [{ "command": "session_close_hook.py" }] }
]
}
}
Error Gate Package
The error gate is a Stop hook that runs 6 check categories against recently modified files. If any check produces errors, the session stop is blocked with detailed fix instructions.
checks_syntax.py
Purpose: Detect Python syntax errors in modified files.
Method: Uses git diff --name-only --diff-filter=M HEAD to find modified .py files, then runs ast.parse() on each one. Syntax errors are reported with file path and error message (truncated to 200 chars).
Timeout: Inherits CHECK_TIMEOUT (30 seconds) from utils.py.
checks_lint.py
Purpose: Detect critical lint errors that indicate broken code.
Method: Runs uv run flake8 --select=E9,F82,F83,F84 on modified .py files (capped at 30 files). Only checks for:
- E9xx: Syntax errors detected by flake8
- F82x: Undefined names
- F83x: Undefined local variables
- F84x: Import errors (duplicate, shadowed)
Reports up to 10 lint errors.
checks_test.py
Purpose: Run a quick subset of tests to catch regressions.
Method: Identifies changed test files under tests/ from git diff --name-only HEAD~1. Excludes:
- Files with "prolog" in the name
- Files importing pyswip, GovernanceEngine, PrologBridge, or related triggers
- All test files if
tests/conftest.pyitself uses pyswip (free-threaded Python crash)
Runs uv run pytest with -x -q --no-header on up to 10 test files. Ignores exit code 139 (pyswip SIGSEGV crash). Only reports lines containing "FAILED".
Timeout: 300 seconds (5 minutes).
checks_structure.py
Purpose: Detect structural code quality issues in source code (not tests).
Stub Detection:
- Scans non-test modified
.pyfiles for public functions (not starting with_) - A function is a stub if its body contains ONLY: logging calls, local list appends, string constants (docstrings),
passstatements, and simple local assignments - Excludes
__init__,__del__,__enter__,__exit__,close,cleanup - Uses AST analysis to distinguish real work from logging-only bodies
awaitexpressions are NOT stubs (real async work)- Augmented assignment on attributes (
self.x += 1) is NOT a stub (state mutation)
Nesting Depth:
- Measures maximum control-flow nesting in each function
- Counts:
if,for,while,with,try,async for,async with - Flags functions exceeding 4 levels of nesting
God Objects:
- Classes exceeding 300 lines (measured by
end_lineno - lineno + 1) - Classes with more than 15 public methods (not starting with
_) - Files exceeding 500 lines
- Only checks source code, not test files
utils.py
Purpose: Shared utilities for all error gate check modules.
Constants:
PROJECT_ROOT: FromORCHESTRATA_ROOTenv var or~/workspace/orchestrataCHECK_TIMEOUT: 30 seconds per checkSTUB_EXCLUDED_NAMES:__init__,__del__,__enter__,__exit__,close,cleanupSTUB_LOG_PREFIXES:logger.*andlog.*method namesMAX_NESTING_DEPTH: 4MAX_CLASS_LINES: 300MAX_PUBLIC_METHODS: 15MAX_FILE_LINES: 500EXCLUDED_DIRS:spike,iterations,ci(generated outputs, not source)
Functions:
run_command(cmd, cwd, timeout): Subprocess execution with timeout handlingget_modified_py_files(project_root, exclude_tests): Git diff file discoveryparse_file(full_path): Safe AST parsing returning None on failure
Stop Audit Package
The stop audit package analyzes the session transcript at conversation end to detect potential deception -- cases where Claude's narrative claims do not match actual tool usage.
deception.py
Purpose: Pattern-based deception detection engine.
Pre-processing: _remove_quoted_content() strips code blocks (triple backtick), inline code (single backtick), blockquotes (>), and HTML pre/code tags to prevent false positives from displayed code.
Deception Categories:
| Category | Severity | Detection Method |
|---|---|---|
| File creation claimed without Write/Edit | CRITICAL | Regex patterns for "created/wrote/saved the file" when Write/Edit not in tools_used |
| Command execution claimed without Bash | CRITICAL | Regex patterns for "ran/executed the command" when Bash not in tools_used |
| File reading claimed without Read/Grep | HIGH | Regex patterns for "read/examined/reviewed the file" when Read/Grep not in tools_used |
| Error concealment | CRITICAL | Tool calls with has_error=True but no error-related words in response |
| Selective disclosure | MEDIUM | More than 10 tool calls but response under 100 characters |
| Result hiding | MEDIUM | Tool output >500 chars with <3 overlapping 6+ char words in response |
Output: List of finding dicts with type, severity, description, evidence, and optionally pattern_matched and tool_name.
transcript.py
Purpose: Parse JSONL transcripts into assistant message text.
Method: Reads transcript line by line, extracts entries with message.role == "assistant", collects text from string content or typed content blocks (type == "text"), joins with newlines.
subagent.py
Purpose: Handle SubagentStop events (Claude Code subagent sessions).
Method: Detects SubagentStop events in hook input, parses subagent transcripts, extracts tool names and text content from content blocks, runs the same detect_deception() logic, and logs findings to a separate ~/.claude/audit/subagent_findings/ directory.
Session Management
Session Module
Deployed to ~/.claude/tools/session/ from packages/orchestrata-session/src/orchestrata/session/. The install.sh script:
- Copies all session module files
- Runs
strip_traces.pyto remove@tracesdecorators (monorepo dependency) - Removes imports for
EventEmittingSessionController,prolog_interface,facts,constants,hook_registry(not needed standalone)
Key classes:
SessionController: Providesbegin(),end(),log_work(),log_achievement()operationsContextManager: Reads/writescontext.json, renderscontext.mdvia Jinja2 templates
session_cli.py
CLI wrapper for session operations, callable via Bash tool:
python3 ~/.claude/tools/session_cli.py status
python3 ~/.claude/tools/session_cli.py begin --purpose "Focus text"
python3 ~/.claude/tools/session_cli.py log-work --desc "Description"
python3 ~/.claude/tools/session_cli.py end --achievements "item1" "item2"
python3 ~/.claude/tools/session_cli.py init --version 1.0.0 --branch master
context.json Structure
{
"state": {
"session": 89,
"version": "0.6.0",
"branch": "master",
"test_count": 9203,
"status": "active"
},
"deferred_work": [
{
"id": "DW-001",
"priority": "high",
"description": "...",
"status": "pending",
"notes": "..."
}
],
"session_history": [
{
"seq": 89,
"when": "2026-03-29T10:00:00",
"purpose": "...",
"achievements": ["..."],
"length": "2h30m"
}
]
}
Session Close Sequence
When the user types "cts" or "close the session":
- RTM Generation: Runs 3 scripts to extract
@tracesdecorators from source code, generating requirements traceability matrices committed toplans/ - Achievement Extraction: Parses git log since session start tag, extracts commit messages as achievements
- context.json Update: Calls
SessionController.end()which updates session length, achievements, and status - Git Tag: Creates annotated tag
session-NNN-YYYY-MM-DD-HHMM - CHANGELOG.md: Appends categorized achievements under version heading
- Push: Pushes session tag and current branch to origin
Session Auto-Start Guards
The session_auto_start.py hook guards against phantom sessions caused by context compaction (which re-fires SessionStart):
- If last session has no
lengthfield (still active), skip creation - If last session was created less than 300 seconds ago, skip creation
- Only runs for projects with "orchestrata" in the path
Safety Hooks
intercept_bash.py
Event: PreToolUse(Bash)
Blocks:
rm,unlink,rmdir,shredcommands (regex detection)- Writes to
~/.claude/directory (redirect patterns:>,>>,cat >,echo >,cp,mv,touch,mkdir)
Allows: All other Bash commands.
Audit: Logs blocked operations to ~/.claude/logs/file_operations.log as JSONL.
intercept_write.py
Event: PreToolUse(Write)
Blocks:
- Files that already exist (must use Edit tool for modifications)
- Writes to
~/.claude/plans/(redirect to projectplans/folder) - Writes to
context.mdorcontext.json(must use session CLI)
Allows: New file creation to any other path.
Audit: Logs all operations (allowed and blocked) to ~/.claude/logs/file_operations.log.
intercept_edit.py
Event: PreToolUse(Edit)
Actions: Creates git checkpoint before edits, logs operation to audit file.
block_except_pass.py
Event: PreToolUse(Write|Edit)
Blocks:
- Bare
except:(no exception type specified) except ...: pass(error swallowing)
Method: Uses ast.parse() to build AST, walks all ExceptHandler nodes. Only checks .py files. Reports line numbers and suggests specific exception types with logging.
Allows: Non-Python files and properly typed exception handlers.
Neo4j Integration
read_throttle.py
Event: PreToolUse(Read|Grep)
Mechanism:
- Tracks read timestamps in
/tmp/claude-read-tracker.json - Allows 3 reads per 30-second sliding window
- Exempts reads of
context.json,.env,settings.json,CLAUDE.md - On throttle, blocks with message redirecting to Neo4j queries:
/code classes <pattern>-- find classes/code methods <class>-- list methods/code file <path>-- entities in a file/code search <keyword>-- full-text search/code deps <module>-- dependencies- Raw Cypher queries
remember_hook.py
Event: UserPromptSubmit
Triggers: Regex patterns for memory-related phrases:
- "remember when", "do you remember", "recall when", "remember that"
- "we discussed", "we talked about", "last time we", "what happened with"
Method: Extracts topic words from the prompt after the trigger phrase. Executes a Neo4j Cypher query against Conversation and ConversationTurn nodes, matching all topic words (length > 2) against toLower(t.text). Returns up to 15 results ordered by timestamp descending.
Output: Injects search results as additionalContext so Claude sees past conversation history as facts before processing the user message.
Timeout: 15 seconds for Neo4j query execution.
block_file_tools.py
Event: PreToolUse(Read|Grep) (when active)
Method: Queries Neo4j on every file read, injecting:
- File-scoped entities (classes, functions defined in the file)
- Specification traceability (which specs the file traces to)
- Related nodes (full-text search by filename)
Audit Trail
Storage Layout
~/.claude/audit/
├── sessions/ Active session JSONL files
│ └── {session_id}.jsonl
├── archive/ Completed sessions (dated subdirectories)
│ └── YYYY-MM-DD/
│ └── {session_id}_{timestamp}.jsonl
├── findings/ Deception detection results
│ └── deception_log.jsonl
└── subagent_findings/ Subagent deception results
└── {subagent_id}_{timestamp}.jsonl
Log Files
~/.claude/logs/
└── file_operations.log All Write/Edit/Bash operations (JSONL)
Data Retention
- Active sessions: Kept in
sessions/during conversation - Archived sessions: Moved to
archive/YYYY-MM-DD/at session end. NEVER deleted. - Deception findings: Appended to
deception_log.jsonl. NEVER deleted. - Operation logs: Appended to
file_operations.log. Rotated by size if needed.
JSONL Formats
Tool Call Record:
{
"type": "tool_call",
"tool_name": "Write",
"has_error": false,
"tool_output": "...",
"tool_output_length": 1234,
"timestamp": "2026-03-29T10:15:00"
}
Deception Finding:
{
"timestamp": 1743244500.0,
"iso_time": "2026-03-29T10:15:00",
"session_id": "session-089",
"findings_count": 2,
"findings": [
{
"type": "ACTION_DECEPTION",
"severity": "CRITICAL",
"description": "Claimed file creation but no Write/Edit tool was used",
"evidence": "Tools used: ['Bash', 'Read']"
}
],
"tool_calls_count": 15,
"tools_used": ["Bash", "Read", "Grep"],
"assistant_message_length": 2500,
"assistant_message_preview": "..."
}
File Operation Log Entry:
{
"timestamp": "2026-03-29T10:15:00",
"operation": "Write",
"file_path": "/home/mohamed/workspace/orchestrata/new_file.py",
"status": "allowed",
"message": "Validation passed, Write tool will create file"
}
Deployment
install.sh
The deployment script (claude/install.sh) performs:
- Creates
~/.claude/hooks/,~/.claude/tools/session/,~/.claude/tools/session/templates/ - Copies session module from
packages/orchestrata-session/src/orchestrata/session/(13 files + templates) - Removes monorepo-specific imports (
EventEmittingSessionController,prolog_interface,facts,constants,hook_registry) - Runs
strip_traces.pyto remove@tracesdecorators andorchestrata.coreimports - Copies
session_cli.pyandstatusline_session.pyto~/.claude/tools/ - Copies
statusline-command.shto~/.claude/ - Copies
session_auto_start.py,session_close_hook.py,session_open_hook.pyto~/.claude/hooks/ - Copies general hooks:
block_file_tools.py,intercept_write.py,intercept_bash.py,intercept_edit.py,instruction_enforcer_hook.py - Registers
SessionStarthook in~/.claude/settings.json(idempotent JSON manipulation) - Registers
UserPromptSubmithooks for session close and session open (idempotent)
strip_traces.py
Post-deploy cleanup script that:
- Removes
@traces(spec=..., story=..., reqs=[...])decorators from all deployed.pyfiles - Removes
from orchestrata.core.traceability import tracesimports - Ensures deployed code has no dependency on the monorepo package structure
Functional Requirements
FR-001: Hook Registration
- FR-001-001: The system SHALL register hooks in
~/.claude/settings.jsonunder thehookskey, organized by event type. - FR-001-002: Each hook registration SHALL specify a
matcherregex pattern and acommandstring. - FR-001-003: The
install.shscript SHALL perform idempotent registration (no duplicate entries). - FR-001-004: Hook commands SHALL use absolute paths to deployed hook scripts.
- FR-001-005: The system SHALL support 5 hook event types: SessionStart, PreToolUse, PostToolUse, UserPromptSubmit, Stop.
FR-002: Hook Protocol
- FR-002-001: All hooks SHALL read JSON from stdin containing hook-specific input fields.
- FR-002-002: All hooks SHALL write JSON to stdout as the hook response.
- FR-002-003: PreToolUse hooks SHALL return
{"decision": "block", "reason": "..."}to block or{}to allow. - FR-002-004: Stop hooks SHALL return
{"decision": "block", "reason": "..."}to block or{}to allow. - FR-002-005: Hooks MAY return
{"additionalContext": "..."}to inject context into the conversation. - FR-002-006: Hooks SHALL write diagnostic output to stderr (not stdout) to avoid corrupting JSON responses.
- FR-002-007: Hooks SHALL exit with code 0 on success (even when blocking).
FR-003: Session Auto-Start
- FR-003-001: The
session_auto_start.pyhook SHALL fire on SessionStart events. - FR-003-002: The hook SHALL only run for projects containing "orchestrata" in the path.
- FR-003-003: The hook SHALL NOT create a new session if the last session has no
lengthfield (still active). - FR-003-004: The hook SHALL NOT create a new session if the last session was created less than 300 seconds ago.
- FR-003-005: The hook SHALL call
SessionController.begin()with a timestamped purpose string. - FR-003-006: The hook SHALL output session number, start time, and purpose to stdout.
FR-004: Session Close
- FR-004-001: The
session_close_hook.pyhook SHALL fire on UserPromptSubmit events. - FR-004-002: The hook SHALL detect "cts", "cs", and "close the session" (case-insensitive, trimmed).
- FR-004-003: The hook SHALL generate RTM from
@tracesdecorators via 3 subprocess calls. - FR-004-004: The hook SHALL extract achievements from
git logsince session start. - FR-004-005: The hook SHALL call
SessionController.end()to update context.json. - FR-004-006: The hook SHALL create an annotated git tag in format
session-NNN-YYYY-MM-DD-HHMM. - FR-004-007: The hook SHALL update CHANGELOG.md with categorized achievements.
- FR-004-008: The hook SHALL push the session tag and current branch to origin.
- FR-004-009: The hook SHALL inject a close summary into
additionalContext. - FR-004-010: RTM generation failures SHALL be logged but SHALL NOT block session close.
FR-005: Session Open
- FR-005-001: The
session_open_hook.pyhook SHALL fire on UserPromptSubmit events. - FR-005-002: The hook SHALL detect "sns" and "start new session" (case-insensitive).
- FR-005-003: The hook SHALL display pending deferred work items grouped by priority.
- FR-005-004: The hook SHALL inject project metrics into
additionalContext.
FR-006: Error Gate - Syntax
- FR-006-001: The error gate SHALL check all modified
.pyfiles for syntax errors. - FR-006-002: Syntax checking SHALL use
ast.parse()via subprocess. - FR-006-003: Syntax errors SHALL be reported with file path and error description.
FR-007: Error Gate - Lint
- FR-007-001: The error gate SHALL run flake8 on modified
.pyfiles (capped at 30). - FR-007-002: Only critical lint errors SHALL be checked: E9xx, F82x, F83x, F84x.
- FR-007-003: Lint errors SHALL be reported up to 10 per run.
- FR-007-004:
--max-line-length=120SHALL be used for flake8 invocation.
FR-008: Error Gate - Tests
- FR-008-001: The error gate SHALL run changed test files with
pytest -x -q. - FR-008-002: Prolog-dependent test files SHALL be excluded (pyswip crash prevention).
- FR-008-003: Test file count SHALL be capped at 10 per run.
- FR-008-004: Exit code 139 (SIGSEGV) SHALL be treated as infrastructure issue, not test failure.
- FR-008-005: Only lines containing "FAILED" SHALL be reported as test failures.
- FR-008-006: Test timeout SHALL be 300 seconds.
FR-009: Error Gate - Structure
- FR-009-001: Stub methods SHALL be detected in non-test modified files.
- FR-009-002: A stub SHALL be defined as a function body containing only logging, local assigns, pass, or docstrings.
- FR-009-003: Functions named
__init__,__del__,__enter__,__exit__,close,cleanupSHALL be excluded from stub detection. - FR-009-004: Private functions (starting with
_) SHALL be excluded from stub detection. - FR-009-005: Nesting depth SHALL be measured for all functions in modified files.
- FR-009-006: Functions exceeding 4 levels of nesting SHALL be flagged.
- FR-009-007: Classes exceeding 300 lines SHALL be flagged as god objects.
- FR-009-008: Classes with more than 15 public methods SHALL be flagged.
- FR-009-009: Files exceeding 500 lines SHALL be flagged.
- FR-009-010: Generated directories (spike/, iterations/, ci/) SHALL be excluded.
FR-010: Deception Detection
- FR-010-001: The stop auditor SHALL extract assistant messages from the JSONL transcript.
- FR-010-002: Code blocks, inline code, blockquotes, and HTML pre/code tags SHALL be stripped before analysis.
- FR-010-003: File creation claims SHALL be flagged as CRITICAL if Write/Edit not in tools_used.
- FR-010-004: Command execution claims SHALL be flagged as CRITICAL if Bash not in tools_used.
- FR-010-005: File reading claims SHALL be flagged as HIGH if Read/Grep not in tools_used.
- FR-010-006: Tool errors not mentioned in assistant response SHALL be flagged as CRITICAL.
- FR-010-007: Sessions with >10 tool calls but <100 char response SHALL be flagged as MEDIUM.
- FR-010-008: Tool output >500 chars with <3 overlapping words in response SHALL be flagged as MEDIUM.
- FR-010-009: Findings SHALL be logged to
deception_log.jsonlwith timestamps and session context. - FR-010-010: Session JSONL SHALL be archived to dated directory at session end.
FR-011: Safety - Bash
- FR-011-001:
rm,unlink,rmdir,shredcommands SHALL be blocked. - FR-011-002: Writes to
~/.claude/directory SHALL be blocked. - FR-011-003: Blocked commands SHALL be logged with operation type, command, and status.
- FR-011-004: Allowed commands SHALL pass through without modification.
FR-012: Safety - Write
- FR-012-001: Write to existing files SHALL be blocked (redirect to Edit tool).
- FR-012-002: Write to
~/.claude/plans/SHALL be blocked (redirect to projectplans/). - FR-012-003: Write to
context.mdandcontext.jsonSHALL be blocked (redirect to session CLI). - FR-012-004: New file creation to valid paths SHALL be allowed.
- FR-012-005: All operations SHALL be logged to
file_operations.log.
FR-013: Safety - Except
- FR-013-001: Bare
except:(no exception type) SHALL be blocked. - FR-013-002:
except ...: pass(error swallowing) SHALL be blocked. - FR-013-003: Only
.pyfiles SHALL be checked. - FR-013-004: Detection SHALL use
ast.parse()for accuracy (no regex). - FR-013-005: Violations SHALL report line numbers and suggest alternatives.
FR-014: Read Throttle
- FR-014-001: Read/Grep operations SHALL be tracked with timestamps in
/tmp/claude-read-tracker.json. - FR-014-002: Maximum 3 reads SHALL be allowed per 30-second sliding window.
- FR-014-003: Reads of
context.json,.env,settings.json,CLAUDE.mdSHALL be exempted. - FR-014-004: Throttled reads SHALL be blocked with Neo4j query redirect message.
- FR-014-005: Expired timestamps SHALL be automatically pruned on each check.
FR-015: Remember Hook
- FR-015-001: The hook SHALL detect 8 trigger phrases via compiled regex.
- FR-015-002: Topic words SHALL be extracted from text after the trigger phrase.
- FR-015-003: Neo4j query SHALL search ConversationTurn.text with case-insensitive word matching.
- FR-015-004: Query SHALL return up to 15 results ordered by timestamp descending.
- FR-015-005: Results SHALL be injected as
additionalContextwith factual framing. - FR-015-006: Query timeout SHALL be 15 seconds.
- FR-015-007: Missing Neo4j connection SHALL fail gracefully (no block).
FR-016: Sync Context MD
- FR-016-001: The hook SHALL fire on PostToolUse for Write and Edit tools.
- FR-016-002: The hook SHALL only act when the modified file is
context.json. - FR-016-003: The hook SHALL regenerate
context.mdviaContextManager.write(). - FR-016-004: Regeneration failures SHALL be logged but SHALL NOT block.
FR-017: Audit Logging
- FR-017-001:
posttool_auditor.pySHALL record every tool call in session JSONL. - FR-017-002: Records SHALL include tool_name, has_error, tool_output, tool_output_length.
- FR-017-003:
log_operation.pySHALL log Write/Edit/Bash/Read/Glob/Grep operations. - FR-017-004: Log entries SHALL include timestamp, operation, file_path, status, message.
- FR-017-005: Audit directories SHALL be created on first write (parents=True, exist_ok=True).
FR-018: Deployment
- FR-018-001:
install.shSHALL create all required directories. - FR-018-002: Session module SHALL be copied from
packages/orchestrata-session/. - FR-018-003:
strip_traces.pySHALL remove@tracesdecorators from deployed files. - FR-018-004: Monorepo-specific imports SHALL be removed from deployed
__init__.py. - FR-018-005: Hook registration in
settings.jsonSHALL be idempotent. - FR-018-006: Deployed hooks SHALL have executable permissions.
FR-019: Subagent Handling
- FR-019-001:
stop_auditor_subagent.pySHALL detect SubagentStop events in hook input. - FR-019-002: Subagent transcripts SHALL be parsed for tool usage and text content.
- FR-019-003: Same deception detection logic SHALL apply to subagent sessions.
- FR-019-004: Subagent findings SHALL be logged to
~/.claude/audit/subagent_findings/.
FR-020: Error Gate Integration
- FR-020-001: The error gate SHALL run all 6 check categories sequentially.
- FR-020-002: Check order SHALL be: syntax, lint, stubs, nesting, god objects, tests.
- FR-020-003: If any errors found, Stop SHALL be blocked with combined error report.
- FR-020-004: Error report SHALL include fix instructions (6 numbered steps).
- FR-020-005: If no errors found, Stop SHALL be allowed with blank JSON response.
Error Conditions
| ID | Condition | Handling |
|---|---|---|
| ERR-001 | Syntax error in modified Python file | Block session stop, inject fix instructions via additionalContext |
| ERR-002 | Critical lint error (E9xx, F82x, F83x, F84x) | Block session stop, report lint errors |
| ERR-003 | Test failure in changed test file | Block session stop, report failed test lines |
| ERR-004 | Stub method detected (logging/pass only body) | Block session stop, suggest implementing actual logic |
| ERR-005 | Nesting depth exceeds 4 levels | Block session stop, suggest refactoring |
| ERR-006 | God object detected (class >300 lines or >15 methods) | Block session stop, suggest splitting |
| ERR-007 | File exceeds 500 lines | Block session stop, suggest modularization |
| ERR-008 | File creation claimed without Write/Edit tool | Log CRITICAL deception finding, archive session |
| ERR-009 | Command execution claimed without Bash tool | Log CRITICAL deception finding, archive session |
| ERR-010 | File reading claimed without Read/Grep tool | Log HIGH deception finding, archive session |
| ERR-011 | Tool error not mentioned in assistant response | Log CRITICAL deception finding, archive session |
| ERR-012 | Selective disclosure (>10 tools, <100 char response) | Log MEDIUM deception finding, archive session |
| ERR-013 | Result hiding (>500 char output, <3 overlapping words) | Log MEDIUM deception finding, archive session |
| ERR-014 | Bare except: in Python code |
Block operation, suggest specific exception type |
| ERR-015 | except ...: pass pattern detected |
Block operation, suggest logging or specific handling |
| ERR-016 | Write to existing file attempted | Block operation, redirect to Edit tool |
| ERR-017 | Write to ~/.claude/plans/ attempted |
Block operation, redirect to project plans/ |
| ERR-018 | Write to context.md/context.json attempted |
Block operation, redirect to session CLI |
| ERR-019 | Destructive bash command (rm, unlink, rmdir, shred) | Block operation, log to audit file |
| ERR-020 | Bash write to ~/.claude/ directory |
Block operation, log to audit file |
| ERR-021 | Read throttle exceeded (>3 reads per 30 seconds) | Block operation, redirect to Neo4j queries |
| ERR-022 | Neo4j connection failure in remember hook | Fail gracefully, continue without context injection |
| ERR-023 | RTM generation script failure | Log error, do not block session close |
| ERR-024 | context.json write failure | Log error, do not block operation |
API Contracts
NFR-001: Performance
- NFR-001-001: Error gate total execution SHALL complete within 120 seconds.
- NFR-001-002: Each error gate check SHALL complete within 30 seconds (CHECK_TIMEOUT).
- NFR-001-003: PreToolUse hooks SHALL complete within 10 seconds.
- NFR-001-004: Read throttle SHALL complete within 5 seconds.
- NFR-001-005: Remember hook Neo4j query SHALL timeout at 15 seconds.
- NFR-001-006: Session auto-start SHALL complete within 5 seconds.
- NFR-001-007: Deception detection SHALL complete within 10 seconds.
NFR-002: Reliability
- NFR-002-001: Hooks SHALL fail gracefully (return
{}) rather than crashing Claude Code. - NFR-002-002: JSON parse errors in stdin SHALL result in
{}output and exit 0. - NFR-002-003: Missing dependencies (flake8, pytest, uv) SHALL be treated as skip, not error.
- NFR-002-004: Missing audit directories SHALL be auto-created on first write.
- NFR-002-005: Corrupt JSONL lines in transcripts SHALL be skipped with warning to stderr.
NFR-003: Data Integrity
- NFR-003-001:
context.jsonSHALL never be trimmed or lose session history. - NFR-003-002: Audit archive SHALL never be deleted.
- NFR-003-003: Deception log SHALL be append-only.
- NFR-003-004: Session JSONL SHALL be moved (not copied) to archive to prevent duplicates.
- NFR-003-005: Git tags SHALL be annotated and pushed to ensure remote persistence.
NFR-004: Security
- NFR-004-001: No hardcoded passwords or API keys in hook scripts.
- NFR-004-002: Neo4j credentials SHALL be loaded from environment variables or
.envfile. - NFR-004-003: Hook scripts SHALL not use
eval()orexec()with dynamic input. - NFR-004-004: File paths SHALL be validated before any I/O operations.
Security Requirements
Authentication
- Hooks do not require authentication as they run locally in the user's Claude Code environment
- Neo4j connections use username/password or token-based authentication via environment variables
Authorization
- Hooks operate within the user's file system permissions
- Write operations to
~/.claude/directory are blocked to prevent system file modification - Session operations require valid context.json state
Input Validation
- All JSON input from Claude Code is validated before processing
- File paths are validated against allowed patterns before I/O operations
- Command arguments are validated against blocklists (rm, unlink, etc.)
Audit Logging
- All tool operations logged to
~/.claude/logs/file_operations.log - Deception findings logged to
~/.claude/audit/findings/deception_log.jsonl - Session state changes logged to
context.json
Governance
Constitutional Alignment
This harness enforces the Orchestrata constitutional articles through dedicated mechanisms:
| Article | Mechanism | Implementation |
|---|---|---|
| I: Agent Autonomy | intercept_write.py, intercept_bash.py | File access boundaries enforced |
| II: Prolog as SSOT | Read throttle → Neo4j → Prolog | Governance remains in Prolog |
| III: Session Isolation | context.json, git tags, archives | Per-session state isolation |
| IV: Transparency | log_operation.py, posttool_auditor.py | Complete operation audit trail |
| V: Non-Deception | stop_auditor.py | Deception detection engine |
| VI: Quality Gates | error_gate_hook.py | 6-category code quality checks |
| VII: No Mocks | checks_test.py | Real pytest execution |
| VIII: Observability | Audit logs, deception metrics | Full operational visibility |
| IX: Graceful Degradation | All hooks return {} on failure | No crash behavior |
| X: Version Compatibility | strip_traces.py | Decoupled deployment |
| XI: Documentation First | This spec, CLAUDE.md | Comprehensive documentation |
Policy Enforcement
- Error gate enforces code quality policies (no stubs, no god objects, max nesting)
- Safety hooks enforce safety policies (no destructive commands, no bare except)
- Read throttle enforces knowledge graph preference policy
Constitutional Compliance
This harness enforces the Orchestrata constitutional articles:
Article I: Agent Autonomy / Ownership Boundaries
Hooks enforce per-volk file access patterns. The intercept_write.py hook prevents overwriting files owned by other components. The intercept_bash.py hook blocks writes to ~/.claude/ which is system-owned. Enforcement operates at file-system boundaries, not at semantic ownership level.
Article II: Prolog as Single Source of Truth
Governance rules remain in Prolog; the harness does not duplicate governance logic. Error gate checks are structural code quality rules, not policy decisions. Read throttle redirects to knowledge graph which itself is governed by Prolog-backed policies.
Article III: Session Isolation
Session auto-start creates isolated session entries. Context.json maintains per-session state. Audit trail attributes findings to specific session IDs. Session close creates a clean boundary (git tag, archive).
Article IV: Hooks for Extensibility / Transparency
All operations are logged. The audit trail provides a complete record of every tool invocation, every blocked operation, and every deception finding. Diagnostic output goes to stderr for operator visibility.
Article V: Non-Deception / Minimal Core
The deception detector is the primary enforcement mechanism for Article V. It compares claims against actual tool usage, detecting fabricated file creation, command execution, and file reading. Error concealment detection ensures tool failures are reported.
Article VI: Quality Gates
The error gate is the primary enforcement mechanism for Article VI. It blocks session end when code has syntax errors, lint failures, test failures, stubs, deep nesting, or god objects. The block_except_pass hook prevents error-swallowing patterns.
Article VII: Testing Without Mocks
The error gate's test runner (checks_test.py) runs actual pytest tests on changed files. No mock infrastructure is involved. The harness itself follows the no-mocking policy.
Article VIII: Metrics and Observability
Audit logs provide observability into all hook operations. Deception findings provide metrics on AI trustworthiness. Session history in context.json tracks development velocity.
Article IX: Graceful Degradation
All hooks fail gracefully, returning {} on errors. Missing Neo4j returns null results. Missing flake8 skips lint checks. Corrupt JSONL lines are skipped. No hook failure crashes Claude Code.
Article X: Version Compatibility
The install.sh script handles version transitions by overwriting deployed files. Session module stripping (strip_traces.py) decouples deployed code from monorepo version.
Article XI: Documentation First
This specification documents the harness comprehensively before any modifications. Every hook has module-level and function-level docstrings. CLAUDE.md documents hook behavior for every conversation.
Non-Functional Requirements
NFR-001: Performance
- NFR-001-001: Error gate total execution SHALL complete within 120 seconds.
- NFR-001-002: Each error gate check SHALL complete within 30 seconds (CHECK_TIMEOUT).
- NFR-001-003: PreToolUse hooks SHALL complete within 10 seconds.
- NFR-001-004: Read throttle SHALL complete within 5 seconds.
- NFR-001-005: Remember hook Neo4j query SHALL timeout at 15 seconds.
- NFR-001-006: Session auto-start SHALL complete within 5 seconds.
- NFR-001-007: Deception detection SHALL complete within 10 seconds.
NFR-002: Reliability
- NFR-002-001: Hooks SHALL fail gracefully (return
{}) rather than crashing Claude Code. - NFR-002-002: JSON parse errors in stdin SHALL result in
{}output and exit 0. - NFR-002-003: Missing dependencies (flake8, pytest, uv) SHALL be treated as skip, not error.
- NFR-002-004: Missing audit directories SHALL be auto-created on first write.
- NFR-002-005: Corrupt JSONL lines in transcripts SHALL be skipped with warning to stderr.
NFR-003: Data Integrity
- NFR-003-001:
context.jsonSHALL never be trimmed or lose session history. - NFR-003-002: Audit archive SHALL never be deleted.
- NFR-003-003: Deception log SHALL be append-only.
- NFR-003-004: Session JSONL SHALL be moved (not copied) to archive to prevent duplicates.
- NFR-003-005: Git tags SHALL be annotated and pushed to ensure remote persistence.
NFR-004: Maintainability
- NFR-004-001: All hook scripts SHALL have module-level docstrings explaining purpose and event type.
- NFR-004-002: All public functions SHALL have Google-style docstrings with Args and Returns.
- NFR-004-003: Hook source SHALL be version-controlled in
claude/directory. - NFR-004-004: Deployment SHALL be a single command:
./claude/install.sh. - NFR-004-005: Hook registration SHALL be idempotent (safe to re-run install.sh).
Examples
Example 1: Session Auto-Start
When Claude Code starts:
Input: SessionStart event
Action: session_auto_start.py runs
- Checks context.json for active session
- If none active, creates new session entry
- Output: "Session 89 started at 2026-03-29T10:00:00"
Example 2: Error Gate Blocking Session Close
When user attempts to close session with broken code:
Input: Stop event
Action: error_gate_hook.py runs checks
- checks_syntax.py finds syntax error in src/main.py
- Session stop BLOCKED
- Output: {"decision": "block", "reason": "Syntax error in src/main.py..."}
Example 3: Deception Detection Finding
After a session where Claude claims to have created a file but used Bash:
Input: Stop event
Action: stop_auditor.py analyzes transcript
- Finds claim: "Created new_component.py"
- Tools used: ['Bash', 'Read', 'Grep'] (no Write/Edit)
- Logs: {"type": "ACTION_DECEPTION", "severity": "CRITICAL", ...}
- Archives session to ~/.claude/audit/archive/2026-03-29/
Example 4: Read Throttle Blocking
When Claude attempts more than 3 reads in 30 seconds:
Input: PreToolUse(Read) event for src/utils.py
Action: read_throttle.py checks tracker
- 3 reads already in window
- BLOCKED with message: "Use Neo4j queries instead: /code classes <pattern>"
Example 5: Session Close with Full Pipeline
User types "cts":
1. session_close_hook.py detects trigger
2. RTM generation scripts run → plans/rtm.md updated
3. Git log parsed → achievements extracted
4. context.json updated → session marked closed
5. Git tag created → session-089-2026-03-29-1000
6. CHANGELOG.md updated
7. Tag and branch pushed to origin
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
| Dependency | Version | Purpose |
|---|---|---|
| Claude Code CLI | >=1.0 | Runtime environment for hooks |
| Python | 3.14t | Hook script execution (with free-threading) |
| Neo4j | 5.x | Knowledge graph for context injection |
| Redis | 7.x | Session state caching (optional) |
| SWI-Prolog | 9.0.4 | Governance rule engine |
| flake8 | latest | Critical lint checking |
| pytest | latest | Test execution for error gate |
| uv | latest | Python package runner |
| Git | any | Version control for checkpoints and tagging |
| Jinja2 | latest | Template rendering for context.md |
Testing Requirements
Test Scenarios
- TS-001: Verify session auto-start creates entry in context.json
- TS-002: Verify session auto-start skips duplicate on context compaction
- TS-003: Verify "cts" triggers full session close pipeline
- TS-004: Verify error gate blocks on syntax error detection
- TS-005: Verify error gate blocks on lint error detection
- TS-006: Verify error gate blocks on stub method detection
- TS-007: Verify error gate blocks on nesting depth violation
- TS-008: Verify error gate blocks on god object detection
- TS-009: Verify deception detection logs findings to JSONL
- TS-010: Verify read throttle blocks excess reads
- TS-011: Verify intercept_bash blocks rm/unlink commands
- TS-012: Verify intercept_write blocks existing file writes
- TS-013: Verify block_except_pass blocks bare except patterns
- TS-014: Verify remember hook queries Neo4j on trigger phrases
- TS-015: Verify audit logs are written to JSONL files
- TS-016: Verify session JSONL archived on session close
Coverage Targets
- Hook scripts: 90% line coverage
- Error gate checks: 100% coverage on detection logic
- Deception detector: 85% pattern coverage
Quality Gates
- All hooks must return valid JSON to stdout
- All hooks must fail gracefully (no crashes)
- Installation must be idempotent
- No blocking operation without user-friendly error message
Integration Points
Claude Code Integration
- Hook Registration: Hooks registered in
~/.claude/settings.jsonby event type - Input/Output: JSON via stdin/stdout protocol
- Event Types: SessionStart, PreToolUse, PostToolUse, UserPromptSubmit, Stop
Neo4j Integration
- Connection: Via
neo4jPython driver - Credentials: From
NEO4J_URI,NEO4J_USER,NEO4J_PASSWORDenv vars - Queries: Cypher queries for code entities, conversation history, specs
- Timeout: 15 seconds per query
Git Integration
- Checkpoints: Created via
git stashbefore destructive operations - Tags: Annotated tags in format
session-NNN-YYYY-MM-DD-HHMM - Log Parsing: Achievements extracted from
git log --onelinesince last tag
File System Integration
- context.json:
~/.claude/context.json - context.md:
~/.claude/context.md - Audit logs:
~/.claude/audit/sessions/,~/.claude/audit/archive/ - Read tracker:
/tmp/claude-read-tracker.json
Success Criteria
Session Management
- [ ] Sessions auto-start on Claude Code launch
- [ ] Sessions persist state across Claude Code restarts
- [ ] "cts" triggers full close pipeline (RTM, tag, changelog, push)
- [ ] "sns" injects deferred work and metrics
Code Quality Enforcement
- [ ] Error gate runs on every session stop
- [ ] All 6 check categories execute (syntax, lint, tests, stubs, nesting, god objects)
- [ ] Session stop blocked when errors detected
- [ ] Fix instructions injected via additionalContext
Deception Detection
- [ ] All assistant messages analyzed against tool usage
- [ ] CRITICAL findings logged for action deception
- [ ] Session archived after every session close
- [ ] Deception log never deleted
Safety
- [ ] Destructive bash commands blocked
- [ ] Existing file writes blocked (Edit required)
- [ ] Bare except patterns blocked
- [ ] All blocked operations logged
Audit Trail
- [ ] All tool calls recorded in session JSONL
- [ ] All file operations logged
- [ ] Archive data never deleted
- [ ] Subagent sessions audited separately
References
- SPEC-006: Session Management
- SPEC-008: Knowledge Graph
- SPEC-007: Prolog Governance
- Claude Code Hook Documentation
- claude/README.md -- Hook deployment documentation
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
Metrics Requirements
- Performance Metrics
- Operation duration histograms
- Request/error rate counters
- Resource utilization gauges
- Business metrics relevant to domain
Tracing Requirements
- Distributed Tracing
- Trace propagation across component boundaries
- Span attributes for domain-specific context
- Trace sampling based on operational needs
Alerting Requirements
- Critical Alerts
- Error rate spikes
- Performance degradation
- Resource exhaustion
- Business metric anomalies
Dashboard Requirements
- Operational Dashboard
- Real-time system health
- Performance trends
- Error analysis
- Resource monitoring
Revision History
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0.0 | 2026-03-29 | Mohamed | Initial specification |
| 1.1.0 | 2026-04-09 | Mohamed | Fixed to comply with SPEC_STANDARD.md: Added 8-field Metadata (Spec ID, Title, Author, Status, Created, Last Updated, Priority, Version, Extends), added Error Conditions section (ERR-001 to ERR-024 in table format), added Data Models section with JSON schemas, added API Contracts section, added Examples section, added Security Requirements section, added Governance section, added Dependencies section, added Testing Requirements section, added Integration Points section, added Success Criteria section, added Revision History section |
Last Updated: 2026-04-09 Version: 1.1.0 Maintained By: Mohamed