First field bug of v0.1-alpha: install.py was committed 100644, so
every install shipped it permission-denied for direct ./install.py use
(start.sh's python3 invocation masked it). Exec bit set on all four
shebang'd files (install.py, board.py, hook_settings.py,
permission_config.py) and install.py added to update.sh's post-update
chmod line so already-broken installs heal on their next update. The
artifact-side invariant test — every shipped #! file executable — is
card 21.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The first-boot guard is now its own predicate gating both the clean and
the local/state/ stamp, so a disarmed run (pre-existing local/.env, or
self-hosted) touches nothing at all — before, it still created the stamp
directory on any vendored run. Removal unlinks symlinks instead of
following them into rmtree. Tests filter BOARD_* out of the subprocess
environment, assert the stamp stays absent when .env disarms the guard,
and cover the symlink case.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A vendored clone of bench arrives carrying bench's own task cards, plans
and reference documents, so a new user's first board opened pre-loaded
with our backlog. install.py now detects first boot — a vendored install
(project root != manager root) whose manager/local/ has never been
populated (no .env, no state/) — and only then clears the stage
directories, tasks/archive/, plans/ and reference/, keeping
task-template.md and the .gitkeep files and printing every removal.
--dry-run lists instead of removing. After cleaning it stamps
manager/local/state/, so the guard is permanently false on every later
run even if the adapter wire fails, and a host's own cards are never
touched. Self-hosted repos (bench itself, including fresh dev clones)
are never cleaned: their tasks/ is the repo's history.
Covered end-to-end in tests/test_install_first_boot.py by running
install.py as a subprocess against scratch host layouts; README notes
the first-boot behaviour under "Install into a repo".
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three layout defects broke ./start.sh in the self-hosted repo, one of
which (missing local/state/) broke every fresh vendored install too:
- install.py resolved the project as TM.parent, a hardcoded vendored-
layout assumption. It now asks git for the toplevel from the manager's
directory (same resolution as config._repo_root), falling back to the
parent when git is unavailable — vendored installs still find the host
repo, self-hosted bench finds itself instead of its parent.
- adapters/claude/wire hardcoded ".task-manager/" into the emit hook
command and plansDirectory. Both are now derived from the manager's
path relative to the project root, so vendored installs keep the
.task-manager/ prefix and self-hosted bench gets prefix-free paths.
_is_ours also recognises the emit.py suffix, so settings wired with
the old literal path count as stale and are repaired idempotently.
- board.py and state.py created the sessions/agent dirs without
parents=True; local/state/ is gitignored and ships empty, so a virgin
checkout died with FileNotFoundError before serving. Boot now creates
the whole chain, wiring or no wiring.
tests/test_self_hosting.py (stdlib unittest) covers both layouts' wiring,
stale-path repair, idempotent re-runs, refusal without .claude/, root
resolution with and without git, and an integration boot of board.py
from a scratch checkout with no local/state/.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Everything core from cicero-pas's .task-manager, with instance data
reduced to skeleton: empty stage directories, the task template, empty
local/ scaffolding, and a README covering install (clone into
.task-manager/, vendored on purpose) and update (update.sh replaces core
wholesale; local/ and tasks/ survive).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>