From 9c324f8939432a6c69a62ecd8bdd1eb28f0f901f Mon Sep 17 00:00:00 2001 From: wenjiefan Date: Tue, 18 Aug 2026 11:45:04 +0200 Subject: [PATCH] Add vetted PR reviewer self-improvement gold Add the 14 adjudicated second-batch concepts and repair the bounded checkpoint false-positive guard. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- dataset/codereview.jsonl | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/dataset/codereview.jsonl b/dataset/codereview.jsonl index 7da2cdda6..436ea8b4e 100644 --- a/dataset/codereview.jsonl +++ b/dataset/codereview.jsonl @@ -122,9 +122,23 @@ {"repo":"microsoft/BCApps","instance_id":"synthetic__breaking-access-modifier-01","base_commit":"72d9d6dd801f26fa953fa5258e42774808857b03","created_at":"2026-08-05T00:00:00Z","environment_setup_version":"29.0","project_paths":[],"metadata":{"area":"breaking-changes"},"patch":"diff --git a/src/DemoVariantNormalizer/DemoVariantNormalizer.Codeunit.al b/src/DemoVariantNormalizer/DemoVariantNormalizer.Codeunit.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/DemoVariantNormalizer/DemoVariantNormalizer.Codeunit.al\n@@ -0,0 +1,19 @@\n+codeunit 50210 \"Demo Variant Normalizer\"\n+{\n+ procedure NormalizeAlternatives(var AlternativeList: List of [Code[10]])\n+ begin\n+ TrimAlternatives(AlternativeList);\n+ end;\n+\n+ internal procedure TrimAlternatives(var AlternativeList: List of [Code[10]])\n+ var\n+ VariantCode: Code[10];\n+ Result: List of [Code[10]];\n+ begin\n+ foreach VariantCode in AlternativeList do\n+ if VariantCode <> '' then\n+ Result.Add(VariantCode);\n+\n+ AlternativeList := Result;\n+ end;\n+}\ndiff --git a/src/DemoVariantNormalizer/app.json b/src/DemoVariantNormalizer/app.json\nnew file mode 100644\n--- /dev/null\n+++ b/src/DemoVariantNormalizer/app.json\n@@ -0,0 +1,17 @@\n+{\n+ \"id\": \"c4d5e6f7-8091-4a3b-c4d5-6e7f80912345\",\n+ \"name\": \"Demo Variant Normalizer\",\n+ \"publisher\": \"Contoso\",\n+ \"version\": \"1.0.0.0\",\n+ \"brief\": \"Demo variant list normalizer.\",\n+ \"description\": \"Demonstrates an internal procedure that is only ever called from within the same object.\",\n+ \"platform\": \"1.0.0.0\",\n+ \"application\": \"29.0.0.0\",\n+ \"idRanges\": [\n+ {\n+ \"from\": 50210,\n+ \"to\": 50219\n+ }\n+ ],\n+ \"runtime\": \"15.0\"\n+}\n","expected_comments":[{"file":"src/DemoVariantNormalizer/DemoVariantNormalizer.Codeunit.al","line_start":8,"line_end":8,"severity":"medium","domain":"breaking-changes","body":"TrimAlternatives is declared internal even though it is only called from within the same object (NormalizeAlternatives). That widens the app's supported/exported surface unnecessarily; keep single-object helpers local unless another object genuinely needs to reuse them (BCApps PR 9364, discussion r3676578767)."}],"category":"code-review","description":"True positive: a helper procedure is declared internal but is only ever invoked from another procedure in the same codeunit. Mirrors BCApps PR 9364's SOAItemSearch.Codeunit.al NormalizeVariantAlternatives finding (accepted, discussion r3676578767): choose access modifiers deliberately -- an internal declaration for a same-object-only helper needlessly expands the app's exported surface and should be local.","expect_findings":true,"source":"vsoadmin"} {"repo":"microsoft/BCApps","instance_id":"synthetic__testing-tolerance-clean-01","base_commit":"70fd0246a0a4dbc72cb183ca719106722c03be4d","created_at":"2026-08-06T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"testing"},"patch":"diff --git a/src/DemoCFDI/DemoCFDIDateAssert.Codeunit.al b/src/DemoCFDI/DemoCFDIDateAssert.Codeunit.al\nnew file mode 100644\nindex 0000000..a1b2c3d\n--- /dev/null\n+++ b/src/DemoCFDI/DemoCFDIDateAssert.Codeunit.al\n@@ -0,0 +1,42 @@\n+codeunit 50201 \"Demo CFDI Date Assert\"\n+{\n+ Subtype = Test;\n+\n+ var\n+ Assert: Codeunit Assert;\n+ DateAssertionLbl: Label '%1. Expected: %2, Actual: %3 (difference: %4 days, tolerance: +/-1 day for timezone shifts)', Comment = '%1 = Error message, %2 = Expected date, %3 = Actual date, %4 = Days difference', Locked = true;\n+\n+ [Test]\n+ procedure StampDateAndPaymentDateAllowTimezoneTolerance()\n+ var\n+ StampDate: Date;\n+ PastWorkDate: Date;\n+ FechaValue: Text;\n+ FechaPagoValue: Text;\n+ begin\n+ StampDate := Today();\n+ PastWorkDate := CalcDate('<-30D>', StampDate);\n+\n+ FechaValue := FormatDateText(StampDate);\n+ FechaPagoValue := FormatDateText(PastWorkDate);\n+\n+ AssertDateWithinOneDayTolerance(StampDate, FechaValue, 'Comprobante/@Fecha date portion must be close to the stamp request date');\n+ AssertDateWithinOneDayTolerance(PastWorkDate, FechaPagoValue, 'FechaPago date portion must be close to the payment posting date');\n+\n+ Assert.AreNotEqual(CopyStr(FechaValue, 1, 10), CopyStr(FechaPagoValue, 1, 10), 'Comprobante/@Fecha and FechaPago must still differ');\n+ end;\n+\n+ local procedure FormatDateText(InputDate: Date): Text\n+ begin\n+ exit(Format(InputDate, 0, '--'));\n+ end;\n+\n+ local procedure AssertDateWithinOneDayTolerance(ExpectedDate: Date; ActualDateText: Text; ErrorMessage: Text)\n+ var\n+ DaysDiff: Integer;\n+ ActualDate: Date;\n+ begin\n+ Evaluate(ActualDate, CopyStr(ActualDateText, 1, 10), 9);\n+ DaysDiff := ActualDate - ExpectedDate;\n+ Assert.IsTrue(Abs(DaysDiff) <= 1, StrSubstNo(DateAssertionLbl, ErrorMessage, Format(ExpectedDate), CopyStr(ActualDateText, 1, 10), DaysDiff));\n+ end;\n+}\n","expected_comments":[],"category":"code-review","description":"Negative example: each serialized XML date is checked against its own expected date with a documented +/-1 day tolerance for timezone conversion. A separate assertion only verifies that the stamp and payment dates differ; it does not assert an exact 30-day gap. Flagging the bounded tolerance alone as masking an off-by-one bug is a false positive without concrete evidence that the accepted timezone variance is broader than intended (BCApps PR 9697, discussion r3701136019).","expect_findings":false,"source":"vsoadmin"} {"repo":"microsoft/BCApps","instance_id":"synthetic__breaking-notification-callback-01","base_commit":"b8b74184bba49dec7b0346b693331e32dd31508f","created_at":"2026-08-14T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"breaking-changes"},"patch":"diff --git a/src/SyntheticNotifMgmt.Codeunit.al b/src/SyntheticNotifMgmt.Codeunit.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SyntheticNotifMgmt.Codeunit.al\n@@ -0,0 +1,20 @@\n+codeunit 50130 \"Synthetic Notif Mgmt\"\n+{\n+ internal procedure ShowMissingLocationNotification(VendorNo: Code[20])\n+ var\n+ MissingLocationNotification: Notification;\n+ begin\n+ MissingLocationNotification.Message := 'The vendor does not have a location code assigned.';\n+ MissingLocationNotification.SetData('VendorNo', VendorNo);\n+ MissingLocationNotification.AddAction('Open vendor card', Codeunit::\"Synthetic Notif Mgmt\", 'OpenVendorCard');\n+ MissingLocationNotification.Send();\n+ end;\n+\n+ procedure OpenVendorCard(MissingLocationNotification: Notification)\n+ var\n+ Vendor: Record Vendor;\n+ begin\n+ if Vendor.Get(MissingLocationNotification.GetData('VendorNo')) then\n+ Page.Run(Page::\"Vendor Card\", Vendor);\n+ end;\n+}\n","expected_comments":[{"file":"src/SyntheticNotifMgmt.Codeunit.al","line_start":13,"line_end":13,"severity":"medium","domain":"breaking-changes","body":"OpenVendorCard is declared as a public procedure even though it is only used in-app as a Notification action callback, which unnecessarily publishes a new API surface that dependent extensions could bind to. Make it internal unless it is meant to be a stable external contract."}],"category":"code-review","description":"Accepted feedback: a Notification action callback (OpenVendorCard) is declared as a bare public procedure even though it is only ever invoked in-app via Notification.AddAction, unnecessarily publishing a new API surface that dependent extensions could bind to. Per choose-access-modifiers-deliberately, notification callbacks with no external caller should default to internal. Expect a flagged finding on the callback procedure (BCApps PR 9787).","expect_findings":true,"source":"vsoadmin"} -{"repo":"microsoft/BCApps","instance_id":"synthetic__perf-batched-commit-checkpoint-01","base_commit":"69c31df3c9bea1d4b0c27d2ef8bc4201953d5a95","created_at":"2026-08-17T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"performance","image_count":null,"persona":null},"patch":"diff --git a/src/SynthArchiveSourceBuffer.Table.al b/src/SynthArchiveSourceBuffer.Table.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SynthArchiveSourceBuffer.Table.al\n@@ -0,0 +1,24 @@\n+table 50173 \"Synth Archive Source Buffer\"\n+{\n+ DataClassification = CustomerContent;\n+\n+ fields\n+ {\n+ field(1; \"Entry No.\"; Integer)\n+ {\n+ Caption = 'Entry No.';\n+ }\n+ field(2; \"Amount\"; Decimal)\n+ {\n+ Caption = 'Amount';\n+ }\n+ }\n+\n+ keys\n+ {\n+ key(PK; \"Entry No.\")\n+ {\n+ Clustered = true;\n+ }\n+ }\n+}\ndiff --git a/src/SynthArchiveDestEntry.Table.al b/src/SynthArchiveDestEntry.Table.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SynthArchiveDestEntry.Table.al\n@@ -0,0 +1,24 @@\n+table 50174 \"Synth Archive Dest Entry\"\n+{\n+ DataClassification = CustomerContent;\n+\n+ fields\n+ {\n+ field(1; \"Entry No.\"; Integer)\n+ {\n+ Caption = 'Entry No.';\n+ }\n+ field(2; \"Amount\"; Decimal)\n+ {\n+ Caption = 'Amount';\n+ }\n+ }\n+\n+ keys\n+ {\n+ key(PK; \"Entry No.\")\n+ {\n+ Clustered = true;\n+ }\n+ }\n+}\ndiff --git a/src/SynthArchiveWatermark.Table.al b/src/SynthArchiveWatermark.Table.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SynthArchiveWatermark.Table.al\n@@ -0,0 +1,24 @@\n+table 50175 \"Synth Archive Watermark\"\n+{\n+ DataClassification = SystemMetadata;\n+\n+ fields\n+ {\n+ field(1; \"Primary Key\"; Code[10])\n+ {\n+ Caption = 'Primary Key';\n+ }\n+ field(2; \"Last Archived Entry No.\"; Integer)\n+ {\n+ Caption = 'Last Archived Entry No.';\n+ }\n+ }\n+\n+ keys\n+ {\n+ key(PK; \"Primary Key\")\n+ {\n+ Clustered = true;\n+ }\n+ }\n+}\ndiff --git a/src/SynthArchiveCopier.Codeunit.al b/src/SynthArchiveCopier.Codeunit.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SynthArchiveCopier.Codeunit.al\n@@ -0,0 +1,55 @@\n+codeunit 50176 \"Synth Archive Copier\"\n+{\n+ procedure TransferDataInBatches()\n+ var\n+ SynthArchiveSourceBuffer: Record \"Synth Archive Source Buffer\";\n+ SynthArchiveDestEntry: Record \"Synth Archive Dest Entry\";\n+ LastArchivedEntryNo: Integer;\n+ BatchCount: Integer;\n+ begin\n+ LastArchivedEntryNo := GetLastArchivedEntryNo();\n+\n+ SynthArchiveSourceBuffer.SetFilter(\"Entry No.\", '>%1', LastArchivedEntryNo);\n+ if not SynthArchiveSourceBuffer.FindSet() then\n+ exit;\n+\n+ repeat\n+ SynthArchiveDestEntry.Init();\n+ SynthArchiveDestEntry.TransferFields(SynthArchiveSourceBuffer);\n+ SynthArchiveDestEntry.Insert();\n+\n+ LastArchivedEntryNo := SynthArchiveSourceBuffer.\"Entry No.\";\n+ BatchCount += 1;\n+\n+ if BatchCount >= 10000 then begin\n+ SetLastArchivedEntryNo(LastArchivedEntryNo);\n+ Commit();\n+ BatchCount := 0;\n+ end;\n+ until SynthArchiveSourceBuffer.Next() = 0;\n+\n+ SetLastArchivedEntryNo(LastArchivedEntryNo);\n+ end;\n+\n+ local procedure GetLastArchivedEntryNo(): Integer\n+ var\n+ SynthArchiveWatermark: Record \"Synth Archive Watermark\";\n+ begin\n+ if SynthArchiveWatermark.Get('') then\n+ exit(SynthArchiveWatermark.\"Last Archived Entry No.\");\n+ exit(0);\n+ end;\n+\n+ local procedure SetLastArchivedEntryNo(EntryNo: Integer)\n+ var\n+ SynthArchiveWatermark: Record \"Synth Archive Watermark\";\n+ begin\n+ if not SynthArchiveWatermark.Get('') then begin\n+ SynthArchiveWatermark.Init();\n+ SynthArchiveWatermark.\"Primary Key\" := '';\n+ SynthArchiveWatermark.Insert();\n+ end;\n+ SynthArchiveWatermark.\"Last Archived Entry No.\" := EntryNo;\n+ SynthArchiveWatermark.Modify();\n+ end;\n+}\n","expected_comments":[],"category":"code-review","description":"False-positive guard: a one-time archive loop commits at a bounded batch size only after persisting a resume watermark in the same transaction, and its initial filter resumes strictly after that watermark. This is deliberate checkpointed batching, not an incidental Commit inside an unbounded loop (BCApps PR 9663, recurring rejected findings across customer, vendor, and item migrators).","expect_findings":false,"source":"vsoadmin"} +{"repo":"microsoft/BCApps","instance_id":"synthetic__perf-batched-commit-checkpoint-01","base_commit":"69c31df3c9bea1d4b0c27d2ef8bc4201953d5a95","created_at":"2026-08-17T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"performance","image_count":null,"persona":null},"patch":"diff --git a/src/SynthArchiveSourceBuffer.Table.al b/src/SynthArchiveSourceBuffer.Table.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SynthArchiveSourceBuffer.Table.al\n@@ -0,0 +1,25 @@\n+table 50173 \"Synth Archive Source Buffer\"\n+{\n+ DataClassification = CustomerContent;\n+\n+ fields\n+ {\n+ field(1; \"Entry No.\"; Integer)\n+ {\n+ AutoIncrement = true;\n+ Caption = 'Entry No.';\n+ }\n+ field(2; Amount; Decimal)\n+ {\n+ Caption = 'Amount';\n+ }\n+ }\n+\n+ keys\n+ {\n+ key(PK; \"Entry No.\")\n+ {\n+ Clustered = true;\n+ }\n+ }\n+}\ndiff --git a/src/SynthArchiveDestEntry.Table.al b/src/SynthArchiveDestEntry.Table.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SynthArchiveDestEntry.Table.al\n@@ -0,0 +1,24 @@\n+table 50174 \"Synth Archive Dest Entry\"\n+{\n+ DataClassification = CustomerContent;\n+\n+ fields\n+ {\n+ field(1; \"Entry No.\"; Integer)\n+ {\n+ Caption = 'Entry No.';\n+ }\n+ field(2; Amount; Decimal)\n+ {\n+ Caption = 'Amount';\n+ }\n+ }\n+\n+ keys\n+ {\n+ key(PK; \"Entry No.\")\n+ {\n+ Clustered = true;\n+ }\n+ }\n+}\ndiff --git a/src/SynthArchiveWatermark.Table.al b/src/SynthArchiveWatermark.Table.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SynthArchiveWatermark.Table.al\n@@ -0,0 +1,24 @@\n+table 50175 \"Synth Archive Watermark\"\n+{\n+ DataClassification = SystemMetadata;\n+\n+ fields\n+ {\n+ field(1; Code; Code[10])\n+ {\n+ Caption = 'Code';\n+ }\n+ field(2; \"Last Archived Entry No.\"; Integer)\n+ {\n+ Caption = 'Last Archived Entry No.';\n+ }\n+ }\n+\n+ keys\n+ {\n+ key(PK; Code)\n+ {\n+ Clustered = true;\n+ }\n+ }\n+}\ndiff --git a/src/SynthArchiveSourceChunk.Query.al b/src/SynthArchiveSourceChunk.Query.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SynthArchiveSourceChunk.Query.al\n@@ -0,0 +1,15 @@\n+query 50176 \"Synth Archive Source Chunk\"\n+{\n+ QueryType = Normal;\n+ OrderBy = ascending(EntryNo);\n+\n+ elements\n+ {\n+ dataitem(Source; \"Synth Archive Source Buffer\")\n+ {\n+ column(EntryNo; \"Entry No.\")\n+ {\n+ }\n+ }\n+ }\n+}\ndiff --git a/src/SynthArchiveCopier.Codeunit.al b/src/SynthArchiveCopier.Codeunit.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SynthArchiveCopier.Codeunit.al\n@@ -0,0 +1,58 @@\n+codeunit 50177 \"Synth Archive Copier\"\n+{\n+ procedure TransferDataInBatches()\n+ var\n+ ArchiveWatermark: Record \"Synth Archive Watermark\";\n+ LastArchivedEntryNo: Integer;\n+ begin\n+ if not ArchiveWatermark.Get('ARCHIVE') then begin\n+ ArchiveWatermark.Init();\n+ ArchiveWatermark.Code := 'ARCHIVE';\n+ ArchiveWatermark.Insert();\n+ end;\n+ LastArchivedEntryNo := ArchiveWatermark.\"Last Archived Entry No.\";\n+\n+ while TransferNextBatch(LastArchivedEntryNo) do begin\n+ ArchiveWatermark.\"Last Archived Entry No.\" := LastArchivedEntryNo;\n+ ArchiveWatermark.Modify();\n+ Commit();\n+ end;\n+ end;\n+\n+ local procedure TransferNextBatch(var LastArchivedEntryNo: Integer): Boolean\n+ var\n+ ArchiveSource: Record \"Synth Archive Source Buffer\";\n+ ArchiveDestination: Record \"Synth Archive Dest Entry\";\n+ TempArchiveKey: Record \"Synth Archive Source Buffer\" temporary;\n+ ArchiveSourceChunk: Query \"Synth Archive Source Chunk\";\n+ LastSelectedEntryNo: Integer;\n+ begin\n+ ArchiveSourceChunk.TopNumberOfRows(1000);\n+ if LastArchivedEntryNo <> 0 then\n+ ArchiveSourceChunk.SetFilter(EntryNo, '>%1', LastArchivedEntryNo);\n+ ArchiveSourceChunk.Open();\n+ while ArchiveSourceChunk.Read() do begin\n+ TempArchiveKey.Init();\n+ TempArchiveKey.\"Entry No.\" := ArchiveSourceChunk.EntryNo;\n+ TempArchiveKey.Insert();\n+ LastSelectedEntryNo := ArchiveSourceChunk.EntryNo;\n+ end;\n+ ArchiveSourceChunk.Close();\n+\n+ if TempArchiveKey.IsEmpty() then\n+ exit(false);\n+\n+ ArchiveSource.LockTable();\n+ ArchiveDestination.LockTable();\n+ TempArchiveKey.FindSet();\n+ repeat\n+ ArchiveSource.Get(TempArchiveKey.\"Entry No.\");\n+ ArchiveDestination.Init();\n+ ArchiveDestination.TransferFields(ArchiveSource);\n+ ArchiveDestination.Insert();\n+ until TempArchiveKey.Next() = 0;\n+\n+ LastArchivedEntryNo := LastSelectedEntryNo;\n+ exit(true);\n+ end;\n+}\n","expected_comments":[],"category":"code-review","description":"False-positive guard: an outer batch loop retrieves exactly the next 1000 ordered keys with TopNumberOfRows, processes only that temporary key buffer, persists the completed watermark in the same transaction, and commits only after the whole chunk succeeds. Source keys are auto-incrementing, retries strictly exclude committed work, and errors propagate without recording partial progress.","expect_findings":false,"source":"vsoadmin"} {"repo":"microsoft/BCApps","instance_id":"synthetic__upgrade-transferfields-field-collision-01","base_commit":"69c31df3c9bea1d4b0c27d2ef8bc4201953d5a95","created_at":"2026-08-17T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"upgrade"},"patch":"diff --git a/src/SampleItemLedgerEntryBuf.Table.al b/src/SampleItemLedgerEntryBuf.Table.al\nnew file mode 100644\nindex 0000000..3679dcd\n--- /dev/null\n+++ b/src/SampleItemLedgerEntryBuf.Table.al\n@@ -0,0 +1,25 @@\n+table 50141 \"Sample Item Ledger Entry Buf\"\n+{\n+ Caption = 'Sample Item Ledger Entry Buffer';\n+ DataClassification = CustomerContent;\n+\n+ fields\n+ {\n+ field(1; \"Entry No.\"; Integer)\n+ {\n+ Caption = 'Entry No.';\n+ }\n+ field(100; \"Journal Batch Name\"; Code[10])\n+ {\n+ Caption = 'Journal Batch Name';\n+ }\n+ }\n+\n+ keys\n+ {\n+ key(PK; \"Entry No.\")\n+ {\n+ Clustered = true;\n+ }\n+ }\n+}\ndiff --git a/src/SampleOldItemLedgEntry.Table.al b/src/SampleOldItemLedgEntry.Table.al\nnew file mode 100644\nindex 0000000..d85f2f0\n--- /dev/null\n+++ b/src/SampleOldItemLedgEntry.Table.al\n@@ -0,0 +1,25 @@\n+table 50142 \"Sample Old Item Ledg. Entry\"\n+{\n+ Caption = 'Sample Old Item Ledger Entry';\n+ DataClassification = CustomerContent;\n+\n+ fields\n+ {\n+ field(1; \"Entry No.\"; Integer)\n+ {\n+ Caption = 'Entry No.';\n+ }\n+ field(100; \"Cost Amount (Actual)\"; Decimal)\n+ {\n+ Caption = 'Cost Amount (Actual)';\n+ }\n+ }\n+\n+ keys\n+ {\n+ key(PK; \"Entry No.\")\n+ {\n+ Clustered = true;\n+ }\n+ }\n+}\ndiff --git a/src/SampleOldItemLedgerMigr.Codeunit.al b/src/SampleOldItemLedgerMigr.Codeunit.al\nnew file mode 100644\nindex 0000000..d935407\n--- /dev/null\n+++ b/src/SampleOldItemLedgerMigr.Codeunit.al\n@@ -0,0 +1,14 @@\n+codeunit 50143 \"Sample Old Item Ledger Migr.\"\n+{\n+ procedure TransferDataInBatches()\n+ var\n+ SourceItemLedgerEntry: Record \"Sample Item Ledger Entry Buf\";\n+ OldItemLedgEntry: Record \"Sample Old Item Ledg. Entry\";\n+ begin\n+ if SourceItemLedgerEntry.FindSet() then\n+ repeat\n+ OldItemLedgEntry.TransferFields(SourceItemLedgerEntry, true);\n+ OldItemLedgEntry.Insert();\n+ until SourceItemLedgerEntry.Next() = 0;\n+ end;\n+}\n","expected_comments":[{"file":"src/SampleOldItemLedgerMigr.Codeunit.al","line_start":10,"line_end":10,"severity":"high","domain":"upgrade","body":"TransferFields(SourceItemLedgerEntry, true) matches fields by field number, but source field 100 \"Journal Batch Name\" (Code[10]) and destination field 100 \"Cost Amount (Actual)\" (Decimal) use the same field number with incompatible types. The true argument initializes primary-key fields; it does not avoid this type collision, so the migration throws at runtime. Renumber the colliding field or replace this call with explicit assignments."}],"category":"code-review","description":"Accepted finding (BCApps PR 9663, discussion r3690213000, THUMBS_UP, fixed in commit 1b978fd715): TransferFields between a migration source buffer and destination archive table fails at runtime because field 100 has incompatible types in the two tables. The optional true argument controls primary-key initialization and does not make incompatible same-numbered fields transferable.","expect_findings":true,"source":"vsoadmin"} {"repo":"microsoft/BCApps","instance_id":"synthetic__data-modeling-excluded-from-calculation-01","base_commit":"69c31df3c9bea1d4b0c27d2ef8bc4201953d5a95","created_at":"2026-08-17T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"data-modeling"},"patch":"diff --git a/src/SampleCustLedgerMigrator2.Codeunit.al b/src/SampleCustLedgerMigrator2.Codeunit.al\nnew file mode 100644\nindex 0000000..5e1332c\n--- /dev/null\n+++ b/src/SampleCustLedgerMigrator2.Codeunit.al\n@@ -0,0 +1,12 @@\n+codeunit 50146 \"Sample Cust Ledger Migrator2\"\n+{\n+ procedure CalcLedgerEntryAmount(CustLedgerEntryNo: Integer): Decimal\n+ var\n+ SampleDetailedCustLE: Record \"Sample Detailed Cust. LE\";\n+ begin\n+ SampleDetailedCustLE.SetRange(\"Cust. Ledger Entry No.\", CustLedgerEntryNo);\n+ SampleDetailedCustLE.SetRange(\"Ledger Entry Amount\", true);\n+ SampleDetailedCustLE.CalcSums(Amount);\n+ exit(SampleDetailedCustLE.Amount);\n+ end;\n+}\ndiff --git a/src/SampleDetailedCustLE.Table.al b/src/SampleDetailedCustLE.Table.al\nnew file mode 100644\nindex 0000000..b809a55\n--- /dev/null\n+++ b/src/SampleDetailedCustLE.Table.al\n@@ -0,0 +1,37 @@\n+table 50145 \"Sample Detailed Cust. LE\"\n+{\n+ Caption = 'Sample Detailed Cust. Ledger Entry';\n+ DataClassification = CustomerContent;\n+\n+ fields\n+ {\n+ field(1; \"Entry No.\"; Integer)\n+ {\n+ Caption = 'Entry No.';\n+ }\n+ field(2; \"Cust. Ledger Entry No.\"; Integer)\n+ {\n+ Caption = 'Cust. Ledger Entry No.';\n+ }\n+ field(3; \"Ledger Entry Amount\"; Boolean)\n+ {\n+ Caption = 'Ledger Entry Amount';\n+ }\n+ field(4; Amount; Decimal)\n+ {\n+ Caption = 'Amount';\n+ }\n+ }\n+\n+ keys\n+ {\n+ key(PK; \"Entry No.\")\n+ {\n+ Clustered = true;\n+ }\n+ key(Key2; \"Cust. Ledger Entry No.\")\n+ {\n+ SumIndexFields = Amount;\n+ }\n+ }\n+}\n","expected_comments":[{"file":"src/SampleDetailedCustLE.Table.al","line_start":1,"line_end":24,"severity":"medium","domain":"data-modeling","body":"\"Sample Detailed Cust. LE\" drops the source table's \"Excluded from calculation\" flag. CalcLedgerEntryAmount sums every detailed entry for a ledger entry that matches \"Ledger Entry Amount\" = true, so any detailed entry the platform excludes from the open-balance FlowField is still counted here, producing an inflated migrated/archived customer balance. Add the boolean field with the source field id and filter Excluded from calculation = false in CalcLedgerEntryAmount."}],"category":"code-review","description":"Accepted finding (BCApps PR 9663, r3691079460, THUMBS_UP): a migration staging buffer for detailed customer ledger entries omits the \"Excluded from calculation\" flag present on the live table, while the migrator's balance-sum procedure filters only on the ledger-entry number and \"Ledger Entry Amount\" = true. Without the exclusion flag, entries the platform normally excludes from open-balance FlowFields get summed anyway, producing an incorrect migrated/archived balance — a genuine schema-completeness bug distinct from the many rejected buffer-schema findings in the same PR.","expect_findings":true,"source":"vsoadmin"} {"repo":"microsoft/BCApps","instance_id":"synthetic__data-modeling-blocked-validation-skip-01","base_commit":"69c31df3c9bea1d4b0c27d2ef8bc4201953d5a95","created_at":"2026-08-17T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"data-modeling"},"patch":"diff --git a/src/SampleCustLedgerMigrator3.Codeunit.al b/src/SampleCustLedgerMigrator3.Codeunit.al\nnew file mode 100644\nindex 0000000..b71b810\n--- /dev/null\n+++ b/src/SampleCustLedgerMigrator3.Codeunit.al\n@@ -0,0 +1,12 @@\n+codeunit 50147 \"Sample Cust Ledger Migrator3\"\n+{\n+ procedure CreateJournalLine(CustomerNo: Code[20])\n+ var\n+ GenJournalLine: Record \"Gen. Journal Line\";\n+ begin\n+ GenJournalLine.Init();\n+ GenJournalLine.Validate(\"Account Type\", GenJournalLine.\"Account Type\"::Customer);\n+ GenJournalLine.Validate(\"Account No.\", CustomerNo);\n+ GenJournalLine.Insert();\n+ end;\n+}\n","expected_comments":[{"file":"src/SampleCustLedgerMigrator3.Codeunit.al","line_start":9,"line_end":9,"severity":"medium","domain":"data-modeling","body":"Gen. Journal Line.Validate(\"Account No.\", ...) applies the standard customer validation and rejects customers whose Blocked value is All. A fully blocked customer can still have open ledger entries that must be recreated during migration, so this path fails instead of reconstructing that balance. Use a migration-specific path that confirms the customer exists while bypassing blocked-status enforcement for opening-balance staging."}],"category":"code-review","description":"Accepted finding (BCApps PR 9663, discussions r3704042966/r3704043330, THUMBS_UP): recreating open balances through Gen. Journal Line.Validate(\"Account No.\", ...) fails for customers or vendors blocked for all transactions. Fully blocked accounts can still have open entries that migration must preserve, so opening-balance reconstruction needs a migration-specific path.","expect_findings":true,"source":"vsoadmin"} {"repo":"microsoft/BCApps","instance_id":"synthetic__error-handling-silent-skip-01","base_commit":"69c31df3c9bea1d4b0c27d2ef8bc4201953d5a95","created_at":"2026-08-17T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"error-handling"},"patch":"diff --git a/src/SampleItemLedgerMigrator.Codeunit.al b/src/SampleItemLedgerMigrator.Codeunit.al\nnew file mode 100644\nindex 0000000..55d1246\n--- /dev/null\n+++ b/src/SampleItemLedgerMigrator.Codeunit.al\n@@ -0,0 +1,30 @@\n+codeunit 50148 \"Sample Item Ledger Migrator\"\n+{\n+ procedure MigrateEntry(var ItemLedgerEntry: Record \"Item Ledger Entry\")\n+ begin\n+ if CreateItemJournalLine(ItemLedgerEntry) then\n+ exit;\n+ end;\n+\n+ local procedure CreateItemJournalLine(var ItemLedgerEntry: Record \"Item Ledger Entry\"): Boolean\n+ var\n+ Location: Record Location;\n+ ItemJournalLine: Record \"Item Journal Line\";\n+ BinCode: Code[20];\n+ begin\n+ Location.Get(ItemLedgerEntry.\"Location Code\");\n+ if Location.\"Bin Mandatory\" then begin\n+ BinCode := ResolveDefaultBin(Location);\n+ if BinCode = '' then\n+ exit(true);\n+ end;\n+ ItemJournalLine.Init();\n+ ItemJournalLine.\"Bin Code\" := BinCode;\n+ ItemJournalLine.Insert();\n+ exit(true);\n+ end;\n+\n+ local procedure ResolveDefaultBin(Location: Record Location): Code[20]\n+ begin\n+ end;\n+}\n","expected_comments":[{"file":"src/SampleItemLedgerMigrator.Codeunit.al","line_start":17,"line_end":19,"severity":"medium","domain":"error-handling","body":"CreateItemJournalLine returns true (success) when the location is bin-mandatory and no postable bin can be resolved, silently skipping the journal line. Because MigrateEntry treats this as a normal successful exit, the open item ledger entry produces no migration error even though it is omitted from live on-hand reconstruction. Surface a migration error or other operator-facing remediation instead of returning success for unpostable open stock."}],"category":"code-review","description":"Accepted finding (BCApps PR 9663, r3703883994, THUMBS_UP): an item-ledger migrator's per-record helper exits with a success-shaped return when it cannot resolve a postable bin for a bin-mandatory location, so the caller's per-record migration loop records no error even though the open item ledger entry is silently dropped from live on-hand reconstruction — a genuine silent-data-loss defect distinct from the many rejected error-handling findings in the same PR.","expect_findings":true,"source":"vsoadmin"} {"repo":"microsoft/BCApps","instance_id":"synthetic__upgrade-broken-hook-copyrows-01","base_commit":"69c31df3c9bea1d4b0c27d2ef8bc4201953d5a95","created_at":"2026-08-17T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"upgrade"},"patch":"diff --git a/src/SampleOldCustLedgerMigr2.Codeunit.al b/src/SampleOldCustLedgerMigr2.Codeunit.al\nnew file mode 100644\nindex 0000000..6a2130a\n--- /dev/null\n+++ b/src/SampleOldCustLedgerMigr2.Codeunit.al\n@@ -0,0 +1,24 @@\n+codeunit 50149 \"Sample Old Cust Ledger Migr2\"\n+{\n+ procedure TransferData()\n+ var\n+ EntryDataTransfer: DataTransfer;\n+ IsConfigured: Boolean;\n+ begin\n+ OnConfigureEntryDataTransfer(EntryDataTransfer, IsConfigured);\n+ if IsConfigured then begin\n+ EntryDataTransfer.CopyRows();\n+ exit;\n+ end;\n+ TransferDataInBatches();\n+ end;\n+\n+ local procedure TransferDataInBatches()\n+ begin\n+ end;\n+\n+ [IntegrationEvent(false, false)]\n+ local procedure OnConfigureEntryDataTransfer(var EntryDataTransfer: DataTransfer; var IsConfigured: Boolean)\n+ begin\n+ end;\n+}\n","expected_comments":[{"file":"src/SampleOldCustLedgerMigr2.Codeunit.al","line_start":9,"line_end":9,"severity":"medium","domain":"upgrade","body":"OnConfigureEntryDataTransfer advertises a subscriber takeover: setting IsConfigured := true makes this normal hybrid-migration codeunit call EntryDataTransfer.CopyRows(). CopyRows() is only allowed from upgrade/install codeunits, so this override path fails at runtime instead of providing a usable customization point. Either let the subscriber perform the transfer itself, or remove this DataTransfer-based takeover contract."}],"category":"code-review","description":"Accepted finding (BCApps PR 9663, r3704047134, THUMBS_UP): a historical migrator publishes an OnConfigureEntryDataTransfer takeover hook that, once a subscriber sets IsConfigured := true, calls DataTransfer.CopyRows() from a normal (non-upgrade) codeunit. CopyRows() is restricted to upgrade/install codeunits, so the advertised extensibility seam throws at runtime instead of working as documented — a genuine broken-hook defect distinct from the many rejected 'Events' naming findings in the same PR.","expect_findings":true,"source":"vsoadmin"} +{"repo":"microsoft/BCApps","instance_id":"synthetic__events-ishandled-reset-boundary-01","base_commit":"70fd0246a0a4dbc72cb183ca719106722c03be4d","created_at":"2026-08-18T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"events"},"patch":"diff --git a/src/SyntheticOrderLineGuard.Codeunit.al b/src/SyntheticOrderLineGuard.Codeunit.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SyntheticOrderLineGuard.Codeunit.al\n@@ -0,0 +1,59 @@\n+codeunit 50140 \"Synthetic Order Line Guard\"\n+{\n+ procedure ValidateQuantity(var SalesLine: Record \"Sales Line\")\n+ var\n+ IsHandled: Boolean;\n+ begin\n+ OnBeforeValidateQuantity(SalesLine, IsHandled);\n+ if IsHandled then\n+ exit;\n+\n+ DoValidateQuantity(SalesLine);\n+ end;\n+\n+ procedure ValidateQuantityAndLocation(var SalesLine: Record \"Sales Line\")\n+ var\n+ IsHandled: Boolean;\n+ begin\n+ OnBeforeValidateQuantity(SalesLine, IsHandled);\n+ if IsHandled then\n+ exit;\n+\n+ DoValidateQuantity(SalesLine);\n+ OnBeforeValidateLocationCode(SalesLine, IsHandled);\n+ if IsHandled then\n+ exit;\n+\n+ DoValidateLocationCode(SalesLine);\n+ end;\n+\n+ procedure ValidateTemporaryLocation(var SalesLine: Record \"Sales Line\")\n+ var\n+ IsHandled: Boolean;\n+ begin\n+ IsHandled := SalesLine.IsTemporary();\n+ OnBeforeValidateLocationCode(SalesLine, IsHandled);\n+ if IsHandled then\n+ exit;\n+\n+ DoValidateLocationCode(SalesLine);\n+ end;\n+\n+ local procedure DoValidateQuantity(var SalesLine: Record \"Sales Line\")\n+ begin\n+ end;\n+\n+ local procedure DoValidateLocationCode(var SalesLine: Record \"Sales Line\")\n+ begin\n+ end;\n+\n+ [IntegrationEvent(false, false)]\n+ local procedure OnBeforeValidateQuantity(var SalesLine: Record \"Sales Line\"; var IsHandled: Boolean)\n+ begin\n+ end;\n+\n+ [IntegrationEvent(false, false)]\n+ local procedure OnBeforeValidateLocationCode(var SalesLine: Record \"Sales Line\"; var IsHandled: Boolean)\n+ begin\n+ end;\n+}\n","expected_comments":[{"file":"src/SyntheticOrderLineGuard.Codeunit.al","line_start":34,"line_end":35,"severity":"medium","domain":"events","body":"ValidateTemporaryLocation seeds IsHandled from SalesLine.IsTemporary() before publishing the event, so temporary lines skip the standard validation even when no subscriber handles the event. Start this raise from false and let subscribers opt in. Do not flag ValidateQuantity's fresh local or ValidateQuantityAndLocation's second raise: reaching that second raise proves the first event did not leave IsHandled true."}],"category":"code-review","description":"Boundary for IsHandled initialization: a fresh local Boolean and a reused Boolean reaching a second raise only after `if IsHandled then exit` are provably false and must not be flagged; a value seeded from non-guaranteed-false state is a finding.","expect_findings":true,"source":"vsoadmin"} +{"repo":"microsoft/BCApps","instance_id":"synthetic__accessibility-setselectionfilter-scope-01","base_commit":"70fd0246a0a4dbc72cb183ca719106722c03be4d","created_at":"2026-08-18T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"ui"},"patch":"diff --git a/src/SyntheticSalesPlanningList.Page.al b/src/SyntheticSalesPlanningList.Page.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SyntheticSalesPlanningList.Page.al\n@@ -0,0 +1,51 @@\n+page 50145 \"Synthetic Sales Planning List\"\n+{\n+ PageType = List;\n+ ApplicationArea = All;\n+ UsageCategory = Lists;\n+ SourceTable = \"Sales Line\";\n+ SourceTableTemporary = true;\n+\n+ layout\n+ {\n+ area(content)\n+ {\n+ repeater(General)\n+ {\n+ field(DocumentNo; Rec.\"Document No.\")\n+ {\n+ ApplicationArea = All;\n+ }\n+ }\n+ }\n+ }\n+\n+ actions\n+ {\n+ area(Processing)\n+ {\n+ action(SyntheticCreateOrders)\n+ {\n+ ApplicationArea = All;\n+ Caption = 'Create Orders';\n+ Image = CreateDocument;\n+\n+ trigger OnAction()\n+ var\n+ TempSalesLine: Record \"Sales Line\" temporary;\n+ begin\n+ TempSalesLine.Copy(Rec, true);\n+ CurrPage.SetSelectionFilter(TempSalesLine);\n+ CreateOrders(TempSalesLine);\n+ end;\n+ }\n+ }\n+ }\n+\n+ local procedure CreateOrders(var TempSalesLine: Record \"Sales Line\" temporary)\n+ begin\n+ if TempSalesLine.FindSet() then\n+ repeat\n+ until TempSalesLine.Next() = 0;\n+ end;\n+}\n","expected_comments":[{"file":"src/SyntheticSalesPlanningList.Page.al","line_start":38,"line_end":38,"severity":"medium","domain":"ui","body":"SyntheticCreateOrders unconditionally applies SetSelectionFilter to a temporary copy that shares the page's table. With no explicit multi-selection, that narrows the buffer to the current row and silently changes this list-wide action into a single-row action. Apply the selection filter only when the user actually marked rows; otherwise preserve the full filtered buffer."}],"category":"code-review","description":"Functional UI selection-scope finding: an unconditional SetSelectionFilter collapses a temporary list buffer to the current row when no rows are marked. This is categorized as UI behavior, not accessibility.","expect_findings":true,"source":"vsoadmin"} +{"repo":"microsoft/BCApps","instance_id":"synthetic__breaking-protected-var-field-01","base_commit":"70fd0246a0a4dbc72cb183ca719106722c03be4d","created_at":"2026-08-18T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"breaking-changes"},"patch":"diff --git a/src/SyntheticCreateOrderWizard.Page.al b/src/SyntheticCreateOrderWizard.Page.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SyntheticCreateOrderWizard.Page.al\n@@ -0,0 +1,23 @@\n+page 50146 \"Synthetic Create Order Wizard\"\n+{\n+ PageType = NavigatePage;\n+ SourceTable = \"Sales Line\";\n+\n+ layout\n+ {\n+ area(content)\n+ {\n+ }\n+ }\n+\n+ protected var\n+ OrderType: Enum \"Sales Document Type\";\n+\n+ var\n+ CreateStatus: Enum \"Create Production Order Status\";\n+\n+ procedure GetParameters(var ParamOrderType: Enum \"Sales Document Type\")\n+ begin\n+ ParamOrderType := OrderType;\n+ end;\n+}\n","expected_comments":[{"file":"src/SyntheticCreateOrderWizard.Page.al","line_start":13,"line_end":14,"severity":"medium","domain":"breaking-changes","body":"OrderType is declared in a protected var block even though the page already exposes it through GetParameters(), turning an implementation detail into an inheritance contract that page extensions can now depend on directly. Keep OrderType in the regular var section unless page extensions are explicitly meant to access it without the getter."}],"category":"code-review","description":"Accepted feedback from BCApps PR 9067 (discussion r3645544898, CreateOrderFromSales.Page.al): moving a field into a protected var block widens the page's inheritance surface, and here it duplicates a value already exposed through GetParameters(). Per choose-access-modifiers-deliberately, keep the field in the regular var section unless subclasses genuinely need direct field access.","expect_findings":true,"source":"vsoadmin"} +{"repo":"microsoft/BCApps","instance_id":"synthetic__data-modeling-tablerelation-restriction-mismatch-01","base_commit":"70fd0246a0a4dbc72cb183ca719106722c03be4d","created_at":"2026-08-18T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"data-modeling"},"patch":"diff --git a/src/SyntheticMfgSetupExt.TableExt.al b/src/SyntheticMfgSetupExt.TableExt.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SyntheticMfgSetupExt.TableExt.al\n@@ -0,0 +1,12 @@\n+tableextension 50148 \"Synthetic Mfg Setup Ext\" extends \"Manufacturing Setup\"\n+{\n+ fields\n+ {\n+ field(50100; \"Synth Default Comp. Item No.\"; Code[20])\n+ {\n+ Caption = 'Default Component Item No.';\n+ DataClassification = CustomerContent;\n+ TableRelation = Item;\n+ }\n+ }\n+}\ndiff --git a/src/SyntheticBOMSeeder.Codeunit.al b/src/SyntheticBOMSeeder.Codeunit.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SyntheticBOMSeeder.Codeunit.al\n@@ -0,0 +1,11 @@\n+codeunit 50149 \"Synthetic BOM Seeder\"\n+{\n+ procedure SeedDefaultComponent(var ProductionBOMLine: Record \"Production BOM Line\")\n+ var\n+ ManufacturingSetup: Record \"Manufacturing Setup\";\n+ begin\n+ ManufacturingSetup.Get();\n+ ProductionBOMLine.Validate(Type, ProductionBOMLine.Type::Item);\n+ ProductionBOMLine.Validate(\"No.\", ManufacturingSetup.\"Synth Default Comp. Item No.\");\n+ end;\n+}\n","expected_comments":[{"file":"src/SyntheticMfgSetupExt.TableExt.al","line_start":9,"line_end":9,"severity":"medium","domain":"data-modeling","body":"\"Synth Default Comp. Item No.\" allows any Item via TableRelation = Item, but SeedDefaultComponent copies this value into \"Production BOM Line\".\"No.\" for Type::Item, whose own relation only permits Inventory and Non-Inventory items. That lets a Service item be persisted into setup and then seed BOM data with a component type the target table intentionally excludes. Restrict this relation to the same item types as the BOM line field (filter(Inventory | \"Non-Inventory\"))."}],"category":"code-review","description":"Accepted feedback from BCApps PR 9067 (discussion r3669143568, ManufacturingSetup.Table.al 'Def. Wiz. Comp Item No.'): a setup field with an unrestricted Item TableRelation feeds directly into a Production BOM Line component field whose relation intentionally excludes Service items, so the wider relation lets an invalid item type reach the BOM line.","expect_findings":true,"source":"vsoadmin"} +{"repo":"microsoft/BCApps","instance_id":"synthetic__style-showmandatory-flowfield-01","base_commit":"3a179e668a0f6256c0a45506b487fc803b951e4a","created_at":"2026-08-18T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"style"},"patch":"diff --git a/src/SampleQltyGenRule.Table.al b/src/SampleQltyGenRule.Table.al\nnew file mode 100644\nindex 00000000..45dd6868\n--- /dev/null\n+++ b/src/SampleQltyGenRule.Table.al\n@@ -0,0 +1,64 @@\n+table 50130 \"Sample Qlty Gen Rule\"\n+{\n+ DataClassification = CustomerContent;\n+\n+ fields\n+ {\n+ field(1; \"Entry No.\"; Integer)\n+ {\n+ Caption = 'Entry No.';\n+ }\n+ field(2; \"Source Table No.\"; Integer)\n+ {\n+ Caption = 'Table No.';\n+ NotBlank = true;\n+ TableRelation = AllObjWithCaption.\"Object ID\" where(\"Object Type\" = const(Table));\n+ ToolTip = 'Specifies the table for this rule.';\n+ }\n+ field(3; \"Table Caption\"; Text[250])\n+ {\n+ CalcFormula = lookup(AllObjWithCaption.\"Object Caption\" where(\"Object Type\" = const(Table),\n+ \"Object ID\" = field(\"Source Table No.\")));\n+ Caption = 'Table';\n+ Editable = false;\n+ FieldClass = FlowField;\n+ ToolTip = 'Specifies the table for this rule.';\n+ }\n+ }\n+\n+ keys\n+ {\n+ key(PK; \"Entry No.\")\n+ {\n+ Clustered = true;\n+ }\n+ }\n+\n+ trigger OnInsert()\n+ begin\n+ CheckSourceTableNoIsSet();\n+ end;\n+\n+ trigger OnModify()\n+ begin\n+ CheckSourceTableNoIsSet();\n+ end;\n+\n+ var\n+ TableMissingErr: Label 'You must select a table before saving.';\n+\n+ local procedure CheckSourceTableNoIsSet()\n+ begin\n+ if Rec.\"Source Table No.\" = 0 then\n+ Error(TableMissingErr);\n+ end;\n+\n+ procedure HandleOnAssistEditSourceTable()\n+ var\n+ AllObjWithCaption: Record AllObjWithCaption;\n+ begin\n+ AllObjWithCaption.SetRange(\"Object Type\", AllObjWithCaption.\"Object Type\"::Table);\n+ if Page.RunModal(Page::\"Objects\", AllObjWithCaption) = Action::LookupOK then\n+ Rec.Validate(\"Source Table No.\", AllObjWithCaption.\"Object ID\");\n+ end;\n+}\ndiff --git a/src/SampleQltyGenRules.Page.al b/src/SampleQltyGenRules.Page.al\nnew file mode 100644\nindex 00000000..eea48bc5\n--- /dev/null\n+++ b/src/SampleQltyGenRules.Page.al\n@@ -0,0 +1,29 @@\n+page 50130 \"Sample Qlty Gen Rules\"\n+{\n+ PageType = List;\n+ ApplicationArea = All;\n+ UsageCategory = Lists;\n+ SourceTable = \"Sample Qlty Gen Rule\";\n+\n+ layout\n+ {\n+ area(Content)\n+ {\n+ repeater(Repeater)\n+ {\n+ field(\"Table Caption\"; Rec.\"Table Caption\")\n+ {\n+ ApplicationArea = All;\n+ AssistEdit = true;\n+ ToolTip = 'Specifies the table for this rule.';\n+\n+ trigger OnAssistEdit()\n+ begin\n+ Rec.HandleOnAssistEditSourceTable();\n+ CurrPage.Update();\n+ end;\n+ }\n+ }\n+ }\n+ }\n+}\n","expected_comments":[],"category":"code-review","description":"False-positive guard: a non-editable FlowField (\"Table Caption\") is populated only through an AssistEdit lookup that validates and sets the backing \"Source Table No.\" field (NotBlank = true, enforced again in OnInsert/OnModify). The page control omits ShowMandatory. Flagging the missing mandatory asterisk here is wrong because the control is not directly editable -- ShowMandatory would incorrectly imply the user can type into it -- and the requirement is already enforced server-side and surfaced through the lookup. Expect no findings (BCApps PR 7938, r3340865203/r3419408926).","expect_findings":false,"source":"vsoadmin"} +{"repo":"microsoft/BCApps","instance_id":"synthetic__error-handling-assistedit-cancel-01","base_commit":"3a179e668a0f6256c0a45506b487fc803b951e4a","created_at":"2026-08-18T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"error-handling"},"patch":"diff --git a/src/SampleAssistEditGenRule.Table.al b/src/SampleAssistEditGenRule.Table.al\nnew file mode 100644\nindex 00000000..19ab7b23\n--- /dev/null\n+++ b/src/SampleAssistEditGenRule.Table.al\n@@ -0,0 +1,54 @@\n+table 50131 \"Sample AE Gen Rule\"\n+{\n+ DataClassification = CustomerContent;\n+\n+ fields\n+ {\n+ field(1; \"Entry No.\"; Integer)\n+ {\n+ Caption = 'Entry No.';\n+ }\n+ field(2; \"Source Table No.\"; Integer)\n+ {\n+ Caption = 'Table No.';\n+ NotBlank = true;\n+ TableRelation = AllObjWithCaption.\"Object ID\" where(\"Object Type\" = const(Table));\n+ ToolTip = 'Specifies the table for this rule.';\n+ }\n+ field(3; \"Table Caption\"; Text[250])\n+ {\n+ CalcFormula = lookup(AllObjWithCaption.\"Object Caption\" where(\"Object Type\" = const(Table),\n+ \"Object ID\" = field(\"Source Table No.\")));\n+ Caption = 'Table';\n+ Editable = false;\n+ FieldClass = FlowField;\n+ ToolTip = 'Specifies the table for this rule.';\n+ }\n+ }\n+\n+ keys\n+ {\n+ key(PK; \"Entry No.\")\n+ {\n+ Clustered = true;\n+ }\n+ }\n+\n+ trigger OnInsert()\n+ begin\n+ if Rec.\"Source Table No.\" = 0 then\n+ Error(TableMissingErr);\n+ end;\n+\n+ var\n+ TableMissingErr: Label 'You must select a table before saving.';\n+\n+ procedure HandleOnAssistEditSourceTable()\n+ var\n+ AllObjWithCaption: Record AllObjWithCaption;\n+ begin\n+ AllObjWithCaption.SetRange(\"Object Type\", AllObjWithCaption.\"Object Type\"::Table);\n+ if Page.RunModal(Page::\"Objects\", AllObjWithCaption) = Action::LookupOK then\n+ Rec.Validate(\"Source Table No.\", AllObjWithCaption.\"Object ID\");\n+ end;\n+}\ndiff --git a/src/SampleAssistEditGenRules.Page.al b/src/SampleAssistEditGenRules.Page.al\nnew file mode 100644\nindex 00000000..9c574f20\n--- /dev/null\n+++ b/src/SampleAssistEditGenRules.Page.al\n@@ -0,0 +1,32 @@\n+page 50131 \"Sample AE Gen Rules\"\n+{\n+ PageType = List;\n+ ApplicationArea = All;\n+ UsageCategory = Lists;\n+ SourceTable = \"Sample AE Gen Rule\";\n+ DelayedInsert = true;\n+\n+ layout\n+ {\n+ area(Content)\n+ {\n+ repeater(Repeater)\n+ {\n+ field(\"Table Caption\"; Rec.\"Table Caption\")\n+ {\n+ ApplicationArea = All;\n+ AssistEdit = true;\n+ ToolTip = 'Specifies the table for this rule.';\n+\n+ trigger OnAssistEdit()\n+ begin\n+ Rec.HandleOnAssistEditSourceTable();\n+ CurrPage.SaveRecord();\n+ if xRec.\"Entry No.\" = Rec.\"Entry No.\" then\n+ CurrPage.Update(true);\n+ end;\n+ }\n+ }\n+ }\n+ }\n+}\n","expected_comments":[{"file":"src/SampleAssistEditGenRules.Page.al","line_start":24,"line_end":24,"severity":"medium","domain":"error-handling","body":"CurrPage.SaveRecord() is called unconditionally after HandleOnAssistEditSourceTable() in OnAssistEdit. HandleOnAssistEditSourceTable() returns without setting \"Source Table No.\" when the user cancels the lookup, so for a new, unsaved DelayedInsert row the subsequent SaveRecord() forces an insert with \"Source Table No.\" = 0, which immediately raises TableMissingErr -- an unexpected error from a cancel action. Only call SaveRecord() when the field was actually set (e.g. guard on Rec.\"Source Table No.\" <> 0)."}],"category":"code-review","description":"Accepted finding: an unconditional CurrPage.SaveRecord() after a cancellable AssistEdit lookup forces insertion of an invalid, still-zero mandatory field when the user cancels, surfacing a confusing validation error from what should be a no-op cancel. Guards recall for the SaveRecord-after-cancel pattern (BCApps PR 7938, r3340865375, THUMBS_UP).","expect_findings":true,"source":"vsoadmin"} +{"repo":"microsoft/BCApps","instance_id":"synthetic__breaking-event-subscriber-suppress-01","base_commit":"397d01199c321e774edaf23a7290fee40f75c6a6","created_at":"2026-08-18T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"breaking-changes"},"patch":"diff --git a/src/Synthetic/UomEventSkip.Codeunit.al b/src/Synthetic/UomEventSkip.Codeunit.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/Synthetic/UomEventSkip.Codeunit.al\n@@ -0,0 +1,8 @@\n+codeunit 50141 \"Uom Event Skip\"\n+{\n+ [EventSubscriber(ObjectType::Table, Database::\"Sales Line\", 'OnBeforeUpdateQuantityFromUOMCode', '', false, false)]\n+ local procedure SkipUpdateQuantityFromUOMCode(var SalesLine: Record \"Sales Line\"; var IsHandled: Boolean)\n+ begin\n+ IsHandled := true;\n+ end;\n+}\n","expected_comments":[{"file":"src/Synthetic/UomEventSkip.Codeunit.al","line_start":6,"line_end":6,"severity":"medium","domain":"breaking-changes","body":"This subscriber unconditionally sets IsHandled := true on Sales Line's OnBeforeUpdateQuantityFromUOMCode, suppressing the standard quantity-from-UOM conversion for every caller, not just the scenario the subscriber is meant to optimize. Any other code or extension that depends on this conversion running when Unit of Measure Code changes will silently stop getting it while this subscriber is bound. Narrow the condition (e.g. only skip when Quantity is 0) or document why unconditional suppression is safe."}],"category":"code-review","description":"Accepted feedback (BCApps PR 8553, discussion r3435791132, THUMBS_UP): an event subscriber unconditionally suppresses Sales Line's OnBeforeUpdateQuantityFromUOMCode for every caller to skip an expensive Quantity revalidation during proposal creation, silently disabling the standard UoM-driven quantity conversion for any other code path that relies on it. The developer accepted the concern and justified rather than dropped the subscriber, confirming this is a genuine (if intentionally-traded-off) risk that should be flagged.","expect_findings":true,"source":"vsoadmin"} +{"repo":"microsoft/BCApps","instance_id":"synthetic__style-field-repurpose-indent-01","base_commit":"397d01199c321e774edaf23a7290fee40f75c6a6","created_at":"2026-08-18T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"style"},"patch":"diff --git a/src/Synthetic/TempAggLine.Table.al b/src/Synthetic/TempAggLine.Table.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/Synthetic/TempAggLine.Table.al\n@@ -0,0 +1,9 @@\n+table 50146 \"Temp Agg Line\"\n+{\n+ fields\n+ {\n+ field(1; \"Line No.\"; Integer) { }\n+ field(2; Description; Text[100]) { }\n+ field(3; Indent; Integer) { }\n+ }\n+}\ndiff --git a/src/Synthetic/AggLineBuilder.Codeunit.al b/src/Synthetic/AggLineBuilder.Codeunit.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/Synthetic/AggLineBuilder.Codeunit.al\n@@ -0,0 +1,15 @@\n+codeunit 50147 \"Agg Line Builder\"\n+{\n+ procedure BuildAggregatedLine(SourceEntryNo: Integer; var TempAggLine: Record \"Temp Agg Line\" temporary)\n+ begin\n+ TempAggLine.Init();\n+ TempAggLine.\"Line No.\" += 1;\n+ TempAggLine.Indent := SourceEntryNo;\n+ TempAggLine.Insert();\n+ end;\n+\n+ procedure GetSourceEntryNo(TempAggLine: Record \"Temp Agg Line\" temporary): Integer\n+ begin\n+ exit(TempAggLine.Indent);\n+ end;\n+}\n","expected_comments":[{"file":"src/Synthetic/AggLineBuilder.Codeunit.al","line_start":7,"line_end":7,"severity":"medium","domain":"style","body":"Indent is Temp Agg Line's UI row-indentation field, but here it is repurposed to stash SourceEntryNo purely to avoid a second lookup, and GetSourceEntryNo reads it back for that unrelated meaning. Repurposing a field for a meaning unrelated to its documented purpose is fragile: any future code that also sets Indent for actual display indentation on this same temp instance will silently corrupt or misread the stored entry number. Add a dedicated field (or a keyed Dictionary) to carry this value instead of overloading Indent."}],"category":"code-review","description":"Accepted feedback (BCApps PR 8553, discussion r3535265290, THUMBS_UP): an aggregated temp-line builder stamps an unrelated source Entry No. into the Indent field -- a field whose real, documented purpose is UI row-indentation -- purely to avoid re-querying the source record, then reads it back for that unrelated meaning. Agent judgement, not backed by a specific knowledge article; the developer agreed to use a dedicated cache (e.g. a Dictionary) instead.","expect_findings":true,"source":"vsoadmin"} +{"repo":"microsoft/BCApps","instance_id":"synthetic__error-handling-drilldown-position-01","base_commit":"397d01199c321e774edaf23a7290fee40f75c6a6","created_at":"2026-08-18T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"error-handling"},"patch":"diff --git a/src/Synthetic/TempGroupedLine.Table.al b/src/Synthetic/TempGroupedLine.Table.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/Synthetic/TempGroupedLine.Table.al\n@@ -0,0 +1,12 @@\n+table 50149 \"Temp Grouped Line\"\n+{\n+ fields\n+ {\n+ field(1; \"Entry No.\"; Integer) { }\n+ field(2; Description; Text[100]) { }\n+ }\n+ keys\n+ {\n+ key(PK; \"Entry No.\") { Clustered = true; }\n+ }\n+}\ndiff --git a/src/Synthetic/GroupedLinesRefresh.Codeunit.al b/src/Synthetic/GroupedLinesRefresh.Codeunit.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/Synthetic/GroupedLinesRefresh.Codeunit.al\n@@ -0,0 +1,16 @@\n+codeunit 50148 \"Grouped Lines Refresh\"\n+{\n+ procedure RefreshGroups(var TempGroupedLine: Record \"Temp Grouped Line\" temporary; var CurrentEntryNo: Integer)\n+ begin\n+ RebuildGroupHeaders(TempGroupedLine);\n+ if not TempGroupedLine.Get(CurrentEntryNo) then\n+ if TempGroupedLine.FindFirst() then;\n+ CurrentEntryNo := TempGroupedLine.\"Entry No.\";\n+ end;\n+\n+ local procedure RebuildGroupHeaders(var TempGroupedLine: Record \"Temp Grouped Line\" temporary)\n+ begin\n+ TempGroupedLine.DeleteAll();\n+ TempGroupedLine.Init();\n+ TempGroupedLine.\"Entry No.\" := -1;\n+ TempGroupedLine.Insert();\n+ end;\n+}\n","expected_comments":[{"file":"src/Synthetic/GroupedLinesRefresh.Codeunit.al","line_start":5,"line_end":6,"severity":"medium","domain":"error-handling","body":"RebuildGroupHeaders deletes and recreates every group header row with a fresh negative \"Entry No.\" each time RefreshGroups runs, so the CurrentEntryNo captured before the rebuild can no longer resolve via TempGroupedLine.Get and silently falls back to FindFirst. A user positioned on a header row who drills down and returns loses their place with no indication why. Restore the current row by a stable business key (e.g. the group's key fields) instead of the temporary, regenerated Entry No."}],"category":"code-review","description":"Accepted feedback (BCApps PR 8553, discussion r3604298340, THUMBS_UP): group header rows are recreated with new negative entry numbers on every refresh, so restoring the current row solely by the temporary Entry No. silently falls back to the first record once the previously-viewed header no longer exists. The developer agreed this was a good catch worth investigating/fixing.","expect_findings":true,"source":"vsoadmin"} +{"repo":"microsoft/BCApps","instance_id":"synthetic__testing-ui-handler-assert-after-run-01","base_commit":"70fd0246a0a4dbc72cb183ca719106722c03be4d","created_at":"2026-08-18T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"testing"},"patch":"diff --git a/src/SyntheticWizardState.Table.al b/src/SyntheticWizardState.Table.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SyntheticWizardState.Table.al\n@@ -0,0 +1,24 @@\n+table 50130 \"Synthetic Wizard State\"\n+{\n+ DataClassification = CustomerContent;\n+\n+ fields\n+ {\n+ field(1; \"Scenario Code\"; Code[20])\n+ {\n+ Caption = 'Scenario Code';\n+ }\n+ field(2; Approved; Boolean)\n+ {\n+ Caption = 'Approved';\n+ }\n+ }\n+\n+ keys\n+ {\n+ key(PK; \"Scenario Code\")\n+ {\n+ Clustered = true;\n+ }\n+ }\n+}\ndiff --git a/src/SyntheticApprovalWizard.Page.al b/src/SyntheticApprovalWizard.Page.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SyntheticApprovalWizard.Page.al\n@@ -0,0 +1,37 @@\n+page 50131 \"Synthetic Approval Wizard\"\n+{\n+ PageType = NavigatePage;\n+ SourceTable = \"Synthetic Wizard State\";\n+\n+ layout\n+ {\n+ area(content)\n+ {\n+ field(\"Scenario Code\"; Rec.\"Scenario Code\")\n+ {\n+ ApplicationArea = All;\n+ Editable = false;\n+ }\n+ }\n+ }\n+\n+ actions\n+ {\n+ area(processing)\n+ {\n+ action(Approve)\n+ {\n+ ApplicationArea = All;\n+ Caption = 'Approve';\n+ InFooterBar = true;\n+\n+ trigger OnAction()\n+ begin\n+ Rec.Approved := true;\n+ Rec.Modify(true);\n+ CurrPage.Close();\n+ end;\n+ }\n+ }\n+ }\n+}\ndiff --git a/src/SyntheticWizardTest.Codeunit.al b/src/SyntheticWizardTest.Codeunit.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SyntheticWizardTest.Codeunit.al\n@@ -0,0 +1,50 @@\n+codeunit 50132 \"Synthetic Wizard Test\"\n+{\n+ Subtype = Test;\n+\n+ var\n+ Assert: Codeunit \"Library Assert\";\n+ CapturedScenarioCode: Code[20];\n+\n+ [Test]\n+ [HandlerFunctions('HandleWizardAndCapture')]\n+ procedure ApproveWizardPersistsSelection()\n+ var\n+ WizardState: Record \"Synthetic Wizard State\";\n+ begin\n+ WizardState.\"Scenario Code\" := 'CAPTURE';\n+ WizardState.Insert();\n+ CapturedScenarioCode := '';\n+\n+ Page.RunModal(Page::\"Synthetic Approval Wizard\", WizardState);\n+\n+ Assert.AreEqual(WizardState.\"Scenario Code\", CapturedScenarioCode, 'The wizard opened for the wrong scenario.');\n+ WizardState.Get('CAPTURE');\n+ Assert.IsTrue(WizardState.Approved, 'The Approve action did not persist its result.');\n+ end;\n+\n+ [Test]\n+ [HandlerFunctions('HandleWizardWithoutAssertion')]\n+ procedure ApproveWizardWithoutVerifyingResult()\n+ var\n+ WizardState: Record \"Synthetic Wizard State\";\n+ begin\n+ WizardState.\"Scenario Code\" := 'NOASSERT';\n+ WizardState.Insert();\n+\n+ Page.RunModal(Page::\"Synthetic Approval Wizard\", WizardState);\n+ end;\n+\n+ [ModalPageHandler]\n+ procedure HandleWizardAndCapture(var SyntheticApprovalWizard: TestPage \"Synthetic Approval Wizard\")\n+ begin\n+ CapturedScenarioCode := SyntheticApprovalWizard.\"Scenario Code\".Value();\n+ SyntheticApprovalWizard.Approve.Invoke();\n+ end;\n+\n+ [ModalPageHandler]\n+ procedure HandleWizardWithoutAssertion(var SyntheticApprovalWizard: TestPage \"Synthetic Approval Wizard\")\n+ begin\n+ SyntheticApprovalWizard.Approve.Invoke();\n+ end;\n+}\n","expected_comments":[{"file":"src/SyntheticWizardTest.Codeunit.al","line_start":35,"line_end":35,"severity":"medium","domain":"testing","body":"HandlerFunctions already proves HandleWizardWithoutAssertion executed, but this test never verifies the semantic result of Approve after RunModal returns. The handler only invokes the action, so the test can stay green if Approve stops persisting Approved. Re-read the state and assert the action result. ApproveWizardPersistsSelection is the valid boundary: it resets and captures a concrete page value, then asserts both that value and the persisted action outcome."}],"category":"code-review","description":"UI-handler boundary aligned with HandlerFunctions semantics: the positive test resets capture storage, records a meaningful page value, and asserts the persisted action result after RunModal; the negative test only invokes navigation behavior and has no semantic postcondition.","expect_findings":true,"source":"vsoadmin"} +{"repo":"microsoft/BCApps","instance_id":"synthetic__error-handling-errorinfo-actionable-boundary-01","base_commit":"70fd0246a0a4dbc72cb183ca719106722c03be4d","created_at":"2026-08-18T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"error-handling"},"patch":"diff --git a/src/SyntheticReservationGuard.Codeunit.al b/src/SyntheticReservationGuard.Codeunit.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SyntheticReservationGuard.Codeunit.al\n@@ -0,0 +1,11 @@\n+codeunit 50147 \"Synthetic Reservation Guard\"\n+{\n+ procedure ValidateReservedQuantity(var SalesLine: Record \"Sales Line\"; QuantityToReserve: Decimal)\n+ begin\n+ if QuantityToReserve > SalesLine.\"Outstanding Quantity\" then\n+ Error(FullyReservedErr, SalesLine.\"Document No.\", SalesLine.\"Line No.\");\n+ end;\n+\n+ var\n+ FullyReservedErr: Label 'Sales line %1, %2 cannot reserve the requested quantity.', Comment = '%1 = document no., %2 = line no.';\n+}\ndiff --git a/src/SyntheticSaveTarget.Page.al b/src/SyntheticSaveTarget.Page.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SyntheticSaveTarget.Page.al\n@@ -0,0 +1,33 @@\n+page 50148 \"Synthetic Save Target\"\n+{\n+ PageType = StandardDialog;\n+\n+ layout\n+ {\n+ area(content)\n+ {\n+ field(SaveTarget; SaveTarget)\n+ {\n+ ApplicationArea = All;\n+ Caption = 'Save Target';\n+\n+ trigger OnValidate()\n+ begin\n+ if SourceIsItem and (SaveTarget = SaveTarget::\"Stockkeeping Unit\") then\n+ Error(TargetNotAllowedErr);\n+ end;\n+ }\n+ }\n+ }\n+\n+ trigger OnOpenPage()\n+ begin\n+ SourceIsItem := true;\n+ SaveTarget := SaveTarget::Item;\n+ end;\n+\n+ var\n+ SaveTarget: Option Item,\"Stockkeeping Unit\";\n+ SourceIsItem: Boolean;\n+ TargetNotAllowedErr: Label 'Select Item when the source is an item.';\n+}\n","expected_comments":[{"file":"src/SyntheticReservationGuard.Codeunit.al","line_start":6,"line_end":6,"severity":"medium","domain":"error-handling","body":"This error already has the exact over-reserved Sales Line, so the dead-end Error leaves the user to find that record manually. Raise ErrorInfo with RecordId = SalesLine.RecordId, PageNo set to the Sales Lines page, and a navigation action so the user can open the line that must be corrected. Do not require a Fix-it for Synthetic Save Target: that validation occurs on the selectable field itself and the user can immediately choose Item on the current page."}],"category":"code-review","description":"Actionable-error boundary: a known Sales Line RecordId and navigation target justify an ErrorInfo Show-it action, while current-field validation that the user can immediately correct does not require a Fix-it.","expect_findings":true,"source":"vsoadmin"} +{"repo":"microsoft/BCApps","instance_id":"synthetic__error-handling-case-unreachable-else-01","base_commit":"3a179e668a0f6256c0a45506b487fc803b951e4a","created_at":"2026-08-18T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"error-handling"},"patch":"diff --git a/src/SyntheticSourceKind.Enum.al b/src/SyntheticSourceKind.Enum.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SyntheticSourceKind.Enum.al\n@@ -0,0 +1,17 @@\n+enum 50150 \"Synthetic Source Kind\"\n+{\n+ Extensible = false;\n+\n+ value(0; Purchase)\n+ {\n+ Caption = 'Purchase';\n+ }\n+ value(1; Transfer)\n+ {\n+ Caption = 'Transfer';\n+ }\n+ value(2; Warehouse)\n+ {\n+ Caption = 'Warehouse';\n+ }\n+}\ndiff --git a/src/SyntheticSourceSelector.Codeunit.al b/src/SyntheticSourceSelector.Codeunit.al\nnew file mode 100644\n--- /dev/null\n+++ b/src/SyntheticSourceSelector.Codeunit.al\n@@ -0,0 +1,14 @@\n+codeunit 50151 \"Synthetic Source Selector\"\n+{\n+ procedure DetermineSourceTableNo(SourceKind: Enum \"Synthetic Source Kind\"): Integer\n+ begin\n+ case SourceKind of\n+ SourceKind::Purchase:\n+ exit(Database::\"Purchase Line\");\n+ SourceKind::Transfer:\n+ exit(Database::\"Transfer Line\");\n+ SourceKind::Warehouse:\n+ exit(Database::\"Warehouse Journal Line\");\n+ end;\n+ end;\n+}\n","expected_comments":[],"category":"code-review","description":"False-positive boundary: the public entry accepts a non-extensible Enum directly and the case covers every legal value. The default return path is therefore unreachable; an else branch would be optional defensive hardening, not a required finding.","expect_findings":false,"source":"vsoadmin"} +{"repo":"microsoft/BCApps","instance_id":"synthetic__breaking-scope-creep-unrelated-api-01","base_commit":"397d01199c321e774edaf23a7290fee40f75c6a6","created_at":"2026-08-18T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"breaking-changes"},"patch":"diff --git a/src/Apps/W1/Subscription Billing/App/Service Commitments/Tables/SubscriptionLine.Table.al b/src/Apps/W1/Subscription Billing/App/Service Commitments/Tables/SubscriptionLine.Table.al\n--- a/src/Apps/W1/Subscription Billing/App/Service Commitments/Tables/SubscriptionLine.Table.al\n+++ b/src/Apps/W1/Subscription Billing/App/Service Commitments/Tables/SubscriptionLine.Table.al\n@@ -906,7 +906,7 @@ table 8059 \"Subscription Line\"\n until BillingLine.Next() = 0;\n end;\n \n- internal procedure UpdateNextBillingDate(LastBillingToDate: Date)\n+ procedure UpdateNextBillingDate(LastBillingToDate: Date)\n var\n NewNextBillingDate: Date;\n OriginalInvoicedToDate: Date;\ndiff --git a/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/BillingProposal.Codeunit.al b/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/BillingProposal.Codeunit.al\n--- a/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/BillingProposal.Codeunit.al\n+++ b/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/BillingProposal.Codeunit.al\n@@ -463,6 +463,7 @@ codeunit 8062 \"Billing Proposal\"\n BillingLine.\"Subscription Contract Line No.\" := ServiceCommitment.\"Subscription Contract Line No.\";\n BillingLine.\"Discount %\" := ServiceCommitment.\"Discount %\";\n BillingLine.Discount := ServiceCommitment.Discount;\n+ ServiceObject.SetLoadFields(Quantity);\n ServiceObject.Get(ServiceCommitment.\"Subscription Header No.\");\n BillingLine.\"Service Object Quantity\" := BillingLine.GetSign() * ServiceObject.Quantity;\n OnAfterUpdateBillingLineFromSubscriptionLine(BillingLine, ServiceCommitment);\n","expected_comments":[{"file":"src/Apps/W1/Subscription Billing/App/Service Commitments/Tables/SubscriptionLine.Table.al","line_start":909,"line_end":909,"severity":"low","domain":"breaking-changes","body":"This performance change also widens the existing UpdateNextBillingDate procedure from internal to public, creating a new external compatibility commitment unrelated to the SetLoadFields optimization. Split the API change into a dedicated review or provide the intended external-consumer and compatibility rationale; otherwise keep it internal. This is an API-scope concern, not a security boundary, so no authorization check is implied."}],"category":"code-review","description":"Low-severity API-scope concern: a real base-to-head internal-to-public change is bundled with an unrelated SetLoadFields optimization. This differs from synthetic__breaking-access-modifier-01, which tests a newly introduced internal helper that should be local.","expect_findings":true,"source":"vsoadmin"} +{"repo":"microsoft/BCApps","instance_id":"synthetic__perf-progress-dialog-deleteall-01","base_commit":"397d01199c321e774edaf23a7290fee40f75c6a6","created_at":"2026-08-18T00:00:00Z","environment_setup_version":"27.0","project_paths":[],"metadata":{"area":"performance"},"patch":"diff --git a/src/Apps/W1/Subscription Billing/App/Billing/Tables/BillingLine.Table.al b/src/Apps/W1/Subscription Billing/App/Billing/Tables/BillingLine.Table.al\n--- a/src/Apps/W1/Subscription Billing/App/Billing/Tables/BillingLine.Table.al\n+++ b/src/Apps/W1/Subscription Billing/App/Billing/Tables/BillingLine.Table.al\n@@ -249,10 +249,11 @@ table 8061 \"Billing Line\"\n var\n BillingLine2: Record \"Billing Line\";\n begin\n if \"Document No.\" <> '' then\n Error(CannotDeleteBillingLinesWithDocumentNoErr);\n+ BillingLine2.SetLoadFields(\"Entry No.\");\n FindFirstBillingLineForServiceCommitment(BillingLine2);\n if (BillingLine2.\"Entry No.\" = \"Entry No.\") then\n ResetServiceCommitmentNextBillingDate()\n else\n Error(OnlyLastServiceLineCanBeDeletedErr, \"Subscription Header No.\");\n RecalculateCustomerContractHarmonizedBillingFields();\ndiff --git a/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/BillingProposal.Codeunit.al b/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/BillingProposal.Codeunit.al\n--- a/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/BillingProposal.Codeunit.al\n+++ b/src/Apps/W1/Subscription Billing/App/Billing/Codeunits/BillingProposal.Codeunit.al\n@@ -557,7 +557,10 @@ codeunit 8062 \"Billing Proposal\"\n ClearBillingProposalOptionsMySuggestionsOnlyTxt: Label 'All billing proposals (user %1 only), Only current billing template proposal', Comment = '%1: User ID';\n ClearBillingProposalQst: Label 'Which billing proposal(s) should be deleted?';\n StrMenuResponse: Integer;\n+ Counter: Integer;\n+ ProgressWindow: Dialog;\n+ DeletingBillingLinesMsg: Label 'Deleted billing lines: #1######';\n begin\n DisplayErrorIfNotAuthorizedToClearProposalOrDeleteDocuments();\n BillingTemplate.Get(BillingTemplateCode);\n if BillingTemplate.\"My Suggestions Only\" then\n@@ -575,12 +578,26 @@ codeunit 8062 \"Billing Proposal\"\n BillingLine.SetRange(Partner, BillingTemplate.Partner);\n if BillingTemplate.\"My Suggestions Only\" then\n BillingLine.SetRange(\"User ID\", UserId());\n- BillingLine.DeleteAll(true);\n+ ProgressWindow.Open(DeletingBillingLinesMsg);\n+ if BillingLine.FindSet() then\n+ repeat\n+ Counter += 1;\n+ ProgressWindow.Update(1, Counter);\n+ BillingLine.Delete(true);\n+ until BillingLine.Next() = 0;\n+ ProgressWindow.Close();\n end;\n 2:\n begin\n BillingLine.SetRange(\"Billing Template Code\", BillingTemplate.Code);\n- BillingLine.DeleteAll(true);\n+ ProgressWindow.Open(DeletingBillingLinesMsg);\n+ if BillingLine.FindSet() then\n+ repeat\n+ Counter += 1;\n+ ProgressWindow.Update(1, Counter);\n+ BillingLine.Delete(true);\n+ until BillingLine.Next() = 0;\n+ ProgressWindow.Close();\n end;\n end;\n end;\n","expected_comments":[],"category":"code-review","description":"False-positive boundary: the replaced operation is DeleteAll(true), and the target Billing Line table visibly has substantive OnDelete work that forces the bulk call to execute row by row. The explicit FindSet/Delete(true) loop preserves that trigger behavior while adding per-row progress; this narrow bulk-regression case is not a generic progress-dialog exemption.","expect_findings":false,"source":"vsoadmin"}