Skip to content

Commit 9343e99

Browse files
committed
test(architecture): refuse ignored MSBuild user hooks
Do not treat .user files as inert machine artifacts: the SDK can import .csproj.user targets after the evaluation-only source census. Keep those formats refused and plant ignored-file negative controls. Record the second independent review counterexample without changing production behavior or expanding supported build shapes.
1 parent d16d2d7 commit 9343e99

5 files changed

Lines changed: 27 additions & 5 deletions

File tree

‎backend/tests/LearnStack.Tests.Architecture/DomainGenericityTests.DemoIdentities.cs‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,13 @@ public void Demo_Literal_Fence_Detects_Decoded_Identities_And_Admits_Platform_Da
125125
IsDemoLocalArtifact(local).Should().BeTrue();
126126
OmitIgnoredDemoLocalFile(local).Should().BeTrue("the actual Git rules omit known machine files");
127127
}
128-
foreach (var name in new[] { ".env.hidden.cs", ".env.hidden.json", "hidden.resx" })
128+
foreach (var name in new[] { ".env.hidden.cs", ".env.hidden.json", "hidden.resx", "Probe.csproj.user" })
129129
IsDemoLocalArtifact(Path.Combine(project, name)).Should().BeFalse("ignored source/unknown formats are not machine-file exemptions");
130+
// MSBuild automatically imports .csproj.user, so ignore rules cannot exempt it.
131+
OmitIgnoredDemoLocalFile(Path.Combine(RepositoryPaths.BackendSrc(), "LearnStack.Api", "Probe.csproj.user"))
132+
.Should().BeFalse();
133+
Action ignoredBuildHook = () => _ = BackendDemoSourceKind(Path.Combine(project, "Probe.csproj.user"), generated);
134+
ignoredBuildHook.Should().Throw<InvalidOperationException>();
130135
Action unknownFormat = () => _ = BackendDemoSourceKind(Path.Combine(project, "Probe.resx"), generated);
131136
unknownFormat.Should().Throw<InvalidOperationException>();
132137
Action linkedInput = () => RequireCompileCoverage([Path.Combine(project, "Probe.cs")], ["/repo/shared/Linked.cs"]);

‎backend/tests/LearnStack.Tests.Architecture/DomainGenericityTests.SourceCensus.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ private static string[] DemoProductionSourceCensus()
3333
private static bool IsDemoLocalArtifact(string path) => Path.GetFileName(path) is
3434
".env" or ".env.local" or ".env.development" or ".env.test" or ".env.production"
3535
or ".env.development.local" or ".env.test.local" or ".env.production.local" or ".DS_Store" or "Thumbs.db"
36-
|| Path.GetExtension(path) is ".swp" or ".log" or ".user" or ".suo" or ".rsuser";
36+
|| Path.GetExtension(path) is ".swp" or ".log" or ".suo" or ".rsuser";
3737

3838
private static bool OmitIgnoredDemoLocalFile(string path)
3939
{

‎docs/roadmap/phase-02d-walking-skeleton.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6440,7 +6440,11 @@ source `4541d63`, including 2,964 backend and 1,537 frontend cases, the real
64406440
ignored `.env.local` and OS/editor files reached raw filesystem classification.
64416441
The follow-up admits only explicitly known machine filenames that Git also
64426442
ignores. Ignored source files, unknown formats and compiler coverage retain
6443-
fail-closed behavior. Focused reviews and replacement final-head CI are pending.
6443+
fail-closed behavior. The first independent round approved `d16d2d7`. The fresh
6444+
second round reproduced an automatically imported `.csproj.user` build hook;
6445+
`.user` is therefore excluded from the machine-file exemption, with explicit
6446+
refusal controls. The same reviewer must verify this fix before closeout.
6447+
Replacement final-head CI remains required.
64446448

64456449
**Other finding dispositions.** The gzip adapter equality checks wiring; the
64466450
independent Script observer checks source-window coverage, not a second byte

‎frontend/apps/web/src/test/demo-genericity-sources.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ export function omitIgnoredDemoLocalFile(
137137
(environment.has(name) ||
138138
name === '.DS_Store' ||
139139
name === 'Thumbs.db' ||
140-
/\.(?:swp|log|user|suo|rsuser)$/.test(name)) &&
140+
/\.(?:swp|log|suo|rsuser)$/.test(name)) &&
141141
ignored(path)
142142
);
143143
}

‎frontend/apps/web/src/test/demo-genericity.test.ts‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,21 @@ describe('P02d-7 exact demo identity boundary', () => {
286286
});
287287
expect(result.status).toBe(0);
288288
}
289-
for (const name of ['.env.hidden.ts', '.env.hidden.json', 'hidden.vue', 'hidden.resx'])
289+
for (const name of [
290+
'.env.hidden.ts',
291+
'.env.hidden.json',
292+
'hidden.vue',
293+
'hidden.resx',
294+
'Probe.csproj.user',
295+
])
290296
expect(omitIgnoredDemoLocalFile(`frontend/apps/web/${name}`, () => true)).toBe(false);
297+
// MSBuild automatically imports this ignored format; it is not inert machine data.
298+
expect(
299+
spawnSync('git', ['check-ignore', '--quiet', '--', 'frontend/apps/web/Probe.csproj.user'], {
300+
cwd: root,
301+
stdio: 'ignore',
302+
}).status,
303+
).toBe(0);
291304
expect(() => demoSourceClassification('frontend/apps/web/hidden.vue')).toThrow('Unclassified');
292305
});
293306
it('refuses unclassified production roots and formats while retaining declared non-runtime ownership', () => {

0 commit comments

Comments
 (0)