Skip to content

v0.99.20

Choose a tag to compare

@jstoup111 jstoup111 released this 06 Aug 16:26
· 822 commits to main since this release
31a58d5

Two months of work between v0.99.17 (2026-05-02) and the move to bot-owned release
pull requests. Entries are condensed by theme; per-change detail is in the linked
issues and in git log v0.99.17..v0.99.20.

Added

  • Multi-provider execution: a built-in codex provider alongside Claude, per-step provider routing (llm_provider as a scalar or an ordered array, with per-step overrides), and provider-aware model, effort and retry-escalation defaults (#902, #927, #1089).
  • tmux-supervised daemons with a full operator surface: conduct-ts daemon start|stop|restart|connect|debug|status|logs, pause/resume, park/unpark, a startup state dashboard, an install-freshness guard, and auto-restart when the running engine goes stale (#215, #307, #486).
  • Rate-limit episode coordination: session- and usage-limit signals are classified as rate limiting beyond HTTP 429, provider reset times are parsed into absolute deadlines, and workers share one abortable, jittered wait instead of each backing off independently. New feature dispatch pauses while an episode is active and halted features are re-kicked when it clears.
  • Retry-as-escalation: a step retry now escalates effort and then model tier rather than repeating an identical attempt, deep-step retry budgets drop from 5 to 3, and unavailable models fall back down a ladder instead of halting (#186, #188).
  • wiring_check — a gating step between build_review and manual_test that verifies every production surface the feature declares is actually reachable from a production entry point.
  • build_review — a fresh-session judged gate at the build seam whose completeness rubric is the authoritative build-completion signal, with deterministic BUILD/REMEDIATE routing on FAIL (#757, #817, #984).
  • Per-feature cost, token and wall-clock accounting: shipped records carry ## Cost and ## Time blocks and an engine_version stamp, conduct-ts kpi renders cost=/time=/engine= per feature, and an opt-in OpenTelemetry exporter publishes conductor run traces (#537, #1090, #1196).
  • GitHub-issues intake for the engineer, with bidirectional write-back, issue-to-PR linkage and auto-close on merge, required priority/size/dependency labels at every capture surface, and a host-wide conduct-ts brain start|stop|status intake loop with desktop notifications (#490, #695).
  • Owner gating: a daemon builds only the merged specs it owns, resolving a machine-scoped operator identity and failing closed when no identity resolves, with write-back onto the gated spec's PR and originating issue (#184).
  • Engineer worktree isolation and full DECIDE authoring — each idea is authored, landed and handed off inside its own worktree, running the complete explore → complexity → prd → architecture → stories → conflict-check → plan set (#142).
  • Self-host guardrails for the harness building itself: a live-checkout boundary guard, a write-fence sandbox, a daemon-owned build-auth token separate from operator OAuth, a version-freeze approval gate, and a release gate that accepts a committed waiver (#174, #261, #354, #380).
  • SHIP automation: the implementation PR is opened as a draft at the start of SHIP, halt PRs are rehabilitated at finish, irrecoverable halts leave a labeled needs-remediation draft PR, a mergeable sweep auto-resolves conflicts, and ci_watch drives bounded automatic fixes for red ships (#271, #274, #439, #499).
  • Parallel SHIP validation: manual_test, prd_audit and architecture_review_as_built fan out as a concurrent group in auto-mode runs, bounded by a new validation_concurrency config key (#469).
  • Autonomous gap remediation — a blocking prd_audit, as-built review or finish gate is routed through /remediate and repaired in place instead of halting for a human (#115).
  • New skills: verify-claims (a cross-cutting correctness and assumption gate), intake, daemon-triage, rebase and coherence-check, plus an operator_only SKILL.md frontmatter field for skills invoked from outside a run.
  • Mermaid rendering at the architecture approval gates, and conduct render-diagrams --check as an authoring-time syntax gate that fails on a broken diagram (#810).
  • The docs/ tree is published as a browsable GitHub Pages site with a landing page and site-wide navigation (#1224).
  • CI runs the harness integrity suite, the conductor build and vitest suite, tsc --noEmit, ESLint and ShellCheck on every pull request; documentation-only PRs skip the heavy jobs (#789, #802, #1040).
  • New conduct-ts surface: kpi, finish-record, overlap-scan, build-auth-status, halt-issues sweep, evidence, memory setup, an --effort <level> global override mirroring --model, and --interactive for conversational steps (#1027).
  • bin/update — a standalone self-update and channel CLI, invoked automatically as a one-shot subprocess at conduct-ts startup.
  • Deterministic project-config scaffolding for new and existing repositories, auto-discovered skill linking into both ~/.claude/skills/ and ~/.agents/skills/, and a bin/install --allow-worktree-root override for the worktree-root guard (#1169).

Changed

  • The CLI is verb-first: the inline pipeline is conduct-ts inline "<feature>" and the daemon is conduct-ts daemon …, replacing the bare-argument and --daemon forms.
  • Every step dispatch starts a fresh provider session, including within-step retries; Codex declares no session-resume capability, so each Codex dispatch is a cold start carrying its context through committed artifacts and the retry prompt (#325, #1110).
  • Model selection is right-sized across the funnel and generated into HARNESS.md from model-table-metadata.ts rather than hand-edited, with front-of-funnel DECIDE steps and recovery steps defaulting to Fable (#190).
  • manual_test is a gating step whose enforcement is locked, with an S-tier SKIP sentinel that satisfies downstream prerequisites; judged gate verdicts are re-validated against current code state rather than accepted on record (#367, #817).
  • Post-rebase gate re-verification is delta-aware: build_review, wiring_check, manual_test, prd_audit and architecture_review_as_built verdicts are preserved rather than re-run when the rebase did not affect them (#655).
  • Daemon scheduling honors GitHub issue dependencies and priority labels, including a new critical band above high; the root checkout fast-forwards on each idle poll and feature worktrees are cut from origin/<default>.
  • Daemon output is timestamped, colorized and transition-only, attributes each completed dispatch to the provider and model that ran it, and keeps routine skip notices behind daemon_verbose: true.
  • .memory/ is a symlink to a shared canonical store rather than a tracked in-project directory; ADRs are named adr-YYYY-MM-DD-<kebab-slug>.md instead of sequentially numbered; run-specific SHIP artifacts moved from tracked .docs/ to gitignored .pipeline/.
  • Configuration resolution deep-merges project .ai-conductor/config.yml over ~/.ai-conductor/config.yml at every entry point, so user-level values survive keys a project omits (#1031, #1199).
  • README reference material moved into docs/, leaving a landing page that points at the documentation tree.

Fixed

  • Daemon reliability across the run lifecycle: halts carry a machine-readable needs-human/mechanical class so the re-kick sweep cannot wipe one needing a human, a step-heartbeat watchdog detects stalls, a no-verdict backstop records where the run stopped, park markers anchor to the main repository root, park/unpark resolve that root from any cwd, restart uses a single-generation handoff, and clearing a HALT marker re-dispatches the feature immediately (#302, #353, #374, #400, #486, #1070, #1148).
  • Build-completion accuracy: the gate resolves the plan scoped to the current feature when several are in flight, accepts the full task-id grammar, sources expected paths from each task's **Files:** line, writes task-status.json atomically, and recovers finished work after a worktree is removed and recreated (#407, #417, #424, #425, #497, #1088, #1102).
  • Rebase handling: conflicts route through a bounded, gated /rebase resolution loop, the rebase step uses --autostash so a dirty tree no longer mis-parks as a conflict, protected-artifact seals rebaseline safely when the base branch changes another feature's DECIDE artifact, and engine-owned rebases translate sha-anchored evidence citations instead of orphaning them (#300, #976, #1121).
  • Self-host builds no longer halt on the harness's own bookkeeping: the live-boundary guard skips .git/, .daemon/, .pipeline/, node_modules caches and provider-state caches, tolerates concurrent operator edits to tracked files, and names the differing paths when it does halt (#985, #1115, #1158).
  • SHIP correctness: a shipped marker requires verified PR evidence, ship-readiness fails while the recorded PR is still a draft, discovery dedups on a shipped record committed to the feature's own branch, parked-feature reconciliation completes cleanup for squash-merged features, and a feature's worktree is retained until its ship is recorded (#337, #439, #1146, #1157, #1185).
  • Test-suite hygiene: the suite no longer leaks real cc-daemon-* tmux sessions, temp directories, .pipeline/ artifacts, engineer-signal writes or undrained child processes into the operator's environment, and is deterministic under forked workers (#252, #257, #377, #437, #573, #861).
  • bin/install never hard-fails on a missing optional dependency, refuses global-mutating modes from a worktree-rooted checkout, builds the conduct-ts bundle itself, discovers skills instead of using a hardcoded list, and exits non-zero on drift under --check (#363).
  • CLI correctness: --help renders the full recursive command reference, daemon logs --follow actually tails and --lines N is parsed, --from <step> validates against the resolved step registry, unknown bin/conduct subcommands fail loudly instead of launching the pipeline, and label mutations survive GitHub's Projects-classic sunset (#178, #1027).

Removed

  • RTK ("Rust Token Killer") from the install path.
  • Serena semantic-search integration from the install path, bootstrap MCP registration, HARNESS.md guidance and the gitignore skeleton (#682, #728, #753).
  • The --daemon flag, replaced by the conduct-ts daemon subcommand, and the --output / --step <step> flags, neither of which ever affected run behavior (#1013).
  • The per-task evidence gate and the semantic attribution judge lane, including the attribution_enforcement_cutover and attribution_judge_cutover config keys — per-task commit attribution is telemetry only (#773, #1211).
  • check_harness_config (the consumer CLAUDE.md to HARNESS.md auto-upgrade) from bin/conduct; detection is retained by the session-start context hook (#226).

Migration

for f in .ai-conductor/config.yml ~/.ai-conductor/config.yml; do
  [ -f "$f" ] || continue
  if grep -Eq '^(attribution_enforcement_cutover|attribution_judge_cutover):' "$f"; then
    sed -i.bak -E '/^(attribution_enforcement_cutover|attribution_judge_cutover):/d' "$f"
    echo "Removed retired attribution cutover keys from $f (backup: $f.bak)."
  else
    echo "$f has no retired attribution cutover keys — nothing to do."
  fi
done
# Reconcile the installed harness catalogs; foreign links and files are preserved.
"${HARNESS_DIR:?HARNESS_DIR must be set by bin/migrate}/bin/install" --update

# Find shell scripts/aliases/CI configs in the current directory tree that still pass the
# removed --output or --step flags to conduct-ts, so they can be edited by hand (nothing here
# is auto-rewritten — these are external, not tracked in this repo).
grep -rEn '\bconduct-ts\b.*(--output\b|--step\b)' \
  --include='*.sh' --include='*.yml' --include='*.yaml' . 2>/dev/null \
  || echo "No conduct-ts invocations with --output/--step found."
# Install the user-scoped HARNESS.md links consumed by generated CLAUDE.md and AGENTS.md.
"${HARNESS_DIR:?HARNESS_DIR must be set by bin/migrate}/bin/install" --update
# Unregister the harness-registered user-scope Serena MCP server (no-op if absent)
if command -v claude >/dev/null 2>&1 && claude mcp get serena >/dev/null 2>&1; then
  claude mcp remove --scope user serena
  echo "Removed user-scope 'serena' MCP registration."
else
  echo "No user-scope 'serena' MCP registration found — nothing to do."
fi
echo "Optional manual cleanup (NOT run automatically):"
echo "  uv tool uninstall serena-agent   # if you don't use Serena elsewhere"
echo "  pkill -f 'serena start-mcp-server'   # stop stray servers from old sessions"
echo "  rm -rf <project>/.serena/            # per-project semantic-index caches"
# Re-run the harness installer's settings merge so the new docs-guard.sh
# PreToolUse hook (Edit|Write|NotebookEdit matcher) is added to
# ~/.claude/settings.json. Safe to re-run: bin/install's settings merge is
# idempotent (matches on hook command path, does not duplicate entries).
cd "$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
"${HARNESS_DIR:?HARNESS_DIR must be set by bin/migrate}/bin/install" --update
echo "docs-guard.sh is now wired as a PreToolUse hook in ~/.claude/settings.json."
echo "Daemon-provisioned worktrees pick this up automatically on next"
echo "worktree-prepare handles newly provisioned worktrees automatically."
echo "If a running daemon needs new provisioning behavior, restart it yourself after this update."
# To use the attribution cutover, add attribution_judge_cutover and optionally
# attribution_audit_sample_pct to .ai-conductor/config.yml. Restart a daemon
# or conductor yourself after editing if it needs to read the new configuration.
echo "Configure attribution_judge_cutover in .ai-conductor/config.yml when ready."
# Link the new intake skill into ~/.claude/skills.
"${HARNESS_DIR:?HARNESS_DIR must be set by bin/migrate}/bin/install"
# Only needed if your project config disables manual_test.
# Remove the `manual_test:` disabled block from .ai-conductor/config.yml, e.g.:
if [ -f .ai-conductor/config.yml ] && grep -qE 'manual_test:' .ai-conductor/config.yml; then
  echo "manual_test step config found in .ai-conductor/config.yml —"
  echo "if it sets 'disabled: true', delete that line (gating steps cannot be disabled)."
  grep -n -A2 'manual_test:' .ai-conductor/config.yml
else
  echo "No manual_test step config found — nothing to do."
fi
# Set up daemon build-token if not already present
BUILD_AUTH_TOKEN_PATH="${HOME}/.ai-conductor/build-auth"

# Only mint if the file doesn't exist (no clobber of operator's existing token)
if [ ! -f "$BUILD_AUTH_TOKEN_PATH" ]; then
  echo "Setting up daemon build-auth token…"
  claude setup-token
  chmod 600 "$BUILD_AUTH_TOKEN_PATH"
else
  echo "Build-auth token already present at $BUILD_AUTH_TOKEN_PATH — no action needed."
fi

# Then configure the path in your harness config (.ai-conductor/config.yml):
echo "Configure in your harness config:"
echo "  harness_self_host:"
echo "    build_auth:"
echo "      token_path: $BUILD_AUTH_TOKEN_PATH"
# Existing worktrees keep their current hooks until they are re-provisioned.
# If you run a daemon, use your normal operator procedure to restart it after
# this update so future worktrees pick up the new provisioning code.
echo "Existing worktrees retain their hooks until re-provisioned."
echo "Enforcement stays OFF until attribution_enforcement_cutover is set in .ai-conductor/config.yml."
# Daemon lifecycle remains operator-controlled. If this project has an old
# detached daemon, stop it with your normal operator procedure before adopting
# the tmux-hosted daemon management workflow.
echo "Review any running daemon and choose its lifecycle action yourself."
# Rebuild the conductor engine (dist is no longer shipped in git).
cd "${HARNESS_DIR:?}/src/conductor" \
  && npm install --no-audit --no-fund \
  && npm run build
# bin/conduct now rejects unknown commands instead of silently launching the
# SDLC pipeline, and forwards conduct-ts verbs to conduct-ts. Verify conduct-ts
# is installed so forwarding works:
if command -v conduct-ts >/dev/null 2>&1; then
  echo "conduct-ts found: bin/conduct verb forwarding will work."
else
  echo "WARNING: conduct-ts not on PATH — forwarded verbs will exit 127."
  echo "Re-run bin/install to build and link conduct-ts."
fi
# Reminder: bare single-word feature descriptions are now rejected — quote
# multi-word descriptions instead, e.g.:  conduct "add user auth"
rm -f .claude/hooks/claude/post-commit-pipeline-sync.sh
# Install the new fast-feedback derive hook in your project's .git/hooks:
PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo '.')"
if [ -d "$PROJECT_ROOT/.git" ] && [ -f "${HARNESS_DIR:?}/hooks/claude/post-commit-derive-feedback.sh" ]; then
  cp "${HARNESS_DIR:?}/hooks/claude/post-commit-derive-feedback.sh" "$PROJECT_ROOT/.git/hooks/post-commit"
  chmod +x "$PROJECT_ROOT/.git/hooks/post-commit"
  echo "Installed fast-feedback post-commit hook"
fi
# Ensure the daemon state dir is gitignored (pidfile + daemon.log live here).
if [ -f .gitignore ]; then
  grep -qxF '.daemon/' .gitignore || printf '.daemon/\n' >> .gitignore
else
  printf '.daemon/\n' > .gitignore
fi
echo "ensured .daemon/ is in .gitignore"
# Migrate .memory/ to the canonical shared store under ~/.ai-conductor/memory/.
# No-op if .memory/ is already a symlink. Run from the project root.
_dir="$(pwd)"
_link="${_dir}/.memory"
if [ -L "${_link}" ]; then
  echo ".memory/ is already a symlink — no migration needed."
elif [ -d "${_link}" ]; then
  if command -v conduct-ts >/dev/null 2>&1; then
    conduct-ts memory setup "${_dir}"
  elif [ -x "${HARNESS_DIR:-}/bin/conduct-ts" ]; then
    "${HARNESS_DIR}/bin/conduct-ts" memory setup "${_dir}"
  else
    echo "conduct-ts not found — please run 'conduct-ts memory setup ${_dir}' after updating." >&2
    exit 1
  fi
  echo "Migration complete. .memory/ is now a symlink to the canonical store."
else
  echo ".memory/ does not exist — it will be created automatically on next 'conduct' run."
fi
# Opt in to the semantic attribution verification lane by adding the cutover
# key to .ai-conductor/config.yml. No-op / prints guidance if the file is
# missing or the key is already present.
CONFIG_FILE=".ai-conductor/config.yml"
if [ ! -f "$CONFIG_FILE" ]; then
  echo "No $CONFIG_FILE found — nothing to migrate. Create one and add" \
       "attribution_judge_cutover to opt in when ready."
elif grep -qF '# attribution_judge_cutover: "2026-07-11T08:30:00Z"' "$CONFIG_FILE" 2>/dev/null; then
  echo "$CONFIG_FILE already contains the attribution configuration template — no migration needed."
else
  cat <<'EOF' >> "$CONFIG_FILE"

# Semantic attribution judgment gate (opt-in; absent = disabled).
# Uncomment and set a past ISO-8601 instant to activate the judge lane for
# unresolved evidence-gate residue. Restart the daemon/conductor to apply.
# attribution_judge_cutover: "2026-07-11T08:30:00Z"
# attribution_audit_sample_pct: 10
EOF
  echo "Appended commented-out attribution_judge_cutover / attribution_audit_sample_pct" \
       "template to $CONFIG_FILE. Uncomment and set a cutover instant to opt in;" \
       "restart your daemon after editing if it needs to reload configuration."
fi
# Refresh an existing worktree's commit-msg hook to the post-#773 (advisory,
# non-rejecting) version. Safe to run repeatedly; writeGitHooks() always
# overwrites both hook scripts with the current generated content.
WORKTREE_ROOT="${1:-.}"
HOOKS_DIR="$WORKTREE_ROOT/.pipeline/git-hooks"
if [ ! -d "$WORKTREE_ROOT/.pipeline" ]; then
  echo "No .pipeline/ found at $WORKTREE_ROOT — not a prepared conductor worktree;" \
       "nothing to migrate."
elif [ -f "$HOOKS_DIR/commit-msg" ]; then
  rm -f "$HOOKS_DIR/commit-msg" "$HOOKS_DIR/prepare-commit-msg"
  echo "Removed stale hook scripts at $HOOKS_DIR. Re-run 'conduct' (or restart the" \
       "daemon build step) for this worktree to have worktree-prepare regenerate" \
       "them with the post-#773 advisory (non-rejecting) commit-msg hook."
else
  echo "No commit-msg hook found at $HOOKS_DIR — nothing to migrate; it will be" \
       "generated fresh (already advisory) the next time this worktree is prepared."
fi