# Changelog

> Release notes for the Blop CLI, newest first.

<!-- Generated by apps/docs/scripts/gen-reference.mjs. Do not edit. -->

## Unreleased

### Per-agent setup commands

- Connect exposes a copyable `blop setup mcp <client>` for every MCP table client,
  including OMP, in both its agent row and manual configuration section.
  Existing registrations are not forced or replaced by the displayed command.
- Overview's first-spans step uses `blop setup agents`, not an individual agent
  launcher.

### Hook-aware setup checks

- Doctor's setup check now reads the same installed hook receipts and enabled
  capture policies as the connected-agent list. It names the installed hooks
  and capture mode, while retaining unrelated framework setup diagnostics.
- CLI and Connect share this check and the collector's advertised policy path.

### Application run suggestions

- Overview and Connect select run commands only from application framework
  adapters. Agent-only projects use `blop run -- npm run dev`, never a coding
  agent launcher. Existing application framework commands remain unchanged.

### Runnable memory setup

- Add `memory add --kind --title --body` with optional tags and an explicit ID,
  using the existing graph change writer and JSON envelope. Default IDs use
  SHA-256 of the exact title/body JSON tuple, so identical retries return the
  existing node without adding a revision.
- Make Memory's agent prompt and human command executable, and use `search --text`
  for the row's copy-read action. Addressed detail reads still use `memory get`.

### Local Markdown knowledge

- Add `memory markdown init|attach|index|list|detach`: bounded, explicit,
  model-free projection of local Markdown notes, heading sections and references.
  Source folders remain authoritative and read-only; failed scans, including
  detected root replacement or mid-scan edits, preserve the last complete
  snapshot with failure freshness. Unsupported administration flags fail before
  any source or database operation.
- Reuse existing memory search/context/get/neighbors and read-only MCP with
  path/section/hash citations. Stable frontmatter IDs survive renames; deletion
  and detach remove current retrieval without altering authored memory history.
- Keep projection references unapproved and reject authored mutations of projected
  IDs. Local reads distinguish `assessment: not_configured`.
  Resolve exact local trace references without reading or uploading trace bodies.
- Preserve authored assertion and approval provenance when a projected endpoint
  disappears or derived rows are rebuilt; restore visibility when its ID returns.
- Anchor Markdown initialization to Linux procfs directory handles. Unsupported
  platforms fail explicitly rather than reverting to race-prone pathname writes.
- Correct `.markdown` ingestion, code-fence closers and heading links; preserve
  real inner inline links after escaped outer brackets and resolve encoded `#`
  and `?` filename characters while rejecting raw queries.
- Preserve exact 1029-character source IDs and 1537-character path/heading
  citations in graph reads, authored commits/proposals and publication previews,
  including their published CLI schemas, without truncating identifiers.

### Repository memory destinations

- Add `memory bind|binding|unbind` for deliberately selected hosted read
  destinations. User configuration contains no credentials or repository edits;
  subdirectories and Git worktrees share the canonical repository identity.
- Bound CLI/stdio MCP reads reuse the existing profile and managed operations
  with fresh workspace/project/grant checks. Hosted failures never fall back to
  local results. Explicit local CLI scope remains offline; Markdown administration
  and publication consent remain separate.

### Optional read assessment

- Add a shared schema-validated assessment boundary and explicit HTTP adapter,
  without a mandatory model, new agent tool or provider fallback.
- Preserve read data while adding task/query-bound receipts, exact source
  revisions/hashes, graph/policy/assessor versions and honest partial coverage.
  Selected memory never claims to cover unseen working-tree files or diff.
- Enforce configured assessment on hosted direct, managed, CLI/MCP and frozen
  chat snapshot reads. Timeout, invalid output, changed sources or revoked grants
  return no unassessed fallback. Authorization stays deterministic code.

### Controlled Markdown publication and web receipts

- Publish one explicitly selected indexed Markdown snapshot using the existing
  destination/payload preview, reviewed hash and hosted owner approval. No
  directory upload, source reread, synchronization, reference-edge transfer or
  projection authority. Hosted corrections remain separate explicit edits.
- Preserve citation identity on no-op reindex so unchanged publication retries
  retain their exact receipt; changed indexed content invalidates the preview.
- Show assessment status, findings, bounded coverage and exact source revisions
  in the web memory browser, including history. Query/detail failures clear stale
  detail rather than presenting it alongside a fail-closed error.

### Retained integration source receipts

- Resolve `blop.external-link` references against existing Linear, Jira and
  GitHub issue/PR link receipts, through the current owning project/team grant.
  Exact receipt IDs, optional recorded URLs and UTC capture timestamps are
  checked without loading provider bodies or returning denied source hints.
- Refresh accessibility after ownership/deletion changes while retaining authored
  knowledge/history. Unsupported content hashes remain unknown; an available
  retained receipt is neither live provider freshness nor claim verification.
- This does not add a general ticket/PR/CI ingestion feed or source cursor.

## 1.9.0 (2026-11-04)

### MCP release gates

- Frame stdio requests on LF rather than Node's text-line separators, preserving
  U+2028 and U+2029 inside JSON strings on Node 24, split UTF-8 characters, CRLF,
  and the final request at EOF.
- Keep the generated MCP client table synchronized with the README only. The
  generated docs CLI reference no longer has a hand-synchronized MCP block.

### Inspector UI rebuilt on the design system (#664)

- Every Inspector page is rebuilt on the design system and reads the real
  collector APIs, with in-place live updates on one shared event stream and no
  page reloads. Focus, selection, open panels and filters survive updates.
- New guarded local writes: `DELETE /api/runs` (dry run first, running runs
  refuse) and `POST /api/inspector/settings/limits`. New reads:
  `/api/inspector/overview`, `/api/inspector/memory`, `/api/inspector/cloud`
  and `/api/test-coverage/history`.
