Add BCApps citations to Tier 1+2 knowledge files; add 2 new rules

- All 7 existing Tier 1/2 knowledge files now include a BCApps Reference
  section with concrete source links and observed patterns
- New: bcpt-scenarios-must-be-app-specific — PerformanceTest apps must
  include app-domain BCPT scenarios, not only Microsoft generic samples
- New: permission-sets-must-follow-least-privilege — View/Edit/Admin
  hierarchy with IncludedPermissionSets, mirroring BCApps BusFound pattern
- api-page-key-fields-must-be-editable-on-insert clarified: SystemId as
  ODataKeyField + Editable=false is valid (auto-generated); rule applies
  to consumer-provided key fields only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Michael Dieringer 2026-06-23 17:48:37 +02:00
parent e11c1fd16c
commit 288f64df16
9 changed files with 365 additions and 355 deletions

View file

@ -1,20 +1,19 @@
# CURABIS MCP: FlowFields on API Pages Must Be CalcFields'd
# CURABIS MCP: FlowFields on API Pages Rule Summary
## Core Principle
## The Rule
**FlowFields on API pages must be explicitly calculated** via `CalcFields()` in the `OnAfterGetRecord` trigger, or they return empty values in OData responses.
FlowFields on API pages return empty or zero unless explicitly calculated. Every FlowField exposed on a `PageType = API` page must be called via `CalcFields` in the `OnAfterGetRecord` trigger — otherwise the OData response will contain empty values regardless of what the underlying data contains.
## Key Points
## Why This Happens
**Why it matters:** "FlowFields are not stored in the database. Business Central only calculates them on demand." Regular pages auto-calculate during rendering, but API pages don't—external consumers receive raw empty values otherwise.
FlowFields are not stored in the database. Business Central only calculates them on demand. Regular pages trigger calculation automatically as part of the page rendering pipeline. API pages do not — the agent or external consumer receives the raw stored (empty) value.
**What to do:** Every FlowField exposed in an API page's layout section requires inclusion in a `CalcFields()` call within `OnAfterGetRecord`. Multiple fields can be combined in one call.
## Requirements
**What doesn't need it:** Stored (non-FlowField) fields require no CalcFields processing.
- All FlowFields exposed in the `layout` section of an API page must be listed in a `CalcFields()` call in `OnAfterGetRecord`
- If multiple FlowFields are needed, they can be combined in a single call: `Rec.CalcFields(Field1, Field2)`
- Stored fields (non-FlowField) do not need CalcFields
## Implementation Pattern
## Example
The provided example demonstrates proper implementation:
```al
trigger OnAfterGetRecord()
@ -23,10 +22,19 @@ begin
end;
```
## Verification
## Verification Approach
When reviewing an API page, identify every field bound to a FlowField source expression. Confirm each appears in the `OnAfterGetRecord` CalcFields call. Any FlowField missing from CalcFields is a defect — it will silently return empty to the MCP consumer.
Audit API pages by:
1. Identifying every field bound to FlowField sources in the layout
2. Confirming each appears in the `OnAfterGetRecord` CalcFields statement
3. Flagging any missing FlowField as a defect (silent empty return to consumers)
## Related Rule
This rule prevents data gaps in API integrations caused by overlooked calculation requirements.
CURABIS-MCP-002 — Stored derived fields must be recalculated in OnAfterGetRecord, not exposed directly.
## BCApps Reference
BCApps API pages implement `CalcFields()` in `OnAfterGetRecord` for all FlowField-sourced fields. The BCPT Suite API page demonstrates the correct pattern for API pages with computed data.
- **Source:** https://github.com/microsoft/BCApps/blob/main/src/Tools/Performance%20Toolkit/App/src/BCPTSuiteAPI.Page.al
- **Additional API pages:** https://github.com/microsoft/BCApps/tree/main/src/Tools/Performance%20Toolkit/App/src
- **Pattern:** Any FlowField appearing in an API page layout is explicitly calculated before the record is returned. Microsoft does not rely on implicit calculation in API contexts.

View file

@ -1,40 +1,43 @@
# CURABIS MCP: ODataKeyFields Must Be Editable for Create Operations
# CURABIS MCP: ODataKeyFields Editability Rule
## Core Principle
## The Rule
Fields declared in `ODataKeyFields` that identify the record must not have `Editable = false` when the API page allows insert. If they are read-only, the OData API rejects them as unknown properties on POST — the create operation fails and the caller receives a `BadRequest` error.
Key fields declared in `ODataKeyFields` cannot have `Editable = false` when the API page permits inserts and **the field is consumer-provided**. This restriction causes the OData layer to reject the field as an unknown property during POST operations.
## Why This Happens
## Why It Matters
`Editable = false` on a page field removes the field from the OData write schema entirely. When a consumer POSTs a new record and includes the key field in the body, BC cannot match it to any writable property and rejects the request.
When a field is marked read-only, Business Central removes it from the OData write schema. If a consumer attempts to POST a new record with that key field in the request body, the system cannot match it to any writable property and returns a `BadRequest` error.
## Pattern to Avoid
## Problematic vs. Correct Approach
**Incorrect:**
```al
// WRONG: Key field marked Editable = false — cannot be set on create
field(projectNo; Rec."Project No.")
{
Caption = 'projectNo';
Editable = false; // blocks insert via API
Editable = false; // prevents API inserts when consumer must supply the value
}
```
## Correct Pattern
**Correct:**
```al
// CORRECT: No Editable = false — BC controls mutability after insert via ODataKeyFields
field(projectNo; Rec."Project No.")
{
Caption = 'projectNo';
// No Editable = false — consumer supplies this on POST
}
```
## Requirements
## Key Takeaways
- Fields listed in `ODataKeyFields` must not carry `Editable = false` on pages where `InsertAllowed = true`
- Fields that should be read-only after creation but writable on insert need no special property — OData key semantics handle immutability after the record exists
- Non-key fields that are genuinely read-only may still use `Editable = false`
- Every **consumer-provided** field referenced in `ODataKeyFields` on pages where `InsertAllowed = true` must remain editable
- The OData specification itself enforces immutability of key fields post-creation — no additional markup required
- Non-key fields can still use `Editable = false` without triggering this issue
- Test create operations via your OData endpoint to verify compliance
## Verification
## BCApps Reference
On any API page with `InsertAllowed = true`, confirm that every field referenced in `ODataKeyFields` does not have `Editable = false` in its field definition. A create test via the OData endpoint is the definitive check.
BCApps `BCPTSuiteAPI.Page.al` uses `ODataKeyFields = SystemId` with `SystemId` marked `Editable = false`. This is a **valid exception**`SystemId` is a system-generated GUID that BC assigns automatically on insert. The consumer never provides it in a POST body, so marking it non-editable does not break API inserts.
- **Source:** https://github.com/microsoft/BCApps/blob/main/src/Tools/Performance%20Toolkit/App/src/BCPTSuiteAPI.Page.al
- **Clarification from BCApps:** The rule distinguishes two key field types:
- **Auto-generated keys** (`SystemId`, auto-numbered codes): May be `Editable = false` — BC supplies the value, not the consumer.
- **Consumer-provided keys** (`"Project No."`, `"Code"`, `"Entry No."`): Must remain editable — the POST request must include this value and BC must accept it.