111 Commits

Author SHA1 Message Date
phatloi0010 ba37b0cd80 SquashFS extended regular file inodes read their 64-bit layout (#384)
Extended SquashFS file inodes are read with their 64-bit layout, so small extended files are no longer extracted empty. Fixes #352.
2026-11-02 22:35:41 +07:00
phatloi0010 bcc08c0ada Truncated sBPF .text sections decode readable bytes and report partial coverage (#381)
A .text section that runs past the end of the file is decoded as far as the bytes go, with a text_truncated limitation and partial coverage. Fixes #306.
2026-11-02 22:35:15 +07:00
GGWA 489f2976b4 SQLite rowid alias columns show the rowid instead of NULL (#409)
db_sqlite_show shows an INTEGER PRIMARY KEY column's value from the rowid instead of NULL. Fixes #405.
2026-11-02 22:29:10 +07:00
BFlat 5185f36ab0 Import maps, speculation rules and data blocks aren't read as JavaScript (#353)
The HTML scanner skipped only `<script>` bodies whose type mentioned "json" or "template". Import maps (`importmap`), speculation rules (`speculationrules`) and data blocks such as `text/html` became JavaScript modules in app maps: an import map was reported as a broken script, and an inert HTML template could add made-up fetch endpoints.

A `<script>` now counts as a script only when the page runs it as code: no type, `module`, or a JavaScript MIME type (parameters such as `; charset=…` are ignored), plus `text/babel` and `text/jsx`, which in-browser Babel runs. Other types are left out, both inline and with `src`.

**Verified:** a new scanner test. Five non-JavaScript types (inline and with `src`) give no scripts; six JavaScript spellings, including a mixed-case type and one with a charset, give one each. `cargo test -p craft-anything-js` (41) and `-p craft-anything-analysis` (85), clippy and fmt pass.

**Not run:** `cargo xtask wasm`. The change is pure string logic in an existing function.

Fixes #285
2026-11-02 15:42:22 +07:00
BFlat 9bec65baf2 The change summary counts a comparison where files only moved as changed (#345)
`app_changes_summarize` decided whether each comparison found changes. For records with `counts` it added added + removed + changed, which left out moves. So an archive or folder comparison whose only difference was a moved file (reported `identical: false`, `moved: 1`) counted as unchanged ("0 of 1 comparisons found changes").

A record's own `identical` verdict now decides when it has one, and moves count otherwise. Archive `counts.changed` keeps its meaning (content-edited files).

**Verified:** a new test of the verdict: a move-only comparison is changed (with and without `identical`), and an identical one is not. The engine's compare tests, clippy and fmt pass.

Fixes #286
2026-11-02 15:38:16 +07:00
BFlat eaaa8d3f8f Comparing folders or archives moves each new file at most once (#343)
`core_archives_compare` matched moves by content through a map from hash to one new path. Two identical old files missing on the new side both "moved" to that one path, so a removal went unreported. With two renamed copies, one copy was reused and the other was reported as added.

Each new-only file is now a move destination at most once. Old files are paired with distinct new copies of the same content, and the rest are removed or added. Every file on each side is accounted for once.

**Verified:** a new test with the reporter's fixtures. Two old copies and one new copy give 1 move and 1 removal; two and two give 2 moves to different paths, with no additions or removals. The existing comparison test still passes. `cargo test -p craft-anything-analysis` (86), the engine's artifact tests, clippy and fmt pass.

Fixes #283
2026-11-02 15:33:05 +07:00
BFlat cc2e7b7eee Comparing two runs tells a deleted file from a created one (#340)
`runtime_captures_compare` compared the filesystem dimension by the set of changed paths only. A run that deleted a watched file and a run that created the same file both "changed" that path, so they matched, and the comparison said the two runs were the same.

Each path is now compared together with how it changed (created, deleted or modified). Two deletions of the same file still match, and different paths still differ.

**Verified:** a new test of the filesystem comparison covering deleted vs created (changed), deleted vs deleted (unchanged), different paths (changed), and an `unchanged` effect against none (unchanged). The engine's process-tool tests, clippy and fmt pass.

Fixes #300
2026-11-02 15:29:06 +07:00
BFlat 5ea84a8f4c Cached prompt tokens from OpenAI-style APIs are counted once (#337)
OpenAI-style APIs report `prompt_tokens` including the cached part (`prompt_tokens_details.cached_tokens`). We stored the whole `prompt_tokens` as `input_tokens` and the cached part again as `cache_read_tokens`. The context budget and the cost meter add the two, so cached tokens counted twice: a 100,000-token prompt with 65,536 cached read as 165,536, crossing the 150k budget and summarising early, and it was charged both rates.

`input_tokens` is now `prompt_tokens` minus the cached tokens, as Anthropic reports it, so every consumer counts each token once.

**Verified:** a new stream test with the reporter's numbers (100,000 prompt, 65,536 cached, 10 output): input 34,464, cache reads 65,536. `cargo test -p craft-anything-ai` (186), clippy and fmt pass.

Fixes #252
2026-11-02 15:19:45 +07:00
BFlat 4ab820fe7f Enabling a toolset in Clean room counts only the tools it lists (#324)
`enable_toolset` reports `visible_tools`, the number of tools now on the connection's list. It counted every tool in the enabled toolsets, but in Clean room `tools/list` leaves out the tools Clean room refuses. So enabling all toolsets said 299 while 86 were listed.

The count and the list now use one check (`listed`): a tool's toolset is shown and, in Clean room, Clean room runs it. Deep mode counts are unchanged, and the two meta-tools are still left out of the count.

**Verified:** a new MCP test in a Clean room session enables all toolsets and checks the count against the tools Clean room runs. It fails without the fix (299 against 86). `cargo test -p craft-anything-mcp` (16), clippy and fmt pass.

Fixes #263
2026-11-02 15:07:10 +07:00
Oliver Mee 8b5826f9bd Selected macOS app folders and nested XPC services are recognised (#297)
Selecting a macOS app folder is recognised as an app bundle (its root-relative Contents/Info.plist), and nested XPC services appear as services and xpc-service graph nodes.

Fixes #282
2026-11-02 15:04:00 +07:00
BFlat 460e2a3c3d The approval check allows npx vite build and still refuses dev servers (#328)
The builder's approval check refused dev servers before looking at the safe build list. `npx vite build` is on that list, but it also starts with `npx vite` (a dev server), so it was refused as one, whatever the mode or Auto setting.

A listed build is no longer read as a dev server: `npx vite build` and `cd app && npx vite build` are allowed, while `npx vite` and `npx vite --host …` are still refused. A command in watch mode (`--watch`) is now refused with the same hint, since it never exits either. That includes a listed build such as `npx vite build --watch`. The safe-list match moved into one helper used by both checks.

**Verified:** new cases in the approval tests (three spellings of the build allowed; two server forms and a watch build refused). The engine's approval tests, clippy and fmt pass.

Fixes #291
2026-11-02 15:02:34 +07:00
BFlat 5b138b88a4 An ACP chat starts the agent's session again after a failed start, and Stop ends a waiting message (#320)
When an ACP agent answered `initialize` with an error (for example, not signed in) and kept running, the error was shown and the turn ended. But the agent's session was never started, and nothing started it later. The next message was queued for a session that never came, so the chat stayed busy. Stop cleared the queue without ending the turn, so every later message was refused as "still working".

- A message sent when the start has failed and nothing is under way now starts the handshake again (`initialize`, then the session). Another failure is reported and ends that message's turn like the first.
- Stop now ends the turn of a message still waiting for the session.

**Verified:** a new test of the protocol core: after a failed `initialize`, the next message sends `initialize` again, a second failure ends its turn, and Stop ends a waiting message. A late answer then sends no prompt. `cargo test -p craft-anything-ai` (184), clippy and fmt pass.

**Not verified:** a live run against an agent that refuses `initialize`.

Fixes #271
2026-11-02 14:53:22 +07:00
BFlat 1dd342f482 A large approved action keeps its allow decision over MCP (#314)
`core_action_approve` answers a coding agent's permission prompt with `{"behavior":"allow","updatedInput":…}`, echoing the action's input. For a large action, such as a ~96 KiB `Write` under Claude Code's 80 KB response budget, the MCP server replaced that answer with an `over_limit` error, so the decision was lost.

Approval answers are now exempt from the response budget. They are protocol messages, not result data, and their size comes from the input the client itself sent. Every other result is limited as before.

**Verified:** a new MCP test with a 300-byte budget and an allowed `Read` carrying a 4 KiB field: the answer is a single text block with `isError: false` and the full allow decision. `cargo test -p craft-anything-mcp` (16), clippy and fmt pass.

**Not verified:** whether Claude Code applies its own output-token limit to permission-prompt answers. If it does, a very large action still can't be approved through it, but the decision is no longer replaced on our side.

Fixes #253
2026-11-02 14:53:01 +07:00
BFlat 09fdc72e8d Saves from two threads at once never collide or mix a project file (#311)
The engine had its own `write_atomic` (used for `craft.json`, the inbox, reports and session files). Its temp file was named by process only, so two threads saving the same file at once wrote into one temp file. One rename then found it gone ("craft.json: No such file or directory"), and the file could end up with two versions mixed ("trailing characters").

The engine now uses the platform crate's `write_atomic`, which already gave each write its own temp file and kept the file's permissions. It also gains the engine's short retry when Windows briefly can't replace a file another program holds open. There is one atomic writer now; docs/pitfalls.md says so.

**Verified:**
- A new engine test: four threads each save one file 50 times and read it back. Every save succeeds, every read is one whole version, and no temp files are left. It fails against the old engine writer (checked) and passes now.
- The existing "file held open for a moment" test still passes.
- `cargo test -p craft-anything-engine` (397), the platform fs tests, clippy on both crates and fmt.

**Not covered:** two saves that overlap can still each start from what was on disk, so the later one wins for fields that aren't the person's (the person's fields are kept, as before). That's separate from the write itself.

Fixes #273
2026-11-02 14:45:46 +07:00
Oliver Mee 2a9bfe628e Objective-C chained offsets past PAGEZERO resolve to the image (#298)
Objective-C pointers whose chained offset lands in file-empty __PAGEZERO now reach the image-base fallback, so the classes are found again.

Fixes #296
2026-11-02 14:44:02 +07:00
BFlat 10fb3cfc8d Changing AI settings never saves over ones that couldn't be read (#295)
`AiHub` read the `[ai]` settings with a fallback: when they couldn't be parsed (for example, a price override missing `output_per_mtok`), they counted as empty. Adding a backend, choosing a role, renaming a model, turning a backend on or off or removing one then saved that empty configuration plus the one change over the person's backends, roles, fallbacks and prices.

Every change now reads the `[ai]` section strictly (`config_to_change`). If it can't be read, the change stops with the existing BadSettings error ("The [ai] settings couldn't be read: … Fix or remove the [ai] section in config.toml") and nothing is saved. Adding a backend reads the settings before storing its key, so a refused add stores no key either. Displaying the configuration still falls back as before.

**Verified:** a new test with two backends, a chosen builder and a broken price override. Adding a backend, clearing a role, turning one off and removing one all fail with BadSettings; the settings file stays byte for byte the same, and the refused add stored no key. `cargo test -p craft-anything-ai` (184), clippy and fmt pass.

Fixes #261
2026-11-02 14:20:30 +07:00
BFlat e6e48e131d Saving a key never replaces a key file it couldn't read (#293)
The fallback key file (`ai-keys.toml`, used when there's no keychain) was read leniently: a file that couldn't be read or parsed counted as empty. The next save then wrote back only the new key, so a stray quote or a bad byte cost every other key in it, while the save reported success.

Saving and deleting a key now read the file strictly. A missing file is still empty. A file that can't be read, isn't UTF-8 or isn't valid TOML stops the save with "Couldn't read the key file …: <why>" and "Fix or move that file (it was left untouched)". Reading a key (`get`) is lenient as before. After a keychain save, the stale-copy cleanup skips a file it can't read instead of replacing it.

**Verified:** a new test with the reporter's two cases (an unclosed quote, and a trailing invalid byte): the set and the delete both fail and the file stays byte for byte the same; a valid file keeps its other key after a save. `cargo test -p craft-anything-ai keys`, clippy and fmt pass.

Fixes #262
2026-11-02 14:14:39 +07:00
BFlat d1d36cd110 android_package_show's summary says signatures are verified (#290)
`android_package_show`'s summary, shown in CLI help and the MCP tool list, said "signatures are not verified". The tool does verify them: v2/v3 signatures over the signed data with a recomputed content digest, and v1 PKCS#7 with the per-file digests. Its own `verification_scope` limitation says so. The summary now says signatures and content digests are verified, but not whether the certificate is one you trust.

**Verified:** text only. `cargo xtask tools` leaves docs/tools.md unchanged, and fmt passes.

Fixes #247
2026-11-02 14:09:26 +07:00
BFlat 4ae6ce70d2 Comparing app maps notices an edited inline script (#269)
An inline `<script>` becomes a module of its own (`index.html#inline-0`). Its summary took its hash from the input file with the same path, and there is no such file, so the hash was always empty. Comparing two maps then read "both empty" as "the same", and an edited inline script counted as unchanged.

- **Inline scripts now get the hash of their own code.**
- **Maps saved before this have no hash for them.** Where either side lacks a hash, the comparison now checks the module's structure fingerprint, size and strings instead.

**Verified:** a new test with a page whose inline script changes from `console.log("A")` to `("B")`:
- the inline module and the page are reported changed, and a map compared with itself has two unchanged modules;
- the same holds for maps with the inline hashes blanked, as saved before this fix.

`cargo test -p craft-anything-analysis` (80), the engine's app tests (31), clippy and fmt pass.

Fixes #236
2026-11-02 14:02:23 +07:00
BFlat 8071522b3d Setup creates Pi's mcp.json when it doesn't exist yet (#268)
Pi rejects an empty `mcp.json`, so setup marks Pi as "empty is invalid". The check also ran when the file didn't exist yet: its absent text read as empty, the plan was blocked ("the file is empty, and this app requires valid JSON"), and `setup --all-detected --yes` stopped with it.

A missing file now starts from `{}` and is created with our `mcpServers` entry. An existing empty file is still refused. The preview of a new file shows only the added lines.

**Verified:** a new setup test where Pi's folder exists without `mcp.json`: the plan isn't blocked, apply writes a valid file with our entry, and an empty file is still blocked. `cargo test -p craft-anything-setup` (24), clippy, fmt.

Fixes #238
2026-11-02 14:00:39 +07:00
BFlat 9a829f7c31 A failed adb download or capture doesn't leave a folder that blocks the retry (#266)
`adb_screenshot_take`, `adb_bugreport_save`, `adb_file_download` and `adb_app_download` create their fresh `output_directory` first, then check adb and the device. A device that was offline (or a missing adb, or a failed pull) left that folder behind, empty. The retry the error recommends then failed with "already exists", without ever reaching adb.

These tools now hold the folder in a small guard (`UnlessWritten`, next to `create_absent_dir`). When the tool stops, the guard removes the folder only if nothing was written into it, so partial pulls stay. On success the tool keeps the folder.

**Verified:**
- A new test: the guard removes an empty folder, keeps one with a file in it and keeps one marked kept.
- The same test calls all four tools through the engine with adb missing: no folder is left, and the identical retry reaches the same adb error rather than "already exists". That half runs only where adb isn't installed, like the other adb tests; it was skipped here because adb is on this machine.
- `cargo test -p craft-anything-engine adb`, clippy, fmt.

**Not changed:** the website, web, firmware and packed tools create output folders the same way. They can take the guard if needed.

Fixes #234
2026-11-02 13:54:31 +07:00
BFlat c89f6603dd adb_app_show says a missing package isn't installed (#258)
`adb_app_show` treated `dumpsys package <name>` output as describing the package if it merely contained the name. Android's answer for a missing package, `Unable to find package: <name>`, contains the name, so the tool returned successful evidence with no fields and complete coverage.

It now requires that package's own `Package [<name>]` block and otherwise returns the existing `NotFound` error with the "List installed packages with adb_apps_list." remediation.

**Verified:** a unit test with Android's missing-package reply, another package's block and an installed package; `cargo test -p craft-anything-engine adb`, clippy, fmt.

**Not verified:** a run against a real device.

Fixes #235
2026-11-02 13:43:22 +07:00
BFlat 27d93b9ae3 A capture's timeout stops every process in the group, not just the leader (#246)
On a timeout with a grace period, a capture sent SIGTERM to the group, then waited only for the leader. When the leader exited, it skipped the SIGKILL, so a helper in the group that ignores SIGTERM kept running after the capture returned (reported as `still_running` / `incomplete`).

Now the grace period waits for the whole group to be gone, reaping the leader as it goes so a zombie doesn't count. Whatever is left afterwards gets SIGKILL. `settle` uses the same group check.

**Verified:**
- A new Linux/macOS test with the reporter's helper (ignores TERM and HUP, outlives the leader): the capture must settle, verify cleanup and report SIGKILL.
- `cargo clippy -p craft-anything-platform --all-targets --target x86_64-unknown-linux-gnu -D warnings`, fmt.

**Not verified locally:** this machine is Windows without WSL, so the new test first runs in CI (Ubuntu). macOS isn't run; it uses the same code.

Fixes #230
2026-11-02 13:39:04 +07:00
BFlat 55816983e8 First-run steps scroll when the window is too short, so Continue stays reachable (#243)
At the smallest window the app allows (760×520), the first-run "What brings you here?" step was taller than the window and had nothing to scroll, so its Continue button sat below the window, out of reach.

The first-run steps now sit in a scroll area that stops above the footer (Back and the step counter). When a step fits, nothing changes; when it doesn't, it scrolls.

**Verified:**
- A new smoke test at 760×520 scrolls Continue into view above the footer and clicks it through to the last step. Without the fix, Continue is drawn at y=646 in a 520-high window and the test fails.
- `cargo test -p craft-anything-ui-egui` (all suites), clippy, fmt.
- Rendered the first-run screens at the default size (unchanged, no scroll bar) and at 760×520 (scrolls, ends above the footer).

Fixes #231
2026-11-02 13:32:55 +07:00
GGWA 35d76945ca ELF files with no section headers show their dynamic symbols and functions (#233)
ELF files whose section headers were removed (common in NAS and router firmware) now show their functions, imports and exports. The dynamic symbol table is found through PT_DYNAMIC (DT_SYMTAB/DT_STRTAB, counted from DT_HASH, then DT_GNU_HASH, then an estimate with a warning), shared with the Flutter reader. The prologue scan walks executable segments when there are no sections.

Fixes #223
2026-11-02 13:26:37 +07:00
BFlat 1207715afd Pseudo-Java keeps the statements before a try (#242)
The pseudo-Java for a method with one try/catch rendered the protected region, the handler and what follows, but never the statements before the `try`. So `v0 = 7; try { v1 = 2; } catch (RuntimeException e) {} return v0;` came out without `v0 = 7;`.

- Those statements are now rendered first. A branch from them into the try region falls back to a labelled `goto`, as elsewhere, so the method is no longer marked fully structured.
- The single-handler wrap is now refused when anything sits between the try's closing `goto` and the handler. That code would have been dropped the same way; such methods now get the plain rendering with the "exception handler(s) in the table" note.

**Verified:** a test with the reporter's 152-byte class (it fails without the fix); `cargo test -p craft-anything-analysis` (79), the engine's Java tool tests (9), clippy, fmt.

**Not checked:** `cargo xtask wasm`. The change is pure logic inside an existing function.

Fixes #232
2026-11-02 13:26:28 +07:00
BFlat 07fe869dc4 A same-size rewrite of an unhashed file reads as unknown, not unchanged (#241)
A runtime capture compared a file's size, type and hash. When the file was over the hashing budget (or couldn't be read), neither snapshot had a hash, so a rewrite at the same size was reported as `unchanged`. Now:

- such a file is `unknown`;
- a file is `modified` only when its size or type changed, or both hashes exist and differ;
- a symlink pointed somewhere else with a target of the same length is `modified` (only the target's length was compared before).

**Verified:** a new unit test of `effects` (unhashed on either side, equal and different hashes, a size change, symlink retargets); `cargo test -p craft-anything-platform` (47 passed), clippy, fmt. The test runs on every OS, unlike the shell-based capture tests.

**Not verified:** a live run of the issue's capture probe on macOS.

Fixes #229
2026-11-02 13:24:22 +07:00
vietanh210304 233556ed1d Hermes Agent builds and chats through its own adapter, and over ACP (#16)
Hermes Agent gets its own adapter: it runs as `hermes [-m <model>] [args] -z <prompt>`, never falls back to a plain `hermes <prompt>` (which opens its interactive session), and can run over ACP (`hermes acp`) when ACP is turned on.
2026-11-02 13:17:02 +07:00
muraa-p b36007cf8b A plain HTML and CSS request builds a static site instead of a Rust app (#225)
A request for a plain HTML and CSS site ("no frameworks", "no build step", "static html") now picks a Plain HTML and CSS stack: index.html and a stylesheet in app/, with nothing to build. Before, the request named no stack and the builder chose Rust + egui.

Fixes #161
2026-11-02 13:16:51 +07:00
Brandon Thomas da83b81be2 A build stops with a plain message when craft-anything-cli is missing (#226)
A local build by Claude Code, Codex or an ACP agent now checks that craft-anything-cli starts before studying or starting the agent. When it is missing or won't start, the build stops with a plain message and what to do, instead of Claude Code's "MCP tool not found" error. A check that times out lets the build go ahead, and stopping the build ends the check.

Refs #91, #64, #113.
2026-11-02 13:16:39 +07:00
BFlat 4419c674cf "It's done" keeps a build as it is and stops offering to carry on (#221)
A paused, stopped or failed build only offered Continue or Try again; there was no way to say "enough now". project_build_finish marks the project Ready and adds a Done step to its conversation, keeping everything; the project screen shows It's done beside Continue and Try again when nothing is running.

Fixes #210
2026-11-02 11:51:58 +07:00
BFlat fe6a8ad98c From the CLI, a capture with no open site says each command starts its own browser (#213)
web_site_open keeps its browser for one session, and each CLI command is a session of its own that closes its browser when it ends. From the CLI, a capture with no open site now says so, and to run the steps together in an MCP client, the app or rebuild. Documented in tool-status.

Fixes #153
2026-11-02 11:31:28 +07:00
GGWA fff225d319 The Windows pipe gap in project_play is noted in its tool help and kept as an ignored test (#212)
project_play's help says that on Windows a started program inherits the caller's output pipe (#207), and the reproducing test is kept in platform as an ignored test.
2026-11-02 11:28:57 +07:00
BFlat 4756c15642 Captured colours are the page's CSS values, not a canvas read that drifted by one (#211)
Every computed colour was painted on a 1x1 canvas and read back; Brave adds noise to canvas reads, so colours came back one unit off per channel (#F5F2EC as #f4f2ed). Computed rgb()/rgba() values are now read as numbers, and only other notations go through the canvas.

Fixes #154
2026-11-02 11:23:32 +07:00
NetterSilver 32b80be890 A 10-byte LEB128 value whose bits don't fit is rejected instead of truncated (#208)
checked_shl only rejects shift amounts of 64 or more, so payload bits shifted out of the u64 on the tenth byte were dropped; uleb128 now returns None when bits would be lost, and u64::MAX still decodes.
2026-11-02 10:40:37 +07:00
GGWA 648fbd7bde A Mach-O method list at the end of the address space is read without overflowing (#205)
Each method entry's address and its fields' addresses are computed with checked arithmetic, so a method list near the end of the address space ends the list instead of panicking (debug) or reading a wrapped address (release).
2026-11-02 10:09:52 +07:00
GGWA fa7f1a114e An A32 doubleword load or store into r15 decodes as invalid instead of stopping the ARM analysis (#204)
An A32 LDRD/STRD with Rt=15 (e.g. 0xc0c7fef3) made the decoder library panic on the nonexistent r16; such a word now decodes as no instruction, before the library sees it.
2026-11-02 10:07:46 +07:00
BFlat 145d65c96c Codex is never run as plain text, which opened its interactive mode (#200)
When the check of codex exec --help didn't find --json, the run fell back to the plain adapter, codex <prompt>, which is Codex's interactive TUI and fails with "stdin is not a terminal". An adapter can now say its plain form doesn't work; Codex keeps its own adapter and the run says the check couldn't confirm the flag.

Fixes #199
2026-11-02 08:57:54 +07:00
one-bit 1dcb46b2f8 Unreal Engine installations and their bundled Clang are found on Linux (#191)
Reads Linux registrations from ~/.config/Epic/UnrealEngine/Install.ini, honours a valid UE_ROOT, takes the version from Build.version, and counts the selected engine's own bundled Clang as its C++ tools.
2026-11-02 07:11:30 +07:00
GGWA 4448b24065 A pill corner made with calc(infinity * 1px) is captured as 9999px (#189)
Chrome reports Tailwind v4's rounded-full as its largest length, 3.35544e+07px; the capture now records any corner above 9999px as 9999px and keeps the other corners.

Fixes #155
2026-11-02 07:08:41 +07:00
Firefly e0f97a2626 The fallback-order test covers a font it doesn't recognise (#187)
Adds an unknown family to the fallback-order test, so FallbackRank::Other is checked and sits between sans and serif faces.
2026-11-02 06:44:38 +07:00
BFlat 2022642554 Setup and the doctor read a client's other names from one table (#184)
agy as another name for antigravity was written out by hand in both; Client::is now matches an id with dashes or underscores, or an alias.
2026-11-02 06:10:56 +07:00
BFlat 34d0d654ba Clean room captures leave out class names and query strings (#183)
A visitor's view of a site no longer keeps the page's class names (outline items and unnamed-control examples) or the query strings and fragments on image and link addresses, which can carry deployment ids and tokens.

Fixes #156
2026-11-02 06:08:01 +07:00
X-EON d0c341739b Antigravity (agy) works as an agent program, in setup and the doctor (#89)
Adds an Antigravity adapter (stream-json output, resume by conversation, builder permissions) and lets setup and the doctor connect and check it under `antigravity` or `agy`.
2026-11-02 05:59:23 +07:00
BFlat 7776b97d2b A question answered outside a build stays in the project's conversation (#180)
Callers take the question before answering, and record_answer adds it with its answer when the saved conversation doesn't have it, once. Fixes #176.
2026-11-02 05:35:28 +07:00
BFlat 706f250c1c A failed build check says where it looked, not app/ for an app edited in place (#178)
The build check's messages name where the app is (Workspace::app_place): app/, or the project folder for an app edited in place. Fixes #174.
2026-11-02 05:26:27 +07:00
BFlat 3a60c04c27 Browser evidence records Clean room when the run is in Clean room (#177)
Results read through a browser record the run's mode in environment.clean_room instead of always false. Fixes #157.
2026-11-02 05:23:44 +07:00
BFlat e4a4cf1f4a A website capture refuses the browser's error page (#171)
web_site_capture checks where the page is first and refuses, without making the output folder, when the browser is on its own error page or a blank one after a failed open. Fixes #158.
2026-11-02 04:54:03 +07:00
BFlat b9905c63a4 An out-of-date Claude Code says so, and how to update it (#169)
Claude Code's "does not support this model" error says Claude Code is too old, with the fix (claude update for the command-line tool, or another model in Settings → AI), and isn't retried. Fixes #165.
2026-11-02 04:41:07 +07:00
BFlat c94483d6a1 A full-page website capture scrolls through the page first (#168)
Before each width's full-page screenshot the page is scrolled through, lazy images are loaded and waited on (5 s at most), and it goes back to the top, so scroll-revealed sections and lazy images are in the picture. Fixes #160.
2026-11-02 04:37:41 +07:00