Skip to content

feat(web): add stack web ask - #98

Merged
arthware-dev merged 10 commits into
feat/web-stacklet-searchfrom
feat/web-ask
Sep 16, 2026
Merged

arthware-dev merged 10 commits into
feat/web-stacklet-searchfrom
feat/web-ask

Conversation

@arthware-dev

Copy link
Copy Markdown
Contributor

Stacked on #97. Phase 3 of docs/design/web/plan.md.

stack web ask "does immich support raw files" searches, reads the top snippets, and answers in a few sentences with a numbered source list. One search, one model call, no page fetching and no tool loop.

The sources print whether or not an answer was found, so a refusal is still useful.

Also carries the landscape survey (docs/design/web/landscape.md) and the correction that the fetcher's constraint is resident memory, not image size.

@arthware-dev

Copy link
Copy Markdown
Contributor Author

Both halves of the plan's verification gate, live against the local model (Qwen3.6-35B-A3B).

A question the snippets answer — grounded, cited, 8 real sources:

$ stack web ask "does immich support raw files"

Immich supports RAW files, although there are specific limitations and ongoing
discussions regarding certain formats and pairing. For example, Sony RAW (.ARW)
support has been noted as a blocker, with suggestions to use `magickload` [2].
Additionally, there are feature requests to support RAW+JPG pairing [6][7].

  Sources
  [1] Supported Media Formats - Immich Docs
      https://docs.immich.app/features/supported-formats/
  [2] [BUG] Sony RAW (.ARW) unsupported · Issue #2156 · immich-app/immich
      https://github.com/immich-app/immich/issues/2156
  ...

A question they don't — the important one. It refuses, says what the results do contain, and still lists them:

$ stack web ask "how many watts does a mac mini m4 draw while immich transcodes 40000 photos"

The provided search results do not contain information regarding the specific
wattage a Mac Mini M4 draws while transcoding 40,000 photos with Immich. The
results only provide general power consumption ranges for the Mac Mini M4
(4–155W) [1][2] and mention Immich in the context of NAS comparisons [7], but
do not link specific workloads to power usage.

  Sources
  [1] What Are Mac Mini Power Requirements? ...

7.5s end to end, which fits inside a chat round trip behind the existing 👀 ack.

Architecture note. The work runs inside the bot-runner via the framework's existing stack.bot_runner.dispatch, not on the host: ./stack is stdlib-only by design and the LLM client wraps the OpenAI SDK. That is the same arrangement as stack docs and stack memory capture, so no third copy of the dispatcher. stack web fetch has no such dependency and still runs with nothing up at all.

Harness: tests/fixtures/web/searxng-response.json is a recorded real response, so the 17 prompt tests run with no network and no model. The load-bearing one asserts the prompt still licenses "I don't know" — drop that instruction in a future rewrite and the command starts inventing answers that read exactly like good ones.

Tests: 53 framework web tests green.

@arthware-dev

Copy link
Copy Markdown
Contributor Author

Design correction, pushed as 80d9c77. No behaviour change: ask runs in the bot-runner exactly as before.

I tried moving ask to the host (one POST is not much code, and it would drop the dependency on core). That was wrong twice over, and both failures are now written into the plan.

It silently broke the model. Hand-rolling the call lost chat_template_kwargs: {enable_thinking: False}, and the answer became:

Here's a thinking process:
1.  **Analyze User Question:** "does immich support raw files"

That knob was verified against oMLX after reasoning_effort, a top-level enable_thinking, a reasoning object and /no_think all failed. One LLM client, not two. The entrypoint docstring now records this so the next reader does not repeat it.

The invariant it was serving is not defensible. The plan said "host-native where possible". But every page we read is attacker-controlled input going into an HTML or XML parser, and soon a browser. On the host that runs as the user who owns the machine, with their SSH keys and the family vault. We already run Docker; isolation is free. The host should run only the Apple Silicon inference that cannot be containerised without losing Metal.

Two consequences now in the plan:

  1. arm64-only was overstated. Docker runs linux/amd64 here, so x86-only projects are usable. Emulation costs latency (the measured 40s SeleniumBase path was the emulation), so judge on measured latency rather than on whether an arm64 wheel exists. Poor trade for a browser, fine for a fast CPU-bound library. This reopens candidates the landscape doc rejected on arm64 grounds.
  2. stack web fetch violates the new rule and is recorded as a known violation rather than left implicit. Worth being precise: the production capture path is already contained, since the archivist imports lib/stack/web inside the bot-runner. Only the operator CLI is exposed, and only when an operator runs it. The fix is the same stack.bot_runner.dispatch route, and it costs the "works with nothing running" property that the invariant now says is not worth its price.