- Cover text runs through typed escaping; writes need same-origin JSON plus a
  marker header. The overview gains a "when failures happen" heatmap (#665).

### Collector QA fixes

- Worker and metric CLI overview paths no longer send a bare ? separator when no filter is set (Node 24 request framing).
- Redact credentials in log bodies from unregistered (plain OTLP) sources, as
  their attributes already were, and cap those bodies at 64 KiB. Rows stored
  before this change are not rewritten.
- App-error issue titles lead with the recorded exception, e.g.
  `TypeError: Cannot read properties of undefined (reading 'amount') in POST /api/checkout (HTTP 500)`.
- Server spans without `http.route` are fingerprinted on the request path
  (`url.path`/`http.target`, ids normalized), so errors on different endpoints
  are separate issues. Such issues regroup once: new occurrences get a new id.
- Hook sessions end: a measured end closes its start span, a `turn_end`
  closes the open turn when the host has no turn ids, and `session_end`
  closes whatever the session left open.
- The Issues list wraps the "What happened" title (up to 3 lines) instead of
  cutting it at 28 characters, so issues that share a prefix are told apart.
- `blop doctor` fails the port check when a non-Blop process holds the port.
- Too many requests in flight answers 503 `overloaded` with `Retry-After: 1`
  instead of `capture_unavailable`, and the connection ceiling is raised so a
  burst gets that 503 rather than a dropped socket.

### Inspector Logs implicit time range (#662)

- Use the default 24-hour Logs range when the URL has no range, including
  history traversal, instead of restoring a remembered selection. Keep explicit
  URL ranges authoritative, consistent with Sessions and Issues.

### Demo MAST contract (#654)

- Align the demo failure test with the current MAST policy: a downstream app
  failure alone establishes neither a mode nor a rule. Keep the classifier and
  the demo's issue, span, and memory-read assertions unchanged.

### Run e2e stitching fixture (#653)

- Exercise the OpenCode plugin OTLP tool-span capture contract alongside AI SDK
  model calls and app spans in the `blop run` end-to-end fixture. Generic AI SDK
  spans alone are application telemetry, not coding-agent evidence for doctor
  stitching. Keep the strict stitching check and verify the plugin-to-app causal
  chain through MCP.

### Quiet hook callbacks (#647)

- Native hook events queue durably before the bounded delivery-time identity
  probe. Offline, restarted, slow, redirected, old or different-store collectors
  do not produce host callback errors or output; mismatched identities receive
  no telemetry POST. Pending events drain when the bound collector answers.
- `blop agents status` and `doctor` expose pending events, paused destinations
  and held-delivery reasons. Native integrity, input, outbox and enqueue failures
  remain loud and return non-zero.
- Legacy `blop agent hook` delivery failures, including an exhausted budget
  after enqueue, are silent. Configuration and capture-loss diagnostics keep
  their existing exit behavior. Legacy queued rows without stable collector
  identity retain destination-only delivery; native legacy callbacks check the
  recorded process instance, but background replay cannot protect store identity.

### Medium review hardening

- Recorded argv also masks prefixed/camelCase credential flags and `-u` basic-auth
  values, without changing the launched process's arguments or treating `-p` as
  a password flag. Init preflights a redirected `.blop` parent before any writes.
- OpenCode resolves the collector before configuration writes, rolls back its
  edits when setup-state publication fails, and refuses switching config paths
  until the existing setup is removed. Pinned collectors use the version handshake
  and report registry ownership rather than being unconditionally external.
- MCP client and marketplace deadlines terminate owned process groups, escalate
  after a bounded grace period, and settle even when descendants retain pipes.
  Verification-skill commands apply the same bounded teardown. Desktop installer
  locks retain owner identity and offer explicit stale-lock recovery instructions;
  a live installer is never automatically displaced.
- Agent wiring snapshots only setup-owned files, not retained telemetry/outbox
  contents. Bulk MCP repair addresses every stale scope and preserves collector
  pins, including environment-based URLs. MCP results reject oversized fixed
  sections and disclose content/diff cuts made by replay previews.
- Evaluation HTTP and CLI share the 128 KiB input limit and the same opaque-ID
  and actor contracts as storage. Interrupted/malformed mutation receipts retain
  unknown-outcome guidance. Manage-only queue authors and review-only completers
  no longer need unrelated read grants; assignment removal releases outstanding
  claims with revisioned audit, and next-item selection skips stale evidence.
- Fresh runs replace inherited `blop.run.id` while preserving other user resource
  attributes. Usage reports enforce their upper time bound, cap group discovery
  and retain validated unscoped Codex counters. Worker raw commands preserve
  leading collector selectors and honor the final output format. Run usage
  errors preserve `--report` after environment and trigger flags.
- Flow discovery traverses explicitly requested directories outside cwd and
  accepts `./`-prefixed globs. Optional-loader errors distinguish absent packages
  from broken transitive imports/exports; repair guidance uses the CLI's original
  installation context, including setup-doctor warnings.
- OpenCode rollback/removal restores original bytes without a UTF-8 round trip.
  MCP conflict diagnostics disclose clipped-key collisions and exhausted node
  budgets; refused MCP rollback selects the drift recovery hint. Init destination
  refusals use structured invalid-config errors, failed alert sends include a next
  command, and recorded argv reserves the computed shell-quoted marker size.
- Collector hostname aliases require the saved collector identity rather than
  equating IPv4/IPv6 origins blindly. Native rollback's deletion-only exception
  rejects added duplicates of owned callbacks. Flow evidence rejects fields
  whose credential redaction expands beyond the byte contract instead of
  silently clipping an identifying signature.
- Doctor normalizes IPv6 socket hosts and HTTPS default ports, shares strict active
  Codex exporter checks, and honors OpenCode's active config override. Pi extension
  recipes are unverified/not-applicable rather than false failures. Monitor init
  respects preferred config precedence; status MCP and foreground-stop hints pin
  the resolved collector. Served OpenAPI3 schemas use nullable scalar types and
  declare alert-action path parameters.
- Storage compaction checks free capacity on each SQLite volume independently.
  Resource retention uses indexed bounded age batches; concurrent legacy-log
  migrations recheck columns under the write lock. Span status filters include
  server 5xx failures and retain successful spans with unspecified kind.
- Delete previews enforce the store-format gate on a read-only connection.
  Deletion and retention remove trace/conversation bookmarks only when their
  exact retained subject disappears, without resurrecting them on recapture.
- Issue retractions repair surviving evidence timestamps/titles independently of
  regression state. Comparisons use one actually observed service/kind/operation
  cohort on both sides and include null-kind/status-less successes. Provider 429s
  retain an error causal chain without promoting ordinary app/tool 429s.
- Service reports select distributed traces through failed spans in that service.
  Issue navigation preserves failure-class filters, and muting/fixing an issue
  removes its owned expanded fix-context preview.
- Why applies project eligibility before limiting open issues and selects that
  project's own occurrence, run and trace for shared fingerprints. Git authorship
  recognizes only final trailer paragraphs, not coauthor examples in body prose.
- Automatic hook setup preflights native files before enabling capture. Late
  filesystem failures report retained paths, current consent and the disable
  command rather than imply rollback. Native removal resumes exact completed
  restoration steps after interruption while still refusing unrelated drift.
- Hook and application consent writers share a cross-process lock. Inspector
  edits verify their expected revision under that lock and return conflict
  instead of overwriting newer capture restrictions.
- Amp, Goose and Hermes project observers accept owned descendant directories
  while retaining the shared bridge's nested/outside-repository refusal. Amp
  propagates public-text selection truncation. Hermes forwarding failures emit
  fixed content-free diagnostics without vetoing host callbacks.
- Cursor compares complete owned handlers, including timeout, while preserving
  foreign entries. Missing installed modules are caught inside its callback
  boundary and still produce the neutral observer response.
- Disabled hook bindings are excluded before the deliverable batch limit, so
  held rows cannot starve another binding at the same destination. Replacing a
  queued start atomically updates lifecycle and pending-end pairings, with new
  audited identities for changed ends and content-free replacement mappings.
- Memory initialization applies a hook's SQLite busy timeout before schema writes,
  not after a fifteen-second wait. OpenCode session-location lookups have a 500ms
  deadline; cleanup abandons pending lookups and late results cannot enqueue an
  event after capture stops.
- Collector startup brackets IPv6 URLs, probes busy ports on their actual host,
  and filters registry reuse by an explicitly selected host. Foreground startup
  refuses an already healthy IPv6 collector rather than starting a duplicate.
- Lost-run sweeps rotate through bounded pages of runs and launchers, so live
  entries cannot permanently hide later dead processes. Live events page run
  starts and completions independently in each cursor's order.
- Run, imported-session and app-trace live-event cursors use persistent insertion
  sequences instead of reusable SQLite rowids. Purging the newest or every row
  cannot hide recaptured evidence; upserts retain their original insertion order.
- Bash, Zsh and Fish live completion honor collector flags on the command line.
  Subcommands offer their own flags and values without list-only parent flags.
  Bash file candidates preserve whitespace and literal glob characters.

- Agent launchers refuse explicit collectors that conflict with saved capture
  bindings and fall back to an unchanged plain launch after wiring refusal.
  OpenCode native telemetry uses the same selected collector as hooks and MCP.
  Undo preserves pre-existing identical MCP entries; conflict and hosted-doctor
  diagnostics do not expose saved credentials or memory content.
- Recorded commands mask credential aliases without hiding adjacent flag names.
  Copyable collector hints quote URLs for the shell. JSON metadata keeps authored
  dictionary keys, prompt versions preserve UTF-8 BOM bytes, `config show --host`
  accepts its advertised override, and pending prompts take the default on EOF.
- Init refuses symlinked output files before writing. Collector prerelease
  versions follow SemVer ordering. Why retains an issue-free trace's inspector
  link and escapes producer-controlled reasons; failure-context escapes all
  terminal fields and pins trace identity on span drill-down.
- Memory publication preserves rule guards in the reviewed payload and hash.
  Replacement edits clear omitted optional content, pinned rules respect project
  applicability in inherited briefs, and approved purge remains possible after
  access to cited evidence is revoked. Read-log queries accept their 500-node cap.
- Native hook identity checks and delivery share one one-second callback budget;
  slow or unavailable collectors leave events durably spooled before host
  deadlines. Project-only adapters retain actionable rollback-drift errors
  without probing unsupported global bindings.
- Alert shutdown waits for active evaluations and delivery receipts before closing
  storage. A drain requested during a send includes newly queued transitions;
  paused-only rules still expire history, and failed `alerts test --send` exits
  nonzero while retaining its delivery diagnostic.
- Metric previews select exact newest nanosecond points before limiting, totals
  have their own serialized-byte budget, and windows without a contributing
  delta interval remain unknown. Overview service edges use trace-wide app
  failure and exclude internal collector services.
- Protobuf log event names reject invalid wire types. Captured content reports
  truncation from actual byte clipping, not a producer's literal marker suffix.
- Flow evidence projection redacts credentials from service names, operations and
  fallback error-log signatures before grouping or returning context.
- The Python application session example batches span export off the asyncio
  event-loop thread, retaining explicit final flush and shutdown.
- Sequential monitoring and hook test commands use Node's native test timeout,
  removing the GNU `timeout` dependency on macOS. Vector-backend PR checks cover
  `main` and both development integration branches.

### Review hardening

- Run JSON/report output, stored argv and lifecycle command lines now use the
  same credential-redacted, bounded representation. Agent runs without content
  capture retain only argv0; split credential flags are masked without changing
  the child's arguments. Fix-context repros and latest-run notes redact before
  collapsing control characters.
- Metric capture classifies original attributes before redaction and omits
  nested content-bearing containers without consent, preserving typed metadata
  without promoting containers into session or worktree identifiers. Unscoped
  numeric token counters retain accounting validation instead of being mistaken
  for credential attributes.
- MCP conflict descriptions mask camelCase credential fields and bound depth,
  nodes and output bytes. Failed recovery-snapshot writes preserve the original
  client failure and report that the previous entry could not be saved.
- Alert history expires during steady-state evaluation as well as transitions.
  Pending events retain their selected destination, including locally recorded
  events promoted on connection; disconnect pauses queued sends. Slow hosted
  delivery no longer blocks evaluation. Agent-connection and native-hook rollbacks
  refuse later byte edits; invalid project configuration blocks collector startup.
- Structural replay titles use registered agent labels only; unknown agent
  values cannot inject content or inherited object properties into hosted titles.

### CLI core jobs

New commands

- `blop why [<run_|iss_|ses_|trace id> | --last] [--all-projects]` explains one
  failure: what broke, the agent step and app span where it broke, the
  deterministic MAST mode (`blop.mast/v1`) and failure kind, and the memory the
  agent read in that session. It ends with the next command and an inspector link.
  A demo answer is marked as demo data and never hands its issue to a coding agent.
- `blop runs [--failed --since --limit --branch --all-projects]` and
  `blop runs show <id> [--timeline]` list the runs of this repo and compare a run
  with the previous run of the same command.
- `blop demo [--clean [--yes]]` records a passing and a failing demo run, an
  issue and a memory read, all tagged `blop.demo=true`, so `blop why --last` has
  an answer before anything is instrumented. `--clean --yes` removes only demo data.
- `blop claude`, `blop codex` and `blop opencode [-- args]` start the agent with
  Blop capture wired: the first launch in a repo wires the hooks, the MCP entry
  and the capture binding (project scope), lists every file it changed and ends
  with the exact undo line. They fail open and return the agent's own exit code.
  `-p <n>` and `--url <url>` before `--` name the collector.
- `blop config show [--json]` prints every setting with its value and source.
- `blop help all` lists every command; root help shows the core ones.

Changed behaviour

- Bare `blop` is now the status table plus one `Next:` line. It starts a stopped
  collector in the background, never prompts and no longer opens the browser.
  `blop start` is an alias of `blop up`.
- `blop status` text uses the same table as bare `blop`. Its `--json` output only
  gains fields, and the envelope `next` now carries the next step.
- `Next:` lines and hints carry the `-p` or `--url` you passed, so the suggested
  command asks the same collector.
- Settings resolve in one order: flags, `BLOP_*` environment, project
  `.blop/config.json` (`.blop/monitor.json` is still read), user config, defaults.
  An invalid `BLOP_*` value is ignored with a warning that names it.
  `blop init` writes `.blop/config.json` and the gitignore lines `.blop/*` and
  `!.blop/config.json`, so the config can be committed.
- Coding agents (`CLAUDECODE`, `CODEX_SANDBOX`, `AI_AGENT`, `BLOP_AGENT=<name>`
  and similar) get `blop.cli/v1` JSON by default from `runs`, `runs show`, `why`,
  `issues`, `issue` and `status`. `-o text` and `BLOP_AGENT=0` keep text.
- `blop doctor` has a `warning` status for checks that do not fail the setup
  (for example a missing optional package).
- `blop run` keeps the child's exit code and summary through Ctrl-C, escalates
  SIGTERM and SIGHUP after a grace period, and prints a local summary when the
  collector never answers.

Store and collector

- A newer or unrecoverable store exits 78 (do not retry). `blop up --recover`
  moves the store aside, never deletes it, and starts an empty one.
- The size cap is enforced, oldest activity first, with recent traces protected.
  Status and health report busy retention passes and a WAL held by a reader.
  `blop up --compact` compacts a large legacy store on request.
- The collector exits 70 when its capture worker is exhausted, and a managed one
  in that state is replaced.
- `runs` record their argv and project root (additive, nullable columns).

Agent hooks and MCP

- Native hooks spool to the outbox before contacting the collector, so a down or
  hung collector never loses an event. Hooks bind to a stable per-store collector
  identity, so a collector restart no longer stops capture. Bindings made before
  this change need `blop agents setup <id>` once.
- A project binding accepts hooks from the repo root or any directory under it
  that no nested git repository owns.
- New MCP tools `list_runs`, `get_run`, `why` and `open_link` mirror the CLI.
- `blop setup mcp <client> --remove` on a JSON config gives back the exact bytes
  from before Blop's add when nothing else changed since (or deletes the file Blop
  created); otherwise it takes out only Blop's entry.
- A project hook rollback blocked by global hooks installed later
  (`blop setup agents`) names the global removal to run first.
- `blop setup opencode --remove` on an OpenCode config edited since puts back the
  user's own `experimental.openTelemetry` value (such as `false`) instead of
  dropping the key. A setup record from an older Blop that did not record its
  edits now says the config was left as is, and what to take out by hand.
- `blop codex` and `blop setup mcp codex` work with Codex 0.165, which prefixes
  "No MCP server named 'blop' found." with `Error: `; before, a fresh machine was
  told its Codex install was broken and nothing was wired. Checked against the
  real Codex 0.165.0 binary in a sandbox.
- The first `blop codex` launch notes that Codex runs new hooks only after you
  trust them (`/hooks` in Codex); `codex exec` skips untrusted hooks silently.
- A client that runs but refuses (such as a missing `CODEX_HOME`) is no longer
  called a moved or removed install; the hint says to fix what the client reports.
- `blop claude -p <port> <args>` without `--` notes that `-p` went to the agent
  and prints the `--` form that names the collector.

Install

- `npm i -g @blopai/cli` works: `@blopai/test` and `@blopai/browser-harness` are
  optional dependencies. `blop test` and flow checks without them exit 2 with an
  install hint.

Removed

- `blop verify` (and its MCP tool, routes and columns): Blop has no trustworthy
  basis for saying a fix worked. Mark an issue fixed in the inspector.
- `blop connect` and `blop disconnect`: the launchers wire on first launch, and
  `blop agents setup`, `blop agents install-hooks` and `blop setup mcp` do the
  pieces (each with `--remove`). Typing a removed command is the plain
  unknown-command error (exit 2).

Known limitations are listed in [docs/cli-spec.md](https://github.com/blop-oss/blop-app/blob/main/docs/cli-spec.md),
section 11.

### Automatic agent hook setup

- Add `blop setup agents` to detect installed coding agents, start or reuse a
  local collector, and install supported user-global hooks in one command.
- Preserve existing modes, exclusions and foreign settings; support explicit
  `--capture`, `--url`, `--port` and per-agent JSON results. Repeated runs do not
  duplicate hooks; failures do not prevent other agents from being installed.
- Keep native OTel recipes and MCP setup separate, and preserve host activation
  requirements and the distinction between installed hooks and live delivery.

### Agent CLI status clarity

- Show capture enabled/disabled, mode and policy revision in `blop agents status`,
  including metadata-only policies whose content permissions do not change when
  disabled. Keep live delivery explicitly unverified.
- Label native telemetry tiers as static capabilities in agent detection and
  status output, separate from the current hook setup. Status rereads state on
  every invocation; JSON contracts are unchanged.

### Web audit stack integration

- Keep the current CLI and plugin shell while replaying the audit fixes. Share
  local-time date, duration and percentage formatting across current web pages;
  expose local collector Issues through the query catalog without mixing hosted
  test-failure counts.
- Disclose taken sign-up emails in the restored password flow without linking
  retired password recovery. Refuse cross-workspace invitations, distinguish
  already-confirmed email links, and retain chat step labels and measured timing.
- Keep permission-denied 403s on the current page. A valid session whose workspace
  membership ended goes to workspace setup instead of sign-in; current shell,
  workspace and billing reads use the same discriminator.
- Hide unshipped per-account notification preferences; existing links continue
  to shipped workspace alert destinations. Preserve shipped Discord and export
  integrations and show paid-plan restrictions consistently.
- Regenerate the triage last-run migration as `0074_flaky_sway`, after the current
  alert-rule and agent-commit migrations, with a merged-schema snapshot.

### Agent graph and map

- Add the hosted Agent Map and session Graph views, `blop map`, `blop session`,
  and the read-only `get_agent_map` MCP tool alongside `get_status` and `get_audit`.
- Preserve the collector's session-tree, capture-policy and durable-metric reads;
  project the agent graph separately and stamp validated hook parent-link attributes.

### Infrastructure resource samples

- Project accepted, policy-sanitized OTLP metrics into latest CPU, memory and
  host/instance uptime samples in the same transaction as durable metric points.
- Keep the existing unknown-payload ingest API, precise metric history and
  protobuf error responses; malformed or disabled-source points never feed samples.

### Inspector: Local Inspector v3 layout

- **Shell:**
  - The inspector follows the Local Inspector v3 design (`docs/inspector-source/v3/`).
  - Content sits in an inset rounded frame. The rail is 232px, and the current page uses the raise fill.
  - A 48px top bar shows This machine, the collector address and the page name.
  - Page titles are 20px. The header holds the page's CLI equivalent and its range control.
  - CLI chips are checked against the CLI registry. Detail pages show none.
  - Phones keep the app bar and drawer. Tablets get the 64px icon rail.
- **Home:**
  - A status strip: Needs attention, Capture, Agent.
  - Overview tiles joined in one strip.
  - One span activity chart with errors stacked in red. Its axes are HTML, so they stay readable at any width.
  - The rail's Issues item counts open issues.
- **Pages:**
  - Tables sit in cards with small uppercase headers. Tabs are pills.
  - Sessions gets a one-line summary. The throughput chart sits in a card.
  - Memory rows expand in place to Body, Connected to and Provenance, fetched on demand.
  - Issue rows lead with Fix context. It opens the exact text it copies, and Escape closes it.
  - The failure heatmap draws hits in red, with HTML axes.
  - Run detail reports Test result, Capture health and Runtime evidence separately.
  - Connect leads with endpoint, agent and skill cards. Every command is real.
  - Settings opens with what agents can do. The local MCP server only reads, so each write names where it happens.
  - Coverage's empty state offers the address to publish to, and a Refresh button that reports what it found.
- **Responsive:**
  - The observation graph uses one column on narrow screens.
  - Long branch names, commands and filter options shrink inside their controls.
  - Every page fits 320px with no sideways scroll (`test/monitoring/inspector-v3-responsive.test.ts`).
- **Keys:** g m opens Memory, and g o opens Coverage.

### Inspector: overview charts from the Blop App design

- Home: an Overview above span activity, on the same range. It has four tiles
  (traces, failure rate, p95 trace duration, model cost), each with its change
  against the previous period of equal length and a sparkline. It also shows
  trace outcomes as a donut and model cost by model.
  - When retention removed any of the previous period, its changes are unknown,
    never computed against a partial count.
  - Cost comes only from priced model-call receipts. Unpriced calls are listed
    as unpriced and kept out of totals and shares, never counted as zero.
- Issues: failure classes over the last 7 days, and a day-by-hour heatmap of
  when failures happen (local time).
- Runs: finished runs per day over 14 days, with failures stacked and the pass
  rate as a line.
- Each read is one bounded read-only snapshot (newest 10,000 rows) with a
  visible truncated state. Each chart has an accessible summary and a data
  table with the same numbers, and is server-rendered SVG with no inline styles
  (the inspector's CSP stays `style-src 'self'`). New fragments:
  `/inspector/fragments/charts/{overview,issue-patterns,runs}`.

### Inspector: Blop App v20 dark theme

- The inspector now matches the Blop App v20 design (`docs/inspector-source/`):
  graphite surfaces (`#161616` page, `#141414` panels), `#ededed` text, one blue
  accent for the current item, selection, focus and chart data, and status
  colours for meaning only. Dark only (`color-scheme: dark`).
- Inter for the interface and Geist Mono for code, both self-hosted from the
  collector (`/inspector-fonts/inter-latin.woff2`, `inter-latin-ext.woff2`); no
  CDN. `scripts/vendor-geist.mjs` now vendors Inter as well.
- The rail, nav items, buttons and fields take v20's proportions: 34px nav rows
  with 8px corners, the current page tinted blue, filled buttons in `#2563eb`
  so white labels hold 4.5:1, and smaller page titles (24px).
- `--pass`, `--warn` and `--idle`, which Memory and Settings read but the theme
  never defined, are now defined, and their light fallbacks are gone.
- A contrast test holds every text token at 4.5:1 and every status colour at
  3:1 on each surface.

### Inspector: memory traversal, provenance and session associations

- Traverse one, two or three hops with explicit `hops` and `cursor` URL fields,
  200 nodes per page, previous/next navigation, and exact omitted-node/edge counts.
  Draw only stored edges whose endpoints belong to the selected scope and page.
- Cite each drawn edge in its SVG title and readable relation list using the
  stored `memory_edges` primary-key tuple `(from, type, to)`; no standalone edge
  ID exists. The drawing retains its 160-edge cap and reports omitted edges.
- Resolve collector provenance once at startup: `blop up --project <id>` and
  `blop monitor start --project <id>` override `BLOP_PROJECT_ID`. Show the git
  origin and repository root without inventing a mapping to authored project IDs;
  unresolved identity remains labelled unresolved with all-local scope. Project
  selection, including an explicit all-local selection, overrides the default.
  Refuse reuse when a requested project differs from the running collector.
- Omit git origins that cannot be parsed rather than exposing raw credentials in
  collector health or inspector provenance.
- Preserve the running collector's startup project during automatic version
  upgrades, including all-local scope, instead of inheriting the invoking
  command's environment. Keep an explicit restart project authoritative; leave
  collectors running with a version warning when their scope or identity cannot
  be verified safely.
- Show separate curated-memory/recorded-evidence session and trace associations
  from exact saved source identities, reusing local accessibility resolution.
  Associations neither merge records nor prove session use or causality; the
  evidence graph has no incoming memory-reference relation.

### Memory: shared libSQL and PostgreSQL vector backends

- Export a SQL-free `VectorBackend` contract with structured scopes, revisioned
  writes, cosine rows, indexing coverage and bounded cursor pagination.
- Preserve local DiskANN/exact scoped retrieval, native warning strings and
  synchronous write-transaction isolation; keep vector SQL in backend modules
  for the memory store and runtime-evidence adapter.
- Add an optional dynamically loaded `pg` adapter with pgvector `vector(384)`,
  HNSW/IVFFlat cosine indexing, scope-before-ranking and pinned model guards.
  No driver dependency or hosted graph migration is imposed on local users.
- Exercise identical backend assertions against temporary libSQL and real
  PostgreSQL, with explicit skips when no PostgreSQL URL is configured.
- Leave inspector views unchanged under their ownership restriction; their
  legacy vector SQL/interface migration remains blocked on permission to edit
  those modules. Backend guarantees and boundary: `docs/vector-backends.md`.

### Inspector: Settings command actions, deep links and truthful states

- Give overview commands native keyboard-accessible Copy controls through the
  shared page-action handler, with polite success/failure announcements and
  visible commands when scripting is unavailable. Redacted API-key values have
  no copy control; configured MCP argv is never exposed as arbitrary copy text.
- Serve sticky section navigation and native `?view=settings#connected`,
  `#agents`, `#memory`, `#evidence` and `#ask` anchors before scripts run.
  Preserve namespaced overview IDs and anchors across capture-policy swaps;
  restore detailed-section fragments after asynchronous rendering and preserve
  the selected project when following section links.
- Distinguish missing evidence from unreadable stores and present memory from
  an unbuilt semantic index. Scope capture freshness to the explicitly selected
  project's identity-resolved hooks, not binary detection or another project's
  activity. Read at most the newest 1,000 collector observations within seven
  days through the timestamp index before parsing metadata; label missing
  sampled capture and excluded older activity explicitly. Unavailable metadata
  and unselected projects stay unknown.
- Preserve validated credential-free collector URL/port pins in stale MCP
  repair commands; withhold unsafe or ambiguous pinned repairs.
### Inspector: read-only, scope-truthful Memory view

- Enforce native read-only graph and evidence handles with SQLite URI `mode=ro`;
  semantic reads preserve schema and vectors from obsolete models.
- Compute semantic coverage from active, index-eligible nodes in the selected
  project and current model/revision, without letting retired or foreign nodes
  block recall. Apply kind/status filters before the 100-hit semantic bound.
- Keep meaning controls and canonical detail/revision/semantic URLs synchronized
  across fragments. List reads return metadata only, bounded to 200 rows.
- Omit unreadable node records with scoped corruption counts. Resolve exact
  evidence identities locally; missing or unreadable evidence never inherits a
  saved positive accessibility label or an unconfirmed link.
- Measure omitted relations, revisions, graph nodes and graph edges separately.
  Show immutable addressed content, source/relation provenance, revision actors,
  and revision-pinned CLI commands with Unicode body-section continuation.

### Universal agent hooks: selectable global or project capture

- Extend native global/project hooks to all 11 registered adapters: Claude Code,
  OMP, Pi, OpenCode, Hermes, Gemini CLI, Codex, Goose, Amp, Cursor CLI and Crush.
  Setup, install-hooks, status, doctor, capture-policy, run and env use selectable
  scopes. Without `--project`, scope defaults to global; `--project <local-id>`
  implies project scope. Explicit project requires a project; global rejects one.
- Use public native hook/plugin contracts without exact host-version allowlists.
  Agent info/list JSON exposes native, global and version-independent hook
  capabilities with refusal reasons. Unknown or incompatible callback formats
  still fail closed; implemented does not mean every release/platform is certified.
- Capture global callbacks under one explicit global source/project label.
  Exact-physical-cwd project-native installations take precedence even when
  disabled; matching drift fails closed without global fallback. Preserve
  collector identity, bounded/redacted local delivery, outbox and policy controls,
  including existing metadata-only reductions and exclusions. New bindings default
  to full supported exposed content with 30-day body retention, never hidden
  thinking or attachment bytes.
- Keep observer integrations non-intercepting: Cursor/Amp omit pre-tool and
  permission interception; Goose's PreToolUseResult observer cannot change
  approvals; Crush exposes top-level PreToolUse only. Unexposed callback content
  remains unavailable rather than being reconstructed from private history.
- Preserve foreign host settings/plugins and preview/apply, atomic writes and
  hash-drift-safe removal. Native-only launchers have no fabricated artifacts or
  fallback: run requires real native installation. Version-2 receipts explicitly
  mark each file's host-config role; version-1 receipts remain safely removable.
  Adapter seams accept config-path/context information for multi-file native
  formats. Idempotent apply correctly reports no changes.
- Resolve `builtinAdapters` through lazy getters to break the real shared-renderer/
  registry import cycle, while retaining extensible registry lookup.
- Observed gates: package build passed; a plain OMP launch delivered 14 global
  events including real tool/reply activity. Pi delivered 9 lifecycle/auth-failure
  events and OpenCode delivered 5 prompt/context/model-error events before
  provider 401 failures. Gemini delivered no callbacks because auth/client and
  workspace-trust gates blocked it. Codex's model run succeeded but its hook
  source/session query returned no events; native `/hooks` trust review remains
  required, with no trust bypass written. Hermes, Goose, Amp, Cursor and Crush
  were unavailable locally; source-grounded callback/bridge fixtures are not live
  host/model delivery. No aggregate test-count or blanket certification is claimed.
- Retain prior Claude Code 2.1.310 evidence: six main-cycle callbacks from global
  and bound-project launches without competing-scope duplicates, with prompt,
  public reply and tool input/output. Subagents, compaction and errors were not
  exercised; this does not qualify the separate native-plugin mechanism.

### Inspector integration: durable metric-only sessions

- Keep metric-only sessions marked Unknown in server-rendered htmx rows as well
  as client-rendered rows. Their exact source/session links expose recorded
  metric totals separately from spans; marking a row wrong cannot fabricate a
  trace-backed issue.
- Retention preserves both metric pruning and consent-bound log/native-span
  body expiry.
- Forward request headers to the capture child before body bytes arrive, so
  route-specific upload limits return 413 immediately instead of a transport
  timeout when an oversized client sends only its declared Content-Length.

### Inspector: chart ranges and reachable empty states

- Home's span-rate, error-rate and p95 charts follow one URL-backed range
  selector, including reload and Back/Forward. Home and Issues chart fragments
  accept the shared presets from 5m through 30d and `all` (retained data, up to
  30 days), while preserving explicit `from`/`to` windows.
- Home and Issues leave an absent range implicit: Home defaults to the last
  24 hours on both the page and chart fragment; Issues defaults to all retained
  data (up to 30 days). Neither rewrites the URL or borrows a remembered
  selection. Explicit changes push history; Back restores the parameter-less
  default window. Sessions and Logs likewise add no default range on mount.
- Empty Home windows identify older retained spans and offer a one-click
  30-day range. Empty retained ranges, expired buckets and capped samples
  remain separate states.
- Issue sparklines aggregate occurrences over the same selected window as
  their list, rather than silently showing a fixed 24 hours. Charts retain
  bounded grids, conditional ETag refresh and activity-driven htmx updates;
  Sessions and Logs retain their existing explicit range-change behavior.

### Inspector: a Resource view in the step detail

- The selected step's span data is a visible Attributes / Events / Resource
  tablist instead of a collapsed "Advanced span attributes" block. The view is
  in the URL (`panel=events|resource`), so a reload returns to it and
  Back/Forward step through view changes.
- Resource groups the span's resource by OpenTelemetry namespace (Service,
  Telemetry SDK, Process, Host, Container, Deployment, Cloud, Other), with
  counts, mono values, a copy control per row and "Copy all as JSON" of the
  bounded set the collector returned. A search filters the loaded set
  client-side ("Showing N of M"); it never reads again or changes the URL.
- It tells the absences apart: an exporter that sent no resource, a span that
  is no longer retained, and a metadata-only agent hook source whose resource
  is only Blop's source stamp. `/api/inspector/span` adds `resourceTotal` and
  `resourceSource`; the 60-entry and 2,000-character caps are unchanged.
- Closing the shortcuts sheet no longer takes focus back from a field focused
  before its queued close event, which could send `g i` typed there to Issues.

### Inspector: reactive lists and a light, denser look

- Add a small dependency-free reactive layer (`/inspector-reactive.js`): a
  state cell, a resource controller with scope generations, one live-feed owner
  per document, keyed DOM patching and disposable mounts. Sessions, Logs, Runs
  and the Settings capture-policy editor use it. A filter change aborts older
  reads, so a slower reply for the previous scope never paints; a failed new
  scope drops old rows instead of showing them as matches; kept rows keep their
  node, focus and scroll position across live refreshes.
- Logs refresh live behind the shared Pause / "N new" control and offer
  Maple's time-range presets inside the 24-hour log bound (5m, 15m, 30m, 1h,
  6h, 12h, 24h; 7d and 30d are listed as unavailable). The log sheet is a
  right-hand drawer.
