Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c4a9f8e
update
MichaelDieringer Jun 13, 2026
5c13823
testdata
MichaelDieringer Jun 13, 2026
a49823a
newrules
MichaelDieringer Jun 13, 2026
e68b833
Merge pull request #1 from Curabis/newrules
MichaelDieringer Jun 13, 2026
f0b473e
translate
MichaelDieringer Jun 14, 2026
52ac0d6
Merge pull request #2 from Curabis:translateRules
MichaelDieringer Jun 14, 2026
0f7021a
code refresh
MichaelDieringer Jun 15, 2026
0287ddb
Merge pull request #3 from Curabis/code-refresh
MichaelDieringer Jun 15, 2026
6ccd603
Add CURABIS shared eval + evidence scripts to custom/scripts
MichaelDieringer Jun 20, 2026
09e4e59
exposed
MichaelDieringer Jun 20, 2026
c2aaf92
Merge pull request #4 from Curabis:add-curabis-eval-scripts
MichaelDieringer Jun 20, 2026
935d756
Add mcp knowledge category with 5 rules
MichaelDieringer Jun 20, 2026
f058095
Add 3 testing knowledge files from book review
MichaelDieringer Jun 20, 2026
42a02b9
Add architecture rule: shared project memory must be in repo
MichaelDieringer Jun 21, 2026
bdda44e
Add BC MCP task lookup recipe and commit message task ID rule
MichaelDieringer Jun 21, 2026
985faf9
new global standard
MichaelDieringer Jun 21, 2026
e403866
Merge pull request #5 from Curabis:startup-&-agents
MichaelDieringer Jun 21, 2026
0b10540
AutoUpdate
MichaelDieringer Jun 21, 2026
4ef1c0e
Merge pull request #6 from Curabis:AutoUpdate
MichaelDieringer Jun 21, 2026
f957899
Add rule: scope task list to current repository
MichaelDieringer Jun 21, 2026
6765df4
[Agent] Add George Carlin - always-active bullshit detector
MichaelDieringer Jun 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .altestrunner/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"containerResultPath": "",
"launchConfigName": "",
"securePassword": "",
"userName": "",
"companyName": "",
"testSuiteName": "",
"vmUserName": "",
"vmSecurePassword": "",
"remoteContainerName": "",
"dockerHost": "",
"newPSSessionOptions": "",
"testRunnerServiceUrl": "",
"codeCoveragePath": ".altestrunner\\codecoverage.json",
"culture": "en-US"
}
65 changes: 65 additions & 0 deletions custom/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,68 @@ custom/
Fork or clone BCQuality into your own repository and add your content here. Knowledge files in `/custom/knowledge/` follow the same frontmatter schema and section requirements as every other layer. Action skills in `/custom/skills/` follow the Action Skill template defined in `/skills/`.

When agents consume BCQuality, the custom layer is loaded alongside Microsoft and Community — your overrides apply automatically.

---

# CURABIS — BCQuality customizations

## Developer onboarding (new machine)

Two files must be placed on the developer's machine. Everything else is automatic.

### 1. Global Claude Code instructions

Copy [`setup/machine/CLAUDE.md`](setup/machine/CLAUDE.md) to `~/.claude/CLAUDE.md`
and fill in your name and username.

This file tells Claude Code about CURABIS Standard in every session —
including brand-new, unconfigured repositories.

### 2. BC MCP credentials

Create `~/.bc-mcp.config.json` with your BC service-to-service credentials:

```json
{
"tenantId": "<your-tenant-id>",
"clientId": "<your-client-id>",
"clientSecret": "<your-client-secret>",
"baseUrl": "https://api.businesscentral.dynamics.com"
}
```

**Never commit this file.** It contains secrets.

## Configuring a new project

Once the two machine files are in place, open any AL-Go repository in VS Code
and tell Claude Code:

> "Konfigurer dette projekt til CURABIS Standard"

Claude fetches [`setup/curabis-standard.agent.md`](setup/curabis-standard.agent.md)
and writes all project files automatically:
`CLAUDE.md`, `.mcp.json`, `.github/.agents/`, `cspell.json`, `projectmemory/`.

The BC MCP bridge (`bc-mcp-bridge.js`) is also installed to `~/.claude/`
from this repo — so it stays up to date every time setup is re-run.

## Folder structure

