Commit Graph
10 Commits
Author SHA1 Message Date
bot-hermes b8a0aa0242 feat(web): resolve walkthrough diffs for GitLab merge requests 2026-08-16 16:23:38 +00:00
Bohdan Triapitsyn ce0e1cea27 fix(git): resolve the base branch from the repository instead of its name
Follow-up to #2629, which stopped the walkthrough from comparing against a
branch that does not exist. The same guessing, and the same near-misses in how
the answer was applied, were left elsewhere:

- The default branch travelled as `rootBranchHint`, whose documented meaning is
  "the branch the project root worktree is on". It gets its own option, because
  a parameter that means two things is one the next caller gets wrong.
- A candidate equal to the branch being compared is skipped. In a plain checkout
  the root hint *is* the current branch, so it won every time and produced a
  comparison with itself; the repository default now wins there.
- The Changes and pull-request surfaces read the default branch too. A pull
  request opened against a branch that does not exist is a worse failure than a
  walkthrough that will not generate.
- `hasResolvableBaseBranch` matched `origin/feature/main` for a base of `main`,
  passing the check and then failing the comparison it exists to prevent.
- `getRangeDiff` promoted only `origin/<base>`. A base carried by any other
  remote stayed a bare name, which git resolves against refs/heads and nowhere
  else, so it failed exactly as before.
- `getBranches` dropped every branch of a remote that did not answer, turning
  "we could not ask" into "these branches are gone" — offline, that silently
  removed comparisons that work fine against local remote-tracking refs.
- A remote with no `remote/HEAD` is asked once with `ls-remote --symref` rather
  than falling back to the guess this data exists to replace.

The `defaultBranches` contract was documented under the status response; it
belongs to the branches response, which now has a section of its own.
2026-08-04 22:41:09 +03:00
Rajat Asthana 80716dbf6c Merge branch 'openchamber:main' into fix/walkthrough-remote-default-branch 2026-08-04 22:27:56 +05:30
RyderAsking b4ced01cc7 fix(walkthrough): use remote default branch 2026-08-04 16:48:24 +00:00
Bohdan Triapitsyn 8c37061886 fix(walkthrough): name an outdated server instead of failing to parse its HTML
A server without these routes does not answer 404 with JSON. The unmatched
/api path reaches the OpenCode proxy, and OpenCode serves its embedded web UI
for anything it does not recognise — HTML, status 200 — so a client newer than
its server parsed a web page as JSON and put "Unexpected token '<', "<!doctype"
in the panel, naming neither the cause nor the remedy.

The client now checks the content type before parsing. A non-JSON answer on 2xx
or 404 blocks with "this server is older than the app, update it and refresh".
A non-JSON 5xx keeps its own failure: a server that answered badly is not a
server missing the feature, and sending that user to upgrade chases the wrong
thing.
2026-08-04 19:06:19 +03:00
Cursor AgentandSerhii Dziupin 35f17e9e96 fix(walkthrough): hide unauthenticated models and disable Generate
Do not present a provider without a login as the selected walkthrough
model, and grey out Generate when readiness is false instead of showing
a login-error blocker or raw auth banner.

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-04 12:22:54 +00:00
Cursor AgentandSerhii Dziupin abb396e080 fix(walkthrough): block unauthenticated providers with a friendly refusal
When the walkthrough small model resolves to a provider with no usable
login, readiness was still ready and generate returned a raw 500 message.
Refuse up front with no-provider-login and surface a blocker instead.

Closes openchamber/openchamber#2607

Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
2026-08-04 11:29:58 +00:00
Bohdan Triapitsyn ce09ca6190 fix(walkthrough): offer German, and catch the next locale that is only half added
German was added to the interface but not to the walkthrough's own language
list, and nothing failed: the picker offered Deutsch because it is built from
the interface locales, the server resolved the tag to English, and a German
reader paid for a walkthrough written in English while the picker still said
Deutsch.

