Skip to content

v0.16.0 — what we cannot see, said out loud

Choose a tag to compare

@peopleworks peopleworks released this 23 Aug 23:04
· 16 commits to main since this release
0a7bc0e

What we cannot see, said out loud.

Every release so far made the same kind of promise: here is something in your application you could
not see from the code. This one keeps that promise — reports are read now, the largest gap left in
the extraction — and then does the opposite thing, which turns out to matter more.

An XAF application's reports are frequently not in its repository at all. With ReportsModuleV2
registered, users design reports at run time and they are stored as rows in a database, out of reach
of anything that reads source. An application with forty reports and none in its code is not an edge
case; it is what a reporting setup looks like once people use it. So the answer had to stop being a
list and start being three different sentences: the list is all of them, the list is a lower
bound
, or the number is unknown rather than zero — and that last one is the common case, the
one where a tool that answers confidently does real damage. An agent told an application has no
reports designs as though none can exist.

The same correction landed on the framework catalog, where it had been wrong for three releases.
The newest catalog on the machine was used for every application whatever version it targeted, and
the result said "these controllers load onto this screen" in one confident sentence either way. On a
machine holding a single 26.1 catalog that sentence was produced for a 23.2 application and for a
17.1 one, unqualified. It now asks for the release the application declares, still falls back when
that catalog is absent, and says which wherever a framework fact is reported.

Both halves of the reports work are a collaboration: [@MBrekhof] extracted them, this side rendered
them and wrote the bound. Two things only the generated document could show were caught that way —
a citation printing an absolute path from the machine that ran the extraction, and one layout's
filter and GetCriteria() printed twice when two registrations shared it.

Five spellings of a DevExpress version are read now, because seven real applications were checked
instead of fixtures: PackageReference, a floating 25.2.*-*, an MSBuild property (what
DevExpress's current template generates), and a pre-NuGet assembly reference from 17.1. Three of
those seven module folders hold more than one .csproj, which had been resolved by whatever order
the file system returned.

457 tests, zero warnings.

Added

  • The reports are written down, and the list says what it is ([#37], the rendering half). They
    now appear in the Markdown pages, in AGENTS.md, and through a new xaf_reports MCP tool — each
    with what it is over, the filter inside its layout, its calculated fields and bound expressions,
    and the parameters dialog it opens with, including the GetCriteria() that turns the answers into
    a filter.

    The sentence under the heading carries more than the list. With ReportsModuleV2 registered,
    users design reports at run time and those are stored as database rows, so an application with
    forty of them and none in its repository is the ordinary case: checked against a production
    application that registers the module, sets ReportStoreMode.XML, and contains no report in
    source at all. Printing "no reports" there is not an incomplete answer but a wrong one, and the
    more use an application makes of reports the wronger it gets. Three states are told apart — the
    module absent and the list complete, the module present and the list a lower bound, and the
    module present with nothing in source, where the number is reported as unknown rather than zero.
    An application that registers nothing and ships no layout gets no section, because there "no
    reports" is the default rather than a finding.

    Two things the generated document showed that reading the code would not have. A layout kept
    beside the module rather than inside it was cited by its absolute path — the drive of whichever
    machine ran the extraction, in a file meant to be committed; citations now fall back to the
    solution root and then to the file name, never to a path that is wrong everywhere but here. And
    two registrations sharing one .repx printed the filter, the bindings and the whole of
    GetCriteria() twice, burying the only thing that differs between them; the second now points at
    the first.

  • The reports an application declares are read ([#37], phases 1–3 — the extraction; the
    Markdown and MCP rendering follow separately). Reports V2 leaves four kinds of trace in a
    repository, all syntax, and none of them was read: the registration
    (PredefinedReportsUpdater.AddPredefinedReport<T>, in the same GetModuleUpdaters the updater
    already lives in), the layout (designer code, a .repx, or a constructor), and the parameters
    dialog (ReportParametersObjectBase, whose GetCriteria() is business logic in the plainest
    sense). ExtractedProject.Reports now carries each registration with what the call says and
    nothing it does not — IsInplaceReport is null when the overload is silent — plus the layout's
    filter, bound expressions, grouping, calculated fields and parameters, and the dialog's fields
    and criteria source. ReferencesReportsModule is the flag the rendering needs for the sentence
    that makes the list safe to believe: with ReportsModuleV2 in, users build reports at run time
    that live only in the database, and the list is a lower bound.
    Three things the real files taught, each pinned by the new ReportsSolution fixture: a .repx
    parameter's type resolves through <ObjectStorage>; a layout exported from the running
    application keeps its data source there too; and a shop that designs reports outside Visual
    Studio keeps the exports beside the module, registered by nothing — so every .repx in the
    solution is read, and the unclaimed ones are listed as unregistered rather than skipped.
    Checked against three licensed applications, one of which registers through reflection over a
    catalog: a syntactic reader correctly sees zero registrations there, the module referenced, and
    the fourteen layouts and two parameters objects it would otherwise have hidden.

Fixed

  • The framework catalog is chosen by the version the application declares, not by file date.
    LoadLatest() took the newest catalog on the machine whatever the application targeted, and the
    output then reported which framework controllers load onto a screen in the same confident sentence
    either way. On a machine holding a single 26.1 catalog, that sentence was produced for a 23.2
    application and for a 17.1 one — three releases and nine years out — with nothing to tell the
    reader. Reported by the external review at 0.12.0 and live until now.

    Extraction now reads the DevExpress major.minor from the project file and asks for that
    release's catalog. When it is not on the machine the newest is still used — most of the framework
    is stable across releases, and withholding it would trade real information for a small error — but
    the difference is now stated wherever a framework fact is reported: the Markdown and HTML pages,
    the AGENTS.md header, and the MCP view detail.

    Both project-file spellings are read. A pre-NuGet XAF project has no PackageReference at all and
    names its version only inside the assembly reference (DevExpress.ExpressApp.Xpo.v17.1) — which is
    exactly the case where the mismatch is widest, so reading only the modern spelling would have
    missed the applications that need this most.

    Two further spellings were found by running this against seven real applications rather than
    against fixtures, and both returned "declares nothing" until they were handled. A version written
    as an MSBuild property — <DevExpressVersion>25.2.7</DevExpressVersion> with
    Version="$(DevExpressVersion)" — is what DevExpress's current template generates, so it is not
    an exotic case to tolerate but what a project created today looks like; properties a project file
    sets are now resolved before anything reads a version, which also stops $(DevExpressVersion)
    reaching the rendered package list. A floating 25.2.*-* already worked and is now pinned.

  • The project file a module folder is named for is the one that is read. With several
    .csproj side by side — a .Net10.csproj from a framework migration, a hand-made
    " - Backup.Module.csproj" — extraction took whichever the file system returned first, so the
    target framework, the package list and the DevExpress version could come from a backup, and two
    machines could describe one repository differently with nothing in the output to say why. Three
    of the seven applications checked have more than one.

    Verified end to end against applications declaring 17.1, 23.2, 25.1, 25.2 and 26.1 — including
    every case above, and one whose version matches the installed catalog, where the new sentence
    correctly says nothing at all.