Skip to content

release: v0.1.0 - #27

Merged
JasonZQH merged 72 commits into
mainfrom
dev
Jul 23, 2026
Merged

release: v0.1.0#27
JasonZQH merged 72 commits into
mainfrom
dev

Conversation

@JasonZQH

Copy link
Copy Markdown
Owner

First public release — brings dev to main (72 commits) and cuts v0.1.0. See CHANGELOG.md. Includes the auto-stash clean-tree guard, TUI tool-call coalescing, README overhaul, and open-source governance.

JasonZQH and others added 30 commits July 8, 2026 22:03
Centered hero + differentiator tagline + centered badges, a two-column
feature table (the signature Hermes layout), a curl one-liner install
leading, a command-reference table, a documentation map, and a concise
contributing section reflecting the dev->PR->main workflow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Validated against Claude Code 2.1.204 --help:
- Remove --max-turns: not a valid flag in this CLI; loop iteration bounding
  is already owned by the scheduler's MAX_ITERATIONS.
- Pass --allowedTools/--disallowedTools as comma-separated single args so
  tool specs containing spaces (e.g. 'Bash(git *)') are not split into
  broken tokens; the space-joined form silently granted no usable tools.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ee deadlock

Validated against live Claude Code 2.1.204 and Codex CLI 0.143.0:
- claude: drop invalid --max-turns; pass --allowedTools/--disallowedTools as a
  single comma-separated arg so specs like 'Bash(git *)' are not split.
- codex: drop nonexistent --ask-for-approval (codex exec is non-interactive;
  --sandbox governs writes).
- workspace: exclude Curator's own .curator/ state from the clean-tree guard,
  and write .curator/.gitignore ('*') at init, so a fresh repo no longer blocks
  the writer on the state directory init just created.

Verified by a real end-to-end run: the Codex writer streams events and creates
files; implementation + validation evidence are captured.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rify guidance)

Surfaced by real end-to-end runs against the live Codex CLI:
- driver: give subprocesses stdin=DEVNULL so CLIs that opportunistically read
  stdin (codex exec) get EOF instead of hanging forever — this was deadlocking
  the writer on every run depending on the parent's stdin.
- clean-tree guard: enforce only on the loop's first writer dispatch; retries
  and resumes legitimately build on the writer's own prior uncommitted output,
  so the guard no longer deadlocks resume. Guard moved from the adapters into
  the scheduler (it is loop policy, not provider policy).
- verifier: clearer, actionable pause message when no verification commands are
  discovered (kept the no-synthetic-success policy).

Tests: engine-level clean-tree guard test; adapter tests updated. 209 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(claude): correct real Claude Code CLI flags (--allowedTools, drop --max-turns)
docs: Hermes-style README landing page
The setup banner used to teach the OS-terminal command form inside the
REPL, where any non-slash input under 41 words auto-started an agent
loop with zero confirmation — typing the banner's own suggestion
spawned a doomed goal loop that ended in a human_handoff pause trap.

- shell/intent.py: classify command-shaped input (curator-prefixed or
  known subcommand shapes) and answer with the slash equivalent or
  terminal guidance instead of goal intake
- setup mode now refuses to start natural-language goals and writes
  nothing to disk; guidance points at /init and /provider add
- gate mode defaults on (--gate/--no-gate); /gate off remains the
  explicit opt-in back into the fast path
- bare yes/no are no longer global reserved words; restart recovery
  moves to the explicit /goal start with a hint when a draft exists
- unknown slash commands get a closest-match suggestion
- hint strings are context-correct per surface: shell text uses slash
  commands, terminal output uses curator commands (provider_setup_hint
  gains a surface parameter; registry/engine pause texts name both)
- tests: intent classifier unit tests; CLI acceptance tests rewritten
  around the new contract with a live-mode fixture and a scripted
  fake claude binary for real dispatch paths

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
.claude/, .codegraph/, and .mcp.json are machine-local session tooling,
not project source.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- shell/banner.py: ASCII CURATOR banner with a version / project path /
  git branch identity line (reads .git/HEAD, handles worktree pointers)
- diagnostics/preflight.py: real environment checks before the first
  prompt — Python floor, git repo + dirty-tree warning, provider CLIs
  probed in parallel with version and login-state heuristics
  (credential files / env keys), verification-command discovery, and
  open-pause detection; rendered as ✓/!/✗ lines with per-check fixes
