Skip to content

feat(desktop): draft checks a build agent keeps: values, paired selectors, noise demoted, and checks for elements without attributes - #9091

Open
earakely-scale wants to merge 236 commits into
morluto:mainfrom
earakely-scale:feat/desktop-draft-values
Open

earakely-scale wants to merge 236 commits into
morluto:mainfrom
earakely-scale:feat/desktop-draft-values

Conversation

@earakely-scale

@earakely-scale earakely-scale commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Desktop task drafts now offer values, selectors, noise annotations and guards that help an author turn a captured change into useful checks.

The draft pairs related records, identifies changes and non-task noise, and provides bounded previews and checks for elements without attributes.

Stacked on #9089.

Maintainer follow-up and validation

XML and JSON draft previews now serialize only through the preview limit instead of escaping or joining entire large values first. A bounded synthetic XML case measured reduced work, but full-app latency, agent tokens and process-family memory remain unmeasured. The existing alias owner is backported so captured state uses the application's known path and explicitly selected aliases are restored at boot, with nested ordering, collision and idempotence coverage. XML-specific noise rules now consult the declared entry format; JSON attributes.history edits retain their checks. Regression cases failed before repair. File-URL and scratch-export parent repairs are included.

At head 40d510bbaf, the affected tests below completed with 149 passed (149); npm run check:fast and git diff --check passed. This includes the updated parent stack and main through 25d3c7237f. Checks use source/domain and controlled boundary fixtures; no real desktop provider qualification was performed. Current-head CI and review remain required before auto-merge.

node node_modules/vitest/vitest.mjs run src/domain/desktop/seedDocument.test.ts src/application/desktop/DesktopExportLaunch.test.ts src/domain/desktop/appTaskDraft.test.ts src/domain/desktop/appTaskDraftValues.test.ts src/domain/desktop/appExport.test.ts tests/boundary/providers/desktopExportPython.test.ts src/application/desktop/DesktopTaskDraftNotes.test.ts src/domain/desktop/stateRoles.test.ts

The export-service suite also passed all 40 tests on this head, including the selected-alias Evidence assertion (src/application/desktop/DesktopApplicationExport.test.ts).

Maintainer follow-up

Integrated the source-versus-seed path distinction and removed the always-empty whole-text guard representation. Existing sibling guards and scalar numeric checks remain covered. Additional checks passed: npm run docs:check.

At head d9b17a4810, the affected tests below completed with 250 passed (250); npm run check:fast, npm run format:check and git diff --check passed. This includes the updated parent stack and main through 25d3c7237f. Checks use source/domain and controlled boundary fixtures; no real desktop provider qualification was performed. Current-head CI and review remain required before auto-merge.

node node_modules/vitest/vitest.mjs run src/domain/desktop/seedDocument.test.ts src/application/desktop/DesktopExportLaunch.test.ts src/domain/desktop/appTaskDraft.test.ts src/domain/desktop/appTaskDraftValues.test.ts src/domain/desktop/appExport.test.ts tests/boundary/providers/desktopExportPython.test.ts src/application/desktop/DesktopApplicationExport.test.ts src/application/desktop/DesktopTaskDraftNotes.test.ts src/domain/desktop/stateRoles.test.ts src/domain/desktop/appTaskDraftAdded.test.ts src/domain/desktop/appTaskDraftFileNotes.test.ts src/domain/desktop/appTaskDraftGuards.test.ts src/domain/desktop/appTaskDraftNoise.test.ts src/domain/desktop/appTaskDraftPositional.test.ts src/domain/desktop/appTaskDraftTextAdded.test.ts

Author readiness

Human owner: @earakely-scale

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

earakely-scale and others added 30 commits November 5, 2026 20:13
…ld computer

- start_build_capture runs REA's capture inside the computer through a
  small controller over computer-exec, after a user-space apt-get update;
  mark_build_checkpoint returns once the capture confirms the checkpoint;
  stop_build_capture returns the draft between seed and end.
- export_build_environment exports with the requester's task inside the
  computer and fetches the bundle through data/get in bounded batches.
- check_build_task grades a computer's data/get with REA's evaluator and,
  through seed, after_edit, near_miss and after_reset stages, records a
  witness with the action log and the screen recording; act_on_build_computer
  gains reset.
- Capture progress lines carry window, checkpoint and refusal events; apps
  start in the home; a pack script builds the runtime archive; long tools
  send progress notifications.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…se the near-miss reset hole

- A capture whose start answer is lost is stopped at once and kept
  unreleased until the stop is confirmed; a lost checkpoint answer is
  recovered from the capture's own log, and draft and export use the
  checkpoints its Evidence records; stop can be repeated to resume
  reading its result.
- A witness can check its seed again after a failed read, and keeps its
  stages in a saving state until the record is written, so a failed save
  can be retried without reading the app again.
- after_edit and near_miss count only click, drag, scroll, type and key,
  so a reset no longer stands in for the near-miss edit.
- A .deb build keeps its package file for capture and passes its URL to
  export; bundle downloads write each checked file before fetching the
  next; an apt-get update failure reports its exit code and log tail;
  reset sends progress while it waits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd first frames drawn

From the blind Inkscape rebuild:
- Paths in ~/.local/share/Trash get a desktop_trash reason and the
  shared_desktop role, so a file trashed before seed is neither owned nor
  seeded; the export names it in a limitation.
- The keep helper runs with PYTHONDONTWRITEBYTECODE=1, so starting a
  computer from a bundle folder no longer writes __pycache__ into it.
- start_build_computer waits, bounded, until the first frame is not one
  colour before returning it.
From review: a .deb that apt also offers exports from apt, since export
passes --package-url only when the capture says the package must come
from its file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…with progress throughout

Each retry of the drawn-first-frame wait runs under an abort that fires at
the 10 s deadline, and the latest frame is returned when time runs out.
connect takes the first frame itself while its progress heartbeat still
runs, reporting "waiting for the desktop to draw". The wait moves to
DrawnScreenshot.ts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ng id, and separate unreadable checks from failures

- One queue per computer: actions, capture steps and witness checks run one
  at a time, so two overlapping after_edit checks can no longer both be filed
  and let the witness hold without a near miss. Close cancels the running and
  queued calls; different computers still run concurrently.
- The witness keeps one recording request id per computer, so a seed retried
  after a lost reply recovers the recording it started.
- A check that could not read its file is undecided, not a failure, so a
  temporary read error can't satisfy seed, near_miss or after_reset; such a
  stage is refused and can be checked again.
- The export's file listing is written to a file and read back in bounded
  batches, so a large bundle no longer overruns exec's 1 MiB output.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ided seed failure at export, and keep a close's cancellation

- A presence check that misses its line, row or pointed value in a view cut
  short is undecided rather than failed, since the data may sit in the part
  left out; a witness stage can no longer be proved by such a failure.
- Export counts only decided failures on the seed, and refuses a task whose
  every seed failure is undecided.
- A step whose turn was aborted by a close or the caller returns cancelled
  even when its work finished, so start_build_capture no longer reports
  success for a computer being removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…during its commit

Reporting cancelled for any step whose turn was aborted after its work
replaced a completed export or witness save with a bare cancellation, losing
what was saved and leaving the witness unreadable. Steps now return what they
completed; only start_build_capture, which commits nothing, checks its turn
after the post-launch screenshot and reports cancelled. Asking again for a
saved witness's final check returns its record.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Asking again for the check that ended a witness returned its saved record
whatever directory came with it, so a caller naming another folder got
success though nothing was saved there. A different folder is now refused,
as an unfinished seed's retry already is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ot their sentences

The new cut-view, missing-value, undecided-refusal and witness tests froze
whole descriptions, so a harmless rewording broke them. They now check each
outcome's id, passed and undecided exactly and the facts its message must
carry (file, pointer, line, row, stage) separately.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…whole text

- Both readers accept a DOCTYPE with only a name and an optional PUBLIC
  or SYSTEM identifier, record it as the view's doctype, and never fetch
  the DTD or expand entities beyond XML's five; an internal subset, an
  external entity and a parameter entity stay refused alike.
- text_equals and text_contains compare the concatenated text of the
  element (or text node or attribute) a pointer reaches, as XPath
  string() does, with optional normalize_whitespace; both evaluators
  agree, and a text check on a truncated element is undecided.
- Whitespace-only text between elements is dropped only when the element
  holds no other text, so text split into runs keeps its spaces.
- The draft suggests text_equals for an element whose text changed.

So an AbiWord .abw reads as XML, and its paragraph's text is checked
whole however AbiWord splits it into <c> runs, as Writer's spans are.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t as text, and skip asctime stamps

- A changed value is read at both ends, so text that became a run still
  marks its paragraph, and the drafted text_equals moves up out of mixed
  content to the nearest ancestor named by an attribute, such as
  AbiWord's p by its xid.