- The capture-policy editor keeps draft, accepted policy and write outcome
  apart: a 409 or an unacknowledged save locks saving until "Read accepted
  policy" reads the current revision; repeated submits write once.
- The inspector now uses light surfaces with hairline borders, tabular numbers
  and dot-and-word indicators; every colour is a theme token.

### Hook adapter package

- Move hook implementations into `src/hooks/`: a single `HookAdapter` registry
  owns host formats and launch conventions; shared engines retain binding,
  outbox, install/rollback and capture behavior. Existing module exports remain
  intact. Add a public adapter-authoring guide with the folder-plus-registry
  rule and verified-host acceptance loop.

### Persistent native hook installation

- Add `blop agents install-hooks <id> --project <local-id>` for persistent OMP
  user plugins and Claude Code user-settings command hooks. Preview exact diffs
  by default; `--apply` writes atomically and `--remove --apply` restores
  hash-checked pre-install bytes. Preserve foreign config and refuse later drift.
- Native hooks resolve exact project bindings/policies on disk without launcher
  environment, reject stale collector instances before enqueue, and report
  native configuration separately from delivery in `agents status`. Project
  launch remains optional and does not register duplicate hooks. OpenCode and
  Codex native installers remain deliberately deferred pending verified contracts.

### Default local content capture

- Local agent content capture is on by default for a newly enabled project
  binding: native harness spans now honour the binding's consent, so prompts,
  public replies and tool input/output/errors are retained locally, bounded and
  redacted. `blop agents env` exports OMP's verified
  `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` value per mode.
- The inspector shows its "content not included" notice only when a step
  genuinely has no retained content.

### Retention and capture policy settings

- Body and metadata retention default to 720 hours (30 days). Per-binding body
  retention uses `blop agents capture-policy set --retention-hours` (1–720);
  store retention uses `BLOP_MONITOR_RETENTION_HOURS` after collector restart.
  Expired bodies become tombstones, and the replay horizon follows maximum retention.
- Settings now edits each existing harness/project binding's mode, body
  retention and class exclusions through the CLI's shared atomic policy writer.
  Accepted values and revisions update only on success; invalid values, stale
  revisions and server errors cannot appear as saved. Changes apply to future
  events and pending delivery, not retained history. Store retention remains
  explicitly restart-only, not hot-reloadable.

## Unreleased (CLI UX overhaul, branch feat/blop-cli-v01)

The JSON contract is `blop.cli/v1`. Its JSON Schemas ship in this package under `schema/` and print with `blop schema`. Within v1, fields are only added. Most changes below touch output that 1.7.0 did not have yet (it always emitted `warnings: []`, and run, tail, workers and setup had no envelope), so the practical break for 1.7.0 users is small. Each change is listed so scripts can be checked.

### Project-bound native harness content

- Reuse the local hook binding as native span content consent. New bindings
  default to full/720-hour bodies; absent, mismatched, disabled and narrower
  bindings remain metadata-only on the native path.
- OMP project `agents env` and `agents run` select its documented full/summary/none
  emission mode and export the exact source/installation/project/adapter identity.
  No new content variables are added to other adapters.
- Keep credential redaction and the existing attribute/event byte limits.
  Native public message structures exclude hidden reasoning, environment dumps
  and attachment/file fields. Native body expiry removes projection/index/raw
  copies without changing metadata retention; readers refuse expired bodies.
- Show bounded native content in session/trace step details. Agent-context
  summaries distinguish retained content from launcher/metadata-only steps;
  captured text remains inert and is not expanded into the copied summary.

### Bounded failure context (#463)

- Add `blop failure-context --source KEY --trace ID --since 15m --json` and
  `GET /api/failure-context`, using one source-scoped read snapshot and the
  `blop.failure-context/v1` data schema (`blop schema failure-context`).
- Return observed span/log symptoms, deterministic ranks with reasons, exact
  source/collector/trace/span/log identities, missing parents, capture coverage
  and pinned exact drill-down queries. Service/session/recorded-attempt
  selectors intersect; source/owner identity prevents reused-ID leakage.
- Enforce server-side window/row/byte caps and a terminating worker deadline,
  with two readers maximum. Preserve disabled history, empty/truncated/unknown
  sources, unavailable stores and stale-revision conflicts as distinct states.
- Keep reads metadata-only, with unknown verification and no inferred agent
  links, model diagnosis, hosted-run join, MCP change or new raw store.
  Offline `--db` never starts or contacts a collector.
- Qualify real Node `/ok`, `/fail`, unrelated failure and recovery exports
  through built CLI, curl and generic `openapi-client-axios` 7.9.1 drill-down.
  Extend the local-monitoring skill and OpenAPI only for this contract.

### Unified evidence-launched browser checks (#490)

- Add `blop test init|add|list|run|watch` and `blop flows` (including `ls`).
  Keep legacy top-level/spec aliases and the older `flow` group; `run -- <cmd>`
  remains the observability wrapper.
- Resolve `flows run --from trace <id>|--from finding <code>` read-only from
  local evidence, with bounded windows/rows/bytes. Missing, stale or over-cap
  evidence exits 2 before spec loading. Recorded context never chooses a target.
- P00 qualification (2026-10-24, Linux x64, Node 22.23.3): clean packed consumers use public SDK revision `691292c78cf737ec6863abd1c65750f61ba31e04`, not a workspace patch; real Chromium trace/finding launches exported SDK-owned `blop.evidence.*` attributes matching JSON, while plain runs stayed unstamped. Provenance is not causality or independent verification; this is not release/platform certification.
- Divergence from older CLI plans: `test` is now the browser-check group, not a
  future issue-to-generated-test command. `add` scaffolds a reviewed spec only.

### Read-only setup doctor (#492)

- Add `blop setup doctor [--json]` for the whole local loop. One `data.checks`
  array reports pass/fail/not-applicable, reasons and exact remediation commands.
  No auto-repair; exit 0 with no failures, otherwise 1.
- Check collector/status health, local store permissions/retention/retained amount,
  pinned Chromium, harness/SDK resolution, Node >=22, agent recipes, Git and
  binding. Never write telemetry/config/state, launch agents/browsers, start a
  collector or contact external hosts. Keep stale registry records untouched.
