Skip to content

Commit b03ea2c

Browse files
committed
Fix lifecycle self improvement workflows
1 parent 8ede030 commit b03ea2c

35 files changed

Lines changed: 1127 additions & 32 deletions

codex/codex-config.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ github_cli:
55
owner_token_env:
66
example-owner: GH_EXAMPLE_TOKEN
77

8+
git_commit:
9+
allowed_env_sample_files:
10+
- development.env
11+
- develop.env
12+
813
notes:
914
- "Update this manifest only when the repository's canonical commands change."
1015
- "Tasks must also copy the chosen commands into /tasks/<task-name>/spec.md under Verification Commands."
@@ -26,6 +31,8 @@ notes:
2631

2732

2833

34+
35+
2936
# PREPARE-TAKEOFF BOOTSTRAP START
3037
bootstrap:
3138
codex_root: "/Users/eric/side-projects/prompts/codex"

codex/project-structure.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
- Maintain canonical codex settings in `codex/codex-config.yaml`.
4040
- Keep this repository structure reference current in `codex/project-structure.md`.
4141
- Scaffold task artifacts via `./codex/scripts/task-scaffold.sh <task-name>`.
42+
- Task scaffolding materializes `tasks/<task-name>/complexity-signals.json` from `codex/tasks/_templates/complexity-signals.template.json`; Stage 3 can also remediate the missing file from the same template.
4243

4344
## Verification
4445
- Lint: `not-configured`

codex/prompts/acac.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Strict trigger: the first non-empty line must be exactly `#ACAC` (case-sensitive
66

77
Use the `acac` skill.
88

9+
After goals are approved, successful intermediate stage verdicts are continuation gates. Do not stop after `READY FOR PLANNING`, `READY FOR IMPLEMENTATION`, or `READY TO LAND` unless a stage emits `BLOCKED` or the user explicitly pauses the run.
10+
911
Inputs:
1012

1113
- Request body after `#ACAC`: `{{USER_REQUEST_AFTER_ACAC}}`

codex/scripts/git-commit-preflight.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ if ! upstream="$(git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/n
2525
fi
2626

2727
echo "Abort: branch '$branch' has no upstream and must be pushed first."
28-
echo "Run the configured git push helper for this branch."
28+
echo "Recovery: run ./codex/scripts/git-push-branch-safe.sh '$branch' after confirming this is the intended PR head branch, then rerun preflight."
2929
exit 1
3030
fi
3131

codex/scripts/git-stage-safe.sh

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,50 @@ if [[ "$#" -eq 0 ]]; then
1313
exit 2
1414
fi
1515

16+
repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
17+
config_file="${repo_root}/codex/codex-config.yaml"
18+
19+
is_env_like_file() {
20+
case "$1" in
21+
.env|.env.*|*.env|*.env.*)
22+
return 0
23+
;;
24+
esac
25+
return 1
26+
}
27+
28+
is_allowed_env_sample_file() {
29+
local path="$1"
30+
31+
if [[ "$path" == "development.env" ]]; then
32+
return 0
33+
fi
34+
35+
if [[ -f "$config_file" ]] && awk '
36+
/^[[:space:]]*allowed_env_sample_files:[[:space:]]*$/ {in_list=1; next}
37+
in_list && /^[^[:space:]-]/ {exit}
38+
in_list && /^[[:space:]]*-[[:space:]]*/ {
39+
value=$0
40+
sub(/^[[:space:]]*-[[:space:]]*/, "", value)
41+
gsub(/^"|"$/, "", value)
42+
gsub(/^'\''|'\''$/, "", value)
43+
print value
44+
}
45+
' "$config_file" | grep -Fx -- "$path" >/dev/null 2>&1; then
46+
git ls-files --error-unmatch -- "$path" >/dev/null 2>&1
47+
return
48+
fi
49+
50+
return 1
51+
}
52+
53+
for path in "$@"; do
54+
base="${path##*/}"
55+
if is_env_like_file "$base" && ! is_allowed_env_sample_file "$path"; then
56+
echo "Abort: refusing to stage env-like file '${path}'."
57+
echo "Allowed sample env files must be listed in codex/codex-config.yaml and already tracked, or be exactly development.env."
58+
exit 1
59+
fi
60+
done
61+
1662
git add -- "$@"