- startup order is banner → preflight → first-run offer → welcome;
  probes run on interactive terminals (CURATOR_PREFLIGHT=force/skip
  overrides), and the welcome text now warns about a persisted pause
  instead of pretending the shell is idle
- /doctor joins the shell (health + preflight, shell-dialect next
  step); curator doctor appends the same preflight report
- welcome text slims down: the banner owns identity, detection moved
  into preflight, so the old per-launch 2×10s --version probes in
  build_welcome_text are gone

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- shell/wizard.py: three-step wizard (team roles -> per-seat provider
  selection with login-state warnings -> summary + one consent write).
  IO is injected (ask/say) so the state machine is testable and can be
  re-skinned by the full-screen TUI without rewrites; nothing touches
  disk before the final confirmation
- /setup joins the shell and  the terminal; the fresh-
  project first-run offer now leads into the wizard instead of a bare
  y/N init, and the setup-mode refusal names /setup first
- provider add records the login-state heuristic in profile metadata
  and warns on unverified logins (degrade, not block); it now requires
  initialized state instead of silently running init — the last
  write_init_state side effects outside explicit init/setup are gone
- preflight exposes probe_provider/provider_auth_state for reuse

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- tui/shell_app.py: Textual app hosting the shared shell contract —
  scrollback log, bottom input, slash-completion suggestion bar, and a
  persistent status line (mode, writer/reviewer bindings, gate, cwd,
  open-pause notice). Engine dispatch runs in a worker thread with
  provider events streamed into the log via ShellState.emit_event;
  /setup suspends the app and reuses the line wizard
- bare curator on an interactive terminal now takes over the screen;
  --no-tui (finally functional) and non-tty sessions keep the plain
  line shell, so scripts and tests are unchanged; the dead --yes root
  flag is gone
- startup preflight runs off the UI thread inside the app; first-run
  wizard offer happens before the screen takeover
- repl: ShellState.emit_event injection point, public KNOWN_SLASH_ROOTS
  and KNOWN_SLASH_COMMANDS; onboarding: public open_pause_exists
- deleted the dead snapshot-only WorkflowApp (tui/app.py) and its test

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude Code stores its OAuth token in the macOS keychain, not a
plaintext ~/.claude/.credentials.json, so the file+env heuristic
reported a logged-in CLI as 'login state unknown' on macOS.

- _macos_keychain_has(service): read-only `security find-generic-password`
  existence check (no -w, so no decrypt and no access prompt); no-op off
  darwin and degrades to False on any security error
- _auth_state consults it for claude-code after the env/file checks,
  reporting 'logged in (keychain)' on a hit; codex is unaffected
- tests: keychain hit/miss, codex never consults it, non-darwin skip,
  security exit-code mapping and failure degradation; the existing
  unknown-login test now pins the keychain off for hermeticity

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix(preflight): detect Claude Code login from the macOS keychain
feat(shell): input guard, setup-mode gate, and context-correct hints (UX PR 1/4)
feat(shell): product banner and startup environment preflight (UX PR 2/4)
feat(shell): guided setup wizard with single-consent writes (UX PR 3/4)
feat(tui): full-screen Curator shell MVP (UX PR 4/4)
…ce ownership

- Persist OUTPUT_CHUNK provider stdout through a new redact_secrets helper (redaction without the error-tail truncation) via _ledger_event_payload, so credentials echoed in provider output can no longer land in the SQLite ledger in cleartext (only provider *errors* were redacted before).

- Derive recovery's durable workspace_owned from persisted IMPLEMENTATION evidence (matching the engine's _has_implementation_evidence) instead of the mere existence of an IMPLEMENT iteration; a writer that crashed before producing evidence no longer claims the workspace, so resume re-runs the clean-tree guard instead of misattributing a dirty tree.

- Document that an untyped provider exception classified as provider_unavailable intentionally STOP_FAILEDs rather than pausing (test_provider_failure_matrix_stops_on_untyped_runtime_error).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…t Esc, and bound growth

- CancellationToken.reset() re-arms the shared token at each new run; previously one Esc/Ctrl+C set the flag permanently, so every later goal/resume aborted before its first step and the full-screen shell was dead until restart.

- Typing a reply over an open proposal menu now submits the text instead of discarding it to run the highlighted option (Enter no longer runs 'yes' when the user typed 'no'); Enter routes through on_input_submitted, which accepts the highlighted option only on empty input.

- Esc on the first-run trust modal now exits: the app's priority Esc binding was shadowing the modal's own handler, leaving its advertised 'Esc to exit' dead.

