| name | ascent-research |
| description | Full ascent-research CLI — incremental, resumable research sessions driven from a Claude Code or Codex instance. Orchestrate postagent (HTTP API) + actionbook browser (CDP) + local file ingest to build reproducible sessions with a persistent wiki layer, autonomous LLM loop, and editorial HTML reports. Covers all command surfaces — online fetch (add / batch / route), local ingest (add-local), session lifecycle (new / list / status / resume / close / rm / series / fork via --from), autonomous loop, wiki knowledge layer (list / show / rm / query / lint), user-editable SCHEMA.md, and renderers (synthesize / report / coverage / diff). Use for any "build a reproducible report on topic X" or "investigate source tree Y" or "compare technologies A and B with citations" request. |
| triggers | research, deep dive, deep-dive, investigate, analyze topic, survey, literature review, compare frameworks, review source, source tree, build knowledge base, library analysis, codebase analysis, ascent-research, ascent-research loop, session report |
| force_tool_turns | 15 |
ascent-research — Full CLI Skill
Build reproducible, figure-rich research reports with a persistent per-session wiki. One CLI, three input modes (HTTP API / browser fallback / local file tree), three output surfaces (narrative report / entity wiki / event log), autonomous loop optional.
Installation (MANDATORY — run before any playbook)
Three binaries must be on PATH: ascent-research (the CLI itself), postagent (HTTP ingest — npm), actionbook (browser fallback — npm).
cargo install ascent-research --features "provider-claude provider-codex"
npm install -g postagent @actionbookdev/cli
Doctor check — run this FIRST in every new session:
ascent-research --json doctor || { echo "INSTALL_REQUIRED"; exit 1; }
If the doctor check exits non-zero, STOP and run the install block above. Do NOT narrate the workflow in chat as a substitute — the CLI is the only thing that actually produces report.html.
For any playbook that will fetch online sources through add, batch, or route, run tool smoke as well:
ascent-research --json doctor --tool-smoke
If a required tool-smoke check fails, STOP and surface the failing tool check. Optional warnings such as postagent_public_dry_run should be treated as routing guidance: prefer browser or local ingest if public postagent fetches are not accepted by the installed postagent contract.
When online hands fail, preserve fallback provenance instead of silently
switching tools. If you cache a web page/source note locally, ingest it with:
ascent-research add-local ./cache --slug <slug> \
--original-url https://example.com/source \
--origin-tool curl \
--origin-note "actionbook daemon unavailable; cached with curl"
Rules for fallback sources:
- Use source notes only as a last resort when postagent/browser cannot fetch.
- Every source note must list the original URLs and explain why direct ingest failed.
- In the final reply, disclose which hand failed and which fallback path was used.
- For legal, medical, financial, current, or compliance claims, do not present a
source note as high-confidence primary evidence unless the report labels the
conclusion limited-confidence or later re-fetches the original URLs.
For any playbook that will call loop, wiki query, or synthesize --bilingual, also run a live provider smoke check first:
ascent-research --json doctor --provider-smoke --provider codex
ascent-research --json doctor --provider-smoke --provider claude
If provider smoke fails, STOP and surface the failing provider check. Do not start the research loop or bilingual synthesis until the provider is healthy.
Data home: all sessions, user preset overrides, wiki pages, and rendered reports live under ~/.actionbook/ascent-research/. Override with ACTIONBOOK_RESEARCH_HOME for sandboxing. Upgraders from v0.2: the legacy ~/.actionbook/research/ tree is read-only — new writes land in the v0.3 canonical root.
V2 Browser Backend Setup (one-time, REQUIRED for add/batch on JS pages)
ascent-research defaults to the V2 actionbook MCP backend
(ACTIONBOOK_BACKEND=v2-mcp). Four things must be set up once — without
all four, every browser-based fetch will fail.
1. Actionbook Chrome extension + dedicated profile (recommended)
Install Actionbook Cloud (v2)
in Chrome, v0.2.0-alpha.4 or later.
Strongly recommend a dedicated Chrome profile, for two reasons:
- chrome.debugger conflict: if actionbook shares a profile with a
password manager / AI sidebar / translation extension that injects
content frames into every page, Chrome will refuse to attach with
Cannot access a chrome-extension:// URL of different extension, and
every V2 attach call fails.
- Cookie scoping per profile: fetching logged-in content (X / GitHub
private / SaaS dashboards) requires actionbook to be in the same
profile that is logged in. A mixed-profile setup = unlogged-in fetches.
How to set up: Chrome top-right avatar → Add → new profile named
"Actionbook Research" → install only actionbook in this profile → log
into every research-target site here. macOS one-shot launcher:
open -na "Google Chrome" --args --profile-directory="Profile X"
If a user reports "V2 doesn't fetch / always EXTENSION_OFFLINE / always
attach fails", your first question is whether the profile is dedicated.
2. ACTIONBOOK_API_KEY permanent export
echo 'export ACTIONBOOK_API_KEY=ak_xxxxxxxxxxxxxxxx' > ~/.actionbook.env
chmod 600 ~/.actionbook.env
echo '[ -f ~/.actionbook.env ] && source ~/.actionbook.env' >> ~/.zshrc
⚠️ Never paste the token into chat / IDE prompt / git commit.
ascent-research never echoes the token in stderr / logs.
3. Claude Code permission allow rule
Claude Code's default classifier blocks certain "advanced" actionbook
patterns (XHR interception / screenshot return / sensitive cookie probes),
even when these are explicitly recommended by actionbook's official
manuals. One settings rule whitelists the whole MCP tool permanently:
{
"permissions": {
"allow": [
"mcp__*__actionbook"
]
}
}
Whitelist actionbook narrowly; other tools (reading Chrome profile,
calling arbitrary external APIs) remain protected by the classifier.
Do not use --dangerously-skip-permissions to bypass globally.
4. postagent secrets — one-time register per site
postagent does not read shell env / gh CLI / macOS Keychain — it has its
own secret store:
postagent auth github
postagent auth x
postagent auth openai
postagent auth status
Without a GitHub token, every github.com/<owner>/<repo> URL in the
tech preset hits unauthenticated GitHub API and will almost certainly
hit rate-limit 403.
When to use V2 vs postagent vs add-local
Research target
│
├── Public API (api.github.com / api.openai.com / etc.)
│ → preset auto-routes to postagent → fast (< 500ms)
│ Requires: postagent auth configured (step 4 above)
│
├── Static / semi-static page (official blog / docs / changelog)
│ → preset fallback routes to V2 browser → ~3-5s
│ Requires: dedicated profile + actionbook online
│
├── Heavy SPA + public content (HN / Reddit feed / x.com timeline)
│ → from inside Claude Code, call the actionbook MCP tool directly
│ (Claude writes its own run-code script + controls timeout)
│ → write captured content to local .md files
│ → `ascent-research add-local <dir>` injects them into the session
│ Requires: step 3 allow rule (otherwise classifier blocks XHR interception)
│
└── Login-required SaaS (Linear / Notion / GitHub private repo)
→ same as above, AND the actionbook profile must be logged into that site
→ cannot use postagent (no public API token, or too expensive)
Pitfalls to avoid
document.cookie is not a reliable login check. X, GitHub, and
most SaaS set their session token (e.g. X's auth_token, GitHub's
user_session) as HttpOnly cookies — JavaScript cannot read them.
To verify "is the user logged in", issue a request to an
authenticated endpoint and check status 200 vs 401, do not grep
document.cookie.
- Don't expect 3 s
networkidle on heavy SPAs. GitHub PR pages and
x.com search results take 5-10 s to hydrate. The V2 inline JS now uses
a three-stage wait (DOMContentLoaded + networkidle + body-content
poll, ~16 s worst case) to handle this; if you write a custom
run-code for an even heavier site, mirror the same pattern.
add-local accepts UTF-8 text now (including dense CJK / Japanese
/ Korean / emoji). Pre-2026-05-17 the text detector mis-flagged these
as binary; that's fixed in this build.
- GitHub PR / repo URLs route through postagent, not V2 browser —
if
postagent auth github is not configured, you'll get
fetch_failed even though V2 looks healthy. Step 4 above prevents
this.
- x.com / twitter.com URLs use a dedicated
XTweet runcode flavor
since v0.4.1. The generic networkidle + body-text poll returned only
X's left-nav chrome (~160 bytes) because X's tweet <article> is
GraphQL-hydrated AFTER networkidle, and the page virtualizes (unmounts
scrolled-off articles). XTweet uses waitForSelector('article[data-testid="tweet"]', …) instead, plus snapshot-collect across scrolls keyed by tweetId so
the main tweet survives virtualization. Thread + media (tweet attach
- link card + video poster) are returned as markdown
 so