codex/scripts/git-track-safe-untracked.sh

Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,95 @@ eligible=()
88
skipped=()
99
needs_permission=()
1010
auto_image_count=0
11+
TASK_NAME="${1:-${CODEX_TASK_NAME:-}}"
12+
13+
usage() {
14+
echo "Usage (canonical): ./.codex/scripts/git-track-safe-untracked.sh [task-name]"
15+
echo "Usage (repo-local fallback): ./codex/scripts/git-track-safe-untracked.sh [task-name]"
16+
echo "Usage (home fallback): ${HOME}/.codex/scripts/git-track-safe-untracked.sh [task-name]"
17+
}
18+
19+
if [ "$#" -gt 1 ]; then
20+
echo "Abort: too many arguments."
21+
usage
22+
exit 2
23+
fi
24+
25+
if [ -n "$TASK_NAME" ] && ! [[ "$TASK_NAME" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then
26+
echo "Abort: task-name must be kebab-case using lowercase letters, digits, and hyphens only."
27+
exit 2
28+
fi
1129

1230
is_exception_env_file() {
13-
[ "$1" = "development.env" ]
31+
local path="$1"
32+
local repo_root
33+
local config_file
34+
35+
[ "$path" = "development.env" ] && return 0
36+
37+
repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
38+
config_file="${repo_root}/codex/codex-config.yaml"
39+
[ -f "$config_file" ] || return 1
40+
41+
awk '
42+
/^[[:space:]]*allowed_env_sample_files:[[:space:]]*$/ {in_list=1; next}
43+
in_list && /^[^[:space:]-]/ {exit}
44+
in_list && /^[[:space:]]*-[[:space:]]*/ {
45+
value=$0
46+
sub(/^[[:space:]]*-[[:space:]]*/, "", value)
47+
gsub(/^"|"$/, "", value)
48+
gsub(/^'\''|'\''$/, "", value)
49+
print value
50+
}
51+
' "$config_file" | grep -Fx -- "$path" >/dev/null 2>&1
52+
}
53+
54+
infer_task_name_from_untracked() {
55+
local inferred=""
56+
local candidate=""
57+
58+
while IFS= read -r -d '' path; do
59+
case "$path" in
60+
tasks/*/*|goals/*/*)
61+
candidate="${path#*/}"
62+
candidate="${candidate%%/*}"
63+
;;
64+
*)
65+
continue
66+
;;
67+
esac
68+
69+
if ! [[ "$candidate" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then
70+
continue
71+
fi
72+
73+
if [ -z "$inferred" ]; then
74+
inferred="$candidate"
75+
elif [ "$inferred" != "$candidate" ]; then
76+
echo ""
77+
return 0
78+
fi
79+
done < <(git ls-files -o --exclude-standard -z)
80+
81+
echo "$inferred"
82+
}
83+
84+
if [ -z "$TASK_NAME" ]; then
85+
TASK_NAME="$(infer_task_name_from_untracked)"
86+
fi
87+
88+
is_task_owned_untracked_path() {
89+
local path="$1"
90+
91+
[ -n "$TASK_NAME" ] || return 1
92+
93+
case "$path" in
94+
"tasks/${TASK_NAME}/"*|"goals/${TASK_NAME}/"*)
95+
return 0
96+
;;
97+
esac
98+
99+
return 1
14100
}
15101

16102
is_env_like_file() {
@@ -87,6 +173,11 @@ while IFS= read -r -d '' path; do
87173
continue
88174
fi
89175

176+
if ! is_task_owned_untracked_path "$path"; then
177+
needs_permission+=("$path (untracked path outside task scope)")
178+
continue
179+
fi
180+
90181
case "$lower_path" in
91182
*.mp4|*.mp3)
92183
skipped+=("$path (video/audio not allowed)")
@@ -144,7 +235,7 @@ fi
144235

145236
if [ "${#needs_permission[@]}" -gt 0 ]; then
146237
echo
147-
echo "Permission-required image files (not added): ${#needs_permission[@]}"
238+
echo "Permission-required files (not added): ${#needs_permission[@]}"
148239
printf '%s\n' "${needs_permission[@]}"
149-
echo "Action required: request explicit user approval before adding these files."
240+
echo "Action required: request explicit user approval before adding these files, or stage intended paths explicitly with git-stage-safe.sh."
150241
fi

codex/scripts/prepare-phased-impl-scaffold.sh

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,15 @@ else
132132
fi
133133

134134
if [[ ! -f "${COMPLEXITY_SIGNALS_FILE}" ]]; then
135-
echo "Abort: missing task complexity signals file ${COMPLEXITY_SIGNALS_FILE}."
136-
echo "Remediation: create ${COMPLEXITY_SIGNALS_FILE} and re-run prepare-phased-impl-scaffold.sh."
137-
exit 1
135+
if CODEX_ROOT_FOR_SIGNALS="$(resolve_codex_root tasks/_templates/complexity-signals.template.json)"; then
136+
signals_template="${CODEX_ROOT_FOR_SIGNALS}/tasks/_templates/complexity-signals.template.json"
137+
cp "${signals_template}" "${COMPLEXITY_SIGNALS_FILE}"
138+
echo "Materialized missing task complexity signals from template: ${COMPLEXITY_SIGNALS_FILE}"
139+
else
140+
echo "Abort: missing task complexity signals file ${COMPLEXITY_SIGNALS_FILE}."
141+
echo "Remediation: create ${COMPLEXITY_SIGNALS_FILE} from codex/tasks/_templates/complexity-signals.template.json and re-run prepare-phased-impl-scaffold.sh."
142+
exit 1
143+
fi
138144
fi
139145

140146
if [[ ! -f "${SELECTED_SIGNALS_FILE}" ]]; then

codex/scripts/prepare-takeoff-worktree.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,5 @@ echo "Uncommitted entries: ${STATUS_COUNT}"
101101
if [[ "${STATUS_COUNT}" -gt 0 ]]; then
102102
echo "Status summary:"
103103
printf '%s\n' "${STATUS_PORCELAIN}" | sed '/^$/d' | sed 's/^/ /'
104+
echo "Dirty worktree decision required before downstream progression: record continue, isolate, or stop in the task spec."
104105
fi

codex/scripts/revalidate-code-review.sh

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -94,24 +94,30 @@ BASE_BRANCH="$(resolve_base_branch)"
9494
DIFF_MODE="base-branch"
9595
DIFF_COMMAND="git diff ${BASE_BRANCH}...HEAD"
9696

97-
if DIFF_TEXT="$(git diff "${BASE_BRANCH}...HEAD" 2>/dev/null)"; then
98-
:
97+
STAGED_DIFF_TEXT="$(git diff --cached 2>/dev/null || true)"
98+
UNSTAGED_DIFF_TEXT="$(git diff 2>/dev/null || true)"
99+
WORKTREE_DIFF_TEXT="${STAGED_DIFF_TEXT}${UNSTAGED_DIFF_TEXT}"
100+
101+
if [[ -n "${WORKTREE_DIFF_TEXT}" ]]; then
102+
DIFF_MODE="working-tree"
103+
DIFF_COMMAND="git diff --cached && git diff"
104+
DIFF_TEXT="${WORKTREE_DIFF_TEXT}"
105+
CHANGED_FILES="$({
106+
git diff --cached --name-only 2>/dev/null || true
107+
git diff --name-only 2>/dev/null || true
108+
} | sed '/^$/d' | sort -u)"
109+
HUNKS="$({
110+
git diff --cached -U0 2>/dev/null || true
111+
git diff -U0 2>/dev/null || true
112+
})"
99113
else
100-
DIFF_TEXT=""
101-
fi
102-
103-
if [[ -z "${DIFF_TEXT}" ]]; then
104-
DIFF_MODE="fallback"
105-
DIFF_COMMAND="git diff"
106-
DIFF_TEXT="$(git diff 2>/dev/null || true)"
107-
fi
108-
109-
if [[ "${DIFF_MODE}" == "base-branch" ]]; then
114+
if DIFF_TEXT="$(git diff "${BASE_BRANCH}...HEAD" 2>/dev/null)"; then
115+
:
116+
else
117+
DIFF_TEXT=""
118+
fi
110119
CHANGED_FILES="$(git diff --name-only "${BASE_BRANCH}...HEAD" 2>/dev/null || true)"
111120
HUNKS="$(git diff -U0 "${BASE_BRANCH}...HEAD" 2>/dev/null || true)"
112-
else
113-
CHANGED_FILES="$(git diff --name-only 2>/dev/null || true)"
114-
HUNKS="$(git diff -U0 2>/dev/null || true)"
115121
fi
116122