The two lists cannot be one — the server cannot import from packages/ui — so
a test reads i18n/runtime.ts and compares them, in both directions and through
normalizeLanguage. Drift this quiet needs a test rather than vigilance.
2026-08-03 13:55:51 +03:00
Bohdan Triapitsyn 1d17cb87b3 feat(walkthrough): write walkthroughs in the reader's language
A guided explanation is only useful in a language the reader reads, so the
panel header gets a language picker alongside the model one, defaulting to
the interface language. Like the model, it is request state rather than a
setting: the language travels with the read and the generation, and the one
a walkthrough was written in is stored with it, so reopening a review
describes what is there instead of what a fresh one would be.

Only prose is translated. Hunk aliases resolve back to hunk ids and
icon/importance are validated against fixed English values, so a translated
one would be dropped by the normalizer — silently losing an anchor or a
style. Identifiers and paths stay as they appear in the code.

The language is part of the cache key, and a read now asks the cache for the
exact request it was given before falling back to the pointer. Without that
the panel answered a request to switch languages with the text it already
had, leaving the other language unused in the cache.

Alongside it:

- The answer budget is derived from the resolved model instead of a flat 24k.
  That number was the same for a 64k-context model and for one that admits to
  384k output tokens, and on the latter it was the only reason generation
  failed: the model spent the whole allowance reasoning and returned nothing.
  It is now min(96k, max(24k, a quarter of the context)) capped by the
  catalog's output limit, decided once so the input reserve and the request
  cannot drift apart.
- A read no longer offers Cancel. It is a few hundred milliseconds of git with
  nothing to cancel, and the button flickered on every model or language
  change. When the panel is showing a fallback, a banner names what is on
  screen versus what was asked for — only once the read has settled.
- The header keeps one 32px control height and drops its labels below 680px
  instead of squeezing them to two letters and an ellipsis.

Docs and module documentation updated in every locale.
2026-08-03 01:27:27 +03:00
Bohdan Triapitsyn 34d0ff7383 feat(walkthrough): guided AI walkthrough for diffs, branches, and PRs (#2572)
A diff is ordered by file path, which is almost never the order in which a
change makes sense. This adds a Walkthrough surface that reorders it: the model
groups related hunks into stops, explains what each group changes about
behavior, and orders the stops so each builds on the last. It explains and
orders; judging code stays with the existing Review action.

Reviews uncommitted work (all, staged, unstaged), a branch against its base, or
a pull request. Generation is always user-initiated — nothing runs on a timer,
on a file change, or as a side effect of opening a panel.

Invariants worth preserving:

- Hunk identity is derived on the server and only there. Ids are content
  hashes, so an anchor that no longer resolves is proof the code it described
  changed, and staleness needs no heuristics. The client matches ids to ids and
  never recomputes them; two implementations would have to agree forever.
- The digest is never truncated. A diff that does not fit the model's context
  is refused with an actionable reason, because a walkthrough written against
  half a diff reads as confident and is wrong.
- Nothing disappears. Lockfiles and other generated output are excluded from
  the model's input by name — never by size — and everything no stop covers is
  listed at the end, so "have I seen all of it" stays answerable.
- Cost is explicit. Results are content-addressed, so returning the working
  tree to an earlier state costs nothing; generation outlives its request, so a
  refresh detaches the client rather than discarding paid-for work, and only an
  explicit cancel stops it.

Supporting changes to shared modules:

- git: expose the existing getRangeDiff as GET /api/git
  listUntrackedPaths and getUntrackedDiffs. The latter resolve the repository
  once for a batch instead of per file, taking a panel
  ~340ms on an 80-file working tree.
- small-model: structured output across four wire forma
  and abort signal, and an onOverflow policy so an oversized prompt fails
  loudly instead of being silently clipped. A provider
  remembered so the prompt-side fallback goes first next time.
- models.dev metadata: surface structured_output as tri
  false blocks a model, a missing field does not, because the catalog omits it
  for roughly half of all models.

Desktop and tablet only: VS Code serves Git through its
these routes, and the mobile shell does not consume the surface registry.

Docs: packages/docs walkthrough page in English and all eight locales.
2026-08-02 16:22:55 +03:00