- Bound the reflow-log cache (and RichLog max_lines), the plain-text transcript, and the on-disk shell_history file so a long streamed run cannot grow memory or disk without limit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…setup

- Compact line setup (curator setup and /setup in the line REPL) assigned the provider chosen at the seat labelled 'Engineer' to the reviewer seat and forced the engineer/writer seat to the PM's provider, so the engineer provider could never be chosen. The pick now binds the engineer seat it is labelled for, with the read-only reviewer mirroring it (Enter = same as PM keeps all three equal).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pre-existing work-in-progress on the onboarding branch, committed as a checkpoint alongside the review fixes: refreshed ASCII banner, simplified welcome/mode copy, /exit plus bare quit/exit exit commands, the user_echo TUI formatting helper, setup/trust screen tweaks, a textual>=8.2.8 pin, and accompanying test updates.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
JasonZQH and others added 29 commits July 20, 2026 14:09
fix(redact): scrub secrets split across streamed provider output chunks
docs(readme): lead the header with the Curator logo
Give every doc except the README a shared visual identity drawn from the
Curator logo — near-black console, chrome wordmark, and an electric
blue -> violet -> magenta signal gradient. One rule carries meaning
everywhere: LIVE / Phase 0 is solid glowing blue, TARGET / V1 is a dashed
violet-magenta outline, so the palette itself encodes the Phase 0 vs V1
boundary that every doc turns on.

- New shared design system: docs/assets/curator-docs.css (masthead, W/H/Y/F
  section markers, tables, callouts, chips, figure/legend, footer doc index,
  responsive + reduced-motion + focus states).
- All diagrams authored as Mermaid (docs/assets/curator-mermaid.js loads
  Mermaid from a pinned CDN and themes it to the brand palette) rather than
  hand-drawn — 13 diagrams across 9 docs, incl. the orchestration control
  loop, the V1 identity boundary, the shell-adapter boundary, the runtime
  kernel loop, the V1.1–V1.6 migration ladder, and the Orca fan-out / target
  artifact / ontology-migration figures.
- Content preserved verbatim (EN + ZH); ASCII <pre> diagrams promoted to
  Mermaid; the previously-untracked Orca design doc is brought into the repo.

Verified: all 13 diagrams render to SVG with zero syntax errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… offline

The diagrams loaded over http:// but not when a doc was opened directly from
disk. Cause: the shared init was an ES module (<script type="module">), and
browsers block module scripts over file:// (null origin / CORS) — so Mermaid
never initialized even though the CSS loaded.

Vendor Mermaid locally (docs/assets/mermaid.min.js, self-contained UMD build,
zero runtime dynamic imports) and load it plus the init as CLASSIC scripts.
Classic local scripts run over file://, and nothing is fetched at render time,
so diagrams now render both when double-clicked and offline — no CDN needed.
curator-mermaid.js keeps the brand theme and calls mermaid.run() on DOM ready.

Verified: all 13 diagrams across the 9 docs render to SVG with the global set
and zero syntax errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The previous logo image spelled the wordmark "CARUTOR" — the earlier fix only
corrected the file name, not the pixels. Swap in the corrected neon wordmark
(2124x740) that reads CURATOR, keeping the same path so the README reference is
unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: branded, diagram-first redesign of all HTML docs (Mermaid)
fix(brand): replace logo artwork with correctly-spelled CURATOR wordmark
…son)

These are local tooling artifacts, not part of the project. Ignoring them keeps
the working tree clean so Curator's clean-baseline guard does not pause a real
run on unrelated local files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…redactor buffer reset

Two audit-fidelity regressions plus a housekeeping ignore.

streamed transcript: run_harness_async ran inside `with transaction(...)`, so
the raised error discarded every OUTPUT_CHUNK event — erasing exactly the
output needed to debug the failing run. Catch the harness error inside the
block so the transaction commits the streamed events, then re-raise into the
existing failure handling. asyncio.CancelledError is deliberately left
uncaught, preserving the rollback-and-reconcile path.

counts redacted chars, but the reset sliced the raw buffer and zeroed
`_emitted`, losing the length difference wherever redaction had shortened the
text. Discount the dropped prefix in redacted coordinates
(`_emitted -= len(redact_secrets(dropped))`) and retain the trailing half of
the window. No secret ever leaked; this restores byte-for-byte transcript
fidelity.

Both fixes are covered by regression tests written test-first (RED: 22 chars
dropped / 0 chunks persisted; GREEN after the fix). Also ignore the generated
.codegraph/ index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… boundary