117123
CITATIONS="$(printf '%s\n' "${HUNKS}" | awk '

codex/scripts/task-scaffold.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ fi
5858
SPEC_TPL="${TEMPLATES_DIR}/spec.template.md"
5959
PHASE_TPL="${TEMPLATES_DIR}/phase.template.md"
6060
FINAL_TPL="${TEMPLATES_DIR}/final-phase.template.md"
61+
COMPLEXITY_SIGNALS_TPL="${TEMPLATES_DIR}/complexity-signals.template.json"
6162

6263
mkdir -p "${TASK_DIR}"
6364

@@ -92,6 +93,13 @@ if [[ ! -f "${TASK_DIR}/final-phase.md" ]]; then
9293
created_files+=("${TASK_DIR}/final-phase.md")
9394
fi
9495

96+
# complexity-signals.template.json -> complexity-signals.json
97+
# This is a neutral planning input template; scoring remains gated by Stage 3.
98+
if [[ -f "${COMPLEXITY_SIGNALS_TPL}" && ! -f "${TASK_DIR}/complexity-signals.json" ]]; then
99+
cp "${COMPLEXITY_SIGNALS_TPL}" "${TASK_DIR}/complexity-signals.json"
100+
created_files+=("${TASK_DIR}/complexity-signals.json")
101+
fi
102+
95103
# Materialize any additional *.template.md files (beyond known ones)
96104
shopt -s nullglob
97105
for tpl in "${TEMPLATES_DIR}"/*.template.md; do

0 commit comments

Comments
 (0)