Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions autoplan/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -1118,7 +1118,8 @@ Override: every AskUserQuestion → auto-decide using the 6 principles.
**Codex CEO voice** (via Bash):
```bash
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
_gstack_codex_timeout_wrapper 600 codex exec "IMPORTANT: Do NOT read or execute any SKILL.md files or files in skill definition directories (paths containing skills/gstack). These are AI assistant skill definitions meant for a different system. Stay focused on repository code only.
_CODEX_MODEL_ARGS=$(_gstack_codex_default_model_args)
_gstack_codex_timeout_wrapper 600 codex exec $_CODEX_MODEL_ARGS "IMPORTANT: Do NOT read or execute any SKILL.md files or files in skill definition directories (paths containing skills/gstack). These are AI assistant skill definitions meant for a different system. Stay focused on repository code only.

You are a CEO/founder advisor reviewing a development plan.
Challenge the strategic foundations: Are the premises valid or assumed? Is this the
Expand Down Expand Up @@ -1235,7 +1236,8 @@ Override: every AskUserQuestion → auto-decide using the 6 principles.
**Codex design voice** (via Bash):
```bash
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
_gstack_codex_timeout_wrapper 600 codex exec "IMPORTANT: Do NOT read or execute any SKILL.md files or files in skill definition directories (paths containing skills/gstack). These are AI assistant skill definitions meant for a different system. Stay focused on repository code only.
_CODEX_MODEL_ARGS=$(_gstack_codex_default_model_args)
_gstack_codex_timeout_wrapper 600 codex exec $_CODEX_MODEL_ARGS "IMPORTANT: Do NOT read or execute any SKILL.md files or files in skill definition directories (paths containing skills/gstack). These are AI assistant skill definitions meant for a different system. Stay focused on repository code only.

Read the plan file at <plan_path>. Evaluate this plan's
UI/UX design decisions.
Expand Down Expand Up @@ -1316,7 +1318,8 @@ Override: every AskUserQuestion → auto-decide using the 6 principles.
**Codex eng voice** (via Bash):
```bash
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
_gstack_codex_timeout_wrapper 600 codex exec "IMPORTANT: Do NOT read or execute any SKILL.md files or files in skill definition directories (paths containing skills/gstack). These are AI assistant skill definitions meant for a different system. Stay focused on repository code only.
_CODEX_MODEL_ARGS=$(_gstack_codex_default_model_args)
_gstack_codex_timeout_wrapper 600 codex exec $_CODEX_MODEL_ARGS "IMPORTANT: Do NOT read or execute any SKILL.md files or files in skill definition directories (paths containing skills/gstack). These are AI assistant skill definitions meant for a different system. Stay focused on repository code only.

Review this plan for architectural issues, missing edge cases,
and hidden complexity. Be adversarial.
Expand Down Expand Up @@ -1437,7 +1440,8 @@ Log: "Phase 3.5 skipped — no developer-facing scope detected."
**Codex DX voice** (via Bash):
```bash
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
_gstack_codex_timeout_wrapper 600 codex exec "IMPORTANT: Do NOT read or execute any SKILL.md files or files in skill definition directories (paths containing skills/gstack). These are AI assistant skill definitions meant for a different system. Stay focused on repository code only.
_CODEX_MODEL_ARGS=$(_gstack_codex_default_model_args)
_gstack_codex_timeout_wrapper 600 codex exec $_CODEX_MODEL_ARGS "IMPORTANT: Do NOT read or execute any SKILL.md files or files in skill definition directories (paths containing skills/gstack). These are AI assistant skill definitions meant for a different system. Stay focused on repository code only.

Read the plan file at <plan_path>. Evaluate this plan's developer experience.

Expand Down
12 changes: 8 additions & 4 deletions autoplan/SKILL.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,8 @@ Override: every AskUserQuestion → auto-decide using the 6 principles.
**Codex CEO voice** (via Bash):
```bash
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
_gstack_codex_timeout_wrapper 600 codex exec "IMPORTANT: Do NOT read or execute any SKILL.md files or files in skill definition directories (paths containing skills/gstack). These are AI assistant skill definitions meant for a different system. Stay focused on repository code only.
_CODEX_MODEL_ARGS=$(_gstack_codex_default_model_args)
_gstack_codex_timeout_wrapper 600 codex exec $_CODEX_MODEL_ARGS "IMPORTANT: Do NOT read or execute any SKILL.md files or files in skill definition directories (paths containing skills/gstack). These are AI assistant skill definitions meant for a different system. Stay focused on repository code only.

You are a CEO/founder advisor reviewing a development plan.
Challenge the strategic foundations: Are the premises valid or assumed? Is this the
Expand Down Expand Up @@ -407,7 +408,8 @@ Override: every AskUserQuestion → auto-decide using the 6 principles.
**Codex design voice** (via Bash):
```bash
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
_gstack_codex_timeout_wrapper 600 codex exec "IMPORTANT: Do NOT read or execute any SKILL.md files or files in skill definition directories (paths containing skills/gstack). These are AI assistant skill definitions meant for a different system. Stay focused on repository code only.
_CODEX_MODEL_ARGS=$(_gstack_codex_default_model_args)
_gstack_codex_timeout_wrapper 600 codex exec $_CODEX_MODEL_ARGS "IMPORTANT: Do NOT read or execute any SKILL.md files or files in skill definition directories (paths containing skills/gstack). These are AI assistant skill definitions meant for a different system. Stay focused on repository code only.

Read the plan file at <plan_path>. Evaluate this plan's
UI/UX design decisions.
Expand Down Expand Up @@ -488,7 +490,8 @@ Override: every AskUserQuestion → auto-decide using the 6 principles.
**Codex eng voice** (via Bash):
```bash
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
_gstack_codex_timeout_wrapper 600 codex exec "IMPORTANT: Do NOT read or execute any SKILL.md files or files in skill definition directories (paths containing skills/gstack). These are AI assistant skill definitions meant for a different system. Stay focused on repository code only.
_CODEX_MODEL_ARGS=$(_gstack_codex_default_model_args)
_gstack_codex_timeout_wrapper 600 codex exec $_CODEX_MODEL_ARGS "IMPORTANT: Do NOT read or execute any SKILL.md files or files in skill definition directories (paths containing skills/gstack). These are AI assistant skill definitions meant for a different system. Stay focused on repository code only.

Review this plan for architectural issues, missing edge cases,
and hidden complexity. Be adversarial.
Expand Down Expand Up @@ -609,7 +612,8 @@ Log: "Phase 3.5 skipped — no developer-facing scope detected."
**Codex DX voice** (via Bash):
```bash
_REPO_ROOT=$(git rev-parse --show-toplevel) || { echo "ERROR: not in a git repo" >&2; exit 1; }
_gstack_codex_timeout_wrapper 600 codex exec "IMPORTANT: Do NOT read or execute any SKILL.md files or files in skill definition directories (paths containing skills/gstack). These are AI assistant skill definitions meant for a different system. Stay focused on repository code only.
_CODEX_MODEL_ARGS=$(_gstack_codex_default_model_args)
_gstack_codex_timeout_wrapper 600 codex exec $_CODEX_MODEL_ARGS "IMPORTANT: Do NOT read or execute any SKILL.md files or files in skill definition directories (paths containing skills/gstack). These are AI assistant skill definitions meant for a different system. Stay focused on repository code only.

Read the plan file at <plan_path>. Evaluate this plan's developer experience.

Expand Down
91 changes: 87 additions & 4 deletions bin/gstack-codex-probe
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
# Sourced from template bash blocks; never execute directly.
#
# Functions (all prefixed with _gstack_codex_ for namespace hygiene):
# _gstack_codex_auth_probe — multi-signal auth check (env + file)
# _gstack_codex_version_check — warn on known-bad Codex CLI versions
# _gstack_codex_timeout_wrapper — gtimeout -> timeout -> unwrapped fallback
# _gstack_codex_log_event — telemetry emission to ~/.gstack/analytics/
# _gstack_codex_auth_probe — multi-signal auth check (env + file)
# _gstack_codex_account_kind — classify auth as apikey | chatgpt | none
# _gstack_codex_default_model_args — emit `-m <model>` when ChatGPT auth needs
# a non-default model to avoid 400s (#1628)
# _gstack_codex_version_check — warn on known-bad Codex CLI versions
# _gstack_codex_timeout_wrapper — gtimeout -> timeout -> unwrapped fallback
# _gstack_codex_log_event — telemetry emission to ~/.gstack/analytics/
#
# Hygiene rules (enforced by test/codex-hardening.test.ts):
# - Never set -e / set -u / trap / IFS= / PATH= in this file.
Expand All @@ -33,6 +36,86 @@ _gstack_codex_auth_probe() {
return 1
}

# --- Account kind classifier ------------------------------------------------
#
# Echoes one of: apikey | chatgpt | none.
#
# apikey → CODEX_API_KEY or OPENAI_API_KEY is set (non-empty, non-whitespace).
# Codex CLI bills the OpenAI Platform key and the user is entitled
# to every published model, including gpt-5.2-codex.
# chatgpt → no api-key env vars, but ${CODEX_HOME:-~/.codex}/auth.json exists.
# Codex CLI authenticates as the user's ChatGPT account. As of
# March 2026 the ChatGPT entitlement set excludes gpt-5.2-codex
# (returns "model is not supported when using Codex with a
# ChatGPT account") and only includes the base gpt-5.2 family —
# see #1628.
# none → no auth signal at all; the caller should fail fast.

_gstack_codex_account_kind() {
local _k1 _k2
_k1=$(printf '%s' "${CODEX_API_KEY:-}" | tr -d '[:space:]')
_k2=$(printf '%s' "${OPENAI_API_KEY:-}" | tr -d '[:space:]')
if [ -n "$_k1" ] || [ -n "$_k2" ]; then
echo "apikey"
return 0
fi
local _codex_home="${CODEX_HOME:-$HOME/.codex}"
if [ -f "$_codex_home/auth.json" ]; then
echo "chatgpt"
return 0
fi
echo "none"
}