```
custom/
README.md ← this file
knowledge/
architecture/ ← AL architecture rules
testing/ ← test quality rules
mcp/ ← BC MCP / API page rules
setup/
curabis-standard.agent.md ← project setup agent
bc-mcp-bridge.js ← BC MCP bridge (authoritative copy)
machine/
CLAUDE.md ← global Claude Code instructions template
templates/
bcquality.agent.md ← BCQuality review agent (per project)
immanuel.agent.md ← Rule guardian agent (per project)
cspell.json ← Standard spell-check config
```
81 changes: 81 additions & 0 deletions custom/knowledge/architecture/al-identifiers-must-be-english.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
bc-version: [all]
domain: architecture
keywords: [naming, english, enu, variable, procedure, field, caption, translation, xliff]
technologies: [al]
countries: [w1]
application-area: [all]
---

## Description

All AL identifiers must be written in English (ENU) regardless of the language
used in conversation with the developer. Translations are handled separately
via XLIFF files — never by writing Danish, German or other language identifiers
in AL source code.

This applies to:
- Variable names
- Procedure names
- Parameter names
- Field names
- Object names (tables, codeunits, pages, enums, reports)
- Enum value names
- Local and global labels (Label data type) — both the identifier and the default text

**Captions and ToolTips** may be in the target language in the source file,
but must also be covered by XLIFF translations for all supported locales.

## Anti Pattern

```al
// WRONG: Danish identifiers
var
Kreditor: Record Vendor;
Beløb: Decimal;
AntalKilo: Decimal;

procedure BeregnTotalbeløb(Antal: Decimal; Pris: Decimal): Decimal
begin
exit(Antal * Pris);
end;

field(50101; "Indgående Mængde"; Decimal) { Caption = 'Indgående Mængde'; }
```

## Best Practice

```al
// CORRECT: English identifiers, Danish captions handled via XLIFF
var
Vendor: Record Vendor;
Amount: Decimal;
QuantityKg: Decimal;

procedure CalculateTotalAmount(Quantity: Decimal; UnitPrice: Decimal): Decimal
begin
exit(Quantity * UnitPrice);
end;

field(50101; "Inbound Quantity"; Decimal) { Caption = 'Inbound Quantity'; }
// Caption translation → da-DK XLIFF: 'Indgående Mængde'

// WRONG: Danish label identifier and text
var
BeløbFejlTxt: Label 'Beløbet må ikke være negativt';

// CORRECT: English label identifier and default text — translated via XLIFF
var
AmountMustNotBeNegativeErr: Label 'Amount must not be negative.', Comment = '%1 = Amount';
```

## Conversation vs. code

The developer may describe requirements in Danish. The agent must translate
the intent into English identifiers when writing AL code:

- "opret en variabel til beløbet" → `var Amount: Decimal;`
- "procedure der beregner lagerværdien" → `procedure CalculateInventoryValue(...)`
- "felt til indgående mængde" → `field(... ; "Inbound Quantity"; Decimal)`

Never echo Danish words from the conversation directly into AL identifiers.
94 changes: 94 additions & 0 deletions custom/knowledge/architecture/clarify-before-building.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
---
bc-version: [all]
domain: architecture
keywords: [clarify, ambiguity, requirements, questions, before-coding, task-evaluation]
technologies: [al]
countries: [w1]
application-area: [all]
---

## Description

Before writing any AL code, the agent must evaluate whether the task is
unambiguously defined. If the task can be interpreted in more than one way,
the agent must ask clarifying questions and wait for answers before proceeding.

No code may be written, edited or deleted until the task is 100% clear.

This rule exists because AL code changes affect compiled extensions, running
BC environments and test databases. An incorrect assumption costs more to
undo than a clarifying question costs to ask.

## When to ask

Ask before coding if any of the following is true:

- The task mentions an object, field or flow that does not exist yet and the
design is not specified
- The expected behaviour could match more than one existing code path
- The task involves a business rule (amounts, thresholds, VAT, posting groups)
where an assumption could produce silently wrong ledger entries
- The scope is unclear: "fix this" or "make it work" without specifying what
correct behaviour looks like
- The counterparty type, document type, direction or posting flags are not
stated and cannot be unambiguously inferred from context
- The test assertions reference calculated values that depend on setup data
the agent has not yet read

## How to ask

State what you understand the task to be, then list the specific questions:

```
I understand the task as: [one sentence summary]

Before I proceed, I need clarification on:
1. [specific question]
2. [specific question]

I will not write any code until these are answered.
```

Do not write partial code while waiting. Do not write "placeholder" code.
Do not write code with TODO comments where a business decision is needed.

## When NOT to ask

Do not ask if:
- The task is fully specified with object names, field names, expected values
and posting behaviour
- The answer can be determined by reading existing source files or .md
documentation in the project
- The ambiguity is purely cosmetic (formatting, comment wording)

If the answer is in the project's source files or documentation, read those
files first and resolve the ambiguity yourself before asking the developer.

## Anti Pattern

```
// WRONG: Assuming and building
// Task: "write a test for the vendor flow"
// Agent assumes: Vendor, Incoming, Handle+Invoice, no contact
// Writes 80 lines of code
// Developer says: "I meant outgoing flow with a customer"
// Result: wasted work, possible production code changes to revert
```

## Best Practice

```
// CORRECT: Clarify first
// Task: "write a test for the vendor flow"

I understand the task as: writing a test codeunit for a Settlement Voucher
flow where the counterparty is a Vendor.

Before I proceed, I need clarification on:
1. Incoming (vendor delivers goods) or Outgoing (vendor picks up goods)?
2. Handle-only, Invoice-only, or combined Handle+Invoice in one run?
3. Should the test use an existing vendor from the database or create one
via LibraryPurchase.CreateVendor?

I will not write any code until these are answered.
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
name: commit-message-must-include-bc-task-id
description: >
Every commit message must begin with the BC task ID in [#id] format,
where id is the global taskId from the CUR MCP Active Tasks page.
layer: 2
category: architecture
---

# Commit Message Must Include BC Task ID

## Description

Every commit must reference the BC sub-task it belongs to by prefixing the
message with `[#taskId]`, where `taskId` is the globally unique, sequential
task identifier from the **CUR MCP Active Tasks** page (field `taskId`).

This links code history directly to customer-facing work items in BC, enables
time registration traceability, and is consistent with the convention already
used across Curabis teams.

## Anti Pattern

```
Add Price Lookup feature — FindPrice page, tier prices, currency conversion
```

No traceability. Impossible to find the BC task from git history.

## Best Practice

```
[#8738] Add Price Lookup feature — FindPrice page, tier prices, currency conversion
[#8738] Add 22 UI tests for PRICING LOOKUP feature
[#8738] Add translations, shared project memory and cspell config
```

## The two task numbers — use taskId, not taskNo

The sub-task has two numbers — do not confuse them:

| Field | Description | Use for |
|---|---|---|
| `taskNo` | Sequential within the project (e.g. 42) | Referencing within a project |
| `taskId` | Globally unique across all projects (e.g. 8738) | **Commit messages** |

Always use `taskId` in commit messages. It is unambiguous across all projects
and repos.

## How to find the taskId before committing

1. Get the current branch: `git branch --show-current`
2. Find the linked project via BC MCP (see `[[bc-mcp-find-active-task-for-branch]]`)
3. Read `taskId` from the matching active task
4. Prefix every commit on this branch with `[#taskId]`

If no task exists for the branch, create one first (see bc-mcp.agent.md
create-task workflow) or ask the project manager to register the work.

## Scope

All commits that reach the main branch — feature, fix, test, chore, docs.
Merge commits and auto-generated commits (renovate, al-go) are exempt.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Exposed objects must be in at least one permission set

**Rule (CURABIS-ARCH-011):** Every *exposed* object in a CURABIS app must be a member of
at least one permission set shipped by that app. "Exposed" means any object reachable from
outside the app's own UI:

- API pages (`PageType = API`)
- Web-service-enabled pages and queries (`ServiceEnabled = true`, published web services)
- API queries

## Why

An exposed object that is in no permission set is **unusable and invisible** to the users
and service identities that are supposed to call it. This is exactly how the MCP API pages
(`CUR MCP Projects`, `CUR MCP Active Tasks`, `CUR MCP Task Comments`) failed: the tables
behind them were granted, but the pages themselves had no `= X` execute permission, so the
MCP server could not see or call them.

It is also a **governance gap**: an endpoint that nobody deliberately put in a permission
set is an endpoint nobody is deciding who may reach. Exposure must be an explicit choice.

## How to apply

1. For every exposed object, add an execute entry (`page "..." = X`, `query "..." = X`) to
a permission set in the app.
2. **Sensitive endpoints go in a dedicated admin permission set** (e.g. `CUR ... Admin`)
that is *not* part of the default assignable set — so reaching them is a deliberate grant,
not the default.
3. If an object should not be reachable from outside at all, **remove the exposure** instead
(drop `PageType = API` / `ServiceEnabled`) rather than leaving an orphaned endpoint.

## How to check

Scan the app for exposed objects and verify each is referenced in a permission set:

- find every `PageType = API`, `ServiceEnabled = true`, and API query
- confirm each appears as a `page`/`query` `= X` entry in at least one `permissionset`
- flag any exposed object with no permission-set membership

A reviewer (or an automated check) should fail the change if an exposed object is missing
from every permission set.
Loading
Loading