diff --git a/.agents/agents/agentic-artifact-reviewer.md b/.agents/agents/agentic-artifact-reviewer.md index 975d7b9..c7d7a28 100644 --- a/.agents/agents/agentic-artifact-reviewer.md +++ b/.agents/agents/agentic-artifact-reviewer.md @@ -27,6 +27,12 @@ A directory (usually `generated//`) or a list of files. For generated and loop from the spec appears with its condition and `maxLoops` behaviour; every human checkpoint uses options plus a recommendation; side effects come after their checkpoint; a Workflow script passes each `agent()` the paths and constraints it needs. + Where the spec has `contextSources`, also check: a live-store write in a step with no + `userTask` before it (`design`); tool lists (`tools:`, `permissions.allow`) broader than the + lane's stores need, wildcards, or write tools in read-only lanes (`design`); a memory store whose + text lacks the fixed sections or whose cap hook is missing (`generate`); a step that needs + knowledge but whose `## Kontextquellen` section or `references/` lacks the store (`generate`); + a store in the spec that no step reads (`design`). 3. Respect the pipeline's own conventions — `bpmn:` frontmatter, verbatim BPMN labels inside English text, the templates' regenerate boundary, the JSON reporting block. They are not findings. diff --git a/.agents/agents/agentic-workflow-architect.md b/.agents/agents/agentic-workflow-architect.md index 28b39d9..e53a218 100644 --- a/.agents/agents/agentic-workflow-architect.md +++ b/.agents/agents/agentic-workflow-architect.md @@ -24,6 +24,11 @@ If a rubric is missing, say so in `summary` and skip rubric checks. `artifacts`, `openQuestions`, `knowledge`. 2. Run orchestration-design's review mode, and agent-authoring's split rule and tool/model guidance per role. Every unanswered or weakly answered question is a finding. + For `contextSources`, also flag: a write into a `live` store with no `userTask` before it on + every path (`bpmn`); `live` tool lists broader than the lane needs, i.e. wildcards or write tools + in a read-only lane (`design`); a `gedaechtnis` store without a writer, a reader or `maxLines` + (`bpmn` or `design`); a task that needs knowledge but has no store input (`question`); a store + with no `readers` (`question`). 3. Classify each finding's route: `design` (fixable in the spec), `bpmn` (the diagram must change — goes to the user, loop A) or `question` (needs a business decision). diff --git a/.agents/skills/agentic-workflow-kb/references/multi-agent-handoffs.md b/.agents/skills/agentic-workflow-kb/references/multi-agent-handoffs.md index 83ea323..59d48ff 100644 --- a/.agents/skills/agentic-workflow-kb/references/multi-agent-handoffs.md +++ b/.agents/skills/agentic-workflow-kb/references/multi-agent-handoffs.md @@ -53,6 +53,15 @@ wrong falls back to one sequential agent with shared context | Role confusion from overloaded prompts | single responsibility, third-person descriptions for routing [multi-agent-handoffs-1: 50, 77–82] | | Cost explosion | zero-token script orchestrators, small models for routine steps, prompt caching, concurrency caps [multi-agent-handoffs-1: 15, 19, 44, 83–88] | +## Context sources at handoff (repo design decision) + +Not notebook-cited; from `docs/plans/kontextquellen/plan.md`. Artifacts carry one run's handoff; +data stores carry what outlives a handoff or a run. A role reads the stores it needs (knowledge +reference, live tool, memory file) instead of receiving them pasted in the handoff, and its tool +list names only those stores' tools. Where lanes are not agents, the read tools are shared and the +plan says so. Memory is a curated file with a line cap that the writing step rewrites, never an +append-only log. + ## For bpmn2agent BPMN data objects become `artifacts.` contracts (path + frontmatter) — this is the diff --git a/.agents/skills/agentic-workflow-kb/references/process-to-agents.md b/.agents/skills/agentic-workflow-kb/references/process-to-agents.md index 031a4e0..404f3fe 100644 --- a/.agents/skills/agentic-workflow-kb/references/process-to-agents.md +++ b/.agents/skills/agentic-workflow-kb/references/process-to-agents.md @@ -15,6 +15,7 @@ Distilled from FAQ `process-to-agents-1`, with the element table from | Parallel branches | parallel subagents, merged downstream | [process-to-agents-1: 18–21] | | Human approval | HITL checkpoint (pause/resume, edit arguments) | [process-to-agents-1: 22–25] | | Data artifact | typed state, memory store or persistent file | [process-to-agents-1: 12, 21, 26, 27, 28] | +| Data store (cylinder) | context source: knowledge reference, live tool access or memory file, by `Art:` | repo design decision, not notebook-cited | ## Where each part belongs @@ -54,6 +55,14 @@ extraction, classification and routine calls. A small model can propose, a stron ## For bpmn2agent +**Data stores (repo design decision, from `docs/plans/kontextquellen/plan.md`; not notebook-cited).** +A data store is a context source with an `Art:` (`wissen` → cited reference loaded at generation, +`live` → tool allowlist read at run time, `gedächtnis` → curated memory file with a line cap) and +an `Ort:`; the arrow direction alone says read or write. Every write into a live store needs a +`userTask` before it plus an `ask` hook behind it, and a phase with such a write is never a +workflow script. Ask during design: does each task have a store for what it must know, and does +each store have a reader? + This confirms the pipeline's defaults (lane → agent, service task → skill or checklist item, script task → script, gateway → orchestrator logic, user task → checkpoint, data object → artifact contract). The questions to add during design: is this lane really one role or two? Is diff --git a/.agents/skills/bpmn-authoring/SKILL.md b/.agents/skills/bpmn-authoring/SKILL.md index 39def46..ffdcd42 100644 --- a/.agents/skills/bpmn-authoring/SKILL.md +++ b/.agents/skills/bpmn-authoring/SKILL.md @@ -5,9 +5,8 @@ description: Creates, extends or refines BPMN 2.0 .bpmn diagrams (process models # Author BPMN -Diagrams here are descriptive process models (`isExecutable="false"`), documenting how a process -works. The `references/` are distilled from an external German BPMN reference (not in this repo); -load each at the step that needs it. +Diagrams are descriptive process models (`isExecutable="false"`). Load each `references/` file at +the step that needs it. ## Workflow @@ -15,8 +14,8 @@ load each at the step that needs it. Read the target file (or start from `assets/skeleton.bpmn`). List its element IDs, sequence flows and `BPMNDiagram`/`BPMNPlane` pairs before changing anything. **Never renumber or rename an existing -ID** — every `sourceRef`/`targetRef`, `dataObjectRef`, `bpmnElement`, `categoryValueRef` would need -updating, and a missed one is a dangling reference only bpmn-moddle (not xmllint) catches. +ID**: a missed `sourceRef`/`targetRef`, `dataObjectRef`, `bpmnElement` or `categoryValueRef` becomes +a dangling reference that only bpmn-moddle (not xmllint) catches. ### 2. Model the semantics first, layout second @@ -34,10 +33,9 @@ Most-missed: ### 3. Refining a task into a collapsed drill-down sub-process -1. Change the tag from `bpmn:task` to `bpmn:subProcess`; **keep its `id`**, so everything pointing - at it keeps working. -2. Move its `dataInputAssociation`/`dataOutputAssociation` onto the subProcess element itself (they - stay at the collapsed level, not on an inner step). +1. Change the tag from `bpmn:task` to `bpmn:subProcess`; **keep its `id`**. +2. Move its `dataInputAssociation`/`dataOutputAssociation` onto the subProcess element itself, not + onto an inner step. 3. Add the inner flow as children: one blank start event, the step tasks, an internal gateway/loop if the phase has one, one or more named end events, and their sequence flows. IDs per `references/xml-and-di.md`. @@ -57,10 +55,10 @@ exclusive gateway. ${CLAUDE_SKILL_DIR}/scripts/validate.sh .bpmn ``` -Runs XSD (xmllint) → bpmn-moddle parse warnings → `bpmnlint:recommended --max-warnings=0` and stops -at the first failing stage. Fix every finding before moving on; `references/validation.md` has what -each stage catches and a table of typical errors. Tooling is fetched on demand into -`${BPMN_TOOLS_CACHE:-$HOME/.cache/bpmn-authoring-tools}`; never add it to this repo's `package.json`. +Runs XSD (xmllint) → bpmn-moddle parse warnings → `bpmnlint:recommended --max-warnings=0`, stopping +at the first failing stage. Fix every finding before moving on; see `references/validation.md`. +Tooling is fetched on demand into `${BPMN_TOOLS_CACHE:-$HOME/.cache/bpmn-authoring-tools}`; never +add it to this repo's `package.json`. ### 6. Render and eyeball @@ -80,10 +78,10 @@ each one, not in a batch. ## Files -- `references/xml-and-di.md` — skeleton, element catalogue, drill-down and lane XML/DI, standard sizes. -- `references/modelling-rules.md` — hard rules, best practices, loops, lanes, naming. -- `references/layout.md` — the coordinate grid, top-level and per child plane. +- `references/xml-and-di.md` — skeleton, element catalogue, data stores, drill-down and lane XML/DI, + standard sizes. +- `references/modelling-rules.md` — hard rules, best practices, lanes, loops, German labels. +- `references/layout.md` — coordinate grid, top-level and per child plane. - `references/validation.md` — what each validation stage catches, typical errors and fixes. -- `assets/skeleton.bpmn` — minimal valid file to start from; `assets/.bpmnlintrc` — the lint config. -- `scripts/validate.sh` (step 5), `scripts/check-moddle.mjs` (called by it), `scripts/render.mjs` - (step 6). +- `assets/skeleton.bpmn` (starting file), `assets/.bpmnlintrc` (lint config). +- `scripts/validate.sh` (step 5, calls `scripts/check-moddle.mjs`), `scripts/render.mjs` (step 6). diff --git a/.agents/skills/bpmn-authoring/references/layout.md b/.agents/skills/bpmn-authoring/references/layout.md index ca1d987..6900309 100644 --- a/.agents/skills/bpmn-authoring/references/layout.md +++ b/.agents/skills/bpmn-authoring/references/layout.md @@ -1,8 +1,7 @@ # Layout conventions (grid & coordinates) -DI is hand-laid on the grid below (`bpmn-auto-layout` can't produce drill-down planes). - -Sizes and why: `xml-and-di.md`, "Standard sizes". +Lay out DI by hand on the grid below (`bpmn-auto-layout` can't produce drill-down planes). Sizes: +`xml-and-di.md`, "Standard sizes". ## Top-level plane grid @@ -16,6 +15,16 @@ Sizes and why: `xml-and-di.md`, "Standard sizes". - **Data objects**: `y=170`, height 50, centred above the task that outputs them; labels above them. Input associations from an earlier data object route via a waypoint at `y=195` before dropping into the consuming task's top edge. +- **Data stores** (`dataStoreReference`, 50×50): below the task row, inside the lane of their first + reader, ~60px under the task bottom, label beside or below the store. Read arrows run straight up + into the task's bottom edge. A store that is also written later gets its write arrow routed in a + channel below the stores (own `y` per store, ~25px apart) so no two arrows cross; with lanes, put + the lane that the happy path leaves and re-enters above the other, so the vertical hops do not + cross those channels. Several stores under one task sit side by side. +- **Process input/output** (`dataInput`/`dataOutput`, 36×50): the input left of the start event, the + output right of the end event, both in the data-object band above the task row (`y=170`, or the + lane's top band). Route the input's association along `y≈195` into the first consuming task's top + edge; route the output's association over the tasks between its producer and the end. - **Groups (phase bands)**: `y=40`, `height=500`, from just left of the phase's first task to just right of its last, ~30–40px padding; label bounds inside the top of the band. - New elements extend this row; never start a second main row. @@ -43,13 +52,13 @@ Each child plane is an independent coordinate space; don't reuse the parent's co ## General -- Never let two shapes' bounding boxes overlap (`no-overlapping-elements` is a warning; treat it as - an error). When in doubt, add horizontal space rather than stacking vertically. +- Never let two shapes' bounding boxes overlap (treat the `no-overlapping-elements` warning as an + error). When in doubt, add horizontal space rather than stacking vertically. - Several loop-backs in one plane (e.g. a three-outcome gateway with two targets): sort them by - horizontal span; the shortest runs closest to the row, longer ones further below — same at the top - level and in child planes. -- Escape every name/label/condition for XML (`&` → `&`, `"`, `<`, `>`) — German labels often - contain `&`, and an unescaped one fails xmllint with `xmlParseEntityRef: no name`. When generating - XML from a script, escape once, centrally. -- Most planes that validate but look wrong have a coordinate slip: an off gap after inserting a - shape mid-row (shift every shape after it), or a waypoint that doesn't touch its shape's border. + horizontal span; the shortest runs closest to the row, longer ones further below — top level and + child planes alike. +- Escape every name/label/condition for XML (`&` → `&`, `"`, `<`, `>`); an unescaped `&` (common + in German labels) fails xmllint with `xmlParseEntityRef: no name`. When generating XML from a + script, escape once, centrally. +- A plane that validates but looks wrong usually has a coordinate slip: an off gap after inserting a + shape mid-row (shift every shape after it), or a waypoint not touching its shape's border. diff --git a/.agents/skills/bpmn-authoring/references/modelling-rules.md b/.agents/skills/bpmn-authoring/references/modelling-rules.md index 95f5715..a3e5b44 100644 --- a/.agents/skills/bpmn-authoring/references/modelling-rules.md +++ b/.agents/skills/bpmn-authoring/references/modelling-rules.md @@ -19,8 +19,8 @@ synchronize parallel paths with AND — matching the split type. - **Split and merge are separate gateways** (never one gateway with n-in and n-out). - **One blank start and at least one end event** per process and per embedded sub-process; no implicit - start/end (nodes with no incoming/outgoing). One end event per distinct outcome is fine; name end - events after the resulting state. + start/end (nodes with no incoming/outgoing). One end event per distinct outcome is allowed; name + each after the resulting state. - A sub-process without an internal check is a plain `Start → S1..Sn → End` chain; don't add a gateway it doesn't need. - **Naming**: tasks = verb + object ("Entwurf prüfen"); events = object + past participle / state @@ -29,8 +29,7 @@ `label-required` expects names on tasks, events, forking XOR/OR gateways, conditional flows, pools and lanes; `superfluous-label` flags names on unconditional flows that don't leave an XOR/OR split and aren't default flows. -- **Groups** only for visual grouping ("Review phase"); they never change behavior and must not stand - in for sub-processes. +- **Groups** only for visual grouping ("Review phase"); never as a stand-in for sub-processes. - Avoid inclusive/complex gateways and `terminateEndEvent` unless the semantics are genuinely needed. - Flow left to right, happy path on a straight line, no overlapping shapes. @@ -56,8 +55,7 @@ flows (initial entry, loop-back), one outgoing flow into that task. The gateway loop is a separate, later XOR **split** gateway after the body, with a question name and named outgoing flows (one of them `default`). If the loop returns to the very first step of a sub-process, the merge gateway sits between the start event and that step. Same pattern at the top level and -inside child planes; see `examples/dark-factory/product-vision-to-user-stories.bpmn` in this repo -for worked examples. +in child planes; worked examples: `examples/dark-factory/product-vision-to-user-stories.bpmn`. ## German labels diff --git a/.agents/skills/bpmn-authoring/references/validation.md b/.agents/skills/bpmn-authoring/references/validation.md index 371f1c5..1a20f90 100644 --- a/.agents/skills/bpmn-authoring/references/validation.md +++ b/.agents/skills/bpmn-authoring/references/validation.md @@ -4,9 +4,8 @@ ${CLAUDE_SKILL_DIR}/scripts/validate.sh .bpmn ``` -Runs the three stages below in order, stops at the first failing one (a structural XSD failure makes -later output noise) and exits non-zero. Read the script for the exact commands; tooling is cached in -`${BPMN_TOOLS_CACHE:-$HOME/.cache/bpmn-authoring-tools}`. +Runs the three stages below in order, stops at the first failing one and exits non-zero. Exact +commands are in the script; tooling is cached in `${BPMN_TOOLS_CACHE:-$HOME/.cache/bpmn-authoring-tools}`. ## Stages @@ -21,22 +20,22 @@ later output noise) and exits non-zero. Read the script for the exact commands; `no-disconnected`, `no-implicit-start`/`-end`, `no-implicit-split`, `single-blank-start-event`, `sub-process-blank-start-event`, `end-event-required`/`start-event-required` (per scope — every sub-process needs its own), `label-required`, `fake-join`, `superfluous-gateway`, - `no-overlapping-elements` (the last three are warnings, which still fail at zero tolerance). + `no-overlapping-elements` (the last three are warnings; they still fail at zero tolerance). Misses: `standard-size` (not in `recommended`) and anything visual. -Then render (SKILL step 6) — the only check for how the planes actually look. +Then render (SKILL step 6): the only check of how the planes look. ## Typical errors and fixes | Symptom | Likely cause | Fix | |---|---|---| -| xmllint: element X not expected | wrong child order | check `xml-and-di.md`'s element order (flow elements before artifacts) | -| xmllint: duplicate ID | copy-pasted a block without renaming IDs | rename per the `_` convention | +| xmllint: element X not expected | wrong child order | follow `xml-and-di.md`'s element order (flow elements before artifacts) | +| xmllint: duplicate ID | copy-pasted block with unchanged IDs | rename per the `_` convention | | xmllint: `xmlParseEntityRef: no name` | unescaped `&` in a name/label | escape it as `&` | -| moddle: unresolved reference | a flow/association points at an ID that doesn't exist yet, or a rename missed a reference | grep the old ID across the file — `sourceRef`, `targetRef`, `bpmnElement`, `dataObjectRef` | -| bpmnlint `no-bpmndi` | added a semantic element without its DI shape/edge | every element added to `process`/`subProcess` needs a matching `BPMNShape`/`BPMNEdge` in its plane | -| bpmnlint `sub-process-blank-start-event` / `single-blank-start-event` | refined a task into a subProcess but forgot the inner start event, or added a second one | exactly one blank `startEvent` per sub-process/process scope | -| bpmnlint `no-implicit-start`/`-end` | a node has no incoming or no outgoing flow and isn't a start/end event | either it's missing a flow, or it should be a start/end event | -| bpmnlint `label-required` | a new task, event, forking gateway or conditional flow has no `name` | name it per `modelling-rules.md`'s naming convention | -| bpmnlint `fake-join` (warn) | a loop-back or merge was wired as two incoming flows into an ordinary task | insert an explicit XOR merge gateway before it | -| Renders but shapes overlap | coordinate arithmetic mistake | recheck against `layout.md`'s grid, especially after inserting a new element mid-row (every shape after it needs its x shifted) | +| moddle: unresolved reference | a flow/association points at a missing ID, or a rename missed a reference | grep the old ID across the file — `sourceRef`, `targetRef`, `bpmnElement`, `dataObjectRef` | +| bpmnlint `no-bpmndi` | semantic element without its DI shape/edge | add the matching `BPMNShape`/`BPMNEdge` in its plane | +| bpmnlint `sub-process-blank-start-event` / `single-blank-start-event` | missing inner start event after refining a task, or a second one | exactly one blank `startEvent` per sub-process/process scope | +| bpmnlint `no-implicit-start`/`-end` | a non-start/end node has no incoming or no outgoing flow | add the missing flow, or make it a start/end event | +| bpmnlint `label-required` | task, event, forking gateway or conditional flow without `name` | name it per `modelling-rules.md` | +| bpmnlint `fake-join` (warn) | loop-back or merge wired as two incoming flows into an ordinary task | insert an explicit XOR merge gateway before it | +| Renders but shapes overlap | coordinate slip | recheck against `layout.md`'s grid; after inserting an element mid-row, shift every shape after it | diff --git a/.agents/skills/bpmn-authoring/references/xml-and-di.md b/.agents/skills/bpmn-authoring/references/xml-and-di.md index 09c3073..e02c494 100644 --- a/.agents/skills/bpmn-authoring/references/xml-and-di.md +++ b/.agents/skills/bpmn-authoring/references/xml-and-di.md @@ -2,6 +2,7 @@ - [Document skeleton](#document-skeleton) - [Element catalogue](#element-catalogue) +- [Data stores and process input/output](#data-stores-and-process-inputoutput) - [Sub-processes and drill-down](#sub-processes-and-drill-down) - [Lanes (`laneSet`)](#lanes-laneset) - [Diagram Interchange (DI)](#diagram-interchange-di) @@ -26,12 +27,12 @@ ``` -Note: the spec *version* is 2.0.2 but XML namespaces still carry the 2.0 date (`20100524`); XSDs live -under `20100501/`. There is no separate 2.0.2 namespace. +Spec version 2.0.2 has no namespace of its own: namespaces carry the 2.0 date (`20100524`); XSDs live +under `20100501/`. -**Element order inside `process`**: `laneSet` → flow elements (events, tasks, gateways, sequence -flows, data objects/associations) → artifacts (group, textAnnotation, association) last. xmllint -enforces this via the XSD's element sequence. +**Element order inside `process`** (xmllint enforces it): `ioSpecification` (if any) → `laneSet` → +flow elements (events, tasks, gateways, sequence flows, data objects/associations) → artifacts +(group, textAnnotation, association) last. ## Element catalogue @@ -52,6 +53,7 @@ enforces this via the XSD's element sequence. `dataObjectRef` pointing at the `dataObject`). `dataInputAssociation`/`dataOutputAssociation` connect a reference to a task; an input association needs a `property` on the task as its `targetRef` (``). +- **Data stores & process input/output**: see "Data stores and process input/output" below. - **Text annotations & associations**: `textAnnotation` + plain `association` (not sequence flow) to the annotated element. - **Groups / category / categoryValue**: `category` + nested `categoryValue` at `definitions` level; @@ -59,6 +61,79 @@ enforces this via the XSD's element sequence. - **Pools/message flows**: not used here. **Lanes**: see "Lanes (`laneSet`)" below; whether to use them is decided in `modelling-rules.md`. +## Data stores and process input/output + +Context sources (where a step reads or writes data that outlives the run) are data stores; what +goes into and out of the whole process is its `ioSpecification`. + +**Data store**: a root element plus a visible reference, like `dataObject`/`dataObjectReference`. +`dataStore` sits directly under `definitions` (before the `process`); `dataStoreReference` sits in +the process among the flow elements, 50×50, with the name = the business data set (e.g. +"Jira-Tickets Projekt LANE", not "Jira"). Its `` carries exactly two lines: + +``` +Art: wissen | live | gedächtnis +Ort: notebook: | mcp: | cli: | | datei: | websearch +``` + +The location line is `Ort:`, never `Quelle:` (that is task provenance). Add no access line: the +arrow direction alone says read or write. + +```xml + + + ... + + Flow_1 + Flow_2 + + + StoreRef_Jira + Property_Analysieren_1 + + + + ... + + StoreRef_Jira + + + + Art: live +Ort: mcp:atlassian + + +``` + +One `property` per input association (as with data objects). Stores are not flow nodes: no lane +`flowNodeRef`, no sequence flow. Several stores may share one `Ort:`. A `wissen` store is only read; a +`gedächtnis` store needs a writing `serviceTask` and a reader; every write into a `live` store needs a +`userTask` on each path before it (`bpmn2agent-verify` reports a violation). + +**Process input/output**: a real, process-wide `ioSpecification` as the *first* child of `process` +(before `laneSet`). The XSD requires at least one `inputSet` and one `outputSet`; an `outputSet` +without refs is valid. + +```xml + + + + + DataInput_TicketId + DataOutput_Bericht + + ... + +``` + +A task consumes a `dataInput` through a `dataInputAssociation` whose `sourceRef` is the `dataInput` id +(plus a `property` target, as above); a `dataOutput` is fed by a `dataOutputAssociation` whose +`targetRef` is the `dataOutput` id. They exist only in XML (bpmn.io's palette lacks them). bpmn-js +draws them as 36×50 data-object shapes (input arrow icon) **only if a `BPMNShape` with +`bpmnElement` = the `dataInput`/`dataOutput` id exists**; bpmnlint does not require it, so always +add it. The pipeline also accepts a convention instead: a data object nobody produces that a task +reads (input), or one nobody reads (output). + ## Sub-processes and drill-down | Element | Meaning | @@ -85,8 +160,7 @@ enforces this via the XSD's element sequence. **ID convention for inner elements**: `_Start`, `_S` (steps, in the step's own order — reuse the parent's numbering, e.g. `Task_1_1_S1`..`Task_1_1_S4`), `_ Gw` (internal gateway), `_End`/`_EndAlt` (multiple named ends allowed), -`_F` (flows) / `_FLoop` (loop-back flow). Keeps every ID globally -unique and greppable back to its parent task. +`_F` (flows) / `_FLoop` (loop-back flow). **DI for a collapsed sub-process**: the parent-level shape gets `isExpanded="false"`; its inner flow gets a **second, separate** `BPMNDiagram`/`BPMNPlane` pair whose `bpmnElement` is the subProcess's @@ -100,18 +174,18 @@ own ID: ``` -One `` per collapsed sub-process, all as siblings of the top-level one, all -direct children of `bpmn:definitions` (after the top-level `BPMNDiagram`). Coordinates inside a -child plane are independent of the parent — start fresh at a convenient origin (see `layout.md`). +One `` per collapsed sub-process, all direct children of `bpmn:definitions` +after the top-level `BPMNDiagram`. Child-plane coordinates are independent of the parent (origin: +`layout.md`). ## Lanes (`laneSet`) No pool/participant is needed for lanes to render in bpmn-js, at process or sub-process level. -**`laneSet` inside a plain process**: `laneSet` is the *first* child of -`bpmn:process`, before any flow element (same element-order rule as above). Each `bpmn:lane` lists -every flow node assigned to it via `flowNodeRef` (events, tasks, gateways — not data objects, -which aren't flow nodes and carry no lane). +**`laneSet` inside a plain process**: `laneSet` is the first child of `bpmn:process` after any +`ioSpecification`, before any flow element (element order above). Each `bpmn:lane` lists every flow +node assigned to it via `flowNodeRef` (events, tasks, gateways — not data objects, which aren't +flow nodes and carry no lane). ```xml @@ -168,6 +242,8 @@ Without DI, bpmn-js can't render the model, and bpmnlint's `no-bpmndi` fires for - Shape attributes: `isMarkerVisible="true"` on **exclusive gateways** (draws the "X"; otherwise renders as an empty diamond); `isHorizontal="true"` on pools/lanes; `isExpanded="true|false"` on sub-processes. +- **Data stores, `dataInput`, `dataOutput`**: a `BPMNShape` each (store 50×50, data input/output + 36×50), with the name as external label; the associations get edges as below. - **Data associations**: an edge whose `bpmnElement` is the `dataInputAssociation`/ `dataOutputAssociation` ID, waypoints from producer to data object (output) or data object to consumer (input). @@ -176,9 +252,10 @@ Without DI, bpmn-js can't render the model, and bpmnlint's `no-bpmndi` fires for ## Standard sizes -bpmn-js defaults (bpmnlint's `standard-size` rule, not in `recommended`), except two deliberate -deviations: bpmn-js doesn't grow a task to fit its label, it clips (German step names run long), and a -too-small gateway label box lets a wrapped question overflow into whatever sits below it. +bpmn-js defaults (bpmnlint's `standard-size` rule, not in `recommended`), with two deliberate +deviations: tasks are 140×100 because bpmn-js clips rather than grows a task for long (German) +names, and forking-gateway labels are 42 high so a wrapped question doesn't overflow into what sits +below. | Element | Width × Height | |---|---| diff --git a/.agents/skills/bpmn-process-design/SKILL.md b/.agents/skills/bpmn-process-design/SKILL.md index 1c6e3f7..01bb595 100644 --- a/.agents/skills/bpmn-process-design/SKILL.md +++ b/.agents/skills/bpmn-process-design/SKILL.md @@ -1,14 +1,14 @@ --- name: bpmn-process-design -description: Designs a new process from what the user wants to build plus domain knowledge from Gemini notebooks (NotebookLM, via gemini-notebook-mcp), and draws it as an agent-ready BPMN 2.0 diagram in the lanecraft notation (lane = role, serviceTask = AI, userTask = human checkpoint, scriptTask = script, data object = handoff, capped loops) that the bpmn2agent pipeline turns into agents and skills without rework. Use for "model a process for X", "draw the BPMN for what I want to build", "design a workflow from my notebook", or when a user has a goal but no diagram yet. To edit an existing .bpmn use bpmn-authoring; to turn a finished diagram into agents use bpmn-to-agentic-workflow. +description: Designs a new process from what the user wants to build plus domain knowledge from Gemini notebooks (NotebookLM, via gemini-notebook-mcp), and draws it as an agent-ready BPMN 2.0 diagram in the lanecraft notation (lane = role, serviceTask = AI, userTask = human checkpoint, scriptTask = script, data object = handoff, data store = context source, capped loops) that the bpmn2agent pipeline turns into agents and skills without rework. Use for "model a process for X", "draw the BPMN for what I want to build", "design a workflow from my notebook", or when a user has a goal but no diagram yet. To edit an existing .bpmn use bpmn-authoring; to turn a finished diagram into agents use bpmn-to-agentic-workflow. --- # Design an agent-ready BPMN process Output: a validated `.bpmn` (default: cwd) that `bpmn2agent-analyze` reads with no -findings, plus every notebook answer logged in `generated//knowledge/faq/`. Speak the -user's language in business terms; BPMN labels use it too. Every question goes through -`AskUserQuestion` with options and a recommendation. +findings, plus every notebook answer logged in `generated//knowledge/faq/`. Use the +user's language and business terms, in BPMN labels too. Ask every question via `AskUserQuestion` +with options and a recommendation. ## 1. Understand the goal @@ -22,9 +22,9 @@ Propose a kebab-case workflow name and confirm it. ToolSearch: select:mcp__gemini-notebook-mcp__notebook_list,mcp__gemini-notebook-mcp__notebook_describe,mcp__gemini-notebook-mcp__notebook_get,mcp__gemini-notebook-mcp__notebook_query ``` -Let the user pick from `notebook_list` by title (several allowed, or none); sanity-check each pick -with `notebook_describe`. Auth error → tell the user to run `nlm login`, ask whether to retry or go -on without. No notebook → offer WebSearch (cite title and URL; recommended unless the domain is +The user picks from `notebook_list` by title (several or none); sanity-check each pick with +`notebook_describe`. Auth error → tell the user to run `nlm login`; ask whether to retry or go on +without. No notebook → offer WebSearch (cite title and URL; recommended unless the domain is generic) or model knowledge. ## 3. Research the process @@ -39,7 +39,7 @@ One question per `notebook_query` call, `new_conversation: true`, scoped to the Follow up per phase while an answer stays vague. Log every answer per "FAQ log" in `${CLAUDE_SKILL_DIR}/../bpmn2agent-knowledge/references/notebook-extraction.md`, with -`--faq generated//knowledge/faq`. Read oversized answers' `.answer` in `jq` chunks. +`--faq generated//knowledge/faq`. Read an oversized answer's `.answer` in `jq` chunks. Agentic-design questions (lane cut, checkpoints, loop caps) go to `${CLAUDE_SKILL_DIR}/../agentic-workflow-kb/` first (`references/process-to-agents.md`, `references/human-in-the-loop.md`). @@ -59,18 +59,33 @@ model-only steps `⚠ unverified`. | exclusive / parallel gateway | branches, joins or loops | orchestrator logic | | collapsed `subProcess` | is a phase with its own sequence | reusable skill or sub-workflow | | data object | is a result a later step needs | artifact contract | +| data store `Art: wissen` | holds reference knowledge a step must know | cited reference in the reading skill, loaded at generation | +| data store `Art: live` | holds data read or changed at run time | tool allowlist + `## Kontextquellen` in the skill | +| data store `Art: gedächtnis` | keeps learnings across runs | curated memory file with a size cap | +| process input / output | is what a run needs to start / finally delivers | argument contract with required fields / result contract | - 2–6 lanes, one per role, never one per task. A role that only decides is a human lane of `userTask`s. - No untyped `task`. Unsure → `serviceTask`; `scriptTask` only when nothing needs judgement. - A `userTask` before every irreversible or outward-facing action (commit, send, publish, delete) and wherever the goal needs sign-off; none on routine steps. -- Names become verbatim skill text: tasks verb + object, forking gateways a question with - answer-labelled flows and one `default`, end events the resulting state. +- Names become verbatim skill text: tasks verb + object; forking gateways a question with + answer-labelled flows and one `default`; end events the resulting state. - Every loop re-enters through a merge gateway, carries its cap on the loop-back label (`Nein (max. 3×)`, default 3), and has a way out (escalation `userTask` or end). - Results cross lanes as data objects. Every task gets `Input: … Output: … Quelle: `. +- A data store is a business data set named in business terms ("Jira-Tickets Projekt LANE"), not a + system; several stores may share one source. Its documentation carries `Art: wissen | live | + gedächtnis` and `Ort: notebook: | mcp: | cli: | | datei: | + websearch`. Notebooks picked in step 2 are candidates for `wissen` stores. +- Valid pairs: `wissen` with `notebook:`, URL, `datei:`, `websearch`, or `mcp:` (a snapshot, only if + the server is connected); `live` with `mcp:`, `cli:`, `notebook:`, URL or `datei:`; `gedächtnis` + with `datei:` only (default `.claude/memory//.md`). Anything else: pick another + Art or Ort. +- The arrow alone sets access: store → task reads, task → store writes. No `Zugriff:` line. +- A `userTask` precedes every write to a `live` store, on every path. +- A `gedächtnis` store needs a writing `serviceTask` and a reader; writing needs no approval. - A phase over ~8 steps becomes a collapsed `subProcess` with its own lanes. - One pool; no message flows, timer or message events, event sub-processes or compensation (rewrites: `${CLAUDE_SKILL_DIR}/../bpmn2agent-analyze/references/unsupported.md`). Error @@ -78,15 +93,23 @@ model-only steps `⚠ unverified`. ## 5. Confirm the outline -Before drawing, show: lanes with what each becomes, then per phase the steps with type -(AI / human / script / rule) and source, gateways with answers, loops with caps, data objects. Ask: -draw as is / change what / ask the notebook more. Repeat until confirmed; never draw an unconfirmed -design. +For each step ask: "What must this step know, and where does it live?" Each answer is a store +(Art, Ort), a data object from an earlier step, or the process input; no answer means the step +needs no context. If the step writes anywhere, name the store and the `userTask` before it. + +Before drawing, show: lanes with what each becomes; per phase the steps with type +(AI / human / script / rule) and source; gateways with answers; loops with caps; data objects; a +store table (name, Art, Ort, readers, writers); the process input with its required fields and the +result the run delivers. Ask: draw as is / change what / ask the notebook more. Repeat until +confirmed; never draw an unconfirmed design. ## 6. Draw Follow `${CLAUDE_SKILL_DIR}/../bpmn-authoring/SKILL.md` steps 2–7, starting from its -`assets/skeleton.bpmn`. Use readable ids (`Task_FeedbackFormulieren`, `Gw_InvestErfuellt`). +`assets/skeleton.bpmn`. Use readable ids (`Task_FeedbackFormulieren`, `Gw_InvestErfuellt`). Draw +stores and the process input/output per its data store and `ioSpecification` conventions +(`${CLAUDE_SKILL_DIR}/../bpmn-authoring/references/xml-and-di.md` and `layout.md`); a +convention-based input is a data object no task produces. ## 7. Check it's agent-ready diff --git a/.agents/skills/bpmn-to-agentic-workflow/SKILL.md b/.agents/skills/bpmn-to-agentic-workflow/SKILL.md index a5b62a1..6f106f7 100644 --- a/.agents/skills/bpmn-to-agentic-workflow/SKILL.md +++ b/.agents/skills/bpmn-to-agentic-workflow/SKILL.md @@ -22,26 +22,26 @@ bpmn: # BPMN → Agentic Workflow -Runs the five `bpmn2agent-*` stage skills in order via the `Skill` tool, carries the gates and loops -between them, and reports the result. It does no analysis, mapping or generation itself. +Run the five `bpmn2agent-*` stage skills in order via the `Skill` tool, carry the gates and loops +between them, and report the result. Do no analysis, mapping or generation yourself. Ask every question via `AskUserQuestion` with options and a recommendation, in the user's language. ## 1. Intake -Gather, asking only where context doesn't already answer it: +Gather only what context doesn't already answer: -- **Source `.bpmn`** — glob for `*.bpmn` if not named; confirm when several candidates exist. No - diagram yet → run `bpmn-process-design` first and use the file it writes. -- **Workflow name** — kebab-case; default to the `.bpmn` basename and confirm. Output goes to - `generated//`; tell the user. +- **Source `.bpmn`** — glob for `*.bpmn` if not named; confirm when several match. No diagram yet → + run `bpmn-process-design` first and use the file it writes. +- **Workflow name** — kebab-case; default to the `.bpmn` basename and confirm. Tell the user output + goes to `generated//`. - **Language** — for questions and generated `README.md`s (generated skills/agents/scripts stay English). Default: the language the user writes in. - **Knowledge sources** — whether Gemini notebooks (NotebookLM) cover this process; knowledge does the lookup. -If `generated//` already exists, this is a re-run: confirm the same name rather than -asking afresh; every stage diffs against its previous output itself. Write nothing in this step. +If `generated//` exists, this is a re-run: confirm the same name instead of asking +afresh; every stage diffs against its previous output itself. Write nothing in this step. ## 2. Analyze @@ -49,7 +49,7 @@ Run `bpmn2agent-analyze` with the intake context; it writes the draft `workflow- ## 3. Knowledge -Run `bpmn2agent-knowledge`. Run it even when the user said no notebook exists — it owns the fallback +Run `bpmn2agent-knowledge`, even when the user said no notebook exists — it owns the fallback choice (WebSearch / unverified model knowledge / pause to build a notebook); don't pre-decide it. ## 4. Design → confirm mapping plan (loop A) @@ -57,7 +57,7 @@ choice (WebSearch / unverified model knowledge / pause to build a notebook); don Run `bpmn2agent-design`. It ends with the user's answer to the mapping plan: - **Accepted** → step 5. The confirmed spec is the safe point to resume from in a new session. -- **Adjust specific elements** → design redoes those and re-confirms, within this step, at most 3 +- **Adjust specific elements** → design redoes those and re-confirms within this step, at most 3 times (design handles the cap). - **The diagram must change** → **loop A**: stop the run. The user edits the `.bpmn` with `bpmn-authoring` (`${CLAUDE_SKILL_DIR}/../bpmn-authoring/SKILL.md`), then re-runs this skill from @@ -71,14 +71,15 @@ they show red in the mapping view. ## 6. Verify (loop B) Run `bpmn2agent-verify`. Pass → step 7. Fail → **loop B**, routing each finding as verify reports: - - mapping/pattern problem → step 4, scoped to the flagged elements; - - generation defect → step 5, only the affected files; - - changed (sha256 mismatch) or invalid `.bpmn` → step 2, then steps 3–6 again. - - Re-run step 6 after every fix. Cap: 3 verify rounds per run. At the cap, or when the same finding - (category + file) returns unchanged twice, stop and ask: send the remaining findings to design, - change the diagram (loop A), or accept a handoff marked **unverified** with the findings listed. - Never report success at the cap. + +- mapping/pattern problem → step 4, scoped to the flagged elements; +- generation defect → step 5, only the affected files; +- changed (sha256 mismatch) or invalid `.bpmn` → step 2, then steps 3–6 again. + +Re-run step 6 after every fix. Cap: 3 verify rounds per run. At the cap, or when the same finding +(category + file) returns unchanged twice, stop and ask: send the remaining findings to design, +change the diagram (loop A), or accept a handoff marked **unverified** with the findings listed. +Never report success at the cap. ## 7. Handoff diff --git a/.agents/skills/bpmn2agent-analyze/SKILL.md b/.agents/skills/bpmn2agent-analyze/SKILL.md index 2dde090..2e23880 100644 --- a/.agents/skills/bpmn2agent-analyze/SKILL.md +++ b/.agents/skills/bpmn2agent-analyze/SKILL.md @@ -1,6 +1,6 @@ --- name: bpmn2agent-analyze -description: Reads a hand-drawn .bpmn, validates and inventories it, flags constructs the pipeline can't generate yet (pools/message flows, timer/message events, event sub-processes, compensation), asks the business user about gaps only, and writes the draft generated//workflow-spec.yaml. On a changed .bpmn it diffs by element id and asks only about what changed. Never edits the .bpmn. Use when a diagram is new or changed, before bpmn2agent-knowledge. +description: Reads a hand-drawn .bpmn, validates and inventories it, flags constructs the pipeline can't generate yet (pools/message flows, timer/message events, event sub-processes, compensation), asks the business user about gaps only (incl. data stores and process input/output), and writes the draft generated//workflow-spec.yaml. On a changed .bpmn it diffs by element id and asks only about what changed. Never edits the .bpmn. Use when a diagram is new or changed, before bpmn2agent-knowledge. bpmn: file: docs/planning/bpmn-to-agentic-workflow.bpmn elements: @@ -18,18 +18,17 @@ bpmn: Input: a `.bpmn` the business user drew. Output: the **draft** `generated//workflow-spec.yaml`, conforming to -`${CLAUDE_SKILL_DIR}/../bpmn2agent-design/assets/workflow-spec.schema.yaml`. Analysis records what is -there, what is missing and what can't be generated. It never decides how an element gets built -(`kind`, `generatedPaths`, `pattern` are `bpmn2agent-design`'s), and never modifies, moves or renames -the source `.bpmn`. +`${CLAUDE_SKILL_DIR}/../bpmn2agent-design/assets/workflow-spec.schema.yaml`. Record what is there, what is +missing and what can't be generated. Never decide how an element gets built (`kind`, `generatedPaths`, +`pattern` belong to `bpmn2agent-design`). Never modify, move or rename the source `.bpmn`. -Every question goes through `AskUserQuestion` with concrete options plus a recommendation, in the -user's language. +Ask every question through `AskUserQuestion`, in the user's language, with concrete options and a +recommendation. ## 1. Locate the source `.bpmn` and the target folder -Ask (or infer) which `.bpmn` to analyze and the workflow name: kebab-case, default the `.bpmn` -basename; confirm rather than ask when unambiguous. Write only under `generated//`. +Ask (or infer) which `.bpmn` to analyze and the workflow name: kebab-case, default the `.bpmn` basename; +when unambiguous, confirm instead of asking. Write only under `generated//`. ## 2. Validate structurally first @@ -38,9 +37,8 @@ ${CLAUDE_SKILL_DIR}/../bpmn-authoring/scripts/validate.sh .bpmn ``` On failure, **stop**: name the failed stage (XSD / bpmn-moddle / bpmnlint, see -`${CLAUDE_SKILL_DIR}/../bpmn-authoring/references/validation.md`) and send the user to -`bpmn-authoring`. Always run it, even on a file you believe is clean: it fills the tool cache step 3 -needs. +`${CLAUDE_SKILL_DIR}/../bpmn-authoring/references/validation.md`) and send the user to `bpmn-authoring`. +Always run it, even on a file you believe is clean: it fills the tool cache step 3 needs. ## 3. Inventory every element @@ -49,16 +47,18 @@ node ${CLAUDE_SKILL_DIR}/scripts/inventory.mjs \ "${BPMN_TOOLS_CACHE:-$HOME/.cache/bpmn-authoring-tools}" .bpmn ``` -Prints JSON: scopes with pattern-rubric signals, lanes, flow nodes, sequence flows, data objects and -associations, annotations, `meta.sha256` and `findings[]`. Save it to a scratch file; later steps -reuse it. How to read role hints, task types and annotations: `references/conventions.md`. What each -`unresolved` finding means and which rewrite to suggest: `references/unsupported.md`. +It prints JSON: scopes with pattern-rubric signals, lanes, flow nodes, sequence flows, data objects and +associations, `dataStores[]` (parsed `Art:`/`Ort:`, readers, writers), `processIO` (workflow input and +output), annotations, `meta.sha256` and `findings[]`. Save it to a scratch file for later steps. How to +read role hints, task types, annotations, data stores and process input/output: +`references/conventions.md`. What each `unresolved` finding means and its rewrite suggestion: +`references/unsupported.md`. ## 4. Flag unsupported elements -Present every `severity: "unresolved"` finding in plain language, quoting the BPMN label verbatim, -with the rewrite suggestion from `references/unsupported.md`, and ask: change the diagram, or keep it -as an open gap? For example: +Present every `severity: "unresolved"` finding in plain language, quoting the BPMN label verbatim, with +the rewrite suggestion from `references/unsupported.md`, and ask: change the diagram, or keep it as an +open gap? For example: > Your diagram has a timer on "Wartezeit prüfen". I can't turn a clock-based wait into a Claude > agent yet. I'd suggest "try up to 3 times" (a retry cap) instead. Change the diagram, or note it @@ -66,12 +66,12 @@ as an open gap? For example: - User will rewrite the `.bpmn` (in `bpmn-authoring`): tell them to re-run this skill afterwards; don't wait in-session unless they say so. -- User proceeds as-is: the element gets `kind: unresolved` with `reason` = the rewrite suggestion, - plus an `openQuestions[]` entry (step 6). +- User proceeds as-is: the element gets `kind: unresolved` with `reason` = the rewrite suggestion, plus + an `openQuestions[]` entry (step 6). ## 5. Interview about gaps — nothing else -Ask only about these gaps, never about generation decisions and never about an element without a gap: +Ask only about these gaps; never about generation decisions or elements without a gap: - **No role**: no lane and no role hint (`sdlc:step` extension or `Rolle:` documentation). Ask which role it belongs to. @@ -83,8 +83,23 @@ Ask only about these gaps, never about generation decisions and never about an e as a note (rename to a typed task in `bpmn-authoring`). Don't write a `kind`. - **Unclear multi-instance collection**: `multiInstance.collectionHint` is `null` or came only from an annotation/name pattern. Ask what the collection is (e.g. "one per epic"). +- **`store-missing-art-ort` finding**: no readable `Art:` or `Ort:` line. Ask for the missing value as + concrete options (the three Arts; the Orts valid for the Art, see the matrix in + `references/conventions.md`), recommending what the store's name and readers suggest. +- **`store-invalid-art-ort` finding**: offer both repairs, a valid Ort for that Art or a different Art + for that Ort (e.g. `gedächtnis` + `mcp:` becomes `live` + `mcp:` or `gedächtnis` + `datei:`), with a + recommendation. +- **`memory-store-incomplete` finding**: a `gedächtnis` store needs a writing `serviceTask` and a reader. + Ask which task writes or reads it (options: the diagram's tasks), or whether it is really + `wissen`/`live`. +- **`store-without-associations` finding**: ask which task reads or writes it, or whether to drop it. +- **Several `processIO.inputs[]` or `outputs[]`**: ask which is the workflow's real input/result + (`workflowIO` holds one each); the others stay plain artifacts. - **Every unresolved finding from step 4.** +Answer a store gap in the spec only: record `art`/`ort` as confirmed and tell the user to fix the +`Art:`/`Ort:` lines in the `.bpmn` (in `bpmn-authoring`) so diagram and spec stay in sync. + ## 6. Write the spec draft Write (or merge, step 7) `generated//workflow-spec.yaml`: @@ -92,8 +107,8 @@ Write (or merge, step 7) `generated//workflow-spec.yaml`: - **`meta`**: `workflowName`, `sourceBpmn.path`/`sha256` (inventory `meta.sha256`), `language` (the user's), `generatorVersion`, `created`/`updated`, and `outputLayout: claude-dir` on every new spec. On a re-run keep the existing value; a spec without the field is on the legacy layout. -- **`roles`**: one per lane (or per confirmed documentation-fallback role): `bpmnLaneId`, `label` - (verbatim lane name), `agentName` guessed as `{workflow}-{role}` for design to confirm. Leave +- **`roles`**: one per lane (or confirmed documentation-fallback role): `bpmnLaneId`, `label` (verbatim + lane name), `agentName` guessed as `{workflow}-{role}` for design to confirm. Leave `modelTier`/`tools` unset. - **`elements`**: one per flow node: `bpmnType`, `label` (verbatim), `lane` (resolved role id, if any), `kind: unresolved` as placeholder for all. `reason`: the rewrite suggestion for unsupported @@ -103,13 +118,26 @@ Write (or merge, step 7) `generated//workflow-spec.yaml`: - **`artifacts`**: one per `dataObject`/`dataObjectReference`, kebab-case id from its name. `pathPattern` defaults to `generated//artifacts//{id}.md`. `producer`/`consumers` from associations; `producer` is an array when more than one element has a real `dataOutputAssociation` - into it (both are producers, not consumers). + into it (all are producers, not consumers). +- **`contextSources`**: one per `dataStores[]` entry, as the schema defines it. Id = kebab-case of the + store name; `name` verbatim; `bpmnElement` = the reference id; `art` and `ort` (`{type, ref}`) as parsed + or confirmed in step 5; `readers`/`writers` from the inventory (only the store's arrows decide read + vs. write). Leave `tools` and `memory` unset (design resolves them). Each store also gets + `elements.` (`bpmnType: dataStoreReference`, `label` verbatim, `kind: context-source` as a + proposal). +- **`workflowIO`**: from `processIO`. `input.artifact`/`output.artifact` = the artifact id of the chosen + entry. A real `dataInput`/`dataOutput` gets its own `artifacts.` (kebab-case of its name; `producer` + unset for an input, `consumers`/`producer` from `readers`/`writers`) and `elements.` (`bpmnType: + dataInput|dataOutput`, `kind: workflow-input|workflow-output`). A convention entry is an ordinary data + object artifact; its `elements.` gets `kind: workflow-input|workflow-output` instead of + `artifact-contract`. For a real input, `input.required` = its name as a camelCase field (`Ticket-ID` + becomes `ticketId`); for a convention input leave it unset. Omit `workflowIO` when `processIO` is empty. - **`pattern`**, **`knowledge`**: leave unset (design and knowledge own them). - **`openQuestions`**: one per gap from steps 4–5 the user deferred, keys `elementId`, `question`, `answer`. Record the user's answer when given; `answer: null` when deferred. -Validate the spec, then read only the spec-schema section of the output (other categories may fail -this early): +Validate the spec, reading only the spec-schema section of the output (other categories may fail this +early): ```bash node ${CLAUDE_SKILL_DIR}/../bpmn2agent-verify/scripts/verify.mjs \ @@ -124,13 +152,14 @@ If the spec already exists: 1. Compare its `meta.sourceBpmn.sha256` with the inventory's `meta.sha256`. Equal: tell the user nothing changed and stop (or confirm they meant to re-run). -2. Otherwise diff by element id across `flowNodes`/`dataObjects`/`lanes` vs. the spec's - `elements`/`artifacts`/`roles`, comparing `bpmnType`, `label`, `lane`, `documentation`, - `multiInstance`, `eventDefinitions`. Classify each id as new, changed, removed or unchanged. +2. Otherwise diff by element id: `flowNodes`/`dataObjects`/`dataStores`/`processIO`/`lanes` vs. the + spec's `elements`/`artifacts`/`contextSources`/`workflowIO`/`roles`, comparing `bpmnType`, `label`, + `lane`, `documentation`, `multiInstance`, `eventDefinitions`; for a store also `art`, `ort`, + `readers`, `writers`. Classify each id as new, changed, removed or unchanged. 3. Interview (step 5) only new and changed elements with a gap. Never touch an unchanged element's `kind`/`reason`/`generatedPaths` or other later-stage decisions. -4. Removed elements: flag them to the user; don't delete without confirmation, especially roles or - artifacts still referenced (`elements.*.lane`, `artifacts.*.producer`/`consumers`). +4. Removed elements: flag them to the user; don't delete without confirmation, especially roles, + artifacts or stores still referenced (`elements.*.lane`, `artifacts.*.producer`/`consumers`, + `workflowIO`). 5. Update `meta.sourceBpmn.sha256` and `meta.updated`; merge into the existing file instead of rewriting it, so untouched sections stay byte-identical. - diff --git a/.agents/skills/bpmn2agent-analyze/references/conventions.md b/.agents/skills/bpmn2agent-analyze/references/conventions.md index f5b8d53..d4938d9 100644 --- a/.agents/skills/bpmn2agent-analyze/references/conventions.md +++ b/.agents/skills/bpmn2agent-analyze/references/conventions.md @@ -1,34 +1,33 @@ # Input contract: how a BPMN diagram maps to roles and hints -What `bpmn2agent-analyze` (and `inventory.mjs`) assumes about a `.bpmn`. Analysis never rewrites -the source `.bpmn` to conform. +What `bpmn2agent-analyze` (and `inventory.mjs`) assumes about a `.bpmn`. Analysis never rewrites the +source `.bpmn` to conform. ## Lane = role/agent (primary) One pool/process with a `laneSet`; each lane becomes a role (`roles.` in `workflow-spec.yaml`). -It becomes a subagent file only under the orchestrator-agent pattern (or a mixed phase using it), -decided by `bpmn2agent-design`. A collapsed sub-process may have its **own** `laneSet` in its -drill-down plane; those lanes are role assignments scoped to that sub-process only. -`flowNodes[].lane` is the lane id from `flowNodeRef`, or `null` if the scope has no `laneSet`. +`bpmn2agent-design` decides whether it becomes a subagent file (only under the orchestrator-agent +pattern, or a mixed phase using it). A collapsed sub-process may have its **own** `laneSet` in its +drill-down plane; those lanes assign roles within that sub-process only. `flowNodes[].lane` is the lane +id from `flowNodeRef`, or `null` if the scope has no `laneSet`. -A scope with a `laneSet` must place every flow node in a lane; otherwise `inventory.mjs` emits an -`element-without-lane` warning. Ask the business user which role those belong to before writing the -spec. +In a scope with a `laneSet`, a flow node outside every lane gets an `element-without-lane` warning. Ask +the business user which role it belongs to before writing the spec. ## `` role fallback (for scopes with no lanes at all) -For a scope with **no** `laneSet`, the role hint per element comes from, in this order: +In a scope with **no** `laneSet`, the role hint per element comes from, in this order +(`extractRoleHint()`): -1. **`sdlc:step agentRole="…"` extension element** — structured and unambiguous; preferred. -2. **`Rolle: X` in ``** — free text (`Rolle: … Input: … Output: …`); only when - there is no extension element. +1. **`sdlc:step agentRole="…"` extension element** — structured, preferred. +2. **`Rolle: X` in ``** — free text (`Rolle: … Input: … Output: …`); only without an + extension element. -`extractRoleHint()` implements this order. Role hints in a lane-less scope surface as a -`documentation-role-fallback` info finding (an accepted input shape, not a problem). The inventory -never resolves hints into `roles.*`; the interview does. +Such hints surface as a `documentation-role-fallback` info finding (an accepted input shape, not a +problem). The inventory never resolves hints into `roles.*`; the interview does. -On **every** element, laned or not, read `Input: … Output: …` in `` as a hint -for `elements..inputs`/`outputs` wiring. +On **every** element, laned or not, read `Input: … Output: …` in `` as a hint for +`elements..inputs`/`outputs` wiring. ## Task-type meanings @@ -37,24 +36,75 @@ How analysis reads each type (the generation decision per type is design's `mapp | BPMN type | Read as | |---|---| | `task` (untyped) | Ambiguous. Ask whether it is a human, agent or script step; don't guess. | -| `userTask` / `manualTask` | A human checkpoint — someone must look at something and decide/confirm before the process continues. | -| `serviceTask` | LLM/agent work — something an agent does on the process's behalf, not a human and not a deterministic script. | -| `scriptTask` | A deterministic script step — no judgement involved, same input always produces the same output. | -| `businessRuleTask` | A deterministic check/decision against a rule (a threshold, a policy lookup) — still not a human, still not free-form LLM judgement. | -| `callActivity` | A call to a reusable, separately-defined process (`calledElement` points at another top-level `bpmn:Process`) — analyzed as its own root scope, independently of where it's called from. | -| Collapsed `subProcess` | A phase with its own inner detail — both a flow node in the parent scope *and* its own nested scope, walked recursively. | +| `userTask` / `manualTask` | Human checkpoint: someone looks and decides/confirms before the process continues. | +| `serviceTask` | LLM/agent work on the process's behalf; not a human, not a deterministic script. | +| `scriptTask` | Deterministic script step: no judgement, same input gives same output. | +| `businessRuleTask` | Deterministic check against a rule (threshold, policy lookup); not a human, not free-form LLM judgement. | +| `callActivity` | Call to a separately defined process (`calledElement` → another top-level `bpmn:Process`), analyzed as its own root scope, independent of the call site. | +| Collapsed `subProcess` | A phase with inner detail: a flow node in the parent scope *and* its own nested scope, walked recursively. | ## Annotations as hints, never as structure Annotations carry no flow or role information. `inventory.mjs` reads them only as the last-resort -multi-instance collection hint. `extractCollectionHint()` order: `sdlc:collection` extension → -formal `loopCardinality`/`loopDataInputRef` → name pattern ("per X": `je`/`pro`/`jede(r/n/s)`, -`for each`) → associated annotation text. Treat an annotation-derived hint as unconfirmed: ask for -the actual collection instead of writing the annotation text into `collection.ref`. +multi-instance collection hint. `extractCollectionHint()` order: `sdlc:collection` extension → formal +`loopCardinality`/`loopDataInputRef` → name pattern ("per X": `je`/`pro`/`jede(r/n/s)`, `for each`) → +associated annotation text. Treat an annotation-derived hint as unconfirmed: ask for the actual +collection instead of writing the annotation text into `collection.ref`. + +## Data stores = context sources + +Every durable source is a `bpmn:DataStoreReference` (cylinder; name = the business data set, e.g. +"Jira-Tickets Projekt LANE", not the system). The root `bpmn:DataStore` is unnamed, so `inventory.mjs` +reads `Art:`/`Ort:` from the reference's documentation. Two lines: + +``` +Art: wissen | live | gedächtnis +Ort: notebook: | mcp: | cli: | | datei: | websearch +``` + +`Art` is normalised for the spec (`gedächtnis` becomes `gedaechtnis`); `Ort` becomes `{type, ref}` +(`ref` omitted for `websearch`; a URL keeps the whole URL as `ref`). The location key is `Ort:` because +task documentation uses `Quelle:` for provenance. Several stores may share one `Ort:`. + +**Arrow direction is the only access rule**: store → task (`dataInputAssociation`) = read, task → store +(`dataOutputAssociation`) = write; there is no access line. An association on a `subProcess` counts for +the sub-process element. Stores are not flow nodes: no lane, no sequence flow. + +### Art × Ort + +| Art | Valid Ort type | +|---|---| +| `wissen` | `notebook`, URL, `datei`, `websearch`, `mcp` | +| `live` | `mcp`, `cli`, `notebook`, URL, `datei` | +| `gedaechtnis` | `datei` | + +Anything else is a `store-invalid-art-ort` finding (e.g. `live` + `websearch`, `wissen` + `cli`, +`gedächtnis` + `mcp`). Further store findings, all `warning`: `store-missing-art-ort` (a line is missing +or unparseable, details in `dataStores[].problems`), `memory-store-incomplete` (a `gedaechtnis` store +needs a writing `serviceTask` and a reader), `store-without-associations`. They are questions for the +user; the inventory never repairs a store. Whether a write into a `live` store has a `userTask` in front +of it is checked by `bpmn2agent-verify`, not here. + +## Process input and output + +`inventory.mjs` reports the workflow's input and output in `processIO.inputs[]`/`outputs[]`, each entry +tagged `source: ioSpecification | convention`. Only the top-level workflow process counts, not +sub-processes and not a process some `callActivity` calls. + +1. **Real**: a process-wide `bpmn:ioSpecification` (first child of the `process`) with `dataInput` / + `dataOutput`. Readers/writers come from the data associations whose `sourceRef` (input) or `targetRef` + (output) is that element. bpmn.io has no palette entry for these; they exist only in XML. +2. **Convention**: an input is a data object reference that no activity produces and some activity reads; + an output is one that is produced and nobody reads. Applies only when the process draws at least one + data-object read; a diagram with output associations only has no "nobody reads" signal and yields none. + +A diagram may mix both. When several entries qualify, the user picks the real input/result in the +interview; `workflowIO` holds one of each. ## Left to `bpmn2agent-design` - skill vs. agent-checklist item per `serviceTask` (mapping rubric); -- the orchestration pattern — `inventory.mjs` only computes the pattern-rubric signals; -- deterministic vs. judgement gateways — `evaluateGateway()` is a heuristic feeding +- the orchestration pattern (`inventory.mjs` only computes the pattern-rubric signals); +- tool lists and placement for `live` stores, the memory path/cap for `gedaechtnis` stores; +- deterministic vs. judgement gateways: `evaluateGateway()` is a heuristic feeding `judgementBranchCount`; design confirms borderline cases. diff --git a/.agents/skills/bpmn2agent-analyze/references/unsupported.md b/.agents/skills/bpmn2agent-analyze/references/unsupported.md index 20f785c..d8f7e40 100644 --- a/.agents/skills/bpmn2agent-analyze/references/unsupported.md +++ b/.agents/skills/bpmn2agent-analyze/references/unsupported.md @@ -1,91 +1,78 @@ # Unsupported constructs (v1) — flag red, suggest a rewrite -`bpmn2agent-analyze` never silently drops a construct it can't generate from. Every element listed -here gets a `findings[]` entry (`severity: "unresolved"`) from `inventory.mjs`, must be surfaced to -the business user during the interview, and — once written to the spec draft by -`bpmn2agent-analyze` — gets `kind: unresolved` with a `reason` explaining *why*, so it renders red in -the mapping view (`mapping-rubric.md`'s legend) instead of vanishing. `bpmn2agent-verify`'s "no red -in the map" check fails the run until every one of these is either rewritten out of the source -`.bpmn` (by the human modeler, never by this pipeline) or explicitly accepted as an open, unresolved -gap. +`bpmn2agent-analyze` never silently drops a construct it can't generate from. For every construct here +`inventory.mjs` emits a `findings[]` entry (`severity: "unresolved"`). Surface it to the business user in +the interview. In the spec draft it gets `kind: unresolved` with a `reason` saying *why*, so it renders +red in the mapping view (`mapping-rubric.md`'s legend). `bpmn2agent-verify`'s "no red in the map" check +fails until each one is rewritten out of the source `.bpmn` (by the human modeler, never by this +pipeline) or explicitly accepted as an open gap. ## Pools and message flows -**Why unsupported**: a second pool + message flows models cross-organization or cross-system -choreography — a different system or company doing its own thing and exchanging messages with this -one. This family generates *one* coherent set of agents/skills for *one* process; it has no model -for "an external system I don't control." +**Why unsupported**: a second pool with message flows models choreography with an external system or +organization. The pipeline generates one set of agents/skills for one process and has no model for a +system it doesn't control. -**Detection**: `inventory.mjs` flags this once per diagram (`unsupported-pools-message-flows`) when -the `bpmn:Collaboration` root element has more than one `bpmn:participant` connected by at least one -`bpmn:messageFlow`. +**Detection**: `unsupported-pools-message-flows`, once per diagram, when the `bpmn:Collaboration` has +more than one `bpmn:participant` connected by at least one `bpmn:messageFlow`. -**Rewrite suggestion**: if the second pool actually represents a *role* your own team/agents play -(not a genuinely external system), flatten it into a lane of the single process instead — that's -exactly what lanes are for. If it's genuinely external (a customer, a vendor system, a different -department that won't run this pipeline), the interaction with it has to be modelled as a step your -process takes (e.g. a `serviceTask` "send the request to X" / a `userTask` "wait for Y's reply"), -not as a second pool. +**Rewrite suggestion**: if the second pool is really a *role* your own team/agents play, flatten it into +a lane of the single process. If it is genuinely external (a customer, a vendor system, a department +that won't run this pipeline), model the interaction as a step of your process (e.g. a `serviceTask` +"send the request to X" / a `userTask` "wait for Y's reply"), not as a second pool. ## Timer events (start, intermediate, boundary) -**Why unsupported**: nothing in this pipeline's generated output (skills, hooks, orchestrator -agents, Workflow scripts) has a wall-clock scheduler. A generated Workflow script only ever runs -when a human deliberately starts it (see `pattern-rubric.md`); there's no daemon to wake up an -orchestrator agent at a timer. +**Why unsupported**: no generated output (skills, hooks, orchestrator agents, Workflow scripts) has a +wall-clock scheduler. A generated Workflow script runs only when a human starts it (see +`pattern-rubric.md`); nothing wakes an orchestrator agent at a timer. -**Detection**: `inventory.mjs` flags any flow node whose `eventDefinitions` includes -`bpmn:TimerEventDefinition` (`unsupported-timer-event`). +**Detection**: `unsupported-timer-event`, any flow node whose `eventDefinitions` includes +`bpmn:TimerEventDefinition`. -**Rewrite suggestion**: model the timer as a loop cap on the enclosing gate instead (`gate.maxLoops` -in the spec) — "retry up to N times" rather than "wait until a clock fires." If the intent is -genuinely a scheduled/recurring run, that's an operational concern for whoever runs the generated -artifacts (e.g. an external cron/scheduler invoking the workflow), not something the BPMN itself -should express. +**Rewrite suggestion**: model the timer as a loop cap on the enclosing gate (`gate.maxLoops` in the +spec): "retry up to N times" instead of "wait until a clock fires". A genuinely scheduled/recurring run +is an operational concern for whoever runs the generated artifacts (e.g. an external cron/scheduler +invoking the workflow), not something the BPMN should express. ## Message events (start, intermediate, boundary) -**Why unsupported**: same root cause as pools/message flows — a message event models a signal from -outside this process's own control flow, which only makes sense with a choreography partner this -pipeline doesn't generate for. +**Why unsupported**: like pools/message flows, a message event models a signal from outside the +process's own control flow, which needs a choreography partner the pipeline doesn't generate for. -**Detection**: `inventory.mjs` flags any flow node whose `eventDefinitions` includes -`bpmn:MessageEventDefinition` (`unsupported-message-event`). +**Detection**: `unsupported-message-event`, any flow node whose `eventDefinitions` includes +`bpmn:MessageEventDefinition`. -**Rewrite suggestion**: if the message's sender is really a role your own agents play, flatten it -into a lane and turn the message exchange into an ordinary sequence flow (a task that produces what -the message would have carried, followed by a task that consumes it). If it's genuinely an external -system, model receiving from it as an explicit step (e.g. a `serviceTask` your agent performs by -calling out, or a `userTask` where a human relays the result in). +**Rewrite suggestion**: if the sender is really a role your own agents play, flatten it into a lane and +turn the exchange into an ordinary sequence flow (a task producing what the message carried, followed by +a task consuming it). If it is genuinely external, model receiving from it as an explicit step (e.g. a +`serviceTask` your agent performs by calling out, or a `userTask` where a human relays the result in). ## Event sub-processes (`subProcess triggeredByEvent="true"`) -**Why unsupported**: an event sub-process runs detached from the main flow, triggered by an event -that can fire at any point while its parent is active (e.g. "budget exhausted, anywhere in this -phase") — there's no equivalent of "a handler that can interrupt any step" in any of the three -generated orchestration patterns; each one processes its steps in an explicit, traceable order. +**Why unsupported**: an event sub-process runs detached from the main flow and can fire at any point +while its parent is active (e.g. "budget exhausted, anywhere in this phase"). None of the three +generated orchestration patterns has a handler that can interrupt any step; each runs its steps in an +explicit, traceable order. -**Detection**: `inventory.mjs` flags any `bpmn:SubProcess`/`bpmn:Transaction` with -`triggeredByEvent="true"` (`unsupported-event-subprocess`). +**Detection**: `unsupported-event-subprocess`, any `bpmn:SubProcess`/`bpmn:Transaction` with +`triggeredByEvent="true"`. -**Rewrite suggestion**: if the event sub-process is really guarding one specific activity (e.g. "if -this step fails, do X"), model it as an explicit boundary error event on that activity instead — -that construct *is* supported (see mapping-rubric.md's v1 supported list). If it genuinely needs to -watch the whole run rather than one activity, express it as its own agent-checklist item ("check for -condition X before/after every step") on the role that owns the surrounding phase, rather than as -BPMN control flow. +**Rewrite suggestion**: if it really guards one specific activity ("if this step fails, do X"), model it +as a boundary error event on that activity, which *is* supported (see mapping-rubric.md's v1 supported +list). If it must watch the whole run, express it as an agent-checklist item ("check for condition X +before/after every step") on the role owning the surrounding phase, not as BPMN control flow. ## Compensation (compensation events and `isForCompensation` activities) -**Why unsupported**: compensation models an automatic, engine-driven rollback triggered by a -cancellation — none of the generated patterns have a transactional engine underneath them to trigger -it; a Claude agent/skill/hook has no built-in "undo this activity" mechanism to hook into. +**Why unsupported**: compensation is an automatic, engine-driven rollback triggered by a cancellation. +No generated pattern has a transactional engine to trigger it, and a Claude agent/skill/hook has no +built-in "undo this activity" mechanism. -**Detection**: `inventory.mjs` flags any flow node whose `eventDefinitions` includes -`bpmn:CompensateEventDefinition`, or that has `isForCompensation="true"` set -(`unsupported-compensation`). +**Detection**: `unsupported-compensation`, any flow node whose `eventDefinitions` includes +`bpmn:CompensateEventDefinition`, or with `isForCompensation="true"`. **Rewrite suggestion**: model the rollback as an explicit, separate task on the happy path's error -branch instead — "if the approval is later reversed, do Y" becomes an ordinary task reachable via a -gateway, not an implicit compensation handler. This keeps the rollback logic visible in the diagram -and traceable to a generated step, exactly like every other task. +branch: "if the approval is later reversed, do Y" becomes an ordinary task reachable via a gateway, not +an implicit compensation handler. This keeps the rollback visible in the diagram and traceable to a +generated step. diff --git a/.agents/skills/bpmn2agent-analyze/scripts/inventory.mjs b/.agents/skills/bpmn2agent-analyze/scripts/inventory.mjs index de29a8e..2c28486 100644 --- a/.agents/skills/bpmn2agent-analyze/scripts/inventory.mjs +++ b/.agents/skills/bpmn2agent-analyze/scripts/inventory.mjs @@ -4,9 +4,12 @@ // Parses with bpmn-moddle and prints a JSON inventory to stdout: every // process/subProcess scope, every lane, every flow node (with lane/scope/documentation/ // incoming/outgoing/multi-instance/boundary-events/eventDefinitions), every sequence flow, -// every data object/reference + data association, every text annotation + its associated -// element, computed pattern-rubric signals (per scope and aggregated), and a `findings` -// array of things bpmn2agent-analyze's SKILL.md should ask the business user about. +// every data object/reference + data association, every data store reference (`dataStores[]`: +// parsed Art:/Ort: documentation, readers/writers from the arrow direction), the process-wide +// input/output (`processIO`: real ioSpecification plus the data-object convention), every text +// annotation + its associated element, computed pattern-rubric signals (per scope and +// aggregated), and a `findings` array of things bpmn2agent-analyze's SKILL.md should ask the +// business user about. // // bpmn-moddle is resolved via createRequire against /package.json, same reasoning // as bpmn-authoring/scripts/check-moddle.mjs: Node's ESM resolver won't find a package @@ -93,11 +96,42 @@ function extractCollectionHint(el, textAnnotationsByAssociatedId) { return null; } +// --- data stores: `Art:` / `Ort:` lines in the store reference's --- +// Art x Ort matrix, same as references/conventions.md and mapping-rubric.md. +const ART_ORT_MATRIX = { + wissen: new Set(['notebook', 'url', 'datei', 'websearch', 'mcp']), + live: new Set(['mcp', 'cli', 'notebook', 'url', 'datei']), + gedaechtnis: new Set(['datei']), +}; +const normaliseArt = (raw) => + raw.trim().toLowerCase().replace(/ä/g, 'ae').replace(/ö/g, 'oe').replace(/ü/g, 'ue').replace(/ß/g, 'ss'); +function parseOrt(raw) { + const v = raw.trim(); + if (/^https?:\/\/\S+$/i.test(v)) return { type: 'url', ref: v }; + if (/^websearch$/i.test(v)) return { type: 'websearch' }; + const m = /^(notebook|mcp|cli|datei):\s*(\S.*)$/i.exec(v); + return m ? { type: m[1].toLowerCase(), ref: m[2].trim() } : null; +} +function parseStoreDoc(doc) { + const artRaw = /^\s*Art:[ \t]*(.*?)\s*$/im.exec(doc || '')?.[1] ?? null; + const ortRaw = /^\s*Ort:[ \t]*(.*?)\s*$/im.exec(doc || '')?.[1] ?? null; + const art = artRaw ? (normaliseArt(artRaw) in ART_ORT_MATRIX ? normaliseArt(artRaw) : null) : null; + const ort = ortRaw ? parseOrt(ortRaw) : null; + const problems = []; + if (!artRaw) problems.push('missing-art'); + else if (!art) problems.push('unparseable-art'); + if (!ortRaw) problems.push('missing-ort'); + else if (!ort) problems.push('unparseable-ort'); + if (art && ort && !ART_ORT_MATRIX[art].has(ort.type)) problems.push('invalid-combination'); + return { artRaw, ortRaw, art, ort, problems }; +} + const scopes = []; const lanes = []; const flowNodes = []; const sequenceFlows = []; const dataObjects = []; +const dataStores = []; const textAnnotations = []; const associationsRaw = []; // bpmn:Association elements, resolved into textAnnotations after the walk const findings = []; @@ -210,6 +244,21 @@ function walkScope(scopeEl, scopeId, parentScopeId, scopeType) { }); continue; } + if (el.$type === 'bpmn:DataStoreReference') { + // The visible node carries the documentation; the root bpmn:DataStore is unnamed (like + // DataObject vs. DataObjectReference), so Art:/Ort: are read from the reference. + dataStores.push({ + id: el.id, + name: el.name || null, + scopeId, + dataStoreRef: el.dataStoreRef?.id || null, + documentation: docText(el), + ...parseStoreDoc(docText(el)), + readers: [], + writers: [], + }); + continue; + } // bpmn:TextAnnotation / bpmn:Association already handled above; bpmn:Group, // bpmn:Category etc. are purely visual and out of scope for the inventory. } @@ -363,6 +412,103 @@ function collectDataAssociations(scopeEl) { } for (const proc of rootProcesses) collectDataAssociations(proc); +// --- data stores: readers/writers from the arrow direction (store -> task = read, task -> +// store = write; no access line), then validity findings. Associations hanging on a subProcess +// element count like those on a task. --- +const flowNodeById = new Map(flowNodes.map((n) => [n.id, n])); +const dataStoreById = new Map(dataStores.map((d) => [d.id, d])); +const addUnique = (arr, id) => { if (!arr.includes(id)) arr.push(id); }; +for (const a of dataObjects) { + if (a.kind === 'dataInputAssociation') { + for (const ref of a.dataRefIds) if (dataStoreById.has(ref)) addUnique(dataStoreById.get(ref).readers, a.activityId); + } else if (a.kind === 'dataOutputAssociation') { + for (const ref of a.dataRefIds) if (dataStoreById.has(ref)) addUnique(dataStoreById.get(ref).writers, a.activityId); + } +} +const storeLabel = (d) => `${d.id}${d.name ? ` ("${d.name}")` : ''}`; +for (const d of dataStores) { + const doc = d.problems.filter((p) => p !== 'invalid-combination'); + if (doc.length) { + pushFinding( + 'store-missing-art-ort', 'warning', + `Data store ${storeLabel(d)}: ${doc.map((p) => `${p.startsWith('missing') ? 'missing' : 'unreadable'} ${p.endsWith('art') ? 'Art:' : 'Ort:'} line`).join(', ')} in its documentation.`, + { elementId: d.id, problems: doc, artRaw: d.artRaw, ortRaw: d.ortRaw } + ); + } + if (d.problems.includes('invalid-combination')) { + pushFinding( + 'store-invalid-art-ort', 'warning', + `Data store ${storeLabel(d)}: Art "${d.art}" does not work with Ort type "${d.ort.type}" (allowed Ort for ${d.art}: ${[...ART_ORT_MATRIX[d.art]].join(', ')}).`, + { elementId: d.id, art: d.art, ort: d.ort, allowedOrt: [...ART_ORT_MATRIX[d.art]] } + ); + } + if (!d.readers.length && !d.writers.length) { + pushFinding( + 'store-without-associations', 'warning', + `Data store ${storeLabel(d)} has no data association at all: no task reads from or writes to it.`, + { elementId: d.id } + ); + } else if (d.art === 'gedaechtnis') { + const serviceWriters = d.writers.filter((id) => flowNodeById.get(id)?.bpmnType === 'bpmn:ServiceTask'); + const missing = []; + if (!serviceWriters.length) missing.push('writer'); + if (!d.readers.length) missing.push('reader'); + if (missing.length) { + pushFinding( + 'memory-store-incomplete', 'warning', + `Memory store ${storeLabel(d)} (Art: gedächtnis) has no ${missing.join(' and no ')}: it needs at least one writing serviceTask and one reader.${missing.includes('writer') && d.writers.length ? ` Its current writer(s) ${d.writers.join(', ')} are not serviceTasks.` : ''}`, + { elementId: d.id, missing, readers: d.readers, writers: d.writers } + ); + } + } +} + +// --- process-wide input/output: a real ioSpecification dataInput/dataOutput on the workflow's +// process, or the data-object convention (input = reference nobody produces that some activity +// reads; output = reference that is produced but nobody reads; both only when the process draws +// at least one data-object read). Only top-level workflow processes count (not sub-processes, +// not processes some callActivity calls). --- +const calledProcessIds = new Set(flowNodes.map((n) => n.calledElement).filter(Boolean)); +const ioScopes = rootProcesses.filter((p) => !calledProcessIds.has(p.id)); +const processIO = { inputs: [], outputs: [] }; +for (const proc of ioScopes) { + const io = proc.ioSpecification; + for (const di of io?.dataInputs || []) { + const readers = []; + for (const a of dataObjects) { + if (a.kind === 'dataInputAssociation' && a.dataRefIds.includes(di.id)) addUnique(readers, a.activityId); + } + processIO.inputs.push({ id: di.id, name: di.name || null, source: 'ioSpecification', scopeId: proc.id, readers }); + } + for (const dout of io?.dataOutputs || []) { + const writers = []; + for (const a of dataObjects) { + if (a.kind === 'dataOutputAssociation' && a.dataRefIds.includes(dout.id)) addUnique(writers, a.activityId); + } + processIO.outputs.push({ id: dout.id, name: dout.name || null, source: 'ioSpecification', scopeId: proc.id, writers }); + } + // "Nobody reads / nobody produces" only says something when the diagram models reads at all; + // a diagram that draws only output associations (reads sit in the task text) has no signal. + const objectRefs = dataObjects.filter((d) => d.kind === 'dataObjectReference' && d.scopeId === proc.id); + const refIds = new Set(objectRefs.map((r) => r.id)); + const readsModelled = dataObjects.some( + (a) => a.kind === 'dataInputAssociation' && a.dataRefIds.some((id) => refIds.has(id)) + ); + for (const ref of readsModelled ? objectRefs : []) { + const readers = []; + const writers = []; + for (const a of dataObjects) { + if (a.kind === 'dataInputAssociation' && a.dataRefIds.includes(ref.id)) addUnique(readers, a.activityId); + if (a.kind === 'dataOutputAssociation' && a.dataRefIds.includes(ref.id)) addUnique(writers, a.activityId); + } + if (!writers.length && readers.length) { + processIO.inputs.push({ id: ref.id, name: ref.name, source: 'convention', scopeId: proc.id, readers }); + } else if (writers.length && !readers.length) { + processIO.outputs.push({ id: ref.id, name: ref.name, source: 'convention', scopeId: proc.id, writers }); + } + } +} + // --- signals per scope (pattern-rubric.md), computed from THIS scope's own flow nodes/ // flows only (nested sub-process scopes get their own signals, computed independently, so // bpmn2agent-design can recurse per callActivity/subProcess as the rubric requires) --- @@ -462,6 +608,8 @@ const inventory = { flowNodes, sequenceFlows, dataObjects, + dataStores, + processIO, textAnnotations, signals: aggregate, findings, diff --git a/.agents/skills/bpmn2agent-design/SKILL.md b/.agents/skills/bpmn2agent-design/SKILL.md index c367355..fed49fa 100644 --- a/.agents/skills/bpmn2agent-design/SKILL.md +++ b/.agents/skills/bpmn2agent-design/SKILL.md @@ -13,11 +13,10 @@ bpmn: # BPMN → Agent Design Input: the spec `bpmn2agent-analyze` drafted and `bpmn2agent-knowledge` enriched; every element is -`kind: unresolved` except constructs analysis flagged as unsupported. Output: a concrete generation -decision per element, an orchestration pattern, and the user's confirmation — written to the spec. -Never edit the `.bpmn` and never patch around a limit of the diagram; that goes back to -`bpmn2agent-analyze`. Ask every question via `AskUserQuestion` with options and a recommendation, -in the user's language (`meta.language`). +`kind: unresolved` except constructs analysis flagged as unsupported. Output, written to the spec: a +generation decision per element, an orchestration pattern, and the user's confirmation. Never edit +the `.bpmn` or patch around a diagram limit; that goes back to `bpmn2agent-analyze`. Ask every +question via `AskUserQuestion` with options and a recommendation, in `meta.language`. ## 1. Read the inputs @@ -54,6 +53,13 @@ Sort the spec's `elements` into: to re-run `bpmn2agent-analyze` once the `.bpmn` is updated. Leave it → set `kind: not-generated` (not `unresolved`) with the same reason text, and set that element's `openQuestions` entry to `answer: "known gap — not generated"`, `answeredAt: `; it needs no further rubric work. +- **Unguarded live write** — a task in `contextSources..writers` of an `art: live` store that + is reachable from a start event without passing a `userTask`. Check on the inventory's + `flowNodes`/`sequenceFlows`: drop every `userTask`; if the writer is still reachable (loops + included), it is unguarded — a diagram defect (`mapping-rubric.md` → "Writing into a live + store"). Tell the user in business words which write lacks a confirmation step, **stop the whole + design pass** and point them at `bpmn-authoring`, then `bpmn2agent-analyze`. Never invent a + checkpoint in the spec. - **Already decided** — concrete `kind` from a previous design pass. Leave as-is unless the element is in the analyze diff's new/changed set. @@ -70,7 +76,7 @@ element: the user's project: `.claude/skills//SKILL.md`, `.claude/skills//scripts/.mjs`, `.claude/agents/.md`, `.claude/hooks/.mjs` (hook registration goes into the one `.claude/settings.json`, which no element claims). Empty for `orchestrator`, `human-checkpoint`, - `not-generated` and `unresolved`. + `context-source`, `workflow-input`, `workflow-output`, `not-generated` and `unresolved`. - `reason` — required for `not-generated`/`unresolved`; it appears verbatim in the mapping report. - `knowledge` — for `skill`/`agent-checklist`/`human-checkpoint`/`hook` only: notebook ids mapped to the element or its lane, else its `knowledge.refs` path, else `"websearch"`/`"model-only"` per @@ -81,8 +87,35 @@ element: - `inputs`/`outputs` — carry forward from analysis; add only artifacts the decision itself introduces (e.g. a sub-workflow's intermediate artifacts). -Don't ask about individual rubric applications. Save every genuinely uncertain call for the step-7 -confirmation. +Data stores and process-wide input/output get `kind: context-source` / `workflow-input` / +`workflow-output` per the rubric's table (a convention input/output data object takes the +`workflow-*` kind, not `artifact-contract`); their content is step 3a. + +Don't ask about individual rubric applications; save every genuinely uncertain call for step 7. + +## 3a. Resolve context sources + +For every `contextSources.` (valid Art × Ort pairs: `mapping-rubric.md` → "Data stores → context +sources"; analyze settled invalid ones, ask again only if one slipped through): + +- **`art: live` → `tools: {read, write}`**, per `Ort` type, classified read/write by the rubric's + "Live stores: tools and placement" heuristic (don't restate it in the spec): + - `mcp:` — ToolSearch with query `mcp____` (raise `max_results` to cover the + server). Nothing found or server not connected → `tools: unresolved`. Keep only tools the + store's readers/writers plausibly need (from their label and documentation); `write` stays + empty when the store has no writers. + - `cli:` — `command -v ` and its `--help` for the subcommands (run nothing else); + patterns `Bash( :*)`. Not installed → `unresolved`. + - `notebook:` — treat as `mcp:gemini-notebook-mcp`: `read: [mcp__gemini-notebook-mcp__notebook_query]`; + write tools only when the store has writers. + - URL → `read: [WebFetch]`; `datei:` → `read: [Read]` (plus `Edit`/`Write` under `write` if the + store has writers). +- **`art: gedaechtnis` → `memory: {path, maxLines: 150}`.** `path` is `ort.ref`; without one, + `.claude/memory//.md`. No `tools`. +- **`art: wissen`**: nothing to resolve (knowledge already extracted); no `tools`/`memory`. + +Each store's and each workflow-I/O element's `elements.` entry keeps its `kind` and has no +`generatedPaths`. ## 4. Apply the pattern rubric @@ -91,6 +124,10 @@ Load `references/pattern-rubric.md` and walk its decision table with step 1's to checkpoint's answer toward `judgementBranchCount` (recompute before consulting the table). Repeat per `scopes[].signals` for every sub-workflow; each gets its own pattern under the same `elements` map. +Add the two context-store signals the inventory doesn't carry, `liveWriteCount` per phase (writers of +`art: live` stores) and `roleToolSpread` (from step 3a's tools per lane), and apply the rubric's +context-store rules. Step 2 already ensured every live write has its `userTask`. + Write `pattern.rationale` in the business phrasing of the rubric's "Explaining the choice" section. Add an `alternativesConsidered` entry when the signals were close to another row. @@ -107,21 +144,37 @@ ask: - a hook needs a tool matcher, or a lane's skills touch production artifacts or external APIs → propose a `tools:` allowlist. +Place the live-store tools from step 3a (placement rule: `mapping-rubric.md` → "Live stores: tools +and placement"): + +- **`orchestrator-agent`** (also an orchestrator-agent phase of `mixed`): each lane agent gets + `roles..tools` with the read tools of the stores its tasks read, the write tools of the + stores its tasks write, and the built-ins its skills need (`Read`, `Grep`, `Glob`, `Write` for its + artifacts and memory file). Nothing for stores it doesn't touch. `unresolved` stores add nothing. +- **Any other pattern**: the union of all read tools goes into `.claude/settings.json` → + `permissions.allow`; write tools never do (the user is asked at the call, backed by the + PreToolUse hook generate emits). Say in the plan: "Lesezugriff nicht pro Rolle getrennt". + +No spec field holds the `permissions.allow` list; generate derives it from +`contextSources.*.tools.read`. + Apply agent-authoring's "Should this be an agent at all?" and split rule to every lane. A lane that -should become two agents or a skill is a question for step 7 (the lane belongs to the diagram), not -a split you make; when the user agrees, record the reason in the spec. +should become two agents or a skill is a step-7 question (the lane belongs to the diagram), not a +split you make; when the user agrees, record the reason in the spec. ## 6. Define artifact contracts For each `artifacts.` (from analysis plus step 3's additions), fill `pathPattern` and `frontmatter`. Artifacts crossing a gate or loop get `version` and `status` frontmatter. Add no -field that no producer or consumer reads. +field that no producer or consumer reads. The artifact behind `workflowIO.input` carries a +frontmatter field (`required: true`) for every name in `workflowIO.input.required`; the one behind +`workflowIO.output` is the contract for the end result. ## 6a. Check the orchestration against the knowledge base 1. Write the draft decisions of steps 2–6 to `generated//workflow-spec.draft.yaml` (same schema; the confirmed spec stays untouched until step 8; delete the draft whenever design stops - before step 8, since verify flags it as untraced) and draft the step-7 plan text. + before step 8, as verify flags it untraced) and draft the step-7 plan text. 2. Delegate the review to the `agentic-workflow-architect` agent with absolute paths: the draft spec, the `.bpmn`, `${CLAUDE_SKILL_DIR}/references/pattern-rubric.md` and `${CLAUDE_SKILL_DIR}/references/mapping-rubric.md`, plus the draft plan text. If the agent fails @@ -134,10 +187,10 @@ field that no producer or consumer reads. diagram's structure in the spec. - `question` → add to `openQuestions` (`answer: null`) and raise in step 7. 4. Agentic-design questions the rubrics leave open (including the `source: agentic-design` entries - `bpmn2agent-knowledge` queued in `openQuestions`): answer from `${CLAUDE_SKILL_DIR}/../agentic-workflow-kb/` (FAQ, then - references); only if it has no answer, ask the `agentic-kb-librarian` agent. Quote the citation - tag when a recommendation rests on it. Write each answer into the entry's `answer`/`answeredAt`; - one neither answers goes to step 7 as a question. + `bpmn2agent-knowledge` queued in `openQuestions`): answer from + `${CLAUDE_SKILL_DIR}/../agentic-workflow-kb/` (FAQ, then references); only if it has no answer, + ask the `agentic-kb-librarian` agent. Quote the citation tag when a recommendation rests on it. + Write each answer into the entry's `answer`/`answeredAt`; one neither answers goes to step 7. ## 7. Write the mapping plan and confirm @@ -154,9 +207,23 @@ lane/role, then element, e.g.: > Insgesamt baue ich das als eine Abfolge von Checklisten, die an deinem Prüfpunkt pausiert > (siehe Begründung oben) — kein separates Steuerprogramm nötig. -Include the pattern and rationale ("how it runs"), any model/tools proposals from step 5, open -questions from 6a, and every `not-generated`/`unresolved` element with its reason. Fold every -pending pattern or model/tools question into this one confirmation. Offer at least: +Add a part "Kontextquellen" with one entry per store, for the user to confirm: name verbatim, Art and +Ort in plain words, who reads and who writes it (by task name), and the access split in business +language, e.g.: + +> **"Jira-Tickets Projekt LANE"** (live, Atlassian) — "Ticket analysieren" liest, "Kommentar im Ticket +> posten" schreibt (erst nach deiner Freigabe in "Antwort freigeben", und Claude Code fragt beim +> Schreiben zusätzlich nach). Lesezugriff nicht pro Rolle getrennt. +> *Technisch — lesen: `mcp__atlassian__getJiraIssue`; schreiben: `mcp__atlassian__addCommentToJiraIssue`.* + +Gedächtnis: file path and the 150-line cap. `unresolved` tools: say the server wasn't reachable and +the step stays without tool access until it is. Tool names appear only in the italic technical +line. Also name the process input with its required fields, and the output. Where `roleToolSpread` +holds and the pattern isn't `orchestrator-agent`, recommend it here. + +Include the pattern and rationale ("how it runs"), step 5's model/tools proposals, 6a's open +questions, and every `not-generated`/`unresolved` element with its reason. Fold every pending +question into this one confirmation. Offer at least: - **Accept as proposed** — recommend when nothing is `unresolved` and no step-3 call was uncertain. - **Adjust specific elements** — collect which and how, redo steps 3–6 (and the draft) for those, @@ -168,8 +235,9 @@ pending pattern or model/tools question into this one confirmation. Offer at lea ## 8. Write the confirmed spec Only after confirmation, write the decisions into `generated//workflow-spec.yaml`: every -element's `kind`/`generatedPaths`/`reason`/`knowledge`/`gate`, `roles.*.modelTier`/`tools` where set, -`artifacts.*`, `pattern`, `openQuestions`; update `meta.updated`; delete +element's `kind`/`generatedPaths`/`reason`/`knowledge`/`gate` (including the store and workflow-I/O +entries), `roles.*.modelTier`/`tools` where set, `artifacts.*`, `contextSources.*.tools`/`memory` +and `workflowIO` as confirmed, `pattern`, `openQuestions`; update `meta.updated`; delete `workflow-spec.draft.yaml`. Validate: run `node ${CLAUDE_SKILL_DIR}/../bpmn2agent-verify/scripts/verify.mjs "$cacheDir" generated/` and read only its spec-schema section (other categories may fail this early). Fix until it passes. @@ -190,12 +258,12 @@ before writing. ## Reference files -- `references/mapping-rubric.md` — element → `kind` table, sub-decisions, v1 supported constructs, - mapping-view colours. -- `references/pattern-rubric.md` — signals, pattern table, per-pattern generation/checkpoint/loop - cap/resume detail, business phrasing. +- `references/mapping-rubric.md` — element → `kind`, sub-decisions, context sources, v1 constructs, + mapping-view colours (steps 3, 3a, 5). +- `references/pattern-rubric.md` — signals, pattern table, per-pattern detail, business phrasing + (steps 3, 4). - `assets/workflow-spec.schema.yaml` — the schema for step 8. - `${CLAUDE_SKILL_DIR}/../agent-authoring/SKILL.md` — naming and split rule (step 5). - `${CLAUDE_SKILL_DIR}/../orchestration-design/SKILL.md` — inline fallback review (step 6a). -- `${CLAUDE_SKILL_DIR}/../agentic-workflow-kb/` — cited answers to design questions. +- `${CLAUDE_SKILL_DIR}/../agentic-workflow-kb/` — cited answers to design questions (step 6a). - Agents `agentic-workflow-architect`, `agentic-kb-librarian` — step 6a. diff --git a/.agents/skills/bpmn2agent-design/assets/workflow-spec.schema.yaml b/.agents/skills/bpmn2agent-design/assets/workflow-spec.schema.yaml index 3ff15dc..081dd64 100644 --- a/.agents/skills/bpmn2agent-design/assets/workflow-spec.schema.yaml +++ b/.agents/skills/bpmn2agent-design/assets/workflow-spec.schema.yaml @@ -27,6 +27,14 @@ # sdlc:collection -> elements..collection # sdlc:bundle -> artifacts..bundledIn (informational, not a separate section) # +# Beyond the sdlc: vocabulary, two optional top-level sections carry context the diagram draws as +# data stores and process-wide input/output (plan: docs/plans/kontextquellen/plan.md): +# +# dataStoreReference -> contextSources. (+ elements..kind: context-source) +# ioSpecification dataInput/dataOutput (or the "no producer" / "no reader" data-object +# convention) -> workflowIO.{input,output} +# (+ elements..kind: workflow-input | workflow-output) +# $schema: "http://json-schema.org/draft-07/schema#" $id: "https://ai-sdlc.internal/schemas/bpmn2agent/workflow-spec.schema.yaml" title: workflow-spec @@ -63,6 +71,18 @@ properties: type: object additionalProperties: $ref: "#/definitions/artifact" + contextSources: + description: > + One entry per durable data store the diagram draws (bpmn:dataStoreReference): knowledge to + ground steps (wissen), a live system read/written at run time (live), or a curated memory file + carried across runs (gedaechtnis). Map key is a kebab-case store id. Deliberately separate from + `artifacts`: artifacts live for one run, stores outlive it and have different fields. Absent in + specs of diagrams without data stores. + type: object + additionalProperties: + $ref: "#/definitions/contextSource" + workflowIO: + $ref: "#/definitions/workflowIO" pattern: $ref: "#/definitions/pattern" knowledge: @@ -155,7 +175,7 @@ definitions: description: > BPMN element type, e.g. task/userTask/manualTask/serviceTask/scriptTask/businessRuleTask/ callActivity/subProcess/exclusiveGateway/parallelGateway/inclusiveGateway/startEvent/ - endEvent/boundaryEvent/dataObjectReference/lane. See mapping-rubric.md for the v1 + endEvent/boundaryEvent/dataObjectReference/dataStoreReference/dataInput/dataOutput/lane. See mapping-rubric.md for the v1 supported/unsupported construct list. label: type: string @@ -172,9 +192,16 @@ definitions: - orchestrator - human-checkpoint - artifact-contract + - context-source + - workflow-input + - workflow-output - not-generated - unresolved - description: Generation decision for this element. See mapping-rubric.md for how each is chosen. + description: > + Generation decision for this element. See mapping-rubric.md for how each is chosen. + context-source = a dataStoreReference (its details live in `contextSources.`); + workflow-input / workflow-output = the process-wide input/output (details in `workflowIO`). + None of the three has a generated file of its own. generatedPaths: type: array items: { type: string } @@ -300,6 +327,133 @@ definitions: items: { type: string } description: Ids of higher-level bundle artifacts this one rolls up into (sdlc:bundle equivalent), if any. + contextSource: + type: object + required: [name, bpmnElement, art, ort] + additionalProperties: false + properties: + name: + type: string + description: Verbatim data store label from the BPMN (a business data set such as "Jira-Tickets Projekt LANE", not a system). + bpmnElement: + type: string + description: id of the bpmn:dataStoreReference; also the key into `elements` (kind context-source). + art: + enum: [wissen, live, gedaechtnis] + description: > + wissen = reference knowledge loaded at generation time into knowledge/.md; live = a + system read (and maybe written) at run time through tools; gedaechtnis = a curated memory + file read at the start and written at the end of a run. Valid Art x Ort combinations are + in mapping-rubric.md. + ort: + type: object + required: [type] + additionalProperties: false + properties: + type: + enum: [notebook, mcp, cli, url, datei, websearch] + ref: + type: string + description: > + notebook title, MCP server name, CLI command, URL or file path, as written after the + prefix of the diagram's "Ort:" line. Omitted for websearch. + readers: + type: array + items: { type: string } + description: Element ids of tasks with a data input association from this store (store -> task = read). + writers: + type: array + items: { type: string } + description: Element ids of tasks with a data output association to this store (task -> store = write). + tools: + description: > + Only for art: live. Tools the readers/writers use, resolved by bpmn2agent-design (MCP tool + names, Bash( :*) patterns, WebFetch, Read) and split by read vs. write; unclear + counts as write. The literal "unresolved" means the source could not be resolved (e.g. MCP + server not connected): verify warns, generate emits no tools for it (never a wildcard). + oneOf: + - type: object + additionalProperties: false + properties: + read: + type: array + items: { type: string } + write: + type: array + items: { type: string } + - const: unresolved + memory: + type: object + required: [path] + additionalProperties: false + description: "Only for art: gedaechtnis." + properties: + path: + type: string + description: > + Memory file relative to the run's cwd; default .claude/memory//.md, + overridden by the diagram's "Ort: datei:". + maxLines: + type: integer + minimum: 1 + default: 150 + description: Line cap enforced by a PostToolUse hook (exit 2 asks Claude to condense). + allOf: + - if: + properties: { art: { const: wissen } } + then: + properties: + ort: + properties: + type: { enum: [notebook, mcp, url, datei, websearch] } + tools: false + memory: false + - if: + properties: { art: { const: live } } + then: + properties: + ort: + properties: + type: { enum: [notebook, mcp, cli, url, datei] } + memory: false + - if: + properties: { art: { const: gedaechtnis } } + then: + properties: + ort: + properties: + type: { const: datei } + tools: false + + workflowIO: + type: object + additionalProperties: false + description: > + Process-wide input and output: a real ioSpecification dataInput/dataOutput, or by convention a + data object nobody produces (input) / nobody reads (output). Both become artifact contracts; + the orchestrator derives its argument-hint from the input's required fields. + properties: + input: + type: object + required: [artifact] + additionalProperties: false + properties: + artifact: + type: string + description: Artifact type id; key into the top-level `artifacts` map. + required: + type: array + items: { type: string } + description: Frontmatter fields the orchestrator must have before starting; it asks when one is missing. + output: + type: object + required: [artifact] + additionalProperties: false + properties: + artifact: + type: string + description: Artifact type id; the contract for the final result. + pattern: type: object required: [chosen, rationale] diff --git a/.agents/skills/bpmn2agent-design/references/mapping-rubric.md b/.agents/skills/bpmn2agent-design/references/mapping-rubric.md index c2f4e5d..8300350 100644 --- a/.agents/skills/bpmn2agent-design/references/mapping-rubric.md +++ b/.agents/skills/bpmn2agent-design/references/mapping-rubric.md @@ -1,16 +1,17 @@ # Mapping rubric: BPMN element → generated artifact -How `bpmn2agent-design` turns each element of the analyzed BPMN inventory into a `kind` in -`workflow-spec.yaml` (see `../assets/workflow-spec.schema.yaml`). Apply this per element, record the -result under `elements.`, and list every `not-generated`/`unresolved` decision in the step-7 -mapping plan. +How `bpmn2agent-design` turns each analyzed BPMN element into a `kind` in `workflow-spec.yaml` +(schema: `../assets/workflow-spec.schema.yaml`). Record the result under `elements.`; list every +`not-generated`/`unresolved` decision in the step-7 mapping plan. - [The rubric](#the-rubric) - [serviceTask → skill vs. agent-checklist item](#servicetask--skill-vs-agent-checklist-item) - [businessRuleTask / gateway condition → hook vs. script](#businessruletask--gateway-condition--hook-vs-script) - [callActivity / subProcess → reusable skill vs. sub-workflow](#callactivity--collapsed-subprocess--reusable-skill-vs-sub-workflow) +- [Data stores → context sources](#data-stores--context-sources) - [v1 supported / unsupported constructs](#v1-supported--unsupported-constructs) - [Legend: mapping view colours](#legend-mapping-view-colours) +- [Language rule](#language-rule) - [Worked mini example](#worked-mini-example) ## The rubric @@ -20,13 +21,17 @@ mapping plan. | Lane | Role (`roles.`) | A subagent file only under `orchestrator-agent` (or a mixed phase using it); otherwise the lane's steps live in the chain skill and lane skill. `agentName` per agent-authoring's naming rule (`{domain}-{role}`, kebab-case, no "-expert"). The lane's elements become checklist items and/or skills. | | `userTask` / `manualTask` | Human checkpoint (`kind: human-checkpoint`) | An `AskUserQuestion` call — options + a recommendation, never a bare prompt — as a step in the owning skill/agent. No file of its own. | | `serviceTask` | Skill, or an agent checklist item — see decision below | | -| `scriptTask` | Script inside a skill, `kind: script` | Deterministic, no LLM call. Lives under the skill **of its lane** — `generated//.claude/skills//scripts/.mjs`, `` = that lane's `roles..agentName` (generate's "lane skill", step 3) — unless shared across lanes (then its own skill, like a reusable `serviceTask`). `.claude/skills//` (the workflow name) is **reserved** for the skill-chain top-level skill; never use it for a lane's scripts, even if the lane has the same name. | +| `scriptTask` | Script inside a skill, `kind: script` | Deterministic, no LLM call. Lives under the skill **of its lane** — `generated//.claude/skills//scripts/.mjs`, `` = that lane's `roles..agentName` (generate's "lane skill", step 3) — unless shared across lanes (then its own skill, like a reusable `serviceTask`). `.claude/skills//` is **reserved** for the skill-chain top-level skill; never put a lane's scripts there, even if the lane has the workflow's name. | | `businessRuleTask` / gateway condition | Hook or script — see decision below | | -| Gateway (any type), loop back-edge, multi-instance marker | Orchestrator logic (`kind: orchestrator`) | No file of its own; control flow inside the pattern `pattern-rubric.md` selects (branch in a Workflow script, delegation logic in an orchestrator agent, or a hook's block/allow decision). Still record it in `elements.`. | +| Gateway (any type), loop back-edge, multi-instance marker | Orchestrator logic (`kind: orchestrator`) | No file of its own; control flow inside the pattern `pattern-rubric.md` selects (Workflow-script branch, orchestrator-agent delegation, or a hook's block/allow decision). Still record it in `elements.`. | | `startEvent` / `endEvent` | `kind: orchestrator` if it does real routing work (e.g. a start event with a form, an end event a loop can short-circuit to); otherwise `kind: not-generated` with `reason: "Start/end event — structural marker only, no generated artifact."` | Never a file of its own. Don't leave these unmapped by omission. | | Intermediate throw/catch event (link, none/signal used as a plain marker) | `kind: orchestrator`, same as a gateway | Timer/message intermediate events are **unsupported in v1** (see below). | | `callActivity` / collapsed `subProcess` | Reusable skill, or a sub-workflow — see decision below | A skill-backed one folds its inner flow nodes into the same skill. | | Data object / data object reference | Artifact contract (`artifacts.`, `kind: artifact-contract` on the producing element) | Path pattern + frontmatter fields, no generated file (see the schema's `artifact` definition). `producer` is one element id, or an array when the diagram has more than one real writer (`dataOutputAssociation`). | +| Data store reference, `Art: wissen` | `contextSources.`, `kind: context-source` | Loaded at generation time into `knowledge/.md` for each reading task, then the task skill's `references/`. No file of its own. | +| Data store reference, `Art: live` | `contextSources.`, `kind: context-source` | Read/written at run time: resolved tool lists (see below), a `## Kontextquellen` section in each reading task's skill, and for writes a PreToolUse `ask` hook. | +| Data store reference, `Art: gedächtnis` | `contextSources.`, `kind: context-source`, `memory` | A curated memory file plus a line-cap hook. Needs at least one writing `serviceTask` and one reader. | +| Process-wide `dataInput` / `dataOutput`, or a data object nobody produces / nobody reads | `workflowIO.input` / `.output`, `kind: workflow-input` / `workflow-output` on the element | Both also get an `artifacts.` contract. The input's required fields become the orchestrator's `argument-hint`; the output is the contract for the end result. | ### `serviceTask` → skill vs. agent-checklist item @@ -46,8 +51,8 @@ reference material. When unsure, default to agent-checklist item. ### `businessRuleTask` / gateway condition → hook vs. script Generate a **Claude Code hook** only when the check must physically gate a *tool call* the agent is -about to make or has just made — enforcement inside Claude Code's tool-execution loop, not a step the -agent chooses to run: +about to make or has just made (enforcement in the tool-execution loop, not a step the agent chooses +to run): | Event | Fires | Fits when the BPMN rule... | |---|---|---| @@ -99,6 +104,47 @@ artifacts: `bpmn2agent-verify` and `render-mapping.mjs` handle exactly this representation; don't invent another (e.g. `not-generated` for every inner step, or a file per inner element). +## Data stores → context sources + +A `dataStoreReference` is one business data set (e.g. "Jira-Tickets Projekt LANE"), not a system. +Its documentation carries `Art:` and `Ort:`; reading or writing comes only from the arrow +direction (store → task = read, task → store = write). A missing line or a combination not in the +matrix is a question for the business user, never a guess. + +| Art | Allowed `Ort:` | Loaded | Becomes | +|---|---|---|---| +| `wissen` | `notebook:`, URL, `datei:`, `websearch` | at generation | `knowledge/.md` → skill `references/` | +| `wissen` | `mcp:` | at generation (snapshot), only if the server is connected | same | +| `live` | `mcp:`, `cli:` | at run time | tool allowlist + `## Kontextquellen` in the skill | +| `live` | `notebook:` | at run time, treated as `mcp:gemini-notebook-mcp` | same | +| `live` | URL | at run time | `WebFetch` | +| `live` | `datei:` | at run time | `Read` | +| `gedächtnis` | `datei:` (default `.claude/memory//.md`) | read at run start, written at the end | curated file + line-cap hook | + +Anything else (e.g. `live` + `websearch`, `gedächtnis` + `mcp:`) is invalid. + +### Live stores: tools and placement + +- **Resolve the tools.** For `mcp:`, fetch the server's tool list with ToolSearch (`mcp____`) + and sort by name: `get/list/search/read/fetch/view` read; + `create/update/delete/add/post/edit/transition/push` write. A server's `readOnlyHint` wins over the + name. **Unclear counts as write.** `cli:` becomes `Bash( :*)` patterns, same heuristic + on the subcommands. The user confirms the list in the mapping plan. Server not connected → + `tools: unresolved` (verify warns, generate emits no tools, never a wildcard). +- **Place them.** Where lanes are agents (`orchestrator-agent`), the needed read tools go into that + lane agent's `tools:`, least privilege per role (a lane that writes also gets that store's write + tools; they stay behind the `userTask` and the hook). Otherwise the union of all read tools goes + into `.claude/settings.json` → `permissions.allow` (write tools never) and the plan says openly + "Lesezugriff nicht pro Rolle getrennt". **If the roles need different privileges, that is a signal + for `orchestrator-agent`** (see `pattern-rubric.md`). + +### Writing into a live store + +A write needs a `userTask` before it on every path; verify errors otherwise. A phase that writes into +a live store is **never `workflow-script`**: that pattern has no human checkpoints. A PreToolUse hook +on the write tools (`permissionDecision: "ask"`) backs the `userTask` up. Writes to a `gedächtnis` +store need no approval. + ## v1 supported / unsupported constructs Supported: @@ -111,6 +157,7 @@ Supported: - `callActivity` - Multi-instance markers - Data objects / data object references +- Data store references (`Art: wissen | live | gedächtnis`) and process-wide data input/output - Start / end events - Error boundary events @@ -128,9 +175,9 @@ a role, not an external system"). ## Legend: mapping view colours Used in `mapping/workflow-mapped.bpmn` (bpmn.io colour extension), `mapping/index.html`'s legend and -`mapping/report.md`. One colour per `kind`; lanes are not tinted. Every annotation also starts with -the kind or status name, so colour is never the only signal (exact values: the header comment of -`bpmn2agent-generate/scripts/render-mapping.mjs`). +`mapping/report.md`. One colour per `kind`; lanes are not tinted. Every annotation starts with the +kind or status name, so colour is never the only signal. Exact values: the header comment of +`bpmn2agent-generate/scripts/render-mapping.mjs`. | Colour | Kind | Meaning | |---|---|---| @@ -141,9 +188,19 @@ the kind or status name, so colour is never the only signal (exact values: the h | Teal | `orchestrator` | Gateways, loop back-edges, multi-instance markers — no file of its own. | | Amber | `human-checkpoint` | `userTask`/`manualTask`: a person decides here. | | Brown | `artifact-contract` | A data object with a path/frontmatter contract. | +| Rose | `context-source`, `workflow-input`, `workflow-output` | A data store (knowledge, live system, memory) or the process-wide input/output; no file of its own. A greener shade than the `orchestrator` teal, so the two stay apart; the annotation names the Art. | | **Grey** | `not-generated` | Deliberately not generated; the element's `reason` is shown in its annotation and on click. Not an error. | | **Red** | `unresolved` | Unmapped or still an open question; blocks `bpmn2agent-verify`'s "no red in the map" check. | +## Language rule + +- BPMN labels (lanes, tasks, gateways, flows, events, data objects, stores) stay verbatim in the + diagram's language (`meta.language`) wherever they appear: skills, agents, scripts, reports. +- Everything else in generated skills, agents, scripts, hooks and `mapping/report.md` is English. +- Exceptions: the generated `README.md` is in `meta.language`; the fixed German tokens stay German + (`## Kontextquellen`, "nur nach Freigabe", "Lesezugriff nicht pro Rolle getrennt", the memory + sections *Bewährt*, *Vermeiden*, *Offene Muster*, and the `Art:`/`Ort:` values). + ## Worked mini example A three-step approval flow: lane **Reviewer**, one `userTask` "Antrag genehmigen" (approve request), diff --git a/.agents/skills/bpmn2agent-design/references/pattern-rubric.md b/.agents/skills/bpmn2agent-design/references/pattern-rubric.md index d291f72..596f3ce 100644 --- a/.agents/skills/bpmn2agent-design/references/pattern-rubric.md +++ b/.agents/skills/bpmn2agent-design/references/pattern-rubric.md @@ -1,10 +1,10 @@ # Pattern rubric: choosing the orchestration pattern -How `bpmn2agent-design` picks `pattern.chosen` in `workflow-spec.yaml` from the shape of the analyzed -diagram, and how it explains that choice to the business user who drew it. The choice is made once -per workflow (and, recursively, once per `callActivity`/`subProcess` that `mapping-rubric.md` decided -is a sub-workflow rather than a plain skill) — always propose it, never silently apply it; confirm via -`AskUserQuestion` with the rationale and the alternatives considered, both recorded in the spec. +How `bpmn2agent-design` picks `pattern.chosen` in `workflow-spec.yaml` from the diagram's shape and +explains it to the business user who drew it. Choose once per workflow and once per +`callActivity`/`subProcess` that `mapping-rubric.md` made a sub-workflow. Always propose, never +silently apply: confirm via `AskUserQuestion` with the rationale and the alternatives considered, +both recorded in the spec. Every pattern caps its loops at `elements..gate.maxLoops`. **Default: 3**, unless the BPMN or interview gives another number; never leave it unset. At the cap the gate stops sending work back @@ -32,6 +32,8 @@ Count these over the diagram (or sub-diagram) being decided: | `loopCount` | Back-edges (loop merge → split pairs per `bpmn-authoring/references/modelling-rules.md`) | | `judgementBranchCount` | `exclusiveGateway`s whose outgoing conditions require an LLM to weigh a case (e.g. "is this good enough?", "which of these applies?") rather than a deterministic threshold a script can evaluate | | `attended` | `humanTaskCount > 0` — a human must be present at least once while it runs | +| `liveWriteCount` | Tasks in the phase that write into a `live` context store (`contextSources.*.writers` of `art: live`). Writes into `gedaechtnis` stores don't count. | +| `roleToolSpread` | Two or more lanes need different live-store tools (a lane reads a store another doesn't need, or only some lanes write). Needs `contextSources.*.tools` resolved first (`SKILL.md` step 3a). | ## Decision table @@ -45,6 +47,20 @@ Count these over the diagram (or sub-diagram) being decided: If two rows plausibly apply, prefer the earliest that fits. Choose `mixed` only when phases are *structurally* different, not because the diagram is large. +Two context-store rules apply on top of the table: + +- **`liveWriteCount > 0` rules out `workflow-script` for that phase.** That pattern has no human + checkpoints, and a live write needs a `userTask` before it on every path (`mapping-rubric.md`'s + write-guard rule). Take the next fitting row, or `mixed` with the writing phase as + `skill-chain-hooks`/`orchestrator-agent`. A live write with no `userTask` before it is a diagram + defect, not a pattern question: route to `bpmn2agent-analyze` (change the `.bpmn`), never invent a + checkpoint in the spec. +- **`roleToolSpread` is a signal for `orchestrator-agent`.** It is the only pattern where each role + is its own agent with its own `tools:`. If the table picks another row, keep that row, say in the + mapping plan that read access is not separated per role ("Lesezugriff nicht pro Rolle getrennt"; + the tools go into `permissions.allow`) and offer `orchestrator-agent` as the option that + separates them; record it under `alternativesConsidered`. + ## Per-pattern detail ### Skill chain + hooks @@ -67,16 +83,16 @@ If two rows plausibly apply, prefer the earliest that fits. Choose `mixed` only ### Claude Code Workflow script - **When**: fully unattended, real parallel/multi-instance structure, deterministic branching. -- **When not**: any human checkpoint inside the flow, or branching that needs judgment a script - condition can't express. +- **When not**: any human checkpoint inside the flow, branching that needs judgment a script + condition can't express, or any write into a live context store (`liveWriteCount > 0`). - **Sanity check**: the three-of-five rule in `${CLAUDE_SKILL_DIR}/../agentic-workflow-kb/references/claude-code-workflows.md`. Workflow `agent()` subagents can't ask the user, so any attended step disqualifies that phase. - **Generated**: one Workflow script, `.claude/workflows/.workflow.mjs`: `agent()` for LLM steps, `parallel()`/`pipeline()` for the fan-out of gateways/multi-instance markers, plain JS for - deterministic gateway conditions. **The script never runs automatically — neither the generator - nor anything else invokes it; the generated `README.md` says the operator starts it deliberately, - on demand.** + deterministic gateway conditions. **The script never runs automatically — nothing, the generator + included, invokes it; the generated `README.md` says the operator starts it deliberately, on + demand.** - **Human checkpoints**: none inside the script; if the diagram has any, choose `mixed`. - **Loop caps**: a plain counter checked each iteration (`while (loopCount < maxLoops)`), matching `elements..gate.maxLoops`. @@ -105,6 +121,8 @@ If two rows plausibly apply, prefer the earliest that fits. Choose `mixed` only phase (Workflow script) feeding a phase that needs a human sign-off between two skills (skill chain), or a deterministic phase followed by a judgment-heavy one (orchestrator agent). - **When not**: the whole diagram fits one pattern; re-check the decision table per phase first. +- **Live writes**: a phase with a live-store write is never the Workflow-script phase; its + `userTask` and the write sit in the same phase. - **Generated**: each phase per its own pattern; the phase boundary is the hand-off point (typically a human checkpoint or an orchestrator decision that starts the next phase). Record the split in `pattern.phases[]` (`[{name, pattern, elements: [...]}]`, one entry per phase in diagram order); @@ -130,3 +148,8 @@ Tie the explanation to the shape they drew, not to the pattern's name. Examples: - **Mixed**: "The first part of your diagram runs entirely on its own; the second part needs your sign-off partway through — I'll build the first part as an automated script and the second as a guided checklist, with a clear handoff between them." +- **Live write forces a human step**: "Your diagram posts into Jira, so I'll build this so that you + confirm first and nothing is written automatically — that rules out the fully automatic script." +- **Different access per role**: "The roles don't need the same access to your systems. As a guided + checklist they all share one set of permissions; a coordinating agent could give each role only + what it needs — I'd recommend that if the separation matters to you." diff --git a/.agents/skills/bpmn2agent-generate/SKILL.md b/.agents/skills/bpmn2agent-generate/SKILL.md index 8f5fe6e..5ef0571 100644 --- a/.agents/skills/bpmn2agent-generate/SKILL.md +++ b/.agents/skills/bpmn2agent-generate/SKILL.md @@ -15,15 +15,15 @@ bpmn: # BPMN → Agent Generation -Input: the **confirmed** `generated//workflow-spec.yaml`. This skill only materializes -what the spec decided; it never makes a mapping or pattern call (that is `bpmn2agent-design`'s job). -It writes only inside `generated//` — never into a real `.agents/` or `.claude/`; the user -copies `generated//.claude/` themselves, using the command the generated `README.md` gives. -Ask every question via `AskUserQuestion` with options + a recommendation, in the user's language. +Input: the **confirmed** `generated//workflow-spec.yaml`. Materialize only what the spec +decided; never make a mapping or pattern call (that is `bpmn2agent-design`'s job). Write only inside +`generated//`, never into a real `.agents/` or `.claude/`; the user copies +`generated//.claude/` with the command the generated `README.md` gives. Ask every question +via `AskUserQuestion` with options + a recommendation, in the user's language. -For a large roster, per-file content may be written by parallel `Agent` calls, each given the spec -path, its `generatedPaths` entry and its template; README, report and the orchestration file stay in -the main thread. +For a large roster, parallel `Agent` calls may write per-file content, each given the spec path, its +`generatedPaths` entry and its template; README, report and the orchestration file stay in the main +thread. ## Output layout @@ -34,18 +34,19 @@ generated// .claude/ ← the whole installable payload, copied as-is agents/.md skills//SKILL.md (+ references/, assets/, scripts/ only where design put a script) - hooks/.mjs (only for kind: hook) + hooks/.mjs (kind: hook, plus -write-guard / -memory-cap, step 6a) workflows/.workflow.mjs (workflow-script pattern only) - settings.json (only when there are hooks: registers every hook) + settings.json (only with hooks or read-tool permissions: step 6) + (memory files are not generated: they appear in the user's project at run time) README.md ← install + what was generated (meta.language) workflow-spec.yaml knowledge/ mapping/ ← review material, never copied ``` -Address files as they will be after the copy: `"$CLAUDE_PROJECT_DIR"/.claude/hooks/.mjs` in +Address files by their post-copy path: `"$CLAUDE_PROJECT_DIR"/.claude/hooks/.mjs` in `settings.json`, `${CLAUDE_SKILL_DIR}/scripts/.mjs` inside a `SKILL.md`. Never `${CLAUDE_PLUGIN_ROOT}`, never a path into `generated/`. -A spec without `outputLayout` is on the **legacy** layout (the dark-factory example): same steps, but +A spec without `outputLayout` uses the **legacy** layout (the dark-factory example): same steps, but `skills/`, `agents/` and `.workflow.mjs` sit directly under `generated//`, and each hook gets a `.hook.settings.json` snippet (from the settings template's `hooks` block) next to `.hook.mjs` instead of the shared `settings.json`. Don't convert a legacy spec without @@ -61,15 +62,15 @@ asking. and read only its spec-schema section (other categories may fail this early). Schema errors → **stop**; design didn't finish its step 8. - `pattern.chosen` unset → **stop**; design hasn't confirmed a pattern. -- Individual elements still at `kind: unresolved` (deferred by the user) are allowed: generate - everything else and let them show red in the mapping report. `bpmn2agent-verify` gates acceptance. +- Single elements still at `kind: unresolved` (deferred by the user) are allowed: generate everything + else and let them show red in the mapping report. `bpmn2agent-verify` gates acceptance. ## 2. The frontmatter/header convention (used by every step below) Every file written carries `bpmn: {file: , elements: [, ...]}` — the ids it was generated from (several for a file spanning several elements): -- **`SKILL.md`, agent `.md`**: `bpmn:` as another top-level frontmatter key next to `name`/`description`: +- **`SKILL.md`, agent `.md`**: `bpmn:` as a top-level frontmatter key next to `name`/`description`: ```yaml bpmn: file: docs/planning/approval-flow.bpmn @@ -83,9 +84,10 @@ Every file written carries `bpmn: {file: , elem ``` - **`.claude/settings.json`**: no header (JSON, copied into the user's project); traceability runs through the hook scripts it registers. +- **Context hooks** (step 6a): `elements` = the `StoreRef_…` ids of the stores they guard. -Quote BPMN labels **verbatim** wherever they appear. Everything else is English, except `README.md`, -which is in `meta.language`. +Quote BPMN labels **verbatim** everywhere. Everything else is English, except `README.md`, which is +in `meta.language`. ## 3. Plan the file set per role (lane) @@ -94,20 +96,22 @@ Before writing, decide per `roles.` what to materialize from its elements - **Lane skill** (`.claude/skills//`): whenever the lane has any `kind: script` element. Every such script lives here (at its own `generatedPaths` entry), wrapped in a minimal `SKILL.md` - (step 5). A lane whose only file-producing elements are scripts gets nothing else — no agent, no - chain file; the top-level orchestration file (step 7) invokes it. + (step 5). A lane whose only file-producing elements are scripts gets nothing else (no agent, no + chain file); the top-level orchestration file (step 7) invokes it. - **Specialist agent** (`.claude/agents/.md`): **only** when `pattern.chosen` is `orchestrator-agent` (or a `mixed` phase using it) **and** the lane has a `human-checkpoint`, - `orchestrator` or `agent-checklist` element — the roster the orchestrator dispatches via `Agent`. - Never for `skill-chain-hooks` or `workflow-script`; there the lane's logic lives inline in the step-7 file. -- **Reusable skill** (`.claude/skills//`, one per `kind: skill` element or one shared dir when - the rubric grouped several): for every pattern, at exactly the element's `generatedPaths`. A + `orchestrator` or `agent-checklist` element: the roster the orchestrator dispatches via `Agent`. + Never for `skill-chain-hooks` or `workflow-script`; there the lane's logic lives inline in the + step-7 file. Its `tools:` line comes only from `roles..tools` (omitted when unset). +- **Reusable skill** (`.claude/skills//`, one per `kind: skill` element, or one shared dir + when the rubric grouped several): for every pattern, at exactly the element's `generatedPaths`. A skill-backed collapsed `subProcess`/`callActivity` folds its inner elements into this one skill as `${CLAUDE_SKILL_DIR}/../bpmn2agent-design/references/mapping-rubric.md` §"Inner elements" decided (inner `scriptTask` → `scripts/.mjs` inside it); no separate file per inner element. -Every path must equal an existing `elements..generatedPaths` entry — never invent one — except -the one top-level orchestration file from step 7, whose path goes in the mapping report's pattern section. +Every path must equal an existing `elements..generatedPaths` entry; never invent one. Exceptions: +the one top-level orchestration file from step 7 (its path goes in the mapping report's pattern +section) and the two context hooks from step 6a (listed in the report's "Context hooks" table). ## 4. Materialize scripts (`kind: script`) @@ -118,15 +122,15 @@ knowledge note. Don't second-guess the `kind` design chose. Every script and hook is one self-contained file using Node built-ins only: no npm imports, no `lib/`, no `package.json`, no install step, JSON rather than YAML for structured input. Write no script that isn't some element's `generatedPaths` entry (no run-state, commit, trace or loader -helpers — that bookkeeping belongs in skill text). A rule that seems to need a package or helper +helpers; that bookkeeping belongs in skill text). A rule that seems to need a package or helper isn't mechanical: send it back to `bpmn2agent-design` as a skill. ## 5. Materialize skills (`kind: skill`, lane skills, and the skill-chain's top-level skill) -Use `assets/templates/skill-template.md` for all three and fill it per the template's authoring -notes; apply `${CLAUDE_SKILL_DIR}/../skill-authoring/SKILL.md`'s checklist while filling. Eval -scenarios come from the diagram — happy path through the element, each outgoing gateway branch it -feeds, the loop hitting `gate.maxLoops`; list them as open items in the generated README. +Use `assets/templates/skill-template.md` for all three, filled per its authoring notes and +`${CLAUDE_SKILL_DIR}/../skill-authoring/SKILL.md`'s checklist. Eval scenarios come from the diagram +(happy path through the element, each outgoing gateway branch it feeds, the loop hitting +`gate.maxLoops`); list them as open items in the generated README. - **Reusable skill**: frontmatter `elements` = the `kind: skill` id(s); `## Procedure` from the element's inputs/outputs/knowledge/gate. @@ -140,11 +144,38 @@ feeds, the loop hitting `gate.maxLoops`; list them as open items in the generate `gate.criteria`; - `orchestrator` (gateway / loop back-edge) → inline branch/loop instructions with a counter against `gate.maxLoops`; at the cap, proceed with an explicit "risk: loop cap reached" note; - - `hook` → narrate that the gated tool call exists and what the hook does (the hook fires on its own). + - `hook` → narrate that the gated tool call exists and what the hook does (it fires on its own). + +**Context sources in the text.** A step in some `contextSources..readers` or `.writers` gets a +`## Kontextquellen` section in the file that owns the step (skill, lane skill, specialist agent, or +chain skill / orchestrator), one bullet per store, store name verbatim; wording per Art is in +`assets/templates/skill-template.md`. Per store: what this step needs from it, and the tool from +`contextSources..tools` (read tools for readers; write tools for writers, marked "nur nach +Freigabe" and naming the `userTask` before the write; `unresolved` → no tool, say so). `wissen` +stores point at the task's `references/domain-knowledge.md` section. A `gedaechtnis` store: readers +load the memory file first (missing or empty is fine); the writer integrates new insights into the +fixed sections *Bewährt*, *Vermeiden*, *Offene Muster* instead of appending, and keeps the file +within `memory.maxLines`. + +**Per-task knowledge.** A task with its own `knowledge/.md` (a reader of a `wissen` store) +does not get that content merged into a skill's `references/domain-knowledge.md` (each file numbers +its footnotes from 1). Once the skills exist, run +`node ${CLAUDE_SKILL_DIR}/scripts/install-knowledge.mjs "$cacheDir" generated/`: it +installs each file as `references/.md` in the skill that owns the task (the top-level skill +for inline checkpoints), keeps the footnote sources, drops the verbatim quotes and FAQ ids (they stay +in `knowledge/`), fixes the pointers between files, adds a contents line to long files, and writes +the owners' `## Kontextquellen` sections for `wissen` stores. Re-run it after any change to +`knowledge/`; it overwrites its own output. + +**Process input and output.** With `workflowIO.input`, the top-level skill gets `argument-hint` +(one `[field]` per `required` entry, the whole value one quoted string: `argument-hint: "[a] [b]"`) +and an `## Input` section that asks for a missing required field via `AskUserQuestion` before step 1. +With `workflowIO.output`, its last step names the end result's contract (`artifacts.`). The +orchestrator agent and Workflow script do the same in step 7. Agent files (steps 3 and 7): apply `${CLAUDE_SKILL_DIR}/../agent-authoring/SKILL.md`'s "Writing an agent"; the template's checklist, `bpmn:` frontmatter and reporting block already cover prompt -sections and report format — add `tools`/`model` only where design decided them. +sections and report format. Add `tools`/`model` only where design decided them. ## 6. Materialize hooks (`kind: hook`) @@ -153,10 +184,34 @@ Write each hook script at its `generatedPaths` (`.claude/hooks/.mjs`) from event table (`PreToolUse`/`PostToolUse`/`Stop`/`SubagentStop`), the gate condition from `condition`/`gate.criteria`. -Then write **one** `.claude/settings.json` from `assets/templates/settings-template.json`: one entry -per hook under `hooks.`, `matcher` for tool events, -`"command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/.mjs"`. Nothing else (no permissions, -model, env) — it must be safe to copy or merge by its `hooks` key. No hooks → no `settings.json`. +Then write **one** `.claude/settings.json` from `assets/templates/settings-template.json`: + +- `hooks`: one entry per hook (step 6a's two included) under `hooks.`, `matcher` for tool + events, `"command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/.mjs"`. +- `permissions.allow`: the union of `contextSources.*.tools.read` of live stores whose readers are + in lanes **without** a generated agent (lanes with one get theirs through `tools:`). Never a write + tool, never a wildcard, nothing for `tools: unresolved`. No such store → no `permissions` key. + +Nothing else (no model, env), so it is safe to merge by its `hooks` and `permissions.allow` keys. +Neither hooks nor permissions → no `settings.json`. + +## 6a. Materialize the context hooks + +Only for stores in `contextSources`. Each hook is one file per workflow, header per step 2 with the +`StoreRef_…` ids, Node built-ins only, and claims no `generatedPaths` entry (list both in the +mapping report's "Context hooks" table): + +- **Write guard**, when a `live` store has `tools.write`: `.claude/hooks/-write-guard.mjs` + from `assets/templates/hook-ask-template.mjs`. PreToolUse, matcher = the write tools of all such + stores (plus `Bash` for `cli:` patterns, checked against the command); answers + `permissionDecision: "ask"` with the store name and approval step label. `tools: unresolved` → + skip that store and say so in the report. +- **Memory cap**, when a `gedaechtnis` store exists: `.claude/hooks/-memory-cap.mjs` from + `assets/templates/hook-memory-cap-template.mjs`. PostToolUse on `Write|Edit|MultiEdit`; over + `memory.maxLines` lines it exits 2 with the "verdichten" message. + +Smoke-test both with piped payloads before moving on (a guarded write, a read, a memory file at the +cap and one line over); expectations are in the templates' header comments. ## 7. Materialize the one top-level orchestration file @@ -164,16 +219,19 @@ Exactly one, by `pattern.chosen` (for `mixed`, one per phase): - **`skill-chain-hooks`**: already written in step 5. - **`workflow-script`**: `assets/templates/workflow-script-template.mjs` → - `.claude/workflows/.workflow.mjs`. If a `workflow-authoring` skill is available read it; - otherwise follow the template's comments. One `phase()` per contiguous run of elements; `agent()` - per `serviceTask`; `pipeline()` for the fan-out of a `parallelGateway`/`inclusiveGateway`/ - multi-instance marker unless the next stage needs all results together (then `parallel()`); plain - `if`/`while` for deterministic conditions and loop caps. **The Workflow script never runs - automatically** — not by this pipeline, only when the user invokes it; say so in the README. + `.claude/workflows/.workflow.mjs`. Read the `workflow-authoring` skill if available; + otherwise follow the template's comments. With `workflowIO.input` it reads the required fields + from `args` and returns `blocked` when one is missing. One `phase()` per contiguous run of + elements; `agent()` per `serviceTask`; `pipeline()` for the fan-out of a + `parallelGateway`/`inclusiveGateway`/multi-instance marker unless the next stage needs all results + together (then `parallel()`); plain `if`/`while` for deterministic conditions and loop caps. + **The Workflow script never runs automatically**, not by this pipeline, only when the user invokes + it; say so in the README. - **`orchestrator-agent`**: `assets/templates/orchestrator-agent-template.md` → `.claude/agents/-orchestrator.md`, roster = step 3's specialists. It owns every `orchestrator` decision and every `human-checkpoint` pause-and-ask. Only the orchestrator calls - `Agent` on other generated agents. + `Agent` on other generated agents. With `workflowIO` it gets the `## Input` section (ask for a + missing required field first) and the end-result contract line. - **`mixed`**: one file per `pattern.phases[]` entry (`{name, pattern, elements}`), each per its own pattern. No `pattern.phases` → send it back to design; don't infer. At each phase boundary, the last element of one phase and the first of the next note the hand-off in their owning file. @@ -181,28 +239,40 @@ Exactly one, by `pattern.chosen` (for `mixed`, one per phase): ## 8. Write `README.md` From `assets/templates/README-template.md`, in `meta.language`. Strip the authoring-notes comment, -fill every placeholder, delete sections that don't apply (no hooks → no "Hooks" section, etc.). Include: +fill every placeholder, delete sections that don't apply (no hooks → no "Hooks" section, etc.). +Include: - What was generated; `generated//` as the only place written; payload (`.claude/`) vs. review material. - Install is one copy: `cp -R generated//.claude/. /.claude/`. Only merge case: - an existing `.claude/settings.json` — merge the `hooks` key by hand (name the events). Suggest an - `ls` for name clashes. No install script, no `npm install`. + an existing `.claude/settings.json`; merge the `hooks` and `permissions.allow` keys by hand (name + the events). Suggest an `ls` for name clashes. No install script, no `npm install`. - `.codex/`/other runtimes: skills only (copy `.claude/skills/*`); hooks, Workflow script and subagents marked **Claude-only**. - The Workflow-script section (if any): runs only when the user explicitly invokes it. +- With `contextSources`: "Voraussetzungen" (MCP servers, CLIs, notebooks; nothing is connected for + the user, no `.mcp.json`), "Gedächtnis" (path, cap, who reads and writes), which hooks guard what, + the permissions merge, and the required input from `workflowIO`. - Every `openQuestions[]` entry with `answer: null`, and the eval scenarios from step 5 as open items. - Pointers to `mapping/report.md` and `mapping/index.html`. ## 9. Write `mapping/report.md` -From `assets/templates/mapping-report-template.md` — English, labels verbatim. One row per element -(`label`/`bpmnType`/`lane`/`kind`/`generatedPaths`/notes); the pattern section with -`pattern.rationale` and **every `pattern.alternativesConsidered` entry verbatim**; grey -(`not-generated`) and red (`unresolved`) sections with `reason` verbatim; a roles table. Its "Review -status" section (nothing red, or how many red elements and open questions remain) is what the user -approves against. Every path listed must exist and match `generatedPaths` exactly — -`bpmn2agent-verify` traces from this file. +From `assets/templates/mapping-report-template.md`, English, labels verbatim: + +- one row per element (`label`/`bpmnType`/`lane`/`kind`/`generatedPaths`/notes); +- the pattern section with `pattern.rationale` and **every `pattern.alternativesConsidered` entry + verbatim**; +- a "Context sources" section with one subsection per store (Art, Ort, readers/writers, tool split, + "Lesezugriff nicht pro Rolle getrennt" where the read tools sit in `permissions.allow`, write guard + or why it was skipped) plus the context hooks table; +- grey (`not-generated`) and red (`unresolved`) sections with `reason` verbatim; +- a roles table; +- a "Review status" section (nothing red, or how many red elements and open questions remain): what + the user approves against. + +Every path listed must exist and match `generatedPaths` exactly; `bpmn2agent-verify` traces from +this file. ## 10. Render the mapping view @@ -222,14 +292,19 @@ node ${CLAUDE_SKILL_DIR}/scripts/check-mapping-view.mjs \ "${BPMN_TOOLS_CACHE:-$HOME/.cache/bpmn-authoring-tools}" generated//mapping/index.html ``` -It must print `mapping view ok` (offline it checks only the fallback; say so). +It must print `mapping view ok` (offline it checks only the fallback; say so) and a `red elements:` +line. Data stores and process input/output are rose (`context-source`); a store with no +`elements` entry shows red like any unmapped node. Add `--no-red` to fail on red. ## 11. Self-check before handoff -- Every file from steps 4–9 carries the step-2 header — spot-check a script, a skill, an agent, the README. +- Every file from steps 4–9 carries the step-2 header; spot-check a script, a skill, an agent, the README. - `node --check` every script/hook `.mjs`. Workflow script: wrap everything after `export const meta` in `(async () => {…})();` before `node --check`. -- `.claude/settings.json` (if any) parses and registers every `.claude/hooks/*.mjs`. +- `.claude/settings.json` (if any) parses and registers every `.claude/hooks/*.mjs`; its + `permissions.allow` holds no write tool and no wildcard. +- Every store name in a `## Kontextquellen` section matches the BPMN label verbatim, and every tool + there appears in that store's `contextSources..tools`. - Step 4's rule holds: no `.mjs` under `.claude/` imports anything but `node:*` or relative files; no `package.json` in the payload. - Every `generatedPaths` path exists; nothing written outside `generatedPaths`, the step-7 file, @@ -247,19 +322,21 @@ It must print `mapping view ok` (offline it checks only the fallback; say so). Report: workflow name, pattern, what was generated (counts of agents/skills/scripts/hooks + the top-level file), whether the mapping view rendered (step 10) or why not, every red element (needs a -decision — design or the diagram — before verify can pass), and open reviewer items. Point at +decision, by design or in the diagram, before verify can pass), and open reviewer items. Point at `README.md` for the copy command and `mapping/report.md` for the trace. ## Reference files -- `assets/templates/agent-template.md` — specialist agent, orchestrator-agent roster only (steps +- `assets/templates/agent-template.md`: specialist agent, orchestrator-agent roster only (steps 3/7); name per agent-authoring's rule (kebab-case `{domain}-{role}`), plus `bpmn:` frontmatter. -- `assets/templates/skill-template.md` — reusable, lane and skill-chain top-level skill (step 5). -- `assets/templates/script-template.mjs` — `kind: script` (step 4). -- `assets/templates/hook-script-template.mjs`, `settings-template.json` — hooks and their one +- `assets/templates/skill-template.md`: reusable, lane and skill-chain top-level skill (step 5). +- `assets/templates/script-template.mjs`: `kind: script` (step 4). +- `assets/templates/hook-script-template.mjs`, `settings-template.json`: hooks and their one `settings.json` (step 6). -- `assets/templates/workflow-script-template.mjs`, `orchestrator-agent-template.md` — step 7. +- `assets/templates/hook-ask-template.mjs`, `hook-memory-cap-template.mjs`: write guard and memory + cap (step 6a). +- `assets/templates/workflow-script-template.mjs`, `orchestrator-agent-template.md`: step 7. - `assets/templates/README-template.md` (step 8), `mapping-report-template.md` (step 9). -- `scripts/render-mapping.mjs`, `scripts/check-mapping-view.mjs` — step 10. -- `assets/mapping-viewer/viewer.css` + `viewer.js` — the viewer's styles and script, inlined by +- `scripts/render-mapping.mjs`, `scripts/check-mapping-view.mjs`: step 10. +- `assets/mapping-viewer/viewer.css` + `viewer.js`: the viewer's styles and script, inlined by render-mapping.mjs (edit the viewer here, not in the generator's HTML template). diff --git a/.agents/skills/bpmn2agent-generate/assets/templates/README-template.md b/.agents/skills/bpmn2agent-generate/assets/templates/README-template.md index 9aac4ee..680c1b3 100644 --- a/.agents/skills/bpmn2agent-generate/assets/templates/README-template.md +++ b/.agents/skills/bpmn2agent-generate/assets/templates/README-template.md @@ -32,19 +32,52 @@ Generated {{created date}} · pattern: **{{pattern.chosen}}** · source: `{{sour cp -R generated/{{workflow}}/.claude/. /.claude/ ``` -That is all: no install script, no `npm install`. {{if hooks:}} If your project already has a +That is all: no install script, no `npm install`. {{if settings.json:}} If your project already has a `.claude/settings.json`, don't overwrite it — copy everything else and merge the `hooks` entry -from `.claude/settings.json` here into yours ({{events}}). {{end}} Check for name clashes first if +({{events}}) and the `permissions.allow` list from `.claude/settings.json` here into yours. {{end}} Check for name clashes first if your project already has skills or agents: `ls /.claude/skills /.claude/agents`. Start it with {{how to start: `/{{workflow}}` for a skill chain, "run the {{workflow}} workflow" for a Workflow script, "use the {{workflow}}-orchestrator agent" for an orchestrator agent}}. +{{if `workflowIO.input`: "Required input: {{required fields}}, e.g. `/{{workflow}} {{example}}`; +if one is missing the workflow asks for it before it starts." If `workflowIO.output`: "Result: +{{output artifact pathPattern}}."}} + +{{Section "Voraussetzungen" (translated) — only when the spec has `contextSources`; delete otherwise. +Nothing is connected for the user: no `.mcp.json`, no endpoints or credentials are generated.}} +## Voraussetzungen + +{{One bullet per distinct need, from `contextSources`: +- `art: live`, `ort.type: mcp` → "MCP server `{{ort.ref}}` connected in your project (Claude Code: + `/mcp`) — used by {{store names verbatim}}". `tools: unresolved` → "(its tools could not be resolved + at design time; connect the server and re-run `bpmn2agent-design`; until then the steps work without it)". +- `ort.type: cli` → "command `{{ort.ref}}` installed and logged in". +- `ort.type: notebook` with `art: live` → "MCP server `gemini-notebook-mcp` connected, notebook + "{{ort.ref}}" accessible"; with `art: wissen` → nothing at run time (the knowledge is already in + the skills' `references/`). +- `ort.type: url` → "{{url}} reachable (WebFetch)"; `datei:` live → "file `{{path}}` exists".}} + +{{Section "Gedächtnis" — only when a `gedaechtnis` store exists; delete otherwise:}} +## Gedächtnis + +{{Per memory store: "„{{store name}}“ lives in `{{memory.path}}` (relative to the project root, so +inside your project, not in the copied `.claude/`), at most {{maxLines}} lines in the fixed sections +*Bewährt*, *Vermeiden*, *Offene Muster*. {{reader task labels}} load it first (missing is fine), +{{writer task labels}} fold new insights in. Commit it or add it to `.gitignore`, your call."}} ## What's in `.claude/` - **Agents** (`agents/`): {{one line each}} - **Skills** (`skills/`): {{one line each}} - **Hooks** (`hooks/` + `settings.json`): {{one line each: what it blocks and when}} — **Claude Code only.** + {{Context hooks, when generated: "`{{workflow}}-write-guard.mjs` — before any tool that writes into + {{live store names, verbatim}} Claude Code asks you first (on top of the approval step in the + process)." and "`{{workflow}}-memory-cap.mjs` — rejects a write that makes a memory file longer than + its cap and asks Claude to condense it." A store whose tools could not be resolved has no guard; + say so.}} +- **Permissions** (`settings.json` → `permissions.allow`): {{the read tools allowed without a prompt, + for stores read by non-agent lanes; "Lesezugriff nicht pro Rolle getrennt" — every role may use + them. Write tools are never pre-approved.}} - **Workflow script** (`workflows/{{workflow}}.workflow.mjs`): runs only when you start it deliberately via the Workflow tool, never automatically. **Claude Code only.** - **Scripts**: {{list every scripts/*.mjs with its BPMN task, or "none"}} — Node built-ins only. diff --git a/.agents/skills/bpmn2agent-generate/assets/templates/agent-template.md b/.agents/skills/bpmn2agent-generate/assets/templates/agent-template.md index 4ae350e..3ad3073 100644 --- a/.agents/skills/bpmn2agent-generate/assets/templates/agent-template.md +++ b/.agents/skills/bpmn2agent-generate/assets/templates/agent-template.md @@ -1,6 +1,8 @@ --- name: {{agentName}} description: {{One sentence: what this role covers in the generated workflow, derived from the BPMN lane label "{{laneLabel}}", plus when it's invoked (e.g. "the {{workflow}} skill chain's {{laneLabel}} step").}} +{{`tools: {{roles..tools joined by ", "}}` — only when design set `roles..tools`; otherwise +delete this line so the agent inherits everything. Never add a tool design didn't list.}} bpmn: file: {{sourceBpmnPath}} elements: [{{elementIds}}] @@ -31,8 +33,18 @@ checklist done, confirm every step fired in order: 'risk: loop cap reached' instead of looping again)."}} - [ ] {{...one item per remaining element...}} - +{{Add this section only when a task of this lane is in some `contextSources..readers` or + `.writers`; same bullets as skill-template.md's `## Kontextquellen` (wissen / live read / live write + "nur nach Freigabe" / unresolved / gedächtnis read-first / gedächtnis write-integrate), store names + verbatim, tools only from `contextSources..tools`.}} + +## Kontextquellen + +- **{{storeName}}** ({{art}}, {{read|write}}): {{what to fetch or write for this step}} with + `{{tool}}`{{; for a write: " — nur nach Freigabe", after "{{approval userTask label}}"}}. + + ## Domain knowledge @@ -78,6 +90,9 @@ Authoring notes for whoever fills this template (bpmn2agent-generate step 5): with kind human-checkpoint, orchestrator, or agent-checklist. A lane whose elements are ALL kind: script gets a skill wrapper instead (assets/templates/skill-template.md), never this file — see SKILL.md step 3. +- `tools:` is filled only from `roles..tools` (orchestrator-agent lanes; design placed the + store's tools there). Where the lane's agent has no entry, leave the line out; don't derive tools + from the stores yourself. - Follows agent-authoring's naming convention: {{workflow}}-{{role}}, kebab-case, no "-expert" suffix. --> diff --git a/.agents/skills/bpmn2agent-generate/assets/templates/hook-ask-template.mjs b/.agents/skills/bpmn2agent-generate/assets/templates/hook-ask-template.mjs new file mode 100644 index 0000000..ea140bf --- /dev/null +++ b/.agents/skills/bpmn2agent-generate/assets/templates/hook-ask-template.mjs @@ -0,0 +1,56 @@ +#!/usr/bin/env node +// bpmn: {"file":"{{sourceBpmnPath}}","elements":[{{storeRefIds of every live store with write tools, as "StoreRef_X","StoreRef_Y"}}]} +// +// Claude Code PreToolUse hook, generated by bpmn2agent-generate from the live data stores +// {{store labels, verbatim}} in {{sourceBpmnPath}}. Backs up the diagram's approval step: every tool +// that WRITES into one of these stores makes Claude Code ask the human at the call +// (permissionDecision "ask"), even if a skill forgot the approval. Reads and every other tool pass +// untouched. CLAUDE-ONLY: hooks are a Claude Code mechanism. +// +// Reads the PreToolUse hook JSON on stdin. Prints the decision JSON on stdout and exits 0 when the +// call writes into a guarded store; prints nothing and exits 0 otherwise. + +// One entry per live store with `contextSources..tools.write` (never for `tools: unresolved`). +// name the store label verbatim +// approval label(s) of the userTask(s) that must confirm the write, verbatim, joined with " / " +// tools exact tool names from tools.write (MCP tool names; Write/Edit for a datei: store) +// bash command prefixes from `Bash( :*)` write patterns, without the "Bash(" and ":*)" +// file only for a datei: store whose write tools are Write/Edit: its path, so other files stay free +const STORES = [ + {{{ name: '{{storeName}}', approval: '{{approvalLabels}}', tools: [{{'mcp__server__writeTool'}}], bash: [{{'gh issue create'}}], file: {{null | 'path'}} },}} +]; + +const input = JSON.parse(await new Response(process.stdin).text()); +const tool = input.tool_name; +const command = String(input.tool_input?.command ?? '').trim(); +const filePath = input.tool_input?.file_path; + +const hit = STORES.find((s) => { + if (tool === 'Bash') return s.bash.some((p) => command === p || command.startsWith(`${p} `)); + if (!s.tools.includes(tool)) return false; + return !s.file || (filePath && filePath.endsWith(s.file)); +}); + +if (hit) { + console.log(JSON.stringify({ + hookSpecificOutput: { + hookEventName: 'PreToolUse', + permissionDecision: 'ask', + permissionDecisionReason: `Write to "${hit.name}" needs approval (step "${hit.approval}")`, + }, + })); +} +process.exit(0); + +/* +Authoring notes for whoever fills this template (bpmn2agent-generate step 6a): +- ONE file per workflow: `.claude/hooks/-write-guard.mjs`, covering every live store that has + write tools. Not generated when no live store has write tools; a store with `tools: unresolved` + gets no entry (note the skip in the mapping report). It claims no `generatedPaths` entry; the + mapping report lists it, and the `bpmn:` header points at the store reference elements. +- settings.json registers it under `hooks.PreToolUse`, `matcher` = the union of all `tools` entries + joined with "|", plus `Bash` when any store has `bash` patterns. Don't use ".*" or "mcp__server__*". +- Keep the output exactly this shape (hookSpecificOutput.permissionDecision "ask"): exit 0, JSON on + stdout. Don't exit 2 here; that would block instead of ask. +- Node built-ins only; strip the {{...}} and the `{{{ ... }}}` row template; one object per store. +*/ diff --git a/.agents/skills/bpmn2agent-generate/assets/templates/hook-memory-cap-template.mjs b/.agents/skills/bpmn2agent-generate/assets/templates/hook-memory-cap-template.mjs new file mode 100644 index 0000000..308367c --- /dev/null +++ b/.agents/skills/bpmn2agent-generate/assets/templates/hook-memory-cap-template.mjs @@ -0,0 +1,43 @@ +#!/usr/bin/env node +// bpmn: {"file":"{{sourceBpmnPath}}","elements":[{{storeRefIds of every gedächtnis store, as "StoreRef_X","StoreRef_Y"}}]} +// +// Claude Code PostToolUse hook, generated by bpmn2agent-generate from the memory data stores +// {{store labels, verbatim}} in {{sourceBpmnPath}}. After Write/Edit/MultiEdit on a memory file it +// counts the file's lines; over the cap it exits 2, which feeds the stderr message back to Claude +// so the writer condenses the file instead of letting it grow. CLAUDE-ONLY: hooks are a Claude Code +// mechanism. +import { existsSync, readFileSync } from 'node:fs'; +import path from 'node:path'; + +// One entry per `contextSources.` with `art: gedaechtnis`: its `memory.path` (relative to the +// project root, where the run's cwd is) and `memory.maxLines`. +const MEMORY = [ + {{{ name: '{{storeName}}', path: '{{memory.path}}', maxLines: {{memory.maxLines}} },}} +]; + +const input = JSON.parse(await new Response(process.stdin).text()); +const root = process.env.CLAUDE_PROJECT_DIR || input.cwd || process.cwd(); +const target = input.tool_input?.file_path ? path.resolve(root, input.tool_input.file_path) : null; + +const mem = target && MEMORY.find((m) => path.resolve(root, m.path) === target); +if (mem && existsSync(target)) { + const text = readFileSync(target, 'utf8'); + const lines = text === '' ? 0 : text.replace(/\n$/, '').split('\n').length; + if (lines > mem.maxLines) { + console.error(`Gedächtnis zu lang (${lines} > ${mem.maxLines} Zeilen): verdichten, Abschnitte zusammenführen`); + console.error(`${mem.name} (${mem.path}): fold entries into the existing sections (Bewährt, Vermeiden, Offene Muster), drop outdated ones, then write the file again.`); + process.exit(2); + } +} +process.exit(0); + +/* +Authoring notes for whoever fills this template (bpmn2agent-generate step 6a): +- ONE file per workflow: `.claude/hooks/-memory-cap.mjs`, covering every memory store. Not + generated when the spec has no `art: gedaechtnis` store. It claims no `generatedPaths` entry; the + mapping report lists it, and the `bpmn:` header points at the store reference elements. +- settings.json registers it under `hooks.PostToolUse` with `"matcher": "Write|Edit|MultiEdit"`. +- Keep the German first stderr line exactly as written (it is the contract with the memory writer's + skill text); exit 2 is what makes Claude Code show it to Claude. +- Node built-ins only; strip the {{...}} and the `{{{ ... }}}` row template; one object per store. +*/ diff --git a/.agents/skills/bpmn2agent-generate/assets/templates/mapping-report-template.md b/.agents/skills/bpmn2agent-generate/assets/templates/mapping-report-template.md index 6ab1309..df63f9b 100644 --- a/.agents/skills/bpmn2agent-generate/assets/templates/mapping-report-template.md +++ b/.agents/skills/bpmn2agent-generate/assets/templates/mapping-report-template.md @@ -25,7 +25,9 @@ Generated {{date}} from `generated/{{workflow}}/workflow-spec.yaml` - "** red element(s), open question(s) — resolve before approval.** Details: "Red — open / unresolved" and "Open questions carried forward" below." Then one line " element(s) deliberately not generated (grey) — not an error." and one line - linking the viewer: "Interactive, read-only view of the same mapping: [`index.html`](index.html)."}} + linking the viewer: "Interactive, read-only view of the same mapping: [`index.html`](index.html)." + When a store has `tools: unresolved`, add: " store(s) without resolved tools — no tool access and no + write guard emitted for them; see "Context sources"."}} ## Pattern @@ -51,6 +53,7 @@ Generated {{date}} from `generated/{{workflow}}/workflow-spec.yaml` | Teal | `orchestrator` | Gateways, loop back-edges, multi-instance markers — no file of its own. | | Amber | `human-checkpoint` | `userTask`/`manualTask`: a person decides here. | | Brown | `artifact-contract` | A data object with a path/frontmatter contract. | +| Rose | `context-source`, `workflow-input`, `workflow-output` | A data store (knowledge, live system, memory) or the process-wide input/output; no file of its own. {{Delete this row when the spec has neither.}} | | **Grey** | `not-generated` | Deliberately not generated; reason shown in the table below. Not an error. | | **Red** | `unresolved` | Unmapped or still an open question; blocks `bpmn2agent-verify`'s "no red in the map" check. | @@ -74,7 +77,9 @@ colours" section — this table is the textual mirror of what `mapping/workflow- not-generated/unresolved too. - "Notes": for not-generated/unresolved, the element's `reason` verbatim (this is the grey/red annotation text). For a gate, its kind + maxLoops. For a hook, its event and matcher as - registered in `.claude/settings.json`. Otherwise blank. + registered in `.claude/settings.json`. For a `context-source`: Art and Ort, e.g. "live · mcp:atlassian" + (details under "Context sources"); for `workflow-input`/`workflow-output`: the artifact id. + Otherwise blank. }} | {{label}} | {{bpmnType}} | {{laneLabel}} | {{kind}} | {{generatedPaths joined}} | {{reason / gate / pairing note}} | @@ -91,6 +96,40 @@ colours" section — this table is the textual mirror of what `mapping/workflow- bpmn2agent-verify's "no red in the map" check will fail until these are resolved (back to bpmn2agent-design, or the diagram itself per its step 2 triage).}} +## Context sources + +{{Only when the spec has `contextSources` or `workflowIO`; delete the section otherwise. One + subsection per `contextSources.`, in spec order, the store name verbatim as the heading:}} + +### {{contextSources..name}} + +- **Art / Ort:** {{art}} / {{ort.type}}{{:ort.ref}} +- **Readers:** {{reader task labels, verbatim}} · **Writers:** {{writer task labels, verbatim, or "none"}} +- **Tools (live):** read `{{tools.read}}` · write `{{tools.write}}` — or "unresolved: no tools emitted" + (verify warns; generate never emits a wildcard). +- **Placement:** {{agent `tools:` of `` (orchestrator-agent lanes) | `.claude/settings.json` + → `permissions.allow`. For the second: "Lesezugriff nicht pro Rolle getrennt" — the read tools are + allowed for every role; write tools are never pre-approved.}} +- **Write guard:** {{approval step label(s) before the write + "PreToolUse hook asks at the call" | + "no writers" | "skipped: tools unresolved"}} +- **Memory (gedächtnis):** {{memory.path}}, cap {{maxLines}} lines, cap hook {{path}} | omit +- **Knowledge (wissen):** {{knowledge file(s) per reading task, e.g. `knowledge/.md`}} | omit + +{{Then one line each for `workflowIO.input` ("Process input: {{artifact}}, required: {{fields}} — becomes + the argument-hint / Input section of {{top-level file}}") and `workflowIO.output` ("Process output: + {{artifact}} — contract for the end result"), when set.}} + +### Context hooks + +{{Hooks that no element's `generatedPaths` claims, by path, with event, matcher as registered in + `.claude/settings.json`, and the store elements their `bpmn:` header names. "None." if the spec has + no live store with write tools and no memory store. A skipped guard is listed here with the reason.}} + +| Hook | Event / matcher | Stores | Purpose | +|---|---|---|---| +| `{{.claude/hooks/-write-guard.mjs}}` | PreToolUse / `{{write tools}}` | {{store names}} | Asks before writing into a live store. | +| `{{.claude/hooks/-memory-cap.mjs}}` | PostToolUse / `Write\|Edit\|MultiEdit` | {{store names}} | Exit 2 above the line cap. | + ## Roles | Role | Lane | Agent / skill generated | Model tier | Tools | diff --git a/.agents/skills/bpmn2agent-generate/assets/templates/orchestrator-agent-template.md b/.agents/skills/bpmn2agent-generate/assets/templates/orchestrator-agent-template.md index 180a0e6..f1c7516 100644 --- a/.agents/skills/bpmn2agent-generate/assets/templates/orchestrator-agent-template.md +++ b/.agents/skills/bpmn2agent-generate/assets/templates/orchestrator-agent-template.md @@ -13,6 +13,15 @@ You are a routing meta-agent for the generated `{{workflow}}` workflow (from yourself — you own this diagram's judgement gateways and hand off each branch to the generated agent whose lane matches, exactly the way the BPMN draws it. +{{Only when the spec has `workflowIO.input`; delete this section otherwise. (Agent frontmatter has no +`argument-hint`, so the input contract lives here.)}} + +## Input + +Required: {{`workflowIO.input.required` fields, each with its description from +`artifacts..frontmatter`}}. Take them from the user's request. If one is missing, ask for it +via `AskUserQuestion` before dispatching anything; never start with a guess. + ## Roster | Agent | Lane / domain | @@ -40,6 +49,10 @@ Walk the diagram in flow order: dark-factory's fail -> pass-with-risk rule at the cap.}} 5. {{...remaining elements, one bullet per branch/step...}} +{{When `workflowIO.output` is set: "The end result is `{{artifacts..pathPattern}}` with +frontmatter {{fields}}; make sure the last specialist's output meets that contract before you report +`done`."}} + For each dispatch: give the specialist the specific step and the artifact(s) it needs (per `elements..inputs`/`outputs`), wait for its report before continuing (unless the diagram's own parallel/multi-instance structure says otherwise — dispatch those concurrently and wait for all @@ -65,6 +78,8 @@ the whole diagram. ## Explicitly out of scope +- Do not read or write a context store yourself; the specialist whose step touches it does (its + `## Kontextquellen`). Approval steps before a live write stay yours: pause-and-ask as drawn. - Do not do a specialist's work yourself — always dispatch, per the roster. - Do not invent a branch the diagram doesn't have, and do not silently drop a gateway path — an unhandled branch is a bug in this file, not a judgement call to make at runtime. diff --git a/.agents/skills/bpmn2agent-generate/assets/templates/settings-template.json b/.agents/skills/bpmn2agent-generate/assets/templates/settings-template.json index 3020c07..8016169 100644 --- a/.agents/skills/bpmn2agent-generate/assets/templates/settings-template.json +++ b/.agents/skills/bpmn2agent-generate/assets/templates/settings-template.json @@ -1,4 +1,7 @@ { + "permissions": { + "allow": ["{{readTool}}"] + }, "hooks": { "{{hookEvent}}": [ { diff --git a/.agents/skills/bpmn2agent-generate/assets/templates/skill-template.md b/.agents/skills/bpmn2agent-generate/assets/templates/skill-template.md index 2ed5577..baad9a2 100644 --- a/.agents/skills/bpmn2agent-generate/assets/templates/skill-template.md +++ b/.agents/skills/bpmn2agent-generate/assets/templates/skill-template.md @@ -4,6 +4,10 @@ description: {{One sentence: the procedure this skill runs, derived from the BPM "{{elementLabel}}" verbatim, plus when it's used — e.g. "Use whenever the {{workflow}} skill chain reaches '{{elementLabel}}'." Trigger-worthy per this repo's sibling skills, not a bare restatement of the element name.}} +{{Skill-chain top-level skill only, and only when the spec has `workflowIO.input`: add the line +`argument-hint: "[{{field1}}] [{{field2}}]"` here — one bracketed name per `workflowIO.input.required` +entry, the whole value in double quotes (unquoted, `[a] [b]` is not valid YAML and `verify` fails on it). +Delete this note otherwise.}} bpmn: file: {{sourceBpmnPath}} elements: [{{elementIds}}] @@ -17,6 +21,15 @@ reused ≥2 places / needs its own template / bundles reference material — per `bpmn2agent-design/references/mapping-rubric.md`'s serviceTask decision, so a reader knows this wasn't an arbitrary split.}} +{{Skill-chain top-level skill with `workflowIO.input` only — put this section before "## Procedure", +delete it otherwise:}} + +## Input + +Required: {{`workflowIO.input.required` fields, each with its description from +`artifacts..frontmatter`}}. Take them from the invocation arguments. If one is missing, ask the +user for it via `AskUserQuestion` before starting step 1; never start with a guess. + ## Procedure 1. {{Step derived from the element's inputs — read `{{inputArtifactPath}}`, required: {{true/false}}.}} @@ -26,9 +39,42 @@ wasn't an arbitrary split.}} {{frontmatter fields from `artifacts.`}}.}} {{If this element has a `gate`: add a step naming the gate.kind (critic/human/deterministic/panel) and listing gate.criteria as a checklist the output must pass before being considered done.}} +{{Skill-chain top-level skill with `workflowIO.output`: the last step names the end result and the + contract it follows — "the end result is `{{artifacts..pathPattern}}` with frontmatter + {{fields}}".}} + +{{Add this section only when the element is in some `contextSources..readers` or `.writers` + (a lane skill carries one bullet group per task of the lane that touches a store). Heading verbatim. + One bullet per store, store name verbatim from `contextSources..name`; pick the line matching + the store's Art and the element's role:}} + +## Kontextquellen + +- **{{storeName}}** (wissen, read): distilled at generation time; see `references/domain-knowledge.md`, + section "{{storeName}}". No tool call at run time. +- **{{storeName}}** (live, read): fetch {{what THIS step needs from the store — derive it from the + element's documentation and inputs, e.g. "the ticket named by `ticketId`: summary, description, + status"}} with `{{one tool from contextSources..tools.read}}`. +- **{{storeName}}** (live, write — nur nach Freigabe): {{what this step writes}} with + `{{one tool from contextSources..tools.write}}`. Only after the approval step + "{{label of the userTask before this write}}" has confirmed it; Claude Code asks again at the call + (write-guard hook). Without that confirmation, do not write. +- **{{storeName}}** (live, tools unresolved): no tool was resolved for this store at design time + (server not connected). Do not guess a tool; tell the user the step cannot reach the store. +- **{{storeName}}** (gedächtnis, read): load `{{memory.path}}` FIRST, before any other step. A missing + or empty file is fine: continue without it. +- **{{storeName}}** (gedächtnis, write): integrate this run's new insights into the fixed sections + *Bewährt*, *Vermeiden* and *Offene Muster* of `{{memory.path}}` (create the file with these three + headings if it is missing): merge with existing entries, drop what is outdated, never append a log. + Keep the file within {{memory.maxLines}} lines; if the memory-cap hook reports it too long (exit 2), + condense and rewrite. Writing the memory needs no approval. ## Domain knowledge +{{A task that has its own `knowledge/.md` (reader of a `wissen` store) is NOT copied here: run +`scripts/install-knowledge.mjs` (SKILL.md step 5), which installs it as `references/.md` and writes the +`## Kontextquellen` bullets for `wissen` stores. The text below is for elements grounded at phase or lane level.}} + {{Copy the distilled, cited content from `generated/{{workflow}}/knowledge/{{ref}}.md` into `references/domain-knowledge.md` verbatim (footnotes intact) when `knowledge.refs` names a file for this element. For a `"websearch"` entry, do the same from the WebSearch-sourced knowledge file. For @@ -66,5 +112,11 @@ Authoring notes for whoever fills this template (bpmn2agent-generate step 5): - Bundle a template/rubric/worked example under `assets/` or `references/` inside this skill dir when the source BPMN's data object / knowledge extraction implies one — don't leave "## Procedure" as the only content for a skill the rubric flagged as needing bundled reference material. -- English throughout except quoted BPMN labels (mapping-rubric.md's language rule). +- English throughout except quoted BPMN labels (mapping-rubric.md's language rule) and the fixed German + tokens `## Kontextquellen`, "nur nach Freigabe" and the memory section names (*Bewährt*, *Vermeiden*, + *Offene Muster*). +- `## Kontextquellen` goes into the file that owns the step's logic (its skill, lane skill, specialist + agent, or the chain skill / orchestrator for steps narrated inline). Tasks without a store get no + such section. Tool names come only from the spec's `contextSources..tools`; never invent one and + never use a wildcard. --> diff --git a/.agents/skills/bpmn2agent-generate/assets/templates/workflow-script-template.mjs b/.agents/skills/bpmn2agent-generate/assets/templates/workflow-script-template.mjs index 69ff4bf..ac11e0d 100644 --- a/.agents/skills/bpmn2agent-generate/assets/templates/workflow-script-template.mjs +++ b/.agents/skills/bpmn2agent-generate/assets/templates/workflow-script-template.mjs @@ -18,6 +18,14 @@ export const meta = { // "Workflow scripts" section, which must say this explicitly per pattern-rubric.md's Workflow // script detail. +{{Only when the spec has `workflowIO.input`: document the input right here, as a comment, and read it + from `args` — `meta` stays a pure literal, so it can't carry the arg list. A script can't ask the + user, so a missing required field ends the run with a `blocked` result the caller acts on:}} +// Input (args): {{field1}}, {{field2}} — {{description per field, from artifacts..frontmatter}}. +const { {{field1}}, {{field2}} } = args ?? {} +const missing = [{{'field1', 'field2'}}].filter((k) => !args?.[k]) +if (missing.length) return { status: 'blocked', summary: 'missing required input', missing } + {{one phase per contiguous run of elements sharing this workflow-script phase, in BPMN flow order}} phase('{{phaseTitle}}') log('{{one-line narrator message for this phase, from the phase\'s elements\' labels}}') @@ -50,6 +58,8 @@ if ({{loopCounter}} >= {{maxLoops}}) { log('{{elementLabel}}: loop cap reached ({{maxLoops}}) — proceeding with a risk flag instead of looping again.') } +{{When `workflowIO.output` is set, `finalResultShape` follows the output artifact's contract + (`artifacts.` frontmatter fields) and the comment names it.}} return {{finalResultShape}} /* @@ -74,6 +84,9 @@ branch): sub-workflow when mapping-rubric.md promoted a callActivity to its own pattern) — this path isn't in any single element's generatedPaths since it spans the whole phase/diagram; record it in the mapping report's own "generated artifacts" list instead (see mapping-report-template.md). +- A phase that writes into a live context store never belongs here (no human checkpoint possible); + design's pattern rubric rules it out. If a spec reaches this template with such a writer, send it + back to bpmn2agent-design instead of approximating an approval. - No AskUserQuestion / human-checkpoint calls belong in this template — a diagram with a real human checkpoint shouldn't have resolved to pattern.chosen: workflow-script in the first place (pattern-rubric.md's Workflow-script "when not"); if bpmn2agent-generate reaches this template diff --git a/.agents/skills/bpmn2agent-generate/scripts/check-mapping-view.mjs b/.agents/skills/bpmn2agent-generate/scripts/check-mapping-view.mjs index 68f00e2..dfe7670 100644 --- a/.agents/skills/bpmn2agent-generate/scripts/check-mapping-view.mjs +++ b/.agents/skills/bpmn2agent-generate/scripts/check-mapping-view.mjs @@ -4,9 +4,12 @@ // its one question ("is anything red, and where?"), that every element can be found (level tree, // search, kind filter), that history and deep links work, and that it stays operable without a mouse. // -// Usage: node check-mapping-view.mjs [--shots ] +// Usage: node check-mapping-view.mjs [--shots ] [--no-red] // cacheDir same tool cache as render-mapping.mjs (playwright is installed there by validate.sh) // --shots optional: write desktop / narrow / offline screenshots into +// --no-red optional: also fail when the viewer lists red elements (unmapped, unresolved, open +// question — e.g. a data store without an elements entry). Without it the red elements +// are only printed ("red elements: ..."), since a red view is a finding, not a viewer bug. // // Prints "mapping view ok" and exits 0, or lists every failed check and exits 1. The viewer loads // bpmn-js from cdn.jsdelivr.net; when that is unreachable the diagram checks are skipped (the @@ -17,11 +20,12 @@ import path from 'node:path'; import { pathToFileURL } from 'node:url'; const args = process.argv.slice(2); +const noRed = args.includes('--no-red'); const shotsIdx = args.indexOf('--shots'); const shotsDir = shotsIdx >= 0 ? path.resolve(args[shotsIdx + 1]) : null; -const [cacheDir, htmlArg] = args.filter((_, i) => shotsIdx < 0 || (i !== shotsIdx && i !== shotsIdx + 1)); +const [cacheDir, htmlArg] = args.filter((a, i) => a !== '--no-red' && (shotsIdx < 0 || (i !== shotsIdx && i !== shotsIdx + 1))); if (!cacheDir || !htmlArg) { - console.error('Usage: node check-mapping-view.mjs [--shots ]'); + console.error('Usage: node check-mapping-view.mjs [--shots ] [--no-red]'); process.exit(2); } const url = pathToFileURL(path.resolve(htmlArg)).href; @@ -34,6 +38,7 @@ const shot = async (page, name) => { if (shotsDir) await page.screenshot({ path: const browser = await chromium.launch(); let offline = false; +let redReport = ''; try { // ── desktop ── const page = await browser.newPage({ viewport: { width: 1440, height: 900 } }); @@ -44,13 +49,20 @@ try { offline = await page.evaluate(() => typeof BpmnJS === 'undefined'); const summary = await page.evaluate(() => SUMMARY); + redReport = summary.red.length + ? `red elements: ${summary.red.length} (${summary.red.join(', ')})` + : 'red elements: none'; + if (noRed) check(!summary.red.length, `the view has ${summary.red.length} red element(s): ${summary.red.join(', ')}`); const statusText = await page.textContent('#status'); check(statusText.trim().length > 0, 'review status block is empty'); check((await page.$$('#status .red-item')).length === summary.red.length, `review status lists ${(await page.$$('#status .red-item')).length} red element(s), spec has ${summary.red.length}`); const legendNames = await page.$$eval('#legend .legend-item .name', (els) => els.map((e) => e.textContent.trim())); - check(legendNames.length === 9 && legendNames.every((n) => n && !/^[a-z]+(-[a-z]+)*$/.test(n)), - `legend must show 9 human-readable kind names, got: ${legendNames.join(', ')}`); + // 9 kinds, 10 when the diagram has data stores / process I/O (context-source, one shared legend entry). + const legendSize = await page.evaluate(() => LEGEND.length); + check((legendSize === 9 || legendSize === 10) && legendNames.length === legendSize + && legendNames.every((n) => n && !/^[a-z]+(-[a-z]+)*$/.test(n)), + `legend must show 9 or 10 human-readable kind names matching LEGEND, got: ${legendNames.join(', ')}`); const docOverflow = await page.evaluate(() => document.documentElement.scrollHeight - innerHeight); check(docOverflow <= 0, `desktop page scrolls vertically by ${docOverflow} px (canvas and sidebar should fill the window)`); @@ -304,4 +316,5 @@ if (failures.length) { for (const f of failures) console.error(' ✗', f); process.exit(1); } +console.log(redReport); console.log(offline ? 'mapping view ok (offline, diagram skipped)' : 'mapping view ok'); diff --git a/.agents/skills/bpmn2agent-generate/scripts/install-knowledge.mjs b/.agents/skills/bpmn2agent-generate/scripts/install-knowledge.mjs new file mode 100644 index 0000000..1f56a9b --- /dev/null +++ b/.agents/skills/bpmn2agent-generate/scripts/install-knowledge.mjs @@ -0,0 +1,136 @@ +// bpmn: {"file":"docs/planning/bpmn-to-agentic-workflow.bpmn","elements":["Task_Generate"]} +// install-knowledge.mjs — copies the per-task knowledge files into the skills that own the tasks, +// in the shape an installed skill can use. Run by bpmn2agent-generate step 5 after the skills exist. +// +// Usage: node install-knowledge.mjs / dir> (cwd = repo root of the run) +// +// A task has a "per-task file" when knowledge.refs. is generated//knowledge/.md. +// For each one: +// - owner skill: the skill dir named by elements..generatedPaths[0]; a step narrated inline +// (a human checkpoint of the skill-chain pattern) belongs to the top-level skill skills//. +// - written to /references/.md (the knowledge file's own `bpmn:` header stays); +// - pointers to `phase-N-….md` become `domain-knowledge.md` in the phase skills (their phase file is +// installed under that name) and stay as they are in the top-level skill, which gets the phase +// files it points at under their own names; a pointer to another per-task file in a different +// skill becomes ${CLAUDE_SKILL_DIR}/..//references/.md; +// - footnotes keep the source and date and drop the verbatim quote and the FAQ reference after the +// first " — "; the body tags like [triage-klaerung-1: 7, 9] (they point into knowledge/faq/, which +// is not installed) are removed; a note says where the quotes are; +// - a file over 100 lines gets a one-line contents list from its headings. +// Then every owner skill gets one `## Kontextquellen` section (replaced when it exists): one line per +// store, the store name verbatim, the task labels verbatim, the reference files. Existing live and +// gedächtnis bullets of that section are kept and follow the wissen lines. +// Idempotent: re-running rewrites the same files from knowledge/. +import fs from 'node:fs'; +import path from 'node:path'; +import { createRequire } from 'node:module'; + +const [cacheDir, genDir] = process.argv.slice(2); +if (!cacheDir || !genDir) { + console.error('Usage: node install-knowledge.mjs / dir>'); + process.exit(2); +} +const require = createRequire(path.join(path.resolve(cacheDir), 'package.json')); +const yaml = require('js-yaml'); + +const spec = yaml.load(fs.readFileSync(path.join(genDir, 'workflow-spec.yaml'), 'utf8')); +const workflow = spec.meta.workflowName; +const skillsDir = path.join(genDir, '.claude', 'skills'); +const knowledgeDir = path.join(genDir, 'knowledge'); +const NOTE = '_Footnotes name the notebook source. The verbatim quotes and FAQ references stay with the generation output (`knowledge/`), which is not installed._'; +const refs = spec.knowledge?.refs || {}; +const stores = Object.values(spec.contextSources || {}).filter((s) => s.art === 'wissen'); + +const perTask = Object.keys(refs).filter((id) => path.basename(refs[id]) === `${id}.md`); +const ownerOf = (id) => { + const p = (spec.elements[id]?.generatedPaths || [])[0] || ''; + const m = p.match(/\.claude\/skills\/([^/]+)\//); + return m ? m[1] : workflow; +}; +const owners = Object.fromEntries(perTask.map((id) => [id, ownerOf(id)])); + +const transform = (id, text) => { + const owner = owners[id]; + const isTop = owner === workflow; + // pointers + if (!isTop) text = text.replace(/`phase-\d+-[a-z0-9-]+\.md`/g, '`domain-knowledge.md`'); + text = text.replace(/`([A-Za-z0-9_]+)\.md`/g, (m, other) => + owners[other] && owners[other] !== owner ? `\`\${CLAUDE_SKILL_DIR}/../${owners[other]}/references/${other}.md\`` : m); + // footnote definitions keep source and date; body tags into the FAQ go + const out = text.split('\n').map((line) => { + if (/^\[\^[^\]]+\]:/.test(line)) { + const i = line.indexOf(' — '); + return i === -1 ? line : line.slice(0, i); + } + return line.replace(/ ?\[[a-z0-9-]+-\d+: [^\]]*\]/g, ''); + }); + let body = out.join('\n'); + // note under the `_Ort:_` line + body = body.replace(/(_Ort: [^\n]*_\n)/, `$1\n${NOTE}\n`); + // contents line for long files + if (body.split('\n').length > 100) { + const heads = []; + for (const l of body.split('\n')) { + const h = l.match(/^###+ (.+)$/) || l.match(/^\*\*([^*]+)\*\*(?: \[[^\]]*\])?:?$/); + if (h) heads.push(h[1].replace(/[:.]$/, '').trim()); + } + if (heads.length >= 3) body = body.replace(NOTE, `${NOTE}\n\nContents: ${heads.join(' · ')}`); + } + return body; +}; + +const written = []; +for (const id of perTask) { + const dir = path.join(skillsDir, owners[id], 'references'); + fs.mkdirSync(dir, { recursive: true }); + const src = fs.readFileSync(path.join(knowledgeDir, `${id}.md`), 'utf8'); + fs.writeFileSync(path.join(dir, `${id}.md`), transform(id, src)); + written.push(path.join(owners[id], 'references', `${id}.md`)); +} + +// the top-level skill gets the phase files its per-task files point at, with the FAQ note +const phaseNote = 'Tags like `[triage-klaerung-1: 7, 9]` point to an entry and its citation numbers in the notebook FAQ kept with the generation output (`knowledge/faq/`), not installed.'; +const topRefs = path.join(skillsDir, workflow, 'references'); +const wantPhase = new Set(); +for (const id of perTask.filter((i) => owners[i] === workflow)) { + const t = fs.readFileSync(path.join(knowledgeDir, `${id}.md`), 'utf8'); + for (const m of t.matchAll(/`(phase-\d+-[a-z0-9-]+\.md)`/g)) wantPhase.add(m[1]); +} +for (const f of wantPhase) { + const src = path.join(knowledgeDir, f); + if (!fs.existsSync(src)) continue; + let t = fs.readFileSync(src, 'utf8'); + if (phaseNote && !/not installed/.test(t)) t = t.replace(/^(# [^\n]*\n)/m, `$1\n${phaseNote}\n`); + fs.mkdirSync(topRefs, { recursive: true }); + fs.writeFileSync(path.join(topRefs, f), t); + written.push(path.join(workflow, 'references', f)); +} + +// Kontextquellen per owner skill +const byOwner = {}; +for (const id of perTask) (byOwner[owners[id]] ??= []).push(id); +for (const [owner, ids] of Object.entries(byOwner)) { + const p = path.join(skillsDir, owner, 'SKILL.md'); + if (!fs.existsSync(p)) continue; + let t = fs.readFileSync(p, 'utf8'); + // live and gedächtnis bullets are written by generate step 5, not here: carry them over + const old = t.match(/\n## Kontextquellen\n[\s\S]*?(?=\n## |$)/)?.[0] || ''; + const keep = old.split('\n').filter((l) => /^- \*\*.+?\*\* \((live|gedächtnis)\b/.test(l)); + t = t.replace(/\n## Kontextquellen\n[\s\S]*?(?=\n## |$)/, '').replace(/\s+$/, '\n'); + const isTop = owner === workflow; + let sec = '\n## Kontextquellen\n\nKnowledge stores, distilled at generation time into one file per task; no tool call at run time.'; + sec += isTop + ? ' The phase files beside them hold the gateway criteria.\n\n' + : fs.existsSync(path.join(skillsDir, owner, 'references', 'domain-knowledge.md')) + ? ' `${CLAUDE_SKILL_DIR}/references/domain-knowledge.md` keeps the phase-level criteria.\n\n' + : '\n\n'; + for (const s of stores) { + const mine = s.readers.filter((r) => ids.includes(r)); + if (!mine.length) continue; + sec += `- **${s.name}** (wissen): ${mine.map((r) => `"${spec.elements[r].label}" → \`\${CLAUDE_SKILL_DIR}/references/${r}.md\``).join('; ')}\n`; + } + if (keep.length) sec += `\nLive and memory stores, used at run time:\n\n${keep.join('\n')}\n`; + fs.writeFileSync(p, t + sec); +} + +console.log(`installed ${written.length} knowledge file(s) into ${Object.keys(byOwner).length} skill(s)`); diff --git a/.agents/skills/bpmn2agent-generate/scripts/render-mapping.mjs b/.agents/skills/bpmn2agent-generate/scripts/render-mapping.mjs index 7cab0b1..0991ca6 100644 --- a/.agents/skills/bpmn2agent-generate/scripts/render-mapping.mjs +++ b/.agents/skills/bpmn2agent-generate/scripts/render-mapping.mjs @@ -30,6 +30,14 @@ // orchestrator teal #E0F7FA / #006064 — gateway / loop-back / multi-instance control flow // human-checkpoint amber #FFFDE7 / #7A5C00 — userTask/manualTask -> AskUserQuestion step // artifact-contract brown #EFEBE9 / #4E342E — data object with a path/frontmatter contract +// context-source rose #F8BBD0 / #880E4F — data store (Art wissen|live|gedaechtnis) and, under +// the same colour, the process-wide input/output +// (kind workflow-input / workflow-output). A hue no +// other kind uses (it sat too close to the +// orchestrator teal before) and a stronger fill than +// the pale pink of unresolved; the annotation names +// the Art / the artifact. The legend entry (and its +// strings) only appears when a diagram has one. // not-generated grey #F5F5F5 / #616161 — deliberately not generated (reason on click/hover) // unresolved red #FFEBEE / #C62828 — also used for two extra red cases not in the // kind enum: an element with NO spec.elements @@ -53,6 +61,9 @@ // added to THAT sub-process's own `artifacts` array and its own BPMNPlane, not the top-level one // (see addAnnotation()/collectElements()) — otherwise bpmn-js would try to render them on the // wrong plane. +// - A `bpmn:DataStoreReference` is NOT exempt: with no `elements.` entry it shows red like any +// unmapped node (verify requires the entry too). Process `ioSpecification` dataInput/dataOutput are +// indexed like flow nodes (they are not flowElements) so workflow-input/-output get coloured. // - `bpmn:DataObject`/`bpmn:DataObjectReference` elements are excluded from the "no spec entry -> // red/unmapped" sweep (see classifyElement): the schema models an artifact TYPE under // `artifacts.`, not a 1:1 link to one specific dataObjectReference id, so most diagrams will @@ -90,13 +101,19 @@ const PALETTE = { orchestrator: { fill: '#E0F7FA', stroke: '#006064' }, 'human-checkpoint': { fill: '#FFFDE7', stroke: '#7A5C00' }, 'artifact-contract': { fill: '#EFEBE9', stroke: '#4E342E' }, + 'context-source': { fill: '#F8BBD0', stroke: '#880E4F' }, 'not-generated': { fill: '#F5F5F5', stroke: '#616161' }, unresolved: { fill: '#FFEBEE', stroke: '#C62828' }, }; const KIND_ORDER = [ 'agent-checklist', 'skill', 'script', 'hook', 'orchestrator', - 'human-checkpoint', 'artifact-contract', 'not-generated', 'unresolved', + 'human-checkpoint', 'artifact-contract', 'context-source', 'not-generated', 'unresolved', ]; +// Spec kinds that share another kind's colour bucket (and legend entry). +const COLOR_KEY_OF = { 'workflow-input': 'context-source', 'workflow-output': 'context-source' }; +// Legend entry, strings and annotation exist only when a diagram has such elements, so diagrams +// without data stores render byte-for-byte as before. +const OPTIONAL_KINDS = ['context-source', 'workflow-input', 'workflow-output']; // Both languages must define the same keys (the viewer reads them without fallbacks). const STRINGS = { @@ -178,9 +195,13 @@ const STRINGS = { orchestrator: 'Orchestrator', 'human-checkpoint': 'Human checkpoint', 'artifact-contract': 'Artifact contract', + 'context-source': 'Context source', + 'workflow-input': 'Process input', + 'workflow-output': 'Process output', 'not-generated': 'Not generated', unresolved: 'Unresolved', }, + artNames: { wissen: 'knowledge', live: 'live system', gedaechtnis: 'memory' }, kindHelp: { 'agent-checklist': 'A checklist item in the responsible agent.', skill: 'Its own skill that Claude runs for this step.', @@ -189,6 +210,9 @@ const STRINGS = { orchestrator: 'Control flow: decision, loop, parallelism — no file of its own.', 'human-checkpoint': 'A person decides here (question to the user).', 'artifact-contract': 'A document with a fixed storage location and format.', + 'context-source': 'A data store (knowledge, live system, memory) or the process input/output — no file of its own.', + 'workflow-input': 'The input the workflow needs to start.', + 'workflow-output': 'The result the workflow ends with.', 'not-generated': 'Deliberately left out, with a reason. Not an error.', unresolved: 'Not mapped or an open question — must be resolved.', }, @@ -271,9 +295,13 @@ const STRINGS = { orchestrator: 'Orchestrierung', 'human-checkpoint': 'Menschliche Prüfung', 'artifact-contract': 'Artefakt-Vertrag', + 'context-source': 'Kontextquelle', + 'workflow-input': 'Prozess-Eingabe', + 'workflow-output': 'Prozess-Ausgabe', 'not-generated': 'Nicht generiert', unresolved: 'Ungelöst', }, + artNames: { wissen: 'Wissen', live: 'Live-System', gedaechtnis: 'Gedächtnis' }, kindHelp: { 'agent-checklist': 'Ein Punkt in der Checkliste des zuständigen Agenten.', skill: 'Eigener Skill, den Claude für diesen Schritt ausführt.', @@ -282,6 +310,9 @@ const STRINGS = { orchestrator: 'Ablaufsteuerung: Entscheidung, Schleife, Parallelität — keine eigene Datei.', 'human-checkpoint': 'Hier entscheidet ein Mensch (Rückfrage an den Nutzer).', 'artifact-contract': 'Dokument mit festem Ablageort und Format.', + 'context-source': 'Datenspeicher (Wissen, Live-System, Gedächtnis) oder Prozess-Ein-/Ausgabe — keine eigene Datei.', + 'workflow-input': 'Eingabe, die der Workflow zum Start braucht.', + 'workflow-output': 'Ergebnis, mit dem der Workflow endet.', 'not-generated': 'Bewusst weggelassen, mit Begründung. Kein Fehler.', unresolved: 'Nicht gemappt oder offene Frage — muss geklärt werden.', }, @@ -296,8 +327,8 @@ main().catch((err) => { async function main() { const spec = yaml.load(readFileSync(specPath, 'utf8')); const lang = (spec.meta && spec.meta.language || 'en').toLowerCase().startsWith('de') ? 'de' : 'en'; - const strings = STRINGS[lang]; assertSameKeys(STRINGS.en, STRINGS.de, 'STRINGS'); + const fullStrings = STRINGS[lang]; const xml = readFileSync(bpmnPath, 'utf8'); const moddle = new BpmnModdle(); @@ -312,10 +343,11 @@ async function main() { // Classify every element the traversal found. const entries = {}; for (const [id, info] of index) { - const cls = classifyElement(id, info.bo, spec, strings); + const cls = classifyElement(id, info.bo, spec, fullStrings); if (!cls) continue; // excluded (unmapped data object with no explicit entry) entries[id] = { ...cls, id, bo: info.bo, containerBo: info.containerBo, topProcessId: info.topProcessId, subProcessChain: info.subProcessChain }; } + const strings = stringsFor(fullStrings, entries); // Colour + annotate. let usedColorExtension = false; @@ -386,6 +418,17 @@ function assertSameKeys(a, b, where) { } } +// Drops the context-source strings when no element uses them (see OPTIONAL_KINDS). +function stringsFor(base, entries) { + const used = new Set(Object.values(entries).flatMap((e) => [e.kind, e.colorKey])); + const out = { ...base, kindNames: { ...base.kindNames }, kindHelp: { ...base.kindHelp } }; + for (const k of OPTIONAL_KINDS) { + if (!used.has(k)) { delete out.kindNames[k]; delete out.kindHelp[k]; } + } + if (!used.has('context-source')) delete out.artNames; + return out; +} + function ensureYaml(dir) { mkdirSync(dir, { recursive: true }); if (!existsSync(path.join(dir, 'package.json'))) { @@ -409,6 +452,12 @@ function collectElements(definitions) { } function walk(container, topProcessId, chain) { + // ioSpecification dataInput/dataOutput are not flowElements; index them so the process-wide + // input/output can be coloured (and flagged red when the spec has no entry for them). + const io = container.ioSpecification; + for (const d of io ? [...(io.dataInputs || []), ...(io.dataOutputs || [])] : []) { + index.set(d.id, { bo: d, containerBo: container, topProcessId, subProcessChain: chain.slice() }); + } for (const fe of container.flowElements || []) { if (fe.$type === 'bpmn:SequenceFlow') continue; index.set(fe.id, { bo: fe, containerBo: container, topProcessId, subProcessChain: chain.slice() }); @@ -463,6 +512,13 @@ function classifyElement(id, bo, spec, strings) { reason: specEl.reason || null, generatedPaths: specEl.generatedPaths || [], }; + if (specEl.kind === 'context-source') { + const cs = Object.values(spec.contextSources || {}).find((c) => c.bpmnElement === id); + if (cs) base.detail = { art: cs.art, ort: cs.ort }; + } else if (specEl.kind === 'workflow-input' || specEl.kind === 'workflow-output') { + const io = spec.workflowIO && spec.workflowIO[specEl.kind === 'workflow-input' ? 'input' : 'output']; + if (io) base.detail = { artifact: io.artifact }; + } if (specEl.kind === 'unresolved') { return { ...base, statusLabel: 'unresolved', colorKey: 'unresolved' }; @@ -473,7 +529,7 @@ function classifyElement(id, bo, spec, strings) { if (specEl.kind === 'not-generated') { return { ...base, statusLabel: 'not-generated', colorKey: 'not-generated' }; } - return { ...base, statusLabel: specEl.kind, colorKey: specEl.kind }; + return { ...base, statusLabel: specEl.kind, colorKey: COLOR_KEY_OF[specEl.kind] || specEl.kind }; } // ── colouring ───────────────────────────────────────────────────────────── @@ -512,6 +568,12 @@ function annotationText(entry, strings) { const names = [...new Set(entry.generatedPaths.map(artifactName))]; return truncate(`${strings.kindNames[entry.kind] || entry.kind}: ${names.join(', ')}`, 120); } + const d = entry.detail; + if (d && d.art) { + const ort = d.ort ? ` · ${d.ort.type}${d.ort.ref ? `:${d.ort.ref}` : ''}` : ''; + return truncate(`${strings.kindNames[entry.kind]}: ${strings.artNames[d.art] || d.art}${ort}`, 120); + } + if (d && d.artifact) return truncate(`${strings.kindNames[entry.kind]}: ${d.artifact}`, 120); return null; } @@ -682,9 +744,10 @@ function buildHtml({ spec, strings, lang, mappedXml, entries, processInfos, outD } for (const l of levelById.values()) if (l.parentId && levelById.has(l.parentId)) levelById.get(l.parentId).children.push(l.id); + const kindOrder = KIND_ORDER.filter((k) => !OPTIONAL_KINDS.includes(k) || strings.kindNames[k]); const mappingData = {}; const filesByPath = new Map(); - const counts = Object.fromEntries(KIND_ORDER.map((k) => [k, 0])); + const counts = Object.fromEntries(kindOrder.map((k) => [k, 0])); const calls = {}; for (const [id, e] of Object.entries(entries)) { const files = (e.generatedPaths || []).map((p) => ({ path: p, href: relHref(outDir, p) })); @@ -735,7 +798,7 @@ function buildHtml({ spec, strings, lang, mappedXml, entries, processInfos, outD }; const filesList = [...filesByPath.values()].sort((a, b) => a.path.localeCompare(b.path)); const processList = processInfos.filter((p) => p.diagramId).map((p) => ({ id: p.id, name: p.name, diagramId: p.diagramId })); - const legend = KIND_ORDER.map((k) => ({ key: k, fill: PALETTE[k].fill, stroke: PALETTE[k].stroke })); + const legend = kindOrder.map((k) => ({ key: k, fill: PALETTE[k].fill, stroke: PALETTE[k].stroke })); // Agents for the highlight filter: every lane of the same role shares one agent (agentName). const agents = new Map(); @@ -764,7 +827,7 @@ function buildHtml({ spec, strings, lang, mappedXml, entries, processInfos, outD const BPMN_JS = 'https://cdn.jsdelivr.net/npm/bpmn-js@17.11.1/dist'; const viewerCss = readViewerAsset('viewer.css'); const viewerJs = readViewerAsset('viewer.js'); - const palette = KIND_ORDER.map((k) => ` --fill-${k}: ${PALETTE[k].fill};\n --stroke-${k}: ${PALETTE[k].stroke};`).join('\n'); + const palette = kindOrder.map((k) => ` --fill-${k}: ${PALETTE[k].fill};\n --stroke-${k}: ${PALETTE[k].stroke};`).join('\n'); const t = (key) => escapeHtml(strings[key]); return ` diff --git a/.agents/skills/bpmn2agent-knowledge/SKILL.md b/.agents/skills/bpmn2agent-knowledge/SKILL.md index a7d2bc9..dd95b7a 100644 --- a/.agents/skills/bpmn2agent-knowledge/SKILL.md +++ b/.agents/skills/bpmn2agent-knowledge/SKILL.md @@ -1,6 +1,6 @@ --- name: bpmn2agent-knowledge -description: Grounds the agents and skills to be generated from a BPMN diagram in domain knowledge. Maps Gemini notebooks (NotebookLM, via gemini-notebook-mcp) to lanes/tasks, uses them to challenge the diagram (gaps become open questions, never silent redesign) and extracts cited reference material per element. Falls back to WebSearch, reviewed repo docs, or model knowledge marked "unverified". Use after bpmn2agent-analyze wrote generated//workflow-spec.yaml, before bpmn2agent-design. +description: Grounds the agents and skills to be generated from a BPMN diagram in domain knowledge. Reads the diagram's knowledge data stores (else maps Gemini notebooks, NotebookLM via gemini-notebook-mcp, to lanes/tasks), resolves each store's Ort, uses the sources to challenge the diagram (gaps become open questions, never silent redesign) and extracts cited reference material per reading task. Falls back to WebSearch, reviewed repo docs, or model knowledge marked "unverified". Use after bpmn2agent-analyze wrote generated//workflow-spec.yaml, before bpmn2agent-design. bpmn: file: docs/planning/bpmn-to-agentic-workflow.bpmn elements: @@ -10,67 +10,104 @@ bpmn: # BPMN → Agent Knowledge Grounding Input: the spec `bpmn2agent-analyze` wrote. Output: the same spec with `knowledge:` and -`openQuestions:` filled, plus `generated//knowledge/*.md` per mapped element/lane. Later -stages read these; they don't query notebooks themselves. +`openQuestions:` filled, plus `generated//knowledge/*.md`: one file per reading task, or per +element/lane under the fallback (§3). Later stages read these and don't query notebooks. -Every question goes through `AskUserQuestion` with concrete options plus a recommendation, in the +The diagram is the source of truth: a `wissen` data store with an arrow into a task **is** the +mapping. Ask the user only where the diagram says nothing. + +Ask every question through `AskUserQuestion` with concrete options plus a recommendation, in the user's business language. ## 1. Read the spec Read `generated//workflow-spec.yaml`. Missing: **stop** and tell the user to run `bpmn2agent-analyze` first; never parse the `.bpmn` here. Its lanes/roles and elements are the -mapping targets and the units extraction writes one file per. +mapping targets. From `contextSources` take every store with `art: wissen`: its `name`, `ort` +(`{type, ref}`), `readers` and `bpmnElement`. Each reader is a **reading task**, the unit extraction +writes one file for. + +Skip `live` and `gedaechtnis` stores entirely (no extraction, no questions); `bpmn2agent-design` +resolves them. -## 2. Ask about notebooks +## 2. Resolve each store's Ort -Ask whether they have Gemini notebooks (NotebookLM) with knowledge the agents should have (process -handbooks, policies, prior write-ups, glossaries). Load the tools first: +Load the notebook tools once, when a `notebook:` store exists or step 3 needs them: ``` ToolSearch: select:mcp__gemini-notebook-mcp__notebook_list,mcp__gemini-notebook-mcp__notebook_describe,mcp__gemini-notebook-mcp__notebook_query,mcp__gemini-notebook-mcp__notebook_get ``` -Call `notebook_list` and offer the actual titles as options (plus "none of these" and a -recommendation), so the user picks by name, not UUID. On an auth/connection error, tell the user to -run `nlm login` in a terminal and ask whether to retry or continue without a notebook. Use -`notebook_describe` to sanity-check a pick before mapping. +Resolve by `ort.type`; ask nothing when the store resolves cleanly: + +- **`notebook`**: call `notebook_list`, match `ort.ref` against the titles (exact, then + case-insensitive). One match: take its id and sanity-check it with `notebook_describe`. No match: ask + which notebook is meant, offering the actual titles (plus "none of these" and a recommendation), + and tell the user to fix the `Ort:` line in the diagram. Auth/connection error: tell the user to run + `nlm login` in a terminal, then retry or treat the store as unresolved. +- **`url`**: `WebFetch` the page. `evidence: cited`, URL as the citation. +- **`datei`**: a local repo doc, cited per `references/notebook-extraction.md` §Citing local repo + docs. File missing: unresolved. +- **`websearch`**: targeted `WebSearch` queries per reading task. `evidence: cited`. +- **`mcp`**: only if the server's tools are connected (check with `ToolSearch` on + `mcp____`). Read through its read tools once and keep the result as a generation-time + snapshot, cited as server, tool and date. Not connected: unresolved. -Allow multiple notebooks. Ask which lanes/tasks each covers and record: +An **unresolved** store gets an `openQuestions` entry (`elementId` = the store reference id); never +drop it silently or downgrade it to model knowledge. Offer the user: fix and re-run, or knowingly use +a fallback from step 3 for its readers. + +Record the notebooks, with `mappedTo` taken from the store readers (task ids), not from a question: ```yaml knowledge: notebooks: - id: title: - mappedTo: [, ...] - mode: notebook | websearch | local-docs | unverified # see §3 + mappedTo: [, ...] + mode: notebook | websearch | local-docs | unverified # see step 3 ``` -## 3. No notebook → offer a fallback +## 3. Fallback for what the stores don't cover + +Covered: every reading task of a resolved `wissen` store. Not covered: -For lanes without a notebook, offer via `AskUserQuestion`. Recommend (d) if reviewed reference docs -for this domain exist in the repo, else (b) for generic domains, else (a). +- **a store with no `ort`** (analyze normally asks; if it slipped through, ask now), +- **a `serviceTask` (or `callActivity`/`businessRuleTask`) that reads no `wissen` store**, and +- **every task of a diagram with no `wissen` store at all.** +Each uncovered `serviceTask` gets an `openQuestions` entry: "No knowledge source is drawn for this +step. Intentional, or a missing source?" (`answer: null` until decided; never an error). Ask once per +lane (not per task) and offer: + +- **(0) Intentional, no knowledge needed** (the recommendation for tasks that only use `live` or + `gedaechtnis` stores): answer the open question, write no file. +- **Notebook**: call `notebook_list`, offer the actual titles (plus "none of these"), record it in + `knowledge.notebooks` with the lane/task ids as `mappedTo`. Several notebooks are fine. - **(a) WebSearch per lane** — targeted queries per lane/phase, cited, `evidence: cited`. - **(b) Model knowledge** — every claim `evidence: unverified`; never silently upgraded later. - **(c) Pause to create a notebook** — the user builds one and re-runs this skill. - **(d) Cite existing repo docs** — ask which reviewed file(s) apply per lane/element; `evidence: cited`. Format: `references/notebook-extraction.md` §Citing local repo docs. -`knowledge.mode`: `notebook` if every element has notebook coverage, else the fallback -(`websearch | local-docs | unverified`). Mixing is fine; record the fallback used for uncovered lanes -as `knowledge.mode` and name each lane's source in its `knowledge/*.md` `sources:`. +Otherwise recommend (d) if reviewed reference docs for this domain exist in the repo, else (b) for +generic domains, else (a). Diagrams without stores use only this flow; mixed diagrams use the stores +where drawn and this flow for the rest. + +`knowledge.mode`: `notebook` if every in-scope task has notebook coverage; otherwise the fallback in +use (`websearch | local-docs | unverified`; `url`/`websearch` stores count as `websearch`, `datei` as +`local-docs`). Mixing is fine; name each source in its file's `sources:`. ## 4. Challenge the BPMN -For each lane/phase, run the **challenge queries** from `references/notebook-extraction.md` -§Challenge queries against its source (notebook, WebSearch or repo file). Under (b), answer them -from model reasoning and mark every finding `unverified`; don't skip the step. +For each reading task (fallback: each lane/phase), run the **challenge queries** from +`references/notebook-extraction.md` §Challenge queries against its source (notebook, page, repo file or +WebSearch). Under (b), answer them from model reasoning and mark every finding `unverified`; don't skip +the step. -Only domain questions belong here. Queue agentic-design questions (patterns, agents, skills, +Ask only domain questions here. Queue agentic-design questions (patterns, agents, skills, checkpoints, hooks) as `openQuestions` entries with `source: agentic-design` and `answer: null` for -`bpmn2agent-design` step 6a instead of querying about them. +`bpmn2agent-design` step 6a; don't query about them. Turn every real finding into a question for the user; **never** change the design based on a source without asking. Record in the spec: @@ -91,37 +128,46 @@ loop-to-analyze path). Never edit the `.bpmn` or the spec's structural fields he `references/notebook-extraction.md` §FAQ log). On a re-run, read `knowledge/faq/README.md` first and re-ask only what it doesn't answer or what the changed diagram makes stale. -## 5. Extract reference material per element +## 5. Extract reference material per reading task -For each mapped element, run the **extraction queries** from `references/notebook-extraction.md` -§Extraction queries and write the distilled, cited result to -`generated//knowledge/.md`. Under (d), cite the repo file directly in -`knowledge.refs` or distill it (same reference, §Citing local repo docs). Under (b), still write one -file per in-scope element, `evidence: unverified` with inline callouts. +For each reading task write `generated//knowledge/.md` (the task's BPMN element id, +e.g. `Task_AntwortEntwerfen.md`) with **one section per `wissen` store it reads**, named with the +store's label verbatim, each with its own citations. Run the **extraction queries** from +`references/notebook-extraction.md` §Extraction queries against that store's source only; the store's +Ort picks the route (§Store-driven extraction there). Under the fallback (§3), write +`.md`; under (b), still one file per in-scope element, `evidence: unverified` with +inline callouts. -With many lanes × notebooks, extraction may run as parallel `Agent` calls, one per lane/notebook; -each writes its `knowledge/.md` and saves its raw query results for the FAQ log. No Workflow -script. +With many tasks × notebooks, extraction may run as parallel `Agent` calls, one per task; each writes its +`knowledge/.md` and saves its raw query results for the FAQ log. No Workflow script. -Frontmatter (incl. the required `bpmn:` key), citation format, evidence tiers, chunking of large -`notebook_query` output, target length and persistence of `knowledge/*.md`: see +Frontmatter (incl. the required `bpmn:` key), section layout, citation format, evidence tiers, chunking +of large `notebook_query` output, target length and persistence of `knowledge/*.md`: see `references/notebook-extraction.md`. -Record the refs: +Record the refs, keyed by element id (a lane under the fallback): ```yaml knowledge: refs: - : generated//knowledge/.md + : generated//knowledge/.md ``` ## 6. Hand off -Before finishing, confirm: every mapped element has a `knowledge.refs` entry or an explicit "no -coverage, mode: " note; every challenge finding has an `openQuestions` entry with an answer -(not null) or is flagged for the user to resolve before design; `source: agentic-design` entries -stay null, design answers them. `bpmn2agent-generate` copies -`knowledge/*.md` into skills' `references/` and agents' "Domain knowledge"; this skill does neither. +Before finishing, confirm: + +- every reading task of a resolved `wissen` store has a `knowledge.refs` entry and a cited section per + store; +- every other in-scope element has a `knowledge.refs` entry, an answered "intentional" open question, + or an explicit "no coverage, mode: " note; +- every `knowledge.notebooks[].mappedTo` lists the reading tasks; +- every challenge finding has an `openQuestions` entry with an answer (not null) or is flagged for the + user to resolve before design; +- `source: agentic-design` entries stay null; design answers them. + +`bpmn2agent-generate`, not this skill, copies `knowledge/*.md` into skills' `references/` and agents' +"Domain knowledge". ## Scripts diff --git a/.agents/skills/bpmn2agent-knowledge/references/notebook-extraction.md b/.agents/skills/bpmn2agent-knowledge/references/notebook-extraction.md index 28a3115..38afb7f 100644 --- a/.agents/skills/bpmn2agent-knowledge/references/notebook-extraction.md +++ b/.agents/skills/bpmn2agent-knowledge/references/notebook-extraction.md @@ -2,8 +2,9 @@ Detail for `bpmn2agent-knowledge` §4/§5. Load this when actually querying, not before. -Contents: Challenge queries · Extraction queries · Citing local repo docs · Citation format · -Evidence marking and frontmatter · Knowledge files persist · Chunking · FAQ log · Keep it short +Contents: Challenge queries · Extraction queries · Store-driven extraction · Citing local repo docs · +Citation format · Evidence marking and frontmatter · Knowledge files persist · Chunking · FAQ log · +Keep it short ## Challenge queries @@ -21,12 +22,13 @@ and the diagram's actual flow: - "What commonly goes wrong at **``** according to the sources — and does this diagram have a check or gate that would catch it?" -Scope each query to one activity/lane; whole-process queries get vague, uncited answers. Only the -resulting question goes into `openQuestions`, not the answer. +Scope each query to one activity/lane; whole-process queries get vague, uncited answers. Put only the +resulting question into `openQuestions`, not the answer. ## Extraction queries -Per element likely to become a skill, agent or gate/critic; skip elements likely to be dropped. +Per element likely to become a skill, agent or gate/critic; skip elements likely to be dropped. Run +only the relevant ones (a `scriptTask` rarely needs domain knowledge, a lane rarely needs a checklist). - **Skill procedure** (serviceTask/callActivity → reusable skill): "Describe the step-by-step procedure for `` as the sources document it — inputs, the ordered steps, and outputs." @@ -40,16 +42,61 @@ Per element likely to become a skill, agent or gate/critic; skip elements likely - **Terminology**: "What domain terms does `` use that a generic reader wouldn't already know, and how do the sources define them?" -Run only the ones relevant to the element (a `scriptTask` rarely needs domain knowledge, a lane -rarely needs a checklist). +## Store-driven extraction + +Per reading task of a `wissen` store (SKILL.md §1), run the relevant extraction queries against +**each store it reads, separately**; the store's `ort.type` picks the route: + +| `ort.type` | Route | Cited as | +|---|---|---| +| `notebook` | `notebook_query` on the notebook found by title (`new_conversation: true`, one question per call, log the FAQ) | notebook source name + quote | +| `url` | `WebFetch` the page, distill what answers the query | page title + URL | +| `datei` | §Citing local repo docs | file title + relative path | +| `websearch` | `WebSearch` queries scoped to the task | page title + URL | +| `mcp` | read through the server's read tools once; snapshot only | ``, tool name, date | + +All five yield `evidence: cited`. An unresolved store (title not found, server not connected, file +missing) gets no section; it is an open question instead, never filled from model knowledge. + +One file per reading task, `knowledge/.md`, one section per store, named with the store label +verbatim: + +```markdown +--- +element: Task_AntwortEntwerfen +evidence: cited +sources: ["Support-Handbuch"] +bpmn: + file: + elements: [Task_AntwortEntwerfen] +--- + +## Support-Handbuch + +_Ort: notebook:Support-Handbuch_ + +Procedure, checklist, terminology for this task, each claim cited [^1]. + +[^1]: "Support-Handbuch", NotebookLM source, queried 2026-10-03 — "short verbatim quote". +``` + +- `element` is the reading task's id; `bpmn.elements` lists only that id (the stores are named in the + section headings and `sources:`). +- `sources:` lists every store's source (notebook title, URL, repo path, server), in section order. + `evidence:` is the dominant tier over all sections. +- Footnotes form one list at the file bottom, numbered across sections. The citation format below + applies per section; a section without a footnote is not `cited`. +- Each section follows the length target (§Keep it short); scale it down when a task reads several + stores. ## Citing local repo docs (mode (d)) -No query to run; point at the reviewed file(s) instead. Per element: +For fallback (d) and `datei:` stores. Run no query; point at the reviewed file(s). Per element: - **Cite directly**: if the file already answers the extraction query briefly and on point (e.g. `bpmn2agent-design/references/mapping-rubric.md` for a mapping-decision element), set - `knowledge.refs.` to its repo-relative path and write no `knowledge/*.md`. + `knowledge.refs.` to its repo-relative path and write no `knowledge/*.md`. For a + `datei:` store only when it is the task's sole `wissen` store; otherwise distill it as a section. - **Distill**: if it is long, broader than this element, or needs combining with another source, write a normal `knowledge/.md` citing it like any source (file title instead of notebook name, relative path instead of URL). @@ -58,7 +105,7 @@ Both count as `evidence: cited`. ## Citation format -Every extracted claim keeps its source inline, not just in a bibliography: +Cite every extracted claim inline, not just in a bibliography: ```markdown Approvals above €10,000 require a second signer [^1]. @@ -71,20 +118,20 @@ Approvals above €10,000 require a second signer [^1]. sentences). Carry through the source names/snippets `notebook_query` returns. - One footnote list per file; numbering restarts per file. - WebSearch claims: page title and URL instead of the notebook source name. -- If two sources agree, cite both: `[^1][^2]`. If sources conflict, say so in the text ("sources - disagree: A says X, B says Y") rather than picking one silently. +- Two sources agree: cite both, `[^1][^2]`. Sources conflict: say so in the text ("sources + disagree: A says X, B says Y"); never pick one silently. ## Evidence marking and frontmatter | Tier | Frontmatter value | Meaning | |---|---|---| -| Cited | `cited` | Backed by at least one notebook or WebSearch citation (footnote present). | +| Cited | `cited` | Backed by at least one notebook, page, repo-file or snapshot citation (footnote present). | | Inferred | `inferred` | Reasoned from cited material or from the BPMN itself, no direct source claim. | | Unverified | `unverified` | Model knowledge only — no notebook, no search, mode (b) from SKILL.md §3. | Every `knowledge/*.md` file gets a frontmatter `evidence:` field naming its **dominant** tier -(strongest wins if mixed: `cited` > `inferred` > `unverified`), and every `unverified` claim inside -an otherwise-cited file gets an inline callout so it can't be mistaken for sourced material: +(strongest wins if mixed: `cited` > `inferred` > `unverified`). Every `unverified` claim inside an +otherwise-cited file gets this inline callout: ```markdown > ⚠ unverified — model knowledge, no source. Confirm before relying on this in production. @@ -114,7 +161,7 @@ turned out unneeded, and then also drop its `knowledge.refs` entry. ## Chunking / large-output handling -A `notebook_query` answer over the inline output limit is saved to a file. Don't read it whole: +A `notebook_query` answer over the inline output limit is saved to a file. Read it in chunks, not whole: ```bash jq -r '.answer' .json | head -c 4000 # first chunk diff --git a/.agents/skills/bpmn2agent-verify/SKILL.md b/.agents/skills/bpmn2agent-verify/SKILL.md index 9818268..d2c1a68 100644 --- a/.agents/skills/bpmn2agent-verify/SKILL.md +++ b/.agents/skills/bpmn2agent-verify/SKILL.md @@ -11,9 +11,9 @@ bpmn: # BPMN → Agent Verification Input: a `generated//` that `bpmn2agent-generate` wrote (fully, partly, or before the -`.bpmn` changed). This skill never edits anything; it says what is wrong, where the fix belongs, and -re-checks after the fix. Ask every question via `AskUserQuestion` with options and a -recommendation, in the user's language. +`.bpmn` changed). Never edit anything: say what is wrong and where the fix belongs, and re-check +after the fix. Ask every question via `AskUserQuestion` with options and a recommendation, in the +user's language. ## 1. Run `verify.mjs` @@ -23,9 +23,9 @@ node ${CLAUDE_SKILL_DIR}/scripts/verify.mjs \ generated/ ``` -Run it from the directory that contains `generated/` (the cwd the pipeline started in); it -resolves `spec.meta.sourceBpmn.path` against the cwd. Exit `0` → step 4. Otherwise at least one -category has a `FAIL`; continue to step 2. `--json` replaces the text report with structured output. +Run it from the directory that contains `generated/` (the pipeline's starting cwd); it resolves +`spec.meta.sourceBpmn.path` against the cwd. Exit `0` → step 4; otherwise (some category has a +`FAIL`) → step 2. `--json` replaces the text report with structured output. Categories, in run order: @@ -34,7 +34,9 @@ Categories, in run order: 3. **element-to-artifact** — every element/lane has a spec entry; every `generatedPaths` entry exists. 4. **artifact-to-element** — every file has a correct `bpmn:` header and is claimed by an element, bundled in a recorded skill/agent directory, listed in `knowledge.refs`, or is the one - orchestration file for `pattern.chosen`. + orchestration file for `pattern.chosen`, or a context hook + (`.claude/hooks/-write-guard.mjs` / `-memory-cap.mjs`) that a store in `contextSources` + needs: a live store with write tools, a memory store. 5. **lint** — scripts pass `node --check`; Workflow script `meta` is a literal and its body parses; `.claude/settings.json` registers exactly the `.claude/hooks/` scripts; installables sit inside `.claude/`, with no npm imports or `package.json` there (legacy layout: each hook @@ -42,6 +44,23 @@ Categories, in run order: 6. **no-red** — no `kind: unresolved`, no `openQuestions[]` with `answer: null`, and `mapping/workflow-mapped.bpmn` (if rendered) validates. +Plus **context-sources**, run after element-to-artifact only when the diagram has data stores or a +process `dataInput`/`dataOutput`, or the spec has `contextSources`/`workflowIO` (otherwise absent, +output unchanged). Each finding ends with `[fix in bpmn2agent-]`, and step 2 routes it +there: + +- every store and every real (`ioSpecification`) process input/output has an `elements.` entry + (`context-source` / `workflow-input` / `workflow-output`) and a `contextSources` entry / + `workflowIO` → design. A data object that only looks like an input or output by convention needs + one only if the user picked it; the rest stay plain artifacts. +- `contextSources.*.readers`/`writers` (and `art`, `ort.type`) match the diagram's arrows → analyze (stale spec) +- **write guard:** a task writing into a `live` store needs a `userTask` on **every** path from the + start (loops and sub-processes included: a loop's first pass must be guarded too) → analyze + (draw an approval step in the diagram) +- a live-store write inside a `workflow-script` phase (`pattern.chosen` or a `mixed` phase) → design +- `tools: unresolved` → design (warning) +- a `gedaechtnis` store without writer or reader in the spec → analyze + ## 2. Translate and route Translate each finding into business language, keeping the element label and file path. Group by @@ -59,13 +78,13 @@ mapping for just this part?"* Surface `WARN` findings too (e.g. a spec element with no current BPMN node usually means the `.bpmn` was edited without re-running analyze); they don't fail the run. -Generate-routed findings: hand them to `bpmn2agent-generate` without asking and report afterwards. -Design- and analyze-routed findings: ask once, one batched `AskUserQuestion` per verify pass. +Hand generate-routed findings to `bpmn2agent-generate` without asking and report afterwards. For +design- and analyze-routed findings, ask once: one batched `AskUserQuestion` per verify pass. ## 3. Loop -After the upstream skill reports its fix, re-run step 1 in full — a spec change can ripple into -files not yet regenerated. Repeat until exit `0`, at most 3 verify rounds per pipeline run. At the +After the upstream skill reports its fix, re-run step 1 in full (a spec change can ripple into +files not yet regenerated). Repeat until exit `0`, at most 3 verify rounds per pipeline run. At the cap, or when the same finding (category + file) returns unchanged twice, stop and ask: send the remaining findings to design, change the diagram (loop A), or accept a handoff marked **unverified** with the findings listed. Never report success at the cap. @@ -81,3 +100,5 @@ and `mapping/report.md` (plus `mapping/index.html` if rendered). - `scripts/verify.mjs > [--json]` — read-only, exit `0` iff no `fail` finding. Read its header comment before changing it. - `fixtures/approval-flow.bpmn` — smoke-test diagram. +- `fixtures/context-flow.bpmn` — one store per Art, a guarded live write, process input/output; + `context-flow-unguarded.bpmn` is the same without the approval step (verify must report the write guard). diff --git a/.agents/skills/bpmn2agent-verify/fixtures/context-flow-unguarded.bpmn b/.agents/skills/bpmn2agent-verify/fixtures/context-flow-unguarded.bpmn new file mode 100644 index 0000000..4e5d444 --- /dev/null +++ b/.agents/skills/bpmn2agent-verify/fixtures/context-flow-unguarded.bpmn @@ -0,0 +1,243 @@ + + + + + + + + + + DataInput_TicketId + + + + + + Start_TicketEingegangen + Task_TicketAnalysieren + Task_AntwortEntwerfen + Task_KommentarPosten + Task_LernnotizFortschreiben + End_Beantwortet + + + + Flow_1 + + + Input: Ticket-ID, Ticket aus Jira, Lernnotizen. Output: Analyse (Problem, Dringlichkeit). Quelle: ⚠ unverified + Flow_1 + Flow_2 + + + + + DataInput_TicketId + Property_Analysieren_1 + + + StoreRef_Jira + Property_Analysieren_2 + + + StoreRef_Lernnotizen + Property_Analysieren_3 + + + DataRef_Analyse + + + + + + Input: Analyse, Support-Handbuch. Output: Antwortentwurf. Quelle: ⚠ unverified + Flow_2 + Flow_3 + + + + DataRef_Analyse + Property_Entwerfen_1 + + + StoreRef_Handbuch + Property_Entwerfen_2 + + + + Input: freigegebener Antwortentwurf. Output: Antwortprotokoll. Quelle: ⚠ unverified + Flow_3 + Flow_5 + + StoreRef_Jira + + + DataRef_Protokoll + + + + + + Input: Ticket, Antwort. Output: aktualisierte Lernnotizen. Quelle: ⚠ unverified + Flow_5 + Flow_6 + + StoreRef_Lernnotizen + + + + Flow_6 + + + Art: wissen +Ort: notebook:Support-Handbuch + + + Art: live +Ort: mcp:atlassian + + + Art: gedächtnis +Ort: datei:.claude/memory/context-flow/lernnotizen-support.md + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.agents/skills/bpmn2agent-verify/fixtures/context-flow.bpmn b/.agents/skills/bpmn2agent-verify/fixtures/context-flow.bpmn new file mode 100644 index 0000000..b10952f --- /dev/null +++ b/.agents/skills/bpmn2agent-verify/fixtures/context-flow.bpmn @@ -0,0 +1,305 @@ + + + + + + + + + + DataInput_TicketId + + + + + + Task_AntwortFreigeben + Gw_Freigegeben + End_Verworfen + + + Start_TicketEingegangen + Task_TicketAnalysieren + Task_AntwortEntwerfen + Task_KommentarPosten + Task_LernnotizFortschreiben + End_Beantwortet + + + + Flow_1 + + + Input: Ticket-ID, Ticket aus Jira, Lernnotizen. Output: Analyse (Problem, Dringlichkeit). Quelle: ⚠ unverified + Flow_1 + Flow_2 + + + + + DataInput_TicketId + Property_Analysieren_1 + + + StoreRef_Jira + Property_Analysieren_2 + + + StoreRef_Lernnotizen + Property_Analysieren_3 + + + DataRef_Analyse + + + + + + Input: Analyse, Support-Handbuch. Output: Antwortentwurf. Quelle: ⚠ unverified + Flow_2 + Flow_3 + + + + DataRef_Analyse + Property_Entwerfen_1 + + + StoreRef_Handbuch + Property_Entwerfen_2 + + + + Input: Antwortentwurf. Output: Freigabeentscheidung. Quelle: ⚠ unverified + Flow_3 + Flow_4 + + + Flow_4 + Flow_Ja + Flow_Nein + + + Input: freigegebener Antwortentwurf. Output: Antwortprotokoll. Quelle: ⚠ unverified + Flow_Ja + Flow_5 + + StoreRef_Jira + + + DataRef_Protokoll + + + + + + Input: Ticket, Antwort. Output: aktualisierte Lernnotizen. Quelle: ⚠ unverified + Flow_5 + Flow_6 + + StoreRef_Lernnotizen + + + + Flow_6 + + + Flow_Nein + + + Art: wissen +Ort: notebook:Support-Handbuch + + + Art: live +Ort: mcp:atlassian + + + Art: gedächtnis +Ort: datei:.claude/memory/context-flow/lernnotizen-support.md + + + + + + + Ja + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.agents/skills/bpmn2agent-verify/scripts/verify.mjs b/.agents/skills/bpmn2agent-verify/scripts/verify.mjs index d807ea6..c073333 100644 --- a/.agents/skills/bpmn2agent-verify/scripts/verify.mjs +++ b/.agents/skills/bpmn2agent-verify/scripts/verify.mjs @@ -25,7 +25,7 @@ // Exit code: 0 iff every check category below has zero "fail"-level findings (warnings don't // block). 2 on a CLI usage error (bad args / not a directory). // -// ── What this checks (six categories, run in this order) ────────────────────────────────── +// ── What this checks (six categories, plus context-sources when applicable, run in this order) ────────────────────────────────── // 1. spec-schema workflow-spec.yaml parses as YAML and validates against // bpmn2agent-design/assets/workflow-spec.schema.yaml (ajv, draft-07). // 2. source-bpmn sha256 of the recorded meta.sourceBpmn.path matches @@ -36,6 +36,9 @@ // spec.elements entry; every BPMN lane has a roles.* entry; every // not-generated/unresolved element has a reason; every generatedPaths // entry for a skill/script/hook/artifact-contract element exists on disk. +// 3b. context-sources (only when the diagram has data stores / ioSpecification input-output, or the +// spec has contextSources / workflowIO; otherwise the category does not appear +// and output is identical to older versions). See the block below. // 4. artifact-to-element every file actually on disk under (except a short, // documented exception list) carries a valid bpmn header whose `file` // matches the spec's source .bpmn and whose `elements` are real spec @@ -136,8 +139,9 @@ if (!existsSync(generatedDir) || !statSync(generatedDir).isDirectory()) { const categories = []; function category(id, title) { const cat = { id, title, findings: [] }; - cat.fail = (message) => cat.findings.push({ level: 'fail', message }); - cat.warn = (message) => cat.findings.push({ level: 'warn', message }); + // `route` (optional): the pipeline stage the fix belongs to (analyze | design | generate); SKILL.md §2. + cat.fail = (message, route) => cat.findings.push({ level: 'fail', message, ...(route ? { route } : {}) }); + cat.warn = (message, route) => cat.findings.push({ level: 'warn', message, ...(route ? { route } : {}) }); cat.info = (message) => cat.findings.push({ level: 'info', message }); categories.push(cat); return cat; @@ -456,6 +460,10 @@ if (!spec || !inventory) { } const flowNodeIds = new Set((inventory.flowNodes || []).map((n) => n.id)); + // data stores and process input/output are real diagram elements too (spec kinds context-source / + // workflow-input / workflow-output), they just are not flow nodes + for (const d of inventory.dataStores || []) flowNodeIds.add(d.id); + for (const io of [...(inventory.processIO?.inputs || []), ...(inventory.processIO?.outputs || [])]) flowNodeIds.add(io.id); const needsGeneratedPaths = new Set(['skill', 'script', 'hook', 'artifact-contract']); const payloadKinds = new Set(['skill', 'script', 'hook']); @@ -514,6 +522,272 @@ if (!spec || !inventory) { } } +// --------------------------------------------------------------------------------------------- +// 3b. context-sources — data stores, process input/output, write guard (plan: docs/plans/kontextquellen) +// Only created when there is something to check, so specs/diagrams without stores or ioSpecification +// verify exactly as before. Each finding carries the route (analyze | design) the fix belongs to. +// (1) every store / process-IO element has elements. (kind context-source / workflow-input / +// workflow-output) and a contextSources entry (by bpmnElement) / workflowIO -> design +// (2) contextSources.*.readers/writers (and art/ort type) match the diagram -> analyze (stale spec) +// (3) WRITE GUARD: a task that writes into a live store must be preceded by a userTask on EVERY +// sequence-flow path from the start -> analyze (diagram) +// (4) live-store write inside a workflow-script phase -> design +// (5) tools: unresolved -> design (warning) +// (6) gedaechtnis store without writer or reader in the spec -> analyze +// --------------------------------------------------------------------------------------------- +const ioInventory = inventory?.processIO || { inputs: [], outputs: [] }; +const ctxDataStores = inventory?.dataStores || []; +const hasIoSpecEntries = [...ioInventory.inputs, ...ioInventory.outputs].some((io) => io.source === 'ioSpecification'); +const ctxNewStyle = !!(spec && (spec.contextSources || spec.workflowIO || ctxDataStores.length || hasIoSpecEntries)); +if (spec && inventory && ctxNewStyle) { + const ctxCat = category('context-sources', 'Context sources (data stores, process input/output, write guard)'); + const specElements = spec.elements || {}; + const nodeById = new Map((inventory.flowNodes || []).map((n) => [n.id, n])); + const labelOf = (id) => nodeById.get(id)?.name || specElements[id]?.label || id; + const ctxSources = spec.contextSources || {}; + const sourcesByElement = new Map(); // bpmnElement -> [[key, entry], ...] + for (const [key, cs] of Object.entries(ctxSources)) { + if (!sourcesByElement.has(cs.bpmnElement)) sourcesByElement.set(cs.bpmnElement, []); + sourcesByElement.get(cs.bpmnElement).push([key, cs]); + } + const sameSet = (a, b) => a.length === b.length && a.every((x) => b.includes(x)); + const names = (ids) => ids.map((i) => `"${labelOf(i)}"`).join(', '); + + // (1) + (2) per data store ------------------------------------------------------------- + for (const store of ctxDataStores) { + const el = specElements[store.id]; + if (!el) { + ctxCat.fail(`Data store "${store.name || store.id}" (${store.id}) has no entry in spec.elements — it must be kind: context-source`, 'design'); + } else if (el.kind !== 'context-source') { + ctxCat.fail(`elements.${store.id} ("${store.name || store.id}") is kind: ${el.kind}, but the diagram draws it as a data store — it must be kind: context-source`, 'design'); + } + const entries = sourcesByElement.get(store.id) || []; + if (!entries.length) { + ctxCat.fail(`Data store "${store.name || store.id}" (${store.id}) has no contextSources entry (bpmnElement: ${store.id}) — the spec does not say what kind of source it is or how it is reached`, 'design'); + } + for (const [key, cs] of entries) { + if (cs.name !== store.name) ctxCat.warn(`contextSources.${key}.name is "${cs.name}", the diagram label is "${store.name}" (labels stay verbatim)`, 'analyze'); + if (store.art && cs.art !== store.art) { + ctxCat.fail(`Data store "${store.name}": the diagram says Art "${store.art}", contextSources.${key}.art says "${cs.art}" — the spec is out of date`, 'analyze'); + } + if (store.ort && cs.ort?.type !== store.ort.type) { + ctxCat.fail(`Data store "${store.name}": the diagram says Ort type "${store.ort.type}", contextSources.${key}.ort.type says "${cs.ort?.type}" — the spec is out of date`, 'analyze'); + } else if (store.ort && store.ort.ref && cs.ort?.ref && cs.ort.ref !== store.ort.ref) { + ctxCat.warn(`Data store "${store.name}": the diagram says Ort "${store.ort.ref}", contextSources.${key}.ort.ref says "${cs.ort.ref}"`, 'analyze'); + } + const specReaders = cs.readers || []; + const specWriters = cs.writers || []; + for (const [role, specIds, diagramIds] of [['readers', specReaders, store.readers || []], ['writers', specWriters, store.writers || []]]) { + if (!sameSet(specIds, diagramIds)) { + const missing = diagramIds.filter((i) => !specIds.includes(i)); + const extra = specIds.filter((i) => !diagramIds.includes(i)); + ctxCat.fail( + `Data store "${store.name}": contextSources.${key}.${role} does not match the arrows in the diagram` + + `${missing.length ? ` — the diagram also has ${names(missing)}` : ''}${extra.length ? ` — the spec lists ${names(extra)}, which the diagram does not connect` : ''}; ` + + `the spec is out of date`, + 'analyze' + ); + } + } + } + } + // spec entries for stores the diagram no longer has + const diagramStoreIds = new Set(ctxDataStores.map((d) => d.id)); + for (const [key, cs] of Object.entries(ctxSources)) { + if (!diagramStoreIds.has(cs.bpmnElement)) { + ctxCat.fail(`contextSources.${key} ("${cs.name}") points at ${cs.bpmnElement}, which is not a data store in the diagram — the spec is out of date`, 'analyze'); + } + } + + // (1) process input/output ------------------------------------------------------------ + const io = spec.workflowIO; + for (const [dir, list, kind] of [['input', ioInventory.inputs, 'workflow-input'], ['output', ioInventory.outputs, 'workflow-output']]) { + // A real ioSpecification entry is always the process input/output and needs its element entry. + // A data-object-by-convention entry is only a candidate: the user picks at most one per direction + // (bpmn2agent-analyze step 5) and the spec marks that one with the workflow-* kind; every other + // candidate stays an ordinary artifact contract and is not checked here. Convention entries only + // count once the spec uses the new vocabulary at all (ctxNewStyle). + const real = list.filter((i) => i.source === 'ioSpecification'); + const chosenByConvention = list.filter((i) => i.source !== 'ioSpecification' && specElements[i.id]?.kind === kind); + for (const item of real) { + const el = specElements[item.id]; + if (!el) { + ctxCat.fail(`Process ${dir} "${item.name || item.id}" (${item.id}) has no entry in spec.elements — it must be kind: ${kind}`, 'design'); + } else if (el.kind !== kind) { + ctxCat.fail(`elements.${item.id} ("${item.name || item.id}") is kind: ${el.kind}, but it is the process ${dir} — it must be kind: ${kind}`, 'design'); + } + } + if ((real.length || chosenByConvention.length) && !io?.[dir]) { + const named = [...real, ...chosenByConvention]; + ctxCat.fail(`The diagram has a process ${dir} (${named.map((i) => `"${i.name || i.id}"`).join(', ')}) but the spec has no workflowIO.${dir}`, 'design'); + } + if (!list.length && io?.[dir]) { + ctxCat.fail(`spec.workflowIO.${dir} exists, but the diagram has no process ${dir} — the spec is out of date`, 'analyze'); + } + const art = io?.[dir]?.artifact; + if (art && !(spec.artifacts || {})[art]) { + ctxCat.fail(`workflowIO.${dir}.artifact "${art}" is not an entry in spec.artifacts`, 'design'); + } + } + + // (5) unresolved tools, (6) memory needs a writer and a reader ------------------------ + for (const [key, cs] of Object.entries(ctxSources)) { + if (cs.tools === 'unresolved') { + ctxCat.warn(`Data store "${cs.name}": its tools could not be resolved (source "${cs.ort?.type}${cs.ort?.ref ? ':' + cs.ort.ref : ''}" not reachable when the mapping was made) — no tool access will be generated for it; connect it and redo the mapping for this store`, 'design'); + } + if (cs.art === 'gedaechtnis') { + const missingParts = [!(cs.writers || []).length && 'step that writes it', !(cs.readers || []).length && 'step that reads it'].filter(Boolean); + if (missingParts.length) { + ctxCat.fail(`Memory store "${cs.name}" (contextSources.${key}) has no ${missingParts.join(' and no ')} in the spec — a memory only makes sense if one step learns into it and another reads it; draw the missing arrow`, 'analyze'); + } + } + } + + // (3) + (4) live-store writes --------------------------------------------------------- + const liveWrites = []; // { storeName, writerId } + const liveSeen = new Set(); + const addLiveWrite = (storeName, writerId) => { + if (liveSeen.has(storeName + '|' + writerId)) return; + liveSeen.add(storeName + '|' + writerId); + liveWrites.push({ storeName, writerId }); + }; + for (const store of ctxDataStores) { + // the spec's art wins (it is what design confirmed); the diagram's is the fallback + const entries = sourcesByElement.get(store.id) || []; + const art = entries[0]?.[1]?.art ?? store.art; + if (art !== 'live') continue; + for (const w of store.writers || []) addLiveWrite(store.name || store.id, w); + } + + if (liveWrites.length) { + // Flow graph over ALL scopes, flattened: a sub-process (or a callActivity whose calledElement is a + // process in this file) is entered at its inner start events and left from its inner end + // events, so a userTask nested inside a sub-process guards the paths that run through it, and an + // unguarded inner path is found like any other. A container node C is split into the entry + // node C and a pseudo exit node "C#exit"; plain sequence flows leave from the exit node. + // Boundary events: for a host that is not a userTask the edge host -> boundary event is added + // (so the handler is as guarded as the host's entry); for a userTask host the boundary event hangs + // off the host's predecessors (the approval has not been given when the boundary fires). For a + // sub-process host the boundary hangs off its ENTRY: conservative, it may over-report a handler + // that can only fire after an inner approval. Event sub-processes have no incoming flow, so + // their inner start events count as extra roots. + // Loops: "guarded" is computed as reachability from the roots in the graph in which userTask + // nodes are not expanded (a reached userTask stops the walk). A write task reached that way has at + // least one userTask-free path from a start, whatever the cycles look like; a BFS with a visited set + // terminates on cycles and a path re-entering through a loop-back edge is not mistaken for a guarded one. + // A writer that is itself a userTask counts as human-performed and is not flagged. + const scopeById = new Map((inventory.scopes || []).map((sc) => [sc.id, sc])); + const flowNodes = inventory.flowNodes || []; + const innerScopeOf = (n) => { + if (n.bpmnType === 'bpmn:SubProcess' || n.bpmnType === 'bpmn:Transaction') return scopeById.has(n.id) ? n.id : null; + if (n.bpmnType === 'bpmn:CallActivity' && n.calledElement && scopeById.has(n.calledElement)) return n.calledElement; + return null; + }; + const exitId = (n) => (innerScopeOf(n) ? `${n.id}#exit` : n.id); + const adj = new Map(); + const addEdge = (a, b) => { + if (!adj.has(a)) adj.set(a, new Set()); + adj.get(a).add(b); + }; + const inScope = (scopeId) => flowNodes.filter((n) => n.scopeId === scopeId && n.bpmnType !== 'bpmn:BoundaryEvent'); + const entriesOf = (scopeId) => { + const nodes = inScope(scopeId); + const starts = nodes.filter((n) => n.bpmnType === 'bpmn:StartEvent'); + if (starts.length) return starts; + return nodes.filter((n) => !(n.incoming || []).length && !scopeById.get(n.id)?.triggeredByEvent); + }; + const endsOf = (scopeId) => inScope(scopeId).filter((n) => !(n.outgoing || []).length); + const preds = new Map(); // node id -> predecessor ids (for userTask boundary events) + for (const sf of inventory.sequenceFlows || []) { + const src = nodeById.get(sf.source); + if (!src) continue; + addEdge(exitId(src), sf.target); + if (!preds.has(sf.target)) preds.set(sf.target, []); + preds.get(sf.target).push(exitId(src)); + } + const roots = []; + for (const n of flowNodes) { + const inner = innerScopeOf(n); + if (inner) { + const entries = entriesOf(inner); + for (const e of entries) addEdge(n.id, e.id); + const ends = endsOf(inner); + for (const e of ends) addEdge(exitId(e), `${n.id}#exit`); + if (!entries.length) addEdge(n.id, `${n.id}#exit`); + } + for (const b of n.attachedBoundaryEvents || []) { + if (n.bpmnType === 'bpmn:UserTask') { + if ((preds.get(n.id) || []).length) for (const p of preds.get(n.id)) addEdge(p, b); + else roots.push(b); + } else { + addEdge(n.id, b); + } + } + } + const calledProcessIds = new Set(flowNodes.filter((n) => n.bpmnType === 'bpmn:CallActivity' && n.calledElement && scopeById.has(n.calledElement)).map((n) => n.calledElement)); + for (const sc of inventory.scopes || []) { + if (sc.scopeType === 'process' && !calledProcessIds.has(sc.id)) for (const e of entriesOf(sc.id)) roots.push(e.id); + else if (sc.triggeredByEvent) for (const e of entriesOf(sc.id)) roots.push(e.id); + } + const isUserTask = (id) => nodeById.get(id)?.bpmnType === 'bpmn:UserTask'; + const parent = new Map(); + const queue = []; + for (const r of roots) if (!parent.has(r)) { parent.set(r, null); queue.push(r); } + while (queue.length) { + const cur = queue.shift(); + if (isUserTask(cur)) continue; // an approval step: everything behind it is guarded on this route + for (const nxt of adj.get(cur) || []) { + if (parent.has(nxt)) continue; + parent.set(nxt, cur); + queue.push(nxt); + } + } + const pathTo = (id) => { + const out = []; + for (let cur = id; cur != null; cur = parent.get(cur)) if (!cur.endsWith('#exit')) out.push(labelOf(cur)); + return out.reverse(); + }; + for (const { storeName, writerId } of liveWrites) { + if (!parent.has(writerId) || isUserTask(writerId)) continue; + ctxCat.fail( + `"${labelOf(writerId)}" writes into the live system "${storeName}" without a human approval first: ` + + `on at least one route from the start (${pathTo(writerId).map((l) => `"${l}"`).join(' → ')}) nobody has approved before the write happens. ` + + `Draw an approval step (a user task) in front of "${labelOf(writerId)}" that every route to it passes — and that a loop cannot skip on its first pass.`, + 'analyze' + ); + } + + // (4) a live write may not sit in a workflow-script phase (that pattern has no human checkpoints) + const chosen = spec.pattern?.chosen; + const ancestors = (id) => { + const out = [id]; + for (let sc = scopeById.get(nodeById.get(id)?.scopeId); sc; sc = scopeById.get(sc.parentScopeId)) out.push(sc.id); + return out; + }; + for (const { storeName, writerId } of liveWrites) { + let where = null; + if (chosen === 'workflow-script') where = 'the workflow is built as a workflow script'; + else if (chosen === 'mixed') { + const ph = (spec.pattern.phases || []).find((p) => p.pattern === 'workflow-script' && ancestors(writerId).some((a) => (p.elements || []).includes(a))); + if (ph) where = `its phase "${ph.name}" is built as a workflow script`; + } + if (where) { + ctxCat.fail( + `"${labelOf(writerId)}" writes into the live system "${storeName}", but ${where} — that pattern cannot pause for a human approval. ` + + `Choose skill chain with hooks or an orchestrator agent for this step.`, + 'design' + ); + } + } + } + if (!ctxCat.findings.length) { + ctxCat.info( + `${ctxDataStores.length} data store(s), ${ioInventory.inputs.length} process input(s), ${ioInventory.outputs.length} process output(s) traced; ` + + `${liveWrites.length} live-store write(s) guarded by an approval` + ); + } +} + // --------------------------------------------------------------------------------------------- // 4. artifact-to-element // --------------------------------------------------------------------------------------------- @@ -563,6 +837,18 @@ if (!spec) { // claude-dir: .claude/settings.json must reference exactly the hook scripts under .claude/hooks/. const hookScripts = allFiles.filter((f) => f.startsWith('.claude/hooks/') && f.endsWith('.mjs')); + // Context hooks (bpmn2agent-generate step 6a) belong to stores, not to one element's generatedPaths, + // like the top-level orchestration file. They count as explained only at the exact expected path + // AND only when the spec has a store that needs them (live store with write tools / memory store). + const contextHookPaths = new Set(); + if (workflowName && outputLayout === 'claude-dir') { + const stores = Object.values(spec.contextSources || {}); + const hasLiveWrite = stores.some( + (s) => s.art === 'live' && s.tools && s.tools !== 'unresolved' && (s.tools.write || []).length + ); + if (hasLiveWrite) contextHookPaths.add(`.claude/hooks/${workflowName}-write-guard.mjs`); + if (stores.some((s) => s.art === 'gedaechtnis')) contextHookPaths.add(`.claude/hooks/${workflowName}-memory-cap.mjs`); + } const expectedTopLevelPathSet = new Set(expectedTopLevelPaths); const topLevelBudget = patternChosen === 'mixed' ? Infinity : 1; let topLevelUsed = 0; @@ -661,7 +947,7 @@ if (!spec) { const claimingSet = claimedBy.get(relPath) || new Set(); const underRoot = [...skillRoots].some((root) => relPath === root || relPath.startsWith(root + '/')); - const budgetExempt = relPath === 'README.md' || relPath === 'mapping/report.md'; + const budgetExempt = relPath === 'README.md' || relPath === 'mapping/report.md' || contextHookPaths.has(relPath); if (unsupportedType) { artCat.warn(`"${relPath}": unknown file type, no bpmn header convention defined for it — header not checked`); @@ -799,7 +1085,7 @@ if (jsonMode) { if (cat.findings.length === 0) console.log(' (no findings)'); for (const f of cat.findings) { const lines = f.message.split('\n'); - console.log(` ${icon[f.level] || '-'} ${lines[0]}`); + console.log(` ${icon[f.level] || '-'} ${lines[0]}${f.route ? ` [fix in bpmn2agent-${f.route}]` : ''}`); for (const extra of lines.slice(1)) console.log(` ${extra}`); } } diff --git a/.agents/skills/grilling/SKILL.md b/.agents/skills/grilling/SKILL.md new file mode 100644 index 0000000..8ca78c6 --- /dev/null +++ b/.agents/skills/grilling/SKILL.md @@ -0,0 +1,28 @@ +--- +name: grilling +description: Grill the user relentlessly about a plan, decision, or idea. Use when the user wants to stress-test their thinking, or uses any 'grill' trigger phrases. +--- + +Interview the user relentlessly until you reach a shared understanding. Map this as a **design tree**: every decision branches into the decisions that hang off it. + +Work the tree in **rounds**. The **frontier** is every decision whose prerequisites are already settled: the questions you can ask _now_ without guessing at answers you haven't heard yet. Ask the whole frontier in one round: number each question and give your recommended answer. Then wait for the user's answers before the next round. + +Format a round like so: + +``` +❓ **Q1** - ****: + +➡️ + +--- + +❓ **Q2** - ****: + +➡️ +``` + +Each round the user answers reshapes the tree: settled decisions push the frontier outward and unblock questions that depended on them. Recompute the frontier and ask the next round. A question whose answer depends on another question still open in this round belongs to a _later_ round, not this one. + +Finding _facts_ is your job, never the user's. When a frontier question needs a fact from the environment (filesystem, tools, etc.), dispatch a sub-agent to find it; don't ask the user for anything you could look up yourself. Don't block on it: a running exploration is an unsettled prerequisite, so only the questions downstream of it wait for the sub-agent to report; ask the rest of the frontier now. The _decisions_ are the user's: put each to them and wait. + +The session is done when the frontier is empty: every branch of the design tree visited, nothing left silently assumed. Do not act on it until the user confirms you have reached a shared understanding. diff --git a/.agents/skills/grilling/agents/openai.yaml b/.agents/skills/grilling/agents/openai.yaml new file mode 100644 index 0000000..ddbdb96 --- /dev/null +++ b/.agents/skills/grilling/agents/openai.yaml @@ -0,0 +1,3 @@ +interface: + display_name: "Grilling" + short_description: "Stress-test thinking a round of questions at a time" diff --git a/.claude/skills/grilling b/.claude/skills/grilling new file mode 120000 index 0000000..e712452 --- /dev/null +++ b/.claude/skills/grilling @@ -0,0 +1 @@ +../../.agents/skills/grilling \ No newline at end of file diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index f0ad891..df79b79 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -62,9 +62,9 @@ erzeugt die Pipeline weiterhin als *Ergebnis*, wenn das gezeichnete Diagramm daz |---|---|---|---| | 0 | `bpmn-process-design` (optional) | Ziel des Anwenders, NotebookLM | das `.bpmn` in lanecraft-Notation, `knowledge/faq/` | | 0 | `bpmn-authoring` | – | das `.bpmn` von Hand (XSD, bpmn-moddle, bpmnlint, Layout) | -| 1 | `bpmn2agent-analyze` | `.bpmn` | `workflow-spec.yaml` als Entwurf, jedes Element `kind: unresolved` | -| 2 | `bpmn2agent-knowledge` | Spec, NotebookLM / Web | `knowledge:`, `openQuestions:`, `knowledge/*.md`, `knowledge/faq/` | -| 3 | `bpmn2agent-design` | Spec, Rubriken | bestätigte Spec: `kind`, Pfade, Muster, Rollen, Artefakte | +| 1 | `bpmn2agent-analyze` | `.bpmn` | `workflow-spec.yaml` als Entwurf, jedes Element `kind: unresolved`; Stores und Prozess-Ein-/Ausgabe als `contextSources` / `workflowIO` | +| 2 | `bpmn2agent-knowledge` | Spec, NotebookLM / Web, Wissens-Stores | `knowledge:`, `openQuestions:`, `knowledge/*.md`, `knowledge/faq/` | +| 3 | `bpmn2agent-design` | Spec, Rubriken | bestätigte Spec: `kind`, Pfade, Muster, Rollen, Artefakte, aufgelöste Tools der Live-Stores | | 4 | `bpmn2agent-generate` | bestätigte Spec, Vorlagen | alle Dateien unter `generated//` | | 5 | `bpmn2agent-verify` | `generated//`, `.bpmn` | nichts; Bericht in sechs Kategorien | @@ -83,6 +83,8 @@ Alle Stufen reden nur über `generated//workflow-spec.yaml` miteinande | `elements` | ein Eintrag je BPMN-Element: `kind`, `lane`, `generatedPaths`, `reason`, `gate` (Kriterien, `maxLoops`), `knowledge` | | `artifacts` | ein Vertrag je Datenobjekt: Pfadmuster, Frontmatter-Felder, Erzeuger, Verbraucher | | `pattern` | gewähltes Orchestrierungsmuster, Begründung, verworfene Alternativen, ggf. Phasen | +| `contextSources` | ein Eintrag je Data Store: `art`, `ort`, `readers`, `writers`, bei `live` die aufgelösten `tools`, bei `gedaechtnis` `memory` (siehe [Kontextquellen](#kontextquellen)) | +| `workflowIO` | Eingabe des ganzen Prozesses (Artefakt, Pflichtfelder) und sein Endergebnis | | `knowledge` | Notebooks je Lane/Element, Modus (`notebook`, `websearch`, `local-docs`, `unverified`), `refs` | | `openQuestions` | Befunde aus der Wissensprüfung mit Antwort des Anwenders | @@ -100,6 +102,8 @@ Alle Stufen reden nur über `generated//workflow-spec.yaml` miteinande | Gateway, Schleife, Start/Ende | Steuerlogik im Orchestrierungsmuster, keine eigene Datei | | aufgeklappter Teilprozess / Call Activity | wiederverwendbarer Skill oder eigener Teil-Workflow | | Datenobjekt | Artefaktvertrag | +| Data Store | Kontextquelle (`contextSources`), je nach `Art:` Wissensreferenz, Tool-Freigabe oder Gedächtnisdatei | +| prozessweite Ein-/Ausgabe | `workflowIO`; die Eingabe wird zum `argument-hint` des Orchestrators | `pattern-rubric.md` wählt aus Signalen des Diagramms (menschliche Aufgaben, Parallelität, Mehrfachinstanzen, Schleifen, Urteils-Gateways) eines von vier Mustern: @@ -119,14 +123,75 @@ Event-Teilprozesse, Kompensation) meldet analyze mit einem Umbauvorschlag (`bpmn2agent-analyze/references/unsupported.md`). Bleibt der Anwender dabei, wird das Element als bewusste Lücke `not-generated`. +## Kontextquellen + +Kontext ist am Ende Daten. Wer einen Prozess modelliert, überlegt deshalb beim Zeichnen, **welche +Daten jeder Schritt braucht und wo sie liegen**. Daraus baut die Pipeline Wissensreferenzen, +Tool-Freigaben, Schreibschutz und ein Gedächtnis über Läufe hinweg. Das Detaildesign steht in +[`docs/plans/kontextquellen/plan.md`](docs/plans/kontextquellen/plan.md). + +**Notation.** Jede dauerhafte Quelle ist ein Data Store (Zylinder), benannt als fachlicher +Datenbestand („Jira-Tickets Projekt LANE“), nicht als System. Seine Dokumentation trägt zwei Zeilen: + +```text +Art: wissen | live | gedächtnis +Ort: notebook: | mcp: | cli: | | datei: | websearch +``` + +Lesen oder Schreiben ergibt sich nur aus der Pfeilrichtung: Store → Task liest, Task → Store +schreibt. Es gibt keine `Zugriff:`-Zeile. Fehlt eine Zeile oder passt die Kombination nicht, fragt +analyze nach, die Pipeline rät nie. (`Ort:` statt `Quelle:`, weil die Task-Dokumentation `Quelle:` +schon für die Herkunft belegt.) + +| Art | erlaubter Ort | geladen | wird zu | +|---|---|---|---| +| `wissen` | `notebook:`, URL, `datei:`, `websearch` (`mcp:` nur als Snapshot bei verbundenem Server) | bei der Generierung | `knowledge/.md`, dann `references/` des Skills | +| `live` | `mcp:`, `cli:` | zur Laufzeit | Tool-Allowlist + `## Kontextquellen` im Skill | +| `live` | `notebook:` (wie `mcp:gemini-notebook-mcp`), URL, `datei:` | zur Laufzeit | Tool-Allowlist bzw. `WebFetch` / `Read` | +| `gedächtnis` | `datei:` (Standard `.claude/memory//.md`) | zu Laufbeginn gelesen, am Ende geschrieben | kuratierte Datei + Cap-Hook | + +**Weg durch die Stufen.** + +| Stufe | Was mit den Stores passiert | +|---|---| +| 0 process-design | fragt je Schritt „Was muss er wissen, und wo liegt es?“ und zeichnet die Stores samt `Art:`/`Ort:` | +| 1 analyze | inventarisiert Stores und Prozess-Ein-/Ausgabe nach `contextSources` / `workflowIO`, fragt bei fehlenden oder unpassenden Zeilen nach | +| 2 knowledge | extrahiert für jeden lesenden Task aus genau den Wissens-Stores seiner Pfeile, eine Datei `knowledge/.md` mit einem Abschnitt je Store; ohne Wissens-Store gilt die alte Lane-Zuordnung | +| 3 design | löst die Tools der Live-Stores per ToolSearch auf (lesen/schreiben nach Name oder `readOnlyHint`, unklar = schreiben), platziert sie (`tools:` je Lane-Agent beim Orchestrator-Agent, sonst `permissions.allow`) und legt alles im Mapping-Plan zur Bestätigung vor | +| 4 generate | schreibt `## Kontextquellen`-Abschnitte, `permissions.allow`, den Write-Guard-Hook (`ask`), den Memory-Cap-Hook und das `argument-hint` des Orchestrators | +| 5 verify | prüft in einer eigenen Kategorie, dass jeder Store im Trace steht und Leser/Schreiber zu den Pfeilen passen, und dass vor jedem Live-Schreiben ein `userTask` liegt | + +**Regeln.** + +- **Ein `userTask` vor jedem Schreiben in einen Live-Store**, auf jedem Pfad. verify meldet sonst + einen Fehler, design bricht ab und schickt den Anwender ins Diagramm. Ein PreToolUse-Hook + (`-write-guard.mjs`) fragt zusätzlich beim Aufruf der Schreib-Tools + (`permissionDecision: "ask"`). Eine Phase mit Live-Schreiben ist nie ein Workflow-Skript. +- **Gedächtnis ist ein kuratiertes Dokument** mit den Abschnitten *Bewährt*, *Vermeiden*, *Offene + Muster* und höchstens 150 Zeilen. Es braucht einen schreibenden `serviceTask` und einen Leser; + Schreiben braucht keine Freigabe. Ein PostToolUse-Hook (`-memory-cap.mjs`) meldet + darüber Exit 2 mit „verdichten“. +- **Nicht aufgelöste Tools werden nie zu Wildcards.** Ist der Server nicht verbunden, bleibt der + Store `tools: unresolved`, verify warnt und generate lässt die Tools weg. +- **Rückverfolgung:** Stores tragen `kind: context-source`, Prozess-Ein-/Ausgabe `workflow-input` / + `workflow-output`; die Mapping-Ansicht färbt Stores rosé, ein Store ohne Eintrag ist rot. Die + Memory-Dateien entstehen erst im Projekt des Anwenders, nicht im Output. + +**Bewusst nicht in v1:** Datenzustände (`[freigegeben]`) und daraus abgeleitete Status-Hooks, +Freigabe-Records zur Laufzeit, zusätzliche Lint-Regeln (toter Store, `serviceTask` ohne Eingang), +Pro-Lane-Tools im Workflow-Skript-Muster und die Erzeugung einer `.mcp.json` (keine Endpunkte oder +Zugangsdaten im Output). Das Beispiel `dark-factory` bleibt Schnappschuss. Als Prüfstand dienen die +Fixtures unter `.agents/skills/bpmn2agent-verify/fixtures/` (`context-flow.bpmn` grün, +`context-flow-unguarded.bpmn` muss den Schreibfehler melden). + ## Ergebnis und Rückverfolgbarkeit ```text generated// .claude/ # Nutzlast im Aufbau eines Projekt-.claude/ (meta.outputLayout: claude-dir) agents/ skills/ # die Artefakte; skills//scripts/ nur für echte Skript-Elemente - hooks/*.mjs # nur bei kind: hook - settings.json # meldet jeden Hook an ("$CLAUDE_PROJECT_DIR"/.claude/hooks/…) + hooks/*.mjs # bei kind: hook; dazu Write-Guard und Memory-Cap bei Kontextquellen + settings.json # meldet jeden Hook an ("$CLAUDE_PROJECT_DIR"/.claude/hooks/…), permissions.allow für Lese-Tools workflows/.workflow.mjs # nur beim Muster Workflow-Skript workflow-spec.yaml # die Drehscheibe workflow-spec.draft.yaml # nur während design (Schritt 6a–8), Eingabe des Architekten @@ -154,6 +219,9 @@ Skripten). `bpmn2agent-verify/scripts/verify.mjs` prüft das in beide Richtungen 5. **lint** – Hook-JSON, `node --check` für Skripte, Workflow-Skript-Syntax. 6. **no-red** – kein `unresolved`, keine offene Frage, die Mapping-Ansicht ist gültig. +Dazu kommt, nur wenn das Diagramm Stores oder Prozess-Ein-/Ausgabe hat, die Kategorie +**context-sources** (siehe [Kontextquellen](#kontextquellen)); sonst bleibt die Ausgabe unverändert. + Die Mapping-Ansicht färbt jedes Element nach Ergebnis (erzeugt, Steuerlogik, bewusst nicht erzeugt, rot = offen) und verlinkt es mit seiner Datei. @@ -188,13 +256,13 @@ Keine eigenen Stufen; die Stufen-Skills rufen sie an festen Stellen auf. | Helfer | Art | Einsatz (genau eine Stelle) | |---|---|---| -| `agentic-workflow-architect` | Agent, nur lesend | prüft den Spec-Entwurf (`workflow-spec.draft.yaml`) vor dem Mapping-Plan; design 6a, immer | +| `agentic-workflow-architect` | Agent, nur lesend | prüft den Spec-Entwurf (`workflow-spec.draft.yaml`) vor dem Mapping-Plan; design 6a, immer; bei Stores auch ungeschütztes Schreiben, zu breite Tool-Listen, Gedächtnis ohne Cap | | `orchestration-design` | Skill | acht Prüffragen zur Orchestrierung; Maßstab des Architekten | | `agentic-kb-librarian` | Agent | Designfragen, die die Referenzen nicht beantworten; fragt das Notebook und ergänzt das FAQ; design 6a | | `agentic-workflow-kb` | Skill | belegte Antworten auf Designfragen (FAQ + Referenzen); überall direkt lesbar | | `agent-authoring` | Skill | Rolle schneiden (design 5), Agenten schreiben (generate) | | `skill-authoring` | Skill | Skills schreiben (generate 5) | -| `agentic-artifact-reviewer` | Agent, nur lesend | Qualitätsprüfung der erzeugten Dateien; generate 11 | +| `agentic-artifact-reviewer` | Agent, nur lesend | Qualitätsprüfung der erzeugten Dateien; generate 11; bei Stores auch Tool-Listen, Cap und Write-Guard | | `trim-the-fat` | Skill, nur auf Anforderung | kürzt Skills, ohne ihr Verhalten zu ändern; generate 11, nur wenn der Anwender zustimmt | Befunde der Prüf-Agenten tragen ein Ziel: `design` (in der Spec lösbar), `generate` (Formulierung, @@ -218,8 +286,11 @@ die Struktur des Diagramms). - `examples/dark-factory/` – vollständiger Lauf „Von der Produktvision zu User Stories“ als Schnappschuss. `generated/` wird nur über `tools/dark-factory-gen/regenerate.sh` erneuert. -- `examples/user-story-refinement/` – neue User Story aus Feedback, erstes Beispiel im `.claude/`-Aufbau; +- `examples/user-story-refinement/` – neue User Story aus Feedback, erstes Beispiel im `.claude/`-Aufbau, + mit Wissens-Stores und dem Product Backlog als Live-Store (`cli:gh`) samt Freigaben und Write-Guard; die Notebook-Frage dahinter liegt in `notebook-faq/`. +- `examples/lanecraft/` – dieser Ablauf selbst als BPMN in lanecraft-Notation (sechs Lanes, neun + Stores); nur gezeichnet, noch nicht durch die Pipeline gelaufen. ## Wo ändere ich was? @@ -228,6 +299,7 @@ die Struktur des Diagramms). | die Übersetzung eines BPMN-Elements ändern | `bpmn2agent-design/references/mapping-rubric.md` | | die Musterwahl ändern | `bpmn2agent-design/references/pattern-rubric.md` | | die Notation neu entworfener Prozesse ändern | `bpmn-process-design/SKILL.md` §4; muss zu `mapping-rubric.md` passen | +| Stores (Art × Ort, Tool-Auflösung, Schreibschutz) ändern | `mapping-rubric.md` („Data stores → context sources“), dann Schema, analyze, design, generate-Vorlagen und verify nachziehen; Design in `docs/plans/kontextquellen/plan.md` | | ein Feld in der Spec ergänzen | `workflow-spec.schema.yaml`, dann analyze/design/generate/verify nachziehen | | Aussehen erzeugter Agenten/Skills ändern | `bpmn2agent-generate/assets/templates/` | | eine Prüfung ergänzen | `bpmn2agent-verify/scripts/verify.mjs` | diff --git a/BPMN-NOTATION.md b/BPMN-NOTATION.md new file mode 100644 index 0000000..7d3bb75 --- /dev/null +++ b/BPMN-NOTATION.md @@ -0,0 +1,234 @@ +# BPMN-Notation in lanecraft + +Was jedes BPMN-2.0-Element bedeutet, wenn du damit einen agentischen Workflow und seinen Kontext +modellierst, und was die `bpmn2agent`-Pipeline daraus erzeugt. Die Regeln selbst stehen in den +Skills; diese Seite fasst sie an einem Ort zusammen (Quellen am Ende). + +**Grundidee:** Lane = Rolle, Aufgabentyp = wer die Arbeit macht (KI, Mensch, Skript, Regel), +Datenobjekt = Übergabe zwischen Schritten, Data Store = Kontextquelle, Gateway = Steuerlogik. +Alles, was du zeichnest, wird entweder zu einer Datei, zu Steuerlogik oder zu einer bewusst +markierten Lücke. Nichts verschwindet still. + +## Rollen + +| Element | bedeutet | wird zu | Regeln | +|---|---|---|---| +| **Pool** | die eine Organisation, deren Prozess modelliert wird | nichts Eigenes | genau ein Pool bzw. Prozess; ein zweiter Pool mit Nachrichtenflüssen ist nicht unterstützt (siehe unten) | +| **Lane** | eine Rolle: eine Funktion oder ein Wissensgebiet (PO, UX, Entwicklung, QA …) | `roles.` in der Spec; ein eigener Subagent nur beim Muster Orchestrator-Agent, sonst eine Perspektive im Lane-Skill und in der Skill-Kette | 2–6 Lanes, eine je Rolle, nie eine je Aufgabe; jedes Flusselement liegt in genau einer Lane, auch in den Ebenen der Teilprozesse; eine Rolle, die nur entscheidet, ist eine Lane aus `userTask`s | +| `Rolle: X` in der Dokumentation | Rollen-Hinweis für Diagramme **ohne** Lanes | wie eine Lane, nach Rückfrage | nur Notlösung; vorrangig gilt die Extension `sdlc:step agentRole="…"` | + +Agentennamen folgen `{domain}-{role}` in kebab-case. Brauchen Rollen unterschiedliche Zugriffe auf +Live-Systeme, spricht das für das Muster Orchestrator-Agent, weil nur dort jede Rolle eigene +`tools:` bekommt. + +## Aufgaben + +| Element | bedeutet | wird zu | +|---|---|---| +| **Service Task** | KI-Arbeit: lesen, schreiben, beurteilen, etwas im Auftrag des Prozesses erledigen | ein **Skill**, wenn der Schritt Material braucht (Vorlage, längere Checkliste, Beispiele, Skript), wiederverwendet wird (≥ 2 Stellen), ein eigenes versioniertes Artefakt liefert oder im BPMN ausdrücklich als Skill benannt ist; sonst ein **Checklistenpunkt** im Agenten bzw. Lane-Skill. Im Zweifel Checklistenpunkt. | +| **User Task** | ein Mensch entscheidet, gibt frei oder liefert Input | ein **Prüfpunkt**: `AskUserQuestion` mit Optionen und Empfehlung, an genau dieser Stelle im Ablauf; keine eigene Datei | +| **Manual Task** | wie User Task | wie User Task | +| **Script Task** | rein mechanisch, gleiche Eingabe gibt immer das gleiche Ergebnis, kein Urteil | ein **Skript** (`.mjs`, nur Node-Bordmittel) unter `skills//scripts/`; über Lanes geteilt als eigener Skill | +| **Business Rule Task** | Prüfung gegen eine Regel | mechanisch (Schwelle, Pflichtfeld, Zählung) → **Skript**; mit Urteil (INVEST, Definition of Ready, „passt zum Fachkonzept?“) → **Checkliste** im Skill; ein **Hook** nur, wenn ein Tool-Aufruf physisch blockiert werden muss (siehe unten) | +| **Task** ohne Typ | unklar | eine Rückfrage, nie geraten. Besser gleich typisieren; im Zweifel `serviceTask`, `scriptTask` nur, wenn nichts beurteilt werden muss | +| Send Task / Receive Task | Austausch mit außen | keine eigene Regel in der Rubrik; als `serviceTask` („Anfrage an X senden“) bzw. `userTask` („Antwort von Y übernehmen“) modellieren | + +**Wann ein Business Rule Task ein Hook wird:** nur wenn die Regel innerhalb der Tool-Ausführung von +Claude Code greifen muss. + +| Hook | feuert | passt, wenn die Regel … | +|---|---|---| +| `PreToolUse` | vor einem Tool-Aufruf, kann ihn blockieren | ein Schreiben/Committen/Deployen stoppen muss („kein Commit vor bestandener DoR“) | +| `PostToolUse` | nach einem Tool-Aufruf | auf etwas reagiert, das ein Tool gerade getan hat | +| `Stop` | bevor der Hauptagent aufhört | eine Prüfung erzwingen muss, bevor die Aufgabe als fertig gilt | +| `SubagentStop` | bevor ein Subagent aufhört | wie `Stop`, für einen delegierten Schritt | + +**Wo User Tasks hingehören:** vor jede irreversible oder nach außen wirkende Aktion (Commit, +Senden, Veröffentlichen, Löschen), vor jedes Schreiben in einen Live-Store und dort, wo das Ziel +eine Freigabe braucht. Nicht vor Routineschritte. + +## Steuerfluss + +| Element | bedeutet | wird zu | +|---|---|---| +| **Sequenzfluss** | Reihenfolge innerhalb einer Ebene | Reihenfolge der Schritte; überquert nie die Grenze eines Teilprozesses | +| **Exklusives Gateway (XOR)**, verzweigend | eine Entscheidung mit genau einem Ausgang | `kind: orchestrator`, keine Datei. Deterministische Bedingung (Zahl, Vergleich, `Grenzwert`, `Schwellenwert`, formale Bedingung) → Zweig im Code; sonst ein **Urteils-Gateway**: der Ablauf schlägt den Zweig mit Begründung vor | +| **Exklusives Gateway**, zusammenführend (Merge) | Wiedereintritt einer Schleife oder Zusammenführung alternativer Pfade | Steuerlogik; unbenannt | +| **Paralleles Gateway (AND)** | mehrere Dinge gleichzeitig, danach synchronisieren | parallele Schritte (`parallel()` im Workflow-Skript); in der Skill-Kette nacheinander als getrennte Perspektiven | +| **Inklusives Gateway (OR)** | einer oder mehrere Zweige | wie parallel; nur verwenden, wenn wirklich nötig | +| Komplexes / ereignisbasiertes Gateway | — | keine Regel in der Rubrik; vermeiden | +| **Standardfluss** (`default`) | der normale Weg | der Zweig, wenn nichts anderes zutrifft; genau einer je verzweigendem Gateway | +| **Schleife** | Nacharbeit | eine Wiederholung mit Obergrenze `gate.maxLoops` (Standard 3); an der Grenze geht es mit Risikohinweis weiter (`fail` → `pass-with-risk`) oder ein User Task eskaliert | + +**Schleifen zeichnen:** Der Rückfluss läuft immer in ein eigenes XOR-**Merge**-Gateway vor dem +ersten Schritt des Schleifenkörpers. Entschieden wird in einem separaten XOR-**Split**-Gateway +danach, als Frage benannt. Die Obergrenze steht auf dem Rückfluss: `Nein (max. 3×)`. Jede Schleife +hat einen Ausgang (Eskalations-User-Task oder Ende). + +**Beschriftung:** Aufgaben „Verb + Objekt“ („Entwurf prüfen“); verzweigende Gateways als Frage +(„Definition of Ready erfüllt?“), ihre Ausgänge als Antworten („Ja“, „Kriterien unklar“); +parallele und zusammenführende Gateways bleiben unbenannt. Die Labels landen wörtlich in den Skills. + +## Ereignisse + +| Element | bedeutet | wird zu | +|---|---|---| +| **Startereignis** (leer) | Auslöser | Einstieg; meist `not-generated` (reiner Marker), `orchestrator`, wenn es echte Arbeit macht | +| **Endereignis** | Ergebnis; je fachlichem Ausgang eins, benannt nach dem Zustand („Story freigegeben“) | Abschluss; ein vorzeitiges Ende hinterlässt eine Übergabenotiz | +| **Fehler-Randereignis** | Fehlerpfad an einer Aktivität | ein Fehlerzweig in der Steuerlogik | +| Zwischenereignis (leer, Link, Signal als bloßer Marker) | Wegmarke im Ablauf | `kind: orchestrator`, wie ein Gateway | +| Terminierendes Endereignis | — | vermeiden | + +Timer- und Nachrichtenereignisse sind nicht unterstützt (siehe unten). + +## Phasen und Wiederholung + +| Element | bedeutet | wird zu | +|---|---|---| +| **Zugeklappter Teilprozess** | eine Phase mit eigenem Innenleben (ab ca. 8 Schritten) | kurz, fast linear → **wiederverwendbarer Skill**, die inneren Schritte werden zu Abschnitten seiner `## Procedure`, innere Script Tasks zu Skripten darin; mit Gateways, Schleifen oder Mehrfachinstanz → **Teil-Workflow** mit eigener Musterwahl | +| **Call Activity** | Aufruf eines separat definierten, wiederverwendbaren Prozesses | wie Teilprozess; der aufgerufene Prozess wird als eigene Ebene analysiert | +| **Mehrfachinstanz** (parallel oder sequenziell) | dasselbe je Element einer Menge („je Epic“) | ein Schritt je Element; parallel im Workflow-Skript als `parallel()`. Die Menge kommt aus `sdlc:collection`, `loopCardinality`/`loopDataInputRef`, dem Namen („je/pro/jede …“, „for each“) oder zuletzt einer Annotation, die nur als Hinweis gilt und nachgefragt wird | + +Ein Teilprozess hat genau ein leeres Startereignis, eigene Endereignisse und, im Agenten-Diagramm, +eigene Lanes in seiner Ebene; diese gelten nur für ihn. + +## Daten und Kontext + +Beim Zeichnen für jeden Schritt fragen: **Was muss er wissen, und wo liegt es?** Die Antwort ist +ein Data Store, ein Datenobjekt aus einem früheren Schritt oder die Prozesseingabe. Und: **Schreibt +er irgendwohin?** Dann den Store benennen und den User Task davor setzen. + +| Element | bedeutet | wird zu | +|---|---|---| +| **Datenobjekt** | ein Arbeitsergebnis, das ein späterer Schritt braucht; Übergabe zwischen Rollen | ein **Artefaktvertrag** `artifacts.`: fester Ablageort (`pathPattern`), Kopfdaten (`status`, `version`), Erzeuger und Verbraucher. Darüber übergeben sich Schritte und wird ein Lauf fortgesetzt | +| **Data Store** | ein dauerhafter Datenbestand, der den Lauf überdauert | eine **Kontextquelle** `contextSources.`, je nach `Art:` (Tabelle unten); keine eigene Datei | +| **Prozesseingabe** (`ioSpecification`/`dataInput`, oder ein Datenobjekt, das niemand erzeugt und ein Schritt liest) | was ein Lauf zum Start braucht | `workflowIO.input`; die Pflichtfelder werden zum `argument-hint` des Orchestrators | +| **Prozessausgabe** (`dataOutput`, oder ein Datenobjekt, das erzeugt und von niemandem gelesen wird) | was der Lauf am Ende liefert | `workflowIO.output`, der Vertrag für das Endergebnis | + +**Pfeilrichtung ist die einzige Zugriffsregel:** Store/Objekt → Aufgabe = lesen, Aufgabe → +Store/Objekt = schreiben. Es gibt keine `Zugriff:`-Zeile. Stores sind keine Flusselemente: keine +Lane, kein Sequenzfluss. Ein Pfeil an einem Teilprozess gilt für den Teilprozess. + +### Data Stores: `Art` und `Ort` + +Benannt als fachlicher Datenbestand („Jira-Tickets Projekt LANE“), nicht als System („Jira“). +Mehrere Stores dürfen denselben Ort teilen. Die Dokumentation trägt genau zwei Zeilen: + +```text +Art: wissen | live | gedächtnis +Ort: notebook: | mcp: | cli: | | datei: | websearch +``` + +| Art | bedeutet | erlaubter Ort | geladen | wird zu | +|---|---|---|---|---| +| `wissen` | Referenzwissen, das ein Schritt kennen muss (Handbuch, Fachkonzept, Regeln) | `notebook:`, URL, `datei:`, `websearch`; `mcp:` nur als Snapshot bei verbundenem Server | bei der Generierung | belegte Datei `knowledge/.md` je lesendem Schritt, dann `references/` in dessen Skill | +| `live` | Daten, die zur Laufzeit gelesen oder geändert werden (Tickets, Repo, Wiki) | `mcp:`, `cli:`, `notebook:` (wie `mcp:gemini-notebook-mcp`), URL (`WebFetch`), `datei:` (`Read`) | zur Laufzeit | Tool-Allowlist und ein Abschnitt `## Kontextquellen` im Skill; beim Schreiben zusätzlich ein `ask`-Hook | +| `gedächtnis` | Erkenntnisse über Läufe hinweg | nur `datei:` (Standard `.claude/memory//.md`) | zu Laufbeginn gelesen, am Ende geschrieben | kuratierte Datei (*Bewährt*, *Vermeiden*, *Offene Muster*, max. 150 Zeilen) plus Cap-Hook | + +Jede andere Kombination (z. B. `live` + `websearch`, `gedächtnis` + `mcp:`) ist ungültig und wird +nachgefragt. + +**Regeln je Art:** + +- `wissen` wird nur gelesen. +- `live`: Vor jedem Schreiben liegt auf **jedem** Pfad ein User Task; `bpmn2agent-verify` meldet + sonst einen Fehler. Ein PreToolUse-Hook (`-write-guard.mjs`, `permissionDecision: + "ask"`) sichert die Schreib-Tools zusätzlich ab. Eine Phase mit Live-Schreiben wird nie ein + Workflow-Skript. Tools werden per Name eingeordnet (`get/list/search/read/fetch/view` lesen, + `create/update/delete/add/post/edit/transition/push` schreiben, `readOnlyHint` geht vor, + unklar = schreiben). Ist der Server nicht verbunden, bleibt `tools: unresolved`, nie eine Wildcard. +- `gedächtnis` braucht einen schreibenden Service Task und einen Leser; Schreiben braucht keine + Freigabe. + +## Dokumentation an den Elementen + +| wo | Zeilen | Zweck | +|---|---|---| +| jede Aufgabe | `Input: … Output: … Quelle: ` | verdrahtet Ein- und Ausgaben, belegt die Herkunft des Schritts | +| Data Store | `Art: …` und `Ort: …` | siehe oben | +| Aufgabe ohne Lane | `Rolle: …` | Rollen-Hinweis, nur ohne Lanes | + +`Quelle:` beschreibt die Herkunft eines Schritts, `Ort:` den Speicherort eines Stores; deshalb zwei +verschiedene Wörter. + +## Rein visuell + +| Element | Wirkung | +|---|---| +| **Gruppe** | nur optische Klammer („Review-Phase“), ersetzt keinen Teilprozess | +| **Textanmerkung** | keine Fluss- oder Rolleninformation; einzig als letzter Hinweis auf die Menge einer Mehrfachinstanz gelesen, und dann nachgefragt | + +## Nicht unterstützt (v1) + +`bpmn2agent-analyze` meldet diese Elemente rot und schlägt einen Umbau vor. Bleibst du dabei, wird +das Element eine bewusste, grau markierte Lücke. + +| Element | warum nicht | stattdessen | +|---|---|---| +| zweiter Pool mit Nachrichtenflüssen | die Pipeline erzeugt Agenten für **einen** Prozess, nicht für fremde Systeme | eigene Rolle → Lane; echtes Außen → Service Task „Anfrage an X senden“ bzw. User Task „Antwort von Y übernehmen“ | +| Timer-Ereignis | kein Scheduler in den erzeugten Artefakten | Schleife mit Obergrenze („bis zu 3 Versuche“); regelmäßige Läufe sind Betriebssache (Cron o. Ä.) | +| Nachrichtenereignis | Signal von außerhalb des eigenen Ablaufs | wie Pool: Lane und normaler Sequenzfluss, oder ein expliziter Schritt | +| Event-Teilprozess | kein „Handler, der jeden Schritt unterbrechen kann“ | Fehler-Randereignis an der betroffenen Aktivität, oder ein Checklistenpunkt „vor/nach jedem Schritt X prüfen“ | +| Kompensation | keine Transaktions-Engine, kein automatisches Rückgängigmachen | ein expliziter Rückbau-Task auf einem Fehlerzweig hinter einem Gateway | + +## Vom Diagramm zum Orchestrierungsmuster + +Die Form des Diagramms bestimmt, wie die Teile zusammenspielen (`pattern-rubric.md`): + +| Diagramm zeigt | Muster | +|---|---| +| User Tasks, fast linear, Schleifen mit einfacher Bestanden/Nicht-bestanden-Rubrik | **Skill-Kette + Hooks** | +| keine User Tasks, parallele Gateways oder Mehrfachinstanz, deterministische Verzweigungen, kein Live-Schreiben | **Workflow-Skript** (startet nur von Hand) | +| Urteils-Gateways, an denen im Einzelfall abgewogen wird | **Orchestrator-Agent** mit einem Agenten je Lane | +| Phasen mit deutlich verschiedener Form | **gemischt**, je Phase eines der drei | + +Im Zweifel gewinnt das einfachere Muster. + +## Farben in der Mapping-Ansicht + +`generated//mapping/` färbt jedes Element nach dem, was daraus wurde. Jede Anmerkung +nennt die Art zusätzlich als Text. + +| Farbe | `kind` | bedeutet | +|---|---|---| +| Blau | `agent-checklist` | Checklistenpunkt im Agenten | +| Grün | `skill` | eigener Skill | +| Lila | `script` | deterministisches Skript in einem Skill | +| Orange | `hook` | Claude-Code-Hook | +| Türkis | `orchestrator` | Gateway, Schleife, Mehrfachinstanz; keine Datei | +| Bernstein | `human-checkpoint` | ein Mensch entscheidet | +| Braun | `artifact-contract` | Datenobjekt mit Ablageort und Kopfdaten | +| Rosé | `context-source`, `workflow-input`, `workflow-output` | Data Store oder Prozess-Ein-/Ausgabe | +| Grau | `not-generated` | bewusst nicht erzeugt, mit Begründung; kein Fehler | +| Rot | `unresolved` | offen; `bpmn2agent-verify` schlägt fehl, bis es geklärt ist | + +## Kurz-Checkliste vor dem Zeichnen + +- Ein Pool, 2–6 Lanes, jedes Element in einer Lane. +- Jede Aufgabe typisiert, als „Verb + Objekt“ benannt, mit `Input: … Output: … Quelle: …`. +- Verzweigende Gateways als Frage, Ausgänge beschriftet, ein `default`. +- Jede Schleife über ein Merge-Gateway, Obergrenze auf dem Rückfluss, mit Ausgang. +- Ergebnisse, die die Lane wechseln, als Datenobjekt. +- Jede dauerhafte Quelle als Data Store mit `Art:` und `Ort:`; Pfeilrichtung = lesen/schreiben. +- User Task vor jedem Live-Schreiben und vor jeder nach außen wirkenden Aktion. +- Prozesseingabe mit ihren Pflichtfeldern und das Ergebnis des Laufs eingezeichnet. +- Keine Timer, Nachrichten, zweiten Pools, Event-Teilprozesse oder Kompensation. + +## Quellen + +- [`bpmn-process-design/SKILL.md`](.agents/skills/bpmn-process-design/SKILL.md): die + lanecraft-Notation beim Entwerfen, Schritt 4 +- [`bpmn2agent-analyze/references/conventions.md`](.agents/skills/bpmn2agent-analyze/references/conventions.md): + wie die Analyse ein Diagramm liest +- [`bpmn2agent-analyze/references/unsupported.md`](.agents/skills/bpmn2agent-analyze/references/unsupported.md): + nicht unterstützte Konstrukte und Umbauten +- [`bpmn2agent-design/references/mapping-rubric.md`](.agents/skills/bpmn2agent-design/references/mapping-rubric.md): + Element → Artefakt, Kontextquellen, Farblegende +- [`bpmn2agent-design/references/pattern-rubric.md`](.agents/skills/bpmn2agent-design/references/pattern-rubric.md): + Wahl des Orchestrierungsmusters +- [`bpmn-authoring/references/modelling-rules.md`](.agents/skills/bpmn-authoring/references/modelling-rules.md) + und [`xml-and-di.md`](.agents/skills/bpmn-authoring/references/xml-and-di.md): Modellierregeln, + Schleifen, XML für Data Stores und `ioSpecification` +- [`ARCHITECTURE.md`](ARCHITECTURE.md#kontextquellen): Kontextquellen durch die Stufen +- Beispiele: [`examples/user-story-refinement/`](examples/user-story-refinement/), + Fixture [`context-flow.bpmn`](.agents/skills/bpmn2agent-verify/fixtures/context-flow.bpmn) diff --git a/CLAUDE.md b/CLAUDE.md index b30cd92..159b0cd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,8 +17,8 @@ spec, traceability, the knowledge layer and the helper skills/agents fit togethe - Notebook answers are kept, not thrown away: record every `notebook_query` result with `.agents/skills/bpmn2agent-knowledge/scripts/notebook-faq.py add`. Agentic-design questions go to `.agents/skills/agentic-workflow-kb/faq/`; check its `README.md` before asking the notebook again. -- The `bpmn2agent-*` skills write relative to the cwd into `generated//`. For the example, run - them (and `bpmn-authoring` on its BPMN) from `examples/dark-factory/`. +- The `bpmn2agent-*` skills write relative to the cwd into `generated//`. For an example, run + them (and `bpmn-authoring` on its BPMN) from that example's folder, e.g. `examples/dark-factory/`. - Never edit `examples/dark-factory/generated/` by hand. Edit the generator sources in `examples/dark-factory/tools/dark-factory-gen/` and run `regenerate.sh`; it must end with `RESULT: PASS`, `no reference problems`, the three smoke lines and `mapping view ok`. diff --git a/README.md b/README.md index 988e2a1..39e62b0 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,7 @@ legt `bpmn2agent-design` dir als Mapping-Plan zur Bestätigung vor, bevor etwas | **Zugeklappter Teilprozess** / **Call Activity** | eine Phase mit eigenem Innenleben | ein wiederverwendbarer Skill oder ein eigener Teil-Workflow | | **Mehrfachinstanz** | dasselbe je Element einer Menge | ein Schritt je Element, z. B. je Epic | | **Datenobjekt** | ein Arbeitsergebnis | ein **Artefaktvertrag**: fester Ablageort und Kopfdaten (`status`, `version`), über die die Schritte sich übergeben und ein Lauf fortgesetzt werden kann | +| **Data Store** | ein dauerhafter Datenbestand (Handbuch, Tickets, Lernnotizen) | eine **Kontextquelle**, je nach `Art:` Wissensreferenz, Tool-Freigabe oder Gedächtnisdatei (siehe [Kontextquellen](#kontextquellen)) | | **Fehler-Randereignis** | ein Fehlerpfad | ein Fehlerzweig in der Steuerlogik | Noch nicht unterstützt: Pools mit Nachrichtenflüssen, Timer- und Nachrichtenereignisse, @@ -154,6 +155,9 @@ Event-Teilprozesse, Kompensation. `bpmn2agent-analyze` meldet sie und schlägt e (z. B. „bis zu 3 Versuche“ statt eines Timers). Bleibst du dabei, wird das Element eine bewusste, grau markierte Lücke. +Alle Elemente mit Regeln, Kontextquellen, Farblegende und Checkliste auf einer Seite: +[`BPMN-NOTATION.md`](BPMN-NOTATION.md). + ### Wie die Teile zusammenspielen `pattern-rubric.md` zählt im Diagramm menschliche Aufgaben, Parallelität, Mehrfachinstanzen, @@ -179,6 +183,8 @@ Im Zweifel gewinnt das einfachere Muster. - **`Input: … Output: …` in die Dokumentation** jeder Aufgabe schreiben und die wichtigen Arbeitsergebnisse als **Datenobjekte** einzeichnen. Daraus werden Eingaben, Ausgaben und Ablageorte. +- **Datenquellen als Data Store** einzeichnen, mit `Art:` und `Ort:` in der Dokumentation, und vor + jedes Schreiben in ein Live-System einen User Task setzen. - **Labels in deiner Sprache**: sie bleiben wörtlich erhalten und tauchen in den Skills wieder auf. `bpmn-process-design` zeichnet nach genau diesen Regeln. `bpmn-authoring` hilft beim Zeichnen von @@ -187,6 +193,29 @@ Hand und prüft gegen XSD, bpmn-moddle und bpmnlint. Die vollständigen Regeln s [`pattern-rubric.md`](.agents/skills/bpmn2agent-design/references/pattern-rubric.md), knapp zusammengefasst in [`ARCHITECTURE.md`](ARCHITECTURE.md#übersetzungsregeln). +## Kontextquellen + +Ein Schritt ist nur so gut wie die Daten, die er sieht. Zeichne deshalb jede dauerhafte Datenquelle +als **Data Store** und schreibe zwei Zeilen in seine Dokumentation: `Art:` (`wissen`, `live` oder +`gedächtnis`) und `Ort:` (`notebook:`, `mcp:`, `cli:`, eine URL, +`datei:` oder `websearch`). Ob ein Schritt liest oder schreibt, sagt allein die Pfeilrichtung. +Die Pipeline macht daraus Wissensreferenzen, Tool-Freigaben, einen Schreibschutz und ein Gedächtnis +über Läufe hinweg. Fehlt eine Angabe, fragt sie nach, statt zu raten. + +Beispiel aus der Fixture +[`context-flow.bpmn`](.agents/skills/bpmn2agent-verify/fixtures/context-flow.bpmn): + +| Data Store | `Art:` / `Ort:` | Pfeil | wird zu | +|---|---|---|---| +| Support-Handbuch | `wissen` / `notebook:Support-Handbuch` | → „Antwort entwerfen“ | belegte Referenz im Skill des Schritts | +| Jira-Tickets Projekt LANE | `live` / `mcp:atlassian` | → „Ticket analysieren“, „Kommentar im Ticket posten“ → | Lese-Tools in der Allowlist; Schreib-Tool nur hinter dem User Task „Antwort freigeben“ und einem `ask`-Hook | +| Lernnotizen Support | `gedächtnis` / `datei:.claude/memory/context-flow/lernnotizen-support.md` | → „Ticket analysieren“, „Lernnotiz fortschreiben“ → | kuratierte Datei, höchstens 150 Zeilen, mit Cap-Hook | + +Das prozessweite Eingangsdatum „Ticket-ID“ wird zum `argument-hint` des erzeugten Orchestrators. +Schreibt ein Schritt in einen Live-Store, ohne dass auf jedem Pfad ein User Task davorliegt, meldet +`bpmn2agent-verify` einen Fehler (Gegenprobe: `context-flow-unguarded.bpmn`). Notation, Regeln und +was bewusst fehlt: [`ARCHITECTURE.md`](ARCHITECTURE.md#kontextquellen). + ## Fachwissen aus Gemini-Notebooks Ein Skill ist nur so gut wie das Fachwissen darin. Schreibt das Modell eine INVEST-Checkliste oder @@ -253,7 +282,7 @@ und beantwortet Fragen dazu, wie man Agenten und Skills baut, nicht zu eurem Fac generated// .claude/ # alles, was installiert wird, im Aufbau eines Projekt-.claude/ agents/ skills/ # die Artefakte - hooks/ settings.json # nur bei Hooks; settings.json meldet jeden Hook an + hooks/ settings.json # nur bei Hooks oder Stores; settings.json meldet jeden Hook an workflows/ # nur beim Muster Workflow-Skript README.md # Installation in einer Zeile, für den Anwender workflow-spec.yaml # die Spezifikation, über die alle Stufen reden @@ -280,12 +309,19 @@ Das ist ein Schnappschuss; das daraus entstandene Dark-Factory-Plugin wird im Re **[`examples/user-story-refinement/`](examples/user-story-refinement/)**: eine neue User Story aus Feedback erstellen und verfeinern. Ein Diagramm mit vier Lanes und sechs Phasen, als erstes Beispiel -im neuen Aufbau durch die Pipeline gelaufen: 7 Skills unter +im neuen Aufbau durch die Pipeline gelaufen: 8 Skills, ein Write-Guard-Hook und eine `settings.json` unter [`generated/user-story-refinement/.claude/`](examples/user-story-refinement/generated/user-story-refinement/.claude/), -installierbar mit einer Kopie, ohne Agenten, Skripte und Hooks. +installierbar mit einer Kopie, ohne Agenten und Skripte. Es zeigt zwei der drei Kontextquellen-Arten im +Einsatz: Notebook-Wissen je Phase und das Product Backlog live in GitHub (`cli:gh`), das +nur nach einer Freigabe direkt davor geschrieben wird. ![User-Story-Refinement als BPMN](examples/user-story-refinement/user-story-refinement.png) +**[`examples/lanecraft/`](examples/lanecraft/)**: lanecraft selbst als BPMN. Sechs Lanes von der +Prozessmodellierung bis zu Prüfung & Übergabe, sieben menschliche Prüfpunkte, beide Schleifen der +Pipeline und neun Data Stores aller drei Arten. Bisher nur gezeichnet, nicht durch die Pipeline +gelaufen. + ## Inhalt des Repos ```text diff --git a/docs/plans/kontextquellen/plan.md b/docs/plans/kontextquellen/plan.md new file mode 100644 index 0000000..77626fa --- /dev/null +++ b/docs/plans/kontextquellen/plan.md @@ -0,0 +1,151 @@ +# Kontextquellen im BPMN modellieren + +Status: Entwurf, abgestimmt am 2026-10-03. Aufgaben: [`tasks.md`](tasks.md). + +## Ziel + +Kontext ist am Ende Daten. Wer einen Prozess für lanecraft modelliert, soll beim Zeichnen +überlegen, **welche Daten jeder Schritt braucht und wo sie liegen**. Aus diesen Angaben baut die +Pipeline Wissensreferenzen, Tool-Freigaben, Schreibschutz und ein Gedächtnis über Läufe hinweg. + +Heute modelliert das Diagramm nur Übergaben (Data Objects → Artefaktverträge). Wissensquellen +ordnet `bpmn2agent-knowledge` erst nachträglich Lanes zu. `dataStoreReference` ignoriert die +Pipeline (`inventory.mjs` überspringt es, die Mapping-Ansicht würde es rot färben). + +## Notation + +Jede dauerhafte Quelle ist ein **Data Store** (Zylinder). Seine Dokumentation trägt zwei Zeilen (`Ort:` statt `Quelle:`, weil die Task-Dokumentation `Quelle:` schon für die Herkunft belegt): + +``` +Art: wissen | live | gedächtnis +Ort: notebook: | mcp: | cli: | | datei: | websearch +``` + +- **Lesen/Schreiben ergibt sich nur aus der Pfeilrichtung.** Store → Task heißt lesen, + Task → Store heißt schreiben. Es gibt keine `Zugriff:`-Zeile. +- **Ein Store ist ein fachlicher Datenbestand**, z. B. „Jira-Tickets Projekt LANE“, und kein + System. Mehrere Stores dürfen denselben `Ort:` haben. +- **Fehlt eine Zeile, fragt analyze nach.** Unsinnige Kombinationen (Matrix unten) ebenfalls. + +### Art × Ort + +| Art | erlaubter Ort | geladen | wird zu | +|---|---|---|---| +| `wissen` | `notebook:`, URL, `datei:`, `websearch` | bei der Generierung | `knowledge/.md` → Skill-`references/` | +| `wissen` | `mcp:` | bei der Generierung (Snapshot), nur wenn der Server verbunden ist | wie oben | +| `live` | `mcp:`, `cli:` | zur Laufzeit | Tool-Allowlist + `## Kontextquellen` im Skill | +| `live` | `notebook:` | zur Laufzeit, behandelt als `mcp:gemini-notebook-mcp` | wie oben | +| `live` | URL | zur Laufzeit | `WebFetch` | +| `live` | `datei:` | zur Laufzeit | `Read` | +| `gedächtnis` | `datei:` (Standard `.claude/memory//.md`) | zu Laufbeginn gelesen, am Ende geschrieben | kuratierte Datei + Cap-Hook | + +### Prozess-Ein- und -Ausgabe + +- **Eingabe:** ein echtes prozessweites `dataInput` (über bpmn-authoring geschrieben; bpmn.io + rendert es, hat aber keinen Palette-Eintrag) **oder** die Konvention „Data Object ohne + Erzeuger, das ein Task liest“. +- **Ausgabe:** `dataOutput` **oder** „Data Object, das niemand liest“. +- **Was daraus wird:** Die Eingabe wird ein Artefaktvertrag mit Pflichtfeldern. Der Orchestrator + bekommt daraus ein `argument-hint` und fragt nach, wenn ein Pflichtfeld fehlt. Die Ausgabe wird + der Vertrag für das Endergebnis. + +## Regeln + +### Wissen + +- **Das Diagramm ist die Quelle der Wahrheit.** Die Pfeile von Wissens-Stores sind die Zuordnung. + knowledge fragt nicht mehr „welches Notebook deckt welche Lane ab“, sondern extrahiert pro + lesendem Task aus genau dieser Quelle. +- **Eine Datei pro Task:** `knowledge/.md`, ein Abschnitt pro Store, Zitate je Abschnitt. +- **Rückfall:** Diagramme ohne Wissens-Store und Tasks ohne Wissens-Eingang nutzen die bisherige + Lane-Zuordnung, mit dem Hinweis, dass Stores genauer wären. + +### Live + +- **Tools auflösen:** design holt die Tool-Liste des Servers per ToolSearch und sortiert nach + Namen. `get/list/search/read/fetch/view` gilt als lesen, + `create/update/delete/add/post/edit/transition/push` als schreiben. Wo der Server + `readOnlyHint` liefert, gilt der. **Unklar heißt schreiben.** Die Nutzerin bestätigt die Liste im + Mapping-Plan. +- **Server nicht verbunden:** Der Store bleibt `unresolved`. verify warnt, generate lässt die + Tools weg (kein Wildcard). +- **Wohin die Tools gehen:** Wo Lanes Agents sind (orchestrator-agent), in `tools:` des + Lane-Agents, nur die nötigen Lese-Tools. Sonst in `.claude/settings.json` → + `permissions.allow` (Vereinigung aller Lese-Tools), und der Mapping-Plan sagt offen + „Lesezugriff nicht pro Rolle getrennt“. Unterscheiden sich die nötigen Rechte der Rollen, gibt + die Rubrik das als Signal für orchestrator-agent aus. +- **Skill-Text:** Jeder lesende Task bekommt einen Abschnitt `## Kontextquellen` mit dem + Store-Namen (wörtlich), was für diesen Schritt zu holen ist und mit welchem Tool. + +### Schreiben in Live-Stores + +- **Vor jedem Schreiben ein `userTask`:** verify meldet einen Fehler, wenn nicht auf jedem Pfad + ein `userTask` vor dem Schreiben liegt. +- **Hook als Rückhalt:** generate erzeugt einen PreToolUse-Hook, der für die Schreib-Tools + `permissionDecision: "ask"` zurückgibt. Claude Code fragt den Menschen dann direkt beim Aufruf. +- **Musterwahl:** Eine Phase, die in einen Live-Store schreibt, darf nicht workflow-script sein, + weil dieses Muster keine menschlichen Checkpoints erlaubt. + +### Gedächtnis + +- **Ablage:** `.claude/memory//.md` relativ zum cwd des Laufs, überschreibbar + mit `Ort: datei:`. Das Gedächtnis liegt im Projekt der Nutzerin, nicht im Plugin. +- **Format:** ein kuratiertes Dokument mit festen Abschnitten (*Bewährt*, *Vermeiden*, + *Offene Muster*) und höchstens 150 Zeilen. Der schreibende Task führt neue Erkenntnisse ein, + statt sie anzuhängen. +- **Schreiber und Leser:** Ein Gedächtnis-Store braucht mindestens einen schreibenden + `serviceTask` und einen Leser. Fehlt einer, fragt analyze nach. Schreiben braucht keine + Freigabe. +- **Cap:** Ein PostToolUse-Hook auf Schreibzugriffe auf den Gedächtnispfad meldet bei über + 150 Zeilen Exit 2 mit „verdichten“ an Claude zurück. + +### Rückverfolgung und Mapping-Ansicht + +- **Stores:** Jeder Store bekommt `elements.` mit `kind: context-source` und eine + eigene Farbe (Rosé). verify verlangt den Eintrag wie bei Flow Nodes; fehlt er, ist der Store + rot. +- **Prozess-I/O:** Bekommt `kind: workflow-input` / `workflow-output` und wird aus + `ioSpecification` indiziert. + +## Spec-Erweiterung (`workflow-spec.yaml`) + +```yaml +contextSources: + jira-tickets-lane: + name: Jira-Tickets Projekt LANE # Label wörtlich + bpmnElement: DataStore_JiraTickets + art: live # wissen | live | gedaechtnis + ort: { type: mcp, ref: atlassian } # notebook|mcp|cli|url|datei|websearch + readers: [Task_TicketAnalysieren] + writers: [Task_KommentarPosten] + tools: # nur art: live; oder: unresolved + read: [mcp__atlassian__getJiraIssue] + write: [mcp__atlassian__addCommentToJiraIssue] + memory: { path: .claude/memory//.md, maxLines: 150 } # nur gedaechtnis +workflowIO: + input: { artifact: , required: [ticketId] } + output: { artifact: } +``` + +`contextSources` steht bewusst neben `artifacts`. Artefakte gelten nur für einen Lauf, Stores +haben einen anderen Lebenszyklus und andere Felder. + +## Nicht in v1 + +- **Datenzustände** (`[freigegeben]`) und daraus abgeleitete Status-Hooks +- **Freigabe-Records** zur Laufzeit (`runs//approvals/…`) +- **Zusätzliche Lint-Regeln:** toter Store, `serviceTask` ohne Eingang +- **Pro-Lane-Tools im workflow-script-Muster** (`agent({ agentType })`) +- **`.mcp.json`-Erzeugung:** keine Endpunkte oder Zugangsdaten im generierten Output +- **Änderungen am dark-factory-Beispiel** (bleibt Snapshot) + +## Prüfstand + +Neue Fixture `.agents/skills/bpmn2agent-verify/fixtures/context-flow.bpmn` mit: +- je einem Store pro Art +- einem geschützten Schreiben (`userTask` → Schreib-Task → Live-Store) +- Prozess-Ein- und -Ausgabe (einmal echt, einmal per Konvention) +- einem ungeschützten Schreiben in einer Negativ-Variante + +Die ganze Pipeline läuft darauf. verify muss für die positive Fixture grün sein und für die +Negativ-Variante den Schreibfehler melden. diff --git a/docs/plans/kontextquellen/tasks.md b/docs/plans/kontextquellen/tasks.md new file mode 100644 index 0000000..d32bad2 --- /dev/null +++ b/docs/plans/kontextquellen/tasks.md @@ -0,0 +1,226 @@ +# Aufgaben: Kontextquellen im BPMN + +Plan: [`plan.md`](plan.md). Branch `feat/context-sources`. Ein Commit pro Aufgabe +(Conventional Commits, Scope in Klammern). Jede Aufgabe endet mit `npm run validate`. + +## Reihenfolge + +``` +Welle 1: T1 Spec-Vertrag T2 Authoring + Fixture T3 process-design + │ │ +Welle 2: └──────── T4 analyze ┘ + │ +Welle 3: T5 knowledge T6 design T7 verify + │ +Welle 4: T8 generate + │ +Welle 5: T9 Doku T10 Ende-zu-Ende +``` + +Aufgaben in derselben Welle sind unabhängig und können parallel laufen. + +--- + +## T1 — Spec-Vertrag: Schema und Mapping-Rubrik + +`feat(design): add contextSources and workflowIO to the spec contract` + +- **Dateien:** + - `.agents/skills/bpmn2agent-design/assets/workflow-spec.schema.yaml` + - `.agents/skills/bpmn2agent-design/references/mapping-rubric.md` +- **Schema:** + - neue Top-Level-Abschnitte `contextSources.` und `workflowIO`, Felder wie in + plan.md §Spec-Erweiterung + - `tools` ist entweder `{read, write}` oder `unresolved` + - `memory` nur bei `art: gedaechtnis` +- **Neue Element-Kinds:** `context-source`, `workflow-input`, `workflow-output`. +- **Rubrik:** + - Zeilen für Data Store je Art und für Prozess-I/O + - die Art×Ort-Matrix + - die Regel „Schreiben in Live-Store braucht `userTask`, nicht workflow-script“ + - Platzierung der Tools (Agent-`tools:` vs. `permissions.allow`) + - Farbe Rosé in der Farbtabelle + - Musterhinweis „unterschiedliche Rechte je Rolle → orchestrator-agent“ +- **Abnahme:** + - Das Schema validiert ein Beispiel-Snippet mit allen drei Arten. + - Bestehende Specs (dark-factory, verify-Fixtures) validieren weiterhin, weil alle neuen + Felder optional sind. + +## T2 — bpmn-authoring: Stores und Prozess-I/O zeichnen, Fixture anlegen + +`feat(bpmn-authoring): support data stores and process io; add context-flow fixture` + +- **Dateien:** + - `.agents/skills/bpmn-authoring/references/xml-and-di.md` + - `.agents/skills/bpmn-authoring/references/layout.md` + - `.agents/skills/bpmn2agent-verify/fixtures/context-flow.bpmn` + - `.agents/skills/bpmn2agent-verify/fixtures/context-flow-unguarded.bpmn` +- **XML und DI:** + - `dataStore` (Root) + `dataStoreReference` (sichtbar, 50×50), Dokumentation mit + `Art:`/`Ort:` + - prozessweite `ioSpecification` mit `dataInput`/`dataOutput` + `inputSet`/`outputSet` + - DI-Hinweis: Shapes für dataInput/dataOutput sind nötig, damit bpmn-js sie zeichnet; + bpmnlint verlangt sie nicht +- **Layout:** Stores unterhalb der Lane ihres ersten Lesers. Prozess-Eingabe links neben dem + Start, Ausgabe rechts neben dem Ende. +- **Positive Fixture:** + - je ein Store `wissen` (notebook), `live` (mcp, gelesen + geschrieben nach `userTask`) und + `gedächtnis` + - ein echtes `dataInput` und eine Ausgabe per Konvention +- **Negativ-Fixture:** dieselbe, aber ohne den `userTask` vor dem Schreiben. +- **Abnahme:** Beide Fixtures bestehen `validate.sh` (XSD, moddle ohne Warnungen, + bpmnlint:recommended ohne Warnungen) und rendern sauber. + +## T3 — bpmn-process-design: Kontext beim Modellieren abfragen + +`feat(skills): ask for context sources in bpmn-process-design` + +- **Datei:** `.agents/skills/bpmn-process-design/SKILL.md` +- **Inhalt:** + - Notationstabelle um Data Store (drei Arten) und Prozess-I/O ergänzen + - Regeln: Store = fachlicher Datenbestand; Pfeilrichtung = lesen/schreiben; `userTask` vor + Schreiben in Live-Stores; Gedächtnis braucht Schreiber und Leser +- **Outline (§5):** pro Schritt die Frage „Was muss dieser Schritt wissen, und wo liegt es?“. + Die Bestätigungsübersicht zeigt Stores mit Art, Ort, Lesern und Schreibern sowie die + Prozess-Eingabe mit Pflichtfeldern. +- **Abhängig von:** nur plan.md (läuft parallel zu T1/T2). +- **Abnahme:** `npm run validate` grün. Der Text bleibt schlank (Skill-Authoring-Regeln). + +## T4 — analyze: Stores und Prozess-I/O inventarisieren + +`feat(analyze): inventory data stores and process io into contextSources` + +- **Abhängig von:** T1, T2 +- **Dateien:** + - `.agents/skills/bpmn2agent-analyze/scripts/inventory.mjs` + - `.agents/skills/bpmn2agent-analyze/SKILL.md` + - `.agents/skills/bpmn2agent-analyze/references/conventions.md` +- **inventory.mjs:** + - `dataStoreReference` mit Name, Dokumentation (geparste `Art:`/`Ort:`), Scope + - Leser und Schreiber aus den Data Associations + - prozessweite `ioSpecification` + - Konventions-I/O: Data Object ohne Erzeuger, das gelesen wird, und Data Object ohne Leser +- **SKILL.md:** + - `contextSources` und `workflowIO` in den Spec-Entwurf schreiben + - per `AskUserQuestion` nachfragen bei: fehlendem `Art:`/`Ort:`, ungültiger Kombination, + Gedächtnis ohne Schreiber oder Leser + - Stores in den ID-Diff bei geänderten Diagrammen aufnehmen +- **Abnahme:** + - Die Inventur der Fixture listet alle drei Stores mit korrekten Lesern/Schreibern und beide + I/O-Varianten. + - dark-factory-Inventur unverändert (Diff leer bis auf neue leere Felder). + +## T5 — knowledge: Zuordnung aus dem Diagramm + +`feat(knowledge): ground tasks from knowledge stores in the diagram` + +- **Abhängig von:** T4 +- **Dateien:** + - `.agents/skills/bpmn2agent-knowledge/SKILL.md` + - `.agents/skills/bpmn2agent-knowledge/references/notebook-extraction.md` +- **Inhalt:** + - Wissens-Stores ersetzen §2 „welches Notebook für welche Lane“. Der Ort wird je nach + Typ aufgelöst (`notebook:` über notebook_list per Titel, URL per WebFetch, `datei:` als + lokales Doc, `websearch`, `mcp:` als Snapshot nur bei verbundenem Server). + - Pro lesendem Task `knowledge/.md`, ein Abschnitt pro Store. + - Lane-Rückfall für Tasks ohne Wissens-Store. + - `serviceTask` ohne jeden Wissens-Eingang wird als offene Frage erfasst, nicht als Fehler. +- **Abnahme:** Ein Lauf auf der Fixture erzeugt Dateien je Task mit Quellenangaben. Diagramme + ohne Stores laufen wie bisher. + +## T6 — design: Tools auflösen und Mapping-Plan + +`feat(design): resolve live store tools and place them per role` + +- **Abhängig von:** T1, T4 +- **Dateien:** + - `.agents/skills/bpmn2agent-design/SKILL.md` + - `references/mapping-rubric.md` (Ergänzung zu T1) + - `references/pattern-rubric.md` +- **Tools auflösen:** + - `mcp:` per ToolSearch auf `mcp____` + - Namensheuristik plus `readOnlyHint`; unklar = schreiben + - `cli:` als `Bash( :*)`-Muster, gleiche Heuristik auf die Unterbefehle + - nicht verbunden → `unresolved` +- **Platzierung:** Agent-`tools:` bei orchestrator-agent, sonst `permissions.allow`. +- **Musterwahl:** Eine Phase mit Schreiben in einen Live-Store wird nicht workflow-script. +- **Mapping-Plan:** listet je Store Art, Ort, Leser/Schreiber und die Tool-Aufteilung zur + Bestätigung, inkl. „Lesezugriff nicht pro Rolle getrennt“, wo zutreffend. +- **Abnahme:** Die Spec der Fixture hat vollständige `contextSources.*.tools` (oder + `unresolved`) und `elements.*` für alle Stores. + +## T7 — verify: Stores prüfen + +`feat(verify): trace context sources and guard live store writes` + +- **Abhängig von:** T1, T4 (läuft parallel zu T5/T6, testet gegen handgeschriebene Spec) +- **Dateien:** + - `.agents/skills/bpmn2agent-verify/scripts/verify.mjs` + - `.agents/skills/bpmn2agent-verify/SKILL.md` +- **Prüfungen:** + - Jeder Store und jedes Prozess-I/O-Element braucht einen `elements`-Eintrag. + - `contextSources.*.readers/writers` müssen mit den Associations übereinstimmen. + - **Schreiben in Live-Store ohne `userTask` auf jedem Pfad → Fehler** (Pfadanalyse über + Sequenzflüsse, Schleifen beachten). + - Live-Schreiben in einer workflow-script-Phase → Fehler. + - `tools: unresolved` → Warnung. +- **Fehlermeldungen:** in Fachsprache, geroutet an analyze/design/generate. +- **Abnahme:** Die positive Fixture besteht diese Prüfungen. Die Negativ-Fixture meldet genau + den Schreibfehler, geroutet an analyze (Diagramm ändern). + +## T8 — generate: Dateien erzeugen + +`feat(generate): emit context sources, write guard and memory hooks` + +- **Abhängig von:** T6 (T7 für die Prüfung) +- **Dateien:** + - `.agents/skills/bpmn2agent-generate/SKILL.md` + - `assets/templates/*` (skill, agent, orchestrator, settings, hook, README, mapping-report) + - `scripts/render-mapping.mjs` + - `scripts/check-mapping-view.mjs` +- **Skill- und Agent-Templates:** Abschnitt `## Kontextquellen`; `tools:`-Platzhalter in + Agent-Templates. +- **settings.json:** `permissions.allow` mit den Lese-Tools, wo Lanes keine Agents sind. +- **Schreibschutz-Hook:** PreToolUse, Matcher = Schreib-Tools aller Live-Stores, + `permissionDecision: "ask"` mit Begründung (Store-Name, Freigabe-Task). +- **Gedächtnis:** + - PostToolUse-Hook auf Schreibzugriffe auf den Gedächtnispfad; über `maxLines` → Exit 2 + „verdichten“ + - Skill-Text für Schreiber: einpflegen statt anhängen, feste Abschnitte, Cap + - Leser laden die Datei zuerst; leer oder fehlend ist in Ordnung +- **Orchestrator:** `argument-hint` aus `workflowIO.input`; fragt nach, wenn ein Pflichtfeld + fehlt. +- **README:** Voraussetzungen (MCP-Server, CLIs), Gedächtnisablage. +- **Mapping-Ansicht:** `context-source` in Rosé, Prozess-I/O aus `ioSpecification` + indizieren, Stores ohne Eintrag rot. +- **Abnahme:** Generate auf der Fixture, danach verify grün und `check-mapping-view` ohne Rot. + +## T9 — Doku + +`docs: describe context sources in README and ARCHITECTURE` + +- **Abhängig von:** T3–T8 +- **Dateien:** + - `ARCHITECTURE.md` + - `README.md` + - `.agents/skills/agentic-workflow-kb/references/process-to-agents.md` + - `.agents/skills/agentic-workflow-kb/references/multi-agent-handoffs.md` +- **ARCHITECTURE.md:** Abschnitt Kontextquellen (Notation, Art×Ort, Weg durch die Stufen). +- **README.md:** Kurzbeschreibung + Beispiel aus der Fixture. +- **Agent-Definitionen:** `agentic-workflow-architect` und `agentic-artifact-reviewer` prüfen + Stores mit: ungeschütztes Schreiben, zu breite Tool-Listen, Gedächtnis ohne Cap. +- **Abnahme:** `npm run validate` grün. Doku auf Deutsch, Skill-Text auf Englisch. + +## T10 — Ende-zu-Ende + +`test: run the full pipeline on the context-flow fixture` + +- **Abhängig von:** alle +- **Ablauf:** `bpmn-to-agentic-workflow` auf `context-flow.bpmn` in einem Scratch-Verzeichnis + (analyze → knowledge → design → generate → verify) laufen lassen. +- **Abnahme:** + - verify grün, Mapping-Ansicht ohne Rot. + - Hooks per Smoke-Test: Schreib-Tool → `ask`; Gedächtnis mit 151 Zeilen → Exit 2. + - Negativ-Fixture scheitert an verify mit der erwarteten Meldung. + - `examples/dark-factory/tools/dark-factory-gen/regenerate.sh` endet weiterhin mit + `RESULT: PASS`, `no reference problems`, den drei Smoke-Zeilen und `mapping view ok`. diff --git a/examples/lanecraft/README.md b/examples/lanecraft/README.md new file mode 100644 index 0000000..7db2740 --- /dev/null +++ b/examples/lanecraft/README.md @@ -0,0 +1,89 @@ +# Beispiel: lanecraft als BPMN + +Der Ablauf dieses Repos, in seiner eigenen Notation gezeichnet: Ein Fachanwender bringt ein Ziel +oder ein fertiges BPMN-Diagramm mit. Ohne Diagramm entwirft `bpmn-process-design` den Prozess aus +Notebook-Wissen. Danach übersetzen die fünf Stufen `bpmn2agent-analyze`, `-knowledge`, `-design`, +`-generate` und `-verify` ihn über die `workflow-spec.yaml` in Agenten, Skills, Hooks, Skripte und +genau eine Orchestrierungsdatei unter `generated//`. Am Ende steht der übergebene oder +installierte Workflow. + +Das Diagramm beschreibt den Ablauf, wie `ARCHITECTURE.md` und die Stufen-Skills ihn festlegen. Es +ist noch nicht durch die Pipeline gelaufen; es gibt also kein `generated/`. + +![lanecraft als BPMN](lanecraft.png) + +## Aufbau + +- **Eine Ebene, sechs Lanes** (Lane = Rolle): + +| Lane | Teil von lanecraft | +|---|---| +| Fachanwender | Intake und alle menschlichen Entscheidungen | +| Prozessmodellierung | `bpmn-process-design`, `bpmn-authoring` (nur ohne vorhandenes Diagramm) | +| Analyse & Fachwissen | `bpmn2agent-analyze`, `bpmn2agent-knowledge` | +| Agentic Design | `bpmn2agent-design` mit den Agenten `agentic-workflow-architect` und `agentic-kb-librarian` | +| Generierung | `bpmn2agent-generate` mit dem Agenten `agentic-artifact-reviewer` | +| Prüfung & Übergabe | `bpmn2agent-verify`, Übergabebericht, Installation | + +- **Aufgabentypen**: `serviceTask` = KI-Arbeit, `userTask` = menschlicher Prüfpunkt, `scriptTask` + = Skript (`validate.sh`/`render.mjs`, `inventory.mjs`, `render-mapping.mjs`, `verify.mjs`, die + Kopie nach `.claude/`). +- **Sieben Prüfpunkte**: Ziel, Diagramm & Wissensquellen angeben · Prozessentwurf bestätigen · + Lücken im Diagramm klären · Offene Fachfragen beantworten · Mapping-Plan bestätigen · Über offene + Befunde entscheiden · Installation entscheiden. +- **Schleifen** mit Obergrenze am Rückweg: + +| Schleife | Rückweg | Ausweg | +|---|---|---| +| Diagramm gültig & agent-ready? | zurück zum Zeichnen (max. 3×) | weiter mit Risikohinweis | +| Mapping-Plan angenommen? | Elemente anpassen (max. 3×) | Diagramm ändern → Lauf endet (Schleife A) | +| Prüfung bestanden? | je nach Befund zurück zu Analyse, Design oder Generierung (max. 3×) | Obergrenze erreicht → als unverifiziert übergeben oder Diagramm ändern | + +- **Acht Datenobjekte** tragen die Übergaben zwischen den Lanes: Prozessentwurf → `.bpmn`-Diagramm → + `workflow-spec.yaml` (Entwurf) → Wissenspaket (`knowledge/`) → Mapping-Plan → + `workflow-spec.yaml` (bestätigt) → Claude-Code-Artefakte (`.claude/`) → Prüfbericht. +- **Prozess-Ein- und -Ausgabe** als `ioSpecification`: *Prozessziel oder BPMN-Diagramm* hinein, + *Generierter Workflow (`generated//`)* heraus. +- **Neun Data Stores**: + +| Store | Art | Ort | Pfeile | +|---|---|---|---| +| Fach-Notebooks: Prozessrecherche | live | `mcp:gemini-notebook-mcp` | → Prozess im Notebook recherchieren | +| Fach-Notebooks: Wissensextraktion | live | `mcp:gemini-notebook-mcp` | → Fachwissen je Task extrahieren, Diagramm mit Fachwissen hinterfragen | +| BPMN-Notation lanecraft | wissen | `datei:BPMN-NOTATION.md` | → BPMN-Diagramm zeichnen | +| Mapping- & Muster-Rubrik | wissen | `datei:.agents/skills/bpmn2agent-design/references/` | → Elemente zuordnen & Muster wählen | +| Agentic-Design-Wissensbasis | wissen | `datei:.agents/skills/agentic-workflow-kb/references/` | → Entwurf gegen Wissensbasis prüfen | +| Notebook „Agentic Workflows“ | live | `mcp:gemini-notebook-mcp` | → Entwurf gegen Wissensbasis prüfen | +| Agentic-Design-FAQ | gedächtnis | `datei:.agents/skills/agentic-workflow-kb/faq/` | ↔ Entwurf gegen Wissensbasis prüfen | +| Generierungs-Vorlagen | wissen | `datei:.agents/skills/bpmn2agent-generate/assets/templates/` | → Skills, Agenten, Hooks & Skripte schreiben | +| Claude-Projekt des Anwenders | live | `datei:/.claude/` | ← Nach Projekt-.claude/ kopieren, nach „Installation entscheiden“ | + +- Jede Aufgabe trägt in `` ein `Input: … Output: … Quelle: …`. Die Quelle ist + der Skill-Abschnitt, das Skript oder die Agentendatei im Repo; keine Aufgabe ist `⚠ unverified`. + +## Vereinfachungen + +- **Agentic-Design-FAQ** ist als `gedächtnis` gezeichnet, ist aber ein wachsender Ordner. Die + Notation erwartet eine kuratierte Datei mit höchstens 150 Zeilen; ein Lauf durch die Pipeline würde + dafür einen Memory-Cap-Hook erzeugen, der nicht passt. +- **Offene Fachfragen beantworten** läuft im Diagramm immer; tatsächlich nur, wenn die + Wissensprüfung etwas findet. +- An der **Prüf-Obergrenze** zeigt das Diagramm zwei Wege. Den dritten, „verbleibende Befunde ins + Design“, lässt es weg. +- **Architekt und Librarian** teilen sich eine Aufgabe. Die Frage nach `trim-the-fat` steht nur in + der Dokumentation von „Erzeugte Dateien reviewen“, der Diff bei Folgeläufen nur in der von „BPMN + validieren & inventarisieren“. + +## Prüfen + +Von der Repo-Wurzel aus: + +```bash +.agents/skills/bpmn-authoring/scripts/validate.sh examples/lanecraft/lanecraft.bpmn +``` + +Stand: XSD gültig, 0 bpmn-moddle-Warnungen, 0 bpmnlint-Befunde; `inventory.mjs` meldet keine +Befunde und keinen untypisierten Task. + +Das Diagramm ist so breit, dass `render.mjs` es auf 1600 px zusammenstaucht. `lanecraft.png` ist +deshalb in voller Größe gerendert und auf 60 % verkleinert. diff --git a/examples/lanecraft/lanecraft.bpmn b/examples/lanecraft/lanecraft.bpmn new file mode 100644 index 0000000..b748a16 --- /dev/null +++ b/examples/lanecraft/lanecraft.bpmn @@ -0,0 +1,631 @@ + + + + + + + + + + + + Der Ablauf, den dieses Repo selbst umsetzt: ein Fachanwender bringt ein Ziel oder ein BPMN-Diagramm mit; optional entwirft bpmn-process-design den Prozess aus Notebook-Wissen, dann übersetzen die fünf Stufen-Skills bpmn2agent-analyze, -knowledge, -design, -generate und -verify ihn über die workflow-spec.yaml in Agenten, Skills, Hooks, Skripte und genau eine Orchestrierungsdatei unter generated/<workflow>/. Einstieg und Steuerung der Schleifen: bpmn-to-agentic-workflow. Helfer: agentic-workflow-architect, agentic-kb-librarian, agentic-artifact-reviewer, orchestration-design, agent-authoring, skill-authoring, trim-the-fat. Quelle: ARCHITECTURE.md. + + + + DataInput_Ziel + DataOutput_Workflow + + + + Start_Anfrage + U_Intake + Gw_DiagrammVorhanden + U_Entwurf + U_Luecken + U_Fachfragen + U_Mapping + Gw_Mapping + End_DiagrammAendern + U_Befunde + Gw_Befunde + End_DiagrammAendern2 + U_Installation + Gw_Installieren + End_Uebergeben + + + P_Recherche + Merge_Zeichnen + P_Zeichnen + P_Validieren + Gw_DiagrammGueltig + + + Merge_Analyse + A_Inventar + Gw_Luecken + Merge_Luecken + A_SpecEntwurf + K_Extrahieren + K_Hinterfragen + + + Merge_Design + D_Zuordnen + D_Rollen + D_Pruefen + D_Plan + D_Spec + + + Merge_Generierung + G_Artefakte + G_Orchestrierung + G_Rendern + G_Review + + + V_Pruefen + V_Routen + Gw_Verify + Merge_Uebergabe + V_Uebergabe + V_Kopieren + End_Installiert + + + + Ein Fachanwender hat ein Ziel oder ein gezeichnetes BPMN-Diagramm und möchte daraus prüfbare Claude-Code-Artefakte. Einstieg ist der Skill bpmn-to-agentic-workflow. + F01 + + + Input: Prozessziel oder Pfad zum .bpmn, Wunschname, Sprache. Output: Bestätigte Intake-Angaben: .bpmn (falls vorhanden), Workflow-Name in kebab-case, Sprache für Fragen und README, ob Gemini-Notebooks den Prozess abdecken. Bei vorhandenem generated/<workflow>/ ist es ein Folgelauf. Quelle: datei:.agents/skills/bpmn-to-agentic-workflow/SKILL.md §1 + F01 + F02 + + DataInput_ZielProperty_DataIn_Ziel_Intake + + + F02 + F03 + F04 + + + Input: Ziel, Auslöser, Endzustände, Beteiligte; gewählte Fach-Notebooks. Output: Prozessentwurf: Lanes, Schritte je Phase mit Typ (KI/Mensch/Skript/Regel) und Quelle, Gateways, Schleifen mit Obergrenze, Datenobjekte, Store-Tabelle, Prozess-Ein-/Ausgabe; jede Notebook-Antwort im FAQ unter generated/<workflow>/knowledge/faq/. Quelle: datei:.agents/skills/bpmn-process-design/SKILL.md §1-5 + F04 + F05 + + StoreRef_FachNotebooksRechercheProperty_DataIn_FachNB_Recherche + DO_Entwurf + + + Input: Prozessentwurf. Output: Entscheidung: so zeichnen, ändern oder Notebook weiter befragen. Nie wird ein unbestätigter Entwurf gezeichnet. Quelle: datei:.agents/skills/bpmn-process-design/SKILL.md §5 + F05 + F06 + + DO_EntwurfProperty_DataIn_Entwurf_U + + + F06 + F11 + F07 + + + Input: Bestätigter Prozessentwurf, lanecraft-Notation, Skeleton und Layout-Raster aus bpmn-authoring. Output: <workflow>.bpmn mit Lanes, typisierten Tasks, Datenobjekten, Data Stores (Art/Ort) und Input/Output-Dokumentation an jedem Task. Quelle: datei:.agents/skills/bpmn-authoring/SKILL.md §2-4 + F07 + F08 + + StoreRef_NotationProperty_DataIn_Notation_Zeichnen + DO_Bpmn + + + Input: <workflow>.bpmn. Output: Ergebnis von validate.sh (XSD, bpmn-moddle, bpmnlint) und inventory.mjs (findings leer, kein untypisierter Task), PNG je Ebene via render.mjs. Quelle: datei:.agents/skills/bpmn-process-design/SKILL.md §7 + F08 + F09 + + + F09 + F10 + F11 + + + F03 + F10 + F42 + F12 + + + Input: .bpmn-Diagramm. Output: Element-Inventar mit Signalen (menschliche Tasks, Parallelität, Schleifen), Data Stores und Prozess-Ein-/Ausgabe, Befunde zu nicht unterstützten Konstrukten; bei Folgeläufen Diff je Element-ID und sha256. Quelle: datei:.agents/skills/bpmn2agent-analyze/SKILL.md §1-4, scripts/inventory.mjs + F12 + F13 + + DO_BpmnProperty_DataIn_Bpmn_Inventar + + + F13 + F14 + F15 + + + Input: Befunde aus dem Inventar: fehlende Namen, fehlende Art/Ort-Zeilen, Pools, Timer, Kompensation u. a. Output: Antworten des Fachanwenders; nicht unterstützte Elemente werden umgebaut oder bewusst not-generated. Das .bpmn wird nie von der Pipeline geändert. Quelle: datei:.agents/skills/bpmn2agent-analyze/SKILL.md §5 + F14 + F16 + + + F15 + F16 + F17 + + + Input: Inventar, Antworten zu den Lücken. Output: generated/<workflow>/workflow-spec.yaml als Entwurf: jedes Element kind: unresolved, contextSources, workflowIO, meta mit sha256. Quelle: datei:.agents/skills/bpmn2agent-analyze/SKILL.md §6-7 + F17 + F18 + DO_SpecEntwurf + + + Input: Spec-Entwurf, Wissens-Stores je lesendem Task, sonst Notebook je Lane; Fallback WebSearch, Repo-Dokumente oder Modellwissen (unverified). Output: knowledge/<taskId>.md mit einem belegten Abschnitt je Store, FAQ-Einträge in knowledge/faq/, knowledge.refs in der Spec. Quelle: datei:.agents/skills/bpmn2agent-knowledge/SKILL.md §1-3, §5 + F18 + F19 + + + DO_SpecEntwurfProperty_DataIn_SpecEntwurf_Extr + StoreRef_FachNotebooksWissenProperty_DataIn_FachNB_Extr + DO_Wissen + + + Input: Wissenspaket, Spec-Entwurf, Challenge-Queries an dieselben Quellen. Output: openQuestions in der Spec: was die Quelle nahelegt, das Diagramm aber nicht hat. Agentic-Design-Fragen werden für design 6a vorgemerkt. Nie stilles Umbauen. Quelle: datei:.agents/skills/bpmn2agent-knowledge/SKILL.md §4 + F19 + F20 + + StoreRef_FachNotebooksWissenProperty_DataIn_FachNB_Hinterfragen + + + Input: openQuestions aus der Wissensprüfung. Output: Antwort je Frage in der Spec; verlangt eine Antwort eine Diagrammänderung, geht sie an den Modellierer zurück. Quelle: datei:.agents/skills/bpmn2agent-knowledge/SKILL.md §4 + F20 + F21 + + + F21 + F29 + F41 + F22 + + + Input: Spec mit Wissen und Antworten, Wissenspaket, Mapping- und Muster-Rubrik. Output: kind je Element (skill, Checkliste, Checkpoint, script, hook, Steuerlogik, context-source), Live-Tools per ToolSearch aufgelöst, Orchestrierungsmuster mit Begründung und verworfenen Alternativen. Quelle: datei:.agents/skills/bpmn2agent-design/SKILL.md §2-4 + F22 + F23 + + + DO_WissenProperty_DataIn_Wissen_Zuordnen + StoreRef_RubrikProperty_DataIn_Rubrik_Zuordnen + + + Input: Zuordnung und Muster. Output: roles (agentName, modelTier, tools) je Lane und artifacts (Pfadmuster, Frontmatter, Erzeuger, Verbraucher) je Datenobjekt; schneidet Rollen nach agent-authoring. Quelle: datei:.agents/skills/bpmn2agent-design/SKILL.md §5-6 + F23 + F24 + + + Input: workflow-spec.draft.yaml, .bpmn, vorgemerkte Agentic-Design-Fragen. Output: Befunde des agentic-workflow-architect (geroutet design, bpmn oder Frage) gegen orchestration-design; offene Designfragen beantwortet der agentic-kb-librarian aus der Wissensbasis, sonst aus dem Notebook, und hält die neue Antwort im FAQ fest. Quelle: datei:.agents/skills/bpmn2agent-design/SKILL.md §6a; .agents/agents/agentic-workflow-architect.md; .agents/agents/agentic-kb-librarian.md + F24 + F25 + + + + StoreRef_WissensbasisProperty_DataIn_Wissensbasis_Pruefen + StoreRef_AgenticNotebookProperty_DataIn_AgenticNotebook_Pruefen + StoreRef_AgenticFaqProperty_DataIn_AgenticFaq_Pruefen + StoreRef_AgenticFaq + + + Input: Geprüfter Spec-Entwurf. Output: Mapping-Plan in Fachsprache: was aus jedem Element wird, Muster, Rollen, Prüfpunkte, Kontextquellen mit Tools. Quelle: datei:.agents/skills/bpmn2agent-design/SKILL.md §7 + F25 + F26 + DO_MappingPlan + + + Input: Mapping-Plan. Output: Angenommen, einzelne Elemente anpassen oder Diagramm muss sich ändern. Quelle: datei:.agents/skills/bpmn2agent-design/SKILL.md §7; .agents/skills/bpmn-to-agentic-workflow/SKILL.md §4 + F26 + F27 + + DO_MappingPlanProperty_DataIn_MappingPlan_U + + + F27 + F28 + F29 + F30 + + + Schleife A: der Fachanwender ändert das .bpmn mit bpmn-authoring und startet neu; analyze vergleicht dann je Element-ID. + F30 + + + Input: Angenommener Mapping-Plan. Output: Bestätigte generated/<workflow>/workflow-spec.yaml; sicherer Punkt zum Fortsetzen in einer neuen Sitzung. Quelle: datei:.agents/skills/bpmn2agent-design/SKILL.md §8-9 + F28 + F31 + DO_Spec + + + F31 + F40 + F32 + + + Input: Bestätigte Spec, Generierungs-Vorlagen, knowledge/*.md. Output: generated/<workflow>/.claude/ mit skills/, agents/, hooks/*.mjs, settings.json; jede Datei mit bpmn: {file, elements}; Write-Guard- und Memory-Cap-Hook bei Kontextquellen. Quelle: datei:.agents/skills/bpmn2agent-generate/SKILL.md §1-6a; skill-authoring; agent-authoring + F32 + F33 + + + DO_SpecProperty_DataIn_Spec_Artefakte + StoreRef_VorlagenProperty_DataIn_Vorlagen_Artefakte + DO_Artefakte + + + Input: Spec, geschriebene Artefakte. Output: Genau eine Orchestrierungsdatei (Skill-Kette, Workflow-Skript oder Orchestrator-Agent), README.md, mapping/report.md. Quelle: datei:.agents/skills/bpmn2agent-generate/SKILL.md §7-9 + F33 + F34 + + + Input: Spec, .bpmn. Output: mapping/workflow-mapped.bpmn (farbig), index.html, PNGs. Quelle: datei:.agents/skills/bpmn2agent-generate/scripts/render-mapping.mjs + F34 + F35 + + + Input: generated/<workflow>/. Output: Selbstcheck (Header, node --check, settings.json) und Befunde des agentic-artifact-reviewer; generate-Befunde sofort umgesetzt, hohe design-Befunde zurück ins Design, trim nur mit Zustimmung (trim-the-fat). Quelle: datei:.agents/skills/bpmn2agent-generate/SKILL.md §11; .agents/agents/agentic-artifact-reviewer.md + F35 + F36 + + + Input: generated/<workflow>/, Quell-.bpmn. Output: Ergebnis von verify.mjs in den Kategorien spec-schema, source-bpmn, element-to-artifact, artifact-to-element, lint, no-red und ggf. context-sources. Quelle: datei:.agents/skills/bpmn2agent-verify/scripts/verify.mjs + F36 + F37 + + DO_ArtefakteProperty_DataIn_Artefakte_Pruefen + + + Input: verify.mjs-Ergebnis. Output: Prüfbericht in Fachsprache, jeder Befund mit Ziel: analyze, design oder generate. Quelle: datei:.agents/skills/bpmn2agent-verify/SKILL.md §2 + F37 + F38 + DO_Pruefbericht + + + F38 + F39 + F40 + F41 + F42 + F43 + + + Input: Prüfbericht nach der dritten Runde oder bei unverändert wiederkehrendem Befund. Output: Entscheidung: als unverifiziert übergeben oder Diagramm ändern. Nie wird am Cap Erfolg gemeldet. Quelle: datei:.agents/skills/bpmn-to-agentic-workflow/SKILL.md §6 + F43 + F44 + + + F44 + F45 + F46 + + + Schleife A nach erreichter Prüf-Obergrenze. + F46 + + + F39 + F45 + F47 + + + Input: Prüfbericht, Spec, generated/<workflow>/. Output: Bericht in der Sprache des Anwenders: Anzahl Agenten/Skills/Skripte/Hooks, Orchestrierungsdatei und Muster, Ort, offene Punkte (rot/grau, offene Fragen), ggf. Vermerk unverifiziert, Installationshinweis. Quelle: datei:.agents/skills/bpmn-to-agentic-workflow/SKILL.md §7 + F47 + F48 + + DO_PruefberichtProperty_DataIn_Pruefbericht_Uebergabe + DataOutput_Workflow + + + Input: Übergabebericht. Output: Ob und wohin der generierte Workflow installiert wird. Die Pipeline schreibt nie selbst in ein echtes .claude/. Quelle: datei:ARCHITECTURE.md §Ergebnis und Rückverfolgbarkeit + F48 + F49 + + + F49 + F50 + F51 + + + F50 + + + Input: generated/<workflow>/.claude/, Zielprojekt. Output: cp -R generated/<workflow>/.claude/. <projekt>/.claude/; hooks in einer vorhandenen settings.json von Hand zusammenführen. Quelle: datei:.agents/skills/bpmn-to-agentic-workflow/SKILL.md §7 + F51 + F52 + StoreRef_Projekt + + + F52 + + + + + + Nein + + + + + + + + + Nein (max. 3×) + + + + + Ja + + + + + + + + + + + + + + + + + Elemente anpassen (max. 3×) + + + Diagramm ändern + + + + + + + + + + + + Generierungsfehler (max. 3×) + + + Mapping-Problem (max. 3×) + + + .bpmn geändert (max. 3×) + + + Obergrenze erreicht + + + + + Diagramm ändern + + + + + + + Ja + + + + + + + + + + + + + + + + + + + + Art: live +Ort: mcp:gemini-notebook-mcp + + + Art: wissen +Ort: datei:BPMN-NOTATION.md + + + Art: live +Ort: mcp:gemini-notebook-mcp + + + Art: wissen +Ort: datei:.agents/skills/bpmn2agent-design/references/ + + + Art: wissen +Ort: datei:.agents/skills/agentic-workflow-kb/references/ + + + Art: live +Ort: mcp:gemini-notebook-mcp + + + Art: gedächtnis +Ort: datei:.agents/skills/agentic-workflow-kb/faq/ + + + Art: wissen +Ort: datei:.agents/skills/bpmn2agent-generate/assets/templates/ + + + Art: live +Ort: datei:<projekt>/.claude/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/lanecraft/lanecraft.png b/examples/lanecraft/lanecraft.png new file mode 100644 index 0000000..09bea7d Binary files /dev/null and b/examples/lanecraft/lanecraft.png differ diff --git a/examples/user-story-refinement/README.md b/examples/user-story-refinement/README.md index b647d09..25fd432 100644 --- a/examples/user-story-refinement/README.md +++ b/examples/user-story-refinement/README.md @@ -20,20 +20,33 @@ wirklich etwas unklar ist. | Phase | Inhalt | Entscheidung / Schleife | |---|---|---| -| 1 Eingang & Triage | Feedback als Problem formulieren (nicht als Lösungswunsch), verwandte Stories und Epics im Backlog suchen | *Fehlverhalten einer bestehenden Funktion?* (→ Fehlerbearbeitung) · *Wie hängt das Anliegen mit dem Backlog zusammen?* (Änderung an offener Story → dorthin übergeben, kein Epic-Bezug → im Opportunity Backlog parken, sonst neue Story unter bestehendem Epic) | +| 1 Eingang & Triage | Feedback als Problem formulieren (nicht als Lösungswunsch), verwandte Stories und Epics im Backlog suchen | *Fehlverhalten einer bestehenden Funktion?* (→ Fehlerbearbeitung) · *Wie hängt das Anliegen mit dem Backlog zusammen?* (Änderung an offener Story → Hinweis freigeben, dann als Kommentar an die Story; kein Epic-Bezug → Parken freigeben, dann im Opportunity Backlog anlegen; sonst neue Story unter bestehendem Epic) | | 2 Fachliche Klärung | Rückfrage beim Feedbackgeber, Prüfung gegen Epic-Ziel und Fachkonzept, danach parallel: Ist-/Soll-Delta (PO), betroffene Screens (UX), technische Grobeinschätzung (Entwicklung) | *Mit dem Fachkonzept vereinbar?* (sonst Fachkonzept-Änderung beantragen) · *Relevante Unsicherheit offen?* (technisch → Spike, Nutzen/Bedienbarkeit → Prototyp-Test, sonst direkt weiter) · *Tragen die Erkenntnisse die Story?* | | 3 Story formulieren | Einordnung unter den Epic in der User Story Map, Connextra-Format, Bezug zu Epic, Fachkonzept und auslösendem Feedback, INVEST-Selbstcheck | *INVEST erfüllt?* (umformulieren) | | 4 Refinement (Three Amigos) | parallel Was (PO), Wie (Entwicklung), Rand- und Negativfälle (QA), Wireframes (UX); danach Given-When-Then, Beispieltabellen, Planning Poker | *Schätzungen konvergiert?* · *Passt die Story in einen Sprint?* (vertikal schneiden, Teil-Stories neu formulieren) | | 5 Definition of Ready | DoR-Checkliste, Abgleich mit dem Domänenmodell, Prüfung auf Fake- oder Waisen-Story | *Definition of Ready erfüllt?* (Kriterien unklar → Refinement, technisches Risiko → Spike → Refinement, nicht mehr relevant → verwerfen) | -| 6 Plausibilität gegen Backlog & Freigabe | parallel: Duplikate/Überschneidungen (PO), Konsistenz mit bestehenden Abläufen (UX), Abhängigkeiten und Reihenfolge (Entwicklung), Akzeptanzkriterien gegen andere Stories und Living Documentation (QA); Befund zusammenführen, betroffene Stories markieren, priorisieren | *Konsistent mit dem übrigen Backlog?* (Widerspruch → zurück ins Refinement, Duplikat → mit bestehender Story zusammenführen) | +| 6 Plausibilität gegen Backlog & Freigabe | parallel: Duplikate/Überschneidungen (PO), Konsistenz mit bestehenden Abläufen (UX), Abhängigkeiten und Reihenfolge (Entwicklung), Akzeptanzkriterien gegen andere Stories und Living Documentation (QA); Befund zusammenführen, Anpassungen an betroffenen Stories vorschlagen, priorisieren und die Übernahme freigeben, dann Story im Backlog anlegen und betroffene Stories markieren | *Konsistent mit dem übrigen Backlog?* (Widerspruch → zurück ins Refinement, Duplikat → Zusammenführung freigeben, dann mit bestehender Story zusammenführen) | - **Aufgabentypen** nach der Lesart von `bpmn2agent-analyze`: `serviceTask` = Agentenarbeit, `userTask` = menschlicher Kontrollpunkt (Rückfrage beim Feedbackgeber, Auswertung von Spike oder - Prototyp-Test, Refinement-Termin, Planning Poker, Priorisierung), `businessRuleTask` = Prüfung gegen + Prototyp-Test, Refinement-Termin, Planning Poker, Priorisierung und Freigabe, dazu je eine Freigabe + vor dem Änderungshinweis, dem Parken und der Zusammenführung), `businessRuleTask` = Prüfung gegen eine Checkliste (INVEST, DoR). -- Jede Aufgabe trägt in `` ein `Input: … Output: …`. Zwölf Datenobjekte zeigen - die wichtigsten Artefakte, darunter drei Eingaben aus dem Projektrahmen: Product Backlog (zweimal: - bei der Triage und bei der Duplikatprüfung) sowie Epic & Fachkonzept. +- Jede Aufgabe trägt in `` ein `Input: … Output: …`. Zehn Datenobjekte zeigen + die wichtigsten Artefakte, darunter die Eingabe Epic & Fachkonzept aus dem Projektrahmen. +- **Sechs Wissens-Stores**, je einer pro Phase (`Product-Methodik: Eingang & Triage`, `… Fachliche + Klärung`, `… Story formulieren`, `… Refinement`, `… Definition of Ready`, `… Plausibilität & + Freigabe`). Alle tragen `Art: wissen` und `Ort: notebook:The Product - Business Design`, also + dasselbe Notebook, nur fachlich nach Phase geschnitten. Die Pfeile gehen vom Store zur Aufgabe + (lesen): Triage → I1, I2 · Klärung → K1, K2, K3, K4, B5 · Story formulieren → C1, C2, C3, C4 · + Refinement → D1, D2a, D3, D4, D5, D6 · DoR → E1, E3 · Plausibilität → E4, P1, P4, P5, P6. + Aufgaben ohne Pfeil von einem Wissens-Store fallen auf die Lane-Zuordnung von + `bpmn2agent-knowledge` zurück. +- **Drei Live-Stores für das Product Backlog** in GitHub (`Art: live`, `Ort: cli:gh`), nach Phase + geschnitten: `Product Backlog & User Story Map` (gelesen von I2, geschrieben von I4 und I6), + `Product Backlog: übrige Stories` (gelesen von P1, P2, P3) und `Product Backlog: freigegebene + Änderungen` (geschrieben von P6, P8, P10). Vor jedem Schreiben steht ein User Task als Freigabe: + I3 vor I4, I5 vor I6, P9 vor P6, E4 vor P8 und P10. ## Wissensgrundlage @@ -61,10 +74,13 @@ Stand: XSD gültig, 0 bpmn-moddle-Warnungen, 0 bpmnlint-Befunde. ## Ergebnis der Pipeline -Am 2026-09-26 mit `bpmn-to-agentic-workflow` durchgelaufen, als erstes Beispiel im neuen Aufbau: -[`generated/user-story-refinement/`](generated/user-story-refinement/). Muster Skill-Kette, 7 Skills -(ein Einstieg, sechs Phasen-Skills mit dem Notebook-Wissen als `references/`), keine Agenten, keine -Skripte, keine Hooks. Installiert wird mit einer Kopie: +Am 2026-09-26 mit `bpmn-to-agentic-workflow` durchgelaufen, als erstes Beispiel im neuen Aufbau, und +am 2026-10-04 mit dem GitHub-Backlog erneut: +[`generated/user-story-refinement/`](generated/user-story-refinement/). Muster Skill-Kette + Hook, +8 Skills (ein Einstieg, sechs Phasen-Skills mit dem Notebook-Wissen als `references/`, ein +Schreib-Skill `story-backlog-publish`), ein Write-Guard-Hook und eine `settings.json` mit den +`gh`-Lesebefehlen; keine Agenten, keine Skripte. Installiert wird mit einer Kopie (eine vorhandene +`settings.json` von Hand zusammenführen): ```bash cp -R generated/user-story-refinement/.claude/. /.claude/ diff --git a/examples/user-story-refinement/generated/user-story-refinement/.claude/hooks/user-story-refinement-write-guard.mjs b/examples/user-story-refinement/generated/user-story-refinement/.claude/hooks/user-story-refinement-write-guard.mjs new file mode 100644 index 0000000..1c2deca --- /dev/null +++ b/examples/user-story-refinement/generated/user-story-refinement/.claude/hooks/user-story-refinement-write-guard.mjs @@ -0,0 +1,68 @@ +#!/usr/bin/env node +// bpmn: {"file":"user-story-refinement.bpmn","elements":["StoreRef_BacklogTriage","StoreRef_BacklogFreigabe"]} +// +// Claude Code PreToolUse hook, generated by bpmn2agent-generate from the live data stores +// "Product Backlog & User Story Map" and "Product Backlog: freigegebene Änderungen" in +// user-story-refinement.bpmn. Backs up the diagram's approval steps: every gh call that is not a +// known read makes Claude Code ask the human at the call (permissionDecision "ask"), even if a skill +// forgot the approval. Reads and every other tool pass untouched. CLAUDE-ONLY: hooks are a Claude +// Code mechanism. +// +// The command is split on shell operators (; && || | newline $( ) backticks) and gh is looked for in +// every segment, also after env assignments, env/command/exec, as a path ending in /gh, or inside +// bash -c / sh -c (quotes are ignored, so the inner command is split too). A segment counts as a read +// only when its gh subcommand is in READS; anything else, including gh api and flags before the +// subcommand, counts as a write. The hook does not pin repo or project: the skills pass -R/--owner and +// the approval steps show the target. +// +// Reads the PreToolUse hook JSON on stdin. Prints the decision JSON on stdout and exits 0 when the +// call may write into the backlog; prints nothing and exits 0 otherwise. + +// Read patterns from contextSources.*.tools.read (Bash(gh :*)). +const READS = [ + 'auth status', 'issue list', 'issue view', 'search issues', + 'project view', 'project item-list', 'project field-list', +]; + +// One entry per live store with tools.write. +const STORES = [ + { + name: 'Product Backlog & User Story Map', + approval: 'Änderungshinweis an offene Story freigeben / Parken im Opportunity Backlog freigeben', + bash: ['gh issue comment', 'gh issue create'], + }, + { + name: 'Product Backlog: freigegebene Änderungen', + approval: 'Zusammenführung freigeben / Story priorisieren & Übernahme ins Backlog freigeben', + bash: ['gh issue create', 'gh issue edit', 'gh issue comment', 'gh project item-add', 'gh project item-edit'], + }, +]; + +const input = JSON.parse(await new Response(process.stdin).text()); +if (input.tool_name !== 'Bash') process.exit(0); +const command = String(input.tool_input?.command ?? ''); + +const isGh = (t) => t === 'gh' || t.endsWith('/gh'); +const writes = []; +for (const segment of command.split(/;|&&|\|\||\||\n|\$\(|\)|`/)) { + const tokens = segment.replace(/["']/g, ' ').trim().split(/\s+/).filter(Boolean); + const i = tokens.findIndex(isGh); + if (i === -1) continue; + const sub = tokens.slice(i + 1, i + 3).join(' '); + if (!READS.includes(sub)) writes.push(`gh ${sub}`.trim()); +} + +if (writes.length) { + const hit = STORES.filter((s) => writes.some((w) => s.bash.includes(w))); + const stores = (hit.length ? hit : STORES); + console.log(JSON.stringify({ + hookSpecificOutput: { + hookEventName: 'PreToolUse', + permissionDecision: 'ask', + permissionDecisionReason: + `"${writes.join('", "')}" may write into ${stores.map((s) => `"${s.name}"`).join(' / ')}; ` + + `needs approval (step "${stores.map((s) => s.approval).join(' / ')}")`, + }, + })); +} +process.exit(0); diff --git a/examples/user-story-refinement/generated/user-story-refinement/.claude/settings.json b/examples/user-story-refinement/generated/user-story-refinement/.claude/settings.json new file mode 100644 index 0000000..38b0fa8 --- /dev/null +++ b/examples/user-story-refinement/generated/user-story-refinement/.claude/settings.json @@ -0,0 +1,25 @@ +{ + "permissions": { + "allow": [ + "Bash(gh auth status:*)", + "Bash(gh issue list:*)", + "Bash(gh issue view:*)", + "Bash(gh search issues:*)", + "Bash(gh project view:*)", + "Bash(gh project item-list:*)" + ] + }, + "hooks": { + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/user-story-refinement-write-guard.mjs" + } + ] + } + ] + } +} diff --git a/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-check/SKILL.md b/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-check/SKILL.md index 179c488..0fd0685 100644 --- a/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-check/SKILL.md +++ b/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-check/SKILL.md @@ -1,16 +1,25 @@ --- name: story-backlog-check -description: Checks a ready user story for plausibility against the rest of the backlog — duplicates and overlaps, consistency with existing flows, dependencies and order, acceptance criteria against other stories and the living documentation — merges the findings, and writes merge and adjustment proposals for affected stories without touching the backlog. Use for the "Plausibilität gegen Backlog & Freigabe" phase of the user-story-refinement flow. +description: Checks a ready user story for plausibility against the rest of the GitHub backlog — duplicates and overlaps, consistency with existing flows, dependencies and order, acceptance criteria against other stories and the living documentation — merges the findings and proposes changes to affected stories, reading the backlog only. Use for the "Plausibilität gegen Backlog & Freigabe" phase of the user-story-refinement flow; the approved changes are written later by story-backlog-publish. bpmn: file: user-story-refinement.bpmn - elements: [P1, P7, P3, P2, P4, P5, P6] + elements: [P1, P7, P3, P2, P4, P5] --- # Plausibilität gegen Backlog Phase 6 of `user-story-refinement`. Criteria and sources: -`${CLAUDE_SKILL_DIR}/references/domain-knowledge.md`. The backlog and other stories are read-only: -everything that would change them goes into `09_aenderungsvorschlaege.md` for the team. +`${CLAUDE_SKILL_DIR}/references/domain-knowledge.md`. This skill only reads the backlog: every change +it finds goes into `09_aenderungsvorschlaege.md` as a proposal; `story-backlog-publish` writes it after +the approval. + +**Reading the backlog.** Repo and project come from `backlog` in `01_feedback.md`. Always pass +`-R ` (or `--repo` for `gh search issues`), ask for `--json` with only the fields you need +and a `--limit`, and open single issues on demand with `gh issue view`. A failed read (not logged in, +missing scope, wrong repo or project) is an error, not an empty result: show the raw error and ask +via `AskUserQuestion` whether to fix access and retry (recommended) or continue with the risk +"Backlog nicht gelesen" written into `08_plausibilitaet.md`. Record per perspective the query, the +outcome and the number of hits. ## Four perspectives → `stories//08_plausibilitaet.md` @@ -32,14 +41,19 @@ From the file only: duplicates, contradictions, dependencies, affected stories; for "Konsistent mit dem übrigen Backlog?": Ja / Widerspruch zu anderen Stories / Duplikat einer bestehenden Story, with the reason. `status: done`. -## "Mit bestehender Story zusammenführen" (only for "Duplikat") +## "Anpassungen an betroffenen Stories vorschlagen" (only for "Ja") -Write the merge proposal to `09_aenderungsvorschlaege.md`: target story, the acceptance criteria and -the feedback link to add, combined estimate if several overlap (`status: merge-proposed`). Set -`04_story.md` to `status: merge-proposed`. The calling skill asks before the merge counts as done. +From the findings: one entry per affected or superseded story under `proposals` in +`09_aenderungsvorschlaege.md` — target issue URL, the exact note to post (what to change, dependency +link, reason), `baseUpdatedAt` from `gh issue view -R --json updatedAt`, `decision` empty. +No findings → write "keine". `status: proposed`. Nothing is written into the backlog here. -## "Betroffene Stories zur Anpassung markieren" (only for "Ja") +## Kontextquellen -From the findings: one entry per affected or superseded story in `09_aenderungsvorschlaege.md` -(story, what to change, dependency link, reason). No findings → write "keine". -`status: proposed`. +Knowledge stores, distilled at generation time into one file per task; no tool call at run time. `${CLAUDE_SKILL_DIR}/references/domain-knowledge.md` keeps the phase-level criteria. + +- **Product-Methodik: Plausibilität & Freigabe** (wissen): "Auf Duplikate & Überschneidungen prüfen" → `${CLAUDE_SKILL_DIR}/references/P1.md`; "Plausibilitätsbefund zusammenführen" → `${CLAUDE_SKILL_DIR}/references/P4.md`; "Anpassungen an betroffenen Stories vorschlagen" → `${CLAUDE_SKILL_DIR}/references/P5.md` + +Live and memory stores, used at run time: + +- **Product Backlog: übrige Stories** (live, read): "Auf Duplikate & Überschneidungen prüfen" searches open and done stories with the same goal or concept with `Bash(gh search issues:*)` and `Bash(gh issue list:*)`; "Abhängigkeiten & Reihenfolge prüfen" lists the project's items and their order with `Bash(gh project item-list:*)`; "Akzeptanzkriterien gegen bestehende Stories abgleichen" reads the scenarios of candidate stories with `Bash(gh issue view:*)`. diff --git a/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-check/references/P1.md b/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-check/references/P1.md new file mode 100644 index 0000000..78250cd --- /dev/null +++ b/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-check/references/P1.md @@ -0,0 +1,59 @@ +--- +element: P1 +evidence: cited +sources: ["The Product - Business Design"] +bpmn: + file: user-story-refinement.bpmn + elements: [P1] +--- + +## Product-Methodik: Plausibilität & Freigabe + +_Ort: notebook:The Product - Business Design_ + +_Footnotes name the notebook source. The verbatim quotes and FAQ references stay with the generation output (`knowledge/`), which is not installed._ + +**Task:** "Auf Duplikate & Überschneidungen prüfen" (PO). Input: the new ready story plus the whole product backlog. + +**Procedure (in order)** + +1. Compare goal and user need (role, "I want", "so that") with existing items. Two stories can be worded or solved + differently and still serve the same need [^1]. +2. Compare terms with glossary and domain model: the same concept may sit under another name (synonym), or the + same word may mean two things (false cognate, e.g. "Charge") [^2][^3]. +3. Compare scope and acceptance criteria: shared functional area or shared test cases signal overlap [^4]. +4. Classify and decide: + - **Full duplicate** (same concept or intent): discard or archive the new story, or link it to the existing + item; never keep the concept described twice [^2][^5]. + - **Partial overlap** (different output, shared work): combine for planning, split again only when slotted + into an iteration [^4]. Hand over to "Mit bestehender Story zusammenführen". + +**Output:** duplicate/overlap findings with the compared stories named; cross-references (Querbezüge) between +interdependent stories [^6]. + +**Checklist** + +- Concrete role? Generic roles ("As a user ...") hide duplicates and pet features [^7]. +- Is the story a solution rather than a need? Compare the goal behind it [^1]. +- Checked the documented "Won't" items, so already rejected requests are not analysed again [^5]. +- Compared against current functional specification and tests, not by reading old stories in reverse + chronological order [^8]. +- Reviewer is not the story's author: authors miss their own duplicates [^9]. + +**Pitfalls** + +- Same wording taken as the same concept (false cognate) [^3]. +- Small overlapping stories left uncombined; estimates then depend on build order [^4]. + +**Not answered by the notebook:** no similarity threshold or tooling is given for deciding "duplicate" versus +"overlap" beyond the definitions above. + +[^1]: "Adzic et al., Fifty Quick Ideas to Improve Your User Stories (2014)", NotebookLM source, queried 2026-10-04 +[^2]: "Evans, Domain-Driven Design (2003)", NotebookLM source, queried 2026-10-04 +[^3]: "Evans, Domain-Driven Design (2003)", NotebookLM source, queried 2026-10-04 +[^4]: "Cohn, User Stories Applied (2004)", NotebookLM source, queried 2026-10-04 +[^5]: "Anforderungsverwaltung.pdf", NotebookLM source, queried 2026-10-04 +[^6]: "Pohl & Rupp, Basiswissen Requirements Engineering (2015)", NotebookLM source, queried 2026-10-04 +[^7]: "Adzic et al., Fifty Quick Ideas to Improve Your User Stories (2014)", NotebookLM source, queried 2026-10-04 +[^8]: "Adzic et al., Fifty Quick Ideas to Improve Your User Stories (2014)", NotebookLM source, queried 2026-10-04 +[^9]: "Pohl & Rupp, Basiswissen Requirements Engineering (2015)", NotebookLM source, queried 2026-09-26 diff --git a/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-check/references/P4.md b/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-check/references/P4.md new file mode 100644 index 0000000..0535851 --- /dev/null +++ b/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-check/references/P4.md @@ -0,0 +1,65 @@ +--- +element: P4 +evidence: cited +sources: ["The Product - Business Design"] +bpmn: + file: user-story-refinement.bpmn + elements: [P4] +--- + +## Product-Methodik: Plausibilität & Freigabe + +_Ort: notebook:The Product - Business Design_ + +_Footnotes name the notebook source. The verbatim quotes and FAQ references stay with the generation output (`knowledge/`), which is not installed._ + +**Task:** "Plausibilitätsbefund zusammenführen" (PO). Merges the four parallel checks (duplicates, flow consistency, +dependencies, acceptance criteria) into one report that feeds "Konsistent mit dem übrigen Backlog?". + +**Procedure** + +1. Collect all findings and consolidate: merge duplicate findings, drop false findings (a reviewer's wrong + assumption or misreading), document the rest in one findings list with corrective actions [^1]. +2. Where reviewers contradict each other, identify and analyse the conflict (factual, interest, value, + relationship, structural) before resolving it, and document the resolution [^2][^3]. +3. Resolve by agreement on facts, compromise, voting, decision matrix; hierarchical decision only if the + others fail [^4][^5]. Comparing independent results in a time-boxed "diverge and merge" exposes + misunderstandings [^6]. +4. Derive the verdict against criteria fixed beforehand [^7]. + +**What the report lists** + +- Story id/version and the affected items (linked stories, flows, criteria); each finding with ID, source + perspective, classification, criticality and status [^8][^9]. +- Quality aspect per finding: content, documentation, agreement [^10]. +- Status values: review (ungeprüft, in Prüfung, überprüft, fehlerhaft, in Korrektur) and agreement (nicht + abgestimmt, abgestimmt, konfliktär) [^9]. +- Open points that cannot be clarified directly go on an open-points list (OPL) [^11]. + +**Verdict** + +- Release only when the story meets the agreed quality criteria: consistent (no contradiction with other + requirements), testable, feasible, agreed [^7][^12]. +- Rework: reviewers only document findings; correction is a separate step, status "in Korrektur" [^13]. +- Reject/drop (e.g. unresolved duplicate): record the reason; rejected items stay documented as "Won't" [^14]. + +**Pitfalls** + +- Shying away from conflict with the requester, or not questioning incoming requirements [^15]. +- Not answered by the notebook: the concrete risks of leaving a conflict resolution undocumented. + +[^1]: "Pohl & Rupp, Basiswissen Requirements Engineering (2015)", NotebookLM source, queried 2026-10-04 +[^2]: "Pohl & Rupp, Basiswissen Requirements Engineering (2015)", NotebookLM source, queried 2026-10-04 +[^3]: "Pohl & Rupp, Basiswissen Requirements Engineering (2015)", NotebookLM source, queried 2026-10-04 +[^4]: "Pohl & Rupp, Basiswissen Requirements Engineering (2015)", NotebookLM source, queried 2026-10-04 +[^5]: "Pohl & Rupp, Basiswissen Requirements Engineering (2015)", NotebookLM source, queried 2026-10-04 +[^6]: "Adzic et al., Fifty Quick Ideas to Improve Your User Stories (2014)", NotebookLM source, queried 2026-10-04 +[^7]: "Pohl & Rupp, Basiswissen Requirements Engineering (2015)", NotebookLM source, queried 2026-10-04 +[^8]: "Anforderungserhebung.pdf", NotebookLM source, queried 2026-10-04 +[^9]: "Pohl & Rupp, Basiswissen Requirements Engineering (2015)", NotebookLM source, queried 2026-10-04 +[^10]: "Pohl & Rupp, Basiswissen Requirements Engineering (2015)", NotebookLM source, queried 2026-10-04 +[^11]: "Einführung in die Business Analyse.pdf", NotebookLM source, queried 2026-10-04 +[^12]: "Pohl & Rupp, Basiswissen Requirements Engineering (2015)", NotebookLM source, queried 2026-10-04 +[^13]: "Pohl & Rupp, Basiswissen Requirements Engineering (2015)", NotebookLM source, queried 2026-10-04 +[^14]: "Anforderungsverwaltung.pdf", NotebookLM source, queried 2026-10-04 +[^15]: "Einführung in die Business Analyse.pdf", NotebookLM source, queried 2026-10-04 diff --git a/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-check/references/P5.md b/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-check/references/P5.md new file mode 100644 index 0000000..2998593 --- /dev/null +++ b/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-check/references/P5.md @@ -0,0 +1,69 @@ +--- +element: P5 +evidence: cited +sources: ["The Product - Business Design"] +bpmn: + file: user-story-refinement.bpmn + elements: [P5] +--- + +## Product-Methodik: Plausibilität & Freigabe + +_Ort: notebook:The Product - Business Design_ + +_Footnotes name the notebook source. The verbatim quotes and FAQ references stay with the generation output (`knowledge/`), which is not installed._ + +**Task:** "Anpassungen an betroffenen Stories vorschlagen" (PO). Proposes changes to stories touched by the new +story; nothing is written here. After the PO's approval, "Betroffene Stories im Backlog markieren" applies them. + +**Procedure** + +1. Impact analysis (Auswirkungsanalyse) over the traceability links: from the new or changed story find the + artifacts that realise or relate to it [^1]. Useful relations between stories: refines, generalises, + replaces [^2]. +2. Where no traceability information exists, ask domain experts and the development team about the + consequences [^3]. +3. Mark each affected story for adjustment, e.g. review status "in Korrektur" [^4]. +4. Record on each marked story (derived from the change-request template): + - **what changes**, as the contrast to today: "Whereas currently ..." / "Instead of ..." [^5]; + - **why** (Begründung) [^6]; + - **who is responsible** and, where a change board exists, its decision and status of the impact + analysis [^7]; + - **new version**: small change raises the increment, larger change the version (v1.0 -> v1.1) [^8][^9]. +5. A story that is replaced gets the relation "replaces" and an explicit state with the reason; rejected or not + implemented items stay documented so they are not proposed again [^2][^10][^11]. +6. Time-sensitive stories carry a "best before" date [^12]. + +**Checklist** + +- Impact traced in both directions (sources/goals above, tests/components below) [^2]. +- Each mark states what, why, who decides, version. +- Nothing is deleted silently; the reason is recorded [^10][^11]. + +**Pitfalls** + +- Stale documents left active cause confusion; documents nobody updates should be archived as history [^13]. +- Silent changes: "Dokumentiere Änderungen und Entscheidungen so, dass alle Beteiligten verstehen, warum etwas + angepasst wurde." [^14] +- Over-marking: record only traceability information with a defined use [^15]; numbering cards adds + "pointless overhead" [^16]. + +**Not answered by the notebook:** no source defines a status literally named "superseded"; the sources give +the relation "ersetzt", the review status "in Korrektur" and the "Won't" category. + +[^1]: "Pohl & Rupp, Basiswissen Requirements Engineering (2015)", NotebookLM source, queried 2026-10-04 +[^2]: "Anforderungsverwaltung.pdf", NotebookLM source, queried 2026-10-04 +[^3]: "Pohl & Rupp, Basiswissen Requirements Engineering (2015)", NotebookLM source, queried 2026-10-04 +[^4]: "Pohl & Rupp, Basiswissen Requirements Engineering (2015)", NotebookLM source, queried 2026-10-04 +[^5]: "Adzic et al., Fifty Quick Ideas to Improve Your User Stories (2014)", NotebookLM source, queried 2026-10-04 +[^6]: "Pohl & Rupp, Basiswissen Requirements Engineering (2015)", NotebookLM source, queried 2026-10-04 +[^7]: "Pohl & Rupp, Basiswissen Requirements Engineering (2015)", NotebookLM source, queried 2026-10-04 +[^8]: "Pohl & Rupp, Basiswissen Requirements Engineering (2015)", NotebookLM source, queried 2026-10-04 +[^9]: "Anforderungsverwaltung.pdf", NotebookLM source, queried 2026-10-04 +[^10]: "Anforderungsverwaltung.pdf", NotebookLM source, queried 2026-10-04 +[^11]: "Richter, The Product Manager's Playbook (2023)", NotebookLM source, queried 2026-09-26 +[^12]: "Adzic et al., Fifty Quick Ideas to Improve Your User Stories (2014)", NotebookLM source, queried 2026-09-26 +[^13]: "Evans, Domain-Driven Design (2003)", NotebookLM source, queried 2026-10-04 +[^14]: "Anforderungsverwaltung.pdf", NotebookLM source, queried 2026-10-04 +[^15]: "Pohl & Rupp, Basiswissen Requirements Engineering (2015)", NotebookLM source, queried 2026-10-04 +[^16]: "Cohn, User Stories Applied (2004)", NotebookLM source, queried 2026-10-04 diff --git a/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-check/references/domain-knowledge.md b/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-check/references/domain-knowledge.md index 45678bd..3720d14 100644 --- a/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-check/references/domain-knowledge.md +++ b/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-check/references/domain-knowledge.md @@ -36,7 +36,7 @@ back to refinement (loop cap 3); duplicate → merge; otherwise continue. link to the existing story; a cluster of overlaps becomes one header story with the others as bullets and a combined estimate [backlog-plausibilitaet-1: 17, 18, 19, 20, 21]. The new card moves to a rejected/trash state with the reason recorded [backlog-plausibilitaet-1: 28, 29]. -- **"Betroffene Stories zur Anpassung markieren"** — impact analysis over the traceability links; +- **"Anpassungen an betroffenen Stories vorschlagen"** (applied after approval by "Betroffene Stories im Backlog markieren") — impact analysis over the traceability links; affected stories get a note and a new version, superseded ones a recorded reason [backlog-plausibilitaet-1: 23, 24, 25, 26, 27, 28, 29]. Time-sensitive stories get a "best before" date [backlog-plausibilitaet-1: 31, 32]. diff --git a/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-publish/SKILL.md b/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-publish/SKILL.md new file mode 100644 index 0000000..ee22e41 --- /dev/null +++ b/examples/user-story-refinement/generated/user-story-refinement/.claude/skills/story-backlog-publish/SKILL.md @@ -0,0 +1,121 @@ +--- +name: story-backlog-publish +description: Writes approved changes into the GitHub product backlog with gh — the note on an open story, the Opportunity Backlog entry, the merge into a duplicate, the new story in the Ready column and the marks on affected stories — only after the matching approval and never twice on a resumed run. Use only from the user-story-refinement skill chain, right after "Änderungshinweis an offene Story freigeben", "Parken im Opportunity Backlog freigeben", "Zusammenführung freigeben" or "Story priorisieren & Übernahme ins Backlog freigeben". +bpmn: + file: user-story-refinement.bpmn + elements: [I4, I6, P6, P8, P10] +--- + +# Freigegebene Änderungen ins Backlog schreiben + +Generated from `user-story-refinement.bpmn`'s write steps "Änderungshinweis an offener Story ergänzen" +(I4), "Feedback im Opportunity Backlog anlegen" (I6), "Mit bestehender Story zusammenführen" (P6), +"Story im Backlog anlegen & in Ready-Spalte stellen" (P8) and "Betroffene Stories im Backlog +markieren" (P10) (serviceTasks, lane "Product Owner / BA") by `bpmn2agent-generate`. They are the only +steps of the workflow that change the backlog, so they live in one skill that refuses to write +without approval. The calling skill names the step to run. + +## Rules for every write + +1. **Approval first.** In `stories//00_run.md`, `checkpoints.` of the approving step + (I3, I5, P9 or E4, named in each section) must have `decision: approved`, and the approved content + (`99_uebergabe.md`, or the entry in `09_aenderungsvorschlaege.md`) must have `status: approved`. + Otherwise stop and report; write nothing. For P8 and P10 also compare the `version` of + `04_story.md`, `05_akzeptanzkriterien.md` and `06_schaetzung.md` with + `checkpoints.E4.approvedVersions`; a higher version means the story changed after the approval → + stop and send the user back to E4. +2. **Only the approved content.** Send exactly the approved text, never a rewrite. The calling skill + wrote it at the approval to `stories//publish/.md`; pass that file with + `--body-file` (never inline with `--body`, which breaks on quotes, `$` and backticks). +3. **Only the backlog of this run.** Take repo and project from `backlog` in `01_feedback.md` + (e.g. `owner/repo, project 3`). Pass `-R ` to every `gh issue` command and + `--owner ` to every `gh project` command except `item-edit`, which takes `--project-id`. + Never write anywhere else. +4. **Never twice.** Before every write, check in this order: + 1. The URL is already recorded (field named in the section) → skip. + 2. The marker exists. Every text this skill sends ends with the literal marker + `` (e.g. + ``; the step id is the one in each + heading). For an issue (I6, P8): + `gh issue list -R --author @me --state all --limit 50 --json url,body --jq '.[] | select(.body | contains("")) | .url'`. + For a comment or an appended section (I4, P6, P10): `gh issue view -R --json body,comments` + and look for the exact marker. Found → record its URL and skip. + 3. Only then the freshness check (rule 6). +5. **Record right away.** Write the URL `gh` returns into the field named in the section before the + next `gh` call. +6. **Freshness before changing someone else's issue** (I4, P6, P10): re-read the target with + `gh issue view -R --json updatedAt,body,state`. If `updatedAt` differs from the + `baseUpdatedAt` recorded at the approval, stop and ask the user to approve again. Add, never + rewrite: notes are comments, a body change appends the approved section to the body as it is now. +7. **Errors stop.** On any `gh` error (not logged in, missing `project` scope, wrong number, missing + label) show the raw error and ask via `AskUserQuestion`: retry after fixing it (recommended) / leave + this write undone and note it as a risk. Never retry by creating something new. + +## I4 — "Änderungshinweis an offener Story ergänzen" + +Approval: `checkpoints.I3`. Input: `99_uebergabe.md` (`target` = URL of the open story), +`publish/I4.md` (the approved comment with its marker). Write the comment with +`gh issue comment -R --body-file stories//publish/I4.md`. Record the comment +URL as `backlogItem` in `99_uebergabe.md`. + +## I6 — "Feedback im Opportunity Backlog anlegen" + +Approval: `checkpoints.I5`. Input: `99_uebergabe.md` (approved `title`), `publish/I6.md` (the approved +body with its marker). Create the entry with +`gh issue create -R --title "" --body-file stories//publish/I6.md --label opportunity`. +Record the issue URL as `backlogItem` in `99_uebergabe.md`. + +## P6 — "Mit bestehender Story zusammenführen" + +Approval: `checkpoints.P9`. Input: the merge entry in `09_aenderungsvorschlaege.md` (target issue, +`baseUpdatedAt`), `publish/P6.md` (the approved section with its marker). After rules 4 and 6, write +the target's current body followed by `publish/P6.md` into `stories//publish/P6-body.md` and +send it with `gh issue edit -R --body-file stories//publish/P6-body.md`. +Record the issue URL as `appliedUrl` of the entry and set the entry and the file to `status: applied`. +Send only the approved section: the merge criteria (`${CLAUDE_SKILL_DIR}/references/P6.md`) were +applied when it was drafted at "Zusammenführung freigeben". + +## P8 — "Story im Backlog anlegen & in Ready-Spalte stellen" + +Approval: `checkpoints.E4` (`priority` = the value for the project field "Priority" in that field's +type, `approvedVersions`). Input: `publish/P8.md` (first line `# `, then the approved +body with its marker), the epic label from `04_story.md`. + +1. **Issue.** If `10_ready-story.md` already has `backlogItem`, use it. Otherwise check the marker + (rule 4); if nothing is found, write the body (everything after the title line) to + `stories//publish/P8-body.md` and create the issue with + `gh issue create -R --title "" --body-file stories//publish/P8-body.md --label "epic:"`. + Write `10_ready-story.md` right away (frontmatter `storyId`, `version`, `status: draft`, `epic`, + `storyPoints`, `priority`, `backlogItem`). +2. **Project item.** `gh project item-add --owner --url --format json` + returns the item, also when the issue is already in the project; take `` from its `id`. +3. **Ready and priority.** Get the project id with `gh project view --owner --format json` + and the ids of the "Status" field, its option "Ready" and the "Priority" field with + `gh project field-list --owner --format json`. Set both with + `gh project item-edit --id --project-id --field-id ` plus + `--single-select-option-id