- Text checks accept only a text node or an attribute value; element
  names and doctype fields fail, in both evaluators.
- isTimestamp also recognises asctime dates (Www Mmm dd hh:mm:ss yyyy).
- XML files are drafted with the keyed diff the archive entries use, so
  no suggestion reaches an element by position; the shared logic moves to
  appTaskDraftNamed.ts.
- Text-check tests assert facts, not whole sentences.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hose text check replaces its runs'

A drafted whole-text check on a paragraph hid every change inside it, so a
link whose label and href both changed drafted only the paragraph's text and
the new destination went unchecked. The text check now covers text and runs
added or removed whole; a changed name or attribute of an element both
versions hold keeps its own check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…bute when the draft keeps its check

The owner of `.../attributes/name` was taken as `.../attributes`, so a
changed `name` attribute inside a paragraph with a text check was dropped
again. The owner is now read by segment, attributes first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…me a text element by its own id before its container

Text typed into a run the diff lists whole was never read, so the draft kept
only a presence check on the run, which wrong text also passed. Such text now
marks the nearest element both ends hold, or, when none can take the check,
the added element's own text. The climb also names an element by an
identifier of its own (id, xml:id, xid) before a larger container, so a
headline under a long layer keeps a whole-text check that survives re-split
runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ng test meets text checks

On this branch a changed element's text is drafted as `text_equals` on the
element, so the identity pairing test now expects that check, under the
same `[@ROLE=body]` key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both change the task checks: morluto#9045 makes a check that may have failed on
what a cut view left out undecided, and morluto#9050 adds checks of an element's
whole text. They combine as the build skill's earlier merge combined them:

- A text check whose pointer names nothing in a cut view is undecided, as
  a value's is: noValue takes whether the view is cut and answers through
  notFound.
- On a cut view, text_equals of an element is undecided in both evaluators;
  text_contains that finds its value in what the element's view kept
  passes, and one that does not find it is undecided.
- The fixtures and tests cover both, and the task, witness and spec docs
  say so.
- The spec list numbers build and witness 13 and XML text checks 14.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The product skill gains references/desktop-build.md, a procedure that
takes Claude from a request such as "an Inkscape environment where the
agent changes a poster's headline" to a witnessed bundle and a report,
through start_build_computer, act_on_build_computer, the build capture
tools, export_build_environment and check_build_task: questions asked
once upfront, pauses only when blocked, a seed made in the app, content
checks by selector, a witness with a near miss and a reset in a fresh
computer, and solver runs only within a budget. setup installs the new
reference; skillVersion 61.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ld skill

key takes an array of one chord's keys; check for a text cursor before
typing in apps with single-key shortcuts; in Save As, click Documents and
type only the file name; anything saved before seed, the Trash included,
is part of the seed; dismiss first-run dialogs before seed; a bundle's app
starts with the seed's files but need not open the document; Inkscape's
window class is Inkscape.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ld skill

Name the act fields and link the guide; a region screenshot becomes the
latest frame; a drawn caret is not focus; confirm dialogs with Return;
check the dialog's folder when the app shows only a name; an XML file the
draft shows as lines was read as text, so use short line checks; and keep
text checks to the value the edit types, since some apps save a reloaded
document differently. Later witness stages take no directory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…raft refusals exactly

The export refuses a task unless at least one check fails on the seed and
every check passes at the end; a file moved to the Trash is left out of
the seed, so trash a stray file rather than starting over; a null draft
names its cause in draft_refusal; and a line check on a format read as
text only approximates a value, so bound it and call it approximate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The skill now checks text an app splits into runs whole with text_equals or
text_contains, finds an AbiWord paragraph by its xid, and drops the Writer
revision-id workaround.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rub replaces

From the AbiWord rebuild: point a text check at the paragraph or another
element the edit keeps, never at a run a reopened document splits; how to
name an unchanged element; the export replaces the host name, user@host
and home path while a bare user name stays; AbiWord's entry and class.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…th evaluators

The XML reader drops text deeper than its depth limit but keeps later
siblings, and an element's whole text joins both sides, so a text_contains
on a cut element could match text the file never holds ("at 3" + deep
"WRONG" + ":30 PM" read as "at 3:30 PM"). Any text check on an element of a
cut view is now undecided again, as morluto#9050 had it, in REA's evaluator and the
exported verifier; a text node or attribute value stays decided. The text
check tests assert facts rather than sentences.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d was made for the task

