Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions SAM_Multitasker/SAM.Core.Multitasker/Classes/Multitasker.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using Microsoft.CodeAnalysis;
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors

using RoslynDiagnostic = Microsoft.CodeAnalysis.Diagnostic;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using System;
Expand Down Expand Up @@ -52,7 +55,7 @@ public async Task<MultitaskerResults> Run(IEnumerable<MultitaskerInput> multitas

Script<object> script = CSharpScript.Create(code, scriptOptions, typeof(MultitaskerInput));

ImmutableArray<Diagnostic> immutableArray = script.Compile();
ImmutableArray<RoslynDiagnostic> immutableArray = script.Compile();
if(immutableArray.Length != 0)
{
return new MultitaskerResults(immutableArray);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
using Microsoft.CodeAnalysis;
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors

using RoslynDiagnostic = Microsoft.CodeAnalysis.Diagnostic;
using System;
using System.Collections.Generic;

namespace SAM.Core.Multitasker
{
public class MultitaskerResults
{
private List<Diagnostic> diagnostics;
private List<RoslynDiagnostic> diagnostics;
private List<MultitaskerResult> multitaskerResults;


public MultitaskerResults(IEnumerable<Diagnostic> diagnostics)
public MultitaskerResults(IEnumerable<RoslynDiagnostic> diagnostics)
{
multitaskerResults = null;
this.diagnostics = diagnostics == null ? null : new List<Diagnostic> (diagnostics);
this.diagnostics = diagnostics == null ? null : new List<RoslynDiagnostic> (diagnostics);
}

public MultitaskerResults(IEnumerable<MultitaskerResult> multitaskerResults)
Expand All @@ -29,7 +32,7 @@ public bool Succedded
}
}

public List<Diagnostic> Diagnostics
public List<RoslynDiagnostic> Diagnostics
{
get
{
Expand Down
3 changes: 3 additions & 0 deletions design/grasshopper-icons/tests/GhIconTest/GhIconTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@
<ItemGroup>
<None Update="Rhino.Testing.Configs.xml" CopyToOutputDirectory="Always" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\SAM_Multitasker\SAM.Core.Multitasker\SAM.Core.Multitasker.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using NUnit.Framework;
using SAM.Core.Multitasker;
using RoslynDiagnostic = Microsoft.CodeAnalysis.Diagnostic;

[TestFixture]
public class MultitaskerDiagnosticsTests
{
[Test]
public void PublishedDiagnosticsApiStillUsesRoslynTypes()
{
Assert.That(typeof(MultitaskerResults).GetProperty(nameof(MultitaskerResults.Diagnostics)).PropertyType,
Is.EqualTo(typeof(List<RoslynDiagnostic>)));
Assert.That(typeof(MultitaskerResults).GetConstructor(new[] { typeof(IEnumerable<RoslynDiagnostic>) }),
Is.Not.Null);
}

[Test]
public void CompilationDiagnosticIsRetainedWithoutConversion()
{
RoslynDiagnostic diagnostic = RoslynDiagnostic.Create(
new DiagnosticDescriptor("SAMREG001", "Compilation", "Invalid script", "Regression",
Microsoft.CodeAnalysis.DiagnosticSeverity.Error, true), Location.None);
var results = new MultitaskerResults(new[] { diagnostic });

Assert.That(results.Succedded, Is.False);
Assert.That(results.Diagnostics, Has.Count.EqualTo(1));
Assert.That(results.Diagnostics[0], Is.SameAs(diagnostic));
}

[Test]
public async Task InvalidScriptReturnsRoslynCompilationErrors()
{
MultitaskerResults results = await new Multitasker("this is not valid C#;").Run();

Assert.That(results.Succedded, Is.False);
Assert.That(results.Diagnostics.Any(d => d.Severity == Microsoft.CodeAnalysis.DiagnosticSeverity.Error), Is.True);
}
}
72 changes: 72 additions & 0 deletions documentation/Diagnostic-Collision-Validation-2026-10-15.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Multitasker Roslyn Diagnostic collision — 2026-10-15

## Status and scope

PR [SAM_Multitasker#12](https://github.com/SAM-BIM/SAM_Multitasker/pull/12), focused correction into `sow/2026-Q4`, based on Multitasker `aa126fa6eeb34cbffb72cbf5a5129aa17be1ec6b`.
Validated content SHA: `cfe9d116c1d9837e23ada308d535029eaf6de773`.
Affected-project build, all four NUnit tests, and full BuildAlls_v5 pass locally. Initial PR-head GitHub Build and SPDX pass; final record-only head checks are reported in the PR description. No merge authorized.
SAM#204 is unchanged and can proceed independently of this correction: the failure reproduces at its Q4 base, before its changes.

## Proven cause and dependency boundary

Within namespace `SAM.Core.Multitasker`, adding `SAM.Core.Diagnostic` changes resolution of the unqualified `Diagnostic` identifier from the imported Roslyn class to the enclosing SAM.Core class. `script.Compile()` still returns Roslyn diagnostics, producing CS0029. The same resolution affects the stored diagnostics and public result constructor/property, so both production files require explicit aliases.

The first breaking SAM commit is `967a8639eb6f7f614ff4168d62ab3dfd979efbe4` (shared Diagnostics Phase 1, SAM#201, 2026-10-15 11:31 +02). Its immediate parent `a71435dd88d28c9b0da0bca43c21fbca5e1038b9` builds successfully with the identical Multitasker source. Multitasker was unchanged at `aa126fa6eeb34cbffb72cbf5a5129aa17be1ec6b`; the failing source originated in `4e7e01e61770b19f1647f387934c788e80b21c8c` (2024).

Isolated `git archive` source builds, without switching the SAM checkout:

| SAM SHA | Multitasker source | SAM.Core | Consumer | API/behavior probe |
|---|---|---|---|---|
| `0649e15ee9c1727af5834d9206017da9860917c5` | `aa126fa` | pass | pass | 4 assertions pass |
| `a71435dd88d28c9b0da0bca43c21fbca5e1038b9` | `aa126fa` | pass | pass | 4 assertions pass |
| `967a8639eb6f7f614ff4168d62ab3dfd979efbe4` | `aa126fa` | pass | CS0029 | not run |
| `6b12e2a65817556693ba408674211610f4a20dda` (Q4 before #204) | `aa126fa` | pass | CS0029 | not run |
| `967a8639eb6f7f614ff4168d62ab3dfd979efbe4` | `aa126fa` + alias fix | reused passing build | pass | 4 assertions pass |

Only the SAM.Core reference varied in this boundary experiment; other SAM assembly references were held at the same local build. Each archive used the actual SAM.Core and consumer project/source, with scratch-only absolute HintPaths and `RunPostBuildEvent=Never`. The ordinary affected-project build independently reproduced the original CS0029 before the fix.

## Last known good full configuration

The latest preserved successful full run is the morning 2026-10-15 `BuildAlls_v5.bat nopause`, completed about 09:19:44 +02 in `00:09:32.0360117`: Restore/Clean/Rebuild across the 26 active repositories, Revit 2025/2026/2027, final native OCCT checks passed.
Machine-local evidence: parent `.buildall-v5-validation/json-fix-2026-10-15/BuildAlls-full-build.log`, `BuildAlls-working-trees.log` and `BuildAlls-JSON-report.md`.

That log did not record SHAs. The exact SHA configuration in [initial-and-morning-configurations.json](evidence/diagnostic-collision-2026-10-15/initial-and-morning-configurations.json) is **reconstructed from local HEAD reflogs**, not a contemporaneous SHA manifest: equal heads at 09:10:12 and 09:19:44 +02, no entries during 09:10–09:20 in any of the 26 repos, and the contemporaneous working-tree log reports all clean. Relevant heads: SAM `0649e15ee9c1727af5834d9206017da9860917c5`, Multitasker `aa126fa6eeb34cbffb72cbf5a5129aa17be1ec6b`.

The same JSON records the clean investigation-start heads/branches. Multitasker's recorded feature branch was created from the clean Q4 head before edits; initial checkout was `sow/2026-Q4`. [full-build-configurations.json](evidence/diagnostic-collision-2026-10-15/full-build-configurations.json) records the current full-run heads, with Multitasker at the tested content SHA and SAM#204 at `4a8afbebb186ace81eb5d49aed32deb502120781`.

## Changes and safeguards

- `SAM_Multitasker/SAM.Core.Multitasker/Classes/Multitasker.cs`: distinct `RoslynDiagnostic` alias for compilation results.
- `SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerResults.cs`: same alias for storage, constructor and public property. Historical CLR types remain `IEnumerable<Microsoft.CodeAnalysis.Diagnostic>` / `List<Microsoft.CodeAnalysis.Diagnostic>`.
- Both touched production files receive required SPDX headers.
- Existing `design/grasshopper-icons/tests/GhIconTest/GhIconTest.csproj`: reference the core project; new `MultitaskerDiagnosticsTests.cs` covers public types, object-preserving diagnostic transfer and invalid-script compilation errors.
- This record and four compact JSON evidence files preserve the boundary and repository configurations.
- No algorithm, component, shared SAM.Core type, workflow or PROJECT_PROGRESS change. No SAM#204 edits.

## Reproduction and local validation

Prerequisites: sibling SAM build first, .NET SDK, installed Rhino 8 for the existing NUnit host fixture. Builds emit existing System.Memory reference and nullable warnings; no new suppression added.

From the Multitasker repository:

```powershell
dotnet build SAM_Multitasker/SAM.Core.Multitasker/SAM.Core.Multitasker.csproj -c Debug --no-restore -p:RunPostBuildEvent=Never
$env:SAM_ICON_MANIFESTS = (Resolve-Path design/grasshopper-icons/manifest.json).Path
$env:SAM_ICON_ASSEMBLY_DIR = (Resolve-Path build).Path
dotnet test design/grasshopper-icons/tests/GhIconTest/GhIconTest.csproj -c Debug -p:RunPostBuildEvent=Never --logger 'trx;LogFileName=Multitasker-regression.trx'
& ..\BuildAlls_v5.bat nopause
```

- Affected project: pass, 0 errors (2 warnings).
- Existing NUnit project: **4 passed, 0 failed, 0 skipped** (three new regression tests plus existing icon test).
- Isolated boundary: exact outcomes above, raw summary in [boundary-results.json](evidence/diagnostic-collision-2026-10-15/boundary-results.json).
- Similar-collision audit: only other active Roslyn source is in SAM_Tas GenOpt test namespaces. It uses inferred diagnostic values and Roslyn DiagnosticSeverity without the affected SAM.Core namespace lookup; no equivalent collision found. No Tas changes.
- Full BuildAlls_v5: **PASS**, exit 0, `00:07:41.6286121`, 2026-10-15 21:07:51–21:15:36 +02. All 26 active repositories Restore/Clean/Rebuild, Revit 2025/2026/2027 and final OCCT ABI/dependency/hash checks passed. CS0029 is absent; no new build errors. Existing warnings remain (package compatibility/duplicate references, assembly-version/architecture conflicts and XML documentation). This is build validation, not general installed-host engineering acceptance.
- GitHub Build at record-only head `7f95bfb7d92006ae37324bf472717cc97929ba1a`: **PASS**, [run 38791612549](https://github.com/SAM-BIM/SAM_Multitasker/actions/runs/38791612549); SPDX also passed. Logs confirm `Cloning SAM-BIM/SAM @ sow/2026-Q4` and actual SAM.Core.Multitasker compilation with no CS0029. Final docs-only commit checks are tracked in the PR description.
- Post-build status: all 26 source trees clean; only Multitasker head advanced from tested content by its record-only commit. SAM#204 remains clean at `4a8afbebb186ace81eb5d49aed32deb502120781`. [validation-results.json](evidence/diagnostic-collision-2026-10-15/validation-results.json) records full-run timing, test totals and SHA256 hashes of the retained machine-local logs/TRX.

Machine-local scratch evidence is under `C:\Users\michal\.codex\visualizations\2026\10\15\01a140c3-07d7-7db2-83db-a3f98397ac92\multitasker-regression`: `investigate.py` archive harness, core/consumer/probe logs, affected-project log, NUnit TRX, and full-run log. These raw logs/harness are not committed; copy/provision them if needed on another machine. The committed tests, JSON matrix and exact SHAs/commands above preserve the critical outcomes. Original pre-fix log: adjacent `pr204-review/Multitasker-before-fix.log`.

## Remaining work

Verify final record-only head GitHub checks, then request owner review and merge only with approval. The tested source remains `cfe9d116c1d9837e23ada308d535029eaf6de773`; subsequent commits change records only. After merge, add the known-merge-SHA docs-only Q4 closeout under repository policy. Keep SAM#204 independent.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[
{
"label": "morning-good",
"sam": "0649e15ee9c1727af5834d9206017da9860917c5",
"multitasker": "aa126fa6eeb34cbffb72cbf5a5129aa17be1ec6b",
"core_build_exit": 0,
"consumer_exit": 0,
"probe_exit": 0
},
{
"label": "parent-good",
"sam": "a71435dd88d28c9b0da0bca43c21fbca5e1038b9",
"multitasker": "aa126fa6eeb34cbffb72cbf5a5129aa17be1ec6b",
"core_build_exit": 0,
"consumer_exit": 0,
"probe_exit": 0
},
{
"label": "first-diagnostic",
"sam": "967a8639eb6f7f614ff4168d62ab3dfd979efbe4",
"multitasker": "aa126fa6eeb34cbffb72cbf5a5129aa17be1ec6b",
"core_build_exit": 0,
"consumer_exit": 1
},
{
"label": "q4-before-204",
"sam": "6b12e2a65817556693ba408674211610f4a20dda",
"multitasker": "aa126fa6eeb34cbffb72cbf5a5129aa17be1ec6b",
"core_build_exit": 0,
"consumer_exit": 1
},
{
"label": "first-diagnostic-fixed",
"sam": "967a8639eb6f7f614ff4168d62ab3dfd979efbe4",
"consumer_exit": 0,
"probe_exit": 0
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
[
{
"repository": "SAM",
"branch": "docs/gh-component-standards-q4",
"head": "4a8afbebb186ace81eb5d49aed32deb502120781",
"status": ""
},
{
"repository": "SAM_Psychrometrics",
"branch": "sow/2026-Q4",
"head": "4db5756ed9957d625837014d6e8ce71e23df56b0",
"status": ""
},
{
"repository": "SAM_Mollier",
"branch": "sow/2026-Q4",
"head": "7f61916adad9ad913904d18047ec6bfe835d1611",
"status": ""
},
{
"repository": "SAM_Systems",
"branch": "sow/2026-Q4",
"head": "f0838ca3eba3e4b5f1cee008ba0831b6d7e95625",
"status": ""
},
{
"repository": "SAM_Windows",
"branch": "sow/2026-Q4",
"head": "11bbecfa8aa854f4d9b2494c8bca737298982751",
"status": ""
},
{
"repository": "SAM_IFC",
"branch": "sow/2026-Q4",
"head": "77046bef504474619cf39afbbc70ee7975aad50c",
"status": ""
},
{
"repository": "SAM_Acoustic",
"branch": "sow/2026-Q4",
"head": "baed72cb439f6c23fdd16725f497be67938dc54a",
"status": ""
},
{
"repository": "SAM_BHoM",
"branch": "sow/2026-Q4",
"head": "995caace61164bf281501613e8ec4f3008053f39",
"status": ""
},
{
"repository": "SAM_gbXML",
"branch": "sow/2026-Q4",
"head": "43b5d018ac7a8a14f332e8bcde9cb0d613bf1efb",
"status": ""
},
{
"repository": "SAM_GEM",
"branch": "sow/2026-Q4",
"head": "1bf41916fefa6dd5fe44a735c294619d3d8df8d9",
"status": ""
},
{
"repository": "SAM_LadybugTools",
"branch": "sow/2026-Q4",
"head": "0ffbaecf1097a33de2044ee919c963eaeb0a0da2",
"status": ""
},
{
"repository": "SAM_Solver",
"branch": "sow/2026-Q4",
"head": "fd63b4670cd4baeb20a74312aacc17eb177ecc18",
"status": ""
},
{
"repository": "SAM_SolarCalculator",
"branch": "sow/2026-Q4",
"head": "277a3a4864f2bf900504a64a4830cb9438312d78",
"status": ""
},
{
"repository": "SAM_Validation",
"branch": "sow/2026-Q4",
"head": "c9a7f193f46289f52eecdd4a7dc6179789f2c4e9",
"status": ""
},
{
"repository": "SAM_Tas",
"branch": "sow/2026-Q4",
"head": "0589f0da7e2633478c26defdb237ceb0821ebdf9",
"status": ""
},
{
"repository": "SAM_Tas_Grasshopper",
"branch": "sow/2026-Q4",
"head": "d827409a10e8911f9eb6b5e81d1cbb341c2fc0ce",
"status": ""
},
{
"repository": "SAM_Excel",
"branch": "sow/2026-Q4",
"head": "6669977d07cc6488c0dd91fe0a48ccd2dc833627",
"status": ""
},
{
"repository": "SAM_SQLite",
"branch": "sow/2026-Q4",
"head": "ec2eb86d00cac6e4cadf52b429f9d293eeabbd5e",
"status": ""
},
{
"repository": "SAM_OpenStudio",
"branch": "feature/openstudio-diagnostics-phase4a-q4",
"head": "3746eed5a82281f22e94df76027a7f99cdbe68b8",
"status": ""
},
{
"repository": "SAM_Origin",
"branch": "sow/2026-Q4",
"head": "74c19a1ebf3fc11a136cc9b055edf68b2b91a087",
"status": ""
},
{
"repository": "SAM_Multitasker",
"branch": "codex/fix-roslyn-diagnostic-collision-q4",
"head": "cfe9d116c1d9837e23ada308d535029eaf6de773",
"status": ""
},
{
"repository": "SAM_OCCT",
"branch": "sow/2026-Q4",
"head": "aad68123373e0dfe5841ec9670ec1b004960c73f",
"status": ""
},
{
"repository": "SAM_UI",
"branch": "sow/2026-Q4",
"head": "ebe5e09b30b95e43e5d047b3aef2d8c5f01110e9",
"status": ""
},
{
"repository": "SAM_Rhino_UI",
"branch": "sow/2026-Q4",
"head": "24fafda03cef5168abf1e43a1669a2fe42490f8d",
"status": ""
},
{
"repository": "SAM_Revit",
"branch": "sow/2026-Q4",
"head": "431d34ef4bcff8f34e8caa32e893ebfc471cb678",
"status": ""
},
{
"repository": "SAM_Revit_UI",
"branch": "sow/2026-Q4",
"head": "06cf245af93dfd29521bbf925a4b7f3cf934af18",
"status": ""
}
]
Loading
Loading