- Identical facts serialize identically: the envelope's `generated_at` uses an
  explicit fixed epoch sentinel, not an audit timestamp. Publish `setup-doctor`
  schema and teach help/quickstarts/the local-monitoring skill to audit first.
- Keep `blop doctor` unchanged: it remains an active OTLP round-trip diagnostic.
  SDK semver is checked, not Git revision integrity; unrecognized recipe shapes
  remain failures, not inferred success. Non-loopback acknowledgement never
  enables external probes.
- Keep SDK execution off CLI bootstrap so a missing SDK is a doctor finding,
  not an import-time crash. Browser flag values are checked exhaustively against
  the SDK public type. Hook checks inspect supported event entries, not arbitrary
  strings/comments; per-signal destinations/protocols override generic settings.

### Bounded local evidence reports

- Add `blop report --since 2h --format markdown|junit|json`, with exact service,
  session, or stored-finding scopes. Read one local query-only snapshot; never
  discover/start a collector, run analysis, contact the network, or upload.
- Include the source database/revision, explicit half-open window, bounded
  service/error/log summaries, stored findings, SDK flow outcomes, exact evidence
  IDs and copyable reproduction/drill-down commands. Keep unknown/unverified/
  verified receipt state separate from execution outcome.
- Bound source rows, metadata fields and rendered UTF-8 bytes with explicit
  truncation. JUnit retains unknown evidence as skipped, not successful checks,
  and is exercised through the ingest parser.
- Add `--until` for byte-identical rendering of an unchanged evidence snapshot;
  report JSON uses the pinned window end as `generated_at`. Publish the report
  data schema through `blop schema report`. Sharing remains a separate decision.

### Scoped rich hook capture (unreleased working implementation)

- Add strict `blop.agent.hook/v3` without widening retained v1/v2. Explicit
  project setup defaults to full supported public content after notice; preserve
  existing reductions. Conversation-only, metadata-only and class exclusions
  filter before producer IPC and again before collector persistence.
- Add owned `agents setup/run --project` artifacts for OMP18.11.3 and Claude
  Code2.1.300, safe removal and `capture-policy show/set`. Refuse binary/version,
  collector-binding, symlink and artifact drift. Do not edit OMP settings or
  silently restart a collector. Configuration is not callback/delivery proof.
- Capture public prompt/assistant/tool input/output/error fields through the
  shared durable outbox and exact receipt path. Store bodies once, reference
  them from metadata, retain the original expiry clock and remove expired
  content indexes. Queued restrictions produce auditable replacement identities.
- Enforce body expiry in exact/token/phrase searches before the physical retention
  sweep, including indexed coverage. Distinguish malformed body errors, a missing
  retained copy and known expiry without failing the whole observation page.
  Bound physical body/index cleanup while retaining observation metadata.
- Open coding-agent sessions on their recorded conversation unless a tab or
  step link was selected. Preserve public-message availability reasons and show
  unknown duration rather than inventing a running state. Detail identity now
  uses the shared resolved activity identity, not a service-name fallback.
- Scoped `agents status --project` checks owned artifacts, binary/version and
  selected collector identity without repair or restart. Show each class's
  policy permission separately from unverified live acceptance. Preserve bounded
  run/git metadata when suppressing unconsented native harness bodies.
- Policy changes preserve exclusions unless an explicit replacement or
  `--clear-exclusions` is requested. Add scoped enable/disable and original-time
  retention updates without requiring a mode change. Use canonical strict flag
  parsing with repeated exclusions and last-value precedence for scalar flags.
- Bind observation pagination to its exact source/session, evidence revision and
  earliest retained-body expiry. Changed/expired snapshots return409 and offer
  an explicit first-page refresh instead of mixing versions or retrying a stale
  cursor. Historical cursor layouts are rejected rather than silently upgraded.
- Keep literal OMP identity independent from historical `opencode` service
  naming. Retain exact same-call and recorded-membership graph associations
  separately from recorded causal parents. Show actual bodies and explicit
  tool outcomes in the existing bounded inspector.
- Claude uses observer-only in-process native mods with the shared sanitizer,
  no transcript scraping. Serialize awaited bridge writes across concurrent
  callbacks; retain fixed content-free failure diagnostics. Organization policy
  can withhold system/compaction callbacks, and is never bypassed.
- Extend the same OpenCode plugin and JSONC editor for scoped2.0.21 setup/run/
  removal. Own a directory reference, preserve other config entries/comments,
  and retain the config document on undo. Pin actual and logical launch cwd.
  Shared selection excludes history/provider state; retain public system text
  and exposed compaction summary only. Native event replay uses a stable
  source-scoped wire identity and the original native clock.
- Classify an exposed nonzero shell exit as failure even when the SDK says
  execution completed. Correlation failure diagnostics no longer echo exception
  messages. Fixture behavior and actual one-prompt inspector readback passed;
  OpenCode's full model/tool/lifecycle cycle is not qualified, nor is V1.
- Main-cycle live receipts cover OMP/Sol and Claude's configured default model.
  Other-host, subagent/compaction/fallback, capacity and publication gates remain
  separate; no all-host or full-plan certification is implied.

### Metadata-only hook lifecycle evidence

- Emit strict `blop.agent.hook/v2` metadata on the existing subprocess → OTLP →
  local observation path. Mint a UUID before first delivery; isolate source,
  installation, adapter, session and call identities. Retain each callback as an
  immutable event observation, with a scoped call key rather than overwriting
  terminal facts. Retained v1 stays readable without v2 receipt claims.
- Keep Stop/agent_end as turn completion, actual shutdown as session end, and
  compaction phases distinct. Preserve unknown outcome, missing/conflicting
  aliases, expired/missing starts and measured-zero duration explicitly.
- Sanitize before local bookkeeping/send and again before evidence persistence;
  omit payload content, paths, claimed machine/worker authority and raw diagnostics.
  Reuse existing source-filtered observation/graph queries; no inferred causal
  parents, authored memory, publication or new graph authority.
- Replace pairing-only files with bounded, private `hook-outbox.sqlite`: pair and
  enqueue sanitized metadata in one durable transaction before network delivery.
  Add exact committed/replayed event-ID/hash/revision receipts in the evidence
  writer transaction, all-or-nothing hash conflicts and bounded replay after
  offline restart, sender/drainer crashes or lost responses.
- Enforce count/byte/age/disk/batch bounds, one drain lease and collector-owned
  timer, credential-revision auth pause, opt-out for capture and replay, explicit
  expiry/refusal/quarantine losses and safe CLI queue/receipt status. Process exit
  or ordinary HTTP 200 is not delivery; capture completeness remains unknown.
- Preserve exact receipts through the existing authenticated TLS receiver. Meter
  actual rollback-journal footprint, including expiry and counter transactions;
  avoid dirtying the outbox on empty collector polls.
- Print explicit OMP/Pi metadata extensions with `agents hook-config`; add Pi
  registry metadata. Reuse the common bridge/outbox, suppress foreign locations
  and disabled capture, and leave tools to configured native telemetry.
- Bound synchronous bridge calls to avoid OMP's detached turn-end/shutdown race.
  Qualify OMP18.11.3 real read/edit, success/failure, shutdown, opt-out and offline
  replay, native-overlap tool ownership and abrupt-exit unknowns; Codex/Pi
  model/auth blockers and untested compaction remain
  explicit. H3 stays partial; H4 associations and full H5 are not implied.

### Local authored scores and human review

- Add `blop evaluation grant|revoke|call` and the existing collector owner's
  `/api/observations/evaluation/*` HTTP actions. Local grants bind one source,
  principal and permission set, expire within 24 hours, are checked fresh and
  keep bearer credentials in private files and trusted backends, not browsers.
- Store typed numeric/categorical/boolean/text/correction scores, immutable config
  revisions, per-score comments/history and grant-bound mutation audit separately
  from recorded evidence. Enforce scoped config/subject checks, explicit expected
  revisions and canonical idempotent replay. Corrections preserve original output.
- Add revision-safe annotation queues, eligible human assignments, atomic claims,
  required-score completion, bounded next/list and per-member bulk outcomes.
  Retain author/reviewer/evaluator provenance and immutable source references
  after source removal; absence does not invent an expiry/deletion cause.
- Keep P3 partial: collaborative comment threads/anchors, mentions/reactions/
  preferences, hosted operation contributions and selected-result memory curation
  remain unimplemented. No hosted review UI, local MCP writes, telemetry upload
  or complete P3/release claim.

### Observation contracts

- Export browser-safe `@blopai/cli/monitoring/contracts` for exact local/hosted
  source-scoped subjects, worker-attempt references, provenance, content-state
  and byte-bounded query/cursor validation. Parsing never grants source access
  or enables content capture, publication or evaluator execution.
- Keep opaque Unicode session IDs distinct from imported coding sessions and
  service-scoped agent conversations. Reject control characters, malformed
  Unicode, ambiguous identity and foreign source/machine bindings.

### General application sessions

- Normalize ten observation types, source-scoped opaque conversation IDs,
  requested/observed models, environment/release/version, user/tags and bounded
  typed metadata in the ingestion transaction. Preserve unknown and conflicting
  dimensions; historical spans require explicit replay to acquire projections.
- Add source-preserving application session list/detail, role-labeled replay,
  parallel tool association, sparse durable bookmarks and bounded continuation.
  Keep coding-agent, hook, worker, run and request investigation paths distinct.
- Add explicit `--capture-gen-ai-content` consent independently of legacy custom
  attribute capture. Persist new application payloads only in local normalized
  observations; omit them from legacy raw span/search and hosted structural
  proxies. Default capture, hook payloads and unclassified status text remain
  metadata-only. Report ingestion metadata clipping separately from previews.
- Bound replay pages to 50 observations/256 KiB, field previews to 4,000 UTF-8
  bytes and explicit single-observation full reads to a separate 1 MiB ceiling.
  Enforce exact source/service/session/trace/span ownership on those reads.
- Bound pending HTTP intake and writer requests to eight with visible overload
  rejection. Cap hook delivery at one second and keep partial tool duration
  unknown rather than assigning a measured zero.
- Add runnable standard OTel TypeScript/Python session recipes with async/manual
  context, parallel tools, W3C client/server extraction, masking and flush/shutdown.
  Actual isolated runs verified three traces/15 spans per language and capture
  mode; these controlled producers are not external-agent/provider certification.
- Keep session bookmark/source controls and bounded-content guidance inside the
  viewport at tablet widths as well as on phones; wrap the action group instead
  of clipping controls or hiding provenance.
- Read application-session model fields in the bounded observation scan without a
  second projection lookup per observation; preserve snapshot and source scoping.
- Make capacity investigations reproducible with a separate 10k reduced fixture,
  raw proc mappings and database inode identities, and per-isolate V8 heap-space
  statistics. Reduced runs cannot pass full-envelope gates. The round-4 summary
  index candidate failed full-envelope latency targets and was withdrawn; native
  allocator retention and the unchanged query/RSS capacity gates remain open.
- Record the round-5 allocator/transactional-summary experiment: exact summary
  compatibility and reduced query/ingestion improvements, but 344.164/346.383 MiB
  natural-idle peaks failed the prerequisite for a full run. Both candidates were
  withdrawn at that checkpoint; no allocator policy or native addon ships.
  The separate round-6 capture-process qualification is recorded below.

### Out-of-process local capture

- Keep the OTLP/query HTTP listener in a database-free parent; normalize, persist,
  retain and query the same local store in a listener-free capture child. Preserve
  JSON/protobuf/gzip, source/consent boundaries, parent collector identity, SSE and
  `blop.cli/v1` across bounded, backpressured fork IPC.
- Fail child outages honestly with `capture_unavailable` for intake/writes and
  `query_unavailable` for reads. Never replay an interrupted mutation. Bound
  automatic recovery to two restarts and terminate the child with the collector.
