diff --git a/custom/agents/roemer.agent.md b/custom/agents/roemer.agent.md index 98f251f..30f6b75 100644 --- a/custom/agents/roemer.agent.md +++ b/custom/agents/roemer.agent.md @@ -1,7 +1,7 @@ --- kind: action-skill id: curabis-standards-inspector -version: 5 +version: 6 title: Rømer — Standards Inspector description: > Owns the uniformity inspection across CURABIS repos: walks one full @@ -103,6 +103,19 @@ Walk ALL stations, every time. A partial round creates false confidence entry — it is a CURABIS artifact; no VS Code command generates it. Evidence for this station: a session wrote AL code it could not compile and only surfaced the gap when asked (Conzept, 2026-07-02). +13. **Task-state trail completeness** (2026-08-03, retrospective, not + structural). Sample the last ~10 closed BC tasks (`taskComments` where + `Status = Done`) and the last ~10 merged PRs with a `## CURABIS Task + State` section. For each: read the `[CURABIS-STATE]` comments / checklist + and confirm `TASK_STARTED` → `RED_CONFIRMED` → `GREEN_CONFIRMED` → + `REVIEW: ` → `MERGED` are all present, in order — the same + check the close gate and al-review already do per-task, run here + across a sample to catch drift no single task's own gate caught (e.g. + an older task from before this rule existed, or a session that bypassed + the gates entirely). A missing trail on a task closed AFTER 2026-08-03 + is a divergence finding → Ferencz. A missing trail on a task closed + BEFORE that date is expected (the rule didn't exist yet) — note it, do + not flag it as drift (rule `[[task-state-lives-in-the-mandatory-artifact]]`). ## Safety rules diff --git a/custom/agents/smiley.agent.md b/custom/agents/smiley.agent.md index f9d78e2..89f2739 100644 --- a/custom/agents/smiley.agent.md +++ b/custom/agents/smiley.agent.md @@ -1,7 +1,7 @@ --- kind: watchdog id: curabis-smiley -version: 5 +version: 6 title: Smiley — Session Watchdog description: > Always-active session observer. Shapes Claude's behavior from within. @@ -170,6 +170,17 @@ all of them mean AL code is about to change. BLOCK verdict from the independent review is the same kind of hard stop as a red test — green tests prove the requirement is met, not that the change is well-built. +- **2026-08-03 — self-verify before merging, don't just trust the session's + own memory.** Read back the actual `[CURABIS-STATE]` trail (BC comments + for PTE, the PR checklist for AppSource) before allowing the merge — not + from what this session remembers doing, from what's actually recorded. + If `TASK_STARTED` → `RED_CONFIRMED` → `GREEN_CONFIRMED` → `REVIEW: ` + aren't all present in order, the merge does not proceed, even if the + current test run is green and the current review just said APPROVE — a + missing earlier checkpoint means the trail itself can't be trusted, which + is the entire reason it exists. This is the one advantage a queryable + state trail has over a plain instruction: it can be checked, not just + followed. - At release (track branch → main, tag, AppSource submission): app.json version consciously bumped before the merge. diff --git a/custom/setup/curabis-standard.agent.md b/custom/setup/curabis-standard.agent.md index 39c5a21..c106426 100644 --- a/custom/setup/curabis-standard.agent.md +++ b/custom/setup/curabis-standard.agent.md @@ -95,6 +95,7 @@ old HTTP-encoding pitfalls do not exist here). | edison.agent.md | `{AGENTS_BASE}/edison.agent.md` | | ferencz.agent.md | `{AGENTS_BASE}/ferencz.agent.md` | | roemer.agent.md | `{AGENTS_BASE}/roemer.agent.md` | +| curabis-task-state-check.yml | `{BASE}/templates/curabis-task-state-check.yml` | | cspell.json | `{BASE}/templates/cspell.json` | | find-altool.ps1 | `{BASE}/machine/find-altool.ps1` (v24: machine artifact, not a repo template) | | feynman-onboarding.md | `{BASE}/templates/feynman-onboarding.md` | @@ -488,6 +489,28 @@ Create the standard documentation structure if it does not exist: Create a `.gitkeep` file in each empty subfolder so git tracks them. +#### 4h. .github/workflows/curabis-task-state-check.yml (2026-08-03) + +Fetch `{BASE}/templates/curabis-task-state-check.yml` and write to +`.github/workflows/curabis-task-state-check.yml`. + +Deterministic (not LLM-instruction-based) enforcement of +`[[task-state-lives-in-the-mandatory-artifact]]`'s AppSource checklist +order — see that rule and `al-review.agent.md`'s "state trail complete" +checklist item for the full picture. This is a real CI check, not an agent +protocol: it parses the PR body for a `## CURABIS Task State` section and +fails if a later stage is checked while an earlier one isn't. It silently +does nothing on PRs with no such section — never make it block an unrelated +PR (a docs fix, an infra change). + +**Manual one-time step, cannot be automated by this file deployment:** +tell the developer/admin to add this check as a **required status check** +in the repo's branch protection settings (GitHub → Settings → Branches → +the target branch's protection rule) if they want it to actually block a +merge rather than just show as a failed check someone could ignore. Report +this explicitly — do not silently assume it's required just because the +workflow file exists. + ### Step 5 — Confirm and offer initial commit List all files written, then ask: @@ -537,6 +560,7 @@ shorter table applies to them. | `.github/.agents/bcquality.agent.md` | Fetch fresh from BCQuality, overwrite | | `.github/.agents/feynman.agent.md` | Fetch fresh from BCQuality, overwrite (add if missing) | | `cspell.json` — words from template | Merge new words, keep project words | +| `.github/workflows/curabis-task-state-check.yml` | Fetch fresh from BCQuality, overwrite (add if missing) — remind about the branch-protection required-check step if just added | | `.apps/*.code-workspace` — reference layout | Create/complete: app projects + `.AL-Go` + relative `../docs` (rule `al-development-must-use-apps-workspace`) | | Alle øvrige `*.code-workspace` (inkl. rodens `al.code-workspace`) | Delete — kun ét workspace pr. repo; rapportér de slettede | | `HEARTBEAT.md` | Create from template if missing (substitute tokens), never overwrite — but run the staleness check below on every Mode B pass | diff --git a/custom/setup/templates/al-review.agent.md b/custom/setup/templates/al-review.agent.md index df16230..c4a3aff 100644 --- a/custom/setup/templates/al-review.agent.md +++ b/custom/setup/templates/al-review.agent.md @@ -1,7 +1,7 @@ --- kind: action-skill id: curabis-al-review -version: 2 +version: 3 title: CURABIS AL independent review (Torvalds & Winters) description: Independent per-change code reviewer. Runs after the TDD green gate and before merge — the fourth checkpoint, separate from the implementer and from portfolio-level rule governance (Rømer/Immanuel/Court, who ask "is the ruleset healthy", not "is THIS change good"). Two lenses - Linus Torvalds (BC/AL domain-technical correctness, backward compatibility, performance, security) and Titus Winters (general software-engineering maintainability, architecture, complexity over time). inputs: [diff, task-description] @@ -102,6 +102,15 @@ remember to request. See `smiley.agent.md`. - Consistency with the rest of the codebase - Should this even be implemented this way at all — not "does it work" but "is this the right way to have solved it"? +- **State trail complete?** (2026-08-03) Read back the `[CURABIS-STATE]` + comments (PTE) or PR checklist (AppSource) — `TASK_STARTED`, + `RED_CONFIRMED`, `GREEN_CONFIRMED` must all be present before this review + even runs. A missing earlier checkpoint is a maintainability finding in + its own right: the record this task claims to have followed the lifecycle + gates can't be trusted after the fact, which defeats the entire point of + `[[task-state-lives-in-the-mandatory-artifact]]`. This is a BLOCKing + finding, not a note — the fix is trivial (go check what actually happened + and record it truthfully), so there's no reason to let it slide. ## Protocol diff --git a/custom/setup/templates/curabis-task-state-check.yml b/custom/setup/templates/curabis-task-state-check.yml new file mode 100644 index 0000000..d6acf0d --- /dev/null +++ b/custom/setup/templates/curabis-task-state-check.yml @@ -0,0 +1,77 @@ +name: CURABIS task-state check + +# Deterministic enforcement (not LLM diligence) for the AppSource state-trail +# format from task-state-lives-in-the-mandatory-artifact.md. Parses the PR +# body for a "## CURABIS Task State" checklist and fails if any checked box +# appears AFTER an unchecked one - i.e. a later stage claimed without an +# earlier one. Silently passes (does nothing) if the section is absent - +# this check only applies to PRs that actually opted into the state trail; +# it must never block an unrelated PR (docs fix, infra change, etc.). + +on: + pull_request: + types: [opened, edited, synchronize, reopened] + +jobs: + check-task-state-order: + runs-on: ubuntu-latest + steps: + - name: Validate CURABIS Task State checklist order + uses: actions/github-script@v7 + with: + script: | + const body = context.payload.pull_request.body || ""; + const heading = "## CURABIS Task State"; + const headingIdx = body.indexOf(heading); + if (headingIdx === -1) { + console.log("No '## CURABIS Task State' section found - not a state-tracked PR, skipping."); + return; + } + + // Take everything after the heading up to the next "## " heading (or end of body). + const rest = body.slice(headingIdx + heading.length); + const nextHeadingIdx = rest.search(/\n##\s/); + const section = nextHeadingIdx === -1 ? rest : rest.slice(0, nextHeadingIdx); + + const lineRe = /^-\s*\[( |x|X)\]\s*(.+)$/gm; + const items = []; + let m; + while ((m = lineRe.exec(section)) !== null) { + items.push({ checked: m[1].toLowerCase() === "x", label: m[2].trim() }); + } + + if (items.length === 0) { + core.setFailed( + "Found a '## CURABIS Task State' heading but no checklist lines under it " + + "(expected '- [ ] ...' / '- [x] ...'). Either add the checklist or remove the heading." + ); + return; + } + + // Valid order is monotonic: once an item is unchecked, every item after it + // must also be unchecked. A checked item after an unchecked one means a + // later stage was marked done while an earlier one wasn't. + let seenUnchecked = false; + let brokenAt = -1; + for (let i = 0; i < items.length; i++) { + if (!items[i].checked) { + seenUnchecked = true; + } else if (seenUnchecked) { + brokenAt = i; + break; + } + } + + if (brokenAt !== -1) { + const lines = items.map((it, i) => + ` ${i === brokenAt ? ">>" : " "} [${it.checked ? "x" : " "}] ${it.label}` + ).join("\n"); + core.setFailed( + "CURABIS Task State checklist is out of order - a later stage is checked " + + "while an earlier one is not. A stage cannot be marked done before the ones " + + "before it. Offending line marked with '>>':\n\n" + lines + ); + return; + } + + console.log(`CURABIS Task State checklist order OK (${items.filter(i => i.checked).length}/${items.length} checked).`);