A drawing set is now read the way a person reads it. Layr finds the drawing index, makes its folders as printed, then reads each sheet's number, title and folder against it. One drag moves pages, folders and files in the outline and the page grid. Hold ⌥ to replace a page instead, and the old one stays, marked superseded. → now follows the sidebar after a sheet changes folder. Staff can see why an import failed. Opening a page beside no longer shrinks the main page.
A PDF uploaded under Drawings is read in two steps. First, one reader looks over the whole set and finds every drawing index, including multi-part, nested and ODOT range indexes. It records the folders as the index prints them. Then each page gets its own reader, which records the sheet number, the title and the folder. The title comes from the index row unless the title block disagrees on what the sheet is, like a different tag, pump number or subject. Spelling mistakes in ordinary words are fixed. A cover that prints no number takes the number its index row gives it. The stored sheet order is the sidebar's order from the first import. On the nine real sets we test against (1,824 pages), index pages, folders, sheet numbers and folder placement were all right, and titles were right 98.5% of the time. On layrbuild.dev, a 103-sheet set was read about three minutes after splitting, for $2.92. Specifications, front-end documents and addenda are read as before. (#881, #883)
Every page row and card, every folder and every file drags, in Drawings, Specs, Addenda and Other docs, in the outline and the page grid. A drop makes the same move as Move to…. A green line marks where it lands. A folded folder springs open after a short rest, and the list scrolls near its edges. Right-click or Esc cancels. Hold ⌥ and the drag replaces a page instead of inserting. The old page stays where it was, dimmed, left out of counts, exports and paging, and watermarked "SUPERSEDED by" the file the new page came from. New file… on the Specs, Addenda and Other docs headers makes an empty, named file, and a file emptied of pages disappears. ⌘Z is one stream: it takes back the newest change, an outline move or a markup. Moves show no toasts; moved pages land selected and flash once. (#878)
Every outline row opens a page menu or a folder menu. Move to… works on drawing folders, spec divisions and files, and New folder… is in the sheet menu. Remove from project works on files, drawing folders and imported sheets, always after a confirm; a file goes back to the unassigned files and is not deleted. The page grid is a third page view: size cards with the bottom-bar slider or Ctrl/⌘ + scroll, fold and drag folders to reorder them, double-click a card to open it. The bottom bar's path sits on the bar as one line, opens on the folder you are in, and the page-view switcher is pinned at its far left. With the left sidebar folded, the project tile is gone from the dock. (#876, #879)
Opening a page beside no longer shrinks the main page. The beside pane floats over the right side of the viewer, and the main page keeps its size, zoom and position. (#877)
People cards show Desktop app opens and Files opened from the Layr PDF desktop app. Someone with no desktop events reads —, not zero. Desktop use now counts toward last seen, so a desktop-only user no longer reads as Quiet. (#880)
122_move_document_leaves_import.sql makes move_document_to_project clear import_id and import_ordinal. layrbuild.dev applied it in deploy 40367744697 (#876, commit 696fe393).123_addendum_package_children.sql makes expand_document_package carry is_addendum to a package's children. layrbuild.dev applied it in deploy 40387297231 (#870, commit d5c1ea37).124_page_supersessions.sql adds page_supersessions: one row per replaced page, naming the page that replaced it. Undo deletes the row. It is not pages.superseded_at; a superseded page here is still listed and still opens. layrbuild.dev applied it in deploy 40535052825 (#878, commit ccbc54c0).deploy.yml, before the new code. Do not apply them by hand, and do not edit them. All three are on layrbuild.dev; none is on production yet.native_structure.RESOLVER_VERSION and its two TypeScript copies). Nothing runs on its own: an upgrade starts only from the project's Upgrade processing step. Each upgrade is paid and re-reads the drawings with the new readers. There is no answer cache, so a re-import or upgrade pays again.INGEST_MAX_DOCUMENT_USD (default $150); pages past the cap are left unread, never guessed. INGEST_CONCURRENCY (default 16) sets how many pages are read at once. Both are optional environment variables on Modal. The readers use OPEN_ROUTER_API_KEY, which production already has. Real cost goes to ingestion_costs.config/vision-models.json as ingest_folders and ingest_pages, both anthropic/claude-sonnet-5.5. Each read's steps and images are stored in Storage under drawings/ingest-runs/<documentId>/<runId>/, best effort.ingest_agent.py and ingest_publish.py are new; vision_folders.py and the plan-set halves of vision_publish.py and vision_read.py are gone. Production's Modal deploy runs with this merge, the same way dev's did.POST /api/projects/[id]/pages/supersede and POST /api/projects/[id]/documents/blank (write members). GET /api/projects/status returns a failed document's detail only to Layr staff and strips it for everyone else, failing closed.package.json's version is 0.39.0../layr ingest run, 13 cases with answers read off the sheets) and the eval workbench (./layr eval inspect). ./layr benchmark run now runs spec-book cases only.