mirror of
https://github.com/nearai/ironclaw.git
synced 2026-09-03 08:06:01 +08:00
* docs: enforce the docs/ publication boundary with a frozen .mintignore and CI gate docs/ mixes the public Mintlify site with internal engineering docs, and omission from docs.json navigation is not a publication boundary: a page left out of navigation is still deployed, reachable by URL, and indexable. docs/design/ and docs/research/ were never added to docs/.mintignore, so both internal docs have been served as hidden pages on the public site. Close the gap and the process hole behind it: * Move docs/design/ and docs/research/ under docs/internal/, the one growing home for internal material — new internal docs now land inside the fence by default instead of requiring a .mintignore edit. * Freeze docs/.mintignore: scripts/ci/docs_publication_boundary.py rejects any new entry (legacy directories stay listed until consolidated into internal/; entries may only be removed). * Gate in CI (Code Style): every .md/.mdx under docs/ must be in docs.json navigation, matched by .mintignore, or carry `hidden: true` frontmatter marking a deliberately unlisted public page; navigation entries must have a source file. The gate has its own has_docs trigger because docs-only PRs skip every Rust lane, and it is checked in the roll-up before the has_code early exit so it blocks docs-only PRs too. Regression coverage: scripts/ci/test_docs_publication_boundary.py (16 cases, run by the CI job before the check; one pins the real docs/ tree as clean). Red/green verified: the checker flagged exactly docs/design/agent-activity-streaming.md and docs/research/pi-agent-deep-dive.md before the fix and passes after. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: relocate legacy internal doc directories under docs/internal/ — text-only Move plans/, superpowers/, qa/, adr/, architecture-video/, and reborn-binary.md from docs/ into docs/internal/, and rewrite every repo reference to the old paths (guidance files, script and workflow comments, Rust doc comments, the render-architecture-video VIDEO_DIR, the architecture-video skill, .coderabbit.yaml). Behavior unchanged: all references to these directories were textual except the video script's VIDEO_DIR, the skill paths, and the .coderabbit.yaml ignore, which are updated in step. docs/reborn/ deliberately stays put: its path is load-bearing (ironclaw_capabilities and ironclaw_architecture_tests read contract files from it at test time, and reborn-e2e.yml scope filters match it — pinned by scripts/ci/ws12_workflow_contracts.py). It consolidates into internal/ in a follow-up when those consumers can move with it; docs/.mintignore and FROZEN_MINTIGNORE_PATTERNS shrink to internal/ + reborn/ accordingly. Verified: docs publication boundary check green, its 16 self-tests green, ws12 workflow contracts green (46), touched YAML parses, zero references to the old paths remain outside git history. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: cover the docs gate's entry point and pin its trigger in ws12 contracts Fixes the three findings from the multi-agent code review of this branch (security/bugs/performance/conventions clean; tests reviewer found 3): * main() was never called by any test — the exit-code contract, the three stderr violation blocks, and main()'s MintignoreSyntaxError handling were uncovered, so a regression returning 0 despite violations would have passed all tests while turning the CI gate into a no-op. Three new tests drive main() directly (clean tree, all violation classes, syntax error). * The has_docs trigger grep and the fail-closed roll-up guard had no pin. ws12_workflow_contracts.py now carries a has_docs CrateScopeFilter (docs/, the gate's own files, and the workflow in scope; crates and README out) plus code_style.yml REQUIRED_MARKERS for the job, both steps, and the roll-up guard — with sabotage tests proving narrowing the grep or removing a marker fails loudly. Red/green verified. * is_ignored()'s slash-glob pattern branch (contains '/' but not trailing) had no fixture; covered with design/*.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: pin the docs-gate guard's ordering, not just its presence Fixes the three findings from review round 2 (security/bugs/performance clean; tests found 2, conventions found 1): * REQUIRED_MARKERS is presence-only, so relocating the docs-gate roll-up guard to after the has_code early exit — the exact silent-skip bug the guard exists to prevent — passed every contract check. New validate_code_style_docs_guard_order() pins guard-before-early-exit in code_style.yml, with a sabotage test that relocates the guard line and a checked-in-order pass test. Red/green verified. * CrateScopeFilterSabotageTests' docstring still described "the three remaining crate-keyed filters"; updated for the fourth, non-crate-keyed has_docs pin (review-discipline.md: guardrail docs must match the code). * is_ignored()'s nested-directory pattern branch (a trailing-slash entry with an internal slash, e.g. `design/sub/`) never executed under the suite; covered by test_mintignore_nested_directory_pattern_fences. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: fix relative ADR links missed by the path sweep; align checker hint with the frozen fence Addresses the Copilot review on #7259: * The reference sweep rewrote literal `docs/adr` strings but not relative markdown links: `../../adr/` in target-architecture/{CHECKLIST,PROPOSAL}.md, `../../../adr/` in families/domains.md, and the hooks CLAUDE.md link (which also carried a pre-existing wrong depth from the WS7 family move) all resolved to the old location. A repo-wide relative-link scan found exactly these seven move-caused breaks; the remaining broken links predate this branch (WS7 crate-move fallout in testing-playbook.md, one dead June plan link) or are Mintlify extensionless links that resolve on the site. * The checker's remediation hint said "add its directory to docs/.mintignore", contradicting the frozen-fence rule the same script enforces; it now directs authors to move internal material under docs/internal/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: only executable guard occurrences satisfy the docs-gate order pin CodeRabbit (Major, #7259): validate_code_style_docs_guard_order used a raw str.find, so a commented-out copy of the guard above the has_code early exit — a realistic refactor leftover — satisfied the pin while the executable guard sat below the exit, silently unhooking the gate for docs-only PRs. The validator now strips comment lines before matching and requires EVERY live guard occurrence to precede the first early-exit occurrence; a comment-only occurrence reports the order as unassertable. New decoy sabotage test red/green verified. Also parenthesized the implicit string concatenations Ruff flagged (ISC004). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(docs): align all Remotion packages to 4.0.499 CodeRabbit flagged the moved architecture-video project's manifest: Remotion requires every @remotion/* package at one identical version, but dependabot #6658 bumped only @remotion/cli and @remotion/tailwind-v4 to 4.0.499, leaving remotion, @remotion/transitions, and @remotion/eslint-config-flat at 4.0.447 — a pre-existing break on main that surfaced here because the directory rename presents as a new project. Aligned all five to 4.0.499 and regenerated the lockfile; `npx remotion versions` now reports all packages at the correct version. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: assert both signals in the literal-pattern fencing case Copilot (#7259): test_mintignore_literal_file_fences used a pattern outside the frozen allowlist but discarded the `unexpected` result, so it passed while the checker it pins would fail — a misleading regression pin. The case now asserts both independent signals explicitly: the literal entry still fences its file (no publication leak) AND trips the frozen-list rule, with the interplay documented in the test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: mark the architecture video as stale pre-Reborn content Copilot flagged the relocated video scenes for citing crates/ironclaw_engine paths that no longer exist. The scenes are untouched April 2026 content (#2365) presenting as new because of the directory rename; regenerating them against the Reborn architecture is deliberately out of scope for this move-only PR. Until that regeneration happens, a prominent README banner states what the video describes, why it is wrong today, where current docs live (openwiki/), and how to regenerate (architecture-video skill) — so the content cannot mislead contributors in the meantime. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: probe docs/docs.json in the has_docs scope pin Copilot (#7259): navigation is half the publication-boundary contract — a nav-only edit can orphan a page into hidden-page territory or reference a missing source file — but no has_docs probe covered docs.json, so a future markdown-only narrowing of the trigger grep (e.g. ^docs/.*\.(md|mdx)$) would silently skip the gate for nav changes while every existing probe stayed green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ci): map the two sweep-touched dev scripts and satisfy rustfmt Two root causes behind the red CI on #7259, both fallout from the docs path sweep touching files no docs-only change normally touches: * reborn_composition_boundaries.rs reads the (moved) composition pub-use snapshot; the longer docs/internal/plans/ path pushed the line past rustfmt's width. Reformatted. * The Reborn PR test planner fails closed on unmapped repo-root scripts. The sweep touched two local dev tools no workflow invokes — check-type-duplicates.py (docstring path) and render-architecture-video.sh (VIDEO_DIR path) — mapped both with per-file decisions in the planner's established style. Verified by running the planner against this branch's full 173-file changed list (green) plus its 61 self-tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: commit the messaging-framework path rewrites dropped by the previous merge The prior merge commit staged files before running the path sweep, so its rewrites of #6831's new files (docs/superpowers -> docs/internal/superpowers in three Rust doc comments, the plan, and standard-operations.md) were left unstaged and its message wrongly called the sweep a no-op. This commit is those rewrites. --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
148 lines
6.3 KiB
Bash
Executable File
148 lines
6.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Launch IronClaw Reborn with the WebChat v2 web UI for local testing.
|
|
#
|
|
# Handles the setup footguns from docs/internal/reborn-binary.md for you:
|
|
# - keeps the Reborn home OUTSIDE the repo (serve uses the cwd as the
|
|
# local-dev workspace root and rejects overlap with it);
|
|
# - configures the model route via `models set-provider`;
|
|
# - generates the WebUI bearer token and sets the WebUI user to the home's
|
|
# `[identity].default_owner` (falling back to `reborn-cli`, config init's
|
|
# default) so serve's owner check doesn't refuse to start.
|
|
#
|
|
# Usage:
|
|
# scripts/run-reborn-webui.sh # NEAR AI defaults
|
|
# PROVIDER=openai scripts/run-reborn-webui.sh
|
|
# PROVIDER=anthropic MODEL=claude-sonnet-4-20250514 scripts/run-reborn-webui.sh
|
|
#
|
|
# Before running, export your provider's API key, e.g.:
|
|
# export NEARAI_API_KEY=... # or OPENAI_API_KEY / ANTHROPIC_API_KEY
|
|
#
|
|
# Overridable via environment:
|
|
# PROVIDER provider id (default: nearai)
|
|
# MODEL model id (default: provider catalog default)
|
|
# REBORN_HOST listen host (default: 127.0.0.1)
|
|
# REBORN_PORT listen port (default: 3000)
|
|
# IRONCLAW_REBORN_HOME (default: $HOME/.ironclaw-reborn-demo)
|
|
# IRONCLAW_REBORN_WEBUI_USER_ID (default: home's [identity].default_owner)
|
|
# IRONCLAW_REBORN_WEBUI_TOKEN (default: generated and printed)
|
|
#
|
|
# REBORN_HOST/REBORN_PORT are deliberately prefixed: a bare HOST would collide
|
|
# with zsh's auto-set $HOST (the machine hostname), which could bind serve to a
|
|
# non-loopback interface and expose the bearer token over plain HTTP.
|
|
|
|
set -euo pipefail
|
|
|
|
PROVIDER="${PROVIDER:-nearai}"
|
|
MODEL="${MODEL:-}"
|
|
REBORN_HOST="${REBORN_HOST:-127.0.0.1}"
|
|
REBORN_PORT="${REBORN_PORT:-3000}"
|
|
|
|
# This launcher prints a login URL for a browser, so a fixed port is required.
|
|
# `serve --port 0` (kernel-picks-a-free-port) is for test harnesses only and
|
|
# would print an unusable http://REBORN_HOST:0/ here.
|
|
if [ "$REBORN_PORT" = "0" ]; then
|
|
echo "error: REBORN_PORT=0 (kernel-assigned port) isn't usable for browser onboarding." >&2
|
|
echo " Set a fixed REBORN_PORT, or run the test-harness form directly:" >&2
|
|
echo " cargo run -q -p ironclaw \\" >&2
|
|
echo " --bin ironclaw -- serve --port 0" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Run cargo from the workspace root regardless of where the script is invoked.
|
|
REPO_ROOT="$(git -C "$(dirname "${BASH_SOURCE[0]}")" rev-parse --show-toplevel)"
|
|
cd "$REPO_ROOT"
|
|
|
|
# Resolved by crate NAME through the shared inventory
|
|
# (scripts/ci/lib/crate_tree.py) rather than a literal `crates/ironclaw_webui`
|
|
# path, so the target-architecture family move (PROPOSAL §5) cannot leave this
|
|
# pointed at a directory that no longer exists
|
|
# (docs/reborn/target-architecture/CHECKLIST.md WS10).
|
|
FRONTEND_DIR="$("$REPO_ROOT/scripts/ci/crate-dir.sh" ironclaw_webui "$REPO_ROOT")/frontend"
|
|
if ! command -v pnpm >/dev/null 2>&1; then
|
|
if command -v corepack >/dev/null 2>&1; then
|
|
corepack enable pnpm
|
|
fi
|
|
fi
|
|
if ! command -v pnpm >/dev/null 2>&1; then
|
|
echo "error: pnpm is required to build WebUI v2 assets." >&2
|
|
echo " Install Node 22 from .nvmrc and enable pnpm with: corepack enable pnpm" >&2
|
|
exit 1
|
|
fi
|
|
echo "==> Building WebUI v2 frontend assets"
|
|
(
|
|
cd "$FRONTEND_DIR"
|
|
pnpm install --frozen-lockfile
|
|
pnpm build
|
|
)
|
|
|
|
export IRONCLAW_REBORN_HOME="${IRONCLAW_REBORN_HOME:-$HOME/.ironclaw-reborn-demo}"
|
|
|
|
# Reject a home inside the repo, which would trip the workspace/skill-root
|
|
# overlap validation in serve. Canonicalize both paths first (resolving `..`
|
|
# and symlinks, like serve does) so e.g. `../reborn-home` isn't mis-flagged.
|
|
# Resolve via the parent dir so we don't have to create the home to normalize
|
|
# it; if the parent doesn't exist yet, skip this friendly check and let serve's
|
|
# own validation handle it.
|
|
case "$IRONCLAW_REBORN_HOME" in
|
|
/*) home_abs="$IRONCLAW_REBORN_HOME" ;;
|
|
*) home_abs="$PWD/$IRONCLAW_REBORN_HOME" ;;
|
|
esac
|
|
home_parent="$(cd "$(dirname "$home_abs")" 2>/dev/null && pwd -P || true)"
|
|
repo_canonical="$(cd "$REPO_ROOT" && pwd -P)"
|
|
if [ -n "$home_parent" ]; then
|
|
home_canonical="$home_parent/$(basename "$home_abs")"
|
|
case "$home_canonical/" in
|
|
"$repo_canonical"/*)
|
|
echo "error: IRONCLAW_REBORN_HOME ($home_canonical) is inside the repo ($repo_canonical)." >&2
|
|
echo " serve uses the cwd as the workspace root and rejects overlap." >&2
|
|
echo " Point it somewhere else, e.g. \$HOME/.ironclaw-reborn-demo." >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
# Generate a WebUI bearer token if the caller didn't supply one.
|
|
if [ -z "${IRONCLAW_REBORN_WEBUI_TOKEN:-}" ]; then
|
|
export IRONCLAW_REBORN_WEBUI_TOKEN="$(openssl rand -hex 32)"
|
|
fi
|
|
|
|
CARGO=(cargo run -q -p ironclaw --bin ironclaw --)
|
|
|
|
# Configure the model route (compiles the binary on first run).
|
|
set_provider_args=(models set-provider "$PROVIDER")
|
|
if [ -n "$MODEL" ]; then
|
|
set_provider_args+=(--model "$MODEL")
|
|
fi
|
|
echo "==> Configuring model route: provider=$PROVIDER ${MODEL:+model=$MODEL}"
|
|
"${CARGO[@]}" "${set_provider_args[@]}"
|
|
|
|
# Match the WebUI user to the home's identity owner so serve's owner check
|
|
# passes (set-provider has now written/seeded config.toml). A caller-supplied
|
|
# IRONCLAW_REBORN_WEBUI_USER_ID wins; otherwise read [identity].default_owner
|
|
# from the config, falling back to reborn-cli (config init's default).
|
|
config_file="$IRONCLAW_REBORN_HOME/config.toml"
|
|
config_owner=""
|
|
if [ -f "$config_file" ]; then
|
|
config_owner="$(sed -n 's/^[[:space:]]*default_owner[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' "$config_file" | head -1)"
|
|
fi
|
|
export IRONCLAW_REBORN_WEBUI_USER_ID="${IRONCLAW_REBORN_WEBUI_USER_ID:-${config_owner:-reborn-cli}}"
|
|
|
|
# Discover the credential env var for this provider and warn if it is unset.
|
|
key_env="$("${CARGO[@]}" models status 2>/dev/null \
|
|
| sed -n 's/^default\.api_key_env: //p' || true)"
|
|
if [ -n "$key_env" ] && [ -z "${!key_env:-}" ]; then
|
|
echo "warning: $key_env is not set. Required-key providers (openai, anthropic, …)" >&2
|
|
echo " fail at startup; export it before turns will work." >&2
|
|
fi
|
|
|
|
cat <<EOF
|
|
|
|
==> Starting WebChat v2 on http://$REBORN_HOST:$REBORN_PORT/
|
|
login token : $IRONCLAW_REBORN_WEBUI_TOKEN
|
|
login user : $IRONCLAW_REBORN_WEBUI_USER_ID
|
|
reborn home : $IRONCLAW_REBORN_HOME
|
|
|
|
EOF
|
|
|
|
exec "${CARGO[@]}" serve --host "$REBORN_HOST" --port "$REBORN_PORT"
|