Rework the "By existing" catalog into a "From a list" scratch worklist that can
be populated three ways and cleared without losing work.
Core model — retire the separate `mdl` assignment axis. Dropping a file on a row
now MATERIALIZES a real "By tracking number" placement (assignFromRow →
addTrackingPath + place(…,'tracking') + title override), reusing the typed-
filename path. Consequences:
- Clearing the list can't lose classifications (no assignment references a row);
dropped files show under By tracking number.
- The two name axes collapse into one (place/deriveTarget/fileCategory simplified).
- Workspace migration: load() materializes any legacy `mdlNodeId` into a tracking
placement BEFORE anything can prune, so saved workspaces keep their work.
- A tracking number's last segment is an ancestor and the revision is always the
leaf, so a blank-revision drop lands on a `pending` placeholder leaf; editing
the row's tracking number or revision re-stamps the row's files onto the new
leaf and prunes the emptied one.
UI:
- One editable Tracking number column (no per-field split) + editable Title — so
you can drop on an entry and bump e.g. the sequence for a new drawing.
- Source column (MDL / arch / pasted) with an amber "new"/"unverified" badge when
a pasted number matches nothing scanned — the typo catcher.
- Toolbar: Load… (now appends) · Paste rows… · ⚡ Match names · Clear list ·
Hide assigned. Tab renamed "From a list"; goal line + hint teach the scratch-pad.
- Paste dialog (Ctrl-V on the panel too) with a live parse preview; parsePastedRows
handles 3-col, a split status column, a pasted full filename, header + bad rows.
- Match names reviews proposeMatches (filename ⊇ a known tracking number) and
assigns the checked pairs.
Tests: materialize+clear, tracking-edit re-stamp + prune, legacy migration,
parsePastedRows, proposeMatches; 66 classify+classifier green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two UI fixes:
- "By existing" catalog columns were far too wide. The seltable forced the
table to width:100% (auto-layout then stretches columns) and — in the
classifier's copy — the per-column filter <input>s had no styling, so each
fell back to its ~170px intrinsic width and dictated the column width. Set
the table to width:auto (cells are already nowrap → fit header/longest cell)
and style .seltable__colfilter to fill its column (min-width:2rem,
box-sizing:border-box) so the inputs never widen a column. Applied to both
the classifier copy and shared/seltable.css (same fix for the tables tool's
"Add from archive" table).
- The left Folder Tree rendered folders and files in raw scan order. Sort both
at render — case-insensitive, natural (so "Rev 2" precedes "Rev 10") — via a
non-mutating slice().sort() at each render point in tree.js.
Tests: a new spec asserts the natural/case-insensitive tree order; 62 classify
+ classifier green (108 across classify/classifier/tables/tables-mdl).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reshape the catalog from a button+overlay into a proper tab and let the user
choose which directories feed it.
- Tabs: the Catalog button becomes a third tab "By existing", grouped with "By
tracking number" (both assign a tracking number) and visually separated from
"By transmittal" (which assigns the path). A brief goal line above the tabs
states the workflow. The overlay #mdlPanel becomes a normal in-flow tab panel.
- Load: instead of auto-scanning a whole project, "Load…" opens a lazy,
multi-select (checkbox) directory tree (new classifier/js/dir-picker.js).
Ticking a directory includes its whole subtree; confirm resolves the topmost
ticked handles. Scope follows where the classifier is served: /_apps/… → all
accessible projects, under <project>/… → that one project, file:// → a picked
folder. The picker is handle-agnostic (HttpDirectoryHandle or native FS).
- Rows: every ticked directory is walked recursively into the union of existing
files (zddc.parseFilename) and MDL deliverables (mdl/*.yaml → inMdl + title),
deduped to one row per tracking number. The "Archive revs" (all) column
becomes a single informational "Latest rev" computed via zddc.compareRevisions.
Drop still assigns the tracking number only; the Revision cell stays blank.
- classify.js is unchanged — the mdl axis model and row shape are reused as-is
(Latest rev derives from the preserved archiveRevisions).
- Tests: the catalog test now asserts latest-rev; new unit tests cover
walkDirInto union/dedupe-to-latest, _latestRevOf draft/modifier ordering,
_detectScope routing, and the dir-picker topmost-ticked resolution. 61 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reframes the By-MDL tab as a "⊞ Catalog" button that opens an overlay over the
target pane (the left filetree stays the drag source). The catalog is the
de-duplicated union of everything the project knows about a tracking number:
its MDL deliverables AND its archive files, merged by tracking number, with an
informational "Archive revs" column (which revisions already exist) and an "MDL"
flag. Nothing is written or altered — the Revision column is classifier-local
and starts blank (never pre-filled from an old archive rev).
- Drag a source file onto a row → assigns the tracking number only (the mdl
axis); set the revision in the bulk-editable Revision column (ctrl-shift
select rows + ctrl-Enter). Per-file Title: MDL/file toggle + ✕ remove kept.
- Columns split the tracking number into the configured pattern fields, each with
its own autofilter (per-column, via the shared seltable). Default pattern is
now the 8-field ORIG-PHASE-PROJECT-AREA-DISC-TYPE-SEQ-SUFFIX.
- Server load merges every party's archive/<party>/mdl/*.yaml with a recursive
walk of the archive documents; local load reads a folder of deliverable yamls.
Test: catalog shows merged archive revisions; drop names a file (tracking only);
bulk revision feeds the derived name.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>