From 87bab46b0f84236274b6384575587d1d6901cb2b Mon Sep 17 00:00:00 2001 From: Rihards Gailums Date: Thu, 21 May 2026 11:12:45 +0000 Subject: [PATCH] =?UTF-8?q?Workspace=20INDEX.md=20+=20flag=20=C2=A707=20DI?= =?UTF-8?q?=20non-conformance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two changes: 1. INDEX.md (new, generated). Workspace-level index that lists every package grouped by UAPF level (L0/L1/L2/L3/L4) for navigation. Per UAPF specification §01-concepts.md, levels are an aggregation and governance scope only and MUST NOT be used to imply modeling semantics; per §04-folder-structure.md the SHOULD-recommended on-disk layout is enterprise/ + domains/ + processes/ regardless of level. INDEX.md is therefore a level-grouped *view* over the spec-conformant on-disk layout, not an alternative layout. Generated by tools/build-index/build_index.py (new), which walks the workspace manifests and emits INDEX.md from current state. 2. docs/methodology.md updated to document the §07 auto-layout DI as a known deliberate non-conformance. §07-package-format.md requires authored DI (from a conforming OMG modeler) and explicitly forbids automatic layout generation as a substitute. The DI in the five .bpmn files in this workspace is auto-generated by tools/register-transcoder/bpmn_di.py and is therefore non-conformant under §07. It is kept for the POC so artefacts preview visually; the conformant path is to re-author each .bpmn in Camunda Modeler or bpmn-js Studio and recommit, which emits authored DI. New *Known non-conformances* section in methodology.md cites §07 verbatim and explains this rationale. The Pass-1 transcoder section and Final-validation-pass section are softened to call out that the DI is auto-generated. uapf-cli validate green on all 13 packages (L1 domains/gramatvediba, L2 fg1-fg6, L3 fg3-2/3/6, L4 fg3-1/4/5). --- INDEX.md | 53 ++++++++++ docs/methodology.md | 40 +++++++- tools/build-index/build_index.py | 160 +++++++++++++++++++++++++++++++ 3 files changed, 248 insertions(+), 5 deletions(-) create mode 100644 INDEX.md create mode 100644 tools/build-index/build_index.py diff --git a/INDEX.md b/INDEX.md new file mode 100644 index 0000000..5986b21 --- /dev/null +++ b/INDEX.md @@ -0,0 +1,53 @@ +# `vk-gramatvediba` workspace index + +This index lists every package in the workspace grouped by UAPF +level (L0–L4). Per UAPF specification `§01-concepts.md`, levels +are an aggregation and governance scope only; per +`§04-folder-structure.md` the SHOULD-recommended workspace layout +is `enterprise/`, `domains/`, `processes/` regardless of level. +This file is therefore a level-grouped *view* over a +spec-conformant on-disk layout, not an alternative layout. + +Regenerate with `python3 tools/build-index/build_index.py`. + +Total packages: **14** (L0: 1, L1: 1, L2: 6, L3: 3, L4: 3). + +## L0 — Enterprise process collection index + +| Package | Path | Description | +|---|---|---| +| `vk.gramatvediba` | `enterprise/enterprise.yaml` | Valsts Kase — Grāmatvedības uzskaite | + +## L1 — Domain process collections + +| Package | Path | Includes | Description | +|---|---|---|---| +| `vk.gramatvediba.domain` | `domains/gramatvediba/` | `../../processes/fg1`, `../../processes/fg2`, `../../processes/fg3`, `../../processes/fg4`, `../../processes/fg5`, `../../processes/fg6` | Grāmatvedības uzskaite — accounting domain | + +## L2 — End-to-end business processes + +| Package | Path | Includes | Description | +|---|---|---|---| +| `vk.gramatvediba.fg1` | `processes/fg1/` | _(none)_ | FG1 — Ilgtermiņa nefinanšu aktīvu un krājumu uzskaite | +| `vk.gramatvediba.fg2` | `processes/fg2/` | _(none)_ | FG2 — Finanšu aktīvu uzskaite | +| `vk.gramatvediba.fg3` | `processes/fg3/` | `../fg3-1`, `../fg3-2`, `../fg3-3`, `../fg3-4`, `../fg3-5`, `../fg3-6` | FG3 — Saistību un izdevumu uzskaite | +| `vk.gramatvediba.fg4` | `processes/fg4/` | _(none)_ | FG4 — Saistību par atlīdzību uzskaite | +| `vk.gramatvediba.fg5` | `processes/fg5/` | _(none)_ | FG5 — Prasību un ieņēmumu uzskaite | +| `vk.gramatvediba.fg6` | `processes/fg6/` | _(none)_ | FG6 — Pārskatu sagatavošana publiskošanai | + +## L3 — Composed subprocesses / variants + +| Package | Path | Includes | Description | +|---|---|---|---| +| `vk.gramatvediba.fg3-2` | `processes/fg3-2/` | _(none)_ | FG3-2 — Iepirkuma līguma darbības izbeigšana | +| `vk.gramatvediba.fg3-3` | `processes/fg3-3/` | _(none)_ | FG3-3 — Klienta datu pārvaldība | +| `vk.gramatvediba.fg3-6` | `processes/fg3-6/` | _(none)_ | FG3-6 — Kopsavilkuma grāmatošana | + +## L4 — Atomic executable processes + +| Package | Path | Cornerstones | Description | +|---|---|---|---| +| `vk.gramatvediba.fg3-1` | `processes/fg3-1/` | bpmn, dmn, resources | FG3-1 — Rēķina/kredītrēķina saņemšana par precēm/pakalpojumiem | +| `vk.gramatvediba.fg3-4` | `processes/fg3-4/` | bpmn, dmn, resources | FG3-4 — Saimnieciskā norēķina veikšana | +| `vk.gramatvediba.fg3-5` | `processes/fg3-5/` | bpmn, dmn, resources | FG3-5 — Komandējuma norēķina veikšana | + diff --git a/docs/methodology.md b/docs/methodology.md index 92d05d2..da3e53e 100644 --- a/docs/methodology.md +++ b/docs/methodology.md @@ -114,10 +114,14 @@ one `bpmn:startEvent` per *entry step* (no in-group predecessor) and one `bpmn:endEvent` per *exit step* (no in-group successor), so the fragment's real boundary is visible rather than hidden behind synthesised gateways. The output then has BPMN Diagram Interchange (`bpmndi:BPMNDiagram` with -`BPMNShape` and `BPMNEdge` elements) appended by `bpmn_di.py` using a +`BPMNShape` and `BPMNEdge` elements) appended by `bpmn_di.py` via a swim-lane left-to-right auto-layout, so the resulting file previews in -bpmn.io, Camunda Modeler and the ProcessGit web view without manual -positioning. +bpmn.io, Camunda Modeler and the ProcessGit web view. **This DI is +auto-generated, not authored** — §07 of the UAPF specification requires +authored DI produced by a conforming OMG modeler and explicitly forbids +automatic layout generation as a substitute. The auto-layout output is +kept as a known deliberate non-conformance pending re-authoring of each +`.bpmn` in a modeler. See *Known non-conformances* below. The output is `isExecutable="false"` and deliberately unembellished: no inferred gateways, no synthesised decision logic, no compensation for @@ -218,8 +222,9 @@ correction (next section): `sourceRef`/`targetRef` nodes; every `flowNodeRef` resolves to a defined node; every `incoming`/`outgoing` reference is consistent with the corresponding flow's source/target. -- All `.bpmn` files now carry BPMN Diagram Interchange — they preview - cleanly in bpmn.io, Camunda Modeler and ProcessGit's web view. +- All `.bpmn` files carry BPMN Diagram Interchange (auto-generated; see + *Known non-conformances* below) — sufficient for preview in bpmn.io, + Camunda Modeler and ProcessGit's web view. - The transcoder is byte-deterministic: re-running it on the FG3 register for 3.5.2 and 3.5.3 reproduces the committed `sample-output/` files exactly. @@ -249,6 +254,31 @@ packages may reference L4 directly when no intermediate composition is needed (`fg3` `includes` references the three L4s and the three L3 stubs in parallel, which is conformant). +## Known non-conformances + +This POC ships with one documented non-conformance against the UAPF +specification, retained deliberately for the POC and tracked here. + +**Authored Diagram Interchange (§07-package-format.md).** §07 makes DI a +MUST for cornerstone BPMN/DMN/CMMN files and explicitly forbids automatic +layout generation as a substitute: + +> "An implementation MUST NOT rely on automatic layout generation as a +> substitute for authored DI: a generated layout is not the authored +> layout and is not deterministic across tools." + +The DI in the five `.bpmn` files (`fg3-1`, `fg3-4`, `fg3-5`, and the two +transcoder samples under `tools/register-transcoder/sample-output/`) is +produced by `tools/register-transcoder/bpmn_di.py` using a swim-lane +left-to-right auto-layout. It renders the diagrams in bpmn.io, Camunda +Modeler and ProcessGit's web view, but it is not authored DI and +therefore non-conformant under §07. The auto-layout is kept so the POC +artefacts are reviewable visually; the conformant path is to open each +`.bpmn` in a conforming OMG modeler (Camunda Modeler, bpmn-js Studio) and +re-save, which emits authored DI. The DMN files are unaffected — §07 +exempts DMNs whose only logic is decision tables, and all three workspace +DMNs are exactly that. + ## Implications for the AI regulatory sandbox The pipeline has four properties that bear on the sandbox's evaluation. diff --git a/tools/build-index/build_index.py b/tools/build-index/build_index.py new file mode 100644 index 0000000..dd763d9 --- /dev/null +++ b/tools/build-index/build_index.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +""" +build_index.py — generate INDEX.md from workspace manifests. + +Walks the `vk-gramatvediba` workspace tree, reads every `uapf.yaml` and the +top-level `enterprise/enterprise.yaml`, groups packages by their declared +level (0–4), and emits a level-grouped index at the workspace root. + +Per UAPF specification §01-concepts.md, levels are an aggregation and +governance scope only — directory structure must not encode them. Per +§04-folder-structure.md the SHOULD-recommended workspace layout is +`enterprise/` + `domains/` + `processes/` regardless of level. INDEX.md is +therefore a level-grouped *view* over a spec-conformant on-disk layout, +not an alternative layout. + +Usage: python3 tools/build-index/build_index.py +Dependencies: pyyaml. +""" +import glob +import os +import sys + +try: + import yaml +except ImportError: + sys.exit("error: pyyaml is required (pip install pyyaml)") + + +def find_workspace_root(start): + """Walk up from `start` until a directory containing `enterprise/` is found.""" + d = os.path.abspath(start) + while d != os.path.dirname(d): + if os.path.isdir(os.path.join(d, "enterprise")) and \ + os.path.isdir(os.path.join(d, "processes")): + return d + d = os.path.dirname(d) + sys.exit("error: not inside a UAPF workspace (no enterprise/ + processes/)") + + +def read_manifest(path): + with open(path, encoding="utf-8") as fh: + return yaml.safe_load(fh) + + +def short(s, n=160): + if not s: + return "" + return s.split("\n")[0][:n] + + +LEVEL_TITLES = { + 0: "L0 — Enterprise process collection index", + 1: "L1 — Domain process collections", + 2: "L2 — End-to-end business processes", + 3: "L3 — Composed subprocesses / variants", + 4: "L4 — Atomic executable processes", +} + + +def collect(ws): + pkgs = {0: [], 1: [], 2: [], 3: [], 4: []} + ent_path = os.path.join(ws, "enterprise", "enterprise.yaml") + if os.path.isfile(ent_path): + m = read_manifest(ent_path) + # L0 enterprise-index nests id/name/description under `enterprise:` + ent = m.get("enterprise") or m + pkgs[0].append({ + "id": ent.get("id", "?"), + "name": ent.get("name", ""), + "desc": short(ent.get("description", "")), + "path": "enterprise/enterprise.yaml", + "includes": [p.get("ref", "") for p in (m.get("packages") or [])], + "cornerstones": {}, + }) + for pat in ("domains/*/uapf.yaml", "processes/*/uapf.yaml"): + for p in sorted(glob.glob(os.path.join(ws, pat))): + m = read_manifest(p) + lvl = m.get("level") + if lvl not in pkgs: + continue + pkgs[lvl].append({ + "id": m.get("id", "?"), + "name": m.get("name", ""), + "desc": short(m.get("description", "")), + "path": os.path.relpath(os.path.dirname(p), ws) + "/", + "includes": m.get("includes") or [], + "cornerstones": m.get("cornerstones") or {}, + }) + return pkgs + + +def render(pkgs): + L = [] + L.append("# `vk-gramatvediba` workspace index") + L.append("") + L.append("This index lists every package in the workspace grouped by UAPF") + L.append("level (L0–L4). Per UAPF specification `§01-concepts.md`, levels") + L.append("are an aggregation and governance scope only; per") + L.append("`§04-folder-structure.md` the SHOULD-recommended workspace layout") + L.append("is `enterprise/`, `domains/`, `processes/` regardless of level.") + L.append("This file is therefore a level-grouped *view* over a") + L.append("spec-conformant on-disk layout, not an alternative layout.") + L.append("") + L.append("Regenerate with `python3 tools/build-index/build_index.py`.") + L.append("") + + total = sum(len(v) for v in pkgs.values()) + L.append(f"Total packages: **{total}** " + "(L0: {0}, L1: {1}, L2: {2}, L3: {3}, L4: {4}).".format( + *(len(pkgs[i]) for i in range(5)))) + L.append("") + + for lvl in (0, 1, 2, 3, 4): + L.append(f"## {LEVEL_TITLES[lvl]}") + L.append("") + items = pkgs[lvl] + if not items: + L.append("_(none)_") + L.append("") + continue + + if lvl == 0: + L.append("| Package | Path | Description |") + L.append("|---|---|---|") + for e in items: + desc = e["name"] or e["desc"] or "—" + L.append(f"| `{e['id']}` | `{e['path']}` | {desc} |") + elif lvl in (1, 2, 3): + L.append("| Package | Path | Includes | Description |") + L.append("|---|---|---|---|") + for e in items: + inc = ", ".join(f"`{i}`" for i in e["includes"]) or "_(none)_" + desc = e["name"] or e["desc"] or "—" + L.append(f"| `{e['id']}` | `{e['path']}` | {inc} | {desc} |") + else: # L4 + L.append("| Package | Path | Cornerstones | Description |") + L.append("|---|---|---|---|") + for e in items: + cs = e["cornerstones"] or {} + present = [k for k in ("bpmn", "dmn", "cmmn", "resources") + if cs.get(k)] + cs_str = ", ".join(present) or "_(none)_" + desc = e["name"] or e["desc"] or "—" + L.append(f"| `{e['id']}` | `{e['path']}` | {cs_str} | {desc} |") + L.append("") + return "\n".join(L) + "\n" + + +def main(): + ws = find_workspace_root(os.path.dirname(os.path.abspath(__file__))) + pkgs = collect(ws) + text = render(pkgs) + out = os.path.join(ws, "INDEX.md") + with open(out, "w", encoding="utf-8") as fh: + fh.write(text) + print(f"wrote {out} ({sum(len(v) for v in pkgs.values())} packages)") + + +if __name__ == "__main__": + main()