Skip to content
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## 0.2.0 — unreleased

- Process Contract Schema v0.2 with typed transitions, I/O contracts, SLA/KPI, risk, evidence and agent policy metadata.
- Process Analysis Canvas with observed timing, ownership, variants, evidence-backed pain points, data flows and a standalone HTML renderer.
- v0.1 migration command.
- BPMN supported-subset import and richer export with lanes and parallel gateways.
- Process governance policy gates.
Expand Down
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ process-code schema -o process.schema.json
```bash
process-code validate examples/customer-creation.process.yaml --strict
process-code mermaid examples/customer-creation.process.yaml
process-code canvas examples/process-analysis/order-entry.process.yaml -o order-entry-analysis.html
process-code bpmn examples/customer-creation.process.yaml -o customer.bpmn
process-code diff examples/customer-creation.process.yaml examples/changes/customer-creation-v2.process.yaml
process-code impact examples/customer-creation.process.yaml examples/changes/customer-creation-v2.process.yaml
Expand Down Expand Up @@ -143,6 +144,7 @@ The source-tree schema is [`schemas/process.schema.json`](schemas/process.schema
| Contract validation | `process-code validate` |
| v0.1 -> v0.2 migration | `process-code migrate` |
| Mermaid | `process-code mermaid` |
| Process Analysis Canvas | `process-code canvas` |
| BPMN 2.0 export | `process-code bpmn` |
| BPMN supported-subset import | `process-code bpmn-import` |
| Markdown documentation | `process-code docs` |
Expand All @@ -163,6 +165,44 @@ The source-tree schema is [`schemas/process.schema.json`](schemas/process.schema
| AI drafting context | `process-code draft-context` |
| Vendor adapter discovery/import | `process-code adapter-list` / `adapter-import` |

## Process Analysis Canvas

Process discovery needs more than a flowchart. A useful assessment view must show the real execution details that disappear from high-level BPMN: who performs each step, which system is used, what goes in and out, how much active and waiting time is observed, what variants exist, where evidence-backed pain points occur, how data moves, and who can change the step.

The optional analysis fields keep those facts in the same governed process contract:

```yaml
steps:
- id: save_draft
name: Save order as draft
actor: sales_ops
system: s4
inputs: [{name: Entered order data}]
outputs: [{name: Draft sales order}]
timing: {touch_time: PT1M, wait_time: PT0M}
ownership:
process_owner: o2c_process_owner
change_owner: sap_sd_team

analysis:
pain_points:
- id: slow_customer_search
step: check_customer
observation: Customer search takes 20 minutes
cause: Search help cannot filter the observed list by creation date
evidence: [observed_session_01]
impact: {duration: PT20M}
```

Generate the one-page HTML artifact with:

```bash
process-code canvas examples/process-analysis/order-entry.process.yaml \
-o order-entry-analysis.html
```

The complete evidence-based example is [`examples/process-analysis/order-entry.process.yaml`](examples/process-analysis/order-entry.process.yaml).

## Pull-request impact gate

The repository contains a reusable composite GitHub Action. In a consumer repository:
Expand Down
34 changes: 33 additions & 1 deletion docs/specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ The core catalog sections are `roles`, `systems`, `objects`, `interfaces`, `cont

Supported `type` values are `task`, `user_task`, `service_task`, `decision`, `parallel`, `event`, `end`, and `subprocess`.

A step may include `actor`, `system`, referenced catalog IDs, `inputs`, `outputs`, `sla`, `kpis`, `raci`, `agent`, and `transitions`.
A step may include `actor`, `system`, referenced catalog IDs, `inputs`, `outputs`, `timing`, `ownership`, `sla`, `kpis`, `raci`, `agent`, and `transitions`.

## Transitions

Expand Down Expand Up @@ -62,6 +62,38 @@ Liveness findings are deterministic model-quality diagnostics. They do not claim

Each contract can declare `id`, `name`, `type`, `ref`, and `required`. At least one of `id`, `name`, or `ref` must exist.

## Process analysis fields

Process discovery and process design are different concerns. The contract therefore separates observed execution from targets:

- `timing.touch_time`: active work time observed or measured for the step.
- `timing.wait_time`: time spent waiting before the step can continue.
- `timing.elapsed_time`: end-to-end time for the step when it is known.
- `sla.duration`: target or service commitment. It is not a substitute for observed duration.
- `ownership.process_owner`: role accountable for the business behavior of the step.
- `ownership.change_owner`: role or team allowed to change the step, configuration, or supporting solution.

The optional top-level `analysis` object records facts that belong to the process analysis rather than to one step:

- `analysis.variants`: conditions that create a different path or handling rule.
- `analysis.pain_points`: evidence-backed observations, causes, impacts, and affected steps.
- `analysis.data_flows`: system-to-system data movement and field-level source-of-truth ownership.

A recommended terminology is:

- **Process Discovery**: the activity of observing the real process.
- **Process Analysis Canvas**: the one-page visual summary generated from the contract.
- **Process Analysis Note**: the detailed evidence-backed analysis document.
- **Process as Code**: the machine-readable source behind those views.

The reference renderer exposes the one-page view with:

```bash
process-code canvas process.yaml -o process-analysis.html
```

The canvas is intentionally not a BPMN replacement. BPMN focuses on process flow semantics. The analysis canvas focuses on how the process actually operates: actor, system, input, output, observed time, variants, pain points, data movement, and ownership.

## Risk, control and evidence

Risks are catalog entities and may include a severity such as `low`, `medium`, `high`, or `critical`. Policy gates can require controls and evidence for high-risk steps.
Expand Down
159 changes: 159 additions & 0 deletions examples/process-analysis/order-entry.process.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
version: "0.2"
process:
id: sales_order_entry_analysis
name: Sales Order Entry — Process Analysis
owner: o2c_process_owner
start: receive_po
trigger: Customer PO arrives in the shared sales mailbox
outcome: Sales order is released for fulfillment
tags: [sap, s4hana, discovery, process-analysis]

roles:
- {id: sales_ops, name: Sales Operations}
- {id: sales_manager, name: Sales Manager}
- {id: o2c_process_owner, name: O2C Process Owner}
- {id: sap_sd_team, name: SAP SD Team}

systems:
- {id: outlook, name: Outlook}
- {id: s4, name: SAP S/4HANA}

evidence:
- {id: observed_session_01, name: Observed user session 01}
- {id: search_screen_01, name: Customer search screenshot}

steps:
- id: receive_po
name: Open customer PO
type: user_task
actor: sales_ops
system: outlook
inputs: [{name: Customer PO, type: document, required: true}]
outputs: [{name: Order request, type: work_item}]
timing: {touch_time: PT2M, wait_time: PT15M, elapsed_time: PT17M}
ownership: {process_owner: o2c_process_owner, change_owner: sales_manager}
transitions: [{to: check_customer}]

- id: check_customer
name: Search and verify customer
type: user_task
actor: sales_ops
system: s4
inputs: [{name: Customer name and address, type: data, required: true}]
outputs: [{name: Confirmed customer account, type: data}]
timing: {touch_time: PT20M, wait_time: PT0M, elapsed_time: PT20M}
ownership: {process_owner: o2c_process_owner, change_owner: sap_sd_team}
evidence: [observed_session_01, search_screen_01]
transitions: [{to: enter_order}]

- id: enter_order
name: Enter sales order data
type: user_task
actor: sales_ops
system: s4
inputs:
- {name: Customer PO, type: document, required: true}
- {name: Confirmed customer account, type: data, required: true}
outputs: [{name: Entered order data, type: data}]
timing: {touch_time: PT8M, wait_time: PT0M, elapsed_time: PT8M}
ownership: {process_owner: o2c_process_owner, change_owner: sap_sd_team}
transitions: [{to: save_draft}]

- id: save_draft
name: Save order as draft
type: user_task
actor: sales_ops
system: s4
inputs: [{name: Entered order data, type: data, required: true}]
outputs: [{name: Draft sales order, type: business_object}]
timing: {touch_time: PT1M, wait_time: PT0M, elapsed_time: PT1M}
ownership: {process_owner: o2c_process_owner, change_owner: sap_sd_team}
transitions: [{to: approval_required}]

- id: approval_required
name: Is manager approval required?
type: decision
actor: sales_ops
system: s4
inputs: [{name: Draft sales order, type: business_object, required: true}]
outputs: [{name: Approval route decision, type: decision}]
timing: {touch_time: PT1M, wait_time: PT0M, elapsed_time: PT1M}
ownership: {process_owner: o2c_process_owner, change_owner: sales_manager}
transitions:
- {to: release_order, when: amount <= 50000, label: no approval}
- {to: send_approval_request, when: amount > 50000, label: approval required}

- id: send_approval_request
name: Send approval request
type: user_task
actor: sales_ops
system: outlook
inputs: [{name: Draft sales order, type: business_object, required: true}]
outputs: [{name: Approval email, type: message}]
timing: {touch_time: PT2M, wait_time: PT0M, elapsed_time: PT2M}
ownership: {process_owner: o2c_process_owner, change_owner: sales_manager}
transitions: [{to: check_approval_email}]

- id: check_approval_email
name: Check approval email
type: user_task
actor: sales_ops
system: outlook
inputs: [{name: Approval email, type: message, required: true}]
outputs: [{name: Approval decision, type: decision}]
timing: {touch_time: PT1M, wait_time: PT45M, elapsed_time: PT46M}
ownership: {process_owner: o2c_process_owner, change_owner: sales_manager}
transitions: [{to: release_order}]

- id: release_order
name: Release sales order
type: user_task
actor: sales_ops
system: s4
inputs: [{name: Draft sales order, type: business_object, required: true}]
outputs: [{name: Released sales order, type: business_object}]
timing: {touch_time: PT2M, wait_time: PT0M, elapsed_time: PT2M}
sla: {duration: PT1H}
ownership: {process_owner: o2c_process_owner, change_owner: sap_sd_team}
transitions: [{to: complete}]

- id: complete
name: Sales order released
type: end

analysis:
variants:
- id: high_value_order
name: High-value order
when: amount > 50000
path: [send_approval_request, check_approval_email, release_order]
notes: Manager approval is required before release.
- id: new_customer
name: New customer
when: customer_account_not_found
notes: Customer creation is handled by a separate master-data process before order entry resumes.

pain_points:
- id: slow_customer_search
step: check_customer
observation: Customer search takes 20 minutes
cause: Search help cannot filter the observed list by creation date
evidence: [observed_session_01, search_screen_01]
impact: {duration: PT20M}
- id: approval_email_wait
step: check_approval_email
observation: Approval waits in email for 45 minutes on the observed case
cause: The approval request has no workflow notification or queue
evidence: [observed_session_01]
impact: {wait_time: PT45M}

data_flows:
- id: customer_po_to_s4
from: outlook
to: s4
data: [customer_po_number, requested_date, material, quantity]
source_of_truth:
customer_po_number: customer_po
requested_date: customer_po
material: s4
quantity: customer_po
10 changes: 9 additions & 1 deletion schemas/process.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,25 @@
"process": {"type": "object", "required": ["id", "name"], "properties": {"id": {"type": "string", "minLength": 1}, "name": {"type": "string", "minLength": 1}, "description": {"type": "string"}, "owner": {"type": "string"}, "start": {"type": "string"}, "trigger": {"type": "string"}, "outcome": {"type": "string"}, "tags": {"type": "array", "items": {"type": "string"}}}, "additionalProperties": true},
"roles": {"$ref": "#/$defs/catalog"}, "systems": {"$ref": "#/$defs/catalog"}, "objects": {"$ref": "#/$defs/catalog"}, "interfaces": {"$ref": "#/$defs/catalog"}, "controls": {"$ref": "#/$defs/catalog"}, "risks": {"$ref": "#/$defs/catalog"}, "evidence": {"$ref": "#/$defs/catalog"},
"artifacts": {"type": "array", "items": {"type": "object", "required": ["id", "uri"], "properties": {"id": {"type": "string"}, "kind": {"type": "string"}, "relation": {"type": "string"}, "uri": {"type": "string"}}, "additionalProperties": true}},
"analysis": {"$ref": "#/$defs/analysis"},
"steps": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/step"}},
"catalogs": {"type": "array", "items": {"type": "object", "required": ["uri"], "properties": {"uri": {"type": "string"}, "include": {"type": "array", "items": {"type": "string"}}}, "additionalProperties": true}}
},
"$defs": {
"catalog": {"type": "array", "items": {"type": "object", "required": ["id"], "properties": {"id": {"type": "string", "minLength": 1}, "name": {"type": "string"}, "description": {"type": "string"}}, "additionalProperties": true}},
"contract": {"type": "object", "properties": {"id": {"type": "string"}, "name": {"type": "string"}, "type": {"type": "string"}, "ref": {"type": "string"}, "required": {"type": "boolean"}}, "anyOf": [{"required": ["id"]}, {"required": ["name"]}, {"required": ["ref"]}], "additionalProperties": true},
"transition": {"type": "object", "required": ["to"], "properties": {"to": {"type": "string"}, "label": {"type": "string"}, "when": {"type": "string"}, "data": {"type": "object"}}, "additionalProperties": true},
"timing": {"type": "object", "properties": {"touch_time": {"type": "string"}, "wait_time": {"type": "string"}, "elapsed_time": {"type": "string"}}, "additionalProperties": true},
"ownership": {"type": "object", "properties": {"process_owner": {"type": "string"}, "change_owner": {"type": "string"}}, "additionalProperties": true},
"variant": {"type": "object", "required": ["id"], "properties": {"id": {"type": "string", "minLength": 1}, "name": {"type": "string"}, "when": {"type": "string"}, "condition": {"type": "string"}, "path": {"type": "array", "items": {"type": "string"}}, "notes": {"type": "string"}}, "additionalProperties": true},
"pain_point": {"type": "object", "required": ["id", "observation"], "properties": {"id": {"type": "string", "minLength": 1}, "name": {"type": "string"}, "step": {"type": "string"}, "observation": {"type": "string", "minLength": 1}, "cause": {"type": "string"}, "evidence": {"type": "array", "items": {"type": "string"}}, "impact": {}}, "additionalProperties": true},
"data_flow": {"type": "object", "required": ["id", "from", "to"], "properties": {"id": {"type": "string", "minLength": 1}, "from": {"type": "string"}, "to": {"type": "string"}, "data": {"type": "array", "items": {"type": "string"}}, "source_of_truth": {"type": "object", "additionalProperties": {"type": "string"}}}, "additionalProperties": true},
"analysis": {"type": "object", "properties": {"variants": {"type": "array", "items": {"$ref": "#/$defs/variant"}}, "pain_points": {"type": "array", "items": {"$ref": "#/$defs/pain_point"}}, "data_flows": {"type": "array", "items": {"$ref": "#/$defs/data_flow"}}}, "additionalProperties": true},
"step": {"type": "object", "required": ["id", "name"], "properties": {
"id": {"type": "string", "minLength": 1}, "name": {"type": "string", "minLength": 1}, "type": {"enum": ["task", "user_task", "service_task", "decision", "parallel", "event", "end", "subprocess"]}, "actor": {"type": "string"}, "system": {"type": "string"},
"objects": {"type": "array", "items": {"type": "string"}}, "interfaces": {"type": "array", "items": {"type": "string"}}, "controls": {"type": "array", "items": {"type": "string"}}, "risks": {"type": "array", "items": {"type": "string"}}, "evidence": {"type": "array", "items": {"type": "string"}}, "artifacts": {"type": "array", "items": {"type": "string"}},
"inputs": {"type": "array", "items": {"$ref": "#/$defs/contract"}}, "outputs": {"type": "array", "items": {"$ref": "#/$defs/contract"}}, "transitions": {"type": "array", "items": {"$ref": "#/$defs/transition"}},
"inputs": {"type": "array", "items": {"$ref": "#/$defs/contract"}}, "outputs": {"type": "array", "items": {"$ref": "#/$defs/contract"}},
"timing": {"$ref": "#/$defs/timing"}, "ownership": {"$ref": "#/$defs/ownership"}, "transitions": {"type": "array", "items": {"$ref": "#/$defs/transition"}},
"sla": {"type": "object", "properties": {"duration": {"type": "string"}, "target": {}, "business_hours": {"type": "boolean"}}, "additionalProperties": true},
"kpis": {"type": "array", "items": {"type": "object", "required": ["id"], "properties": {"id": {"type": "string"}, "name": {"type": "string"}, "target": {}, "unit": {"type": "string"}}, "additionalProperties": true}},
"agent": {"type": "object", "properties": {"exposable": {"type": "boolean"}, "executable": {"type": "boolean"}, "allowed_actions": {"type": "array", "items": {"type": "string"}}}, "additionalProperties": true},
Expand Down
5 changes: 4 additions & 1 deletion src/process_as_code/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from .process_tests import affected_process_tests, run_process_tests
from .raci import extract_raci, raci_markdown
from .refs import resolve_artifacts
from .render import to_markdown, to_mermaid
from .render import to_analysis_canvas, to_markdown, to_mermaid
from .testgen import generate_test_scope, test_scope_markdown
from .validate import validate_process

Expand All @@ -42,6 +42,7 @@ def build_parser() -> argparse.ArgumentParser:

for name, help_text in (
("mermaid", "render Mermaid flowchart"),
("canvas", "render one-page Process Analysis Canvas HTML"),
("bpmn", "export BPMN 2.0 XML"),
("docs", "generate Markdown documentation"),
("raci", "extract RACI table"),
Expand Down Expand Up @@ -278,6 +279,8 @@ def main(argv: list[str] | None = None) -> int:
return 1
if args.command == "mermaid":
text = to_mermaid(data)
elif args.command == "canvas":
text = to_analysis_canvas(data)
elif args.command == "bpmn":
text = to_bpmn(data)
elif args.command == "docs":
Expand Down
Loading
Loading