Skip to content

fix: re-enable history shortcuts - #503

Closed
jakst wants to merge 1 commit into
anomalyco:devfrom
jakst:jakst/history-shortcuts
Closed

fix: re-enable history shortcuts#503
jakst wants to merge 1 commit into
anomalyco:devfrom
jakst:jakst/history-shortcuts

Conversation

@jakst

@jakst jakst commented Jun 28, 2025

Copy link
Copy Markdown

Arrow keys up/down were not working for navigating through history. They were commented out in 568c047, looks like an accident, but I could be wrong.

Arrow keys up/down were not working for navigating through history. Looks like they were commented out in 568c047, looks like an accident, but I could be wrong.
@adamdotdevin

Copy link
Copy Markdown
Member

not an accident, there were other issues, but those might be resolved now. however, currently working on a feature that likely supplants this, stay tuned

@thdxr thdxr self-assigned this Jul 2, 2025
@jakst jakst closed this Aug 20, 2025
andreipromarketing-dev pushed a commit to andreipromarketing-dev/opencode that referenced this pull request Apr 7, 2026
…on for any source (anomalyco#503)

* feat(skill): add data-scraper-agent skill

Workflow skill for building AI-powered public data collection agents.
Covers any scraping target: job boards, prices, news, GitHub, sports, events.

- Full architecture guide (config.yaml, scraper/, ai/, storage/)
- Gemini Flash free tier client with 4-model fallback chain
- Batch API pattern (5 items/call) — stays within free tier
- Feedback learning loop from user decisions
- Notion / Sheets / Supabase storage templates
- GitHub Actions cron schedule (100% free)
- Anti-patterns table, free tier limits reference, quality checklist
- Real-world examples and reference implementation (job-hunt-agent)

* fix(skill): address PR anomalyco#503 review violations in data-scraper-agent

- Read batch_size from config.yaml instead of hardcoded constant
- Branch main.py on storage.provider; label example as Notion-only
- Replace undefined sync_feedback() with load_feedback() + comment
- Add commented Playwright browser install step to CI workflow
- Add permissions: contents: write; remove silent `git push || true`
- Remove external unvetted repo link from Reference Implementation
- Move import json to top of pipeline.py block (was after usage)
- Guard context.md read with exists() check; fall back to empty string
- Replace deprecated datetime.utcnow() with datetime.now(timezone.utc)
- Remove duplicate config.yaml entry from project directory template
mayoalexander added a commit to FREELABEL/iris-opencode that referenced this pull request Jun 24, 2026
Ships the recent multi-session work now on main: #150147 push-retry reliability
fix, iris agents update (--enable-integration/--add-tools), bloqs make-public/share,
atlas:item publish-markdown + harden + image-upload, iris integrations pathways
onboard (anomalyco#503), iris mcp register (#150264), outreach-strategy alias, learn/okf.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mayoalexander added a commit to FREELABEL/iris-opencode that referenced this pull request Jul 5, 2026
Wire the two primitives IRIS already shipped but never connected: on-device
whisper.cpp STT (lib/transcription.transcribeLocal, already used by `ideas
capture`) + the V6 ReactLoop chat brain (streamAgentChat). Adds the missing
local TTS + push-to-talk turn-taking so you can hold a spoken conversation
with any agent (e.g. TOBI anomalyco#642) fully on-device — $0/turn, offline-capable,
HIPAA-safe. Cloud voices (ElevenLabs/VAPI) stay in `iris voice` for phone.

- lib/voice.ts: captureMic (ffmpeg push-to-talk → 16kHz WAV), speak (macOS
  `say` default, Piper optional, never throws), listMics (device discovery).
- chat: --voice loop with multi-turn conversation_history (no server session,
  no cross-turn poisoning); flags --mic / --tts / --tts-voice / --list-mics.

Plan + tracking: bloq anomalyco#503 item #158044, bug #158045.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mayoalexander added a commit to FREELABEL/iris-opencode that referenced this pull request Jul 5, 2026
…tdin hang

The push-to-talk loop hung on the SECOND stdin read ("press ENTER to stop"):
manual `process.stdin.once('data')` + pause/resume fought the prompt lib's
raw-mode TTY in Bun, so the stop keypress was never seen.

Fix:
- captureMic now auto-stops on silence (ffmpeg `silencedetect`, ending the turn
  on trailing silence after speech / a paused opening utterance), with a manual
  ENTER override via `stopSignal` for rooms where the VAD threshold misfires.
- The loop uses ONE readline interface for every turn (question + rl.once('line')
  stop, removed after) instead of raw stdin juggling — works across turns.
- Dropped the clack spinner inside the loop (it flips stdin to raw mode); status
  is plain stderr now. Blank/`[BLANK_AUDIO]` transcripts are skipped.

Result: talk → pause (or ENTER) → agent replies + speaks → repeat. No hang.
Tracking: bloq anomalyco#503 #158044, bug #158045.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mayoalexander added a commit to FREELABEL/iris-opencode that referenced this pull request Jul 5, 2026
…+ clean UI

Field feedback: "works but not seamless." Three fixes:
- Hands-free after turn 1: the agent answers, then the loop auto-listens again
  (no ENTER per turn). ENTER at start; two silent captures fall back to ENTER so
  we never hot-loop on room noise; say "goodbye"/"quit" (or Ctrl-C) to hang up.
- Speakable replies: a per-turn voice hint steers 1-2 short spoken sentences,
  no markdown/lists (appended to the query only, not stored/displayed, so it's
  backend-agnostic — no dependency on a system role). Long paragraphs were the
  worst part of TTS.
- Clean line-based output: dropped the \r status writes that collided with the
  readline echo (the stray ">"/"TER)" artifacts). Tighter 1.1s silence cutoff.

Tracking: bloq anomalyco#503 #158044, bug #158045.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mayoalexander added a commit to FREELABEL/iris-opencode that referenced this pull request Jul 5, 2026
…can't hang

Silence auto-stop was unreliable across rooms/mics (field report: "really bad at
detecting silence", and it hung when the user didn't speak — silencedetect never
fired and the ENTER override via rl.once('line') could miss the keystroke).

Rewrite to a fully deterministic loop:
- START and STOP are BOTH rl.question() reads (the start read always worked in
  the field, so reuse the exact mechanism for stop). ENTER records, ENTER sends.
  No keystroke can be missed; ffmpeg is SIGINT'd the instant ENTER resolves.
- Silence detection is now opt-in behind captureMic({autoStop}); default off.
- 60s hard cap as a safety backstop only. Short-reply voice hint retained.

Result: predictable, hang-proof turn-taking that works in any audio environment.
Tracking: bloq anomalyco#503 #158044, bug #158045.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mayoalexander added a commit to FREELABEL/iris-opencode that referenced this pull request Jul 13, 2026
…I (#165979)

Three subcommands over the fl-iris-api /api/threads API (IRIS_API base):
- message <agent> <content> --thread|--to : post into a room AS an internal
  agent (uses the as_agent_id keystone); --no-trigger suppresses replies;
  --to opens a fresh DM thread with the recipient auto-responding.
- thread [id] : list threads, or show one room's participants + messages.
- inbox <agent> : list rooms an agent participates in.

Ensures the sender is an internal participant before posting (server rejects
otherwise). buildThreadMessageBody() is pure + unit-tested. Surface half of the
multi-agent rooms epic (bloq anomalyco#503 / list anomalyco#1688); pairs with fl-iris-api c4be6710.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
mayoalexander added a commit to FREELABEL/iris-opencode that referenced this pull request Jul 19, 2026
Every data path we shipped pointed inward. `data-sources sync` pulls cloud
storage INTO a bloq, `bloqs ingest` uploads a file INTO a bloq. The only way
out was a per-entity `pull <id>` (boards/leads/agents/workflows/pages/products/
events), one id at a time, CLI-only — and `iris bloqs` had no pull at all, so
the container itself (lists, items, attachments) could not be exported.
`iris export` exists but exports a chat session, not workspace data.

So "can I get my data out?" had no good answer, which is both a real data-loss
exposure and the shape that reads as lock-in to the enterprise/HIPAA buyers we
are selling to.

  iris bloqs export <id|name> [-o dir] [--attachments] [--no-markdown] [--json]

Writes bloq-<id>-<slug>/ containing:
  - bloq.json     full API payload, verbatim (fidelity copy)
  - items/NN-list/NNN-title.md   one markdown file per item, metadata in
                                 frontmatter (the copy that stays readable
                                 without us)
  - files.json    attachment manifest; --attachments downloads the bytes
  - manifest.json format version, counts, and what was NOT included

Notes:
  - resolves by name as well as id, reusing the existing resolveBloqId
  - attachment fetch failures are non-fatal and counted in the manifest —
    a partial export beats no export, but the gap is recorded, not silent
  - does not stack a second H1 on bodies that already open with one
    (everything from `bloqs publish` carries its own title)

Verified against bloq anomalyco#503: 32 lists, 350 items, 350/350 markdown files,
round-tripped by both id and name.

Next (not in this commit): --schedule via the existing scheduler, a workspace-
level export across bloqs, and UI + API surfaces — today a non-CLI client still
has no path to their own data.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mayoalexander added a commit to FREELABEL/iris-opencode that referenced this pull request Jul 19, 2026
A per-bloq export answers "can I get this bloq out?". It does not answer
"can I get my data out?" — you'd have to enumerate your bloqs by hand and
remember to add new ones. One command, and therefore one cron line, has to
capture everything.

  iris bloqs export --all -o ~/iris-backup

Walks every bloq the user owns, exports each with the existing single-bloq
logic (now factored into exportOneBloq), and writes workspace-manifest.json
at the root: per-bloq counts, totals, and an explicit failures[] array.

Design notes:
  - a per-bloq failure does NOT abort the run. One bad bloq must not cost you
    the entire backup, so failures are collected and reported, never silently
    dropped. failures[] is in the manifest whether or not it's empty.
  - the no-arg case now guards BEFORE the intro banner, which previously
    printed "Export bloq undefined" at you before saying what it wanted.

Verified end-to-end against the live account:
  109/109 bloqs, 778 lists, 8,924 items, 0 failures, ~3 min, 84 MB on disk.
  Integrity checked: 8,924 .md files on disk == 8,924 items in the manifest.
  Single-bloq export re-run as a regression (anomalyco#503: 32 lists / 350 items).

Scheduling is deliberately NOT in this commit. A scheduled export has to run
where the disk is, and the server-side scheduler cannot write to a user's
machine — the honest home is a hive_task_dispatch job on the local daemon, and
I'm not guessing at that payload contract. Today `--all` is cron-able as-is.
Still open: UI + API surfaces — a non-CLI client has no path to their data.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mayoalexander added a commit to FREELABEL/iris-opencode that referenced this pull request Jul 29, 2026
Closes competitive gap G2 (bloq anomalyco#503 item #178177, bug #178193) from the
Agent Orchestrator gap analysis: Hive owned the harder half (multi-machine
dispatch + peer mesh) but had no cockpit, so blocked work was invisible
until you thought to ask.

`hive tasks` answers "what is on this node". `hive board` answers "what is
blocked on me", across every node:

- Aggregates the three existing sources (local bridge daemon queue, claimed
  pending tasks, cloud fleet history) and dedupes by task id.
- Groups into NEEDS YOU / WORKING / QUEUED / DONE, mirroring AO's lane model.
  DONE is hidden by default — surface what's blocked, hide what's finished.
- Attributes every task to a node via the node roster.
- Flags queued tasks older than --stale-after (default 60m) as stuck, so
  silently-wedged work lands in NEEDS YOU instead of looking merely queued.
- Prints an explicit degraded-source warning when a fetch fails, so a partial
  read can never masquerade as an idle fleet.

Flags: --all --node --since --limit --stale-after --json

First run on the live fleet surfaced 23 failed dispatches in 24h and 1/11
nodes online — none of it previously visible from any single command.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mayoalexander added a commit to FREELABEL/iris-opencode that referenced this pull request Aug 15, 2026
…R-14

`iris imessage send` shelled out to osascript; `iris mail send` POSTed straight to the
bridge; `iris run send_imessage` / `send_email` mapped onto the bridge directly. All three
worked, and all three were invisible — nothing wrote lead_comms, so the log was only ever as
fresh as the last time somebody remembered `atlas:comms ingest`. Measured in production
(#178647): 27 of 28 leads with iMessage history were more than a week stale, several by
~2 months.

The bridge is still the transport. The router is now the bookkeeper.

comms-send.ts is the single call into POST /api/v1/comms/send, so there is one place that
knows the wire format rather than three. `iris run` deliberately FALLS THROUGH to the bridge
if the router is unreachable — it is the low-level escape hatch, and removing someone's
ability to send because the API is down would be a worse failure than an unlogged send.

Epic: bloq anomalyco#503 / list anomalyco#1922

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mayoalexander added a commit to FREELABEL/iris-opencode that referenced this pull request Aug 16, 2026
… with the tests they went out without

Gemini CLI support for `iris mcp install`:

  iris mcp install --client gemini   ->  ~/.gemini/settings.json

Keyed "iris", not the canonical "IRIS OS". Gemini names every tool
mcp_<server>_<tool> and then parses the server back out by splitting at the
FIRST underscore, so a key containing a space or an underscore resolves to a
server that does not exist and silently breaks per-server includeTools /
excludeTools / trust. The entry also declares IRIS_API_KEY explicitly: Gemini
force-redacts *KEY* host vars from stdio servers, and an explicit `env` is
merged in after that redaction (unlike `headers`, which it expands against the
SANITIZED env and would turn into an empty bearer).

Verified end to end against Gemini CLI 0.55.1 — server reached, tools listed:
  ✓ iris: ~/.iris/bin/iris mcp serve (stdio) - Connected

Also lands the tests for two fixes that were committed without them:

  platform-bloqs-list-filter.test.ts   `bloqs items -l <list>` filtered only the
    single page it happened to fetch, so on bloq anomalyco#503 (563 items) it answered
    "No items found" for a list with 28. An empty result wearing the costume of
    a definitive one. Covers paging-until-satisfied and the honesty flag that
    separates "empty" from "I stopped looking".

  platform-pages-scaffold.test.ts      `pages create` scaffolded a SiteFooter
    with no `copyright` — a prop COMPONENT_REGISTRY in the same file declares
    required — so it rejected the page it had just built, and since `pages push`
    404s on a slug that does not exist there was no create-then-push path at all.
    Checks every scaffolded component against its own registry contract.

20 tests, typecheck clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LiYuu8tdfepPpYD5PxRHh2
mayoalexander added a commit to FREELABEL/iris-opencode that referenced this pull request Aug 21, 2026
`publish` reading scaffold/how-to is correct and stays — publishing from ~/.iris/how-to
would push unreviewed files nobody merged. The bug was the other half: `add` wrote to
~/.iris/how-to silently, so a recipe was authored into the one directory `publish` never
reads, and every surface reported success. Six recipes had been sitting there unpublishable,
and nothing named them.

- `add` writes to the repo by default and mirrors to ~/.iris/how-to so view/search/MCP see
  it without a reinstall. It prints the path it actually used. `--local` is now an explicit
  choice, and says plainly that the recipe cannot be published.
- `how-to promote <name>|--all` — copy a local-only recipe into the repo. Bare `promote`
  lists what is stranded.
- `list` marks local-only recipes; `publish` names them on both the dry-run and success
  paths. The absence was previously rendered nowhere, which is the same shape as a deploy
  check that cannot tell "healthy" from "not measured".

Promotes four of the six stranded recipes and adds remote-support-a-family-pc.

SANITISED — /how-to is public and two live recipes were leaking:
pulse.md and crowdfunding-opportunities.md carried a real person's name, a real lead ID and
a deal value. manage-staff-and-contracts carried a real prospect's name and email address as
example values. All replaced with example data. andrew-esher-full-demo and onboard-new-client
are held back deliberately — they name a prospect throughout and disclose his client pricing.

The only control that caught this was reading the files by hand. A refusing pre-publish scan
is specced as HOWTO-06 in bloq anomalyco#503.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GjMn3kzkiaNWMrA4REERpu
mayoalexander added a commit to FREELABEL/iris-opencode that referenced this pull request Aug 22, 2026
…already written

`iris timeline [--since 4mo] [--source ...] [--grep ...]` — 6,330 episodes across 18 weeks,
grouped by week, from four channels that already record what happened:

  diary     473  2026-04-24 → 08-21   GET /api/v6/diary/list (126 days already synced)
  git      5726  2026-04-24 → 08-21   commit subjects across all 11 nested repos
  opencode   79  2026-05-12 → 08-18   session `title`, 79/79 titled
  claude     52  2026-07-16 → 08-21   `custom-title` / `ai-title`, 53/68 titled

NO MODEL, NO NEW TABLE, NO SUMMARISATION. Every line was written by a human or by the tool that
produced the session, so it cannot hallucinate and costs nothing to run. Persisting these as
`signal_episodes` on the corpus spine is the follow-up (TL-01, bloq anomalyco#503 list anomalyco#2124) — worth doing
once the shape is proven, not before.

SESSION CONTENT IS NEVER READ — titles and timestamps only. `corpus-collect.mjs` set that rule
because prompts carry credentials and client data, and this inherits it rather than re-opening it.
Today's session alone contains two live production passwords, which is the argument.

COVERAGE IS PRINTED PER SOURCE, and that is the feature. These channels begin on different dates —
Claude Code only reaches mid-July, opencode to May. A merged view without per-source ranges
silently presents "nothing happened before May" as a fact about the business rather than a fact
about the instrument. The first run proved the point: diary returned 0 and the coverage line said
so instead of quietly dropping 473 episodes (missing user_id — the endpoint is user-scoped, and it
answers for nobody without it, with a 200).

Per-week truncation names what it hid ("… 616 more from git this week"), same rule as how-to.

Two traps carried over from the corpus collector rather than re-learned: this monorepo has ELEVEN
nested repos and NO .gitmodules, so gitlinks are read from the index; and each candidate must be
its own `rev-parse --show-toplevel`, because `git -C` on an uninitialised gitlink walks UP and
answers about the parent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GjMn3kzkiaNWMrA4REERpu
mayoalexander added a commit to FREELABEL/iris-opencode that referenced this pull request Aug 23, 2026
productName/identifier/window title/menu/dialogs/CLI-install messaging,
Cargo.toml metadata, package.json name, AppStream metainfo, and a full
regenerated icon set (from the existing IRIS app icon) all move from
"OpenCode" / ai.opencode.desktop to "IRIS" / io.heyiris.desktop. Updater
endpoint now points at FREELABEL/iris-opencode's own release feed instead
of anomalyco/opencode's, with a freshly generated signing keypair (pubkey
only, committed; private key handed off out of band).

Also fixes a real bug found along the way: cli.rs's CLI-install path
checker was hardcoded to ~/.opencode/bin/opencode, but the actual `install`
script at the repo root has installed to ~/.iris/bin/iris for a while —
the desktop app's "Install CLI" / version-sync logic was checking a path
the real installer never creates.

Renamed the sidecar handle (opencode-cli -> iris-cli) and the desktop
package's SIDECAR_BINARIES target names to iris-<platform>, matching what
release.yml's `build` job actually produces. Left @opencode-ai/* package
names, OPENCODE_* env vars (the desktop<->CLI subprocess protocol), and
the Rust crate/lib name untouched -- internal contracts, not user-facing
branding, and out of scope here.

NOT done in this pass (tracked in bloq anomalyco#503 item #182113): wiring this
package into release.yml at all (it isn't built by CI today), real code
signing/notarization, and rewriting prepare.ts's artifact download to
match release.yml's actual per-target archive shape.
mayoalexander added a commit to FREELABEL/iris-opencode that referenced this pull request Aug 24, 2026
Local Rust builds keep hitting ENOSPC (disk is at 99% capacity on the
machine doing this work) -- adding a workflow_dispatch-only job that
builds packages/desktop on a macos-latest runner and uploads the .app/.dmg
as workflow artifacts, so the rebrand can be tested without local disk
headroom.

Unsigned, ad-hoc output for local testing only -- not wired into the real
release process (release.yml is untouched). Safe to delete once real CI
wiring for this package lands (tracked in bloq anomalyco#503 item #182113).
mayoalexander added a commit to FREELABEL/iris-opencode that referenced this pull request Aug 24, 2026
Adds build-desktop-tauri + desktop-tauri-assets, mirroring the existing
build-desktop + desktop-assets pattern used for the Electron/SaaS-wrapper
app: independent, continue-on-error, non-blocking, attaches to the
already-created release asynchronously so a slow/failed desktop build
never holds up CLI releases.

Reuses the exact build steps already proven working in
desktop-test-build.yml (fresh bun install -> build the iris CLI -> stage
it as the Tauri sidecar -> tauri build -> zip the .app), but with the
real release version instead of a hardcoded test version.

Deliberately NOT done here: code signing / notarization (no Apple
Developer ID cert, no Windows cert, no owned TAURI_SIGNING_PRIVATE_KEY
exist for this repo -- gh secret list confirms only CROSS_REPO_PAT is
set) -- updater artifact generation is explicitly disabled until a real
signing key exists. Output is unsigned/ad-hoc-signed, fine for continued
testing, NOT yet fine for a public landing-page download. Tracked in
bloq anomalyco#503 item #182113.

This wiring itself is untested end-to-end -- exercising it for real means
triggering an actual tagged release (or a workflow_dispatch version bump),
which publishes a real public GitHub Release. Deliberately not doing that
as part of this commit.
mayoalexander added a commit to FREELABEL/iris-opencode that referenced this pull request Aug 24, 2026
build-desktop-tauri-windows -- same sequence proven working in
desktop-test-build.yml moments ago (this is the first real Windows build
of packages/desktop that has ever existed): build the iris CLI, stage it
as the Tauri sidecar (bin/iris.exe -- confirmed via a live run, no
ambiguity like the Mac path had), tauri build --bundles nsis, rename the
installer to the IRIS-tauri-* convention so desktop-tauri-assets picks it
up alongside the macOS zips.

desktop-tauri-assets now needs both Tauri jobs and uploads *.exe alongside
*.zip. Independent job, continue-on-error, same non-blocking pattern as
everything else here.

Unsigned -- no Windows code-signing cert exists for this repo. Alex's
call: acceptable for now (bloq anomalyco#503 item #182113). SmartScreen will warn
on first run.
mayoalexander added a commit to FREELABEL/iris-opencode that referenced this pull request Aug 29, 2026
`iris playbook publish` succeeded, printed scope/bloq/access, and handed back
nothing you could send anyone. `iris playbook show` printed a filesystem path.
So a playbook could be published to a team and reach nobody — publishing that
produces no reachable address is not publishing.

Both now print https://<host>/p/playbook?name=<name>, backed by the
PlaybookDetail page shipped in iris-api 61197d24. That page resolves by name and
inherits the API's visibility exactly, so the link is safe to hand out: public to
anyone, project to the bloq, private to the owner, 404 to everyone else.

Built from IRIS_API rather than hardcoded, so a staging or self-hosted install
prints its own host instead of confidently sending somebody to production.

publish also prints WHO CAN OPEN IT — "unlisted — anyone in bloq anomalyco#503 can open
it, nobody else". "Published" does not say that on its own, and the gap between
what an operator thinks they shared and who can actually read it is exactly where
an internal procedure ends up somewhere public.

show prints the URL unconditionally rather than only when published: an
unpublished playbook 404s there, and seeing that is a clearer answer than being
told nothing at all.

IRIS_API moved to the static import alongside the rest of the helper surface;
it was dynamically imported at two call sites for historical reasons, which kept
playbookUrl from being synchronous.

Refs #182116

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ToSHC9fFDT6d88d1riP95B
mayoalexander added a commit to FREELABEL/iris-opencode that referenced this pull request Sep 4, 2026
IH-08, the CLI half of the Integration Health epic (bloq anomalyco#503 list anomalyco#2315).
Closes the reporting ask in #182476.

    iris integrations health            what the last sweep found
    iris integrations health --probe    probe every connection now
    iris integrations test gmail        probe one connector now

Both read the verdicts the server-side sweep writes, so the CLI and
/p/integrations cannot disagree about whether something works. One definition of
"working", computed in one place. A second client-side opinion is how
list-connected ended up printing [unverified] next to connections that executed
their functions fine (#182861).

FOUR STATES, KEPT APART:

    ✓ working        a probe ran and passed
    ✗ failing        a probe ran and was refused — with the reason
    ? never checked  nothing has ever probed this
    ∅ n/a            runs in-process; there is no remote thing to check

"never checked" is deliberately neither the pass marker nor the fail marker. It
is an absence, and drawing it as either is a claim we cannot support — the same
distinction list-connected already makes between "we asked and were refused" and
"we could not ask". `status` says 'active' for a connection nobody has ever
contacted, so any renderer reading status alone shows a green nobody earned.

The summary prints every state including the zeroes, so "0 failing" is a measured
zero rather than something inferred from an absence of red lines, and a fleet
where nothing was ever checked reads as exactly that instead of as healthy.

--json emits {error, connections: null} on a failed lookup rather than an empty
list. An agent parsing [] states it as fact; that is what told a client her live
Gmail was not connected, and the retry it prompted created the dead connection it
had described.

A 404 'not_connected' renders as "you have not connected this" with a connect
hint, never as a failure — different answer, different fix.

Verified end-to-end, not just compiled: served the fl-api branch locally, seeded
one connection per state, and confirmed all four render distinctly from the real
payload. 14 unit tests cover the formatting (bun test). tsc --noEmit reports no
errors in these files; the one pre-existing error in src/session/llm.ts is
untouched by this change.

NOT pushed. This repo has only a pre-push hook, no pre-commit one, so the
--no-verify on the commit skipped nothing — noted because the message previously
implied otherwise. The pre-push guard has not been run against this branch yet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A2ZSEpG7FUdNk8ZpU9YFrR
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants