deepflow 0.1.54 → 0.1.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,11 +28,30 @@ INTERRUPTED=false
28
28
  # restarted with --resume to get a fresh context window.
29
29
  CONTEXT_THRESHOLD_PCT=50
30
30
 
31
- # Associative array for per-spec status tracking
32
- # Values: "converged", "halted", "in-progress"
33
- declare -A SPEC_STATUS
34
- # Associative array for per-spec winner slugs
35
- declare -A SPEC_WINNER
31
+ # Per-spec status/winner tracking (bash 3.2 compatible, no associative arrays)
32
+ # Uses a temp directory with one file per key.
33
+ _SPEC_MAP_DIR=""
34
+ _spec_map_init() {
35
+ if [[ -z "$_SPEC_MAP_DIR" ]]; then
36
+ _SPEC_MAP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/deepflow-spec-map.XXXXXX")"
37
+ fi
38
+ }
39
+ _spec_set() { # usage: _spec_set STATUS myspec converged
40
+ _spec_map_init
41
+ printf '%s' "$3" > "${_SPEC_MAP_DIR}/${1}__${2}"
42
+ }
43
+ _spec_get() { # usage: _spec_get STATUS myspec [default]
44
+ _spec_map_init
45
+ local f="${_SPEC_MAP_DIR}/${1}__${2}"
46
+ if [[ -f "$f" ]]; then cat "$f"; else printf '%s' "${3:-}"; fi
47
+ }
48
+ _spec_isset() { # usage: _spec_isset STATUS myspec
49
+ _spec_map_init
50
+ [[ -f "${_SPEC_MAP_DIR}/${1}__${2}" ]]
51
+ }
52
+ _spec_map_cleanup() {
53
+ [[ -n "$_SPEC_MAP_DIR" && -d "$_SPEC_MAP_DIR" ]] && rm -rf "$_SPEC_MAP_DIR"
54
+ }
36
55
 
37
56
  # ---------------------------------------------------------------------------
38
57
  # Logging