- Record parent and child RSS separately. The sanctioned parent RSS gate does
  not mean total capture memory disappeared; unchanged targets and retention
  remain authoritative in the capacity plan.
- Record round 6's full 100k envelope: ingestion/query p95 pass at 356.756/164.489ms,
  but parent natural loaded-idle peak 151.023 MiB fails strict <150 MiB. Disclose
  capture child peak 358.516 MiB; retain the architecture and stop without a
  rerun, waiver, IPC tuning or summary-rollup reapplication.
- Reuse a parent-owned, bounded HTTP forwarding connection pool instead of
  constructing and closing a virtual socket for every request. Keep the 128
  connection ceiling, 64 KiB frames, backpressure, deadlines and no-replay outage
  behavior unchanged; destroy the pool when the parent stops.
- Close the parent capacity gate in round 7's single authorized full rerun:
  natural loaded-idle peak 141.887 MiB, ingestion/query p95 313.222/141.847ms,
  all three capacity gates PASS. Disclose separate capture child peak
  327.324 MiB and parent overall peak 151.547 MiB. At that checkpoint, summary
  qualification was blocked by missing archives; round 8 resolves it below.
- Maintain exact writer-owned application-session summaries with intake, retention
  and lost-launcher updates. Read at most 201 indexed aggregate rows globally;
  preserve the 3,000-observation fold, ties, model/source precedence, partial
  flags and independent earliest-window detail/replay behavior. Historical WAL
  snapshots remain read-only and retain their original pre-migration view.
- Rebuild the rollup from the recorded design after temporary source loss and
  qualify it with fresh all-3,001-row differential proof and one full 100k A+B
  run: ingestion/query p95 399.192/92.089ms, parent natural loaded-idle peak
  138.523 MiB, all three capacity gates and 15 correctness probes PASS.
  Disclose separate child sampled peak 354.367 MiB and the higher full ingestion
  tail versus round 7; this is not a total-machine memory or release claim.

### Observation investigation

- Add source-bound typed filters, exact bounded facets and token/phrase/full-field
  content search over already masked, consented observations. API, inspector URL,
  saved views and JSON page export use the same validated query interpretation;
  missing, null and empty values remain distinct. Historical, truncated and
  unsupported metadata/index coverage is explicit; reads never backfill content.
- Persist mutually exclusive usage buckets and immutable exact-decimal cost
  receipts. Recorded cost, including zero, takes precedence; invalid or missing
  usage/currency is unavailable, not zero. Append source/model price revisions
  with effective dates and input-token tiers without repricing earlier receipts.
- Add scoped application-end-user summaries and expanded/grouped execution
  graphs with exact recorded-parent edges, repeated-call identity, cycle/missing-
  parent disclosures, bounded responses and an accessible evidence list.
- Add a Sessions → Observations inspector view and a workspace-admitted,
  read-only hosted telemetry view. Price and saved-view writes stay same-origin
  local operations; these additions do not enable remote localhost access,
  publication, arbitrary SQL, tokenization/model downloads or provider calls.
- Adapt selected non-enterprise Langfuse T04/T05 behavioral cases with per-case
  provenance and controlled-fault receipts. This is not full upstream parity or
  a performance/release certification; P1 capacity gates remain open.

### Shared authored graph core

- Export `@blopai/cli/graph`: shared native libSQL access, the existing pinned local embedding model and vector ranking, plus revisioned authored memory with source/proposer/approver attribution. Keep the monitoring evidence adapter and its wire schemas separate.
- Apply identity/project grants before retrieval, counts and ranking. Preserve explicit rule lookup, conflicts and approved supersession independently of semantic similarity; similarity creates no evidence edges.
- Require exact target/policy revisions and action-bound approval for consequential knowledge changes. Reject ambiguous patches and brief overflow atomically; retain revision history, explicit retire/restore and content-free purge receipts.
- Persist applied graph writes and explicit index receipts in the same transaction as revisions/vectors. Exact index replay and restart recovery do not invoke the model again. Exercise native checkpointed `VACUUM INTO` backup/restore for content, history, vectors and action receipts; the driver's declared `backup()`/`serialize()` APIs are unimplemented.

### Plugin management integration

- Reject chat continuation through another member's conversation or thread ID before saving messages or granting tools. Reject an explicit inaccessible project instead of silently anchoring the chat elsewhere. A disposable Postgres smoke reproduced the prior foreign-thread resolution and confirmed the ownership rejection.
- Keep existing mixed-case operation IDs stable while deriving bounded, collision-checked MCP labels. Authorize native run, evidence and historical artifact reads through the same management descriptors without changing their browser query normalization.
- Freeze run targets, refs and scenario selections before launch; atomically claim queued runs once and distinguish cancellation requests from observed executor/provider outcomes. Exercise concurrent claims and native HTTP reads against isolated PostgreSQL.
- Keep reviewed bulk deletion and its completion receipt in one transaction. A failed receipt rolls back child cascades; projects created after the reviewed ALL snapshot remain untouched.
- Make local authored memory available without an account or collector: exact add/patch/history through `blop memory`, with stale-revision rejection. The official MCP client can retrieve that same local revision through the eleven-tool read-only stdio catalog while the collector is offline.
- Preserve the browser fetch receiver in the shared management client; requester-bound handoff navigation now reaches the authenticated security page in Chromium instead of failing with an illegal invocation. Keep an empty MCP tool catalog usable after every permitted app is disabled, without advertising disabled operations.
- Add delegated device login, protected local profiles, live cloud operation discovery/calls and explicit reviewed-hash publication. Logout revokes the grant before deleting local credentials. Keep payload `--input` independent of the global `--no-input` control.
- Load local and hosted memory/procedure sections with `--field` and Unicode continuation bounds. Bind local proposals to their retained idempotency key; freshness filters apply before authorized search ranking and pagination.
- Move active web knowledge and learned procedures into per-workspace graphs through verified, resumable migration; retain legacy Postgres rows only as inert migration data. Resolve run sources against current workspace/project/attempt/artifact ownership and retention.
- Freeze only the small task-consumed memory brief under fresh conversation/grant bindings, outside privileged prompt instructions. Live policy still gates every write; revocation and membership changes invalidate access to the frozen snapshot.
- Expose one shared memory editor with revision-addressed receipts, historical comparison, explicit relationship direction and reviewed per-file document extraction. Keep tall/phone panels scrollable and restore keyboard focus after closing.
- Reject streamed oversized HTTP requests promptly without waiting for both branches of a cloned request body to cancel. Restore a matched native graph/Postgres backup and recover an interrupted completion from its applied graph action without replaying a later revision.
- Bound local CLI/MCP and hosted/web memory reads by bytes as well as row counts: share metadata and Unicode-safe preview pages with lossless hit/edge/revision cursors, metadata-only skill lists and explicitly selected Unicode procedure sections. Preserve complete edge identities even when JSON escaping requires omitting previews. Count addressed metadata omissions and make complete metadata progressively retrievable with a snapshot hash; the editor reconstructs it before changing content, preserving hidden references. Report neighborhood source omissions with `sourceCount`/`sourcesTruncated`; preserve complete core graph-store contracts. Local MCP exposes four collector evidence tools plus seven offline memory reads without account, collector or model initialization. Return compact commit/proposal manifests with explicit omissions so successful large writes are not misreported as unknown oversized responses.
- Parse only the registry's exact declared negative spellings across Node versions; `--no-input` cannot be interpreted as negating the string-valued payload `--input`.

### Local inspector design

- Give Home an issue-first layout, with active agents and recent sessions in a quieter supporting column; stack the same content on smaller screens.
- Refine the dark neutral palette, type hierarchy, navigation, filter boundaries, button states and spacing. Keep fonts self-hosted and status labels explicit.
- Remove status-dot halos, pulsing loading placeholders and repeating onboarding motion. Empty issue queues link to recorded runs instead of repeating setup commands, and explicitly avoid implying that every flow was verified.
- Redesign Sessions, scoped and recent logs, worker evidence, issue detail, run review, Connect, Settings, search and optional Ask around observe → investigate → verify. Make Logs and Workers discoverable from Sessions and keep advanced metadata behind disclosures.
- Show the copied fix-context occurrence beside the handoff action, preserve exact session/worker evidence in agent briefs, and retain source receipts when copying an Ask answer. Distinguish command success, recorded checks and AI interpretation from independent verification.
- Separate collector/agent/app/correlation readiness on Home and Connect. Explain bounded search, effective read-only settings, capture consent and configured-provider traffic without claiming complete evidence or zero network activity.

### Active coding agents

- Record agent-launch process start and exit before the agent's SDK flushes. Home lists observed active agents; Sessions links and filters use the same lifecycle evidence. Nested launches keep their enclosing run open, and lost local launchers stop reading as active after the collector's sweep.
- Recognize OMP's native instrumentation scope even with a custom service name, instead of counting its model/tool activity as application requests.
- Refresh Home on streamed activity, recover with polling while disconnected, and use the observed start time for unfinished spans instead of showing a 1970 timestamp.
- Include the required `protocol = "binary"` in Codex OTLP configuration recipes.
- Keep native conversation details consistent with the Sessions list: agent identity, recorded model, run and git stamp survive the drill-down, including custom-named OMP services.
- Exclude Codex's anonymous auth/runtime traces from coding-session counts and read its native `model` attribute. A model turn remains visible without inventing conversation links.
- Keep agent prompt arguments out of launcher command metadata unless content capture is explicitly enabled; lifecycle observation still records start, exit and run correlation.
- Explain missing application spans without claiming that agent telemetry is absent, and keep Home's relative timestamps advancing without new telemetry.

### Worker lifecycle and cancellation evidence

- Expose exact lifecycle/model evidence IDs and unknown source coverage/dropped records in mounted worker details. Verify authenticated receiver, CLI, API and inspector agreement without confusing stale connectivity, zero exit codes or unfinished spans with verified task success.
- Mount the shared escaped cancellation renderer in exact worker details: distinguish source-reported exit from digest-bound independent observer evidence, retain execution/child/transport coverage and post-request activity, and expose no worker controls.
- Add optional `startMonitoringServer` caller-supplied `cancellationEvidence: {events, policy}`. Copy the authenticated upstream snapshot at startup; raw OTLP/HTTP payloads still cannot grant observer authority. Ordinary daemon behavior remains default-deny and does not install an observer or add a transport.
- Export `cancellationEventDigest` and `cancellationCss` from the monitoring API. Embeddings must load the CSS separately; the fragment no longer emits inline styles, preserving the inspector's same-origin CSP and theme.

### Pi MCP client

- Add Pi to the inspector's Coding agent (MCP) tabs and `blop setup mcp pi`. Setup preserves existing servers and supports project `.mcp.json` and user `~/.config/mcp/mcp.json`, with handshake verification and scoped removal.
- Pi needs `pi-mcp-adapter`; the Connect instructions include installation and reload steps. OMP reads the project configuration natively without that adapter.

### Main-branch integration

- Keep authenticated trace ownership, source capture consent, OpenCode model/version compatibility and interruption cleanup alongside the CLI UX changes.
- Route multi-agent setup, hook/launch, source/filelog commands and graph index/search through the command registry. Agent setup uses the shared persistent collector policy; an explicit port wins over an environment URL.
- All local inspector, chat, run and issue stores use the libSQL adapter against existing SQLite files. Graph snapshots retain user-recorded verification receipts.
- Preserve opaque telemetry `attributes` and `tags` keys in machine output, query `observation` provenance, and status ingestion receipts.
- Document source policy configuration and agent-hook timeout flags in command help. Help checks cover behavior and readability rather than pinning exact wording snapshots.