Close the release-readiness gaps between the README's promises and what the
code actually delivers, per the three v0.1.0 gating decisions (all resolved in
favor of documenting the real contract, not new features):

- Reviewer is advisory, not a gate. README now states the reviewer surfaces
  findings for the human confirm gate and does not automatically block delivery.
- OUTPUT_CHUNK is a redacted continuous delta, not a per-source-chunk record.
  StreamRedactor's emission contract is spelled out in its docstring and in a
  new README "Known limitations" section.
- README "replayable" reconciled with the release notes: the auditable
  system-of-record is the decision/evidence/pause rows (resumable via /resume);
  the provider transcript is not replayed.

Also add two characterization tests locking the cancellation boundary:
ProviderCancelledError (the bundled CLI-adapter path) preserves its streamed
transcript and pauses; a bare asyncio.CancelledError rolls back the in-flight
transcript batch by design. Closes the previously-uncovered protocol edge.

No behavior change. 331 tests pass, ruff clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-reset

fix(scheduler,redact): preserve failed-run transcript and fix stream …
The local .curator/ ledger can accumulate hand-seeded demo rows (mock sessions,
provider='mock' runs). Those never belong in the repo, and shipping code must
never gain a synthetic provider — Curator's contract is "no fallback theater".
Add guards so a regression fails before merge.

Guards (run in the existing PR checks):
- tests/test_no_mock_or_seed.py:
  - ProviderName stays exactly {codex, claude-code}.
  - src/ defines no Mock/Fake/Stub/Synthetic/Dummy provider or driver class.
  - no .curator/ state or *.sqlite* ledger is git-tracked.
  - a fresh `curator init` seeds zero sessions/goals/provider_runs/loop rows.
- A dedicated fast "Guard" CI job repeats the two repo-hygiene checks with no
  Python install, so the most important rule fails in seconds with a clear name.

Verified: 335 tests pass, ruff clean, and both guards were checked to catch a
real MockProvider class / tracked .sqlite while ignoring ProviderDriver etc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ci: guard against synthetic providers and committed seed/mock state
The completion palette showed a generic "Run this Curator command" for most
commands (only ~13 of ~35 had a description), and there was no way to fill a
command by mouse or a dimmed inline hint. Pre-v0 UX polish:

- repl.py: give every command in KNOWN_SLASH_COMMANDS a specific description;
  arg-taking commands spell their usage (e.g. "/agent bind <slot> <provider>",
  "/approve <id> [note]"). A test asserts none fall back to the generic text.
- palette.py: render the command in an accent color and the description dimmed,
  so the description reads as a hint, not equal-weight noise.
- shell_app.py: attach SuggestFromList to the input for dimmed ghost-text
  completion (Right/End accepts; Tab still cycles the palette), and add a
  palette click handler that fills the input with the chosen command so the
  user can type its arguments next.

Verified: ghost text completes slash prefixes and stays silent for normal goals;
clicking a palette row fills the input; 340 tests pass, ruff clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat(tui): explain every slash command + ghost-text & click-to-fill
…ken use

Make an active run readable and expressive above the input.

1. Tool-call detail. Both adapters only kept the tool *name*, so the transcript
   showed opaque "tool_call command_execution". Now:
   - Codex: extract the command (or changed paths, + non-zero exit) from the item.
   - Claude Code: extract the tool_use input (command / file_path / …).
   Details are redacted (redact_secrets) and length-bounded, and the TUI renders
   them dimmed next to the tool name.

2. Music-production phase word. The flat "Working…" becomes the loop phase read
   live from the ledger's running iteration: plan→Composing, implement→Arranging,
   validate→Mixing, review→Auditioning, confirm→Mastering (default: Cueing up).
   This covers the deterministic verify step too, which emits no provider event.

3. Provider token usage. Adapters now report the provider name (on STARTED) and a
   token total (on the usage/result event), parsed defensively. The working line
   shows e.g. "codex: 1.5k". Counts reset per run.

Note: exact Codex/Claude usage + tool field names vary by CLI version; parsing is
defensive and simply omits a field it cannot find, so it degrades gracefully and
should be smoke-checked against the installed CLIs.

Verified: 351 tests pass, ruff clean; a captured TUI render shows tool commands,
"Auditioning…", and "codex: 1.5k".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat(tui): tool-call detail + music-themed phase + provider token usage
…es don't pause runs

