@guanlin4924/devflow-pi 0.1.40 → 0.1.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/plugins/devflow-core/extensions/devflow-core.ts +609 -42
- package/plugins/devflow-core/skills/wireframe-proposal/SKILL.md +13 -0
- package/plugins/devflow-core/skills/wireframe-proposal/agents/openai.yaml +4 -0
- package/plugins/devflow-core/skills/wireframe-render/SKILL.md +8 -0
- package/plugins/devflow-core/skills/wireframe-review/SKILL.md +8 -0
- package/plugins/devflow-core/skills/wireframe-validate/SKILL.md +8 -0
- package/plugins/devflow-core/skills/wireframe-validate/agents/openai.yaml +4 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/SKILL.md +16 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/contracts/business-candidate-delta.schema.yaml +41 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/contracts/business-claim.schema.yaml +10 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/contracts/business-model-policy.yaml +51 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/contracts/business-model.schema.yaml +241 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/contracts/business-source-registry.schema.yaml +80 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/contracts/proposal.schema.yaml +44 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/contracts/questions.schema.yaml +14 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/acceptance_test_suite.rb +13 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/advance_state.rb +143 -28
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_claim_validate.py +77 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_closure_e2e_seed.json +516 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_cross_model_check.py +76 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_impact.py +51 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_invalidation.rb +108 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_model_commit.rb +570 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_model_gate.rb +89 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_model_render.py +131 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_model_validate.py +1063 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_source_resolver.rb +365 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/checker_registry.rb +2 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/execution_contract.rb +3 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/gate_contract.rb +3 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/gate_runtime.rb +13 -4
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/parallel_recover.rb +386 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/parallel_run.rb +28 -2
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/requirement_graph.rb +139 -14
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/requirement_graph_backfill.rb +143 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/session_rollover.rb +15 -4
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/sync_artifacts.rb +45 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/validate_state.rb +10 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/wireframe_commit.rb +222 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/wireframe_render.py +231 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/wireframe_validate.py +457 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/templates/feature-development.yaml +106 -1
- package/plugins/devflow-implementation/agents/implementation-worker.md +6 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/code_index.py +107 -37
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/scanner.py +20 -1
- package/plugins/devflow-quality/skills/workflow-acceptance-test-design/SKILL.md +1 -1
- package/src/constants.ts +1 -1
- package/src/runtime.ts +53 -22
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Render a deterministic, human-readable business model view."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def text(value: Any) -> str:
|
|
12
|
+
return str(value if value is not None else "")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def md(value: Any) -> str:
|
|
16
|
+
return text(value).replace("|", "\\|").replace("\r", "").replace("\n", "<br>")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def mermaid(value: Any) -> str:
|
|
20
|
+
return text(value).replace('"', """).replace("[", "[").replace("]", "]").replace("\r", "").replace("\n", " ")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def cell_list(values: Any) -> str:
|
|
24
|
+
return ", ".join(md(item) for item in values) if isinstance(values, list) else md(values)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def action_map(model: dict[str, Any]) -> dict[str, dict[str, Any]]:
|
|
28
|
+
return {text(item.get("id")): item for item in model.get("actions", []) if isinstance(item, dict)}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def render_flow(model: dict[str, Any]) -> list[str]:
|
|
32
|
+
actions = action_map(model)
|
|
33
|
+
lines = ["```mermaid", "flowchart LR"]
|
|
34
|
+
for action_id, action in actions.items():
|
|
35
|
+
lines.append(f' {action_id.replace("-", "_")}["{mermaid(action.get("name", action_id))}"]')
|
|
36
|
+
for action_id, action in actions.items():
|
|
37
|
+
routing = action.get("routing") if isinstance(action.get("routing"), dict) else {}
|
|
38
|
+
for target in routing.get("next_action_refs", []) or []:
|
|
39
|
+
if text(target) in actions:
|
|
40
|
+
lines.append(f' {action_id.replace("-", "_")} -->|next| {text(target).replace("-", "_")}')
|
|
41
|
+
decision = routing.get("decision_rule_ref")
|
|
42
|
+
if decision:
|
|
43
|
+
lines.append(f' {action_id.replace("-", "_")} -. decision .-> {text(decision).replace("-", "_")}')
|
|
44
|
+
for rule in model.get("rules", []):
|
|
45
|
+
if isinstance(rule, dict):
|
|
46
|
+
lines.append(f' {text(rule.get("id")).replace("-", "_")}{{"{mermaid(rule.get("name", rule.get("id")))}"}}')
|
|
47
|
+
lines.append("```")
|
|
48
|
+
return lines
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def render(model: dict[str, Any]) -> str:
|
|
52
|
+
actors = {text(item.get("id")): item for item in model.get("actors", []) if isinstance(item, dict)}
|
|
53
|
+
actions = action_map(model)
|
|
54
|
+
entities = {text(item.get("id")): item for item in model.get("entities", []) if isinstance(item, dict)}
|
|
55
|
+
resources = {text(item.get("id")): item for item in model.get("resources", []) if isinstance(item, dict)}
|
|
56
|
+
lines = [f"# 业务模型视图:{md(model.get('feature'))}", "", "> 本文件由 canonical.json 确定性生成,仅供阅读和评审;不是事实源。", ""]
|
|
57
|
+
lines += ["## 业务流程", ""] + render_flow(model) + [""]
|
|
58
|
+
|
|
59
|
+
lines += ["## Actor × Action 覆盖矩阵", "", "| Actor | Action | Owner | Requirement |", "|---|---|---|---|"]
|
|
60
|
+
for action in model.get("actions", []):
|
|
61
|
+
if not isinstance(action, dict):
|
|
62
|
+
continue
|
|
63
|
+
actor = actors.get(text(action.get("actor_ref")), {})
|
|
64
|
+
lines.append(f"| {md(actor.get('name', action.get('actor_ref')))} | {md(action.get('name', action.get('id')))} | {md(action.get('owner_ref'))} | {cell_list(action.get('requirement_refs', []))} |")
|
|
65
|
+
lines.append("")
|
|
66
|
+
|
|
67
|
+
lines += ["## 状态迁移", "", "| Entity | From | Action | To |", "|---|---|---|---|"]
|
|
68
|
+
for action in model.get("actions", []):
|
|
69
|
+
if not isinstance(action, dict) or not action.get("from_state_ref"):
|
|
70
|
+
continue
|
|
71
|
+
entity = entities.get(text(action.get("entity_ref")), {})
|
|
72
|
+
states = {text(item.get("id")): item for item in entity.get("states", []) if isinstance(item, dict)}
|
|
73
|
+
lines.append(f"| {md(entity.get('name', action.get('entity_ref')))} | {md(states.get(text(action.get('from_state_ref')), {}).get('name', action.get('from_state_ref')))} | {md(action.get('name', action.get('id')))} | {md(states.get(text(action.get('to_state_ref')), {}).get('name', action.get('to_state_ref')))} |")
|
|
74
|
+
lines.append("")
|
|
75
|
+
|
|
76
|
+
lines += ["## 决策表", "", "| Rule | Branch | Default | Next action |", "|---|---|---|---|"]
|
|
77
|
+
for rule in model.get("rules", []):
|
|
78
|
+
if not isinstance(rule, dict):
|
|
79
|
+
continue
|
|
80
|
+
for branch in rule.get("branches", []) or []:
|
|
81
|
+
if not isinstance(branch, dict):
|
|
82
|
+
continue
|
|
83
|
+
lines.append(f"| {md(rule.get('name', rule.get('id')))} | {md(branch.get('id'))} | {md(branch.get('default', False))} | {cell_list(branch.get('next_action_refs', []))} |")
|
|
84
|
+
lines.append("")
|
|
85
|
+
|
|
86
|
+
lines += ["## Resource producer / consumer", "", "| Resource | Origin | Producers | Consumers | Boundary |", "|---|---|---|---|---|"]
|
|
87
|
+
for resource_id, resource in resources.items():
|
|
88
|
+
producers = [text(a.get("id")) for a in actions.values() if resource_id in (a.get("produces") or [])]
|
|
89
|
+
consumers = [text(a.get("id")) for a in actions.values() if resource_id in (a.get("consumes") or [])]
|
|
90
|
+
origin = resource.get("origin") if isinstance(resource.get("origin"), dict) else {}
|
|
91
|
+
lines.append(f"| {md(resource.get('name', resource_id))} | {md(origin.get('kind'))} | {cell_list(producers)} | {cell_list(consumers)} | {md(origin.get('boundary_ref'))} |")
|
|
92
|
+
lines.append("")
|
|
93
|
+
|
|
94
|
+
lines += ["## Swimlane projection", "", "| Owner | Action | Trigger | Next |", "|---|---|---|---|"]
|
|
95
|
+
for action in model.get("actions", []):
|
|
96
|
+
if isinstance(action, dict):
|
|
97
|
+
routing = action.get("routing") if isinstance(action.get("routing"), dict) else {}
|
|
98
|
+
next_actions = routing.get("next_action_refs", []) or []
|
|
99
|
+
lines.append(f"| {md(action.get('owner_ref'))} | {md(action.get('name', action.get('id')))} | {md(action.get('trigger'))} | {cell_list(next_actions)} |")
|
|
100
|
+
lines.append("")
|
|
101
|
+
|
|
102
|
+
lines += ["## Data flow", "", "| Action | Consumes | Produces |", "|---|---|---|"]
|
|
103
|
+
for action in model.get("actions", []):
|
|
104
|
+
if isinstance(action, dict):
|
|
105
|
+
lines.append(f"| {md(action.get('name', action.get('id')))} | {cell_list(action.get('consumes', []))} | {cell_list(action.get('produces', []))} |")
|
|
106
|
+
lines.append("")
|
|
107
|
+
|
|
108
|
+
assumptions = model.get("assumptions", []) or []
|
|
109
|
+
lines += ["## Assumptions", "", "| ID | Statement | Status |", "|---|---|---|"]
|
|
110
|
+
for item in assumptions:
|
|
111
|
+
if isinstance(item, dict):
|
|
112
|
+
lines.append(f"| {md(item.get('id'))} | {md(item.get('statement'))} | {md(item.get('status'))} |")
|
|
113
|
+
lines.append("")
|
|
114
|
+
lines += ["## Requirement refs", "", ", ".join(f"`{md(item)}`" for item in model.get("requirement_refs", [])), ""]
|
|
115
|
+
return "\n".join(lines)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def main() -> int:
|
|
119
|
+
parser = argparse.ArgumentParser()
|
|
120
|
+
parser.add_argument("--canonical", type=Path, required=True)
|
|
121
|
+
parser.add_argument("--output", type=Path, required=True)
|
|
122
|
+
args = parser.parse_args()
|
|
123
|
+
model = json.loads(args.canonical.read_text(encoding="utf-8"))
|
|
124
|
+
args.output.parent.mkdir(parents=True, exist_ok=True)
|
|
125
|
+
args.output.write_text(render(model), encoding="utf-8")
|
|
126
|
+
print(json.dumps({"ok": True, "path": str(args.output), "bytes": args.output.stat().st_size}, ensure_ascii=False))
|
|
127
|
+
return 0
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
if __name__ == "__main__":
|
|
131
|
+
raise SystemExit(main())
|