Skip to content

Read a project's labels, run order and collection order as MATLAB writes them - #59

Merged
ww-mw merged 1 commit into
mainfrom
project-parity-corpus
Oct 2, 2026
Merged

ww-mw merged 1 commit into
mainfrom
project-parity-corpus

Conversation

@ww-mw

@ww-mw ww-mw commented Oct 2, 2026

Copy link
Copy Markdown
Member

A generated corpus of real project stores, and the three parser defects it found.

Why

Every store in the project suite was hand-typed from reading one real project —
the one thing test/parity/matlab/README.md says never to do. Over a thousand
lines of them agreed with the parser on three things that are false, and the
first real store disagreed on all three.

The three fixes

what the bug a user saw
ReadOnly has a vocabulary per element: READ_ONLY/WRITABLE on a label <Info>, 1/0 on a category's. The old rule tested the attribute's presence. A label the user added read back as one of MATLAB's own, so the project page drew it as a built-in instead of a custom chip.
"First in the chain" is spelled two ways — an explicit Value="HEAD", or no Extension at all on the first entry, which is what a freshly built project writes. The walk started only at the literal HEAD. Startup files listed in reverse run order for the common store shape.
The label catalog and the working folders came back in store order, which differs per layout. The same project read two ways, and the project page rendered working folders in whatever order the file held.

entryPoints is deliberately left unsorted: its order is the run order.

The corpus

test/parity/matlab/gen_project.m builds one small project carrying one of
everything the seven collection readers look for, converts it with
matlab.project.convertDefinitionFiles into all four DefinitionFiles formats,
and records what MATLAB itself says each one holds.

format MetadataType store docs .prj
SingleFile monolithic 1 yes
FixedPathMultiFile fixedPathV2 66 yes
MultiFile distributed 30 yes
Toml — 0 no

230 files, 38 KB, every XML format an openable .prj — small enough to commit,
where the 108 MB project that found the first defect is not.

Ownership is recorded by refusal: no MATLAB property reports it, so the
generator tries to remove each definition on a throwaway copy and writes down
the error identifier (MATLAB:project:management:CanNotModifyReadOnlyLabel).
That makes the expectation MATLAB's answer rather than our reading of the same
bytes that were misread in the first place.

What the suite asserts

The three kinds of claim the .slx layout corpus makes — the layout on disk is
real, each format against MATLAB's truth, and the three XML layouts deeply
equal
to one another. That last one is licensed by MATLAB: it is asserted only
because those three conversions reported no warning. Toml warns
MATLAB:Project:Issues:LabelDataLoss and is compared against its own record.

The hand-typed stores stay. What they cover is damage — a document that is not
XML, a File entity that is its own child, a chain that is a cycle — which a
real conversion cannot be made to produce.

drift.mjs regenerates the project corpus too, compared per format and per
section. project_truth.json holds no uuid and no timestamp, and two runs of one
release came out byte-identical, so every diff there is a real change of answer.

Verification

npm run verify green: 5195 tests passing, 174 files.

…tes them

The project suite's stores were all hand-typed from reading one real project,
which is the one thing test/parity/matlab/README.md says never to do. They
agreed with the parser on three things that are false. A generated corpus of
real stores disagrees on all three:

- ReadOnly has a vocabulary per element: READ_ONLY/WRITABLE on a label <Info>,
  1/0 on a category's. The rule tested the attribute's PRESENCE, so a real
  project's explicit WRITABLE read as read-only and a label the user added was
  drawn on the project page as one of MATLAB's built-ins. Test the affirmative
  values instead; a missing attribute stays writable, which is how an older
  store spells a hand-made label.

- "First in the chain" is spelled two ways. Run order is a linked list of
  nested <Extension Name="StartUpPrev" Value="<uuid>|HEAD"/>, and a freshly
  built project puts no Extension at all on its first entry where an older one
  writes Value="HEAD". The walk started only at the literal HEAD, so the common
  shape came out in reverse: startup files ran bottom-up in the view. It now
  starts at every head -- an entry whose prev names no entry of this kind --
  and keeps a taken set, which is also what makes it terminate on a cycle.

- The label catalog and the working folders came back in store order, which
  differs per layout, so the same project read two ways and the project page
  rendered its working folders in whatever order the file happened to hold.
  Both are sorted now, beside the files and path folders that already were.
  entryPoints is deliberately left alone: its order is the run order.

The corpus: test/parity/matlab/gen_project.m builds one small project carrying
one of everything the seven collection readers look for, converts it with
matlab.project.convertDefinitionFiles into all four DefinitionFiles formats,
and records what MATLAB itself says each one holds. 230 files, 38 KB, every XML
format an openable .prj -- small enough to commit, where the 108 MB project
that found the first defect is not.

Ownership is recorded by refusal, not by reading an attribute: no MATLAB
property reports it, so the generator tries to remove each definition on a
throwaway copy and writes down the error identifier
(MATLAB:project:management:CanNotModifyReadOnlyLabel). That is what makes the
expectation MATLAB's answer rather than our reading of the bytes that misread
it in the first place.

project.parity.test.ts asserts the three kinds of claim the .slx layout corpus
does -- the layout on disk is real, each format against MATLAB's truth, and the
three XML layouts deeply equal to one another, licensed by MATLAB reporting no
warning for those three conversions. Toml warns
MATLAB:Project:Issues:LabelDataLoss and is compared against its own record: it
deletes resources/ AND the .prj, leaving a 423-byte matlab.toml as the whole
definition, so a Toml project is a missing file type rather than a parse gap.

The hand-typed stores stay. What they cover is damage -- a document that is not
XML, a File entity that is its own child, a chain that is a cycle -- which a
real conversion cannot be made to produce. The corpus covers what MATLAB
really writes; only that kind may state a convention.

drift.mjs regenerates the project corpus too, compared per format and per
section. project_truth.json holds no uuid and no timestamp, and two runs of one
release came out byte-identical, so every diff there is a real change of answer.
@ww-mw
ww-mw merged commit 131698e into main Oct 2, 2026
4 checks passed
@ww-mw
ww-mw deleted the project-parity-corpus branch October 2, 2026 16:06
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.

1 participant