Normalize SCM knowledge and review ownership

Align article and AL sample conventions, keep BC facts separate from review mechanics, and clarify reciprocal Finance ownership without bespoke shared test assertions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Jesper Schulz-Wedde 2026-09-17 18:59:22 +02:00
parent 9ac62e3967
commit 525e84e183
30 changed files with 148 additions and 362 deletions

View file

@ -110,28 +110,6 @@ try {
}
}
$scm = @($skills | Where-Object id -eq 'al-scm-review')
if ($scm.Count -ne 1 -or
(@($scm[0].inputs) -join ',') -cne 'pr-diff,file-path,folder-path' -or
(@($scm[0].filters.technologies) -join ',') -cne 'al') {
throw 'SCM must be discoverable as an AL leaf accepting diffs, files, and complete folders.'
}
$scmText = Get-Content -LiteralPath (Join-Path $Root $scm[0].path) -Raw
$scmExamples = [regex]::Matches($scmText, '(?s)```json\s*(\{.*?\})\s*```')
if ($scmExamples.Count -ne 2) {
throw "Expected two SCM findings-report examples, found $($scmExamples.Count)."
}
foreach ($example in $scmExamples) {
if (-not ($example.Groups[1].Value | Test-Json -SchemaFile $reportSchema -ErrorAction Stop)) {
throw 'An SCM output example does not satisfy schemas/findings-report.schema.json.'
}
$report = $example.Groups[1].Value | ConvertFrom-Json
if ($report.skill.id -cne 'al-scm-review' -or
@($report.findings | Where-Object domain -cne 'Supply Chain Management').Count) {
throw 'SCM output examples must retain the leaf id and complete display domain.'
}
}
$minimalReport = @{
skill = @{ id = 'al-style-review'; version = 1 }
outcome = 'completed'

View file

@ -194,8 +194,8 @@ Available knowledge is **not** a promise that every rule will run. Selection
depends on the task, target context, enabled layers, and source evidence.
A whole-folder review is a current-state snapshot: detecting a published API
removal or another comparison-only regression requires an actual baseline.
The corpus combines technical AL guidance with a focused SCM functional
domain, not exhaustive functional validation or AppSource certification.
The corpus combines technical AL guidance with targeted functional-domain
invariants, not exhaustive functional validation or AppSource certification.
The SCM leaf owns selected inventory/value, application, reservation, tracking,
and warehouse/posting invariants. It prunes unrelated AL using the actual
@ -205,6 +205,12 @@ must not be replaced with an assumed posting defect. Manufacturing, assembly,
planning, and other supply-chain areas are covered only where an article
explicitly names the shared interface or invariant.
SCM owns Item/Value/Capacity/Warehouse and inventory-application posting
records. Pure G/L, customer/vendor/detailed/VAT and financial-only posting
mutations belong to Finance, even when that domain is not enabled. Equivalent
findings for one inventory-originated posting bypass have one SCM primary
owner; distinct independent financial defects remain separate.
BCQuality intentionally does not duplicate mechanical diagnostics already
enforced by the AL compiler or standard analyzers. Run the consuming app's
normal compiler and analyzer pipeline alongside review and authoring. Knowledge

View file

@ -1,8 +1,3 @@
namespace BCQuality.SCM.Samples;
using Microsoft.Inventory.Tracking;
using Microsoft.Sales.Document;
codeunit 50106 "SCM Cancel Reservation Bad"
{
procedure CancelSalesReservation(ReservationEntryNo: Integer)

View file

@ -1,8 +1,3 @@
namespace BCQuality.SCM.Samples;
using Microsoft.Inventory.Tracking;
using Microsoft.Sales.Document;
codeunit 50107 "SCM Cancel Reservation Good"
{
procedure CancelSalesReservation(ReservationEntryNo: Integer)

View file

@ -15,22 +15,21 @@ Persistent `"Reservation Entry"` rows are not disposable allocation markers. Res
## Best Practice
For explicit cancellation of an existing binding reservation, use `"Reservation Engine Mgt.".CancelReservation`. It checks the reservation status and `"Disallow Cancellation"`, handles the counterpart, and preserves or retracks the remaining source quantities as appropriate. For source-line quantity changes, use the source-specific reservation management path rather than deleting its reservation rows yourself.
Explicit cancellation of an existing binding reservation uses `"Reservation Engine Mgt.".CancelReservation`. It checks the reservation status and `"Disallow Cancellation"`, handles the counterpart, and preserves or retracks the remaining source quantities as appropriate. Source-line quantity changes have their own source-specific reservation management path.
Do not require every Reservation Entry to have a partner or identical lot/serial values on both sides: Surplus/Prospect entries and supported late-binding scenarios need different treatment. Temporary buffers, engine-owned updates, and supported publisher metadata are not independent cancellation. Cancelling a reservation is also different from intentionally removing an item-tracking assignment.
Not every Reservation Entry has a partner or identical lot/serial values on both sides: Surplus/Prospect entries and supported late-binding scenarios have different relationships. Temporary buffers, engine-owned updates, and supported publisher metadata are not independent cancellation. Cancelling a reservation is also different from intentionally removing an item-tracking assignment.
The samples retrieve the negative side of a persistent sales-line reservation and cancel only the binding. They do not delete the sales line or remove its tracking specifications.
See sample: [`cancel-reservations-through-reservation-management.good.al`](cancel-reservations-through-reservation-management.good.al).
## Anti Pattern
Report `Delete(true)`, `DeleteAll`, or a status/source rewrite on persistent `"Reservation Entry"` records used as a replacement for cancelling a reservation. Deleting both sides is not a sufficient repair: it can still discard tracking that should survive and omit retracking.
`Delete(true)`, `DeleteAll`, or a status/source rewrite on persistent `"Reservation Entry"` records does not perform binding-reservation cancellation. Even deleting both sides can discard tracking that should survive and omit retracking.
Require cancellation intent and a binding reservation in the visible context. Do not flag normal processing of temporary Prospect/Surplus buffers or diagnose every single row as an orphan.
Normal processing of temporary Prospect/Surplus buffers is outside that cancellation workflow, and an unpaired row is not intrinsically an orphan.
## Samples
- [`cancel-reservations-through-reservation-management.bad.al`](cancel-reservations-through-reservation-management.bad.al)
- [`cancel-reservations-through-reservation-management.good.al`](cancel-reservations-through-reservation-management.good.al)
See sample: [`cancel-reservations-through-reservation-management.bad.al`](cancel-reservations-through-reservation-management.bad.al).
## References

View file

@ -1,9 +1,3 @@
namespace BCQuality.SCM.Samples;
using Microsoft.Inventory.Requisition;
using Microsoft.Purchases.Document;
using Microsoft.Sales.Document;
codeunit 50116 "SCM Requisition Action Bad"
{
procedure CarryOutAcceptedNewPurchase(TemplateName: Code[10]; BatchName: Code[10]; LineNo: Integer; OrderDate: Date; PostingDate: Date; ReceiptDate: Date; CutoffDate: Date)

View file

@ -1,9 +1,3 @@
namespace BCQuality.SCM.Samples;
using Microsoft.Inventory.Requisition;
using Microsoft.Purchases.Document;
using Microsoft.Sales.Document;
codeunit 50117 "SCM Requisition Action Good"
{
procedure CarryOutAcceptedNewPurchase(TemplateName: Code[10]; BatchName: Code[10]; LineNo: Integer; OrderDate: Date; PostingDate: Date; ReceiptDate: Date; CutoffDate: Date)

View file

@ -15,24 +15,23 @@ A requisition/planning line is a pending change to a supply/demand network, not
## Best Practice
For requisition batch carry-out, initialize `"Req. Wksh.-Make Order"` with `Set` and invoke `CarryOutBatchAction` on the intended accepted lines. Supply the order/posting/receipt defaults separately from the ending-order-date cutoff, and preserve the selected worksheet/batch/line filters. A plain `Run` or a single order-line insertion helper is not a replacement for this batch initialization and finalization.
Requisition batch carry-out initializes `"Req. Wksh.-Make Order"` with `Set` and invokes `CarryOutBatchAction` on the intended accepted lines. Order/posting/receipt defaults are separate from the ending-order-date cutoff, and worksheet/batch/line filters define the selection. A plain `Run` or a single order-line insertion helper is not a replacement for this batch initialization and finalization.
Use the standard `"Carry Out Action"` dispatch for broader planning output and its configured purchase, transfer, assembly, or manufacturing choices. Do not turn every action into a new purchase order, bypass source-specific reservation transfer, or delete proposals before the owning workflow has completed their supply change.
The standard `"Carry Out Action"` dispatch handles broader planning output and its configured purchase, transfer, assembly, or manufacturing choices. Each action's supply change and source-specific reservation transfer precede proposal finalization; not every action creates a new purchase order.
Ordinary manual purchase creation that does not consume planning output is outside this rule. Users may reject or delete unwanted proposals without creating supply; temporary planning simulations, pre-carry-out enrichment, and engine-owned cleanup are also legitimate. `Delete(true)` on a requisition line is not intrinsically a defect.
The samples select an existing accepted New/Purchase item proposal with sales-demand context. Dates are explicit, the source selection remains bounded, and the clean sample leaves order creation and reservation handoff to the standard workflow; it is not a complete planning-run generator.
See sample: [`carry-out-requisition-actions-through-the-standard-workflow.good.al`](carry-out-requisition-actions-through-the-standard-workflow.good.al).
## Anti Pattern
Report code that consumes accepted persistent `"Requisition Line"` action messages, manually creates or changes supply from a subset of fields, and then deletes or marks the proposal handled without the standard carry-out/source-reservation handoff. Running purchase-field validation and the requisition delete trigger does not first move the proposal's demand links to the new purchase line.
Manually creating or changing supply from a subset of an accepted persistent `"Requisition Line"`, then deleting or marking that proposal handled, skips the standard carry-out/source-reservation handoff. Purchase-field validation and the requisition delete trigger do not first move the proposal's demand links to the new purchase line.
Require both proposal-consumption intent and a visible supply conversion. Do not flag an isolated deletion of an unwanted suggestion, an ordinary purchase-order API, or the standard carry-out engine's own insert/delete sequence.
Deleting an unwanted suggestion or creating an ordinary purchase order without consuming planning output is a separate operation. The standard carry-out engine's own insert/delete sequence participates in the source handoff rather than replacing it.
## Samples
- [`carry-out-requisition-actions-through-the-standard-workflow.bad.al`](carry-out-requisition-actions-through-the-standard-workflow.bad.al)
- [`carry-out-requisition-actions-through-the-standard-workflow.good.al`](carry-out-requisition-actions-through-the-standard-workflow.good.al)
See sample: [`carry-out-requisition-actions-through-the-standard-workflow.bad.al`](carry-out-requisition-actions-through-the-standard-workflow.bad.al).
## References

View file

@ -1,7 +1,3 @@
namespace BCQuality.SCM.Samples;
using Microsoft.Inventory.Ledger;
codeunit 50104 "SCM Item Application Bad"
{
procedure ChangeSalesQuantityApplication(ApplicationEntryNo: Integer; NewInboundEntryNo: Integer)

View file

@ -1,8 +1,3 @@
namespace BCQuality.SCM.Samples;
using Microsoft.Inventory.Ledger;
using Microsoft.Inventory.Posting;
codeunit 50105 "SCM Item Application Good"
{
procedure ChangeSalesQuantityApplication(ApplicationEntryNo: Integer; NewInboundEntryNo: Integer)

View file

@ -15,22 +15,21 @@ An `"Item Application Entry"` connects quantity application to cost flow; changi
## Best Practice
Prefer the Application Worksheet for interactive corrections. For a narrowly controlled programmatic correction of an ordinary quantity application, use the same `"Item Jnl.-Post Line"` instance for `UnApply`, reload the affected outbound item entry, then `ReApply` it to the compatible inbound entry. Complete the application's `RedoApplications`, `CostAdjust`, and `ClearApplicationLog` lifecycle; do not commit a half-completed replacement.
The Application Worksheet provides the interactive correction workflow. A narrowly controlled programmatic correction of an ordinary quantity application uses the same `"Item Jnl.-Post Line"` instance for `UnApply`, a reload of the affected outbound item entry, and `ReApply` to the compatible inbound entry. Its finalization lifecycle includes `RedoApplications`, `CostAdjust`, and `ClearApplicationLog`.
Respect the posting routines' inventory-period, correction, transfer, and drop-shipment restrictions rather than bypassing them. `"Transferred-from Entry No."`, outbound transfers, and special application types are not permission to reuse the ordinary-sales sample unchecked. Do not enable application-check bypasses or borrow the worksheet's multi-step recovery flags for a standalone transaction.
The posting routines enforce inventory-period, correction, transfer, and drop-shipment restrictions. Entries with `"Transferred-from Entry No."`, outbound transfers, and special application types are outside the ordinary-sales sample's scope. Application-check bypasses remove those protections, and the worksheet's multi-step recovery flags belong to its UI lifecycle rather than a standalone transaction.
`CostAdjust` honors automatic-cost-adjustment setup; calling it does not promise that all costs are settled when adjustment is disabled or deferred. Retain the required scheduled/manual adjustment process. Temporary application projections, extension metadata, and source-document reservation/order-tracking changes are not edits to the persistent item-application graph.
`CostAdjust` honors automatic-cost-adjustment setup; calling it does not mean all costs are settled when adjustment is disabled or deferred. Scheduled/manual adjustment remains necessary in those configurations. Temporary application projections, extension metadata, and source-document reservation/order-tracking changes are not edits to the persistent item-application graph.
See sample: [`change-item-applications-through-posting-routines.good.al`](change-item-applications-through-posting-routines.good.al).
## Anti Pattern
Report independent `Modify`, `Delete`, or replacement `Insert` operations on persistent `"Item Application Entry"` rows used to repoint a receipt/shipment application, including a change to `"Inbound Item Entry No."` that leaves remaining quantities and cost propagation untouched. Valid item numbers, matching quantities, or running table triggers do not complete reapplication.
Independent `Modify`, `Delete`, or replacement `Insert` operations on persistent `"Item Application Entry"` rows can repoint a receipt/shipment application without updating remaining quantities or cost propagation. Valid item numbers, matching quantities, and running table triggers do not complete reapplication.
Also report a visibly incomplete custom unapply/reapply transaction that omits finalization or commits between the two operations. Do not flag code merely because the standard posting/application workflow internally writes these tables.
Omitting finalization or committing between unapply and reapply exposes an incomplete replacement. The standard posting/application workflow's internal table writes differ because they participate in that lifecycle.
## Samples
- [`change-item-applications-through-posting-routines.bad.al`](change-item-applications-through-posting-routines.bad.al)
- [`change-item-applications-through-posting-routines.good.al`](change-item-applications-through-posting-routines.good.al)
See sample: [`change-item-applications-through-posting-routines.bad.al`](change-item-applications-through-posting-routines.bad.al).
## References

View file

@ -1,8 +1,3 @@
namespace BCQuality.SCM.Samples;
using Microsoft.Inventory.Journal;
using Microsoft.Inventory.Ledger;
codeunit 50100 "SCM Stock Adjustment Bad"
{
procedure PostPreparedPositiveAdjustment(ItemJournalLine: Record "Item Journal Line"; NewEntryNo: Integer)

View file

@ -1,8 +1,3 @@
namespace BCQuality.SCM.Samples;
using Microsoft.Inventory.Journal;
using Microsoft.Inventory.Posting;
codeunit 50101 "SCM Stock Adjustment Good"
{
procedure PostPreparedPositiveAdjustment(var ItemJournalLine: Record "Item Journal Line")

View file

@ -15,24 +15,23 @@ An item ledger entry is not an independently insertable stock balance. Posting c
## Best Practice
For a prepared standalone item-journal movement, enter through codeunit `"Item Jnl.-Post Line".RunWithCheck`. For a persisted journal batch, use `"Item Jnl.-Post Batch"`; for a sales, purchase, transfer, assembly, or production transaction, retain that workflow's owning document/posting orchestration rather than replacing it with a naked journal call. Let the posting engine create the ledger, value, and application records and perform its checks.
The posting entry point for a prepared standalone item-journal movement is `"Item Jnl.-Post Line".RunWithCheck`. Persisted journal batches use `"Item Jnl.-Post Batch"`; sales, purchase, transfer, assembly, and production transactions retain their owning document/posting orchestration. Those workflows create the ledger, value, and application records together with their required checks.
Extend supported posting events and pass validated journal/source data into the owning workflow. Read-only ledger queries, temporary previews, extension-owned metadata fields, and supported publisher parameters consumed by the poster are not independent ledger posting and must not be flagged merely because they assign record fields. A publisher's `var` parameter or `IsHandled` flag is not blanket authorization to recreate quantity/cost state. A change inside the posting engine itself requires tracing that engine's surrounding invariants, not a ban on its own inserts.
Supported posting events enrich validated journal/source data within the owning workflow. Read-only ledger queries, temporary ledger previews, extension-owned metadata fields, and publisher parameters consumed by the poster are not independent ledger posting. A temporary item-journal buffer can still produce persistent entries when passed to a posting codeunit. A publisher's `var` parameter or `IsHandled` flag alone does not supply the missing quantity/cost coordination; the normal engine's own inserts operate within that coordination.
Do not require every value entry to point to an item ledger entry: capacity and production WIP have their own supported posting relationships. Assembly and manufacturing posting retain order/component/routing and capacity context; one bare output/consumption call is not full order completion.
Not every value entry points to an item ledger entry: capacity and production WIP have their own supported posting relationships. Assembly and manufacturing posting retain order/component/routing and capacity context; one bare output/consumption call is not full order completion.
The clean sample takes an already prepared positive-adjustment journal line. It is not a substitute for journal preparation, batch revaluation, warehouse reconciliation, or source-document posting.
See sample: [`post-item-ledger-changes-through-item-journals.good.al`](post-item-ledger-changes-through-item-journals.good.al).
## Anti Pattern
Report extension code that independently inserts/deletes persistent `"Item Ledger Entry"` or `"Value Entry"` transaction rows, or overwrites posted quantity, remaining quantity, application identity, or cost amounts to implement a receipt, shipment, adjustment, or cost correction. `Insert(true)`, `Modify(true)`, and balanced-looking quantities do not supply the missing posting orchestration.
Independent inserts/deletes of persistent `"Item Ledger Entry"` or `"Value Entry"` transaction rows, or overwrites of posted quantity, remaining quantity, application identity, or cost amounts, bypass the coordinated receipt, shipment, adjustment, or cost-correction workflow. `Insert(true)`, `Modify(true)`, and balanced-looking quantities do not supply that orchestration: stock can change without the corresponding application/value graph, or downstream cost flow can remain stale.
Require evidence of a persistent transaction mutation and its business purpose; a table declaration or a write to a custom annotation field is insufficient. For a more specific revaluation or application defect, prefer the corresponding SCM article rather than reporting the same correction twice.
A table declaration or custom annotation-field update does not change inventory quantities or costs and is outside this transaction-state concern.
## Samples
- [`post-item-ledger-changes-through-item-journals.bad.al`](post-item-ledger-changes-through-item-journals.bad.al)
- [`post-item-ledger-changes-through-item-journals.good.al`](post-item-ledger-changes-through-item-journals.good.al)
See sample: [`post-item-ledger-changes-through-item-journals.bad.al`](post-item-ledger-changes-through-item-journals.bad.al).
## References

View file

@ -1,8 +1,3 @@
namespace BCQuality.SCM.Samples;
using Microsoft.Inventory.Journal;
using Microsoft.Inventory.Posting;
codeunit 50102 "SCM Revaluation Batch Bad"
{
procedure PostCalculatedRevaluationBatch(TemplateName: Code[10]; BatchName: Code[10])

View file

@ -1,8 +1,3 @@
namespace BCQuality.SCM.Samples;
using Microsoft.Inventory.Journal;
using Microsoft.Inventory.Posting;
codeunit 50103 "SCM Revaluation Batch Good"
{
procedure PostCalculatedRevaluationBatch(TemplateName: Code[10]; BatchName: Code[10])

View file

@ -15,22 +15,21 @@ A calculated revaluation line with nonblank `"Inventory Value Per"` represents a
## Best Practice
Post a prepared revaluation batch through `"Item Jnl.-Post Batch"`. Keep the calculated line's valuation date, aggregation scope, location/variant filters, and revaluation fields intact. The batch expands summarized values into per-entry postings and checks that the eligible inventory has not changed; for partial revaluation it also rechecks remaining quantity before posting.
Posting a prepared revaluation batch through `"Item Jnl.-Post Batch"` preserves the calculated line's valuation date, aggregation scope, location/variant filters, and revaluation fields. The batch expands summarized values into per-entry postings and checks that the eligible inventory has not changed; for partial revaluation it also rechecks remaining quantity before posting.
Do not treat the public `"Item Jnl.-Post Line".RunWithCheck` API as a replacement for that orchestration. It remains legitimate for finalized individual-entry revaluation lines within a workflow that already supplies the necessary checks; the batch itself uses the line poster. A call to that API without evidence of summarized or partial revaluation is not this defect.
The public `"Item Jnl.-Post Line".RunWithCheck` API does not replace that orchestration. It remains legitimate for finalized individual-entry revaluation lines within a workflow that already supplies the necessary checks; the batch itself uses the line poster. Ordinary quantity journals and finalized per-entry revaluations are distinct from this summarized/partial-revaluation case.
The samples explicitly require `"Value Entry Type" = Revaluation` and a nonblank `"Inventory Value Per"` in an existing calculated journal batch. They demonstrate posting, not how to calculate a new valuation or choose a standard cost.
See sample: [`post-revaluation-through-the-item-journal-batch.good.al`](post-revaluation-through-the-item-journal-batch.good.al).
## Anti Pattern
Report a loop that sends calculated aggregate revaluation lines straight to `"Item Jnl.-Post Line"`, or a custom partial-revaluation workflow that bypasses the remaining-quantity recheck visible in the standard batch. A loop over the journal is not equivalent to distributing the aggregate over its underlying item entries.
A loop that sends calculated aggregate revaluation lines straight to `"Item Jnl.-Post Line"` skips distribution over the underlying item entries. A partial-revaluation workflow without the remaining-quantity recheck can post a valuation against inventory that no longer matches the calculation.
Do not recommend directly editing existing `"Value Entry"` cost amounts or the Item's unit cost to repair the result. Use the revaluation/cost-adjustment workflow appropriate to the correction.
Directly editing existing `"Value Entry"` cost amounts or the Item's unit cost does not repair those allocation and adjustment relationships. Their correction belongs to the revaluation/cost-adjustment workflow.
## Samples
- [`post-revaluation-through-the-item-journal-batch.bad.al`](post-revaluation-through-the-item-journal-batch.bad.al)
- [`post-revaluation-through-the-item-journal-batch.good.al`](post-revaluation-through-the-item-journal-batch.good.al)
See sample: [`post-revaluation-through-the-item-journal-batch.bad.al`](post-revaluation-through-the-item-journal-batch.bad.al).
## References

View file

@ -1,10 +1,3 @@
namespace BCQuality.SCM.Samples;
using Microsoft.Inventory.Journal;
using Microsoft.Inventory.Location;
using Microsoft.Inventory.Posting;
using Microsoft.Inventory.Transfer;
codeunit 50112 "SCM Transfer Posting Bad"
{
procedure ShipTransferOrder(TransferOrderNo: Code[20]; var ItemJournalLine: Record "Item Journal Line")

View file

@ -1,8 +1,3 @@
namespace BCQuality.SCM.Samples;
using Microsoft.Inventory.Location;
using Microsoft.Inventory.Transfer;
codeunit 50113 "SCM Transfer Posting Good"
{
procedure ShipTransferOrder(TransferOrderNo: Code[20])

View file

@ -15,22 +15,21 @@ A two-step transfer order preserves a continuous quantity, reservation, and cost
## Best Practice
For a prepared non-direct transfer order without required warehouse documents, use `"TransferOrder-Post Shipment".Run` at shipment and `"TransferOrder-Post Receipt".Run` at receipt, passing the actual `"Transfer Header"`. Validate the intended quantities to ship/receive through the source document; do not assign posted quantity counters as preparation.
A prepared non-direct transfer order without required warehouse documents uses `"TransferOrder-Post Shipment".Run` at shipment and `"TransferOrder-Post Receipt".Run` at receipt, with the actual `"Transfer Header"`. Validated source quantities to ship/receive prepare the operation; posted quantity counters are results of posting.
When warehouse shipment or receipt is required, use the warehouse document posting workflow that invokes the transfer poster with its real source context. Retain the configured standard direct-transfer workflow for direct transfers; the two-step sample's in-transit guard is not a universal requirement.
Required warehouse shipment or receipt enters the warehouse document posting workflow, which invokes the transfer poster with its real source context. Direct transfers have their configured standard workflow; the two-step sample's in-transit guard is not a universal requirement.
Standalone item reclassification journals and bin movements are legitimate separate operations. Do not demand a fixed number of item ledger entries, or a nonzero `"Transferred-from Entry No."` on every transfer application: tracking/application splits and average-cost transfer handling differ. Require evidence that code is replacing completion of an existing transfer order, not merely moving stock through another supported process.
Standalone item reclassification journals and bin movements are legitimate separate operations, not completion of an existing transfer order. Tracking/application splits and average-cost handling mean transfers do not have one fixed item-entry count or a nonzero `"Transferred-from Entry No."` on every application.
See sample: [`post-transfers-through-shipment-and-receipt-codeunits.good.al`](post-transfers-through-shipment-and-receipt-codeunits.good.al).
## Anti Pattern
Report ad-hoc item postings, independent positive/negative adjustments, manually created posted-transfer rows, or changes to source shipment/receipt counters used to stand in for transfer-order posting. Updating `"Last Shipment No."` after a bare item-journal call does not create the posted shipment, source-line progress, or transfer application lineage.
Ad-hoc item postings, independent positive/negative adjustments, manually created posted-transfer rows, and direct shipment/receipt-counter changes cannot substitute for transfer-order posting. Updating `"Last Shipment No."` after a bare item-journal call does not create the posted shipment, source-line progress, or transfer application lineage.
Do not repair this by changing an existing item ledger entry's location or inventing application links. Route the source transaction through its owning shipment/receipt or configured direct-transfer workflow. Metadata enrichment inside that workflow is not itself a posting bypass.
Changing an existing item ledger entry's location or inventing application links does not repair that missing workflow. Metadata enrichment within the normal shipment/receipt or direct-transfer workflow is distinct from replacing the posting operation.
## Samples
- [`post-transfers-through-shipment-and-receipt-codeunits.bad.al`](post-transfers-through-shipment-and-receipt-codeunits.bad.al)
- [`post-transfers-through-shipment-and-receipt-codeunits.good.al`](post-transfers-through-shipment-and-receipt-codeunits.good.al)
See sample: [`post-transfers-through-shipment-and-receipt-codeunits.bad.al`](post-transfers-through-shipment-and-receipt-codeunits.bad.al).
## References

View file

@ -1,10 +1,3 @@
namespace BCQuality.SCM.Samples;
using Microsoft.Inventory.Item;
using Microsoft.Inventory.Journal;
using Microsoft.Inventory.Location;
using Microsoft.Warehouse.Journal;
codeunit 50110 "SCM Warehouse Adjustment Bad"
{
procedure ReconcileRegisteredWarehouseAdjustment(ItemNo: Code[20]; LocationCode: Code[10]; TemplateName: Code[10]; BatchName: Code[10]; PostingDate: Date; DocumentNo: Code[20]): Boolean

View file

@ -1,11 +1,3 @@
namespace BCQuality.SCM.Samples;
using Microsoft.Inventory.Item;
using Microsoft.Inventory.Journal;
using Microsoft.Inventory.Location;
using Microsoft.Inventory.Posting;
using Microsoft.Warehouse.Journal;
codeunit 50111 "SCM Warehouse Adjustment Good"
{
procedure ReconcileRegisteredWarehouseAdjustment(ItemNo: Code[20]; LocationCode: Code[10]; TemplateName: Code[10]; BatchName: Code[10]; PostingDate: Date; DocumentNo: Code[20]): Boolean

View file

@ -15,24 +15,23 @@ At a Directed Put-away and Pick location, registering an ordinary warehouse quan
## Best Practice
After the warehouse adjustment has been registered, run `"Calculate Whse. Adjustment"` for the intended item/location and prepared item-journal batch, then post the generated lines through `"Item Jnl.-Post Batch"`. The calculation derives the reconciliation by location, variant, units of measure, and tracking, marks the lines `"Warehouse Adjustment"`, and accounts for already prepared unposted adjustments.
After warehouse registration, `"Calculate Whse. Adjustment"` prepares item-journal lines for the intended item/location and batch; `"Item Jnl.-Post Batch"` posts those lines. The calculation derives the reconciliation by location, variant, units of measure, and tracking, marks the lines `"Warehouse Adjustment"`, and accounts for already prepared unposted adjustments.
Keep reconciliation separate from source-document posting: warehouse receipts/shipments use their document workflows. Intentional warehouse-only staging is valid when a separately owned reconciliation step completes the process; do not flag the registration call just because that later job is outside the diff.
Reconciliation is separate from source-document posting: warehouse receipts/shipments use their document workflows. Intentional warehouse-only staging is valid when a separately owned reconciliation step completes the process; registration need not perform both phases in one call.
Do not generalize this rule to every warehouse operation. Bin movements need not change total inventory, and warehouse tracking/expiration reclassification has a standard batch path that can also post item-journal entries. Standard reclassification and basic-location item adjustments are not this ordinary advanced-warehouse quantity-adjustment case.
Bin movements need not change total inventory, and warehouse tracking/expiration reclassification has a standard batch path that can also post item-journal entries. Standard reclassification and basic-location item adjustments are not this ordinary advanced-warehouse quantity-adjustment case.
The samples start after warehouse quantity registration and report whether inventory reconciliation completed. The clean sample calculates and posts into an empty dedicated batch; it is not a complete warehouse physical-count workflow.
See sample: [`reconcile-warehouse-adjustments-with-the-item-ledger.good.al`](reconcile-warehouse-adjustments-with-the-item-ledger.good.al).
## Anti Pattern
Report a workflow that claims to reconcile a registered advanced-warehouse quantity adjustment by posting a manually mirrored ordinary item-journal line, or that marks reconciliation complete after only warehouse registration or adjustment calculation. Calculation prepares journal lines; it does not post those lines. Require explicit synchronization intent and location/workflow evidence.
A manually mirrored ordinary item-journal line does not reconcile a registered advanced-warehouse quantity adjustment. Likewise, a reconciliation function that returns completion after only registration or adjustment calculation leaves any generated adjustment lines unposted. Calculation prepares journal lines; it does not post them.
Do not repair the defect by inventing positive/negative quantities or flipping `"Warehouse Adjustment"` on an arbitrary line. Use the calculation step so the adjustment-bin balance and the actual tracked quantities drive inventory reconciliation.
Invented positive/negative quantities or flipping `"Warehouse Adjustment"` on an arbitrary line does not establish the required relationship to the adjustment-bin balance and tracked quantities. That relationship comes from the calculation step.
## Samples
- [`reconcile-warehouse-adjustments-with-the-item-ledger.bad.al`](reconcile-warehouse-adjustments-with-the-item-ledger.bad.al)
- [`reconcile-warehouse-adjustments-with-the-item-ledger.good.al`](reconcile-warehouse-adjustments-with-the-item-ledger.good.al)
See sample: [`reconcile-warehouse-adjustments-with-the-item-ledger.bad.al`](reconcile-warehouse-adjustments-with-the-item-ledger.bad.al).
## References

View file

@ -1,8 +1,3 @@
namespace BCQuality.SCM.Samples;
using Microsoft.Inventory.Tracking;
using Microsoft.Sales.Document;
codeunit 50108 "SCM Tracking Transfer Bad"
{
procedure TransferBlanketOrderTracking(var SourceBlanketOrderLine: Record "Sales Line"; var DestinationSalesOrderLine: Record "Sales Line"; QuantityBaseToTransfer: Decimal)

View file

@ -1,7 +1,3 @@
namespace BCQuality.SCM.Samples;
using Microsoft.Sales.Document;
codeunit 50109 "SCM Tracking Transfer Good"
{
procedure TransferBlanketOrderTracking(var SourceBlanketOrderLine: Record "Sales Line"; var DestinationSalesOrderLine: Record "Sales Line"; QuantityBaseToTransfer: Decimal)

View file

@ -15,22 +15,21 @@ Moving lot/serial tracking between document lines is a source-ownership operatio
## Best Practice
Use the reservation codeunit for the source workflow. For the tracking portion of blanket-sales-order or quote conversion to a sales order, `"Sales Line-Reserve".TransferSaleLineToSalesLine` takes the existing source line, prepared destination line, and quantity to transfer in **base units**. It delegates the source/status and quantity movement to `"Create Reserv. Entry".TransferReservEntry`.
Reservation codeunits provide source-specific tracking workflows. For the tracking portion of blanket-sales-order or quote conversion to a sales order, `"Sales Line-Reserve".TransferSaleLineToSalesLine` takes the existing source line, prepared destination line, and quantity to transfer in **base units**. It delegates the source/status and quantity movement to `"Create Reserv. Entry".TransferReservEntry`.
The caller still owns document conversion and destination-line preparation; this method does not create a sales order. Keep item, variant, location, and source identity consistent, and use other source-specific wrappers for purchases, transfers, assembly, or production instead of reusing a sales wrapper indiscriminately.
The caller still owns document conversion and destination-line preparation; this method does not create a sales order. The source and destination must have compatible item, variant, location, and source identity. Purchases, transfers, assembly, and production have their own source-specific wrappers rather than sharing the sales conversion contract.
`"Item Tracking Management".CopyItemTracking` serves a different purpose: it creates Prospect copies, not a transfer of reservation ownership. That is valid for its intended copy workflow. Temporary Tracking Specification processing is also normal, and persisted historical tracking specifications are not forbidden; distinguish the working/historic representation from the current source booking.
`"Item Tracking Management".CopyItemTracking` serves a different purpose: it creates Prospect copies, not a transfer of reservation ownership. That is valid for its intended copy workflow. Temporary Tracking Specification processing and persisted historical tracking specifications are also normal; the working/historic representation differs from the current source booking.
See sample: [`transfer-item-tracking-through-source-reservation-codeunits.good.al`](transfer-item-tracking-through-source-reservation-codeunits.good.al).
## Anti Pattern
Report direct rewrites of persistent `"Reservation Entry"` source type/subtype, ID, reference number, or quantities to implement source-line conversion or a partial tracking transfer. Changing only `"Quantity (Base)"` and source keys can drop the remainder or leave the other tracking/reservation quantities attached to the wrong source.
Direct rewrites of persistent `"Reservation Entry"` source type/subtype, ID, reference number, or quantities do not perform the source-line conversion or partial tracking-transfer workflow. Changing only `"Quantity (Base)"` and source keys can drop the remainder or leave the other tracking/reservation quantities attached to the wrong source.
Also report use of a tracking copy as a replacement for moving an existing binding reservation when that intent is explicit. Do not flag a legitimate Prospect copy, temporary tracking buffer, historical tracking read, or source-specific engine call merely because it uses these tables.
A tracking copy cannot replace movement of an existing binding reservation. Legitimate Prospect copying, temporary tracking buffers, historical tracking reads, and source-specific engine calls serve distinct purposes and are not independent reservation transfers.
## Samples
- [`transfer-item-tracking-through-source-reservation-codeunits.bad.al`](transfer-item-tracking-through-source-reservation-codeunits.bad.al)
- [`transfer-item-tracking-through-source-reservation-codeunits.good.al`](transfer-item-tracking-through-source-reservation-codeunits.good.al)
See sample: [`transfer-item-tracking-through-source-reservation-codeunits.bad.al`](transfer-item-tracking-through-source-reservation-codeunits.bad.al).
## References

View file

@ -1,7 +1,3 @@
namespace BCQuality.SCM.Samples;
using Microsoft.Inventory.Item;
codeunit 50114 "SCM Additional Promise Bad"
{
procedure CanPromiseAdditionalDemand(ItemNo: Code[20]; LocationCode: Code[10]; VariantCode: Code[10]; ShipmentDate: Date; RequestedAdditionalQuantityBase: Decimal; LookaheadDateFormula: DateFormula): Boolean

View file

@ -1,9 +1,3 @@
namespace BCQuality.SCM.Samples;
using Microsoft.Foundation.Enums;
using Microsoft.Inventory.Availability;
using Microsoft.Inventory.Item;
codeunit 50115 "SCM Additional Promise Good"
{
procedure CanPromiseAdditionalDemand(ItemNo: Code[20]; LocationCode: Code[10]; VariantCode: Code[10]; ShipmentDate: Date; RequestedAdditionalQuantityBase: Decimal; LookaheadDateFormula: DateFormula): Boolean

View file

@ -15,22 +15,21 @@ application-area: [all]
## Best Practice
For an additional demand not already recorded on a source line, use `"Available to Promise".CalcQtyAvailableToPromise` with the Item's location/variant filters, date range ending on the shipment date, and the configured period/lookahead horizon. Compare in base units. Use a fresh calculation context or the codeunit's recalculation support rather than carrying cached quantities between unrelated items or requests.
For additional demand not already recorded on a source line, `"Available to Promise".CalcQtyAvailableToPromise` uses the Item's location/variant filters, date range ending on the shipment date, and configured period/lookahead horizon. Its result and the additional quantity are compared in base units. A fresh calculation context or the codeunit's recalculation support avoids carrying cached quantities between unrelated items or requests.
For an existing sales-line change, retain the source-aware order-promising/availability workflow, which accounts for the line's own quantity or delta; blindly applying an additional-demand calculation can double-count that line. Assembly and production requirements/supply likewise need the standard availability context, not just a sales-only stock subtraction.
The source-aware order-promising/availability workflow accounts for an existing sales line's own quantity or delta; applying an additional-demand calculation to that line can double-count it. Assembly and production requirements/supply likewise participate in the standard availability context, not just a sales-only stock subtraction.
An ATP result is not a reservation or a guarantee of warehouse pickability. Lot/serial constraints, bins, warehouse activity, and later concurrent changes still need their own checks. Conversely, an on-hand display, valuation report, or deliberately immediate-stock-only check is allowed to use `Item.Inventory`; do not replace its distinct business question with ATP.
An ATP result is not a reservation or a guarantee of warehouse pickability. Lot/serial constraints, bins, warehouse activity, and later concurrent changes still need their own checks. Conversely, an on-hand display, valuation report, or deliberately immediate-stock-only check can use `Item.Inventory`: it answers a different business question from ATP.
See sample: [`use-date-aware-availability-for-promising.good.al`](use-date-aware-availability-for-promising.good.al).
## Anti Pattern
Report `CalcFields(Inventory)` or an equivalent sum of item ledger quantities used as the complete decision for a dated additional-demand promise, including code that applies location/variant filters but ignores other demand and supply. Require explicit promising intent; an Inventory FlowField read alone is not a finding.
`CalcFields(Inventory)` or an equivalent item-ledger quantity sum used as the complete decision for a dated additional-demand promise ignores existing demand and incoming supply, even with location/variant filters. An Inventory FlowField read for an on-hand display has no such promising contract.
Also report a visible loss of location, variant, date, or source-line context in that calculation. Do not invent missing demand in an unseen caller or require this exact API when a visible supported workflow already supplies the correct availability semantics.
Losing location, variant, date, or source-line context changes the calculation's business meaning. Another supported workflow that preserves the same availability semantics does not have to call this exact API.
## Samples
- [`use-date-aware-availability-for-promising.bad.al`](use-date-aware-availability-for-promising.bad.al)
- [`use-date-aware-availability-for-promising.good.al`](use-date-aware-availability-for-promising.good.al)
See sample: [`use-date-aware-availability-for-promising.bad.al`](use-date-aware-availability-for-promising.bad.al).
## References

View file

@ -15,77 +15,57 @@ application-area: [all]
# AL Supply Chain Management review
Reviews AL source against the `scm` knowledge domain. This leaf invokes no
sub-skills and is composed by `al-code-review`. It accepts diffs, individual
files, and complete app folders; for a folder, inspect every relevant AL file,
not a representative sample. A folder supplies no historical baseline.
sub-skills and is composed by `al-code-review`. For a folder, inspect every
relevant AL file; a folder supplies no historical baseline.
## Source
Apply the source-surface gate in Relevance before retrieving knowledge. If the
gate passes, use READ's **Bounded retrieval for review skills** workflow with
`-Domain scm` and `-Technologies @('al')`. Consume every catalog page across
enabled layers, preserving exact paths, complete keywords, applicability, and
unknown dimensions. Select articles using catalog metadata only; never use an
index row as the basis for a finding.
Entry owns index preparation. Do not rebuild the index in this leaf. When a
helper or prepared index is unavailable or invalid, follow READ's explicit
path-discovery and bounded native-read fallback through EOF. A retrieval
failure is not an empty or clean review.
Apply Relevance's source gate before retrieval. For a relevant scope, use READ's
**Bounded retrieval for review skills** with `-Domain scm` and
`-Technologies @('al')`. Consume every catalog page across enabled layers,
preserving exact paths and applicability. Select from metadata, then read only
worklisted complete articles. Entry owns index preparation; the leaf does not
rebuild. Unavailable/invalid helpers or indexes use READ's bounded native-read
fallback, never a success-shaped empty result.
## Relevance
First inspect the supplied scope for an SCM source surface: a changed
procedure, trigger, subscriber, or bound action that writes or posts inventory,
cost, application, reservation, tracking, warehouse, transfer, or planning
state, or calculates availability for a supply/demand decision. Resolve record
and codeunit declarations, source tables, event publishers, and nearby calls
within the supplied scope. Do not infer object identity from a variable name.
Resolve changed record/codeunit types, source tables, publishers and calls.
Gate on code that mutates or posts inventory, application, reservation,
tracking, warehouse, transfer or planning state, or makes a supply/demand
availability decision. Stock displays and other read-only queries without
that decision do not pass the gate. Names, comments, captions, an `Item`
reference or a broad `ApplicationArea` alone are not signals.
If no SCM surface remains, return `not-applicable` with zero coverage
and no article-body retrieval; in mixed diffs, retain only relevant procedures
and their visible supporting context.
An `Item` reference, a field caption, an unrelated ledger read, an object name
containing "warehouse", or a broad `ApplicationArea` alone does not pass this
gate. Comments and display strings are not execution evidence. When no source
surface passes, return `not-applicable` with zero coverage and no article-body
reads. In a mixed diff, worklist only the relevant procedures and their visible
supporting context, not every AL file in the app. Unknown application areas do
not by themselves exclude codeunits or subscribers.
SCM owns `"Item Ledger Entry"`, `"Value Entry"`, `"Capacity Ledger Entry"`,
`"Warehouse Entry"` and inventory posting/application records. Pure `"G/L Entry"`,
`"Cust. Ledger Entry"`, `"Vendor Ledger Entry"`, `"Detailed Cust. Ledg. Entry"`,
`"Detailed Vendor Ledg. Entry"`, `"VAT Entry"` and financial-only posting
mutations belong to Finance. They remain outside SCM even if Finance is absent
or disabled; do not reclaim them as SCM agent findings. Ownership is not a
claim that every owned surface already has a dedicated article.
For candidates, apply READ's frontmatter matching semantics:
- `bc-version`: the target BC major version from application dependency or
host context, not the extension's own version; otherwise unknown.
- `technologies`: AL.
- `countries`: the known target localization or host context; otherwise
unknown, not a guess based on the developer's language.
- `application-area`: the actual known task/object areas, not a substituted
`[all]`. Use explicit inventory, warehousing, assembly, manufacturing, or
supply-chain context to narrow the relevant source, not as proof of a defect.
Discard nonmatching articles. Retain conditionally applicable articles only
when configuration permits; cap their findings at `medium` confidence and
name every unknown dimension in the message.
Apply READ's frontmatter filters using the target BC major version from
application dependency/host context (not the extension version), AL, known
localization and actual task/object application areas. Omitted context stays
unknown, not `[all]`; unknown areas alone do not exclude codeunits/subscribers.
Retain conditional articles only when configured, cap their findings at
`medium`, and name every unknown dimension.
## Worklist
Extract deterministic tokens from the gated source: resolved object/type
names, quoted field names, methods, enum members, and called publishers.
Lowercase invariantly, replace punctuation and whitespace runs with one hyphen,
and trim leading/trailing hyphens. Thus `"Item Ledger Entry"` becomes
`item-ledger-entry`, `"Qty. (Base)"` becomes `qty-base`, and `RunWithCheck`
becomes `runwithcheck`. Apply the same normalization to catalog keywords.
Match whole normalized tokens/phrases, not substrings such as `item` in an
unrelated identifier. Do not manufacture synonyms that are not supported by
the changed source or the targeted cues.
Extract resolved object/type names, quoted fields, methods, enum members and
publishers. Normalize these and catalog keywords by lowercasing invariantly,
replacing punctuation/whitespace runs with one hyphen and trimming hyphens:
`"Item Ledger Entry"` becomes `item-ledger-entry`; `RunWithCheck` becomes
`runwithcheck`. Match whole tokens/phrases, not identifier substrings.
Select a catalog row only when a keyword intersects these tokens, or its
path/title/description identifies the same gated source surface **and
operation**. Object declarations establish context; field assignments, calls,
and decision logic establish the operation to evaluate. A shared table name
does not select every rule using that table.
Use these targeted candidate-selection cues, resolving each slug to its
actual enabled catalog paths. They select articles to read, not findings to
emit; all platform reasoning and exceptions remain in those articles.
Select matching keywords or catalog topics only for the same source surface
**and operation**. The following cues resolve slugs to actual enabled catalog
paths; they select articles, not findings. Facts and exceptions stay in articles.
| Changed source surface and operation | Article slug |
| --- | --- |
@ -99,115 +79,43 @@ emit; all platform reasoning and exceptions remain in those articles.
| `Inventory`, `CalcQtyAvailableToPromise`, or stock sums used in a dated supply/demand promise, including changed location/variant/date filters and source-demand context | `use-date-aware-availability-for-promising` |
| `"Requisition Line"` action-message execution, accepted planning suggestions, `"Req. Wksh.-Make Order"`, `CarryOutBatchAction`, or linked supply creation/change plus requisition-line deletion | `carry-out-requisition-actions-through-the-standard-workflow` |
Do not select a cue solely from a caption, comment, or unrelated declaration.
Use the same gates for clean supported calls so their article exclusions are
evaluated, not just suspicious writes. Applicability is never an anti-pattern.
Resolve normative conflicts per READ after reading the selected complete
articles. Keep enabled-layer candidates additive unless guidance actually
contradicts; do not deduplicate merely by filename. Record losing candidates
in `suppressed` with `layer-precedence`, and configuration-hidden candidates
with `configuration`. Noncandidates are not suppressions.
Order exact worklisted paths ordinally and retrieve complete bodies in stable
chunks of at most eight, following every continuation within each chunk.
Never turn the chunk size into a top-eight cutoff. Read samples only when
needed, via their exact READ links and bounded sample retrieval.
Route clean supported calls through the same cues, not just suspicious writes.
Resolve actual normative conflicts per READ, preserving additive layers and
recording `layer-precedence`/`configuration` suppressions, not noncandidates.
Retrieve exact paths in ordinal chunks of at most eight, consume every
continuation, and never impose a top-eight cutoff. Samples use exact READ links.
## Action
Evaluate the visible source against each opened article's normative Best
Practice and Anti Pattern, including its scope and exclusions. Establish the
record's persistence, caller contract, document type/state, and affected
operation from evidence before reporting. Consult the article for treatment
of temporary buffers, supported publisher parameters, managed posting paths,
and legitimate read-only calculations; the skill itself defines no BC rule.
Do not infer missing work in an unseen caller or report every use of a routed
API. A supported alternative is not a defect.
Evaluate every opened article's normative facts, scope and exclusions against
visible persistence, caller contract, document state and operation. Emit only
concrete violations with business consequences and supported remediation; a
declaration, valid alternative or unseen caller is not evidence of a defect.
Emit only a concrete violation with its business consequence and supported
remediation. Use `major` for a demonstrated material SCM defect, `minor` for
a narrower best-practice conflict, and `blocker` only if the opened article
establishes a violated platform-level guarantee. Relevance alone produces no
finding. Deduplicate overlapping findings that prescribe the same correction;
prefer the article that owns the specific operation and retain any other
applicable article as a supporting reference.
- Use `major` for material SCM defects, `minor` for narrower best-practice
conflicts, and `blocker` only for an article-established platform guarantee.
Applicability alone produces no finding. High confidence requires unambiguous
evidence and known applicability; inference/conditional applicability caps it
at `medium`.
- Apply DO's single-owner deduplication. Equivalent findings for the same
inventory-originated posting bypass and correction have one SCM primary
owner, even when financial records are downstream. Prefer the most specific
SCM article and retain other applicable references as supporting evidence.
Distinct independent financial defects remain Finance; do not duplicate them.
- Agent findings stay strictly SCM-scoped under DO's precision bar, with
`references: []`, an `agent:` id and `minor`/`medium` ceilings. Generic AL and
other domains' concerns remain outside this leaf.
- Supply literal `suggested-code` only for a complete, local, unambiguous fix,
not a sample call that omits workflow setup/source identity. Explain omitted
mechanical-looking fixes with `suggested-code-omission-reason`.
Copy `findings[].id` verbatim from the primary article's exact catalog path;
it must equal `references[0].path`. Cite only complete articles actually read.
Use `high` confidence only for unambiguous source evidence with known
applicability, `medium` for justified inference or conditional applicability.
Never label a guessed API signature or missing workflow context high confidence.
Agent findings are optional and strictly SCM-scoped. Follow DO's precision
bar: concrete, material defects only, with `references: []`, an `agent:` id,
severity at most `minor`, and confidence at most `medium`. Omit generic AL,
style, performance, privacy, and unrelated technical findings owned by other
leaves. Do not invent a finding to compensate for an empty worklist.
For an unambiguous local fix, supply literal replacement AL in
`suggested-code`, with a location range covering exactly those lines. Do not
replace an entire business workflow with a sample call that omits the
caller's setup, filters, source identity, or validations. When a mechanical-
looking fix cannot be expressed safely, give `suggested-code-omission-reason`.
Outcomes follow DO: `completed` after evaluating the complete worklist,
including a clean result; `not-applicable` when the source gate fails;
`no-knowledge` when no applicable corpus survives filtering/configuration;
`partial` when only part of the worklist was evaluated; `failed` when no
reliable result can be produced. A source match with no matching article is
`completed` with an empty worklist, not a claimed evaluation of every SCM
concern. Explain partial/failed results and report accurate coverage.
Outcome selection follows DO, including accurate coverage and reasons for
`partial`/`failed`. No surviving applicable corpus is `no-knowledge`; an existing
corpus with no matching operation is `completed` with an empty worklist.
## Output
Return one strict JSON findings-report per DO and
`schemas/findings-report.schema.json`, with no surrounding prose. Every
finding, including an agent finding, must have
`domain: "Supply Chain Management"`. Do not set `from-sub-skill` in a leaf
report; the coordinator adds it. All locations must identify existing lines
in the supplied source, and any range must start at `location.line`.
A knowledge-backed finding with an exact article id:
```json
{
"skill": { "id": "al-scm-review", "version": 1 },
"outcome": "completed",
"summary": {
"counts": { "blocker": 0, "major": 1, "minor": 0, "info": 0 },
"coverage": { "worklist-size": 1, "items-evaluated": 1 }
},
"findings": [
{
"id": "microsoft/knowledge/scm/cancel-reservations-through-reservation-management.md",
"severity": "major",
"message": "This cancellation deletes only the negative reservation row. Use Reservation Engine Mgt. cancellation so counterpart and surviving tracking are handled by the owning workflow.",
"location": { "file": "src/CancelReservation.Codeunit.al", "line": 16 },
"references": [
{ "path": "microsoft/knowledge/scm/cancel-reservations-through-reservation-management.md" }
],
"confidence": "high",
"domain": "Supply Chain Management",
"suggested-code-omission-reason": "The replacement also requires a codeunit declaration outside the reported line."
}
],
"suppressed": []
}
```
An unrelated AL change, excluded before article retrieval:
```json
{
"skill": { "id": "al-scm-review", "version": 1 },
"outcome": "not-applicable",
"outcome-reason": "The supplied AL changes contain no SCM posting, state mutation, or supply/demand availability surface.",
"summary": {
"counts": { "blocker": 0, "major": 0, "minor": 0, "info": 0 },
"coverage": { "worklist-size": 0, "items-evaluated": 0 }
},
"findings": [],
"suppressed": []
}
```
Output conforms to the DO findings-report contract and shared schema. Every
finding MUST set `domain` to `"Supply Chain Management"`. Knowledge-backed ids
equal the primary opened article's exact catalog path. The coordinator, not
this leaf, sets `from-sub-skill`.