asyncio's StreamReader defaults to a 64 KiB line limit. A single provider JSONL
event that inlines a diff, tool output, or a review of the change routinely
exceeds that, so readline() raised ValueError("Separator is not found, and chunk
exceeds the limit"). That was classified as invalid_output and paused the loop
with a misleading "Provider invalid output" — even though the model output was
fine and only Curator's own reader crashed.

Pass a generous limit (16 MiB) to create_subprocess_exec so a big-but-ordinary
line streams through. Regression test emits a 128 KiB line and asserts the run
succeeds; it fails with the exact original error when the limit is removed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(providers): raise subprocess stream-reader limit so big JSONL lines don't pause runs
…learly

The Claude Code and Codex CLIs don't expose remaining quota programmatically
(no stream-json field, no quota command, no config file), so Curator can't
compute "95% of your limit". But when a run actually hits the limit, the CLI
says so in its error output — often with a reset time. Relay that instead of
inventing a threshold.

- providers/limits.py: tolerant detection of usage/rate-limit phrasing and a
  reset-time hint (wording varies by CLI version; unmatched errors fall through
  to the ordinary path).
- ProviderErrorKind.USAGE_LIMIT; both adapters classify a rate-limited exit as
  USAGE_LIMIT with a provider-attributed message ("Codex usage limit reached —
  resets 15:00").
- decide_runtime pauses on it with a clear, actionable reason ("… Wait for the
  reset, then /resume to retry.") instead of a generic "provider unavailable".

Scope note: this fires when the provider itself reports the limit (at/near the
cap). A proactive running percentage is not available from the CLIs' headless
output, so it is intentionally not attempted.

359 tests pass, ruff clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat(providers): surface the provider's own usage/rate-limit signal clearly
… summary

Before: every provider TOOL_CALL rendered as its own transcript line with the
full command, so a big shell command wrapped over 3 lines and repeated — noisy
and hard to read.

Now, following the Claude Code "⏺ tool(arg)" pattern (and grok-build's compact
scrollback / Orca's status-oriented display):
- Consecutive same-type tool calls stream into ONE live block above the input
  ("⏺ command_execution ×3  <latest cmd, one line>"), updated in place.
- When the group closes (a non-tool event, a new tool type, or the run ending),
  a single concise summary lands in the scrollback ("⏺ command_execution ·
  4 calls"; a lone call keeps its truncated command).
- The command is collapsed to one line and truncated — no more wrap-spam. Glyph
  and tool name in accent, args/count dimmed.

5 tests cover coalescing, the summary flush (non-tool event / new type / run
end), single-call detail, and one-line truncation. 364 tests pass, ruff clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The writer clean-tree guard pauses on a dirty tree so the recorded diff stays
attributable to the writer alone. Add an opt-in convenience: `/resume stash`
tucks the user's uncommitted work into a git stash (excluding `.curator/`, so
Curator's own ledger is never swept) and re-runs the writer on a clean
baseline; the user restores later with `git stash pop`. Pair it with a clearer
dirty-pause card that names shell-valid commands (`/resume stash` |
`/resume continue`) instead of a bare `/resume`.

Deliberate ceilings: no auto-pop (avoids mid-loop merge conflicts), and the
stash return is advisory — the clean-tree guard remains the fail-safe, so a
failed or empty stash simply re-pauses. Both documented in code.

Also document the future multi-worktree parallel model in
docs/future-optimizations.html — the V1 direction that supersedes this guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
README: lead with the product vision and the multi-provider pain points
Curator solves; Getting Started is now global-install-then-run only (drop the
from-source/dev path); add a Versions list (v0.1.0 + the V1.1–V1.6 roadmap)
and an expanded Commands list; remove the developer docs/*.html table. The
dev/local setup moves into CONTRIBUTING.md.

Governance (all greenfield): CONTRIBUTING.md documents the gated model —
anyone files issues, invited contributors open PRs into dev, admin merges
dev→main as a version bump. Adds CODEOWNERS, SECURITY.md, CODE_OF_CONDUCT.md
(Contributor Covenant 2.1), structured issue forms (blank issues disabled) +
a PR template, and CHANGELOG.md as the version-history source.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… as default)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(tui): coalesce streamed tool calls into one live block + concise summary
feat(workspace): opt-in /resume stash for the clean-tree guard
chore(release): v0.1.0 README overhaul + open-source governance
@JasonZQH
JasonZQH merged commit eea5ac9 into main Jul 23, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant