@guanlin4924/devflow-pi 0.1.57 → 0.1.60

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guanlin4924/devflow-pi",
3
- "version": "0.1.57",
3
+ "version": "0.1.60",
4
4
  "type": "module",
5
5
  "description": "Devflow 专项研发 Pi,基于固定版本 OMP Runtime 组装研发状态机、代码智能、设计、实现与质量门禁。",
6
6
  "license": "UNLICENSED",
@@ -3722,6 +3722,7 @@ export default function devflowCore(pi: ExtensionAPI) {
3722
3722
  "project-context-validate",
3723
3723
  "orchestrator-manage-lessons",
3724
3724
  "orchestrator-update-triage",
3725
+ "wireframe-validate",
3725
3726
  ]),
3726
3727
  args: z.array(z.string()).default([]),
3727
3728
  }),
@@ -3732,6 +3733,7 @@ export default function devflowCore(pi: ExtensionAPI) {
3732
3733
  "project-context-validate": ["python3", "workflow-project-context/scripts/validate_initialization.py"],
3733
3734
  "orchestrator-manage-lessons": ["ruby", "workflow-orchestrator/scripts/manage_lessons.rb"],
3734
3735
  "orchestrator-update-triage": ["ruby", "workflow-orchestrator/scripts/update_triage_route.rb"],
3736
+ "wireframe-validate": ["python3", "workflow-orchestrator/scripts/wireframe_validate.py"],
3735
3737
  };
3736
3738
  const [runtime, relativeScript] = scripts[params.script];
3737
3739
  const result = await runProcess(
@@ -11,3 +11,11 @@ Read the controlled requirement baseline and the wireframe schema/registry. Prod
11
11
  - .planning/features/{feature}/questions.yaml
12
12
 
13
13
  Use only registered element types and screen-level requirement_refs. Keep IDs stable during revision. Do not add API fields, coordinates, CSS, nested children, or approval fields. Emit valid YAML only and never claim that the proposal is validated or approved. The validator and Core decide legality, fingerprints, and approval.
14
+
15
+ The proposal file is strict. Its only top-level keys are `schema_version`, `feature`, and `screens`. Each screen must contain `id`, `title`, `requirement_refs`, `elements`, and `states: [default]`; optional screen keys are only `presentation`, `platforms`, and `actions`.
16
+
17
+ Allowed element types are only `page_header`, `filter_bar`, `table`, `card_list`, `bottom_navigation`, `tabs`, `input`, `select`, `button`, and `icon_button`. Do not emit `text`, `alert`, `modal`, `empty_state`, or `description_list`. Put explanatory text in supported `title`, `label`, or `meta` fields. `table.columns` must be objects shaped as `{id, label}`, and all nested item/field IDs must be stable IDs.
18
+
19
+ The questions file is also strict. Its only top-level keys are `schema_version`, `feature`, and `questions`; do not add `step`, `notes`, or other metadata. Each question must contain `id`, `text`, `kind`, and `status`, with optional `target` and `answer`.
20
+
21
+ Before finishing, compare both files against `skill://workflow-orchestrator/contracts/proposal.schema.yaml` and `skill://workflow-orchestrator/contracts/questions.schema.yaml`. Do not invent legacy fields such as `name`, `route_hint`, `proposal_id`, `source_refs`, `principles`, or `flows`.
@@ -5,4 +5,4 @@ description: Render validated canonical wireframe JSON into deterministic Markdo
5
5
 
6
6
  # Wireframe Render
7
7
 
8
- Call the workflow-orchestrator wireframe_render.py script only with the current Core-built canonical JSON. Generate preview Markdown, flow SVG, page SVGs, and the optional static gallery under the wireframe output directory. Never read proposal YAML as a substitute, mutate canonical JSON, write approval, or modify workflow state.
8
+ Call the workflow-orchestrator wireframe_render.py script only with the current Core-built canonical JSON and the validation metadata. Generate preview Markdown, flow SVG, page SVGs, and the optional static gallery under the wireframe output directory. The `canonical.meta.json` file is created later by the Core approval path and is not an input to this step. Never read proposal YAML as a substitute, mutate canonical JSON, write approval, or modify workflow state.
@@ -5,4 +5,20 @@ description: Run deterministic validation for a wireframe proposal and build can
5
5
 
6
6
  # Wireframe Validate
7
7
 
8
- Call the workflow-orchestrator wireframe_validate.py script with the current proposal, traceability JSON, and questions file. Preserve its structured verdict: allow, await_user, or block_unsafe. Do not edit proposal or questions, repair unknown fields, write approval, or modify workflow state.
8
+ Run the bundled validator through Core's `devflow_skill_script` tool:
9
+
10
+ ```text
11
+ devflow_skill_script(
12
+ script="wireframe-validate",
13
+ args=[
14
+ "--proposal", ".planning/features/<feature>/proposal.yaml",
15
+ "--traceability", ".planning/requirements/<feature>/traceability.json",
16
+ "--questions", ".planning/features/<feature>/questions.yaml",
17
+ "--canonical-out", ".planning/features/<feature>/canonical.json"
18
+ ]
19
+ )
20
+ ```
21
+
22
+ The bundled script is `plugins/devflow-core/skills/workflow-orchestrator/scripts/wireframe_validate.py`.
23
+ Do not read `skill://wireframe-validate/scripts/wireframe_validate.py`; that path does not exist.
24
+ Preserve its structured verdict: allow, await_user, or block_unsafe. Do not edit proposal or questions, repair unknown fields, write approval, or modify workflow state.
@@ -55,6 +55,37 @@ def issue(errors: list[dict[str, str]], code: str, path: str, message: str) -> N
55
55
  errors.append({"code": code, "path": path, "message": message})
56
56
 
57
57
 
58
+ def traceability_refs(traceability: Any) -> set[str]:
59
+ """Return graph-backed refs from both legacy and Core traceability views."""
60
+ if not isinstance(traceability, dict):
61
+ return set()
62
+ requirements = traceability.get("requirements")
63
+ if isinstance(requirements, list):
64
+ return {
65
+ item.get("id")
66
+ for item in requirements
67
+ if isinstance(item, dict)
68
+ and isinstance(item.get("id"), str)
69
+ and item.get("status") in {"confirmed", "approved"}
70
+ and item.get("scope", "in") == "in"
71
+ }
72
+ nodes = traceability.get("nodes")
73
+ if not isinstance(nodes, list):
74
+ return set()
75
+ allowed_kinds = {"requirement", "rule", "behavior", "boundary"}
76
+ refs: set[str] = set()
77
+ for node in nodes:
78
+ if not isinstance(node, dict) or node.get("kind") not in allowed_kinds:
79
+ continue
80
+ node_id = node.get("id")
81
+ status = node.get("status")
82
+ data = node.get("data") if isinstance(node.get("data"), dict) else {}
83
+ scope = data.get("scope", node.get("scope", "in"))
84
+ if isinstance(node_id, str) and status in {"candidate", "confirmed", "approved"} and scope != "out":
85
+ refs.add(node_id)
86
+ return refs
87
+
88
+
58
89
  def unknown_keys(value: Any, allowed: set[str], path: str, errors: list[dict[str, str]]) -> None:
59
90
  if not isinstance(value, dict):
60
91
  return
@@ -161,7 +192,7 @@ def validate_inline(element: dict[str, Any], index: int, errors: list[dict[str,
161
192
  ids: set[str] = set()
162
193
  for j, col in enumerate(columns):
163
194
  if not isinstance(col, dict):
164
- issue(errors, "WIREFRAME_INVALID_FIELD_VALUE", f"{path}.columns[{j}]", "column must be an object")
195
+ issue(errors, "WIREFRAME_INVALID_FIELD_VALUE", f"{path}.columns[{j}]", "column must be an object with id and label")
165
196
  continue
166
197
  unknown_keys(col, {"id", "label"}, f"{path}.columns[{j}]", errors)
167
198
  require_id(col.get("id"), "WIREFRAME_ELEMENT_ID_MISSING", f"{path}.columns[{j}].id", errors, ids)
@@ -234,9 +265,7 @@ def validate(data: Any, traceability: Any, schema: dict[str, Any], business_mode
234
265
  if not isinstance(screens, list) or not screens:
235
266
  issue(errors, "WIREFRAME_SCREENS_MISSING", "screens", "must be a non-empty array")
236
267
  return errors, None
237
- reqs = set()
238
- if isinstance(traceability, dict):
239
- reqs = {r.get("id") for r in traceability.get("requirements", []) if isinstance(r, dict) and r.get("status") in {"confirmed", "approved"} and r.get("scope", "in") == "in"}
268
+ reqs = traceability_refs(traceability)
240
269
  screen_ids: set[str] = set()
241
270
  canonical_screens: list[dict[str, Any]] = []
242
271
  for i, screen in enumerate(screens):
@@ -568,7 +568,7 @@ steps:
568
568
  - wireframe-validate
569
569
  inputs:
570
570
  - ".planning/features/{feature}/canonical.json"
571
- - ".planning/features/{feature}/canonical.meta.json"
571
+ - ".planning/features/{feature}/wireframe-validation.meta.json"
572
572
  outputs:
573
573
  - path: ".planning/features/{feature}/wireframes/preview.md"
574
574
  type: wireframe-preview
@@ -586,7 +586,7 @@ steps:
586
586
  - wireframe-render
587
587
  inputs:
588
588
  - ".planning/features/{feature}/canonical.json"
589
- - ".planning/features/{feature}/canonical.meta.json"
589
+ - ".planning/features/{feature}/wireframe-validation.meta.json"
590
590
  - ".planning/features/{feature}/questions.yaml"
591
591
  - ".planning/features/{feature}/wireframes/preview.md"
592
592
  - ".planning/features/{feature}/wireframes/flow.svg"
package/src/constants.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export const PRODUCT_NAME = "Devflow Pi";
2
- export const PRODUCT_VERSION = "0.1.57";
2
+ export const PRODUCT_VERSION = "0.1.60";
3
3
  export const PROJECT_SCHEMA_VERSION = 1;
4
4
  export const OMP_RUNTIME_VERSION = "17.2.7";
5
5