Tests: 98 framework web tests green.

Assessment of stealth fetching, extraction, search backends, standards
and licence constraints behind the web plan.

Corrects the plan's stated constraint. Image size is cheap and the
stacklet is opt-in, so a larger image for a better fetcher is an
acceptable trade. The binding constraint is resident memory, since a
browser held warm competes with photo thumbnailing and OCR. The phase 4
gate is now a memory measurement rather than a disk budget.

Scrapling no longer bundles Camoufox, so the fallback the plan named
does not exist.
stack web ask searches, reads the top result snippets, and answers in a
few sentences with a numbered source list.

The sources are printed whether or not an answer was found. When the
results do not cover the question, the answer says so and still lists
what was consulted rather than presenting something unsupported.

One search and one model call, with no page fetching and no tool loop.

Refs docs/design/web/plan.md (phase 3).
Replaces the plan's "host-native where possible" rule. Pages are
attacker-controlled input passed to an HTML or XML parser, and later to
a browser. On the host a parser fault runs with the machine owner's
privileges. The host should run only the Apple Silicon inference that
cannot be containerised without losing Metal.

Two consequences are recorded. Containers remove the arm64-only
constraint, since Docker runs linux/amd64 images on this hardware, so
candidates are judged on measured latency rather than wheel
availability. And stack web fetch does not yet satisfy this rule.

Also documents why stack web ask runs in the bot-runner.
The survey was written treating x86-only projects as disqualified, and
rejected candidates on that basis. Since these components run in
containers and Docker runs linux/amd64 on this hardware, that rule was
too strict.

Emulation is a cost to measure rather than a barrier: significant for a
browser, negligible for a CPU-bound library. Candidates dismissed only
for missing arm64 wheels are marked for re-assessment.
Re-assessed after the arm64 constraint was relaxed, and rejected. The
project has a single contributor, has been inactive since April, remains
at v0.1.1, and the benchmark it leads is published by its own author.

It would also improve extraction where current results are already
adequate. The one known extraction failure was a recipe with missing
quantities, which structured data resolves; the remaining failures are
sites declining to serve content, which no extractor addresses.

Names magic-html as the cheaper candidate should extraction quality
become a measured problem.
Sources often report several related figures under one loosely worded
question, for different periods and under different definitions.
Selecting one and labelling it with the questioner's term produces an
answer that is wrong but well sourced.

The prompt now takes the measure's name from the source rather than
restating the question, reports both figures when the sources separate
what the question conflates, and states the period each covers.

It is also given the current date, and asked to answer in the language
of the question.
Extraction requires trafilatura, which the host does not provide. A
missing extractor and a page with no article body were
indistinguishable, so the command reported pages as empty without having
read them.

Fetch now runs where the archivist runs, giving the CLI the same code
and the same library versions as production. A missing extractor raises
ExtractorUnavailable instead of resolving to a verdict about the page,
and trafilatura is pinned to one minor line in both the container and
the test environment, which had been resolving different versions.
Search-result and round-up pages carry no article body, so extraction
returned almost nothing and the capture was rejected as empty. These
pages publish their entries as schema.org ItemList markup.

Tier 1 now renders that list as the entry, one line per item with its
link. Readers are tried in priority order across the whole document so
a breadcrumb trail cannot outrank the page's actual subject, and lists
shorter than four entries are declined as probable breadcrumbs.
@arthware-dev
arthware-dev force-pushed the feat/web-stacklet-search branch from 89d3ea5 to a721c7e Compare September 16, 2026 08:51
@arthware-dev arthware-dev changed the title feat(web): ask a question, get an answer with its sources feat(web): add stack web ask Sep 16, 2026
stack web search names the command to run when the web stacklet is
down; stack web ask reported the same condition as an open question.
Both now give the same instruction.
SearXNG keeps no index and no state, so the declared data directory was
never created. Removing it means teardown has nothing to clean up.

The hints still told users that stack web fetch works with nothing
running, which stopped being true when it moved into the bot-runner. It
does not need this stacklet; it does need core.
@arthware-dev
arthware-dev merged commit 8f1b966 into feat/web-stacklet-search Sep 16, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant