Hardening: stable-branch som udrulningskanal - merge til main er ikke laengere deploy

Alle forbrugere laeser nu fra stable i stedet for main:
- curabis-standard.agent.md v10: BASE/AGENTS_BASE + self-heal-URL -> stable
- sync-bcquality-knowledge.ps1: $branch = stable
- machine/CLAUDE.md: auto-update gater paa stable-SHA og fetcher setup fra stable
- templates/bcquality.agent.md + al-triage.agent.md: knowledge-URLer -> stable
  (+ fix af doed reference: branch-merge-to-main-workflow.md var omdoebt til
  feature-branch-must-merge-to-track-branch.md - templaten shippede et 404)
- francis.agent.md + Invoke-CurabisEvidence.ps1: raw-base -> stable
- CONSUMPTION.md: Release channel-sektion med promote-procedure og rationale

Deploy sker herefter kun ved bevidst fast-forward af stable (kun Michael).
Kanal oprettet: stable @ 57b8292, tag v1.0.0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Michael Dieringer 2026-07-02 00:02:44 +02:00
parent 57b8292848
commit 3ef2e95af6
8 changed files with 294 additions and 269 deletions

View file

@ -38,6 +38,31 @@ future CI/PR-review integration:
currently consumed by nothing. Keep it — but do not mistake it for active currently consumed by nothing. Keep it — but do not mistake it for active
configuration of the session model. configuration of the session model.
## Release channel: `stable`
Merging to `main` is **not** a deployment. All consumers — the machine
CLAUDE.md auto-update, `sync-bcquality-knowledge.ps1`, the setup agent's
fetch URLs, and the agent templates' knowledge references — read from the
**`stable`** branch, never from `main`. `main` is where PRs land and CI runs;
`stable` is what every developer machine actually executes.
Deploying is a deliberate act (Michael only):
git checkout stable
git merge --ff-only main
git push origin stable
git checkout main
Optionally cut a version tag at the same commit (`git tag vX.Y.Z && git push
origin vX.Y.Z`) for a historical record. If a bad change reaches `stable`,
roll back by force-moving `stable` to the previous good commit — consumers
follow the branch, so recovery is one push.
Rationale: `main` used to be the live deploy channel — any merge silently
overwrote `bc-mcp-bridge.js` (which handles S2S credentials) on every
developer machine at next session start. The `stable` gate separates "CI
accepted it" from "the organization runs it".
## Known deltas to close before activating the Entry flow ## Known deltas to close before activating the Entry flow
1. **`custom/skills/` is empty.** The CURABIS review pass lives in the 1. **`custom/skills/` is empty.** The CURABIS review pass lives in the

View file

@ -78,7 +78,7 @@ reviews what happened. He asks one question about every significant event:
He compares against the full BCQuality knowledge base: He compares against the full BCQuality knowledge base:
``` ```
BASE = https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge BASE = https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge
``` ```
Domains: `architecture/`, `testing/`, `mcp/` Domains: `architecture/`, `testing/`, `mcp/`

View file

@ -29,7 +29,7 @@ param(
[string]$BCQualityHome, [string]$BCQualityHome,
# Bruges naar der ikke er en lokal klon: knowledge-filer HTTP-tjekkes herfra. # Bruges naar der ikke er en lokal klon: knowledge-filer HTTP-tjekkes herfra.
[string]$RawBase = 'https://raw.githubusercontent.com/Curabis/BCQuality/main', [string]$RawBase = 'https://raw.githubusercontent.com/Curabis/BCQuality/stable',
[switch]$Quiet [switch]$Quiet
) )

View file

@ -1,7 +1,7 @@
--- ---
kind: action-skill kind: action-skill
id: curabis-standard-setup id: curabis-standard-setup
version: 9 version: 10
title: CURABIS Standard — Project Setup title: CURABIS Standard — Project Setup
description: > description: >
Configures a new or existing repository to the CURABIS Standard development Configures a new or existing repository to the CURABIS Standard development
@ -37,8 +37,8 @@ Detect which mode based on the trigger phrase and proceed accordingly.
## Source URLs (BCQuality — always fetch fresh) ## Source URLs (BCQuality — always fetch fresh)
``` ```
BASE = https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/setup BASE = https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/setup
AGENTS_BASE = https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/agents AGENTS_BASE = https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/agents
``` ```
| Artefakt | URL | | Artefakt | URL |
@ -168,7 +168,7 @@ If `~/.claude/bcquality-knowledge/` is missing or empty, self-heal before contin
If the sync script itself is missing, first download it AS RAW BYTES (do not If the sync script itself is missing, first download it AS RAW BYTES (do not
decode/re-encode — use `Invoke-WebRequest -OutFile`) from decode/re-encode — use `Invoke-WebRequest -OutFile`) from
`https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/setup/sync-bcquality-knowledge.ps1` `https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/setup/sync-bcquality-knowledge.ps1`
to `~/.claude/sync-bcquality-knowledge.ps1`, then run it. to `~/.claude/sync-bcquality-knowledge.ps1`, then run it.
These rules are always active. These rules are always active.

View file

@ -8,11 +8,11 @@ These instructions apply to every Claude Code session, regardless of project.
If the current project has `.github/.agents/bcquality.agent.md`: If the current project has `.github/.agents/bcquality.agent.md`:
1. Fetch the latest commit SHA for `custom/setup/` in BCQuality: 1. Fetch the latest commit SHA for `custom/setup/` in BCQuality:
`https://api.github.com/repos/Curabis/BCQuality/commits?path=custom/setup&per_page=1` `https://api.github.com/repos/Curabis/BCQuality/commits?path=custom/setup&sha=stable&per_page=1`
2. Read stored SHA from `~/.claude/.bcquality-version` (may not exist yet) 2. Read stored SHA from `~/.claude/.bcquality-version` (may not exist yet)
3. If SHA is different (or file missing): 3. If SHA is different (or file missing):
a. Fetch and execute Mode B from: a. Fetch and execute Mode B from:
`https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/setup/curabis-standard.agent.md` `https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/setup/curabis-standard.agent.md`
b. Write new SHA to `~/.claude/.bcquality-version` b. Write new SHA to `~/.claude/.bcquality-version`
c. Report: "BCQuality opdateret til [short-sha] — [antal] filer tjekket" c. Report: "BCQuality opdateret til [short-sha] — [antal] filer tjekket"
4. If SHA is unchanged: continue silently 4. If SHA is unchanged: continue silently
@ -25,7 +25,7 @@ unless something actually changed.
When the user says either of these commands, fetch and follow the setup agent: When the user says either of these commands, fetch and follow the setup agent:
``` ```
https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/setup/curabis-standard.agent.md https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/setup/curabis-standard.agent.md
``` ```
- **"Konfigurer dette projekt til CURABIS Standard"** → fuld setup af nyt repo - **"Konfigurer dette projekt til CURABIS Standard"** → fuld setup af nyt repo

View file

@ -1,128 +1,128 @@
# Refresh the MACHINE-LOCAL mirror of the Curabis BCQuality knowledge base. # Refresh the MACHINE-LOCAL mirror of the Curabis BCQuality knowledge base.
# #
# Mirrors three layers from https://github.com/Curabis/BCQuality: # Mirrors three layers from https://github.com/Curabis/BCQuality:
# custom/ - Curabis org-specific rules (ALWAYS read in full each session) # custom/ - Curabis org-specific rules (ALWAYS read in full each session)
# community/ - BC community patterns (loaded on relevance via INDEX.md) # community/ - BC community patterns (loaded on relevance via INDEX.md)
# microsoft/ - platform guardrails (loaded on relevance via INDEX.md) # microsoft/ - platform guardrails (loaded on relevance via INDEX.md)
# #
# The upstream file list is discovered dynamically from the GitHub tree API, so # The upstream file list is discovered dynamically from the GitHub tree API, so
# new/removed upstream files propagate automatically - nothing is hardcoded. # new/removed upstream files propagate automatically - nothing is hardcoded.
# After downloading, an INDEX.md is generated (one line per file, with domain + # After downloading, an INDEX.md is generated (one line per file, with domain +
# keywords from each file's frontmatter) so an agent can scan and pull only the # keywords from each file's frontmatter) so an agent can scan and pull only the
# files relevant to a task instead of loading all ~100 every session. # files relevant to a task instead of loading all ~100 every session.
# #
# The mirror is per developer machine (~/.claude/bcquality-knowledge/), shared # The mirror is per developer machine (~/.claude/bcquality-knowledge/), shared
# by every CURABIS repo on it. It is NEVER committed to a project repository - # by every CURABIS repo on it. It is NEVER committed to a project repository -
# see BCQuality rule bcquality-knowledge-must-mirror-to-machine-not-repo. # see BCQuality rule bcquality-knowledge-must-mirror-to-machine-not-repo.
# One sync per machine covers every repo. Run periodically: # One sync per machine covers every repo. Run periodically:
# powershell -ExecutionPolicy Bypass -File "$env:USERPROFILE\.claude\sync-bcquality-knowledge.ps1" # powershell -ExecutionPolicy Bypass -File "$env:USERPROFILE\.claude\sync-bcquality-knowledge.ps1"
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
$repo = 'Curabis/BCQuality' $repo = 'Curabis/BCQuality'
$branch = 'main' $branch = 'stable'
$dest = Join-Path $env:USERPROFILE '.claude\bcquality-knowledge' $dest = Join-Path $env:USERPROFILE '.claude\bcquality-knowledge'
$staging = "$dest.tmp" $staging = "$dest.tmp"
$rawBase = "https://raw.githubusercontent.com/$repo/$branch" $rawBase = "https://raw.githubusercontent.com/$repo/$branch"
$treeUrl = "https://api.github.com/repos/$repo/git/trees/$branch" + '?recursive=1' $treeUrl = "https://api.github.com/repos/$repo/git/trees/$branch" + '?recursive=1'
# Upstream path prefix -> local layer folder # Upstream path prefix -> local layer folder
$layerMap = [ordered]@{ $layerMap = [ordered]@{
'custom/knowledge/' = 'custom' 'custom/knowledge/' = 'custom'
'community/knowledge/' = 'community' 'community/knowledge/' = 'community'
'microsoft/knowledge/' = 'microsoft' 'microsoft/knowledge/' = 'microsoft'
} }
function Get-Frontmatter { function Get-Frontmatter {
param([string]$Path) param([string]$Path)
$lines = Get-Content -Path $Path $lines = Get-Content -Path $Path
$fm = @{} $fm = @{}
if ($lines.Count -eq 0 -or $lines[0].Trim() -ne '---') { return $fm } if ($lines.Count -eq 0 -or $lines[0].Trim() -ne '---') { return $fm }
for ($i = 1; $i -lt $lines.Count; $i++) { for ($i = 1; $i -lt $lines.Count; $i++) {
if ($lines[$i].Trim() -eq '---') { break } if ($lines[$i].Trim() -eq '---') { break }
if ($lines[$i] -match '^\s*([\w-]+):\s*(.*)$') { if ($lines[$i] -match '^\s*([\w-]+):\s*(.*)$') {
$fm[$matches[1]] = $matches[2].Trim() $fm[$matches[1]] = $matches[2].Trim()
} }
} }
return $fm return $fm
} }
Write-Host "Fetching file tree from $repo@$branch ..." Write-Host "Fetching file tree from $repo@$branch ..."
$headers = @{ 'User-Agent' = 'wareco-bcquality-sync'; 'Accept' = 'application/vnd.github+json' } $headers = @{ 'User-Agent' = 'wareco-bcquality-sync'; 'Accept' = 'application/vnd.github+json' }
$tree = (Invoke-RestMethod -Uri $treeUrl -Headers $headers).tree $tree = (Invoke-RestMethod -Uri $treeUrl -Headers $headers).tree
# Build the download worklist from the tree # Build the download worklist from the tree
$files = @() $files = @()
foreach ($node in $tree) { foreach ($node in $tree) {
if ($node.type -ne 'blob') { continue } if ($node.type -ne 'blob') { continue }
if ($node.path -notlike '*.md') { continue } if ($node.path -notlike '*.md') { continue }
foreach ($prefix in $layerMap.Keys) { foreach ($prefix in $layerMap.Keys) {
if ($node.path.StartsWith($prefix)) { if ($node.path.StartsWith($prefix)) {
$relative = $node.path.Substring($prefix.Length) # e.g. performance/avoid-commit-inside-loops.md $relative = $node.path.Substring($prefix.Length) # e.g. performance/avoid-commit-inside-loops.md
$files += [pscustomobject]@{ $files += [pscustomobject]@{
Url = "$rawBase/$($node.path)" Url = "$rawBase/$($node.path)"
Layer = $layerMap[$prefix] Layer = $layerMap[$prefix]
Relative = $relative Relative = $relative
LocalPath = Join-Path $staging (Join-Path $layerMap[$prefix] $relative) LocalPath = Join-Path $staging (Join-Path $layerMap[$prefix] $relative)
} }
break break
} }
} }
} }
if ($files.Count -eq 0) { throw 'No knowledge files found in upstream tree - aborting.' } if ($files.Count -eq 0) { throw 'No knowledge files found in upstream tree - aborting.' }
# Download into a staging folder so a mid-run failure never wipes the live copy # Download into a staging folder so a mid-run failure never wipes the live copy
if (Test-Path $staging) { Remove-Item -Recurse -Force $staging } if (Test-Path $staging) { Remove-Item -Recurse -Force $staging }
New-Item -ItemType Directory -Force $staging | Out-Null New-Item -ItemType Directory -Force $staging | Out-Null
Write-Host "Downloading $($files.Count) knowledge files ..." Write-Host "Downloading $($files.Count) knowledge files ..."
$rc = 0 $rc = 0
foreach ($f in $files) { foreach ($f in $files) {
New-Item -ItemType Directory -Force (Split-Path $f.LocalPath) | Out-Null New-Item -ItemType Directory -Force (Split-Path $f.LocalPath) | Out-Null
try { try {
Invoke-WebRequest -Uri $f.Url -OutFile $f.LocalPath -UseBasicParsing -ErrorAction Stop Invoke-WebRequest -Uri $f.Url -OutFile $f.LocalPath -UseBasicParsing -ErrorAction Stop
Write-Host "OK $($f.Layer)/$($f.Relative)" Write-Host "OK $($f.Layer)/$($f.Relative)"
} catch { } catch {
Write-Error "FAIL $($f.Layer)/$($f.Relative)" Write-Error "FAIL $($f.Layer)/$($f.Relative)"
$rc = 1 $rc = 1
} }
} }
# Generate INDEX.md (relevance index for all layers) # Generate INDEX.md (relevance index for all layers)
Write-Host "Generating INDEX.md ..." Write-Host "Generating INDEX.md ..."
$idx = [System.Collections.Generic.List[string]]::new() $idx = [System.Collections.Generic.List[string]]::new()
$idx.Add('# BCQuality Knowledge Index') $idx.Add('# BCQuality Knowledge Index')
$idx.Add('') $idx.Add('')
$idx.Add('<!-- Generated by sync-bcquality-knowledge.ps1 - do not edit by hand. -->') $idx.Add('<!-- Generated by sync-bcquality-knowledge.ps1 - do not edit by hand. -->')
$idx.Add('') $idx.Add('')
$idx.Add('Layers: `custom` is ALWAYS read in full each session. For `community` and') $idx.Add('Layers: `custom` is ALWAYS read in full each session. For `community` and')
$idx.Add('`microsoft`, scan this index and read only the files whose domain/keywords') $idx.Add('`microsoft`, scan this index and read only the files whose domain/keywords')
$idx.Add('match the task at hand.') $idx.Add('match the task at hand.')
$idx.Add('') $idx.Add('')
foreach ($layer in @('custom', 'community', 'microsoft')) { foreach ($layer in @('custom', 'community', 'microsoft')) {
$layerFiles = $files | Where-Object { $_.Layer -eq $layer } | Sort-Object Relative $layerFiles = $files | Where-Object { $_.Layer -eq $layer } | Sort-Object Relative
if (-not $layerFiles) { continue } if (-not $layerFiles) { continue }
$note = if ($layer -eq 'custom') { ' (always-on)' } else { ' (load on relevance)' } $note = if ($layer -eq 'custom') { ' (always-on)' } else { ' (load on relevance)' }
$idx.Add("## $layer$note") $idx.Add("## $layer$note")
$idx.Add('') $idx.Add('')
foreach ($f in $layerFiles) { foreach ($f in $layerFiles) {
$fm = Get-Frontmatter $f.LocalPath $fm = Get-Frontmatter $f.LocalPath
$domain = if ($fm.ContainsKey('domain')) { $fm['domain'] } else { '' } $domain = if ($fm.ContainsKey('domain')) { $fm['domain'] } else { '' }
$keywords = if ($fm.ContainsKey('keywords')) { $fm['keywords'].Trim('[', ']') } else { '' } $keywords = if ($fm.ContainsKey('keywords')) { $fm['keywords'].Trim('[', ']') } else { '' }
$rel = $f.Relative -replace '\.md$', '' $rel = $f.Relative -replace '\.md$', ''
$idx.Add("- ``$layer/$rel`` - domain: $domain; keywords: $keywords") $idx.Add("- ``$layer/$rel`` - domain: $domain; keywords: $keywords")
} }
$idx.Add('') $idx.Add('')
} }
Set-Content -Path (Join-Path $staging 'INDEX.md') -Value $idx -Encoding utf8 Set-Content -Path (Join-Path $staging 'INDEX.md') -Value $idx -Encoding utf8
# Swap staging into place # Swap staging into place
if (Test-Path $dest) { Remove-Item -Recurse -Force $dest } if (Test-Path $dest) { Remove-Item -Recurse -Force $dest }
Rename-Item -Path $staging -NewName (Split-Path $dest -Leaf) Rename-Item -Path $staging -NewName (Split-Path $dest -Leaf)
Write-Host '' Write-Host ''
Write-Host "Done. $($files.Count) files across $($layerMap.Count) layers." Write-Host "Done. $($files.Count) files across $($layerMap.Count) layers."
Write-Host "Machine-local mirror updated: $dest" Write-Host "Machine-local mirror updated: $dest"
exit $rc exit $rc

View file

@ -1,110 +1,110 @@
--- ---
kind: action-skill kind: action-skill
id: curabis-al-triage id: curabis-al-triage
version: 1 version: 1
title: CURABIS AL triage title: CURABIS AL triage
description: On-demand reactive diagnosis of a failing build, test, or runtime error. Reproduces the symptom, finds the root cause, and recommends a minimal fix. Read-only - never applies changes. description: On-demand reactive diagnosis of a failing build, test, or runtime error. Reproduces the symptom, finds the root cause, and recommends a minimal fix. Read-only - never applies changes.
inputs: [error-message, file-path, test-name, stack-trace] inputs: [error-message, file-path, test-name, stack-trace]
outputs: [diagnosis-report] outputs: [diagnosis-report]
bc-version: [all] bc-version: [all]
technologies: [al] technologies: [al]
countries: [w1] countries: [w1]
application-area: [all] application-area: [all]
domain: diagnostics domain: diagnostics
keywords: [triage, diagnose, root-cause, minimal-fix, compile-error, test-failure, runtime-error, reproduce, regression] keywords: [triage, diagnose, root-cause, minimal-fix, compile-error, test-failure, runtime-error, reproduce, regression]
sub-skills: sub-skills:
- microsoft/skills/review/al-code-review.md - microsoft/skills/review/al-code-review.md
--- ---
# CURABIS AL triage # CURABIS AL triage
## Who I Am ## Who I Am
My name is Dominique Jean Larrey. I was born on 8 July 1766 in Beaudéan, France, My name is Dominique Jean Larrey. I was born on 8 July 1766 in Beaudéan, France,
and died on 25 July 1842 in Lyon. I was chief surgeon of Napoleon Bonaparte's Grande and died on 25 July 1842 in Lyon. I was chief surgeon of Napoleon Bonaparte's Grande
Armée and I served in over sixty battles across twenty years of almost continuous war. Armée and I served in over sixty battles across twenty years of almost continuous war.
I invented **triage**. Before my system, the wounded were treated in the order they I invented **triage**. Before my system, the wounded were treated in the order they
arrived at the field hospital — which meant those nearest the front were treated last, arrived at the field hospital — which meant those nearest the front were treated last,
often after hours of waiting, often too late. I reversed this. I classified the wounded often after hours of waiting, often too late. I reversed this. I classified the wounded
by urgency of need, not by rank or order of arrival, and I moved treatment forward to by urgency of need, not by rank or order of arrival, and I moved treatment forward to
the battlefield rather than waiting for the wounded to come to me. the battlefield rather than waiting for the wounded to come to me.
I designed the **flying ambulance** — a horse-drawn vehicle that could move rapidly I designed the **flying ambulance** — a horse-drawn vehicle that could move rapidly
across the battlefield to collect the wounded during the fighting itself, not after it. across the battlefield to collect the wounded during the fighting itself, not after it.
This was radical. The previous practice was to wait until a battle ended. By then, This was radical. The previous practice was to wait until a battle ended. By then,
many who could have been saved were not. many who could have been saved were not.
Napoleon called me "the most virtuous man I have ever known." After Waterloo, where I Napoleon called me "the most virtuous man I have ever known." After Waterloo, where I
served on the losing side, the Duke of Wellington ordered that my life be spared on served on the losing side, the Duke of Wellington ordered that my life be spared on
the battlefield. Enemies respected the work. the battlefield. Enemies respected the work.
I did not work on the easy cases. I worked on the ones where speed and accuracy I did not work on the easy cases. I worked on the ones where speed and accuracy
of diagnosis were the difference between recovery and loss. of diagnosis were the difference between recovery and loss.
Here at CURABIS, I am called when something is already broken. I find the cause. Here at CURABIS, I am called when something is already broken. I find the cause.
I recommend the minimal fix. I do not apply it — that is the developer's decision. I recommend the minimal fix. I do not apply it — that is the developer's decision.
On-demand specialist. Invoke this agent when something is **already broken** - a build On-demand specialist. Invoke this agent when something is **already broken** - a build
error, a failing test, an AppSourceCop violation, or a runtime error - and you need a error, a failing test, an AppSourceCop violation, or a runtime error - and you need a
diagnosis, not a feature. This agent operates outside the normal build loop, runs diagnosis, not a feature. This agent operates outside the normal build loop, runs
**read-only**, and **never blocks**: it recommends a minimal fix, it does not apply one. **read-only**, and **never blocks**: it recommends a minimal fix, it does not apply one.
Loop: **reproduce -> root-cause -> minimal-fix recommendation.** Loop: **reproduce -> root-cause -> minimal-fix recommendation.**
## Source ## Source
Layer 1 - Microsoft BCQuality: https://github.com/microsoft/BCQuality Layer 1 - Microsoft BCQuality: https://github.com/microsoft/BCQuality
Layer 2 - CURABIS custom knowledge (fetch before citing a finding): Layer 2 - CURABIS custom knowledge (fetch before citing a finding):
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/architecture/pages-must-not-contain-business-logic.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/architecture/pages-must-not-contain-business-logic.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/architecture/namespace-must-be-verified-from-source.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/architecture/namespace-must-be-verified-from-source.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/architecture/al-identifiers-must-be-english.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/architecture/al-identifiers-must-be-english.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/architecture/clarify-before-building.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/architecture/clarify-before-building.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/testing/test-setup-must-use-library-codeunit.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/testing/test-setup-must-use-library-codeunit.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/testing/test-data-must-be-random-and-complete.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/testing/test-data-must-be-random-and-complete.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/testing/tests-must-adapt-to-existing-code.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/testing/tests-must-adapt-to-existing-code.md
If a source is unreachable, **degrade gracefully**: fall back to the triage protocol If a source is unreachable, **degrade gracefully**: fall back to the triage protocol
below plus the CURABIS-ARCH rules in `bcquality.agent.md`, note that BCQuality was below plus the CURABIS-ARCH rules in `bcquality.agent.md`, note that BCQuality was
unavailable, and carry on. Nothing blocks. unavailable, and carry on. Nothing blocks.
## Tools ## Tools
Use the AL MCP server (already allowed in `.claude/settings.json`) to reproduce and Use the AL MCP server (already allowed in `.claude/settings.json`) to reproduce and
localize before forming any hypothesis: localize before forming any hypothesis:
- `al_compile` / `al_getdiagnostics` - reproduce a build error and read the exact diagnostic code. - `al_compile` / `al_getdiagnostics` - reproduce a build error and read the exact diagnostic code.
- `al_run_tests` - reproduce a failing test. - `al_run_tests` - reproduce a failing test.
- `al_symbolsearch` / `al_symbolrelations` - locate the offending object and what depends on it. - `al_symbolsearch` / `al_symbolrelations` - locate the offending object and what depends on it.
- `al_getpackagedependencies` - check for version/dependency mismatches. - `al_getpackagedependencies` - check for version/dependency mismatches.
## Action - triage protocol ## Action - triage protocol
CURABIS-TRIAGE-001 Reproduce first. Capture the exact symptom (diagnostic code, test CURABIS-TRIAGE-001 Reproduce first. Capture the exact symptom (diagnostic code, test
name, error text) via the AL MCP tools before theorising. No reproduction = state that name, error text) via the AL MCP tools before theorising. No reproduction = state that
and stop; do not guess. and stop; do not guess.
CURABIS-TRIAGE-002 Localize. Identify the precise object, procedure, and line. Use CURABIS-TRIAGE-002 Localize. Identify the precise object, procedure, and line. Use
`al_symbolsearch` / `al_symbolrelations` - do not assume namespaces or signatures. `al_symbolsearch` / `al_symbolrelations` - do not assume namespaces or signatures.
CURABIS-TRIAGE-003 Root-cause, not symptom. Name the underlying cause. A compile error on CURABIS-TRIAGE-003 Root-cause, not symptom. Name the underlying cause. A compile error on
a Modify() is a symptom; the missing FindSet(true) or the page-level data write is the a Modify() is a symptom; the missing FindSet(true) or the page-level data write is the
cause. Cross-check against CURABIS-ARCH-001..010. cause. Cross-check against CURABIS-ARCH-001..010.
CURABIS-TRIAGE-004 Minimal fix. Recommend the smallest change that removes the root cause. CURABIS-TRIAGE-004 Minimal fix. Recommend the smallest change that removes the root cause.
No refactors, no opportunistic cleanup, no scope creep. No refactors, no opportunistic cleanup, no scope creep.
CURABIS-TRIAGE-005 Cite or flag. Back every finding with a specific BCQuality knowledge CURABIS-TRIAGE-005 Cite or flag. Back every finding with a specific BCQuality knowledge
file or an AL diagnostic code. A finding with no citation must be labelled file or an AL diagnostic code. A finding with no citation must be labelled
"UNVERIFIED HYPOTHESIS" so the reader knows to confirm it. "UNVERIFIED HYPOTHESIS" so the reader knows to confirm it.
CURABIS-TRIAGE-006 Read-only. Output a diagnosis report only. Never edit, never apply the CURABIS-TRIAGE-006 Read-only. Output a diagnosis report only. Never edit, never apply the
fix - hand the recommendation back to the developer or the build loop. fix - hand the recommendation back to the developer or the build loop.
CURABIS-TRIAGE-007 Regression awareness. Before recommending, check what `al_symbolrelations` CURABIS-TRIAGE-007 Regression awareness. Before recommending, check what `al_symbolrelations`
says depends on the object so the minimal fix does not break callers. says depends on the object so the minimal fix does not break callers.
## Output format ## Output format
``` ```
SYMPTOM <reproduced error / failing test, with diagnostic code> SYMPTOM <reproduced error / failing test, with diagnostic code>
LOCATION <object - procedure - line> LOCATION <object - procedure - line>
ROOT CAUSE <the actual cause, with citation or UNVERIFIED HYPOTHESIS> ROOT CAUSE <the actual cause, with citation or UNVERIFIED HYPOTHESIS>
MINIMAL FIX <smallest change that removes the cause> MINIMAL FIX <smallest change that removes the cause>
EVIDENCE <BCQuality knowledge file(s) or AL diagnostic code(s)> EVIDENCE <BCQuality knowledge file(s) or AL diagnostic code(s)>
BLAST RADIUS <callers/dependents that the fix could affect, from al_symbolrelations> BLAST RADIUS <callers/dependents that the fix could affect, from al_symbolrelations>
``` ```

View file

@ -47,28 +47,28 @@ of the quality in the code they write.
Layer 1 - Microsoft BCQuality: https://github.com/microsoft/BCQuality Layer 1 - Microsoft BCQuality: https://github.com/microsoft/BCQuality
Layer 2 - CURABIS custom knowledge (fetch before applying rules): Layer 2 - CURABIS custom knowledge (fetch before applying rules):
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/architecture/pages-must-not-contain-business-logic.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/architecture/pages-must-not-contain-business-logic.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/architecture/namespace-must-be-verified-from-source.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/architecture/namespace-must-be-verified-from-source.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/architecture/al-identifiers-must-be-english.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/architecture/al-identifiers-must-be-english.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/architecture/clarify-before-building.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/architecture/clarify-before-building.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/architecture/xliff-translation-workflow.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/architecture/xliff-translation-workflow.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/architecture/new-file-requires-vscode-refresh.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/architecture/new-file-requires-vscode-refresh.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/architecture/exposed-objects-must-be-in-a-permission-set.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/architecture/exposed-objects-must-be-in-a-permission-set.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/architecture/shared-project-memory-must-be-in-repo.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/architecture/shared-project-memory-must-be-in-repo.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/architecture/commit-message-must-include-bc-task-id.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/architecture/commit-message-must-include-bc-task-id.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/architecture/branch-merge-to-main-workflow.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/architecture/feature-branch-must-merge-to-track-branch.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/testing/test-setup-must-use-library-codeunit.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/testing/test-setup-must-use-library-codeunit.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/testing/test-data-must-be-random-and-complete.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/testing/test-data-must-be-random-and-complete.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/testing/tests-must-adapt-to-existing-code.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/testing/tests-must-adapt-to-existing-code.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/testing/test-one-when-per-test.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/testing/test-one-when-per-test.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/testing/ui-test-codeunit-naming.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/testing/ui-test-codeunit-naming.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/testing/test-feature-scenario-tags.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/testing/test-feature-scenario-tags.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/mcp/api-page-flowfields-must-be-calcfields.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/mcp/api-page-flowfields-must-be-calcfields.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/mcp/stored-derived-fields-must-not-be-exposed-directly.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/mcp/stored-derived-fields-must-not-be-exposed-directly.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/mcp/api-page-key-fields-must-be-editable-on-insert.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/mcp/api-page-key-fields-must-be-editable-on-insert.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/mcp/api-page-least-privilege-write-access.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/mcp/api-page-least-privilege-write-access.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/mcp/agent-must-not-write-business-process-status.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/mcp/agent-must-not-write-business-process-status.md
- https://raw.githubusercontent.com/Curabis/BCQuality/main/custom/knowledge/mcp/bc-mcp-find-active-task-for-branch.md - https://raw.githubusercontent.com/Curabis/BCQuality/stable/custom/knowledge/mcp/bc-mcp-find-active-task-for-branch.md
## Action ## Action