From 27695a60b479b88fecbe6d4107ac7d1e3fd01e76 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 24 Aug 2026 08:14:49 +0000 Subject: [PATCH 1/5] =?UTF-8?q?chore(deps):=20SqlClient=20=E3=82=92=205.2.?= =?UTF-8?q?3=20=E3=81=B8=E4=B8=8A=E3=81=92=E3=80=81=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E5=81=B4=E3=83=AD=E3=83=83=E3=82=AF=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=A4=E3=83=AB=E3=82=82=E5=90=8C=E6=99=82=E3=81=AB=E5=86=8D?= =?UTF-8?q?=E7=94=9F=E6=88=90=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dependabot の PR #180 は src/IncidentInsight.Web 側の packages.lock.json しか 更新しないため、tests/IncidentInsight.Tests/packages.lock.json が 5.1.7 のまま 取り残され、CI の `dotnet restore --locked-mode` が NU1004 で落ちていた (「The project references incidentinsight.web whose dependencies has changed」)。 ロックファイルは全プロジェクト分を同一変更セットで更新する必要がある。 - src/: Dependabot が生成した csproj / ロックファイルをそのまま採用(5.1.7 → 5.2.3) - tests/: 同じ解決結果を反映。SqlClient 5.2.3 が依存を整理した結果、 SNI.runtime 5.2.0 / ConfigurationManager 8.0.0 / Runtime.Caching 8.0.0 / ProtectedData 8.0.0 へ上がり、Cng は他要求元(IdentityModel.Tokens)の 4.5.0 に落ち着く。要求元を失った 8 パッケージは削除する。 うち System.Runtime.CompilerServices.Unsafe はテスト側だけで消える (web 側は PrivateAssets の EF Design 経由 CodeAnalysis.Common が要求元として残るため)。 - csproj のコメントが旧ピン 5.1.7 を指したままだったので現状に合わせる。 major は据え置き(5 のまま)なので dependabot.yml の保留と EfCorePackageAlignmentTests のメジャー一致・床値検査はいずれも維持される。 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01VWXdwuq13R27U1P6fYwp4i --- .../IncidentInsight.Web.csproj | 10 +- src/IncidentInsight.Web/packages.lock.json | 95 +++++------------- .../IncidentInsight.Tests/packages.lock.json | 99 ++++--------------- 3 files changed, 47 insertions(+), 157 deletions(-) diff --git a/src/IncidentInsight.Web/IncidentInsight.Web.csproj b/src/IncidentInsight.Web/IncidentInsight.Web.csproj index bb6a9904..2a31868f 100644 --- a/src/IncidentInsight.Web/IncidentInsight.Web.csproj +++ b/src/IncidentInsight.Web/IncidentInsight.Web.csproj @@ -19,16 +19,18 @@ - + diff --git a/src/IncidentInsight.Web/packages.lock.json b/src/IncidentInsight.Web/packages.lock.json index ac516021..3720ff75 100644 --- a/src/IncidentInsight.Web/packages.lock.json +++ b/src/IncidentInsight.Web/packages.lock.json @@ -14,23 +14,18 @@ }, "Microsoft.Data.SqlClient": { "type": "Direct", - "requested": "[5.1.7, )", - "resolved": "5.1.7", - "contentHash": "awBwR6pCRyiFqB5z1iu+eMaFmt986JWgaA1+LR+vsdIRBgeBI5X8f3u+ZPnTqlHUwfTugl6ptIObzalWeAPugQ==", + "requested": "[5.2.3, )", + "resolved": "5.2.3", + "contentHash": "KtBQ2ZmPrGwIe8W8n3urW8hyDrPQgyNg7sE7T70zi5+Tdre5r5e7aAYMsJTRIKq8Y+wZB4UAfAAnUlK728xWOg==", "dependencies": { "Azure.Identity": "1.11.4", - "Microsoft.Data.SqlClient.SNI.runtime": "5.1.2", + "Microsoft.Data.SqlClient.SNI.runtime": "5.2.0", "Microsoft.Identity.Client": "4.61.3", "Microsoft.IdentityModel.JsonWebTokens": "6.35.0", "Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.35.0", "Microsoft.SqlServer.Server": "1.0.0", - "System.Configuration.ConfigurationManager": "6.0.1", - "System.Diagnostics.DiagnosticSource": "6.0.1", - "System.Runtime.Caching": "6.0.0", - "System.Security.Cryptography.Cng": "5.0.0", - "System.Security.Principal.Windows": "5.0.0", - "System.Text.Encoding.CodePages": "6.0.0", - "System.Text.Encodings.Web": "6.0.1" + "System.Configuration.ConfigurationManager": "8.0.0", + "System.Runtime.Caching": "8.0.0" } }, "Microsoft.EntityFrameworkCore.Design": { @@ -235,8 +230,8 @@ }, "Microsoft.Data.SqlClient.SNI.runtime": { "type": "Transitive", - "resolved": "5.1.2", - "contentHash": "Q3mjL/oG7rYKDI1D34HLxf0FvhEAwOGzbiDfwv9/HaGP9f9yNV8KeXAS6ehxOaoBNqBRM6sTA19f9XVtf8rvLA==" + "resolved": "5.2.0", + "contentHash": "po1jhvFd+8pbfvJR/puh+fkHi0GRanAdvayh/0e47yaM6CXWZ6opUjCMFuYlAnD2LcbyvQE7fPJKvogmaUcN+w==" }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", @@ -526,11 +521,6 @@ "resolved": "1.0.0", "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==" }, - "Microsoft.Win32.SystemEvents": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "hqTM5628jSsQiv+HGpiq3WKBl2c8v1KZfby2J6Pr7pEPlK9waPdgEO6b8A/+/xn/yZ9ulv8HuqK71ONy2tg67A==" - }, "Mono.TextTemplating": { "type": "Transitive", "resolved": "3.0.0", @@ -646,11 +636,11 @@ }, "System.Configuration.ConfigurationManager": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "jXw9MlUu/kRfEU0WyTptAVueupqIeE3/rl0EZDMlf8pcvJnitQ8HeVEp69rZdaStXwTV72boi/Bhw8lOeO+U2w==", + "resolved": "8.0.0", + "contentHash": "JlYi9XVvIREURRUlGMr1F6vOFLk7YSY4p1vHo4kX3tQ0AGrjqlRWHDi66ImHhy6qwXBG3BJ6Y1QlYQ+Qz6Xgww==", "dependencies": { - "System.Security.Cryptography.ProtectedData": "6.0.0", - "System.Security.Permissions": "6.0.0" + "System.Diagnostics.EventLog": "8.0.0", + "System.Security.Cryptography.ProtectedData": "8.0.0" } }, "System.Diagnostics.DiagnosticSource": { @@ -658,13 +648,10 @@ "resolved": "9.0.19", "contentHash": "UW+FCKPoM15boJwYTsWueNxTgUaqipnZx3z68khaWutVnbtjNpgl9WO7tCcmt7/x3eEYoXn6TZSoHyLwryJUAA==" }, - "System.Drawing.Common": { + "System.Diagnostics.EventLog": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "NfuoKUiP2nUWwKZN6twGqXioIe1zVD0RIj2t976A+czLHr2nY454RwwXs6JU9Htc6mwqL6Dn/nEL3dpVf2jOhg==", - "dependencies": { - "Microsoft.Win32.SystemEvents": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "fdYxcRjQqTTacKId/2IECojlDSFvp7LP5N78+0z/xH7v/Tuw5ZAxu23Y6PTCRinqyu2ePx+Gn1098NC6jM6d+A==" }, "System.Formats.Asn1": { "type": "Transitive", @@ -723,10 +710,10 @@ }, "System.Runtime.Caching": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "E0e03kUp5X2k+UAoVl6efmI7uU7JRBWi5EIdlQ7cr0NpBGjHG4fWII35PgsBY9T4fJQ8E4QPsL0rKksU9gcL5A==", + "resolved": "8.0.0", + "contentHash": "4TmlmvGp4kzZomm7J2HJn6IIx0UUrQyhBDyb5O1XiunZlQImXW+B8b7W/sTPcXhSf9rp5NR5aDtQllwbB5elOQ==", "dependencies": { - "System.Configuration.ConfigurationManager": "6.0.0" + "System.Configuration.ConfigurationManager": "8.0.0" } }, "System.Runtime.CompilerServices.Unsafe": { @@ -734,37 +721,15 @@ "resolved": "6.0.0", "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, - "System.Security.AccessControl": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ==" - }, "System.Security.Cryptography.Cng": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "jIMXsKn94T9JY7PvPq/tMfqa6GAaHpElRDpmG+SuL+D3+sTw2M8VhnibKnN8Tq+4JqbPJ/f+BwtLeDMEnzAvRg==", - "dependencies": { - "System.Formats.Asn1": "5.0.0" - } + "resolved": "4.5.0", + "contentHash": "WG3r7EyjUe9CMPFSs6bty5doUqT+q9pbI80hlNzo2SkPkZ4VTuZkGWjpp77JB8+uaL4DFPRdBsAY+DX3dBK92A==" }, "System.Security.Cryptography.ProtectedData": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "rp1gMNEZpvx9vP0JW0oHLxlf8oSiQgtno77Y4PLUBjSiDYoD77Y8uXHr1Ea5XG4/pIKhqAdxZ8v8OTUtqo9PeQ==" - }, - "System.Security.Permissions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "T/uuc7AklkDoxmcJ7LGkyX1CcSviZuLCa4jg3PekfJ7SU0niF0IVTXwUiNVP9DSpzou2PpxJ+eNY2IfDM90ZCg==", - "dependencies": { - "System.Security.AccessControl": "6.0.0", - "System.Windows.Extensions": "6.0.0" - } - }, - "System.Security.Principal.Windows": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + "resolved": "8.0.0", + "contentHash": "+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg==" }, "System.Text.Encoding": { "type": "Transitive", @@ -776,14 +741,6 @@ "System.Runtime": "4.3.0" } }, - "System.Text.Encoding.CodePages": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.Text.Encodings.Web": { "type": "Transitive", "resolved": "9.0.19", @@ -807,14 +764,6 @@ "type": "Transitive", "resolved": "4.5.4", "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" - }, - "System.Windows.Extensions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "IXoJOXIqc39AIe+CIR7koBtRGMiCt/LPM3lI+PELtDIy9XdyeSrwXFdWV9dzJ2Awl0paLWUaknLxFQ5HpHZUog==", - "dependencies": { - "System.Drawing.Common": "6.0.0" - } } } } diff --git a/tests/IncidentInsight.Tests/packages.lock.json b/tests/IncidentInsight.Tests/packages.lock.json index 8f48cf17..a5316b1c 100644 --- a/tests/IncidentInsight.Tests/packages.lock.json +++ b/tests/IncidentInsight.Tests/packages.lock.json @@ -155,28 +155,23 @@ }, "Microsoft.Data.SqlClient": { "type": "Transitive", - "resolved": "5.1.7", - "contentHash": "awBwR6pCRyiFqB5z1iu+eMaFmt986JWgaA1+LR+vsdIRBgeBI5X8f3u+ZPnTqlHUwfTugl6ptIObzalWeAPugQ==", + "resolved": "5.2.3", + "contentHash": "KtBQ2ZmPrGwIe8W8n3urW8hyDrPQgyNg7sE7T70zi5+Tdre5r5e7aAYMsJTRIKq8Y+wZB4UAfAAnUlK728xWOg==", "dependencies": { "Azure.Identity": "1.11.4", - "Microsoft.Data.SqlClient.SNI.runtime": "5.1.2", + "Microsoft.Data.SqlClient.SNI.runtime": "5.2.0", "Microsoft.Identity.Client": "4.61.3", "Microsoft.IdentityModel.JsonWebTokens": "6.35.0", "Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.35.0", "Microsoft.SqlServer.Server": "1.0.0", - "System.Configuration.ConfigurationManager": "6.0.1", - "System.Diagnostics.DiagnosticSource": "6.0.1", - "System.Runtime.Caching": "6.0.0", - "System.Security.Cryptography.Cng": "5.0.0", - "System.Security.Principal.Windows": "5.0.0", - "System.Text.Encoding.CodePages": "6.0.0", - "System.Text.Encodings.Web": "6.0.1" + "System.Configuration.ConfigurationManager": "8.0.0", + "System.Runtime.Caching": "8.0.0" } }, "Microsoft.Data.SqlClient.SNI.runtime": { "type": "Transitive", - "resolved": "5.1.2", - "contentHash": "Q3mjL/oG7rYKDI1D34HLxf0FvhEAwOGzbiDfwv9/HaGP9f9yNV8KeXAS6ehxOaoBNqBRM6sTA19f9XVtf8rvLA==" + "resolved": "5.2.0", + "contentHash": "po1jhvFd+8pbfvJR/puh+fkHi0GRanAdvayh/0e47yaM6CXWZ6opUjCMFuYlAnD2LcbyvQE7fPJKvogmaUcN+w==" }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", @@ -699,11 +694,6 @@ "Microsoft.TestPlatform.ObjectModel": "18.9.0" } }, - "Microsoft.Win32.SystemEvents": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "hqTM5628jSsQiv+HGpiq3WKBl2c8v1KZfby2J6Pr7pEPlK9waPdgEO6b8A/+/xn/yZ9ulv8HuqK71ONy2tg67A==" - }, "Npgsql": { "type": "Transitive", "resolved": "9.0.3", @@ -763,11 +753,11 @@ }, "System.Configuration.ConfigurationManager": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "jXw9MlUu/kRfEU0WyTptAVueupqIeE3/rl0EZDMlf8pcvJnitQ8HeVEp69rZdaStXwTV72boi/Bhw8lOeO+U2w==", + "resolved": "8.0.0", + "contentHash": "JlYi9XVvIREURRUlGMr1F6vOFLk7YSY4p1vHo4kX3tQ0AGrjqlRWHDi66ImHhy6qwXBG3BJ6Y1QlYQ+Qz6Xgww==", "dependencies": { - "System.Security.Cryptography.ProtectedData": "6.0.0", - "System.Security.Permissions": "6.0.0" + "System.Diagnostics.EventLog": "8.0.0", + "System.Security.Cryptography.ProtectedData": "8.0.0" } }, "System.Diagnostics.DiagnosticSource": { @@ -780,14 +770,6 @@ "resolved": "8.0.1", "contentHash": "n1ZP7NM2Gkn/MgD8+eOT5MulMj6wfeQMNS2Pizvq5GHCZfjlFMXV2irQlQmJhwA2VABC57M0auudO89Iu2uRLg==" }, - "System.Drawing.Common": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "NfuoKUiP2nUWwKZN6twGqXioIe1zVD0RIj2t976A+czLHr2nY454RwwXs6JU9Htc6mwqL6Dn/nEL3dpVf2jOhg==", - "dependencies": { - "Microsoft.Win32.SystemEvents": "6.0.0" - } - }, "System.Formats.Asn1": { "type": "Transitive", "resolved": "9.0.19", @@ -837,48 +819,21 @@ }, "System.Runtime.Caching": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "E0e03kUp5X2k+UAoVl6efmI7uU7JRBWi5EIdlQ7cr0NpBGjHG4fWII35PgsBY9T4fJQ8E4QPsL0rKksU9gcL5A==", + "resolved": "8.0.0", + "contentHash": "4TmlmvGp4kzZomm7J2HJn6IIx0UUrQyhBDyb5O1XiunZlQImXW+B8b7W/sTPcXhSf9rp5NR5aDtQllwbB5elOQ==", "dependencies": { - "System.Configuration.ConfigurationManager": "6.0.0" + "System.Configuration.ConfigurationManager": "8.0.0" } }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Security.AccessControl": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ==" - }, "System.Security.Cryptography.Cng": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "jIMXsKn94T9JY7PvPq/tMfqa6GAaHpElRDpmG+SuL+D3+sTw2M8VhnibKnN8Tq+4JqbPJ/f+BwtLeDMEnzAvRg==", - "dependencies": { - "System.Formats.Asn1": "5.0.0" - } + "resolved": "4.5.0", + "contentHash": "WG3r7EyjUe9CMPFSs6bty5doUqT+q9pbI80hlNzo2SkPkZ4VTuZkGWjpp77JB8+uaL4DFPRdBsAY+DX3dBK92A==" }, "System.Security.Cryptography.ProtectedData": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "rp1gMNEZpvx9vP0JW0oHLxlf8oSiQgtno77Y4PLUBjSiDYoD77Y8uXHr1Ea5XG4/pIKhqAdxZ8v8OTUtqo9PeQ==" - }, - "System.Security.Permissions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "T/uuc7AklkDoxmcJ7LGkyX1CcSviZuLCa4jg3PekfJ7SU0niF0IVTXwUiNVP9DSpzou2PpxJ+eNY2IfDM90ZCg==", - "dependencies": { - "System.Security.AccessControl": "6.0.0", - "System.Windows.Extensions": "6.0.0" - } - }, - "System.Security.Principal.Windows": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + "resolved": "8.0.0", + "contentHash": "+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg==" }, "System.Text.Encoding": { "type": "Transitive", @@ -890,14 +845,6 @@ "System.Runtime": "4.3.0" } }, - "System.Text.Encoding.CodePages": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.Text.Encodings.Web": { "type": "Transitive", "resolved": "9.0.19", @@ -917,14 +864,6 @@ "resolved": "4.5.4", "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" }, - "System.Windows.Extensions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "IXoJOXIqc39AIe+CIR7koBtRGMiCt/LPM3lI+PELtDIy9XdyeSrwXFdWV9dzJ2Awl0paLWUaknLxFQ5HpHZUog==", - "dependencies": { - "System.Drawing.Common": "6.0.0" - } - }, "xunit.abstractions": { "type": "Transitive", "resolved": "2.0.3", @@ -969,7 +908,7 @@ "type": "Project", "dependencies": { "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "[8.0.30, )", - "Microsoft.Data.SqlClient": "[5.1.7, )", + "Microsoft.Data.SqlClient": "[5.2.3, )", "Microsoft.EntityFrameworkCore.SqlServer": "[9.0.19, )", "Microsoft.EntityFrameworkCore.Sqlite": "[9.0.19, )", "Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore": "[8.0.30, )", From 79bc85e5b2b3b0b40b92ac0c5a8b232b98286cf9 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 24 Aug 2026 08:26:10 +0000 Subject: [PATCH 2/5] =?UTF-8?q?docs(deps):=20SqlClient=20=E3=83=94?= =?UTF-8?q?=E3=83=B3=E3=81=AE=E6=A4=9C=E5=87=BA=E7=B6=B2=E3=81=AE=E9=99=90?= =?UTF-8?q?=E7=95=8C=E3=81=A8=E3=80=81=E3=83=AD=E3=83=83=E3=82=AF=E3=83=95?= =?UTF-8?q?=E3=82=A1=E3=82=A4=E3=83=AB=E5=86=8D=E7=94=9F=E6=88=90=E3=81=AE?= =?UTF-8?q?=E6=89=8B=E9=A0=86=E3=82=92=E6=9B=B8=E3=81=8D=E6=AE=8B=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /code-review ultra の指摘 3 件に対応する(コメントとドキュメントのみ、依存関係の変更なし)。 1. 床値検査の過大表現: EfCorePackageAlignmentTests の「床値の後退」検査が比べる相手は EF Core の宣言(5.1.6)であって、いま書いてあるピンの値ではない。つまりピンを 5.1.6 まで 下げる/丸ごと消す変更はテストを素通りする。5.1.7 のときは高々 1 パッチ分だった穴が、 5.2.3 で 1 マイナー分に広がるため、限界を csproj に明記する。 (期待値をテストに書かない方針とのトレードオフなので、テスト側は変更しない) 2. 手順が記録されていない: Dependabot の nuget PR は src/ 側のロックファイルしか更新せず、 毎回 NU1004 で赤くなる。今回手作業で直した内容が CLAUDE.md にも csproj にも 残っていなかったため、§3 に不変条件として追記し csproj から参照する。 3. 誤った説明: 「Relational は上位版を採用する」は Relational から SqlClient への依存辺が 無く機構の説明として誤り。実際に 5.2.3 が選ばれるのは EF Core SqlServer の 5.1.6 要求と 直接参照の [5.2.3, ) を NuGet が突き合わせる highest-wins のため。正しい機構に直す。 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01VWXdwuq13R27U1P6fYwp4i --- CLAUDE.md | 1 + .../IncidentInsight.Web.csproj | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 48082b17..a418ae67 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -113,6 +113,7 @@ catch (DbUpdateConcurrencyException) { TempData["Warning"] = "..."; return ...; ### 注意点(固有の不変条件) - **マイグレーションは起動時 `Database.Migrate()` で自動適用** — モデル変更は同一変更セットでマイグレーションを追加する。 +- **ロックファイルは全プロジェクト分を同時に再生成する** — `packages.lock.json` は `src/IncidentInsight.Web` と `tests/IncidentInsight.Tests` の 2 つある。テストプロジェクトは web を `ProjectReference` で参照しているため、**web の依存が動くとテスト側の解決結果も動く**。片方だけ更新すると CI の `dotnet restore --locked-mode` が `NU1004: The project references incidentinsight.web whose dependencies has changed` で落ちる。**とくに Dependabot の nuget PR は `src/` 側しか更新しないので、そのままではマージできない**(実例: PR #180 → #181 で手当て)。取り込むときは `dotnet restore` を流して 2 ファイルとも再生成し、同一コミットに含める。ロックファイルを持つプロジェクトを増やしたときも同じ(`RestorePackagesWithLockFile` を宣言し忘れたプロジェクトは locked-mode restore も `EfCorePackageAlignmentTests` の検査もすり抜けるため、同テストが宣言漏れ自体を検出する)。 - **`HasAtLeastOneValidMeasure` をバイパスしない**(インシデントは予防策が最低 1 件ないと登録不可)。 - **重症度/部署/インシデント種別の enum は `Incident` クラス上**(DB ではない)。値追加は static 辞書/配列+それを回す View を更新(マイグレーション不要)。enum の日本語ラベルと Bootstrap カラーは `Models/Enums/EnumLabels.cs` に集約し、`EnumLabels.Japanese()` と `EnumLabels.Color()` の両方を更新。`IncidentTypeMapping.cs` が日本語↔DB 文字列の双方向変換を担う。 - **再発ロジックは `IRecurrenceService`**(`Services/RecurrenceService.cs`)に集約。`HomeController.Index`(90 日窓 `FindRecurrenceAlertsAsync`)と `IncidentsController.Details`(時間無制限 `FindRecurrencesForIncidentAsync`)が委譲。マッチャ `RecurrenceDetector.FindSimilar` は純粋関数。ルール変更時はサービスとそのテストを更新。 diff --git a/src/IncidentInsight.Web/IncidentInsight.Web.csproj b/src/IncidentInsight.Web/IncidentInsight.Web.csproj index 2a31868f..7bbee22e 100644 --- a/src/IncidentInsight.Web/IncidentInsight.Web.csproj +++ b/src/IncidentInsight.Web/IncidentInsight.Web.csproj @@ -23,13 +23,22 @@ オンプレ SQL Server 配備が本番ターゲットのため、セキュリティ更新のバージョンを 後退させないよう直接依存として床値をピン留めしている。床値そのもの(現在 5.2.3、 付随する SNI.runtime は 5.2.0)は Dependabot の minor / patch 更新で引き上げていく - ——止めているのは下の理由で major だけ。 - EF Core 側の 5.1.6 要求とは互換で、Relational は上位版を採用する。 + ——止めているのは下の理由で major だけ。ただし Dependabot は + src/IncidentInsight.Web/packages.lock.json しか更新せず、tests 側のロックファイルが + 取り残されて CI が NU1004 で落ちる。取り込む際は必ず 2 つを同時に再生成すること + (CLAUDE.md §3「ロックファイルは全プロジェクト分を同時に再生成する」)。 + 実際に 5.2.3 が採用されるのは、EF Core SqlServer の 5.1.6 要求とこの直接参照の + [5.2.3, ) 要求を NuGet が突き合わせて上位を採る(highest-wins)ため。 【メジャー版は EF Core の SqlServer プロバイダに合わせる】このドライバはプロバイダの 実体で、動作保証があるのはプロバイダが宣言したメジャー版に対してだけ。ここだけを 別メジャーへ上げても、テストは InMemory / SQLite しか触らないため全て緑のまま通り、 SQL Server 配備でだけ実行時に壊れる。dependabot.yml の ignore が major 更新を保留し、 - EfCorePackageAlignmentTests がメジャー一致と床値の後退を検出する。 --> + EfCorePackageAlignmentTests がメジャー一致と床値の後退を検出する。 + 【検出網の限界】その床値検査が比べる相手は EF Core の宣言(現在 5.1.6)であって、 + いま書いてあるピンの値ではない。つまりこのピンを 5.1.6 まで下げる/丸ごと消す変更は + 検査を素通りする(テストは緑のまま、SQL Server 配備だけが 1 マイナー分古いドライバに + 戻る)。期待値をテストに書かない方針(CLAUDE.md §3)と引き換えの穴なので、 + この行を下げる・消す変更は必ずレビューで理由を確認すること。 --> From aa93b6cae8d551f65dc1b6d968c044fdb6269c42 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 24 Aug 2026 08:34:23 +0000 Subject: [PATCH 3/5] =?UTF-8?q?test(packaging):=20SqlClient=20=E3=81=AE?= =?UTF-8?q?=E5=BA=8A=E5=80=A4=E3=83=94=E3=83=B3=E3=81=8C=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E5=8F=82=E7=85=A7=E3=81=AE=E3=81=BE=E3=81=BE=E6=AE=8B=E3=82=8B?= =?UTF-8?q?=E3=81=93=E3=81=A8=E3=82=92=E6=A9=9F=E6=A2=B0=E6=A4=9C=E6=9F=BB?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2 巡目の /code-review ultra の指摘 3 件に対応する。 1 巡目で「床値検査の比較相手は EF Core の宣言(5.1.6)であってピンの値ではない」という 穴を csproj のコメントに書き残したが、このリポジトリの流儀は穴を文章で残さず機械化する ことなので、書ける範囲を検査に落とす。 - SqlClientPin_StaysADirectReference を追加。ロックファイルの type が Direct のまま であることだけを見る。ピンを「冗長だから」と削除すると解決版は EF Core の宣言どおり 5.1.6 まで落ちるが、5.1.6 >= 5.1.6 で既存の床値検査は素通りしてしまう。type を見れば 削除も推移依存への差し戻しも捕まえられ、期待する版をテストに書かずに済むので 「期待値は宣言側から読む」方針とも両立する。 - CLAUDE.md §3 が「床値の後退を検出する」と無条件に書いていて csproj のコメントと 矛盾していたため、検出できる 5 つと、残る穴(値を 5.1.6 ちょうどまで下げる変更)を 明記する。 - Dependabot / NU1004 の手順を csproj と CLAUDE.md の両方に全文で書いていたのを CLAUDE.md 側に一本化し、csproj からは参照だけにする(DRY)。 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01VWXdwuq13R27U1P6fYwp4i --- CLAUDE.md | 2 +- .../IncidentInsight.Web.csproj | 18 ++++---- .../Packaging/EfCorePackageAlignmentTests.cs | 41 +++++++++++++++++++ 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index a418ae67..f507ac64 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -121,7 +121,7 @@ catch (DbUpdateConcurrencyException) { TempData["Warning"] = "..."; return ...; - **PHI を `AuditLog.ChangesJson` に平文で残さない** — 自由記述・個人名カラムには `[Sensitive(Mask.Redact)]` か `[Sensitive(Mask.Hash)]`(`Models/Auditing/SensitiveAttribute.cs`)を付与。インターセプタが `[REDACTED]` か HMAC-SHA256 擬似匿名化(鍵 = `Audit:HashSalt`)に置換。**新しい自由記述/個人名カラムは必ず annotate**。本番で `Audit:HashSalt` 空は起動失敗。salt ローテーションは過去ハッシュとの相関を失うので runbook に記録。 - **時刻は常に `IClock`**(`Services/IClock.cs`、JST)。`DateTime.Now/Today/UtcNow` を直接呼ばない。永続業務タイムスタンプと `AuditLog.ChangedAt` は運用ローカル時刻(JST)で保存。新規 View では `@inject IClock Clock` + `Clock.Today` を使う。 - **EF Core 本体とプロバイダ実装はメジャー版を全プロジェクトで揃える** — プロバイダ実装(Sqlite / SqlServer / Npgsql)は EF Core の内部 API に結び付くため、本体と同じメジャー版でしか動作保証がない。一方 `Microsoft.AspNetCore.Identity.EntityFrameworkCore` と `Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore` は公開 API しか使わず、メジャー版は .NET のリリース(`net8.0` → `net9.0`)に追随する(9.0.0 は `net9.0` 専用)ため、この規則の対象外にしている(束ねると EF Core 9 への更新が復元不能になる)。一部だけ上げてもビルドもテストも通る(テストは InMemory / SQLite しか触らない)ため、**PostgreSQL 配備でだけ実行時に壊れる**。Dependabot は `.github/dependabot.yml` の `nuget-ef-core`(通常の版更新)と `nuget-ef-core-security`(セキュリティ更新。`applies-to` の既定は version-updates なので別グループが要る)が major も含めて 1 本の PR に束ね、版ズレ自体は `EfCorePackageAlignmentTests` が検出する。検査は `packages.lock.json` の解決済み版を読む(壊れる主役は推移依存の `Relational` で、どの csproj にも直接書かれていないため)。「EF Core 系とは何か」の定義は dependabot.yml の `patterns` が唯一の真実の源で、名前に `EntityFrameworkCore` **または** `EFCore`(`EFCore.NamingConventions` 等)を含むパッケージがパターンから漏れていないことも同テストが固定する(上記 .NET リリース列の 2 つだけは理由付きで明示除外)。**プロバイダを足すときは 3 箇所(上記 2 グループの `patterns` と `nuget-minor-and-patch.exclude-patterns`)すべてに反映する。** 2 グループの `patterns` は集合として一致している必要があるが、`exclude-patterns` 側は書き方を問わず「EF Core 系を 1 つも拾わないこと」だけを求める。判定は文字列の一致ではなく、解決済みのパッケージ ID と `patterns` が表す範囲の代表値の両方が拾われないことで行う(`patterns` 側が前方一致 `"Foo*"` のときは、除外もその前置詞で始まるものをまるごと覆う必要があり、`"Foo"` と `"Foo.*"` のようにドット区切りの形を列挙する書き方では `FooBar` 等の同族が漏れるため足りない。完全一致のパターンは 1 つの ID しか指さないので、その ID を覆えていれば足りる)。そのため`"Npgsql*"` のように広く書くのは可、逆に `"Microsoft.EntityFrameworkCore*"` から末尾 `*` を落とすと(`.Sqlite` / `.SqlServer` / `.Relational` など既存のプロバイダ実装がその時点で漏れるため)通らない。広げすぎて上記 .NET リリース列の 2 つまで minor / patch の束ねから外すのも同テストが検出する。 -- **SQL Server の ADO.NET ドライバ(`Microsoft.Data.SqlClient`)のメジャー版は EF Core の SqlServer プロバイダに合わせる** — このドライバは `Microsoft.EntityFrameworkCore.SqlServer` の実体で、動作保証があるのはプロバイダが宣言したメジャー版に対してだけ。ところが名前に `EntityFrameworkCore` / `EFCore` を含まないため上記の `nuget-ef-core` グループには入らず、メジャー更新が単独 PR として現れる(実際 PR #178 が 5.1.7 → 7.0.2 で出ていた)。**壊れ方は EF Core 本体とプロバイダの版ズレとまったく同じ**で、テストは InMemory / SQLite しか触らないため**ビルドも全テストも緑のまま通り、SQL Server 配備でだけ実行時に壊れる**。`.github/dependabot.yml` の nuget エコシステムの `ignore` が major 更新だけを保留し(minor / patch は csproj の「床値」を上げる更新なので止めない)、`EfCorePackageAlignmentTests` が 4 つを検出する: 解決版と EF Core 宣言版のメジャー不一致 / 床値の後退 / 保留の消失 / 保留の効きすぎ(`update-types` の欠落・`versions` の追加・重複エントリ)。**期待値はテストに書かず EF Core の宣言側(`packages.lock.json` の `dependencies`)から読む**ので、EF Core を上げれば検査が自動で追随する。ドライバのメジャーを上げたいときは、それを宣言する版の `Microsoft.EntityFrameworkCore.SqlServer` へ同じ変更セットで上げる。 +- **SQL Server の ADO.NET ドライバ(`Microsoft.Data.SqlClient`)のメジャー版は EF Core の SqlServer プロバイダに合わせる** — このドライバは `Microsoft.EntityFrameworkCore.SqlServer` の実体で、動作保証があるのはプロバイダが宣言したメジャー版に対してだけ。ところが名前に `EntityFrameworkCore` / `EFCore` を含まないため上記の `nuget-ef-core` グループには入らず、メジャー更新が単独 PR として現れる(実際 PR #178 が 5.1.7 → 7.0.2 で出ていた)。**壊れ方は EF Core 本体とプロバイダの版ズレとまったく同じ**で、テストは InMemory / SQLite しか触らないため**ビルドも全テストも緑のまま通り、SQL Server 配備でだけ実行時に壊れる**。`.github/dependabot.yml` の nuget エコシステムの `ignore` が major 更新だけを保留し(minor / patch は csproj の「床値」を上げる更新なので止めない)、`EfCorePackageAlignmentTests` が 5 つを検出する: 解決版と EF Core 宣言版のメジャー不一致 / 床値の後退 / **ピンが直接参照でなくなること**(`packages.lock.json` の `type` が `Direct` から外れる = csproj のピン削除や推移依存への差し戻し)/ 保留の消失 / 保留の効きすぎ(`update-types` の欠落・`versions` の追加・重複エントリ)。**期待値はテストに書かず EF Core の宣言側(`packages.lock.json` の `dependencies`)から読む**ので、EF Core を上げれば検査が自動で追随する。**ただし「床値の後退」の比較相手はあくまで EF Core の宣言(現在 5.1.6)であって、csproj に書かれたピンの値ではない**——ピンを削除した場合は上記の `Direct` 検査が捕まえるが、**ピンを EF Core の宣言版ちょうどまで下げる変更だけは 5 つすべてを素通りする**(期待値を書かない方針と引き換えに残している穴なので、ピンの値を下げる差分はレビューで理由を確認する)。ドライバのメジャーを上げたいときは、それを宣言する版の `Microsoft.EntityFrameworkCore.SqlServer` へ同じ変更セットで上げる。 - **`SameDepartmentHandler`(`Authorization/SameDepartmentHandler.cs`)は `Incident` の eager-load が前提**。`PreventiveMeasure` / `CauseAnalysis` で `AuthorizeAsync` する前に `.Include(x => x.Incident)`。**fail-closed**(`Incident` が null なら拒否)。 ### 新規 POST アクション追加時のチェックリスト diff --git a/src/IncidentInsight.Web/IncidentInsight.Web.csproj b/src/IncidentInsight.Web/IncidentInsight.Web.csproj index 7bbee22e..a98d45ed 100644 --- a/src/IncidentInsight.Web/IncidentInsight.Web.csproj +++ b/src/IncidentInsight.Web/IncidentInsight.Web.csproj @@ -23,22 +23,20 @@ オンプレ SQL Server 配備が本番ターゲットのため、セキュリティ更新のバージョンを 後退させないよう直接依存として床値をピン留めしている。床値そのもの(現在 5.2.3、 付随する SNI.runtime は 5.2.0)は Dependabot の minor / patch 更新で引き上げていく - ——止めているのは下の理由で major だけ。ただし Dependabot は - src/IncidentInsight.Web/packages.lock.json しか更新せず、tests 側のロックファイルが - 取り残されて CI が NU1004 で落ちる。取り込む際は必ず 2 つを同時に再生成すること - (CLAUDE.md §3「ロックファイルは全プロジェクト分を同時に再生成する」)。 + ——止めているのは下の理由で major だけ。その更新 PR をそのままマージすると CI が + NU1004 で落ちる(手順は CLAUDE.md §3「ロックファイルは全プロジェクト分を同時に再生成する」)。 実際に 5.2.3 が採用されるのは、EF Core SqlServer の 5.1.6 要求とこの直接参照の - [5.2.3, ) 要求を NuGet が突き合わせて上位を採る(highest-wins)ため。 + [5.2.3, ) 要求を NuGet が突き合わせて上位を採るため。 【メジャー版は EF Core の SqlServer プロバイダに合わせる】このドライバはプロバイダの 実体で、動作保証があるのはプロバイダが宣言したメジャー版に対してだけ。ここだけを 別メジャーへ上げても、テストは InMemory / SQLite しか触らないため全て緑のまま通り、 SQL Server 配備でだけ実行時に壊れる。dependabot.yml の ignore が major 更新を保留し、 EfCorePackageAlignmentTests がメジャー一致と床値の後退を検出する。 - 【検出網の限界】その床値検査が比べる相手は EF Core の宣言(現在 5.1.6)であって、 - いま書いてあるピンの値ではない。つまりこのピンを 5.1.6 まで下げる/丸ごと消す変更は - 検査を素通りする(テストは緑のまま、SQL Server 配備だけが 1 マイナー分古いドライバに - 戻る)。期待値をテストに書かない方針(CLAUDE.md §3)と引き換えの穴なので、 - この行を下げる・消す変更は必ずレビューで理由を確認すること。 --> + 【この行を消さない】床値検査が比べる相手は EF Core の宣言(現在 5.1.6)であって + ここに書かれた値ではないため、この行を「冗長だから」と消しても床値検査は素通りする + (解決版が 5.1.6 まで落ちても 5.1.6 >= 5.1.6 は成立する)。削除だけは + SqlClientPin_StaysADirectReference がロックファイルの type で捕まえる。 + 残る穴は「値を 5.1.6 ちょうどまで下げる」変更だけで、これはレビューでしか止まらない。 --> diff --git a/tests/IncidentInsight.Tests/Packaging/EfCorePackageAlignmentTests.cs b/tests/IncidentInsight.Tests/Packaging/EfCorePackageAlignmentTests.cs index fb7f219f..8a5f6f6a 100644 --- a/tests/IncidentInsight.Tests/Packaging/EfCorePackageAlignmentTests.cs +++ b/tests/IncidentInsight.Tests/Packaging/EfCorePackageAlignmentTests.cs @@ -170,6 +170,10 @@ public class EfCorePackageAlignmentTests // ロックファイルで、実際に解決された版を表す JSON キー private const string ResolvedKey = "resolved"; + // ロックファイルの type が「csproj に直接書かれた参照」を表すときの値 + // (推移依存なら "Transitive"、ProjectReference なら "Project" になる) + private const string DirectPackageKind = "Direct"; + // Dependabot 設定ファイルのリポジトリルートからの位置 private static readonly string DependabotConfigPath = Path.Combine(".github", "dependabot.yml"); @@ -574,6 +578,43 @@ public void SqlClientPin_StaysWithinEfCoreDeclaredMajor() + "セキュリティ更新を後退させない床値として置かれています。下げないでください。"); } + [Fact] + public void SqlClientPin_StaysADirectReference() + { + // 全ロックファイルから、このドライバの記録(どのプロジェクトが・直接か推移か)を集める + var entries = ResolvedPackages.Value + .Where(package => string.Equals(package.Id, SqlClientPackageId, StringComparison.OrdinalIgnoreCase)) + .ToList(); + + // 1 件も無いのは、参照そのものが消えたか検出網が劣化したかのどちらか(fail-closed) + Assert.True(entries.Count > 0, + $"{LockFileName} に {SqlClientPackageId} の記録がありません。参照が外れた可能性があります。"); + + // csproj に直接書かれた参照として記録しているプロジェクトを取り出す + var direct = entries + .Where(package => string.Equals(package.Kind, DirectPackageKind, StringComparison.OrdinalIgnoreCase)) + .ToList(); + + // 【なぜ「直接参照であること」まで固定するのか】 + // 上の SqlClientPin_StaysWithinEfCoreDeclaredMajor が床値の比較相手にするのは + // EF Core SqlServer の宣言(現在 5.1.6)であって、csproj に書かれたピンの値ではない。 + // そのためピンを「冗長だから」と削除すると、解決版が EF Core の宣言どおりに落ちても + // 5.1.6 >= 5.1.6 で床値検査を素通りし、ビルドもテストも緑のまま SQL Server 配備だけが + // 古いドライバに戻る(このリポジトリで最も起きやすい形の無言の後退)。 + // ここで type が Direct であることを見ておけば、削除も「推移依存に戻す」リファクタも + // 検出できる。期待する版をテストに書かずに済むので、期待値を宣言側から読む方針とも両立する + Assert.True(direct.Count > 0, + $"{SqlClientPackageId} が {LockFileName} のどこにも {DirectPackageKind} として記録されていません。" + + "csproj の直接参照(床値のピン)が外れた可能性があります。\n" + + "このピンは、EF Core SqlServer が推移的に引く版が古いサービシングパッチであるために" + + "置かれています。外すと解決版はその古い版まで落ちますが、床値検査の比較相手が" + + "EF Core の宣言そのものであるため検査は素通りし、SQL Server 配備でだけ古いドライバが" + + "読み込まれます。\n" + + "意図して外すのであれば、EF Core SqlServer の宣言版が十分新しくなったことを確認し、" + + "csproj のコメントと本テストを同じ変更セットで畳んでください。\n" + + "現在の記録:\n" + Describe(entries)); + } + [Fact] public void DependabotConfig_HoldsSqlClientMajorUpdates() { From 7000eb86fb607c9694863b96c7e980a016f63df2 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 24 Aug 2026 08:42:07 +0000 Subject: [PATCH 4/5] =?UTF-8?q?refactor(packaging):=20=E3=83=AD=E3=83=83?= =?UTF-8?q?=E3=82=AF=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E7=85=A7=E5=90=88?= =?UTF-8?q?=E3=82=92=E5=85=B1=E9=80=9A=E5=8C=96=E3=81=97=E3=80=81=E6=AE=8B?= =?UTF-8?q?=E3=82=8B=E7=A9=B4=E3=81=AE=E7=AF=84=E5=9B=B2=E3=82=92=E6=AD=A3?= =?UTF-8?q?=E7=A2=BA=E3=81=AB=E6=9B=B8=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 3 巡目の /code-review ultra の指摘 2 件に対応する。 - 残る穴の記述が狭すぎた。床値検査は resolved >= EF Core の宣言(5.1.6)を見るので、 素通りするのは「5.1.6 ちょうど」だけでなく 5.1.6 以上・現在のピン未満の範囲すべて。 たとえば今回の更新を revert して 5.1.7 に戻す差分は 5.1.7 >= 5.1.6 が成立して全て緑で 通り、5.2.x のサービシングパッチを静かに失う。この書き方だとレビュアーが「5.1.6 に なっている差分」しか警戒せず 5.1.7 を素通しさせるため、csproj と CLAUDE.md の両方を 正確な範囲に直す。 - ID でロックファイルの記録を集める処理が ReadResolvedVersion と新テストに重複していた ので LockEntriesFor へ切り出す(このファイルが LockFilePathOf で既に踏んでいる前例と 同じ理由。照合規則を片方だけ直して他方が静かに意味を変えるのを防ぐ)。 YAML 側の ReadIgnoreEntriesFor と役割が紛れないよう Lock を冠した名前にする。 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01VWXdwuq13R27U1P6fYwp4i --- CLAUDE.md | 2 +- .../IncidentInsight.Web.csproj | 4 +++- .../Packaging/EfCorePackageAlignmentTests.cs | 17 ++++++++++++----- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f507ac64..d5156b29 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -121,7 +121,7 @@ catch (DbUpdateConcurrencyException) { TempData["Warning"] = "..."; return ...; - **PHI を `AuditLog.ChangesJson` に平文で残さない** — 自由記述・個人名カラムには `[Sensitive(Mask.Redact)]` か `[Sensitive(Mask.Hash)]`(`Models/Auditing/SensitiveAttribute.cs`)を付与。インターセプタが `[REDACTED]` か HMAC-SHA256 擬似匿名化(鍵 = `Audit:HashSalt`)に置換。**新しい自由記述/個人名カラムは必ず annotate**。本番で `Audit:HashSalt` 空は起動失敗。salt ローテーションは過去ハッシュとの相関を失うので runbook に記録。 - **時刻は常に `IClock`**(`Services/IClock.cs`、JST)。`DateTime.Now/Today/UtcNow` を直接呼ばない。永続業務タイムスタンプと `AuditLog.ChangedAt` は運用ローカル時刻(JST)で保存。新規 View では `@inject IClock Clock` + `Clock.Today` を使う。 - **EF Core 本体とプロバイダ実装はメジャー版を全プロジェクトで揃える** — プロバイダ実装(Sqlite / SqlServer / Npgsql)は EF Core の内部 API に結び付くため、本体と同じメジャー版でしか動作保証がない。一方 `Microsoft.AspNetCore.Identity.EntityFrameworkCore` と `Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore` は公開 API しか使わず、メジャー版は .NET のリリース(`net8.0` → `net9.0`)に追随する(9.0.0 は `net9.0` 専用)ため、この規則の対象外にしている(束ねると EF Core 9 への更新が復元不能になる)。一部だけ上げてもビルドもテストも通る(テストは InMemory / SQLite しか触らない)ため、**PostgreSQL 配備でだけ実行時に壊れる**。Dependabot は `.github/dependabot.yml` の `nuget-ef-core`(通常の版更新)と `nuget-ef-core-security`(セキュリティ更新。`applies-to` の既定は version-updates なので別グループが要る)が major も含めて 1 本の PR に束ね、版ズレ自体は `EfCorePackageAlignmentTests` が検出する。検査は `packages.lock.json` の解決済み版を読む(壊れる主役は推移依存の `Relational` で、どの csproj にも直接書かれていないため)。「EF Core 系とは何か」の定義は dependabot.yml の `patterns` が唯一の真実の源で、名前に `EntityFrameworkCore` **または** `EFCore`(`EFCore.NamingConventions` 等)を含むパッケージがパターンから漏れていないことも同テストが固定する(上記 .NET リリース列の 2 つだけは理由付きで明示除外)。**プロバイダを足すときは 3 箇所(上記 2 グループの `patterns` と `nuget-minor-and-patch.exclude-patterns`)すべてに反映する。** 2 グループの `patterns` は集合として一致している必要があるが、`exclude-patterns` 側は書き方を問わず「EF Core 系を 1 つも拾わないこと」だけを求める。判定は文字列の一致ではなく、解決済みのパッケージ ID と `patterns` が表す範囲の代表値の両方が拾われないことで行う(`patterns` 側が前方一致 `"Foo*"` のときは、除外もその前置詞で始まるものをまるごと覆う必要があり、`"Foo"` と `"Foo.*"` のようにドット区切りの形を列挙する書き方では `FooBar` 等の同族が漏れるため足りない。完全一致のパターンは 1 つの ID しか指さないので、その ID を覆えていれば足りる)。そのため`"Npgsql*"` のように広く書くのは可、逆に `"Microsoft.EntityFrameworkCore*"` から末尾 `*` を落とすと(`.Sqlite` / `.SqlServer` / `.Relational` など既存のプロバイダ実装がその時点で漏れるため)通らない。広げすぎて上記 .NET リリース列の 2 つまで minor / patch の束ねから外すのも同テストが検出する。 -- **SQL Server の ADO.NET ドライバ(`Microsoft.Data.SqlClient`)のメジャー版は EF Core の SqlServer プロバイダに合わせる** — このドライバは `Microsoft.EntityFrameworkCore.SqlServer` の実体で、動作保証があるのはプロバイダが宣言したメジャー版に対してだけ。ところが名前に `EntityFrameworkCore` / `EFCore` を含まないため上記の `nuget-ef-core` グループには入らず、メジャー更新が単独 PR として現れる(実際 PR #178 が 5.1.7 → 7.0.2 で出ていた)。**壊れ方は EF Core 本体とプロバイダの版ズレとまったく同じ**で、テストは InMemory / SQLite しか触らないため**ビルドも全テストも緑のまま通り、SQL Server 配備でだけ実行時に壊れる**。`.github/dependabot.yml` の nuget エコシステムの `ignore` が major 更新だけを保留し(minor / patch は csproj の「床値」を上げる更新なので止めない)、`EfCorePackageAlignmentTests` が 5 つを検出する: 解決版と EF Core 宣言版のメジャー不一致 / 床値の後退 / **ピンが直接参照でなくなること**(`packages.lock.json` の `type` が `Direct` から外れる = csproj のピン削除や推移依存への差し戻し)/ 保留の消失 / 保留の効きすぎ(`update-types` の欠落・`versions` の追加・重複エントリ)。**期待値はテストに書かず EF Core の宣言側(`packages.lock.json` の `dependencies`)から読む**ので、EF Core を上げれば検査が自動で追随する。**ただし「床値の後退」の比較相手はあくまで EF Core の宣言(現在 5.1.6)であって、csproj に書かれたピンの値ではない**——ピンを削除した場合は上記の `Direct` 検査が捕まえるが、**ピンを EF Core の宣言版ちょうどまで下げる変更だけは 5 つすべてを素通りする**(期待値を書かない方針と引き換えに残している穴なので、ピンの値を下げる差分はレビューで理由を確認する)。ドライバのメジャーを上げたいときは、それを宣言する版の `Microsoft.EntityFrameworkCore.SqlServer` へ同じ変更セットで上げる。 +- **SQL Server の ADO.NET ドライバ(`Microsoft.Data.SqlClient`)のメジャー版は EF Core の SqlServer プロバイダに合わせる** — このドライバは `Microsoft.EntityFrameworkCore.SqlServer` の実体で、動作保証があるのはプロバイダが宣言したメジャー版に対してだけ。ところが名前に `EntityFrameworkCore` / `EFCore` を含まないため上記の `nuget-ef-core` グループには入らず、メジャー更新が単独 PR として現れる(実際 PR #178 が 5.1.7 → 7.0.2 で出ていた)。**壊れ方は EF Core 本体とプロバイダの版ズレとまったく同じ**で、テストは InMemory / SQLite しか触らないため**ビルドも全テストも緑のまま通り、SQL Server 配備でだけ実行時に壊れる**。`.github/dependabot.yml` の nuget エコシステムの `ignore` が major 更新だけを保留し(minor / patch は csproj の「床値」を上げる更新なので止めない)、`EfCorePackageAlignmentTests` が 5 つを検出する: 解決版と EF Core 宣言版のメジャー不一致 / 床値の後退 / **ピンが直接参照でなくなること**(`packages.lock.json` の `type` が `Direct` から外れる = csproj のピン削除や推移依存への差し戻し)/ 保留の消失 / 保留の効きすぎ(`update-types` の欠落・`versions` の追加・重複エントリ)。**期待値はテストに書かず EF Core の宣言側(`packages.lock.json` の `dependencies`)から読む**ので、EF Core を上げれば検査が自動で追随する。**ただし「床値の後退」の比較相手はあくまで EF Core の宣言(現在 5.1.6)であって、csproj に書かれたピンの値ではない**——ピンを削除した場合は上記の `Direct` 検査が捕まえるが、**ピンの値を「EF Core の宣言版以上・現在のピン未満」の範囲(今なら 5.1.6 以上 5.2.3 未満、たとえば Dependabot の更新を revert して 5.1.7 に戻す変更)へ下げると 5 つすべてを素通りする**。床値検査が比べるのは EF Core の宣言であって直前のピンではないため、`5.1.7 >= 5.1.6` が成立してしまう。期待値を書かない方針と引き換えに残している穴なので、**ピンの値が下がる差分は(下げ幅の大小によらず)レビューで理由を確認する**。ドライバのメジャーを上げたいときは、それを宣言する版の `Microsoft.EntityFrameworkCore.SqlServer` へ同じ変更セットで上げる。 - **`SameDepartmentHandler`(`Authorization/SameDepartmentHandler.cs`)は `Incident` の eager-load が前提**。`PreventiveMeasure` / `CauseAnalysis` で `AuthorizeAsync` する前に `.Include(x => x.Incident)`。**fail-closed**(`Incident` が null なら拒否)。 ### 新規 POST アクション追加時のチェックリスト diff --git a/src/IncidentInsight.Web/IncidentInsight.Web.csproj b/src/IncidentInsight.Web/IncidentInsight.Web.csproj index a98d45ed..ec2a1da9 100644 --- a/src/IncidentInsight.Web/IncidentInsight.Web.csproj +++ b/src/IncidentInsight.Web/IncidentInsight.Web.csproj @@ -36,7 +36,9 @@ ここに書かれた値ではないため、この行を「冗長だから」と消しても床値検査は素通りする (解決版が 5.1.6 まで落ちても 5.1.6 >= 5.1.6 は成立する)。削除だけは SqlClientPin_StaysADirectReference がロックファイルの type で捕まえる。 - 残る穴は「値を 5.1.6 ちょうどまで下げる」変更だけで、これはレビューでしか止まらない。 --> + 残る穴は「この値を 5.1.6 以上・現在のピン未満へ下げる」変更(例: この更新を revert して + 5.1.7 に戻す)で、5.1.7 >= 5.1.6 が成立するため検査は全て緑のまま通る。 + 下げ幅の大小によらずレビューでしか止まらない。 --> diff --git a/tests/IncidentInsight.Tests/Packaging/EfCorePackageAlignmentTests.cs b/tests/IncidentInsight.Tests/Packaging/EfCorePackageAlignmentTests.cs index 8a5f6f6a..cf7f08ca 100644 --- a/tests/IncidentInsight.Tests/Packaging/EfCorePackageAlignmentTests.cs +++ b/tests/IncidentInsight.Tests/Packaging/EfCorePackageAlignmentTests.cs @@ -582,9 +582,7 @@ public void SqlClientPin_StaysWithinEfCoreDeclaredMajor() public void SqlClientPin_StaysADirectReference() { // 全ロックファイルから、このドライバの記録(どのプロジェクトが・直接か推移か)を集める - var entries = ResolvedPackages.Value - .Where(package => string.Equals(package.Id, SqlClientPackageId, StringComparison.OrdinalIgnoreCase)) - .ToList(); + var entries = LockEntriesFor(SqlClientPackageId); // 1 件も無いのは、参照そのものが消えたか検出網が劣化したかのどちらか(fail-closed) Assert.True(entries.Count > 0, @@ -1007,8 +1005,7 @@ private static string ReadDeclaredDependencyVersion(string dependentId, string d private static string ReadResolvedVersion(string packageId) { // 全ロックファイルから、その ID で解決されている版を集める - var versions = ResolvedPackages.Value - .Where(package => string.Equals(package.Id, packageId, StringComparison.OrdinalIgnoreCase)) + var versions = LockEntriesFor(packageId) .Select(package => package.Version) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); @@ -1024,6 +1021,16 @@ private static string ReadResolvedVersion(string packageId) return versions[0]; } + // 指定パッケージが全ロックファイルでどう記録されているかを集める。 + // 【なぜ切り出すか】「ID で照合して該当行を集める」は解決版の読み出しと + // 直接参照の検査の 2 箇所で必要になる。照合規則(大文字小文字を無視する等)を + // 書き写すと、片方だけ直したときにもう片方の検査が静かに意味を変える(§6 DRY) + private static IReadOnlyList LockEntriesFor(string packageId) => + // ID が一致する行だけを残して一覧にする + ResolvedPackages.Value + .Where(package => string.Equals(package.Id, packageId, StringComparison.OrdinalIgnoreCase)) + .ToList(); + // 版文字列からメジャー版(最初のドットまで)を取り出す private static int MajorOf(string version) { From c4437fca6d96f66c73f1857ddabc65a022fc76b0 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 24 Aug 2026 08:49:21 +0000 Subject: [PATCH 5/5] =?UTF-8?q?docs(deps):=20Cng=20=E3=81=AE=E7=89=88?= =?UTF-8?q?=E3=81=8C=E4=B8=8B=E3=81=8C=E3=82=8B=E4=BB=B6=E3=82=92=E6=98=8E?= =?UTF-8?q?=E8=A8=98=E3=81=97=E3=80=81=E6=A4=9C=E5=87=BA=E7=B6=B2=E3=81=AE?= =?UTF-8?q?=E4=B8=80=E8=A6=A7=E3=82=92=20CLAUDE.md=20=E3=81=AB=E4=B8=80?= =?UTF-8?q?=E6=9C=AC=E5=8C=96=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 4 巡目の /code-review ultra の指摘のうち 2 件に対応する(コメントのみ)。 - この更新で唯一「版が下がる」System.Security.Cryptography.Cng 5.0.0 → 4.5.0 を csproj に明記する。SqlClient 5.2.3 が要求をやめ、残る要求元 IdentityModel.Tokens の 4.5.0 に落ちたもの。net8.0 では共有フレームワーク側が優先されるため実行時の意味は無いが、 「版を後退させない」ことが主題の変更で唯一の後退なので、見落としでなく確認済みだと分かるようにする。 - dependabot.yml に検出項目を書き写していた行が、SqlClientPin_StaysADirectReference を 足したときに 1 件少ないまま取り残されていた。同じ一覧が 3 箇所に散っているのが原因なので、 一覧は CLAUDE.md §3 を唯一の参照元とし、dependabot.yml からは参照だけにする。 見送り: MajorOf と MajorVersionOf の重複(本 PR 以前からある実装で、依存更新とは無関係な リファクタになるため。§6「変更は最小スコープに保つ」)。 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01VWXdwuq13R27U1P6fYwp4i --- .github/dependabot.yml | 6 ++++-- src/IncidentInsight.Web/IncidentInsight.Web.csproj | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6805e4bd..12e74705 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -98,8 +98,10 @@ updates: # Microsoft.EntityFrameworkCore.SqlServer へ同じ変更セットで上げる # (この ignore は残したままでよい)。 # - # 解決版と EF Core の宣言版のメジャー一致・床値の後退・この保留の消失や効きすぎは、 - # いずれも EfCorePackageAlignmentTests が機械的に検出する。 + # この保留と対になる検査は EfCorePackageAlignmentTests にある。 + # 【何を検出し、何が漏れるか】は CLAUDE.md §3 の同名の節を唯一の一覧とする + # (ここに書き写すと検査を足したとき片方だけ古くなる。実際 SqlClientPin_StaysADirectReference + # を足したときに、この行が 1 件少ないまま取り残された)。 - dependency-name: "Microsoft.Data.SqlClient" update-types: ["version-update:semver-major"] diff --git a/src/IncidentInsight.Web/IncidentInsight.Web.csproj b/src/IncidentInsight.Web/IncidentInsight.Web.csproj index ec2a1da9..1c4cc00b 100644 --- a/src/IncidentInsight.Web/IncidentInsight.Web.csproj +++ b/src/IncidentInsight.Web/IncidentInsight.Web.csproj @@ -27,6 +27,10 @@ NU1004 で落ちる(手順は CLAUDE.md §3「ロックファイルは全プロジェクト分を同時に再生成する」)。 実際に 5.2.3 が採用されるのは、EF Core SqlServer の 5.1.6 要求とこの直接参照の [5.2.3, ) 要求を NuGet が突き合わせて上位を採るため。 + 【5.2.3 で 1 件だけ版が下がる】5.1.7 が要求していた System.Security.Cryptography.Cng + 5.0.0 は 5.2.3 では要求されなくなり、残る要求元(Microsoft.IdentityModel.Tokens 6.35.0) + の 4.5.0 に落ちる。net8.0 では共有フレームワーク側の実装が優先されるためこのパッケージ版に + 実行時の意味は無く、確認のうえ許容している(この床値ピンが守る対象はドライバ本体)。 【メジャー版は EF Core の SqlServer プロバイダに合わせる】このドライバはプロバイダの 実体で、動作保証があるのはプロバイダが宣言したメジャー版に対してだけ。ここだけを 別メジャーへ上げても、テストは InMemory / SQLite しか触らないため全て緑のまま通り、