Skip to content
This repository was archived by the owner on Apr 11, 2026. It is now read-only.

Commit 783b80e

Browse files
z23ccclaude
andcommitted
fix(fn-110.8): consolidate hooks, add phase skipping, remove schema_version from output
- Reduce ralph-guard hook registrations from 4 to 2 (PreToolUse Bash, Stop) - Remove ralph-guard from PostToolUse, SubagentStop, and PreToolUse Edit|Write - Add 60-second filesystem check cache for commit-gate hook - Add is_memory_enabled() config reader; phase 11 (Memory Auto-Save) now skipped when memory.enabled is false/unset (default: false) - Phase 9 (Outputs) and Phase 4 (TDD) already skip via config/flags - Add skip_serializing to schema_version on Epic and Task structs so it no longer appears in serialized JSON output or disk files Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 737017c commit 783b80e

3 files changed

Lines changed: 45 additions & 35 deletions

File tree

flowctl/crates/flowctl-cli/src/commands/workflow/phase.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,25 +104,65 @@ fn is_outputs_enabled() -> bool {
104104
}
105105
}
106106

107+
/// Read `memory.enabled` from .flow/config.json. Default: false.
108+
fn is_memory_enabled() -> bool {
109+
use flowctl_core::types::{CONFIG_FILE, FLOW_DIR};
110+
let cfg_path = std::env::current_dir()
111+
.unwrap_or_else(|_| std::path::PathBuf::from("."))
112+
.join(FLOW_DIR)
113+
.join(CONFIG_FILE);
114+
if !cfg_path.exists() {
115+
return false;
116+
}
117+
match std::fs::read_to_string(&cfg_path) {
118+
Ok(content) => {
119+
let cfg: serde_json::Value =
120+
serde_json::from_str(&content).unwrap_or(serde_json::json!({}));
121+
cfg.get("memory")
122+
.and_then(|m| m.get("enabled"))
123+
.and_then(serde_json::Value::as_bool)
124+
.unwrap_or(false)
125+
}
126+
Err(_) => false,
127+
}
128+
}
129+
107130
/// Build the phase sequence based on mode flags.
131+
/// Skips phases based on config:
132+
/// - Phase 4 (TDD): only included when --tdd flag is set
133+
/// - Phase 9 (Outputs): only included when outputs.enabled is true (default)
134+
/// - Phase 11 (Memory): only included when memory.enabled is true (default: false)
108135
fn build_phase_sequence(tdd: bool, review: bool) -> Vec<&'static str> {
109136
let mut phases = HashSet::new();
137+
// Start with default sequence (excludes phase 11 — added conditionally below)
110138
for p in PHASE_SEQ_DEFAULT {
139+
if *p == "11" {
140+
continue; // handled by is_memory_enabled() below
141+
}
111142
phases.insert(*p);
112143
}
113144
if tdd {
114145
for p in PHASE_SEQ_TDD {
146+
if *p == "11" {
147+
continue;
148+
}
115149
phases.insert(*p);
116150
}
117151
}
118152
if review {
119153
for p in PHASE_SEQ_REVIEW {
154+
if *p == "11" {
155+
continue;
156+
}
120157
phases.insert(*p);
121158
}
122159
}
123160
if is_outputs_enabled() {
124161
phases.insert("9");
125162
}
163+
if is_memory_enabled() {
164+
phases.insert("11");
165+
}
126166
CANONICAL_ORDER.iter().copied().filter(|p| phases.contains(p)).collect()
127167
}
128168

flowctl/crates/flowctl-core/src/types.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ impl std::fmt::Display for ReviewStatus {
119119
#[derive(Debug, Clone, Serialize, Deserialize)]
120120
pub struct Epic {
121121
/// Schema version for forward compatibility.
122-
#[serde(default = "default_schema_version")]
122+
#[serde(default = "default_schema_version", skip_serializing)]
123123
pub schema_version: u32,
124124

125125
/// Unique ID, e.g. `fn-1-add-auth`.
@@ -191,7 +191,7 @@ pub struct Epic {
191191
#[derive(Debug, Clone, Serialize, Deserialize)]
192192
pub struct Task {
193193
/// Schema version for forward compatibility.
194-
#[serde(default = "default_schema_version")]
194+
#[serde(default = "default_schema_version", skip_serializing)]
195195
pub schema_version: u32,
196196

197197
/// Unique ID, e.g. `fn-1-add-auth.3`.

hooks/hooks.json

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -39,34 +39,14 @@
3939
"hooks": [
4040
{
4141
"type": "command",
42-
"command": "F=\"${DROID_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}/bin/flowctl\"; { [ ! -d .flow ] || [ ! -x \"$F\" ]; } || \"$F\" hook commit-gate",
42+
"command": "F=\"${DROID_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}/bin/flowctl\"; C=\"${TMPDIR:-/tmp}/flowctl-commit-gate-ts\"; { [ ! -d .flow ] || [ ! -x \"$F\" ]; } && exit 0; [ -f \"$C\" ] && [ $(( $(date +%s) - $(cat \"$C\" 2>/dev/null || echo 0) )) -lt 60 ] && exit 0; \"$F\" hook commit-gate; echo $(date +%s) > \"$C\"",
4343
"timeout": 10
4444
}
4545
],
46-
"description": "Block git commit when flow task is in_progress but guard has not passed"
47-
},
48-
{
49-
"matcher": "Edit|Write",
50-
"hooks": [
51-
{
52-
"type": "command",
53-
"command": "F=\"${DROID_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}/bin/flowctl\"; [ -x \"$F\" ] && \"$F\" hook ralph-guard || true",
54-
"timeout": 5
55-
}
56-
]
46+
"description": "Block git commit when flow task is in_progress but guard has not passed (cached 60s)"
5747
}
5848
],
5949
"PostToolUse": [
60-
{
61-
"matcher": "Bash|Execute",
62-
"hooks": [
63-
{
64-
"type": "command",
65-
"command": "F=\"${DROID_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}/bin/flowctl\"; [ -x \"$F\" ] && \"$F\" hook ralph-guard || true",
66-
"timeout": 5
67-
}
68-
]
69-
},
7050
{
7151
"matcher": "Bash",
7252
"hooks": [
@@ -100,17 +80,7 @@
10080
"description": "Auto-capture session memories to .flow/memory/ (requires memory.enabled or memory.auto in config)"
10181
}
10282
],
103-
"SubagentStop": [
104-
{
105-
"hooks": [
106-
{
107-
"type": "command",
108-
"command": "F=\"${DROID_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}/bin/flowctl\"; [ -x \"$F\" ] && \"$F\" hook ralph-guard || true",
109-
"timeout": 5
110-
}
111-
]
112-
}
113-
],
83+
"SubagentStop": [],
11484
"SubagentStart": [
11585
{
11686
"matcher": "",

0 commit comments

Comments
 (0)