rich-html renders them as <img> and Obsidian / VS Code preview shows
them. Three explicit preset rules — x-tweet-status (3-seg path),
x-search-live (/search), x-profile (1-seg) — route through this
flavor; everything else x.com routes via fallback (also XTweet by host
sniff).
- Default
--min-bytes is 500 — short single tweets need --min-bytes 200.
A typical single tweet returns ~400-500 bytes (text + author + metrics);
multi-article threads naturally exceed 500. If a known-good single X
URL gets SMELL_REJECTED reject_reason=empty_content, pass
--min-bytes 200. Future spec may auto-adjust per x-tweet-status
kind.
Mandatory Tail (MANDATORY — finish is preferred)
ascent-research loop does NOT render report.html. Before you declare the task done, prefer the single completion protocol:
ascent-research finish <slug> [--bilingual] [--open]
finish runs coverage -> synthesize -> audit and returns non-zero unless all three stages pass. If you need to debug a stage, run the inspection commands individually:
ascent-research coverage <slug>
ascent-research synthesize <slug> [--bilingual] [--open]
ascent-research --json audit <slug>
Rules:
- Always run
finish after loop (or after manual add / write work) before replying.
- If the user asks for Chinese, bilingual output, or 中英文 output, you MUST run
finish <slug> --bilingual; plain finish renders English-only HTML.
- If
finish fails at coverage, do NOT claim the report is complete. Surface the blockers from report_ready_blockers and keep working or ask the user what to relax.
- If
finish fails at synthesize, do NOT claim report.html exists. Surface the synthesize error and keep working or ask the user what to relax.
- If
finish fails at audit, do NOT claim the session is验收-complete. Surface audit_blockers and keep working or ask the user what to relax.
- If
--bilingual reports bilingual_skipped, do NOT claim Chinese output is complete. Fix the provider and rerun finish <slug> --bilingual.
- In the final reply, include the exact
<session>/report.html path and audit_status.
Chinese/bilingual generation:
ascent-research finish <slug> --bilingual --open
ASR_BILINGUAL_PROVIDER=codex ascent-research finish <slug> --bilingual --open
--bilingual produces an English/中文 toggle in report.html by injecting <p class="tr-zh"> siblings. Without --bilingual, the report intentionally has no Chinese paragraphs.
Dynamic Fact Topics Require Fact Check
For live, sports, news, current roster, current price, latest version, market, legal, medical, financial, or other time-sensitive factual research, create the session with --tag fact-check.
ascent-research new "<topic>" --slug <slug> --preset tech --tag fact-check
For sports/current-roster tasks, prefer the sports preset and seed at least one roster source URL from an official roster page, Basketball-Reference team season page, or ESPN roster page before synthesis:
ascent-research new "<topic>" --slug <slug> --preset sports --tag fact-check
ascent-research add "https://www.nba.com/<team>/roster" --slug <slug>
ascent-research add "https://www.basketball-reference.com/teams/<TEAM>/<YEAR>.html" --slug <slug>
When --tag fact-check is present, the loop must emit fact_check actions for concrete person, team, date, number, price, roster, standing, release version, or current-status claims before the final report depends on them. If evidence is stale or conflicting, emit fact_check with outcome:"uncertain" and continue fetching instead of writing a confident assertion.
Before final synthesis on these sessions, run:
ascent-research coverage <slug> --json
Verify fact_check_required=true and fact_checks_total >= 1. If report_ready_blockers contains fact_checks_total or fact_check_invalid_sources, do NOT run synthesize; continue fetching accepted sources and emitting fact_check actions.
GitHub Trust / Fake-Star Audit
For “fake GitHub stars”, repository trust, suspicious star growth, or investor/developer due diligence tasks, start with the deterministic audit hand before asking the LLM to interpret anything. Do not ask the model to decide whether stars are fake from vibes.
ascent-research github-audit <owner>/<repo> --depth timeline --sample 500 --out audit.json --html audit.html
ascent-research new "<owner>/<repo> GitHub trust audit" --slug <owner>-<repo>-trust --preset github-trust --tag fact-check
ascent-research add-local audit.json --slug <owner>-<repo>-trust
ascent-research loop <owner>-<repo>-trust --provider codex --iterations 8
ascent-research finish <owner>-<repo>-trust --open
Rules:
github-audit outputs trust score, risk score, band, confidence, reasons, and evidence; never call a repo “fake” or “real” deterministically.
- If the user asks whether a repo looks trustworthy, show
audit.html first. It is the deterministic scorecard: trust score, risk score, confidence, metric dashboard, reasons, and evidence gaps.
- Use
--depth repo for anonymous quick checks; use stargazers or timeline only when postagent can resolve $POSTAGENT.GITHUB.TOKEN.
- The audit JSON is the evidence artifact. Add it with
add-local before loop so the report cites the deterministic signals instead of re-deriving them.
--preset github-trust is for contextual follow-up sources; it does not replace github-audit scoring.
- Finish still goes through the normal completion protocol:
coverage -> synthesize -> audit.
Mental Model
One research project = one session under ~/.actionbook/ascent-research/<slug>/
session.toml metadata (topic, preset, tags, parent slug)
SCHEMA.md user-editable loop guidance
session.md narrative — Overview / 01·WHY / 02·HOW / ... report spine
session.jsonl append-only event log — authoritative machine state
raw/ every accepted source, one file
wiki/<slug>.md persistent entity + concept + analysis pages
diagrams/*.svg hand-drawn figures, inlined in the HTML report
report.html rendered editorial output with wiki TOC + bilingual toggle
Three-layer control flow:
LLM orchestrator (this skill / active-research / custom agent)
| [CLI ONLY — observability > terseness]
v
research CLI ──> route presets ──> postagent (HTTP)
──> actionbook browser (CDP)
──> local file reader (file://)
| [infra-enforced smell test on every fetched body]
v
session.md + session.jsonl + wiki/ + diagrams/ + report.html
Everything downstream of the CLI is stateless between turns — the agent addresses state only by --slug. The CLI holds the event log and the preset registry.
Command Surface (all of it)
Session lifecycle
ascent-research new <topic> --slug <s> [--preset tech] [--tag t]... [--from <parent>] [--force]
ascent-research list [--tag t] [--tree]
ascent-research show <slug>
ascent-research status [<slug>]
ascent-research resume <slug>
ascent-research close [<slug>]