### Breaking: the collector's HTTP API is same-origin, and checks Host

- **No more `Access-Control-Allow-Origin: *` outside OTLP ingest.** The inspector, its APIs, sessions, session trees and transcripts, issues, fix context, runs, settings, health, the query API and `/api/stream` send no CORS header. A page on another origin can no longer read them; a script or tool that called the API from a browser page on another origin breaks. Call it from a local process (curl, `fetch` in Node, the CLI or MCP), or from the inspector itself.
- **CORS stays on OTLP ingest only:** `POST /v1/traces`, `/v1/logs` and `/v1/metrics` and their `OPTIONS` preflight answer `Access-Control-Allow-Origin: *`, so an OpenTelemetry SDK running in a browser page on your dev server keeps exporting. Those routes take telemetry in and answer only an accepted count.
- **Host check on every route, OTLP included, with no exemptions** (any future `/mcp` route too): the `Host` header must be `127.0.0.1:<port>`, `localhost:<port>` or `[::1]:<port>`, else `403 Forbidden: Host is not this collector's loopback address`. This defeats DNS rebinding. A local SDK, the CLI, MCP and the inspector send one of those already. A request through another name (`host.docker.internal`, a LAN name or an `/etc/hosts` alias) is refused; the collector binds loopback only. From a container, run the OTel SDK's exporter against a port forwarded to the host's loopback (for example `ssh -L` or a sidecar) with `Host: 127.0.0.1:<port>`, or run the instrumented process on the host. A dev-server proxy in front of `/v1` (Vite `server.proxy`) must rewrite Host (`changeOrigin: true`), or the SDK can export straight to the collector, since OTLP answers CORS.
- **Origin check outside OTLP ingest:** a present `Origin` must be the collector's own (`http://` plus one of those hosts), on GET too, else 403.
- **Guarded writes and chat follow the same rule.** Issue state, mark-wrong, run start and finish, the workers acknowledgement and the inspector chat accept any of the three loopback Hosts, with `Origin` equal to `http://` plus that same Host, and still require their marker header, JSON and no cross-site fetch. The inspector opened as `localhost:<port>` or `[::1]:<port>` now works fully; before, its chat and writes answered 403.

### Breaking: JSON

- **Errors.** Every `--json` failure is one envelope on stdout with `ok: false` and `error: {code, message, hint, docs, next}`.
  - Rejected HTTP queries retain optional `error.details` with the responding collector instance, evidence revision and HTTP status.
  - Renamed codes: `collector_unavailable` is now `collector_not_running`, `query_failed` is now `query_rejected`, and `invalid_argument` is now `invalid_value` (or a more specific code such as `unknown_flag` or `missing_value`).
  - workers' `invalid_or_unavailable_snapshot` is split into `invalid_value`, `invalid_snapshot`, `collector_not_running` and others.
  - `graph` and `evidence` errors use the envelope instead of a `blop.graph/v1` error document.
- **Envelope.**
  - Success envelopes carry `ok: true`.
  - Every envelope has a top-level `next: [{command, when}]`. On a failure it repeats `error.next`, which is also objects now, not strings.
  - `warnings` entries are `{code, message}` objects, not strings. One example is `version_mismatch` on `up` and `status`.
- **`blop run --json`.** The old one-line camelCase object `{runId, exitCode, sessions, requests, failedRequests, newIssues, regressedIssues, url}` is gone.
  - It is now a `blop.cli/v1` envelope. On stderr it is the last line Blop writes, on a line of its own; with `--report <file>` it goes to that file instead.
  - `data` has snake_case keys. `new_issues` and `regressed_issues` are lists of `{id, title, url}`. The old `url` is now `data.collector_url`.
  - `ok` is true when the command started, and false only when it never started.
  - Usage errors of `blop run --json` also go to stderr (or to the report), never to stdout.
- **`blop tail -o ndjson` (also `--json` and `-f json`).** Each line now has `schema_version` and `type`. Keys are snake_case: `exitCode` is now `exit_code`, `endReason` is now `end_reason`, and `traceId` is now `trace_id`.
- **`blop issues --json`.** Rows gain `trend_24h`. `trend24h` stays until `blop.cli/v2`, and every document carries one `deprecated_field` warning, so a non-empty `warnings` no longer means something went wrong: branch on `code`.
- **`blop issue <id> --fix-context --json`** is an envelope with the markdown in `data.markdown`. It used to print the raw markdown.
- **`blop init --json`.** `data.next` (a string) is removed. The suggested command is in the envelope's `next[]`.
- **`blop workers overview|detail --json`** is a full envelope: `ok`, `generated_at`, `page` and `next` are added, and the document is pretty-printed. Its `data` keys are snake_case, for example `worker_id` and `connection.connection_state`.
- **`blop setup opencode --json`** is a `blop.cli/v1` envelope with the setup as `data`. It used to be a separate `blop.setup/v1` document.
- **`blop evidence ... --json` and `blop graph ... --json`** are envelopes. Text output is still the raw document.
- **`blop setup mcp --json`** (new) is an envelope with `command: "setup mcp"` and `data: {client, scope, target, invocation, action, snippet, backup?, verified, activation?}`, or `{clients: [...]}` with no client. Its error envelopes say `"setup mcp"` too. `blop setup opencode --json` keeps `command: "setup"`. `action` is `added`, `replaced`, `unchanged`, `printed`, `command`, `removed` or `absent`; `invocation` is null with `--remove`; `backup` is a path under Blop's state dir (`backups/<client>/`).
- **`blop init --json`** gains `data.mcp` with `--mcp <client>`, and without it one more `next[]` step, `blop setup mcp`.
- **The OpenCode MCP snippet** (README, Connect page, `blop setup mcp opencode`) gains `"timeout": 30000` when the command is `npx`, because a cold `npx` start outlasts OpenCode's 5 second default.
- **`BLOP_MCP_VERIFY=1`** (new, internal; do not set it): `blop mcp` answers the protocol but never contacts a collector, whatever `--url`/`--port` say. It says so once on stderr (`blop mcp: verify mode, no collector contact`), and every tool call answers that it is in verify-only mode. `blop setup mcp` uses it for its start check and for the client's own health-check, so setup never marks a collector "MCP connected".
- **MCP invocation detection** no longer trusts an inherited `npm_command=exec`: a `blop` on PATH that is this installation is always used. The Connect page shows install buttons only for that, or for a published pinned npx; another installation on PATH gets the update step (`mcp.install_reason: "other_install"`).
- **The npx MCP command is pinned:** `npx -y @blopai/cli@<version> mcp` (was `npx -y @blopai/cli mcp`, which fetched 1.7.0, a version without `mcp`). The Connect page shows it only when that version is known to be published; otherwise it shows `npm i -g @blopai/cli` and no install buttons (`mcp.links` is null, `mcp.install` is the step).
- **`blop doctor --json`.** Rows are `{id, category, status, ok, detail, fix, advisory}`. A failed blocking check prints `ok: false`, the error, and the checks in `data`.

### Breaking: `blop mcp` speaks MCP 2026-07-28 too, and its tool results changed shape

- **Both eras.** Legacy clients keep the `initialize` handshake and `ping`; `2025-11-25` joins the accepted versions, and an unknown requested version now gets `2025-11-25` back (it was `2025-06-18`). Modern clients get `server/discover` (`supportedVersions` `["2026-07-28", "2025-11-25", "2025-06-18", "2025-03-26", "2024-11-05"]`, `capabilities.tools`, `instructions`, `ttlMs: 3600000`, `cacheScope: "public"`, `_meta["io.modelcontextprotocol/serverInfo"]`, `resultType: "complete"`). A request whose `params._meta` carries the per-request protocol fields is served statelessly: a missing `protocolVersion` or `clientCapabilities` is `-32602`, a version other than `2026-07-28` is `-32022` with `data.supported` and `data.requested`, and results carry `resultType`, `serverInfo` in `_meta`, and on `tools/list` `ttlMs` and `cacheScope`. `ping` is not a modern method (`-32601` with modern `_meta`).
- **JSON-RPC edge cases.** A request with `"id": null` (or a non-integer, non-string id), a JSON array (batches are not part of MCP), a non-object line and a request without a string `method` get `-32600`; unparseable JSON stays `-32700` with `id: null`. Notifications are never answered.
- **Tools.** Each tool has a `title` (`List Blop issues`, `Get Blop issue`, `Get fix context`, `Get session or trace timeline`) and `annotations: {title, readOnlyHint: true, openWorldHint: false}`; `serverInfo` has `title: "Blop"`; `instructions` end with "Needs a running Blop collector (`blop up`). Read-only."
- **Results are structured, snake_case and compact.** Each result carries `structuredContent` that matches the tool's new `outputSchema`, and the same JSON, compact (no pretty-printing), in its text block (`get_fix_context`: the markdown). The shapes are the CLI's: `list_issues` is `{data, returned, total, truncated, note?}` with `blop issues --json` rows (`not_seen_in_latest_run`, `trend_24h`, absolute `url`; was camelCase); `get_issue` is the `blop issue <id> --json` detail (snake_case, plus `url`, `truncated`, `occurrences_returned`, `dropped?`, `note?`); `get_fix_context`'s structured copy is `{id, markdown, fix_context_url, url, truncated}`; `get_session` keys are snake_case (`session_id`, `trace_id`, `source_id`, `duration_ms`, `span_id`, `parent_span_id`, `metadata.call_id`, ...). The outputSchemas are composed from `schema/issues`, `schema/issue` and the new `schema/mcp-tools` (`blop schema mcp-tools`).
- **Size.** Results are capped near 36 KB (was 50 KB, pretty-printed), under Claude Code's 10,000-token warning. A cut result stays valid JSON with `truncated: true` and a `note` naming the command that shows everything (`blop issues --json`, `blop issue <id> --json`, `blop issue <id> --fix-context`, `blop open <id>`). `get_issue` empties whole sections (objects to null, lists to empty) before it drops chain steps, so it still matches `schema/issue`.
- **Which collector.** `blop mcp` reads the collector of the client's project: `BLOP_PROJECT_DIR`, else `CLAUDE_PROJECT_DIR` (Claude Code sets it), else its working directory; `--url` and `BLOP_MONITOR_URL` still win. When several collectors run and none matches, a tool call is an `isError` result listing each one (url and workdir) and the exact `BLOP_MONITOR_URL=...` fix; it used to answer silently from the default port. A collector that answers 200 with a body that is not JSON is an `isError` result too (it used to read as an empty list).
- **`get_fix_context`** whose first line alone is over the cap keeps as much of that line as fits (whole characters), then the note; it used to return the note alone.
- **stdout** carries only JSON-RPC messages (now covered by a test that feeds valid, invalid and failing input); diagnostics go to stderr.

### `blop setup mcp`: one client table, Gemini CLI, Windsurf, Claude Desktop, stale entries