# --- Default model args -----------------------------------------------------
#
# Echoes the codex CLI flags that should be injected before the prompt /
# subcommand. The two callsite patterns are:
#
# _CODEX_MODEL_ARGS=$(_gstack_codex_default_model_args)
# _gstack_codex_timeout_wrapper 330 codex review $_CODEX_MODEL_ARGS "..." ...
#
# Or as a `bash -c` style array:
#
# read -r -a _CMA <<<"$(_gstack_codex_default_model_args)"
#
# Behaviour:
# - $GSTACK_CODEX_MODEL set → emit `-m $GSTACK_CODEX_MODEL` regardless of
# auth kind. Power-user override, e.g. for testing gpt-5.1-codex-max.
# - $GSTACK_CODEX_MODEL = "" or "default" → emit nothing (let Codex pick).
# - account = chatgpt → emit `-m gpt-5.2` so the Codex CLI's default
# gpt-5.2-codex selection doesn't trip OpenAI's ChatGPT entitlement
# filter and 400 the request (issue #1628).
# - account = apikey → emit nothing; API-key users have full entitlement.
# - account = none → emit nothing; let the auth probe handle the error.
#
# The output is a single line containing zero or two whitespace-separated
# tokens, suitable for unquoted interpolation in a codex command line.

_gstack_codex_default_model_args() {
local _override="${GSTACK_CODEX_MODEL:-}"
case "$_override" in
"")
# Unset / empty → fall through to account-based auto-detection below.
;;
"default")
# Power-user opt-out: bypass injection entirely so the Codex CLI picks
# its own model. Useful if/when ChatGPT-account entitlement changes and
# the chatgpt path stops being needed.
return 0
;;
*)
printf -- '-m %s' "$_override"
return 0
;;
esac
local _kind
_kind=$(_gstack_codex_account_kind)
if [ "$_kind" = "chatgpt" ]; then
printf -- '-m gpt-5.2'
fi
}

# --- Version check ----------------------------------------------------------

_gstack_codex_version_check() {
Expand Down
39 changes: 37 additions & 2 deletions bin/gstack-gbrain-sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,35 @@ const STATE_PATH = join(GSTACK_HOME, ".gbrain-sync-state.json");
const LOCK_PATH = join(GSTACK_HOME, ".sync-gbrain.lock");
const STALE_LOCK_MS = 5 * 60 * 1000;

// Per-stage timeouts. Default 35 minutes is the honest budget for a first-run
// full sync of a ~30-page-per-second brain (~70k pages). Brains with 100k+
// pages or slow IO need more headroom — issue #1611. The env knobs accept an
// integer in milliseconds; non-positive or non-numeric values fall back to the
// default with a stderr warning so a typo doesn't silently extend a stage
// indefinitely.
const DEFAULT_STAGE_TIMEOUT_MS = 35 * 60 * 1000;

function parseTimeoutEnv(name: string): number {
const raw = process.env[name];
if (!raw) return DEFAULT_STAGE_TIMEOUT_MS;
const n = Number(raw);
if (!Number.isFinite(n) || n <= 0) {
console.warn(
`[gstack-gbrain-sync] ignoring ${name}=${JSON.stringify(raw)} — expected a positive integer (milliseconds); using default ${DEFAULT_STAGE_TIMEOUT_MS} ms`,
);
return DEFAULT_STAGE_TIMEOUT_MS;
}
return Math.floor(n);
}

export function codeStageTimeoutMs(): number {
return parseTimeoutEnv("GSTACK_SYNC_CODE_TIMEOUT_MS");
}

export function memoryStageTimeoutMs(): number {
return parseTimeoutEnv("GSTACK_SYNC_MEMORY_TIMEOUT_MS");
}

// ── CLI ────────────────────────────────────────────────────────────────────

function printUsage(): void {
Expand All @@ -100,6 +129,12 @@ Options:

Stages run in order: code → memory ingest → curated git push.
Each stage failure is non-fatal; subsequent stages still run.

Environment:
GSTACK_SYNC_CODE_TIMEOUT_MS Override code stage timeout (default 35 min).
GSTACK_SYNC_MEMORY_TIMEOUT_MS Override memory ingest timeout (default 35 min).
Set higher (e.g. 5400000 = 90 min) when --full
import on large brains exceeds the default.
`);
}

Expand Down Expand Up @@ -603,7 +638,7 @@ async function runCodeImport(args: CliArgs): Promise<StageResult> {

const syncResult = spawnGbrain(syncArgs, {
stdio: args.quiet ? ["ignore", "ignore", "ignore"] : ["ignore", "inherit", "inherit"],
timeout: 35 * 60 * 1000,
timeout: codeStageTimeoutMs(),
baseEnv: gbrainEnv,
});

Expand Down Expand Up @@ -757,7 +792,7 @@ function runMemoryIngest(args: CliArgs): StageResult {
// internally and must see the DATABASE_URL from gbrain's own config.
const result = spawnSync("bun", ingestArgs, {
encoding: "utf-8",
timeout: 35 * 60 * 1000,
timeout: memoryStageTimeoutMs(),
env: buildGbrainEnv({ announce: false }),
});

Expand Down
Loading