@@ -436,6 +455,7 @@ run_single_spike() {
436
455
  local slug="$2"
437
456
  local hypothesis="$3"
438
457
  local method="$4"
458
+ local spec_file="$5"
439
459
 
440
460
  local worktree_path="${PROJECT_ROOT}/.deepflow/worktrees/${spec_name}-${slug}"
441
461
  local branch_name="df/${spec_name}-${slug}"
@@ -455,6 +475,12 @@ run_single_spike() {
455
475
  }
456
476
  fi
457
477
 
478
+ # Extract acceptance criteria from spec (the human's judgment proxy)
479
+ local acceptance_criteria=""
480
+ if [[ -f "$spec_file" ]]; then
481
+ acceptance_criteria="$(sed -n '/^## Acceptance Criteria/,/^## /{ /^## Acceptance Criteria/d; /^## /d; p; }' "$spec_file")"
482
+ fi
483
+
458
484
  # Build spike prompt
459
485
  local spike_prompt
460
486
  spike_prompt="You are running a spike experiment to validate a hypothesis for spec '${spec_name}'.
@@ -465,13 +491,19 @@ Hypothesis: ${hypothesis}
465
491
  Method: ${method}
466
492
  --- END HYPOTHESIS ---
467
493
 
494
+ --- ACCEPTANCE CRITERIA (from spec — the human's judgment proxy) ---
495
+ ${acceptance_criteria}
496
+ --- END ACCEPTANCE CRITERIA ---
497
+
468
498
  Your tasks:
469
499
  1. Validate this hypothesis by implementing the minimum necessary to prove or disprove it.
500
+ The spike must demonstrate that the approach can satisfy the acceptance criteria above.
470
501
  2. Write an experiment file at: .deepflow/experiments/${spec_name}--${slug}--active.md
471
502
  The experiment file should contain:
472
503
  - ## Hypothesis: restate the hypothesis
473
504
  - ## Method: what you did to validate
474
505
  - ## Results: what you observed
506
+ - ## Criteria Check: for each acceptance criterion, can this approach satisfy it? (yes/no/unclear)
475
507
  - ## Conclusion: PASSED or FAILED with reasoning
476
508
  3. Write a result YAML file at: .deepflow/results/spike-${slug}.yaml
477
509
  The YAML must contain:
@@ -573,7 +605,7 @@ run_spikes() {
573
605
  auto_log "Spawning spike for ${slug} (hypothesis ${i}/${count})"
574
606
  echo "Spawning spike: ${slug}"
575
607
 
576
- run_single_spike "$spec_name" "$slug" "$hypothesis" "$method" &
608
+ run_single_spike "$spec_name" "$slug" "$hypothesis" "$method" "$spec_file" &
577
609
  pids+=($!)
578
610
  done
579
611
 
@@ -875,6 +907,13 @@ $(cat "$experiment_file")
875
907
  # -----------------------------------------------------------------------
876
908
  # 2. Build selection prompt
877
909
  # -----------------------------------------------------------------------
910
+
911
+ # Extract acceptance criteria from spec (the human's judgment proxy)
912
+ local acceptance_criteria=""
913
+ if [[ -f "$spec_file" ]]; then
914
+ acceptance_criteria="$(sed -n '/^## Acceptance Criteria/,/^## /{ /^## Acceptance Criteria/d; /^## /d; p; }' "$spec_file")"
915
+ fi
916
+
878
917
  local selection_prompt
879
918
  selection_prompt="You are an adversarial quality judge in an autonomous development workflow.
880
919
  Your job is to compare implementation approaches for spec '${spec_name}' and select the best one — or reject all if quality is insufficient.
@@ -883,6 +922,11 @@ IMPORTANT:
883
922
  - This selection phase ALWAYS runs, even with only 1 approach. With a single approach you act as a quality gate.
884
923
  - You CAN and SHOULD reject all approaches if the quality is insufficient. Do not rubber-stamp poor work.
885
924
  - Base your judgment ONLY on the artifacts provided below. Do NOT read code files.
925
+ - Judge each approach against the ACCEPTANCE CRITERIA below — these represent the human's intent.
926
+
927
+ --- ACCEPTANCE CRITERIA (from spec) ---
928
+ ${acceptance_criteria}
929
+ --- END ACCEPTANCE CRITERIA ---
886
930
 
887
931
  There are ${#approach_slugs[@]} approach(es) to evaluate:
888
932
 
@@ -1025,18 +1069,18 @@ generate_report() {
1025
1069
  all_spec_names+=("$sname")
1026
1070
 
1027
1071
  # If status was not set by the main loop, determine it now
1028
- if [[ -z "${SPEC_STATUS[$sname]+x}" ]]; then
1072
+ if ! _spec_isset STATUS "$sname"; then
1029
1073
  # Check if winner file exists
1030
1074
  if [[ -f "${PROJECT_ROOT}/.deepflow/selection/${sname}-winner.json" ]]; then
1031
- SPEC_STATUS[$sname]="converged"
1075
+ _spec_set STATUS "$sname" "converged"
1032
1076
  # Extract winner slug
1033
1077
  local w_slug
1034
1078
  w_slug="$(grep -o '"winner"[[:space:]]*:[[:space:]]*"[^"]*"' "${PROJECT_ROOT}/.deepflow/selection/${sname}-winner.json" | sed 's/.*"winner"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')" || w_slug=""
1035
- SPEC_WINNER[$sname]="$w_slug"
1079
+ _spec_set WINNER "$sname" "$w_slug"
1036
1080
  elif [[ "$INTERRUPTED" == "true" ]]; then
1037
- SPEC_STATUS[$sname]="in-progress"
1081
+ _spec_set STATUS "$sname" "in-progress"
1038
1082
  else
1039
- SPEC_STATUS[$sname]="halted"
1083
+ _spec_set STATUS "$sname" "halted"
1040
1084
  fi
1041
1085
  fi
1042
1086
  done
@@ -1045,15 +1089,15 @@ generate_report() {
1045
1089
  # If interrupted, override any unfinished specs
1046
1090
  if [[ "$INTERRUPTED" == "true" ]]; then
1047
1091
  for sname in "${all_spec_names[@]}"; do
1048
- if [[ "${SPEC_STATUS[$sname]}" != "converged" ]]; then
1049
- SPEC_STATUS[$sname]="in-progress"
1092
+ if [[ "$(_spec_get STATUS "$sname")" != "converged" ]]; then
1093
+ _spec_set STATUS "$sname" "in-progress"
1050
1094
  fi
1051
1095
  done
1052
1096
  overall_status="in-progress"
1053
1097
  else
1054
1098
  # Determine overall status from per-spec statuses
1055
1099
  for sname in "${all_spec_names[@]}"; do
1056
- local s="${SPEC_STATUS[$sname]}"
1100
+ local s="$(_spec_get STATUS "$sname")"
1057
1101
  if [[ "$s" == "halted" ]]; then
1058
1102
  overall_status="halted"
1059
1103
  elif [[ "$s" == "in-progress" ]]; then
@@ -1075,7 +1119,7 @@ generate_report() {
1075
1119
  # Winner info (if converged)
1076
1120
  if [[ "$overall_status" == "converged" ]]; then
1077
1121
  for sname in "${all_spec_names[@]}"; do
1078
- local w="${SPEC_WINNER[$sname]:-}"
1122
+ local w="$(_spec_get WINNER "$sname")"
1079
1123
  if [[ -n "$w" ]]; then
1080
1124
  local summary=""
1081
1125
  local winner_file="${PROJECT_ROOT}/.deepflow/selection/${sname}-winner.json"
@@ -1092,8 +1136,8 @@ generate_report() {
1092
1136
  echo "| Spec | Status | Winner |"
1093
1137
  echo "|------|--------|--------|"
1094
1138
  for sname in "${all_spec_names[@]}"; do
1095
- local s="${SPEC_STATUS[$sname]:-unknown}"
1096
- local w="${SPEC_WINNER[$sname]:--}"
1139
+ local s="$(_spec_get STATUS "$sname" "unknown")"
1140
+ local w="$(_spec_get WINNER "$sname" "-")"
1097
1141
  echo "| ${sname} | ${s} | ${w} |"
1098
1142
  done
1099
1143
  echo ""
@@ -1104,7 +1148,7 @@ generate_report() {
1104
1148
 
1105
1149
  local has_changes=false
1106
1150
  for sname in "${all_spec_names[@]}"; do
1107
- local w="${SPEC_WINNER[$sname]:-}"
1151
+ local w="$(_spec_get WINNER "$sname")"
1108
1152
  if [[ -n "$w" ]]; then
1109
1153
  has_changes=true
1110
1154
  local branch_name="df/${sname}-${w}"
@@ -1171,10 +1215,10 @@ run_spec_cycle() {
1171
1215
  auto_log "Selection accepted for ${spec_file} at cycle ${cycle}"
1172
1216
  echo "Selection accepted for $(basename "$spec_file") at cycle ${cycle}"
1173
1217
  # Track convergence
1174
- SPEC_STATUS[$spec_name]="converged"
1218
+ _spec_set STATUS "$spec_name" "converged"
1175
1219
  local w_slug
1176
1220
  w_slug="$(grep -o '"winner"[[:space:]]*:[[:space:]]*"[^"]*"' "${PROJECT_ROOT}/.deepflow/selection/${spec_name}-winner.json" | sed 's/.*"winner"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')" || w_slug=""
1177
- SPEC_WINNER[$spec_name]="$w_slug"
1221
+ _spec_set WINNER "$spec_name" "$w_slug"
1178
1222
  break
1179
1223
  fi
1180
1224
 
@@ -1183,8 +1227,8 @@ run_spec_cycle() {
1183
1227
  done
1184
1228
 
1185
1229
  # If we exited the loop without converging, mark as halted
1186
- if [[ "${SPEC_STATUS[$spec_name]:-}" != "converged" ]]; then
1187
- SPEC_STATUS[$spec_name]="halted"
1230
+ if [[ "$(_spec_get STATUS "$spec_name")" != "converged" ]]; then
1231
+ _spec_set STATUS "$spec_name" "halted"
1188
1232
  fi
1189
1233
  }
1190
1234
 
@@ -1257,6 +1301,7 @@ handle_signal() {
1257
1301
  }
1258
1302
 
1259
1303
  trap handle_signal SIGINT SIGTERM
1304
+ trap _spec_map_cleanup EXIT
1260
1305
 
1261
1306
  # Safety assertions: this script must never modify spec files or push to remotes.
1262
1307
  # These constraints are enforced by design — no write/push commands exist in this script.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepflow",
3
- "version": "0.1.54",
3
+ "version": "0.1.56",
4
4
  "description": "Stay in flow state - lightweight spec-driven task orchestration for Claude Code",
5
5
  "keywords": [
6
6
  "claude",