- **One table** (`src/monitoring/mcp-clients.ts`) now drives `blop setup mcp`, the Connect page's tabs, snippets and install links, `mcpConfigSnippets`, `blop mcp --help` and the README and docs client section (generated between markers by `node scripts/render-mcp-docs.mjs`; a test fails when they drift).
- **New clients.** `gemini` runs `gemini mcp add -s project|user blop <command>` (and `gemini mcp remove -s <scope> blop`), never `--trust`, and verifies by reading `mcpServers.blop` back from `.gemini/settings.json` or `~/.gemini/settings.json` (read only; a backup of the previous file goes to Blop's state dir). It used to be refused. `windsurf` and `claude-desktop` print the entry and where it goes and never write (`action: "printed"`, a `print_only` warning): Windsurf's config path is moving (`~/.config/devin/mcp_config.json`, older `~/.codeium/windsurf/mcp_config.json`), and Claude Desktop has no project and a minimal PATH, so its entry uses the absolute path of `blop`. `blop init --mcp` and completion offer all eight.
- **User-scope Cursor and VS Code entries carry `env.BLOP_PROJECT_DIR = "${workspaceFolder}"`**, and so do the Connect page's Cursor snippet and both install links (which install at user level). A user-scope entry written before this has no env and now reads as stale (`missing_project_env`); `blop setup mcp cursor --scope user --force` rewrites it.
- **The VS Code snippet on Windows** wraps npx in `cmd /c` on the Connect page and in its install link too, as setup already did. Codex's TOML snippet gains `startup_timeout_sec = 30` when the command is npx.
- **Connect page JSON.** `mcpSnippets` is keyed by client id (`claude`, `codex`, `cursor`, `vscode`, `opencode`, `gemini`, `windsurf`, `claude-desktop`; `claude-code` is now `claude`), and `mcpClients` lists the tabs in order.
- **Stale entries.** `blop setup mcp` with no client reports, per client, every `blop` entry in `entries[]` (`{scope, target, command, args, status, reason, detail, update}`). An entry is stale when it pins an older `@blopai/cli`, runs an unpinned one, uses npx while this `blop` is on PATH, names a command that no longer exists, or (user-scope Cursor and VS Code) lacks `BLOP_PROJECT_DIR`; text output prints one `warning:` line on stderr per stale entry with its update command, and `next[]` lists them. A server someone else named `blop` is `status: "other"` and never offered for `--force`. A client's `status` can now be `other`, and the text word for a stale one is `stale` (was `stale command`).
- **`blop doctor`** gains an `mcp` check under Agents: ok when no Blop entry is stale (none at all is fine), a warning naming each stale entry with its update command as the fix. It is advisory, so it never fails doctor; the Connect page shows it too.

### Breaking: exit codes and refusals

- **Exit-code table.** 0 ok, 1 failure, 2 usage, 3 Blop not running, 4 query rejected, 5 timeout, 6 could not verify (`blop init`, `blop setup mcp`: the result could not be confirmed, or nothing was written because it would not work), 129 terminal closed (SIGHUP), 130 interrupted, 143 terminated (SIGTERM) (`blop help exit-codes`). 129 and 143 come from `blop setup mcp` when its start check is cut short.
  - Bad flags used to exit 1; they now exit 2.
  - An unreachable collector exits 3.
  - `--state bogus` exits 2 before any request is sent.
- **Interrupts.** Ctrl-C now exits 130 for `blop tail`, a foreground collector and `blop monitor start`. It used to exit 0.
- **Strict parsing.** Unknown flags, extra arguments and a leading `--` exit 2. `--reasoning-effort` was never implemented and is now an unknown flag.
- **Output formats.**
  - `-o` takes only the formats a command lists, and anything else exits 2. For example, `skills --json` used to be ignored and is now refused.
  - `--json -o text`, `-f json -o text` and `workers --json -o text` exit 2.
- **`blop flow init`** asks before overwriting. Without a terminal it exits 2 unless `--force` is given.
- **One collector precedence on every command: flag, then env, then the registry or the default.** `blop up -p/--port` and `--host` (and `blop monitor start`) now win over `BLOP_MONITOR_URL`, as they already did on status, stop, run, open, init, mcp, doctor and the query commands. Before, `blop up -p N` in a shell where `eval "$(blop status -o env)"` had exported `BLOP_MONITOR_URL` silently reused that collector, or exited 3 when it was gone.
- **`blop up` reads `BLOP_MONITOR_HOST`**, the env of `--host`, as the client commands already did (`blop monitor start` did before this release too). It is held to the same rule: 127.0.0.1, ::1 or localhost, anything else exits 2.
- **`host_refused` (exit 4).** A request the collector refuses for its `Host` (a `BLOP_MONITOR_URL` or `--url` that names `host.docker.internal`, a LAN name or an `/etc/hosts` alias) is `host_refused`, with a hint naming the loopback forms. It read as `The collector rejected the query (HTTP 403)` (`query_rejected`). `blop mcp` tool results say the same.
- **`blop setup opencode --url`** exits 2 (`unsupported_flag`). It was accepted and ignored; the command starts or reuses a collector by port (`-p`).
- **`blop tail`'s help** no longer lists exit codes 3, 4 and 5: a collector that is down is waited for, with one warning, and tail reconnects. `blop status`'s note now says `-q` silences stdout only (errors still go to stderr).
- **Exit never waits more than 5 s for stdout and stderr to drain.** A reader that keeps the pipe open and never reads used to hold the process forever; it now exits with the intended code.

### Breaking: text output

- **`blop issues`.** Piped output is tab-separated with no header (use `--header` for one): ISO times, the full commit, and the inspector URL. The terminal shows an aligned table.
- **`blop run`.** The summary on stderr is a 7-line block instead of one line.
- **`blop tail`.** The status lines are now `warning: Blop is not running at <url>; waiting for it (start it with blop up)` and `Connected to Blop at <url>`.
- **Errors.** All errors use `error:`, `usage:`, `hint:` and `docs:` lines on stderr.
- **Help.** All help is generated from the command registry. Root help's `setup` row reads `Connect a coding agent:  blop setup mcp claude`, and `blop setup --help` lists JSON FIELDS per subcommand (`blop setup opencode --help`, `blop setup mcp --help`).
- **`blop init`** ends with one more line: `Agents: blop setup mcp <client> ...`. Its `Next:` line (and `next[0]` in JSON) names the dev command bare `blop run` would start, with the lockfile's package manager (`blop run -- pnpm run dev`); it always said `npm run dev`.
- **The MCP line `blop up` prints for a coding agent, and the `m` shortcut's copy,** are now `blop setup mcp claude` (plus `-p N` or `--url <url>` only when you passed one), spelled `npx -y @blopai/cli@<version> setup mcp claude` when `blop` is not on PATH and that version is known to be published, and `npm i -g @blopai/cli && blop setup mcp claude` otherwise, the same rule the Connect page uses. They were a hand-built `claude mcp add blop -- blop mcp [--url ...]`, which registered a server that could not start for npx users and pinned the URL of a non-default port. `blop setup mcp claude` checks that the server starts before it adds it.
- **`blop up` opens the inspector by the rule `blop open` uses:** not under `--no-input` or `BLOP_NO_INPUT`, `BROWSER=none`, over SSH or with no display (it already skipped coding agents and pipes).
- **`blop run` with no command** runs the package.json `dev` script with the package manager the lockfile names (pnpm, yarn, bun, else npm) and says so in one stderr line. Without a `dev` script it still exits 2, now with the scripts it found in the hint. A dev script that runs blop itself (`blop run`, `npx blop`, `bun run src/cli/index.ts`) is refused (exit 2, `dev_script_runs_blop`). A coding agent running `blop run` gets one short stderr block before the command starts (inspector URL, `blop issues --json`, `blop run --json`).
- **`blop run` prints one line before the command starts:** `blop run: telemetry to <url> · service <name> · content capture off (--capture-content) · found ai-sdk (blop init prints their setup)` (or `no agent SDK found here`). `--no-summary` drops it and the end summary block. The command's own stdio is untouched.
- **`blop run` inside `blop run`.** Every command `blop run` starts gets `BLOP_LOCAL_RUN_ID`, `BLOP_LOCAL_RUN_PID`, `BLOP_LOCAL_RUN_START` and `BLOP_LOCAL_RUN_DEPTH`, with a collector or without one. While that outer `blop run` is still running, a `blop run -- <cmd>` inside it joins its run (no second run, the outer TRACEPARENT and run id are kept; its own `--capture-content`, `--url` and `--port` do not apply, and it says so once; `--json` reports `recorded: false` with `nested_run`, plus `nested_flags_ignored` when flags were dropped), and a bare `blop run` inside it exits 2 (`nested_run`), so a dev script cannot recurse, even indirectly. A marker whose `blop run` has ended (a shell or terminal that outlived it) is ignored with one stderr note (`stale_run_marker` in JSON) and a new run starts. With 8 runs already enclosing it, `blop run` exits 2.
- **`--no-summary`** also drops the coding-agent block.
- **The lockfile and `packageManager` for bare `blop run`** are also looked up in parent folders, up to the git root (workspaces).
- **Root help** lists `open` (INVESTIGATE) and `completion` (MANAGE).

### New commands

- **`blop open [<issue|session|run|trace id> | <page>] [--print] [--json]`** opens the inspector at that object or page (home, sessions, issues, runs, connect, settings) on the resolved collector. It prints the URL instead with `--print` or `--json`, for a coding agent, with `--no-input`, with `BROWSER=none`, over SSH and with no display. An unknown target exits 2 with did-you-mean. `blop issue <id> --web` and `blop trace <id> --web` do the same for one object. `BROWSER=<command>` now names the program that opens a URL (also for `blop up`): a `:`-separated list (`;` on Windows), tried in order, with `%s` for the URL. When no browser starts, `blop open` prints the URL. On Windows the URL goes to `rundll32 url.dll,FileProtocolHandler` (not `cmd /c start`, which cut URLs at `&`).
- **`blop completion bash|zsh|fish`** prints a completion script generated from the command registry: commands, subcommands, flags and their values, plus live issue and run ids from the running collector (through the hidden `blop __complete`, which prints nothing when the collector does not answer).

### Renamed commands (old names still work, with a notice on stderr)

- `blop run x.blop.ts` (flow checks) is now `blop flow test`. `blop run` wraps a command.
- `blop test`, `blop watch` and `blop list` are now `blop flow test|watch|list`.
- `blop monitor start|stop|status` are now `blop up`, `blop stop` and `blop status`.
- `blop status` is a lifecycle command (`-o text|json|env`, `--logs`), not a query.

### Standalone agent testing and local coverage

- Move the agent-test DSL, runner, configuration, reporters, optional platform upload, templates and authored examples into `@blopai/test`. Import tests from `@blopai/test`; `@blopai/cli` retains the `blop` command and delegates Browser Harness flows to that package. Remove the old runtime/reporters/platform exports rather than preserving aliases.
- Add opt-in Chromium JavaScript coverage with `--coverage` and explicit collector delivery through `--coverage-endpoint`. Save the report before hosted artifact upload, remove stale coverage artifacts on non-coverage runs, and preserve test identities when a run ends early.
- Add the local inspector's Coverage view: latest report, file/test filters, executed UTF-16 source-character totals, paginated source annotations, test outcomes and visible collection gaps. Persist bounded reports separately from trace storage; do not imply statement/branch coverage, source maps, unloaded-file coverage or a run/trace join.
- Exclude oversized browser-script measurements with explicit warnings instead of losing the run's report. Validate collector payloads, enforce body and evidence limits, and apply the local write boundary.
- Discover DSL registrations, default exports and named test arrays together, deduplicate repeated exported objects, and retain registered checks on repeated Bun imports.
- Document agent-driven E2E goals, deterministic assertions, evidence, safe targets, runtime/provider failures, flake handling, CI artifacts and coverage limits.
- Move testing ownership to the independent `blop-oss/blop-test` repository; preserve the genuine May 2026 implementation and full dated provenance, squash the standalone import, and consume an immutable public Git source until npm publication is provisioned. Remove the workspace package, portable browser patch and testing release workflow; CLI, dashboard and generated GitHub workflows use the external SDK. Align skill tool results with the browser harness's caller-origin, untrusted content boundary.
- Verify canonical and legacy CLI flow execution against the standalone SDK's public exports with real Chromium and loopback app/provider fixtures: actual assertions produce exit 0/1 and matching results, JUnit, events and progress. Preserve the immutable shipping Git dependency while using the local clone for this verification; repair the clone's root example import to use `@blopai/test`.