An exported app now opens the seed's one document at launch when its desktop
entry's Exec takes files, so a solving agent starts in the document rather
than on the app's start screen; a command that already names a file is kept,
and several documents or an Exec without a file code keep the launch and say
why. `--synthetic-seed`, which the build export passes, words the README and
limitations for a seed made in the build computer instead of asking to keep
the user's data local.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…m its working directory, as already opened

A settings path under the home, such as geany's --config, kept the seed's
document from opening, and a relative document argument was missed and
appended again. An argument now counts only when it resolves, from the
captured working directory, to a file the capture gave the document role.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ters, and the facts on their own

The tests froze the README's and limitations' sentences, so a harmless
rewrite broke them. The wording now comes from exported formatters
(`seedWording`, `seedLimitation`, `launchDocumentNote`,
`NO_LAUNCH_DOCUMENT`), the tests compare against those, and check the facts
separately: which document opens, the respelled path in the command, the
README naming it, `--synthetic-seed` and the Evidence's `synthetic_seed`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dule

The merged export module passed the file and function size limits; the seed
digests and the launch command now come from `DesktopExportLaunch.ts`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ll and tools where the five-app batch stumbled

- start_build_capture: when the app exits before any window, the error
  leads with the program, its exit code or signal and how long it ran, and
  quotes a bounded tail of its stderr; the capture's record stays in
  structured details instead of a pasted output blob.
- Skill: apps that keep their own data and save on every change; Save As
  dialogs that open elsewhere, run off the screen or hide their button;
  confirming a save without an unsaved mark; finding the desktop entry and
  not trusting StartupWMClass; uncompressed formats; text kept in
  attributes; rewriting coordinate selectors; reading values when the
  draft shows none; Qt spin boxes.
- Tools and docs: start_build_capture opens the app, not the menu;
  consistent key names; concurrent starts can wait on agentenv's lock; and
  export refusals point at the draft on both the CLI and build paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…every capture step's failure the same way

- A session keeps the last 8 KiB of stderr rather than the first, so an app
  that writes a long log before failing still shows its final error.
- Command output read back from the computer goes through the same exact
  control-token redaction as exec output before it reaches an error.
- Start, checkpoint and stop failures share one reader, so a checkpoint that
  fails (disk full) keeps its cause.
- A wrapped failure keeps the original under diagnostics, with its cleanup
  resources, so cleanup advice names what to stop or remove.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…raft is empty, and list the folders a bundle leaves unowned

- start_build_capture takes state_paths: files or folders under the home
  that are the app's state, passed to the capture as --state-path, so a
  document the window title never names (MuseScore's score) becomes the
  bundle's document. Paths outside the home or in the controller's folder
  are refused before anything launches.
- A draft with no files carries notes: no file changed, files changed but
  none owned (listed with their roles, pointing at state_paths), or no
  document.
- Export lists, in its limitations and README, the folders the app wrote
  that the bundle neither owns nor seeds (first-run wizards on every boot
  come from these), and names seeded lock files and browser-engine caches.
- A selected path under a hidden folder is app state, not a document, so
  naming a settings folder keeps the launch opening one document.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread src/domain/desktop/appExportPython.ts Resolved
Comment thread src/domain/desktop/seedDocument.ts Resolved
Comment thread src/domain/desktop/seedDocument.ts Resolved
# Conflicts:
#	src/domain/desktop/seedDocument.ts
# Conflicts:
#	.agents/skills/reverse-engineer-anything/references/desktop-build.md
#	docs/desktop-build-witness.md
#	docs/specs/desktop-app-environments/build-witness/verification.md
#	src/application/desktop/DesktopApplicationExport.test.ts
#	src/application/desktop/DesktopApplicationExport.ts
#	src/application/desktop/DesktopBuildCapture.ts
#	src/application/desktop/DesktopExportLaunch.ts
#	src/domain/desktop/appExport.test.ts
#	src/domain/desktop/appExport.ts
#	src/domain/desktop/seedDocument.test.ts
#	src/domain/desktop/seedDocument.ts
#	tests/boundary/mcp/buildCaptureMcp.test.ts
#	tests/fixtures/fakeBuildComputerRea.ts
Comment thread src/domain/desktop/appTaskDraftNamed.ts Resolved
Comment thread src/domain/desktop/appTaskDraftNoise.ts Resolved

This branch has not been deployed

No deployments
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