Repository navigation
Q2 2026 — System.Text.Json migration + quarterly work - #13
Conversation
SAM-BIM core migrated from Newtonsoft.Json to System.Text.Json.Nodes. - Replace each csproj's Newtonsoft package reference with System.Text.Json 10.0.8 - FullTypeName.cs: JObject -> JsonObject, using directive swap Build verification not run here: SAM_Revit_UI.sln pulls in projects from SAM_Revit (a Wave 3 sibling, not yet migrated) and requires the Revit API (Autodesk.Revit.DB) which isn't installed. Will be verified when SAM_Revit is migrated. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
During the SAM-BIM Newtonsoft.Json -> System.Text.Json migration the quarterly sow/2026-Q2 branch carries the binary-breaking change. CI needs to consume the migrated SAM (and any sibling dep) from sow/2026-Q2, not from master, until the quarter-end merge. - Add "sow/2026-Q2" to push/pull_request branch triggers - For each dep clone, ls-remote sow/2026-Q2 and prefer it when present; fall back to default branch (e.g. master) otherwise. After the quarter merges back to master, this fallback naturally restores prior behaviour. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Refines the previous workflow patch so CI on the migration PR can succeed before anything has been merged. Each dep repo is cloned from: 1. github.head_ref (feature/remove-newtonsoft on these PRs) - has the migration right now on every dep 2. sow/2026-Q2 - source of truth after these PRs merge 3. default branch - source of truth after quarter-end merge Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The SPDX-check workflow requires the top 6 lines of every changed .cs file to contain both: // SPDX-License-Identifier: LGPL-3.0-or-later // Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors (with an en-dash between the years). Migration commits modified existing files without preserving the header. Prepend the two lines now. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
SAM_Revit / SAM_Revit_UI workflows define $token for the private SAM_Solver dep but the patched cascade loop wasn't using it - resulting in 'Cannot prompt because user interactivity has been disabled' on the ls-remote for the private repo. Pipe $token through the cascade URL and add LASTEXITCODE guards to surface clone failures clearly. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Remove Newtonsoft.Json — migrate to System.Text.Json.Nodes
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0da2770ea0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| foreach ($r in $deps) { Clone-Repo $r } | ||
| foreach ($r in $deps) { | ||
| if (Test-Path $r) { continue } | ||
| $headRef = '${{ github.head_ref }}' |
There was a problem hiding this comment.
Escape untrusted head_ref before injecting into PowerShell
On pull_request runs, github.head_ref is attacker-controlled input, but here it is interpolated directly into a single-quoted PowerShell string. A branch name containing a single quote can break out of the literal and inject arbitrary commands in this step, which executes before dependency cloning and can run with workflow token permissions. Please pass the value via environment/inputs or explicitly escape it before embedding in script text.
Useful? React with 👍 / 👎.
| $headRef = '${{ github.head_ref }}' | ||
| $candidates = @() | ||
| if ($headRef) { $candidates += $headRef } | ||
| $candidates += 'sow/2026-Q2' |
There was a problem hiding this comment.
Avoid forcing dependency clones to sow/2026-Q2 on pushes
For push and workflow_dispatch, github.head_ref is empty, so the candidate list always prioritizes sow/2026-Q2. As a result, builds on main/master will clone quarterly-branch dependencies whenever that branch exists, so CI is no longer validating the dependency versions those branches would actually consume. This can mask integration failures until later merges; prefer github.ref_name (or default branch) first for non-PR runs.
Useful? React with 👍 / 👎.
Aligns the repo with the SAM-BIM workspace-wide pin to the LTS-aligned System.Text.Json 8.0.5, replacing the previously pinned 10.0.8 preview.
Pin System.Text.Json to 8.0.5
* build: Directory.Build.props for centralised SAMVersion stamping Mirrors SAM-BIM/SAM#7. Stage 2 of the AssemblyVersion versioning migration. * fix: replace literal u{2013} escape with actual en-dash in SPDX header PowerShell 5.1 doesn't support backtick-u escape sequences; the apply-stage2.ps1 script leaked them as literal text in 'Copyright (c) 2020u{2013}2026'. * fix: relax SAMVersion.g.cs Target condition to != 'true' Codex P1 on SAM_LadybugTools#4: classic (non-SDK) csprojs don't set GenerateAssemblyInfo at all, so the previous '== false' condition skipped them. Switching to '!= true' catches both GenerateAssemblyInfo=false (SDK projects with legacy AssemblyInfo.cs) AND empty (classic projects). * ci: Codex P2 fixes — four-part SAMVersion, prefer head_ref for sow PRs - Append .0 so SAMVersion is 4-part (AssemblyFileVersion expects 4 parts). - Prefer github.head_ref when it matches sow/yyyy-Qx (release-promotion PRs). - Mirrors SAM sow/2026-Q2 6d87d98e.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 31facfe309
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| $ErrorActionPreference = 'Stop' | ||
| # Prefer head_ref when its a sow branch (release-promotion PRs from sow/* to main), | ||
| # else use base_ref on PR events / ref_name on push events. | ||
| $headRef = '${{ github.head_ref }}' |
There was a problem hiding this comment.
Escape untrusted head_ref in Compute SAMVersion step
github.head_ref is attacker-controlled on pull_request events, and interpolating it directly into a single-quoted PowerShell literal allows branch-name injection when the ref contains ' (valid in Git branch names). In this step, that would let a malicious PR execute arbitrary PowerShell before the build runs, so this value should be passed via environment/input parameters or escaped before embedding in script text.
Useful? React with 👍 / 👎.
Mirrors SAM sow/2026-Q2. Adds AssemblyInformationalVersion attribute generation to Path D's SAMVersion.g.cs Target — covers Grasshopper / Tas-bridge assemblies that have GenerateAssemblyInfo=false, so they also get the CI commit SHA stamp in their ProductVersion field. SDK projects pick up InformationalVersion via PropertyGroup auto-gen as before. Local dev builds unchanged.
Mirrors SAM sow/2026-Q2. Adds AssemblyInformationalVersion attribute generation to Path D's SAMVersion.g.cs Target — covers Grasshopper / Tas-bridge assemblies that have GenerateAssemblyInfo=false, so they also get the CI commit SHA stamp in their ProductVersion field. SDK projects pick up InformationalVersion via PropertyGroup auto-gen as before. Local dev builds unchanged.
) * feat(stage-1): replace WinForms SimulateForm with WPF SimulateWindow Replace the WinForms SimulateForm (which embedded the now-deleted WinForms SimulateControl) with a full WPF Window that hosts the shared SAM.Analytical.UI.WPF.SimulateControl plus the Revit-specific GeometryCalculationMethod combo. WindowInteropHelper parents the dialog to the Revit main window HWND for correct modal behaviour. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: explicitly initialise UpdateConstructionLayersByPanelType to true in Load() Mirrors the WinForms SimulateForm default so simulations do not silently skip construction-layer updates when using the new WPF dialog. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f8723f4bdc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| $has = (git ls-remote --heads $url $cand 2>$null | Out-String).Trim() | ||
| if ($has) { | ||
| Write-Host "Cloning $org/$r @ $cand" | ||
| git clone --depth 1 --branch $cand $url $r |
There was a problem hiding this comment.
Match remote branches exactly before cloning candidate ref
In .github/workflows/build.yml (checked the Clone dependency repos PowerShell section), the existence test uses git ls-remote --heads $url $cand, but ls-remote treats the last argument as a pattern, not an exact ref. That means a candidate like abc can match refs/heads/feature/abc, set $has truthy, and then git clone --branch abc immediately fails because abc does not actually exist. This can make CI fail unpredictably whenever dependency repos contain suffix-matching branch names; use an exact ref check (for example refs/heads/$cand) before cloning.
Useful? React with 👍 / 👎.
* ci: build SAM_Mollier before SAM_Systems
SAM_Systems now consumes SAM_Mollier's built DLLs (the new
SAM.Analytical.Systems.Mollier bridge references
..\..\..\SAM_Mollier\build\SAM.*.Mollier.dll), so SAM_Systems.sln must be
restored/built after SAM_Mollier. Reorder the dependency build list so
SAM_Mollier precedes SAM_Systems while SAM_Systems stays ahead of SAM_Windows
(canonical order: SAM_Psychrometrics -> SAM_Mollier -> SAM_Systems ->
SAM_Windows), matching SAM_Deploy/BuildAll_Release_net.csproj. Without this the
build breaks once the SAM_Systems Mollier bridge lands on sow/2026-Q3.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(simulate): progress, per-stage notes and Cancel for the Revit Simulate command
Revit was in worse shape than Grasshopper. The workflow itself ran through
Analytical.UI.WPF.Modify.RunWorkflow, which has no progress dialog and no
cancellation at all - a silent multi-minute freeze with no way out. The
"Preparing Model" dialog covered only the pre-steps, and two of those
("Converting to TBD", "Updating Shading") are long COM calls that block the Revit
UI thread, so Windows ghosted the dialog and discarded any click on it.
Both phases now use ProgressFormHost, which runs the dialog on its own UI thread
so it never ghosts and the Cancel click is always recorded. The work itself stays
on the calling thread: no TAS COM object changes apartment and no Revit API call
leaves the API thread.
- New Modify.RunWorkflow drives WorkflowCalculator with a hosted dialog, per-stage
notes and a cooperative token, reporting cancellation through out bool cancelled
rather than an ambiguous null. It mirrors the Grasshopper twin rather than
sharing it, because that lives in a Grasshopper assembly and the only sensible
common home - SAM.Analytical.Tas - is deliberately free of WinForms. The part
that would actually drift, the note wording and the uninterruptible-stage list,
IS shared through Analytical.Tas.Query.CancelNote.
- The "Preparing Model" phase becomes cancellable, checks the token between COM
calls, and names the two stages that cannot be interrupted.
- A cancel in either phase reports that partial .tbd/.tsd files may remain and
returns Result.Cancelled, so it is not mistaken for a failure.
Cancellation semantics match Grasshopper: cooperative, observed only between
stages, and unable to interrupt an in-flight simulate or sizing.
"Inserting Results" is deliberately left alone - it steps per space so it pumps
constantly and never ghosts, and it runs inside a Revit Transaction where a
half-applied cancel would be worse than no cancel.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(simulate): elapsed time no longer wraps after a day
The completion message built its time from stopwatch.Elapsed.Hours, which is the
hours component of the TimeSpan, not the total - so a run of 25 hours reported
"01h". Exactly the bug class fixed in ProgressForm, where a plain mm:ss wrapped
after an hour.
It now uses Core.Windows.Query.Duration, which promotes past the hour correctly
and is the formatter the progress dialog uses, so the two agree instead of each
rolling their own.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* ci: build SAM_Validation before SAM_Tas
The build has been failing on SAM_Tas\benchmark\SAM.Analytical.Tas.Benchmark with
CS0234 - "the type or namespace name 'Benchmark' does not exist in the namespace
'SAM.Analytical'" - because that project references
SAM_Validation\build\SAM.Analytical.Benchmark.dll by HintPath and SAM_Validation
was neither cloned nor built here.
Latent since the benchmark project landed in SAM_Tas: this repo's last green run
was 2026-06-28 and there have been no PRs against it since, so nothing surfaced
it until now. It is not caused by the Simulate changes on this branch.
SAM_Validation goes into both the clone list and the ordered build list in the
same slot BuildAlls_v3.csproj uses locally - after SAM_SolarCalculator, before
SAM_Tas and SAM_OpenStudio, the two benchmark producers that need it. It needs
only SAM and SAM_Mollier, both already built above it.
The same gap exists in SAM_Revit, SAM_Rhino_UI and SAM_UI, each fixed separately.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(simulate): resolve Query.CancelNote correctly and close two cancellation gaps
Build fix plus both Codex findings.
CS0117 - 'Query' does not contain a definition for 'CancelNote'. Inside namespace
SAM.Analytical.Revit.UI the name Query binds to this assembly's own Query class,
which wins over the using directive for SAM.Analytical.Tas; Simulate.cs already
qualified it as Analytical.Tas.Query and compiled, Modify/RunWorkflow.cs did not.
Now qualified the same way. My local build never caught this - it turned out to be
a no-op rather than a real compile, which is why CI was the right check.
P1 - a Cancel clicked during "Updating Shading" was ignored. The token was checked
only on the way INTO each stage, leaving the last one uncovered: after UpdateShading
returned, the document was saved and the workflow started, so the exact scenario the
note tells the user to expect - waiting out a long uninterruptible stage - did
nothing. Now observed after the save, so the click is honoured and nothing is left
half-written. Same gap as the one closed in WorkflowCalculator for terminal stages.
P2 - cancelling during the materials loop or UpdateConstructionLayersByPanelType
still deleted the user's existing .tbd on the way out, because the next check came
after that block. The token is now observed immediately before the delete.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(simulate): honour a cancel that lands as the workflow returns
Codex, P1. WorkflowCalculator observes the token before each stage and again before
each of its returns, so a cancel during the last stage is caught there and surfaces
as OperationCanceledException. What remained was the sliver between that final check
and Calculate handing back: a click landing there left cancelled false, and Simulate
went on to insert results into the Revit document after the user had asked it to
stop - a write to the model, not just a wasted run.
The linked token is now observed immediately after Calculate returns, inside the
same try, so it is reported through the existing catch and needs no new path.
The Grasshopper twin has the identical sliver and is fixed in a companion SAM_Tas PR
rather than left to diverge.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(workflow): close the cancel race instead of moving it
Codex, P2, against my own previous commit - and right. The Cancel button lives on the
progress dialog's own UI thread, so a click can land at any instant, including after a
check placed at the end of this method. Checking and then letting the using block
dispose the host merely relocated the race: CancelRequested could still fire in
between, leaving cancelled false.
Reordered so the dialog is torn down first and the token observed afterwards.
ProgressFormHost.Dispose closes the form and joins its thread, so once it returns no
further CancelRequested can arrive and any in-flight one has already run - which makes
the observation after it final rather than merely later.
That means the host can no longer be a using, since a using would dispose it after the
check rather than before; it is disposed in a finally instead. The final check uses
IsCancellationRequested rather than throwing, so it needs no second catch.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(simulate): close the same cancel race in the preparation block
Codex, P2. Modify.RunWorkflow was reordered to dispose the hosted dialog before its
final token check; the preparation block here still checked first and let the using
dispose afterwards, so it kept the race that fix removed. A Cancel raised after the
check but before disposal was never observed, and the TAS workflow started anyway -
which for this command means results written into the Revit document.
Same ordering as RunWorkflow now: the dialog is disposed in a finally, then the token
is observed, so once Dispose returns no further CancelRequested can arrive and any
in-flight one has already run.
The separate post-save check is gone with it - the single post-dispose observation
covers the last stage and is genuinely final, where that one was not.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(progress): let the user cancel the per-item Revit loops
Simulate's "Inserting Results", RemoveParameters and ImportTags all step once
per item and call Update every step, and Update pumps the message queue, so the
form never stops responding and a Cancel click is always seen. They simply had
no Cancel button. No ProgressFormHost is needed for any of them.
The check sits after Update rather than before it: Update is the call that
pumps, so it is what turns a click made during the previous item into a set
flag. Checking ahead of it would read the flag one item stale.
Every one of these loops writes inside a Revit transaction, so cancelling rolls
that transaction back - that is what makes the note on the dialog true. The
document is left exactly as it was however far the loop got, and the command
returns Result.Cancelled rather than Result.Succeeded.
ImportTags does not own its transaction; both of its callers start one around
it. It therefore cannot undo what it has placed, so it now reports through an
out parameter and ImportExportTags and ImportExportAnalyticalModel roll back on
its behalf. The existing two-argument overload is kept and delegates, so no
caller outside this repo has to change.
For Simulate a cancel also skips the JSON save and the room data sheets, since
those are the remaining work of the same command. The simulation itself has
already run either way, so the closing message says the results were not
written into the model rather than claiming the run succeeded.
Adds the SPDX header to the four touched files that were missing it.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(progress): title the RemoveParameters dialog for what it does
It read "Creating Shared Parameters" while removing them - copied from
AddParameters, where the wording is right. Now that the dialog has a Cancel
button the title matters more: it is what tells the user which run they are
about to stop.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(simulate): read the weather data the user actually selected
Simulate rejected every .epw on any model that had not been simulated before.
Picking a file, then OK, gave "Provide Weather Data" with no way past it.
SimulateControl exposes two properties. WeatherData looks the item up by a fixed
internal key, so it only ever returns the entry seeded from the document's
SAM_WeatherFile parameter. SelectedWeatherData returns what is selected -
whether that is the seeded entry or a file the user browsed to.
The Revit window seeded with one and read back the same one, so a browsed .epw
was invisible to both the validation and the command. It worked only on a model
that already carried SAM_WeatherFile, which is why it looked like it had never
worked rather than like a regression.
SAM.Analytical.UI.WPF's own SimulateWindow has always had both properties and
reads SelectedWeatherData; this brings the Revit twin in line. The window gains
the SelectedWeatherData property it was missing, and the validation and
Simulate.cs both read it now. Both properties are documented so the next person
does not pick the wrong one.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: migrate Revit topology conversion to OCCT
Generated by Michal Dengusiak & Codex
* chore: add required SPDX headers
Generated by Michal Dengusiak & Codex
* ci: build SAM_OCCT as a dependency instead of SAM_Topologic
The Revit UI now references SAM_OCCT\build\SAM.{Core,Geometry,Analytical}.OCCT.dll,
but the dependency workflow still cloned and rebuilt SAM_Topologic, so on a clean
runner there was no sibling SAM_OCCT\build to resolve against and the 2025/2026
builds failed with CS0103.
- Replace SAM_Topologic with SAM_OCCT in both the clone list and the ordered build
list in .github/workflows/build.yml. SAM_OCCT is placed after SAM_Solver, its only
sibling dependency besides SAM. No repo in the list references SAM_Topologic now.
- Set SAM_OCCT_SKIP_NATIVE_BUILD=true, mirroring SAM_OCCT's own workflow: the
vcpkg/OpenCASCADE toolchain is not on the runner.
- Guard the SAM.Occt.Native.dll, TK*.dll and tbb*.dll copies in the
SAM.Analytical.Revit.UI post-build event with "if exist", so a managed-only OCCT
build does not fail the post-build step. The redundant TKernel.dll copy is folded
into the TK*.dll wildcard.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore: drop Revit 2017-2024 support (supported range now 2025-2026)
Mirrors the same drop in SAM_Revit:
- SAM.Analytical.Revit.UI.csproj and SAM.Core.Revit.UI.csproj: removed the
dead $(RevitYear)-conditioned TargetFramework lines (2020-2024), trimmed
Configurations, and stripped the dead-year branches from PostBuildEvent.
Also fixed a pre-existing duplicate Debug2025 TargetFramework line found
in SAM.Analytical.Revit.UI.csproj while touching that block.
- #if Revit2017..Revit2024 blocks in the IExternalCommands/*.cs and
Forms/CopyTagsForm.cs sources collapsed to their live branch
(preprocessor-resolved, not hand-edited)
- matching Debug20XX/Release20XX entries dropped from SAM_Revit_UI.sln
- references/Revit 2020..2024/ deleted
- added the SPDX + copyright header to 10 pre-existing .cs files that were
missing it, since every changed .cs file must carry it for CI
Revit 2027 not added here (no SDK DLLs/install on this machine) - follow-up
once available, same as SAM_Revit.
Verified via MSBuild.exe -t:Rebuild: Debug2025 and Debug2026 both build clean
with zero errors, Debug2024 correctly fails as an invalid solution
configuration (MSB4126).
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(progress): report an unconfirmed dialog shutdown as cancelled
The Revit call sites made their final cancellation check on the token alone, so
a run whose dialog thread could not be shut down cleanly was reported as a
success. ProgressFormHost has exposed ShutdownCompleted since SAM_Windows#8 but
nothing read it, because the rule was still unsettled when it was added.
It is settled now, and the four WPF call sites already behave this way: on
ShutdownCompleted == false - the dialog thread was not joined within 5s, or a
handler did not quiesce within 2s - the run is reported as cancelled. A dialog
that will not shut down may be sitting on a click nobody observed, so success
cannot be claimed. For RunWorkflow what is given up is only the in-memory model
handoff, since the .tbd/.tsd are already on disk; for Simulate's preparation
stage the document has already been saved, so nothing is left half-written.
Both sites already disposed the host before the final check and kept it in
scope afterwards, so this is only the extra term.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: add Revit 2027 support (supported range now 2025-2027)
Adds Debug2027/Release2027 alongside 2025 and 2026, plus the Revit 2027 SDK
assemblies under references/Revit 2027.
This repo derives everything from $(RevitYear), so each project needs only the
target framework for the new year and the two configuration names - the
DefineConstants, HintPaths and debug StartProgram all follow automatically.
Revit 2027 is built on .NET 10, not .NET 8: RevitAPI 27.0.4.0 binds
System.Runtime 10.0.0.0, and a net8.0-windows target fails with CS1705. The
2027 configurations therefore target net10.0-windows. 2025 and 2026 are
untouched.
Depends on the matching SAM_Revit change: SAM_Revit_UI.sln includes
SAM.Analytical.Revit and its siblings directly, so those projects need their
own 2027 configurations before this solution can build one.
Verified: SAM.Analytical.Revit.UI rebuilds clean under Release2027, and under
Release2025 and Release2026, so neither existing year regressed.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore: drop the dead Topologic and OCCT binaries from references
references/ carried 72 loose DLLs at its root - the whole OCCT kernel (TK*.dll),
tbb, and TopologicCore/TopologicNET - 62 MB of tracked binaries that nothing
builds against.
Nothing references them. Every HintPath in this repo that points into
references/ points into references/Revit <year>, and those six Revit SDK
assemblies are untouched. OCCT arrives a different way entirely: the managed
assemblies come from ..\..\..\SAM_OCCT\build, and the native kernel is copied
post-build out of SAM_OCCT\build, never out of references. Topologic is not
referenced by any csproj or any .cs file in this repo, and SAM_Topologic is no
longer used.
No other repo reaches into SAM_Revit_UI/references either.
Verified by building SAM.Analytical.Revit.UI clean under Release2026 with the
files gone.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor(ui): move the Revit tree view dialogs onto the WPF window
Replaces all eight SAM_Windows TreeViewForm call sites with
Core.UI.WPF.MultipleSelectionTreeViewWindow. TreeViewForm was the largest
single SAM_Windows consumer left in this repo, and it is now gone from it
entirely.
Depends on SAM_UI#65, which adds the GettingChecked event. Every one of these
sites used TreeViewForm's five-argument constructor to decide each item's
initial tick, and the WPF window had no equivalent - that, not the rewiring,
was the actual blocker.
The shape changes from a generic class with Func<> constructor arguments to a
non-generic window with events, matching how SAM.Analytical.UI/Query/Clean.cs
already drives this window:
TreeViewForm<T>(name, items, text, group, checked) -> Title +
GettingText + GettingCategory + GettingChecked + SetObjects
SelectedItems -> GetObjects<T>()
ShowDialog() != DialogResult.OK -> ShowDialog() != true
Three things worth knowing:
CollapseAll is dropped rather than reimplemented. A WPF TreeViewItem starts
collapsed; the WinForms tree had to be told to. Same rendered result.
The seven IExternalCommands now set the window owner from
ExternalCommandData.Application.MainWindowHandle, as Simulate.cs already did.
An unowned WPF window in Revit's process can end up behind the main window and
look like a hang, which matters more than it did for the WinForms dialogs.
Query.ParameterNames is a plain extension method with no ExternalCommandData to
take a handle from, so it keeps the old unowned behaviour.
ParameterNames also carries its explicit 430x850 sizing across, as Width and
Height rather than Size.
Behaviour is otherwise preserved exactly, including the inverted
unselectedParameterGroups condition in ParameterNames - that is a real bug, but
fixing it here would stop this being a behaviour-preserving migration. It is
flagged separately.
Verified: SAM.Analytical.Revit.UI rebuilds clean under Release2025 and
Release2026. Not hand-tested in Revit.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(deploy): send Revit 2027 output to its own %APPDATA%\SAM folder (#16)
The post-build events in SAM.Analytical.Revit.UI and SAM.Core.Revit.UI map a
Revit-year configuration onto a deploy folder, then copy that year's assemblies
into %APPDATA%\SAM\Revit <year>\ - which is exactly where each year's
SAM.addin points its <Assembly>.
Neither had a branch for Debug2027 or Release2027. Version was left empty, so
set SAM_DIR=%APPDATA%\SAM\%Version%
collapsed to %APPDATA%\SAM itself and a 2027 build scattered its assemblies
loose into the root of that folder - which is the shared Rhino/Grasshopper
deploy directory, not a Revit one. Two consequences: no "Revit 2027" folder was
ever created, so the 2027 add-in had nothing to load; and eight net10
assemblies were dropped next to the net8 ones Grasshopper loads.
Adds the missing 2027 branches, and makes the failure loud rather than silent:
an unmapped Revit-year configuration now fails the post-build with an
explanatory message instead of quietly deploying to the wrong place. That guard
is the actual fix - the missing branch was easy to add, but nothing surfaced it
for however long 2027 had been buildable.
Verified locally: after this change a Debug2027 build creates
%APPDATA%\SAM\Revit 2027\ with 251 files, and reading the TargetFramework
attribute out of the deployed SAM.Analytical.Revit.UI.dll gives
.NETCoreApp v10.0 for 2027 against v8.0 for 2025 and 2026.
Generated by Michal Dengusiak and CodeClaude
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* ci: build and snapshot Revit 2027 (#17)
Same gap as SAM_Revit's companion PR: build.yml only ever built Debug/Release
2025 and 2026, so CI never actually compiled a 2027 configuration for any of
the Revit 2027 work (#13, #15, #16) - all of it was CI-green on 2025/2026
alone, with 2027 verified only by hand, locally, in the session that added it.
Clones the existing per-year clean+build+snapshot+verify pattern for 2027,
and installs the .NET 10 SDK alongside 8 (Revit 2027 targets
net10.0-windows; RevitAPI 27.0.4.0 binds System.Runtime 10.0.0.0, so a net8
target fails CS1705). The upload-artifact step already globs
_ci_artifacts/**/*, so it needed no change.
Generated by Michal Dengusiak and CodeClaude
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* docs: point stale BuildAlls_v3 comment reference at BuildAlls_v4
BuildAlls_v3.csproj was retired locally in favor of BuildAlls_v4.csproj.
Update the "same slot as" comments so they still point at a real file.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: migrate ComboBoxForm and TextBoxForm call sites to WPF
Part of the SAM_Windows retirement. Four dialog sites moved off
SAM.Core.Windows.Forms onto the existing WPF equivalents in SAM.Core.UI.WPF;
no SAM_UI change was needed, both replacements already covered the call sites.
- CreateFloorPlans.cs ComboBoxForm<ViewPlan> -> ComboBoxWindow<ViewPlan>
- CreateSheets.cs ComboBoxForm<ViewSheet> -> ComboBoxWindow<ViewSheet>
- ImportExportAnalyticalModel.cs
ComboBoxForm<GeometryCalculationMethod>
-> ComboBoxWindow<GeometryCalculationMethod>
- AddWallTags.cs TextBoxForm<double> -> TextBoxWindow
Translation applied at each site:
- ShowDialog() != DialogResult.OK -> ShowDialog() != true
- dropped the using block; a WPF Window is not IDisposable
- set the owner via WindowInteropHelper + Application.MainWindowHandle, so the
dialog cannot end up behind Revit's main window and read as a hang
Two details worth calling out:
1. ComboBoxWindow<T>'s single-argument constructor populates the drop-down from
the enum values via Core.Query.Description, exactly as the WinForms
ComboBoxForm<T> did. ImportExportAnalyticalModel relies on that - it passes
no item list.
2. TextBoxWindow is not generic, so unlike TextBoxForm<double> it attaches no
numeric key filter. TextBoxForm got one from SetValue wiring up
EventHandler.ControlText_NumberOnly; the Validation predicate set here is
that handler's own WPF (TextCompositionEventArgs) overload, verbatim.
GetValue<double>() with no default still returns 0 on an unparseable entry,
matching TextBoxForm<double>.Value - preserved rather than quietly improved.
Behaviour-preserving throughout: same titles, same default selections (including
the hardcoded Id.Value == 312 / == 725533 lookups, left alone), same Cancel
semantics. ProgressForm usages in these files are untouched - a separate task.
Verified: SAM.Analytical.Revit.UI compiles clean at Release2025, Release2026 and
Release2027.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix: negate the inverted condition in Query.ParameterNames
Query.ParameterNames(objects, index_Group, index_Name, unselectedParameterGroups)
pre-ticked exactly the groups named in unselectedParameterGroups and left
everything else unticked - backwards from both the parameter name and its only
caller, AddParameters.cs:71, which passes a variable literally called
"unselected".
Confirmed as a genuine bug, not a compensating workaround: AddParameters's loop
filters with "if (!names_Selected.Contains(name)) continue;" - a plain
skip-if-not-selected with no negation elsewhere that would have depended on the
old (backwards) tick state.
Fix: negate the non-null branch. The null branch (used by RemoveParameters.cs,
which passes no 4th argument - everything ticked) is unchanged.
This does not touch #18's WPF migration of the tree/dialog rendering - only the
initial Checked value each item is built with.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* feat: migrate ModifyInternalCondition off InternalConditionForm/SpacesForm
T6b of the SAM_Windows retirement, following T6a (SAM_UI PR #67) which added the
ProfileLibrary/AdjacencyCluster getters this needed on InternalConditionWindow
and SpacesWindow.
Both WinForms forms took ProfileLibrary/AdjacencyCluster as separate constructor
arguments; the WPF windows take a single AnalyticalModel. profileLibrary at this
call site can differ from analyticalModel.ProfileLibrary (it may have been
substituted with Analytical.Query.DefaultProfileLibrary() a few lines above), so
a combined AnalyticalModel is built once before branching and passed into
whichever window is used, preserving that distinction.
Translation:
- ShowDialog() != DialogResult.OK -> ShowDialog() != true
- dropped the using block; a WPF Window is not IDisposable
- set the owner via WindowInteropHelper + Application.MainWindowHandle (this
command had no owner before either window kind - added now, consistent with
every other Revit dialog site touched in this retirement)
- new Space(tuples[0].Item1) copy-construction preserved as-is
One behavioural fix, called out explicitly: both branches used to reassign
`profileLibrary` from the form's result and then rebuild analyticalModel with
the 2-argument AnalyticalModel(analyticalModel, adjacencyCluster) constructor -
which keeps analyticalModel's own PRE-dialog ProfileLibrary. profileLibrary was
written but never actually consumed again; any profile-library edit made via
the form's "select from library" picker was silently discarded. Switched to the
4-argument constructor, which is what the ProfileLibrary/AdjacencyCluster
getters added in T6a exist to enable, and matches the pattern
InternalConditionControl and SpacesWindow already use internally for the exact
same kind of update.
Verified: SAM.Analytical.Revit.UI compiles clean at Release2025, Release2026 and
Release2027.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix: add missing SPDX header to ModifyInternalCondition.cs
The file never carried the SPDX/copyright header before this PR touched it -
adding it now since the header check applies to every changed .cs file, not
just newly created ones.
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* feat: migrate the Revit ProgressFormHost sites to ProgressWindowHost
Swaps the two SAM.Core.Windows.Forms.ProgressFormHost consumers in this repo for
SAM.Core.UI.WPF.ProgressWindowHost, part of retiring SAM_Windows:
IExternalCommands/Simulate.cs:139 "Preparing Model"
Modify/RunWorkflow.cs:62 "Tas Workflow"
ProgressWindowHost mirrors ProgressFormHost's surface exactly - same constructor,
Max/Note/Update/CancelRequested/CancellationRequested/ShutdownCompleted, same
dispose-then-observe contract - so every cancel/shutdown decision at these sites is
preserved verbatim and nothing about the control flow changes:
- still not a `using`; still try/finally with Dispose in the finally, so the host is
torn down BEFORE the token is observed for the last time
- an unconfirmed shutdown (!ShutdownCompleted) still reports CANCELLED, not success
- no Cancel button added to any coarse-stepped step, and none removed
- the title-bar close is still not a cancel: dismissing the dialog lets the run finish
and report success. ProgressWindow's title bar does carry an X where ProgressForm set
ControlBox = false, so this is now reachable by mouse as well as Alt+F4; it lands on
ProgressWindowHost's Closed handler, which ends the dispatcher loop and leaves the job
running - the behaviour the WinForms host already had via Alt+F4.
No owner is set on either dialog, deliberately, and both files now say why: a WPF owner
must live on the same thread as the window it owns and the host's window does not, by
design. ProgressWindowHost sets Topmost = true instead, which is what keeps it in front of
a Revit window that has stopped painting.
The two remaining SAM.Core.Windows.Forms.ProgressForm sites named in the migration list
(RemoveParameters, ImportTags) are caller-thread dialogs, not hosts - only their now-stale
comment references to ProgressFormHost are touched here. They and the repo's other seven
ProgressForm sites are left for a separate change.
Built clean at Release2025, Release2026 and Release2027.
Generated by Michal Dengusiak and CodeClaude
* ci: pre-build SAM.Core.UI.WPF before SAM_Tas
SAM_Tas's SAM.Analytical.Grasshopper.Tas is gaining a reference to
SAM_UI\build\SAM.Core.UI.WPF.dll (SAM_Tas#23, ProgressWindowHost). This workflow builds
SAM_Tas.sln long before SAM_UI.sln, so without this it starts failing CS0234 the moment
that lands - even though nothing in this repo changed.
SAM_UI cannot simply move above SAM_Tas: SAM.Analytical.UI and SAM.Analytical.UI.WPF both
reference SAM_Tas\build\*.dll, so the two repos are mutually dependent at solution level.
SAM.Core.UI.WPF is the break in that cycle - no SAM_Tas dependency, needs only
SAM\build\SAM.Core.dll and SAM_Excel\build\SAM.Core.Excel.dll, and pulls in SAM.Core.UI
through a ProjectReference.
- move SAM_Excel above SAM_Tas in $orderedRepos (its only dependency is SAM)
- build SAM_UI\WPF\SAM.Core.UI.WPF\SAM.Core.UI.WPF.csproj immediately before SAM_Tas,
via a new Restore-BuildProject helper
- SAM_UI still builds in full at the end of $orderedRepos, unchanged
Harmless before SAM_Tas#23 merges - it just builds one extra project - so this can go in
first, which is the point: it has to.
The same pre-build lands in SAM_Tas and SAM_Rhino_UI's build.yml and in the local
BuildAlls_v4.csproj; all four have to change together.
Generated by Michal Dengusiak and CodeClaude
* feat(progress): migrate Revit progress dialog to SAM.Core.Windows.WPF, drop pre-Tas workaround
Switches the two ProgressWindowHost call sites (Modify/RunWorkflow.cs,
IExternalCommands/Simulate.cs) from SAM.Core.UI.WPF to SAM.Core.Windows.WPF
(SAM_Windows#10), using the SAM.Core.Windows reference already present in
SAM.Analytical.Revit.UI.csproj - no csproj change needed. The
SAM.Core.UI.WPF reference itself is kept: nothing else in this repo's
source or XAML currently touches it, but removing an existing reference is
out of scope for this migration.
ci: SAM_Tas no longer needs SAM.Core.UI.WPF pre-built before it (SAM_Tas#24
removed that dependency), so this reverts exactly what SAM_Revit_UI#22
("ci: pre-build SAM.Core.UI.WPF before SAM_Tas") added: SAM_Excel moves
back below SAM_Tas in $orderedRepos, and the $preTasProjects pre-build (the
Restore-BuildProject helper and its call before SAM_Tas) is removed. The
pre-existing SAM_Windows\build / SAM_UI\build ReferencePath setup predates
that workaround (confirmed against SAM_Revit_UI#22's parent commit) and is
untouched - SAM_UI still builds in full at the end of $orderedRepos for its
own legitimate reasons.
Verified: Release2025, Release2026 and Release2027 all rebuild clean with
full Visual Studio MSBuild - 0 errors in each, SAM.Analytical.Revit.UI.dll
and SAM.Revit.UI.dll produced for all three.
Not verified: the interactive Revit hand test (licence-dependent) - not run
in this session.
Part of the SAM_Tas / SAM_Tas_Grasshopper repository-split programme.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* build: declare SupportedOSPlatform("windows") on GenerateAssemblyInfo=false projects
Both SAM.Analytical.Revit.UI and SAM.Core.Revit.UI set
GenerateAssemblyInfo=false and target net8.0-windows (Revit 2025/2026)
or net10.0-windows (Revit 2027), so the SDK never emits its usual
implicit [assembly: SupportedOSPlatform("windows...")]. Without it, the
CA1416 platform-compatibility analyzer cannot tell these assemblies are
Windows-only and flags every WinForms/WPF/Revit-UI API call site as
"reachable on all platforms" - the actual runtime constraint never
changed, only the analyzer's visibility into it.
Root cause confirmed by full-build CI log analysis (SAM_Deploy run
30620168476): CA1416 was the single largest warning code across the
whole SAM stack (3656 raw / 2597 distinct), with SAM.Analytical.Revit.UI
(185 distinct) and SAM.Core.Revit.UI (26 distinct) both matching this
exact GenerateAssemblyInfo=false + net8.0/10.0-windows pattern. Same
fix already applied to SAM_Windows's 5 matching projects.
Verified: SAM_Revit_UI.sln Restore + Rebuild, Configuration=Release2025,
0 errors, CA1416 count 0 (down from hundreds), no other warning code
count changed. (SolutionDir-relative RevitAPI HintPaths require
building via the .sln, not the .csproj directly, to resolve - verified
that way.)
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Add AI agent instructions and project progress tracking
Generated by Michal Dengusiak and CodeClaude
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* chore: remove internal agent/progress files and evidence artifacts from upstream contribution
Removes AGENTS.md, PROJECT_PROGRESS.md and internal evidence/.claude artefacts that are SAM-BIM workflow records, not upstream project documentation.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: SAM-BIM-AI <vm@sambim.xyz>
Summary
Quarterly Q2 2026 work from the SAM-BIM fork merging back into the upstream.
Includes the Newtonsoft.Json → System.Text.Json.Nodes migration of the
entire SAM-BIM stack (per-repo PRs already merged into SAM-BIM's
sow/2026-Q2).Highlights of this quarter
IJSAMObjectimplementations migrated fromJObject/JArray/JTokentoJsonObject/JsonArray/JsonNode.FromJObject/ToJObjectrenamed toFromJsonObject/ToJsonObject.System.Text.Json10.0.8 replacesNewtonsoft.Json13.0.3 in every csproj(except SAM_Revit which keeps Newtonsoft loadable for Revit's runtime).
Log identity loss, SearchWrapper missing type discriminator, RelationCollection
wrong-jObject-passed).
Verification
BuildAlls_v3 RestoreCleanRebuildagainst the merged sow/2026-Q2 chain:0 errors, 144 artifacts.
Test plan
IJSAMObjectAPI renamemasterfrom upstream🤖 Generated with Claude Code