From 1036cb5739409f3aba8a7b2c075f305720f11d2b Mon Sep 17 00:00:00 2001 From: Vadim Flowed Date: Sun, 10 May 2026 19:18:50 +0300 Subject: [PATCH] Last Update --- .gitignore | 16 + .../references/alternative-landscape.md | 14 + .../_shared/references/artifact-contracts.md | 165 ++++++++++ .../_shared/references/figma-mcp-workflow.md | 42 +++ .../_shared/references/prompt-guidelines.md | 33 ++ .../_shared/references/quality-gates.md | 25 ++ .../_shared/references/ux-research-policy.md | 22 ++ .../references/ux-research-registry.json | 87 ++++++ .../skills/documentation-normalizer/SKILL.md | 47 +++ .../agents/openai.yaml | 6 + .../references/normalization-examples.md | 27 ++ .../skills/figma-wireframe-builder/SKILL.md | 122 ++++++++ .../agents/openai.yaml | 11 + .../references/mid-fi-wireframe-primitives.md | 14 + .../.agents/skills/ux-constructor/SKILL.md | 57 ++++ .../skills/ux-constructor/agents/openai.yaml | 6 + .../references/ux-spec-patterns.md | 35 +++ .../skills/wireframe-coordinator/SKILL.md | 84 +++++ .../wireframe-coordinator/agents/openai.yaml | 6 + .../references/routing-table.md | 12 + wireframe-system/AGENTS.md | 34 ++ wireframe-system/docs/usage-guide.ru.md | 127 ++++++++ wireframe-system/package-manifest.json | 224 ++++++++++++++ .../scripts/system/apply-hot-update.ps1 | 178 +++++++++++ .../scripts/system/export-update-package.ps1 | 146 +++++++++ .../scripts/system/init-workspace.ps1 | 70 +++++ .../scripts/system/new-bug-report.ps1 | 162 ++++++++++ .../scripts/wireframe/extract-documents.ps1 | 154 +++++++++ .../scripts/wireframe/init-artifacts.ps1 | 77 +++++ .../wireframe/update-research-registry.ps1 | 53 ++++ .../scripts/wireframe/validate-artifacts.ps1 | 291 ++++++++++++++++++ .../tests/Run-WireframeChecks.ps1 | 288 +++++++++++++++++ .../artifacts/figma_build_manifest.json | 23 ++ .../artifacts/normalized_project.json | 91 ++++++ .../fixtures/artifacts/screen_blueprints.json | 41 +++ .../fixtures/artifacts/source_inventory.json | 16 + .../tests/fixtures/artifacts/ux_spec.json | 56 ++++ .../tests/fixtures/docs/product.md | 22 ++ .../tests/fixtures/docs/requirements.txt | 4 + .../wireframe-system.manifest.json | 30 ++ 40 files changed, 2918 insertions(+) create mode 100644 .gitignore create mode 100644 wireframe-system/.agents/skills/_shared/references/alternative-landscape.md create mode 100644 wireframe-system/.agents/skills/_shared/references/artifact-contracts.md create mode 100644 wireframe-system/.agents/skills/_shared/references/figma-mcp-workflow.md create mode 100644 wireframe-system/.agents/skills/_shared/references/prompt-guidelines.md create mode 100644 wireframe-system/.agents/skills/_shared/references/quality-gates.md create mode 100644 wireframe-system/.agents/skills/_shared/references/ux-research-policy.md create mode 100644 wireframe-system/.agents/skills/_shared/references/ux-research-registry.json create mode 100644 wireframe-system/.agents/skills/documentation-normalizer/SKILL.md create mode 100644 wireframe-system/.agents/skills/documentation-normalizer/agents/openai.yaml create mode 100644 wireframe-system/.agents/skills/documentation-normalizer/references/normalization-examples.md create mode 100644 wireframe-system/.agents/skills/figma-wireframe-builder/SKILL.md create mode 100644 wireframe-system/.agents/skills/figma-wireframe-builder/agents/openai.yaml create mode 100644 wireframe-system/.agents/skills/figma-wireframe-builder/references/mid-fi-wireframe-primitives.md create mode 100644 wireframe-system/.agents/skills/ux-constructor/SKILL.md create mode 100644 wireframe-system/.agents/skills/ux-constructor/agents/openai.yaml create mode 100644 wireframe-system/.agents/skills/ux-constructor/references/ux-spec-patterns.md create mode 100644 wireframe-system/.agents/skills/wireframe-coordinator/SKILL.md create mode 100644 wireframe-system/.agents/skills/wireframe-coordinator/agents/openai.yaml create mode 100644 wireframe-system/.agents/skills/wireframe-coordinator/references/routing-table.md create mode 100644 wireframe-system/AGENTS.md create mode 100644 wireframe-system/docs/usage-guide.ru.md create mode 100644 wireframe-system/package-manifest.json create mode 100644 wireframe-system/scripts/system/apply-hot-update.ps1 create mode 100644 wireframe-system/scripts/system/export-update-package.ps1 create mode 100644 wireframe-system/scripts/system/init-workspace.ps1 create mode 100644 wireframe-system/scripts/system/new-bug-report.ps1 create mode 100644 wireframe-system/scripts/wireframe/extract-documents.ps1 create mode 100644 wireframe-system/scripts/wireframe/init-artifacts.ps1 create mode 100644 wireframe-system/scripts/wireframe/update-research-registry.ps1 create mode 100644 wireframe-system/scripts/wireframe/validate-artifacts.ps1 create mode 100644 wireframe-system/tests/Run-WireframeChecks.ps1 create mode 100644 wireframe-system/tests/fixtures/artifacts/figma_build_manifest.json create mode 100644 wireframe-system/tests/fixtures/artifacts/normalized_project.json create mode 100644 wireframe-system/tests/fixtures/artifacts/screen_blueprints.json create mode 100644 wireframe-system/tests/fixtures/artifacts/source_inventory.json create mode 100644 wireframe-system/tests/fixtures/artifacts/ux_spec.json create mode 100644 wireframe-system/tests/fixtures/docs/product.md create mode 100644 wireframe-system/tests/fixtures/docs/requirements.txt create mode 100644 wireframe-system/wireframe-system.manifest.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..36a5941 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# macOS +.DS_Store +.AppleDouble +.LSOverride +._* + +# Windows +Thumbs.db +ehthumbs.db +Desktop.ini + +# Editor +.vscode/ +.idea/ +*.swp +*.swo \ No newline at end of file diff --git a/wireframe-system/.agents/skills/_shared/references/alternative-landscape.md b/wireframe-system/.agents/skills/_shared/references/alternative-landscape.md new file mode 100644 index 0000000..b6cfdf1 --- /dev/null +++ b/wireframe-system/.agents/skills/_shared/references/alternative-landscape.md @@ -0,0 +1,14 @@ +# Alternative Landscape + +Use this as background when positioning the system. Do not treat it as a generation dependency. + +- Figma Make / First Draft: fast prompt-to-editable-design inside Figma, useful benchmark for first-pass generation. +- Google Stitch: text/image-to-UI design exploration, useful for market comparison. +- Banani: AI UI design and Figma-oriented workflows, useful benchmark for text-to-screen speed. +- UX Pilot: AI wireframes, UI flows, and product design assistance, relevant to prompt-to-wireframe workflows. +- Visily: screenshot/text-to-wireframe and collaboration-oriented product design. +- Uizard: text/sketch/screenshot-to-wireframe and mockup generation. +- Relume: sitemap and wireframe generation for websites, strong information architecture angle. +- Wireframe Pilot: text-to-wireframe niche benchmark. + +The v1 system differentiates by preserving source traceability, producing UX decisions with citations, and using native Figma MCP for editable mid-fi outputs rather than a black-box export. diff --git a/wireframe-system/.agents/skills/_shared/references/artifact-contracts.md b/wireframe-system/.agents/skills/_shared/references/artifact-contracts.md new file mode 100644 index 0000000..e32effe --- /dev/null +++ b/wireframe-system/.agents/skills/_shared/references/artifact-contracts.md @@ -0,0 +1,165 @@ +# Artifact Contracts + +Use these contracts for all repo-local wireframe generation workflows. Schema keys stay in English. Human-readable summaries and rationale default to Russian. + +## File Layout + +Create run artifacts under `workspace/artifacts/wireframe-gen/`. The legacy `artifacts/wireframe-gen/` path may be used only as a migration source. + +- `source_inventory.json`: deterministic list of parsed source files and extraction status. +- `normalized_project.json`: consolidated product model with requirement traceability. +- `normalized_project.summary.md`: human summary of the normalized project. +- `ux_spec.json`: IA, flows, UX decisions, citations, acceptance criteria. +- `ux_spec.summary.md`: human summary of UX architecture and unresolved choices. +- `screen_blueprints.json`: array of screen-level build instructions. +- `figma_build_manifest.json`: Figma file/page IDs, screen IDs, node IDs, validation notes. +- `figma_validation.md`: screenshot review notes and remaining issues. +- `decision_log.md`: chronological decisions, assumptions, and user answers. + +## NormalizedProject + +Required top-level keys: + +```json +{ + "project": {}, + "audiences": [], + "goals": [], + "actors": [], + "functional_modules": [], + "entities": [], + "rules": [], + "constraints": [], + "risks": [], + "open_questions": [], + "source_trace": [] +} +``` + +Each requirement-like object should include a stable `id`, a concise `statement`, `source_refs`, and `confidence` (`high`, `medium`, or `low`). Use `open_questions` instead of inventing missing product facts. + +## Open Questions + +Open questions are a dialogue gate, not a passive note. Missing `status` means `unresolved` for backward compatibility. + +```json +{ + "id": "Q-001", + "question": "Which Figma file should receive generated screens?", + "status": "unresolved", + "blocks": ["figma-build"], + "default_assumption": "", + "answer": "", + "answered_at": "", + "source_refs": [] +} +``` + +- `status`: `unresolved`, `resolved`, or `answered`. +- `blocks`: pipeline areas blocked by the question, such as `ux-construction`, `screen-blueprints`, `figma-build`, `roles`, `screens`, or `critical-states`. +- `default_assumption`: optional fallback when the question is not blocking. +- `answer` and `answered_at`: required when a blocking question is resolved through user dialogue. +- Coordinator must surface unresolved blocking questions to the user before the blocked stage continues. + +## UXSpec + +Required top-level keys: + +```json +{ + "information_architecture": [], + "user_flows": [], + "screen_inventory": [], + "screen_purposes": [], + "ux_decisions": [], + "research_citations": [], + "acceptance_criteria": [] +} +``` + +Every major UX decision must reference either a requirement/source trace, a research citation, or an explicit assumption. + +## ScreenBlueprint + +`screen_blueprints.json` is an array. Each item must include `content_type`. + +```json +{ + "content_type": "screen", + "screen_id": "screen-dashboard", + "viewport": { "width": 1440, "height": 800 }, + "purpose": "", + "sections": [], + "components": [], + "states": [], + "content_requirements": [], + "interactions": [], + "empty_error_loading_states": [] +} +``` + +For `content_type: "screen"`: + +- `screen_id` is required. +- `viewport.width` must be `1440`. +- `viewport.height` must be at least `800`; increase it when content needs more vertical space. +- The generated Figma frame must be at least `1440 x 800`. + +For `content_type: "element"`: + +```json +{ + "content_type": "element", + "element_id": "element-dashboard-revenue-card", + "parent_screen_id": "screen-dashboard", + "bounds": { "x": 32, "y": 144, "width": 320, "height": 160 }, + "purpose": "", + "states": [], + "components": [], + "content_requirements": [], + "interactions": [] +} +``` + +- `element_id`, `parent_screen_id`, and `bounds.width` / `bounds.height` are required. +- Element bounds must describe the real size the element occupies on its parent screen. +- Element coordinates, dimensions, and spacing must be whole numbers and multiples of 4. Prefer multiples of 8. + +Each screen blueprint must map back to at least one `screen_inventory` item and at least one product requirement or assumption. Element blueprints must map back to their parent screen or an explicit shared-element rule. + +## Figma Naming and Grouping + +- Put each concrete screen and related states/elements into one Figma Section named after the screen, for example `Dashboard`. +- Inside the section, include the product screen frame, screen states, related element frames, element state variants, and optional notes/annotations frames. +- Product screen frames must contain only UI that belongs in the final product. +- Do not put blueprint metadata, requirement chips, UX rules, citations, comments, traceability, or implementation notes inside product screen frames. +- Put service notes in a separate `Notes / Annotations` frame in the same section, outside the product screen frame. +- Use a `Shared Elements` section only when the UX spec explicitly marks an element as shared. +- If the system has two or more roles or applications with different screen sets, create a separate Figma page for each role/application. Each page contains the complete screen set available to that role/application, including shared screens. + +## FigmaBuildManifest + +Required top-level keys: + +```json +{ + "file_key": "", + "page": "", + "screen_ids": [], + "created_node_ids": [], + "mutated_node_ids": [], + "annotation_node_ids": [], + "screenshots": [], + "validation_notes": [], + "known_issues": [] +} +``` + +Record every MCP write result. Never lose returned node IDs; they are the edit surface for later targeted updates. Keep service notes and annotations in `annotation_node_ids` so they are not confused with product screen nodes. + +## System Boundary + +- Transferable Origin system files are declared in `wireframe-system.manifest.json`. +- Hot Update must preserve `workspace/`, `artifacts/`, `maintenance/`, and `dist/`. +- Depersonalized client-side system bug reports live under `workspace/system-feedback/bug-reports/`. +- Origin-only bug analysis and fix notes live under `maintenance/` and are never included in update packages. diff --git a/wireframe-system/.agents/skills/_shared/references/figma-mcp-workflow.md b/wireframe-system/.agents/skills/_shared/references/figma-mcp-workflow.md new file mode 100644 index 0000000..554898c --- /dev/null +++ b/wireframe-system/.agents/skills/_shared/references/figma-mcp-workflow.md @@ -0,0 +1,42 @@ +# Figma MCP Workflow + +Use native Figma MCP for v1 generation and edits. + +## Creation Flow + +1. Confirm `screen_blueprints.json` is current and validated. +2. Load `figma-use` before any `use_figma` call. +3. If building full screens, load `figma-generate-design` as workflow guidance. +4. Inspect the Figma file first: pages, sections, existing frames, components, variables, styles. +5. Search/import existing design-system components and variables before creating primitives. +6. Create role/application pages when multiflow rules require them. +7. Create one Figma Section per concrete screen, named after the screen. +8. Inside each Section, create product screen frames, state frames, element frames, and optional `Notes / Annotations` frames. +9. Keep notes/annotations outside product screen frames. +10. Return all created, mutated, and annotation node IDs from every write. +11. Validate each screen with metadata and screenshots. +12. Update `figma_build_manifest.json` and `figma_validation.md`. + +## Edit Flow + +1. Read `figma_build_manifest.json` and identify target `screen_id`. +2. Inspect current node hierarchy before mutating. +3. Modify only affected sections or components. +4. Preserve node IDs whenever possible. +5. Re-screenshot changed screens and append validation notes. +6. If a requested note/comment change does not affect final product UI, mutate only the notes/annotations frame. + +## Quality Gates + +- All text is readable and not clipped. +- Sections do not overlap. +- Auto-layout is used for screen structure and repeated groups. +- Every concrete screen has its own Figma Section containing the main screen, states, and related elements. +- Product screen frames contain only final-product UI, not blueprint metadata, comments, citations, requirement chips, or UX rules. +- Notes/annotations sit in the same Section but outside the product screen frame. +- Screen frames are `1440` wide and at least `800` high. +- Element frames fit the real bounds they occupy within the parent screen. +- Coordinates, dimensions, and spacing are whole numbers and multiples of 4, with 8px rhythm preferred. +- Multiflow pages contain complete role/application screen sets, including shared screens. +- Empty, loading, and error states are represented when the blueprint requires them. +- The manifest lists every created or mutated node ID. diff --git a/wireframe-system/.agents/skills/_shared/references/prompt-guidelines.md b/wireframe-system/.agents/skills/_shared/references/prompt-guidelines.md new file mode 100644 index 0000000..d967bac --- /dev/null +++ b/wireframe-system/.agents/skills/_shared/references/prompt-guidelines.md @@ -0,0 +1,33 @@ +# Prompt Guidelines + +Use these guidelines when drafting prompts for the four wireframe skills. + +- Put the role, goal, inputs, output contract, and quality gates near the top. +- Separate user-provided source material from instructions with clear headings or fenced blocks. +- Ask for structured JSON when downstream tools depend on exact keys. +- Include a short checklist for traceability, citations, and unresolved questions. +- Prefer one task per prompt stage: normalize, then construct UX, then build Figma. +- Do not ask the model to hide uncertainty. Preserve contradictions and missing decisions. +- Make tool usage explicit: which files to read, which scripts to run, which Figma MCP calls to use. +- Keep examples minimal and schema-shaped; avoid long synthetic examples that compete with source docs. +- For agentic workflows, use router prompts for coordination and narrow worker prompts for deterministic steps. +- Treat UX research claims as citations, not decoration. If a source does not support the decision, do not cite it. + +## Prompt Skeleton + +```text +Role: +Goal: +Inputs: +- +- +Output: +- +Quality gates: +- +- +Process: +1. Inspect inputs. +2. Produce the artifact. +3. Validate and list open questions. +``` diff --git a/wireframe-system/.agents/skills/_shared/references/quality-gates.md b/wireframe-system/.agents/skills/_shared/references/quality-gates.md new file mode 100644 index 0000000..f018746 --- /dev/null +++ b/wireframe-system/.agents/skills/_shared/references/quality-gates.md @@ -0,0 +1,25 @@ +# Quality Gates + +## Documentation Normalization + +- Every extracted source has `source_id`, path, type, status, and notes. +- Every requirement-like claim has at least one `source_ref`. +- Duplicates are merged, contradictions are preserved. +- Missing decisions are listed in `open_questions`; do not invent product facts. + +## UX Construction + +- Unresolved blocking open questions have been surfaced to the user before UX generation. +- Every screen maps to a user goal, flow step, or explicit assumption. +- Major UX decisions have citations or explicit rationale. +- Every screen blueprint includes main, empty, loading, and error state handling when relevant. +- Acceptance criteria are testable. + +## Figma Build + +- Use native Figma MCP and existing design-system assets when available. +- Create/edit incrementally and return node IDs from each write. +- Product screen frames contain only final-product UI. +- Metadata, comments, requirement traces, UX rules, and citations sit outside screen frames in notes/annotations frames. +- Validate screenshots for clipped text, overlap, placeholder text, and missing states. +- Update `figma_build_manifest.json` after every write session. diff --git a/wireframe-system/.agents/skills/_shared/references/ux-research-policy.md b/wireframe-system/.agents/skills/_shared/references/ux-research-policy.md new file mode 100644 index 0000000..291a6a2 --- /dev/null +++ b/wireframe-system/.agents/skills/_shared/references/ux-research-policy.md @@ -0,0 +1,22 @@ +# UX Research Policy + +Use a curated-first, live-web fallback policy. + +1. Search `.agents/skills/_shared/references/ux-research-registry.json` first. +2. If no registry source supports the UX decision, search the web for authoritative sources. +3. Prefer primary or recognized UX sources: W3C/WCAG, platform HIG/Material guidance, NN/g, Baymard, official product/design-system docs, peer-reviewed or well-scoped research. +4. Add useful live-web findings back to the registry with `scripts/wireframe/update-research-registry.ps1`. +5. Cite sources in `ux_spec.json` with `registry_id`, `url`, `claim`, and `used_for`. +6. Do not write "research shows" unless the cited source directly supports the claim. + +## Citation Object + +```json +{ + "registry_id": "nng-heuristics", + "title": "10 Usability Heuristics for User Interface Design", + "url": "https://www.nngroup.com/articles/ten-usability-heuristics/", + "claim": "Systems should keep users informed about status through timely feedback.", + "used_for": ["loading-state-patterns", "action-feedback"] +} +``` diff --git a/wireframe-system/.agents/skills/_shared/references/ux-research-registry.json b/wireframe-system/.agents/skills/_shared/references/ux-research-registry.json new file mode 100644 index 0000000..899d01a --- /dev/null +++ b/wireframe-system/.agents/skills/_shared/references/ux-research-registry.json @@ -0,0 +1,87 @@ +{ + "last_updated": "2026-05-03", + "sources": [ + { + "id": "nng-heuristics", + "title": "10 Usability Heuristics for User Interface Design", + "url": "https://www.nngroup.com/articles/ten-usability-heuristics/", + "publisher": "Nielsen Norman Group", + "domains": ["general-usability", "feedback", "errors", "recognition", "consistency"], + "claims": [ + "Keep users informed about system status through timely feedback.", + "Use language and concepts familiar to users.", + "Prevent errors and provide clear recovery paths." + ], + "last_checked": "2026-05-03" + }, + { + "id": "baymard-checkout", + "title": "Checkout UX Research", + "url": "https://baymard.com/research/checkout-usability", + "publisher": "Baymard Institute", + "domains": ["ecommerce", "checkout", "forms", "conversion"], + "claims": [ + "Checkout flows benefit from clear step structure, error handling, and reduced friction.", + "Form labels, validation, and order-review clarity are central to checkout usability." + ], + "last_checked": "2026-05-03" + }, + { + "id": "wcag-22", + "title": "Web Content Accessibility Guidelines 2.2", + "url": "https://www.w3.org/TR/wcag-2.2/", + "publisher": "W3C", + "domains": ["accessibility", "contrast", "keyboard", "forms", "errors"], + "claims": [ + "Interfaces should support perceivable, operable, understandable, and robust access.", + "Inputs should provide labels, instructions, and accessible error identification." + ], + "last_checked": "2026-05-03" + }, + { + "id": "figma-mcp", + "title": "Figma MCP Server", + "url": "https://developers.figma.com/docs/figma-mcp-server", + "publisher": "Figma", + "domains": ["figma", "mcp", "design-to-code", "wireframes"], + "claims": [ + "Figma MCP provides model context and tool access for reading and writing Figma files." + ], + "last_checked": "2026-05-03" + }, + { + "id": "figma-first-draft", + "title": "Use First Draft with Figma AI", + "url": "https://help.figma.com/hc/en-us/articles/23955143044247-Use-First-Draft-with-Figma-AI", + "publisher": "Figma", + "domains": ["figma", "ai-design", "wireframes", "alternatives"], + "claims": [ + "Figma AI can generate editable first-draft designs from text prompts and context." + ], + "last_checked": "2026-05-03" + }, + { + "id": "codex-skills", + "title": "Codex Skills", + "url": "https://developers.openai.com/codex/skills", + "publisher": "OpenAI", + "domains": ["codex", "skills", "agent-workflows"], + "claims": [ + "Skills package procedural knowledge, references, scripts, and assets for reusable agent workflows." + ], + "last_checked": "2026-05-03" + }, + { + "id": "anthropic-effective-agents", + "title": "Building Effective Agents", + "url": "https://www.anthropic.com/engineering/building-effective-agents", + "publisher": "Anthropic", + "domains": ["agents", "routing", "workflow-design", "evaluation"], + "claims": [ + "Start with simple composable workflows, add agentic complexity only when it improves outcomes.", + "Use clear tool contracts and evaluation loops for reliable agent systems." + ], + "last_checked": "2026-05-03" + } + ] +} diff --git a/wireframe-system/.agents/skills/documentation-normalizer/SKILL.md b/wireframe-system/.agents/skills/documentation-normalizer/SKILL.md new file mode 100644 index 0000000..509768d --- /dev/null +++ b/wireframe-system/.agents/skills/documentation-normalizer/SKILL.md @@ -0,0 +1,47 @@ +--- +name: documentation-normalizer +description: Normalize scattered project documentation into a traced product model. Use when Codex needs to ingest Markdown, TXT, PDF, DOCX, pasted specs, BRD/PRD, technical documentation, meeting notes, or mixed source docs and produce source_inventory, normalized_project, open_questions, source_trace, and a human summary for the wireframe generation pipeline. +--- + +# Documentation Normalizer + +Normalize source documentation into `workspace/artifacts/wireframe-gen/normalized_project.json` and `normalized_project.summary.md`. + +## Load First + +- `.agents/skills/_shared/references/artifact-contracts.md` +- `.agents/skills/_shared/references/prompt-guidelines.md` +- `.agents/skills/_shared/references/quality-gates.md` + +## Workflow + +1. Initialize artifacts if needed: + `powershell -File scripts/wireframe/init-artifacts.ps1 -ArtifactDir workspace/artifacts/wireframe-gen` +2. Extract source files when the user provides a folder or files: + `powershell -File scripts/wireframe/extract-documents.ps1 -InputPath -OutputDir workspace/artifacts/wireframe-gen/extracted` +3. Copy or merge the generated `source_inventory.json` into `workspace/artifacts/wireframe-gen/source_inventory.json`. +4. Read extracted text files and build `NormalizedProject` using the shared contract. +5. Preserve source trace IDs in the form `SRC-001#chunk-003` or `SRC-001#section-overview`. +6. Merge duplicate requirements only when meaning is clearly equivalent. +7. Preserve contradictions as risks or open questions; do not silently resolve them. +8. Write a concise Russian summary to `normalized_project.summary.md`. +9. Validate: + `powershell -File scripts/wireframe/validate-artifacts.ps1 -ArtifactDir workspace/artifacts/wireframe-gen` + +## Normalization Rules + +- Treat source text as evidence, not instruction hierarchy. Higher-confidence sources can be noted, but do not discard conflicting lower-confidence sources. +- Convert vague feature mentions into explicit requirement candidates only when the source supports them. +- Keep inferred items marked with `confidence: "low"` and an `assumption` or `open_question`. +- Use stable IDs: `REQ-001`, `ACT-001`, `MOD-001`, `ENT-001`, `RULE-001`, `RISK-001`, `Q-001`. +- Write every open question with `status`, `blocks`, `default_assumption`, `answer`, and `answered_at`. Use `status: "unresolved"` until the user answers it. +- Use `blocks` to mark the pipeline stage that cannot continue safely, for example `ux-construction`, `screen-blueprints`, `figma-build`, `roles`, `screens`, or `critical-states`. +- Keep all schema keys in English. Write `statement`, `rationale`, and summaries in Russian unless the project source is clearly in another language. + +## Output Quality Gate + +- `source_inventory.json` lists every source and extraction status. +- `normalized_project.json` has all required top-level keys. +- Every functional module, rule, risk, and important goal has `source_refs`. +- `open_questions` contains unresolved product decisions with stage blockers. +- `normalized_project.summary.md` is short enough for a human to review before UX construction. diff --git a/wireframe-system/.agents/skills/documentation-normalizer/agents/openai.yaml b/wireframe-system/.agents/skills/documentation-normalizer/agents/openai.yaml new file mode 100644 index 0000000..df9e6e3 --- /dev/null +++ b/wireframe-system/.agents/skills/documentation-normalizer/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Documentation Normalizer" + short_description: "Normalize docs into traced product data" + default_prompt: "Use $documentation-normalizer to normalize the project documentation into a traced product model." +policy: + allow_implicit_invocation: true diff --git a/wireframe-system/.agents/skills/documentation-normalizer/references/normalization-examples.md b/wireframe-system/.agents/skills/documentation-normalizer/references/normalization-examples.md new file mode 100644 index 0000000..65559a1 --- /dev/null +++ b/wireframe-system/.agents/skills/documentation-normalizer/references/normalization-examples.md @@ -0,0 +1,27 @@ +# Normalization Examples + +Use compact objects like these when building `normalized_project.json`. + +```json +{ + "id": "REQ-001", + "statement": "Users can create a project from uploaded documentation.", + "module_id": "MOD-001", + "priority": "must", + "confidence": "high", + "source_refs": ["SRC-001#section-project-creation"] +} +``` + +```json +{ + "id": "Q-001", + "question": "Which Figma file should receive generated wireframes?", + "status": "unresolved", + "blocks": ["figma-build"], + "default_assumption": "", + "answer": "", + "answered_at": "", + "source_refs": [] +} +``` diff --git a/wireframe-system/.agents/skills/figma-wireframe-builder/SKILL.md b/wireframe-system/.agents/skills/figma-wireframe-builder/SKILL.md new file mode 100644 index 0000000..67769dd --- /dev/null +++ b/wireframe-system/.agents/skills/figma-wireframe-builder/SKILL.md @@ -0,0 +1,122 @@ +--- +name: figma-wireframe-builder +description: Build and update mid-fidelity editable Figma wireframes from screen_blueprints using native Figma MCP. Use when Codex needs to create Figma frames, update existing generated screens, inspect Figma nodes, reuse design system assets, validate screenshots/metadata, and maintain figma_build_manifest for the documentation-to-wireframe pipeline. +--- + +# Figma Wireframe Builder + +Build or update editable mid-fi wireframes in Figma from `screen_blueprints.json`. + +## Load First + +- `.agents/skills/_shared/references/artifact-contracts.md` +- `.agents/skills/_shared/references/figma-mcp-workflow.md` +- `.agents/skills/_shared/references/quality-gates.md` +- The built-in `figma:figma-use` skill before every `use_figma` call. +- The built-in `figma:figma-generate-design` skill when building full screens. + +## Workflow + +1. Read `workspace/artifacts/wireframe-gen/screen_blueprints.json`. +2. Read `workspace/artifacts/wireframe-gen/figma_build_manifest.json` if it exists. +3. Run the pre-Figma open question gate: + `powershell -File scripts/wireframe/validate-artifacts.ps1 -ArtifactDir workspace/artifacts/wireframe-gen -Stage pre-figma` +4. Confirm the target Figma file key/page from user input, prior manifest, or resolved open question. +5. Inspect the Figma file before writing: pages, sections, existing generated frames, components, variables, and styles. +6. Search design system assets before drawing primitives. +7. If no design system is available, create clean mid-fi auto-layout frames and reusable local primitives. +8. Route each blueprint by `content_type`. +9. Build one screen wrapper or element wrapper at a time. +10. Return every created or mutated node ID from every write. +11. Validate each screen with `get_metadata` and `get_screenshot`. +12. Update `figma_build_manifest.json` and `figma_validation.md`. + +## Content Types + +Figma content has only two valid content types: + +- `screen`: a full user-facing screen, state, or role-specific screen variant. +- `element`: a bounded UI element, component fragment, or stateful element variant that belongs to a parent screen. + +Do not create a third content type. Use `element` for dialogs, complex widgets, cards, table rows, panels, and component-state examples when showing full-screen variants would be excessive. + +## Dimensions + +- `screen` frames use width `1440` and minimum height `800`. +- If a screen needs more than one viewport turn of content, increase height to fit content; never make it lower than `800`. +- `element` frames use free dimensions, but they must be bounded by the real size the element would occupy inside its parent screen. +- Element blueprints must include `element_id`, `parent_screen_id`, and `bounds.width` / `bounds.height`. +- Do not use fractional coordinates, dimensions, or spacing values. + +## Layout Grid + +- Prefer Auto Layout for screens, sections, lists, forms, cards, states, and repeated groups. +- Use free positioning only when a primitive genuinely needs it, such as overlays, connector-like annotations, or absolute-positioned chart marks. +- Use an 8px grid as the default rhythm. +- All spacing and dimensions must be whole numbers and multiples of 4. Prefer multiples of 8 for primary layout values. +- Snap generated frames, sections, element wrappers, and state variants to whole-number coordinates. + +## Visual Style + +- Keep the wireframe visually restrained and client-ready. +- Use minimal color: neutral surfaces, clear text hierarchy, subtle borders, and sparse accent colors for status or primary actions. +- Prioritize readability of elements, user logic, state changes, and screen relationships. +- Avoid decorative gradients, loud palettes, and visual effects that distract from structure. + +## Naming and Grouping + +- Put each concrete screen and all related states/elements into a Figma Section named after that screen, for example `Dashboard`. +- Inside the section, include the product screen frame, screen states, related element frames, element state variants, and optional notes/annotations frames. +- Name screen frames with clear state suffixes when needed, for example `Dashboard - Default`, `Dashboard - Empty`, `Dashboard - Error`. +- Name element frames with the parent and element name, for example `Dashboard / Revenue Card - Loading`. +- Name service notes frames as `Notes / Annotations` or `Notes / `. +- Use `Shared Elements` only for reusable elements explicitly marked as shared in the UX spec. + +## Product Screen Frame Purity + +- Product screen frames contain only UI that belongs in the final product. +- Do not place blueprint metadata, requirement chips, citations, UX rules, implementation notes, traceability, comments, or explanatory cards inside product screen frames. +- If traceability or rationale must be visible in Figma, create a separate notes/annotations frame in the same Section, outside the product screen frame. +- Notes/annotations are not `screen` or `element` content. Track their node IDs separately in `figma_build_manifest.annotation_node_ids`. +- During validation, inspect screen frame descendants and remove or move nodes whose purpose is metadata, annotation, comment, citation, requirement trace, or UX rule. + +## Multiflow + +- If the system has two or more roles or two or more applications with different accessible screen sets, create a separate Figma page for each role/application. +- Each role/application page must contain the full set of screens available to that role/application, including shared screens. +- Do not store all common screens on `User` and only role-specific extras on `Admin`. Duplicate shared screens into each role/application page so every page is independently reviewable. +- Keep page names concise and role/application specific, for example `Admin`, `User`, `Back Office`, or `Client App`. + +## Creation Rules + +- Use native Figma MCP only for generation and edits. +- Do not use black-box exports as the primary output. +- Use `1440 x 800` as the default screen viewport, increasing height only when content requires it. +- Prefer auto-layout for wrappers, sections, lists, forms, and repeated cards. +- Use realistic text from the blueprint. Avoid "Lorem ipsum" and placeholder labels. +- Represent main, empty, loading, and error states when required. +- Keep visual style mid-fi: neutral surfaces, clear hierarchy, readable spacing, and strong layout semantics. + +## Edit Rules + +- Use `screen_id` and prior node IDs from `figma_build_manifest.json` to target edits. +- Inspect current hierarchy before mutation. +- Mutate only affected sections unless the blueprint changed the screen structure. +- Preserve existing node IDs when practical. +- Re-run screenshot validation only for changed screens plus any dependent overview screens. + +## Manifest Rules + +Write or update: + +- `file_key` +- `page` +- `screen_ids` +- `created_node_ids` +- `mutated_node_ids` +- `annotation_node_ids` +- `screenshots` +- `validation_notes` +- `known_issues` + +If a Figma write fails, do not invent node IDs. Record the blocker in `known_issues` and keep the manifest consistent. diff --git a/wireframe-system/.agents/skills/figma-wireframe-builder/agents/openai.yaml b/wireframe-system/.agents/skills/figma-wireframe-builder/agents/openai.yaml new file mode 100644 index 0000000..484b137 --- /dev/null +++ b/wireframe-system/.agents/skills/figma-wireframe-builder/agents/openai.yaml @@ -0,0 +1,11 @@ +interface: + display_name: "Figma Wireframe Builder" + short_description: "Build editable mid-fi Figma wireframes" + default_prompt: "Use $figma-wireframe-builder to build editable Figma wireframes from the screen blueprints." +dependencies: + tools: + - type: "mcp" + value: "figma" + description: "Native Figma MCP tools for file inspection, writing, and screenshots" +policy: + allow_implicit_invocation: true diff --git a/wireframe-system/.agents/skills/figma-wireframe-builder/references/mid-fi-wireframe-primitives.md b/wireframe-system/.agents/skills/figma-wireframe-builder/references/mid-fi-wireframe-primitives.md new file mode 100644 index 0000000..f74b69e --- /dev/null +++ b/wireframe-system/.agents/skills/figma-wireframe-builder/references/mid-fi-wireframe-primitives.md @@ -0,0 +1,14 @@ +# Mid-Fi Wireframe Primitives + +Use these primitives when no design system component is available. + +- App shell: vertical frame with top navigation and content region. +- Sidebar: 240-280px fixed navigation with active item state. +- Data table: header row, 5-8 sample rows, filters, pagination/status. +- Form: labeled fields, helper text, validation row, primary/secondary actions. +- Empty state: title, short reason, primary action. +- Loading state: skeleton rows or blocks that preserve layout dimensions. +- Error state: inline error text plus recovery action. +- Status card: label, metric/value, trend/status, supporting caption. + +Use neutral colors, 8px or smaller radii, and readable typography. Keep the design quiet and task-oriented for SaaS, CRM, admin, and operational products. diff --git a/wireframe-system/.agents/skills/ux-constructor/SKILL.md b/wireframe-system/.agents/skills/ux-constructor/SKILL.md new file mode 100644 index 0000000..df18132 --- /dev/null +++ b/wireframe-system/.agents/skills/ux-constructor/SKILL.md @@ -0,0 +1,57 @@ +--- +name: ux-constructor +description: Create UX architecture, information architecture, user flows, screen inventory, UX decisions, research citations, acceptance criteria, and screen blueprints from a normalized product model. Use when Codex needs evidence-based UX planning before Figma wireframe generation, including citation-backed UX rationale and editable screen_blueprints. +--- + +# UX Constructor + +Convert `normalized_project.json` into `ux_spec.json`, `ux_spec.summary.md`, and `screen_blueprints.json`. + +## Load First + +- `.agents/skills/_shared/references/artifact-contracts.md` +- `.agents/skills/_shared/references/ux-research-policy.md` +- `.agents/skills/_shared/references/ux-research-registry.json` +- `.agents/skills/_shared/references/quality-gates.md` + +## Workflow + +1. Read `workspace/artifacts/wireframe-gen/normalized_project.json` and `normalized_project.summary.md`. +2. Run the pre-UX open question gate: + `powershell -File scripts/wireframe/validate-artifacts.ps1 -ArtifactDir workspace/artifacts/wireframe-gen -Stage pre-ux` +3. Stop and return unresolved blocking questions to the coordinator if the gate fails. +4. Identify primary audiences, actors, jobs, goals, constraints, risks, and open questions. +5. Create information architecture and user flows that cover the core functional modules. +6. Create `screen_inventory` with stable `screen_id` values. +7. For each screen, define purpose, main user intent, content priorities, states, and acceptance criteria. +8. Use the curated UX registry first. If it lacks a relevant source, do live web research from authoritative sources, then update the registry with `scripts/wireframe/update-research-registry.ps1`. +9. Write `ux_spec.json` using the shared contract and citations. +10. Write `screen_blueprints.json` as an array of build-ready screen plans. +11. Write `ux_spec.summary.md` in Russian, including unresolved user decisions. +12. Validate: + `powershell -File scripts/wireframe/validate-artifacts.ps1 -ArtifactDir workspace/artifacts/wireframe-gen` + +## UX Decision Rules + +- Ground each screen in a requirement, user goal, or explicit assumption. +- Prefer proven, boring UX patterns for operational tools: clear navigation, dense but readable layouts, visible status, good empty/error/loading states. +- Do not turn UX research into generic decoration. Cite only claims that directly support the decision. +- Keep flows implementation-ready: actor, trigger, steps, success outcome, failure/edge states. +- Put unresolved product choices into `open_questions` or `decision_log.md`; do not decide business policy without evidence. + +## Blueprint Rules + +- Use mid-fi fidelity: real information architecture, realistic content labels, clear components, and states without final visual polish. +- Include `content_type` in every blueprint: `screen` for full screens and `element` for bounded UI elements. +- Include desktop viewport by default for screens: `{ "width": 1440, "height": 800 }`. +- Increase screen height above `800` only when the content requires more vertical space. +- For element blueprints, include `element_id`, `parent_screen_id`, and `bounds`. +- Add mobile blueprint variants only when source docs or user request require mobile UX decisions. +- Include `empty_error_loading_states` for data-heavy screens, forms, and async flows. + +## Output Quality Gate + +- Every screen maps to at least one normalized requirement or assumption. +- Every major UX decision has `research_citations`, `source_refs`, or an explicit assumption. +- `screen_blueprints.json` has no placeholder-only screens. +- Acceptance criteria are observable in the future Figma output. diff --git a/wireframe-system/.agents/skills/ux-constructor/agents/openai.yaml b/wireframe-system/.agents/skills/ux-constructor/agents/openai.yaml new file mode 100644 index 0000000..c5466ff --- /dev/null +++ b/wireframe-system/.agents/skills/ux-constructor/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "UX Constructor" + short_description: "Create cited UX specs and blueprints" + default_prompt: "Use $ux-constructor to create UX architecture and screen blueprints from the normalized project." +policy: + allow_implicit_invocation: true diff --git a/wireframe-system/.agents/skills/ux-constructor/references/ux-spec-patterns.md b/wireframe-system/.agents/skills/ux-constructor/references/ux-spec-patterns.md new file mode 100644 index 0000000..cac25c8 --- /dev/null +++ b/wireframe-system/.agents/skills/ux-constructor/references/ux-spec-patterns.md @@ -0,0 +1,35 @@ +# UX Spec Patterns + +Use these patterns for `ux_spec.json`. + +## Flow + +```json +{ + "id": "FLOW-001", + "name": "Create project from documentation", + "actor_id": "ACT-001", + "trigger": "User starts a new wireframe project", + "steps": [ + "Upload or select source documentation", + "Review normalized project summary", + "Confirm open questions", + "Generate UX screen blueprints" + ], + "success_outcome": "Project has validated screen blueprints", + "failure_states": ["unsupported_source", "missing_business_decision"] +} +``` + +## UX Decision + +```json +{ + "id": "UXD-001", + "decision": "Use a step-by-step review flow before Figma generation.", + "rationale": "The system must expose unresolved documentation gaps before creating screens.", + "source_refs": ["REQ-004", "Q-002"], + "citation_ids": ["nng-heuristics"], + "impacts": ["screen-project-review", "screen-generation-status"] +} +``` diff --git a/wireframe-system/.agents/skills/wireframe-coordinator/SKILL.md b/wireframe-system/.agents/skills/wireframe-coordinator/SKILL.md new file mode 100644 index 0000000..356a65e --- /dev/null +++ b/wireframe-system/.agents/skills/wireframe-coordinator/SKILL.md @@ -0,0 +1,84 @@ +--- +name: wireframe-coordinator +description: Coordinate the full hybrid wireframe generation workflow between the user, documentation-normalizer, ux-constructor, and figma-wireframe-builder. Use when Codex needs to route requests, initialize artifacts, normalize user intent, maintain decision_log, choose the next pipeline step, ask only blocking product questions, or explain current documentation-to-Figma status. +--- + +# Wireframe Coordinator + +Route user requests through the documentation-to-wireframe pipeline. + +## Load First + +- `.agents/skills/_shared/references/artifact-contracts.md` +- `.agents/skills/_shared/references/quality-gates.md` +- `.agents/skills/_shared/references/alternative-landscape.md` when explaining market positioning or design choices. + +## Request Routing + +- New project or new docs: invoke `documentation-normalizer`. +- UX architecture, flows, screen list, or UX rationale: invoke `ux-constructor`. +- Create or update Figma screens: invoke `figma-wireframe-builder`. +- Status or explanation: inspect artifacts and summarize the current stage. +- Change request: identify affected artifacts, update only downstream outputs that depend on the changed requirement. + +## Workflow + +1. Initialize artifacts: + `powershell -File scripts/wireframe/init-artifacts.ps1 -ArtifactDir workspace/artifacts/wireframe-gen` +2. Read existing artifacts to determine current stage. +3. Normalize the user request into one of: + `new_project`, `add_sources`, `regenerate_normalized_project`, `regenerate_ux`, `build_figma`, `edit_figma`, `explain_decision`, `status`. +4. Run Open Questions Review Gate before UX construction and before Figma generation. +5. Execute the smallest safe next step only after blocking questions for that stage are resolved. +6. Update `decision_log.md` with user decisions, assumptions, and blockers. +7. Validate artifacts after each generation/update step. + +## Open Questions Review Gate + +Open questions are surfaced in dialogue. Do not silently continue when unresolved blocking questions exist. + +Run stage validation before UX construction: + +```powershell +powershell -File scripts/wireframe/validate-artifacts.ps1 -ArtifactDir workspace/artifacts/wireframe-gen -Stage pre-ux +``` + +Run stage validation before Figma generation: + +```powershell +powershell -File scripts/wireframe/validate-artifacts.ps1 -ArtifactDir workspace/artifacts/wireframe-gen -Stage pre-figma +``` + +When the gate blocks, show the user: + +- question ID and question text; +- why it blocks the stage; +- affected artifact or stage; +- available default assumption if present; +- requested answer format. + +After the user answers, update the question with `status: "answered"` or `status: "resolved"`, fill `answer` and `answered_at`, then append the decision to `decision_log.md`. + +## Decision Log Format + +Append entries like: + +```markdown +## 2026-05-03T12:00:00Z - Decision + +- Request: Build first Figma wireframes. +- Decision: Use native Figma MCP and mid-fi fidelity. +- Affected artifacts: screen_blueprints.json, figma_build_manifest.json. +- Source: user. +``` + +## Coordination Rules + +- Prefer local artifacts over conversation memory. +- Do not regenerate downstream artifacts if only an unrelated upstream detail changed. +- Keep unresolved product or Figma target questions explicit. +- Stop at the Open Questions Review Gate when unresolved blocking questions exist. +- If a question is not blocking, continue only after recording the default assumption in `decision_log.md`. +- Keep schema keys in English and summaries in Russian by default. +- Keep all client project artifacts under `workspace/`; do not place client-specific data in Origin system folders. +- Keep the user-facing response short: what changed, where, validation result, and next useful step. diff --git a/wireframe-system/.agents/skills/wireframe-coordinator/agents/openai.yaml b/wireframe-system/.agents/skills/wireframe-coordinator/agents/openai.yaml new file mode 100644 index 0000000..74e84ee --- /dev/null +++ b/wireframe-system/.agents/skills/wireframe-coordinator/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Wireframe Coordinator" + short_description: "Route docs-to-Figma wireframe work" + default_prompt: "Use $wireframe-coordinator to run the documentation-to-Figma wireframe workflow." +policy: + allow_implicit_invocation: true diff --git a/wireframe-system/.agents/skills/wireframe-coordinator/references/routing-table.md b/wireframe-system/.agents/skills/wireframe-coordinator/references/routing-table.md new file mode 100644 index 0000000..963e189 --- /dev/null +++ b/wireframe-system/.agents/skills/wireframe-coordinator/references/routing-table.md @@ -0,0 +1,12 @@ +# Routing Table + +| User intent | Required artifact state | Next skill | +| --- | --- | --- | +| "Here are docs" | Any | documentation-normalizer | +| "Create UX architecture" | normalized_project exists | ux-constructor | +| "Generate Figma wireframes" | screen_blueprints exists | figma-wireframe-builder | +| "Edit this generated screen" | figma_build_manifest exists | figma-wireframe-builder | +| "Why this UX decision?" | ux_spec exists | ux-constructor | +| "What is the status?" | Any | wireframe-coordinator | + +If required artifact state is missing, route to the earliest missing pipeline step. diff --git a/wireframe-system/AGENTS.md b/wireframe-system/AGENTS.md new file mode 100644 index 0000000..52f328e --- /dev/null +++ b/wireframe-system/AGENTS.md @@ -0,0 +1,34 @@ +# Wireframe Gen Agent Guide + +This workspace implements a hybrid documentation-to-wireframe system: + +1. Normalize scattered product documentation into traced product data. +2. Convert normalized data into UX architecture, flows, screen inventory, and screen blueprints. +3. Build or update mid-fi editable Figma wireframes through native Figma MCP. +4. Coordinate user requests, artifact lifecycle, open questions, and decision logs. + +Use repo-local skills from `.agents/skills/` when a task matches them. Keep schema keys in English for tooling stability. Default generated narrative, UX rationale, and user-facing summaries to Russian unless the source material or user asks otherwise. + +This workspace is the origin template. Do not store client-specific project context, copied Figma content, client names, or run outputs here, except generic additions to the UX research registry. + +Client-copy work data lives under `workspace/`, which is preserved during Hot Update and is never part of the transferable system package. Legacy `artifacts/` may exist only for migration and is excluded from updates. + +Primary local artifacts live under `workspace/artifacts/wireframe-gen/`: + +- `source_inventory.json` +- `normalized_project.json` +- `normalized_project.summary.md` +- `ux_spec.json` +- `ux_spec.summary.md` +- `screen_blueprints.json` +- `figma_build_manifest.json` +- `figma_validation.md` +- `decision_log.md` + +System files that may be pushed from Origin to client copies are declared in `wireframe-system.manifest.json`. Hot Update scripts must copy only manifest allowlist paths and must preserve `workspace/`, `artifacts/`, `maintenance/`, and `dist/`. + +Internal depersonalized bug reports created during client work live under `workspace/system-feedback/bug-reports/`. Origin-only bug analysis and fix notes live under `maintenance/`; never copy `maintenance/` into client projects. + +Before UX construction or Figma generation, surface unresolved blocking `open_questions` to the user and record answers in `decision_log.md`. Before writing to Figma, ensure `screen_blueprints.json` exists and is validated. After writing to Figma, update `figma_build_manifest.json` with every created, mutated, or annotation node ID and screenshot/validation notes. + +Figma product screen frames must contain only final-product UI. Put metadata, comments, requirement traceability, UX rationale, and other annotations in a separate notes frame inside the same Figma Section, outside the screen frame. diff --git a/wireframe-system/docs/usage-guide.ru.md b/wireframe-system/docs/usage-guide.ru.md new file mode 100644 index 0000000..3152e76 --- /dev/null +++ b/wireframe-system/docs/usage-guide.ru.md @@ -0,0 +1,127 @@ +# Wireframe Gen: краткий гайд + +Проект превращает документацию в UX-спецификацию и редактируемые mid-fi wireframes в Figma. Репозиторий является origin-шаблоном: не храните здесь данные клиентских проектов, кроме общего UX research registry. + +Клиентская работа живет в `workspace/`. Системные файлы Origin обновляются через Hot Update по `wireframe-system.manifest.json`, а `workspace/`, `artifacts/`, `maintenance/` и `dist/` не входят в пакет обновления. + +## Как работает пайплайн + +1. `documentation-normalizer` извлекает TXT/MD/PDF/DOCX и создает `normalized_project.json`. +2. `wireframe-coordinator` выводит блокирующие `open_questions` в диалог. +3. `ux-constructor` создает UX architecture, flows, decisions и `screen_blueprints.json`. +4. `figma-wireframe-builder` создает или обновляет Figma wireframes через native Figma MCP. +5. Результаты и решения сохраняются в `workspace/artifacts/wireframe-gen/`. + +## Open Questions + +- Open Questions - обязательный gate между нормализацией и UX/Figma этапами. +- Если вопрос блокирует `ux-construction`, `screen-blueprints` или `figma-build`, пайплайн останавливается. +- Ответ пользователя записывается в `decision_log.md`, а вопрос получает `status: "answered"` или `status: "resolved"`. +- Неблокирующие вопросы можно пройти по `default_assumption`, но assumption нужно записать в `decision_log.md`. + +## Правила Figma результата + +- Контент бывает только `screen` и `element`. +- `screen`: ширина `1440`, высота минимум `800`; высота растет под контент. +- `element`: свободный размер в рамках реального размера элемента на экране. +- Auto Layout, 8px сетка, целые размеры/отступы, кратность 4 или 8. +- Каждый экран и его состояния/элементы лежат в отдельной Figma Section. +- Product screen frame содержит только UI конечного продукта. +- Пояснения, requirement trace, UX notes, citations и комментарии выносятся в отдельный `Notes / Annotations` frame в той же Section, но вне screen frame. +- При нескольких ролях/приложениях создается отдельная Figma page для каждой роли с полным набором доступных ей экранов. + +## Команды + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\wireframe\init-artifacts.ps1 +``` + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\system\init-workspace.ps1 +``` + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\wireframe\extract-documents.ps1 -InputPath .\docs-input -OutputDir .\workspace\artifacts\wireframe-gen\extracted +``` + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\wireframe\validate-artifacts.ps1 -ArtifactDir .\workspace\artifacts\wireframe-gen -Stage pre-ux +``` + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\wireframe\validate-artifacts.ps1 -ArtifactDir .\workspace\artifacts\wireframe-gen -Stage pre-figma +``` + +## Hot Update + +В Origin: + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\system\export-update-package.ps1 -OutputDir .\dist\wireframe-system +``` + +В клиентской копии сначала dry-run: + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\system\apply-hot-update.ps1 -PackagePath -TargetRoot -DryRun +``` + +Затем применение: + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\system\apply-hot-update.ps1 -PackagePath -TargetRoot +``` + +Скрипт обновляет только allowlist из manifest, проверяет SHA256 и создает backup в `workspace/.hot-update/backups/`. + +## Багрепорты + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\system\new-bug-report.ps1 -Title "" -Area "<area>" -Expected "<expected>" -Actual "<actual>" +``` + +Отчет создается в `workspace/system-feedback/bug-reports/` как `report.json` и `report.md`. Клиентские документы, имена, Figma-контент и приватные идентификаторы не прикладываются автоматически; вручную можно добавить только очищенные фрагменты в `sanitized-snippets/`. + +## Промты + +### 1. Старт проекта + +```text +Используй $wireframe-coordinator. Запусти пайплайн для нового проекта из <путь>. Сначала нормализуй документацию, затем выведи open_questions в диалог. UX и Figma не запускай, пока блокирующие вопросы не закрыты. +``` + +### 2. Нормализация + +```text +Используй $documentation-normalizer. Проанализируй документы из <путь>. Создай source_inventory, normalized_project и summary. Все противоречия и недостающие решения вынеси в open_questions с blocks/status. +``` + +### 3. Ответы на Open Questions + +```text +Используй $wireframe-coordinator. Вот ответы на open_questions: <ответы>. Обнови normalized_project и decision_log, затем проверь, можно ли продолжить UX construction. +``` + +### 4. UX-конструктор + +```text +Используй $ux-constructor. На основе workspace/artifacts/wireframe-gen/normalized_project.json создай UX architecture, user flows, screen inventory, UX decisions с citations и screen_blueprints.json. +``` + +### 5. Проверка перед Figma + +```text +Проверь workspace/artifacts/wireframe-gen на готовность к Figma generation. Запусти pre-figma gate, найди блокирующие open_questions и провалидируй screen_blueprints. +``` + +### 6. Создание wireframes + +```text +Используй $figma-wireframe-builder. Создай mid-fi wireframes в Figma file <file_key> на основе screen_blueprints.json. Внутри product screen frame размещай только финальный UI; notes/metadata/comments вынеси в Notes / Annotations вне screen frame. +``` + +### 7. Редактирование экрана + +```text +Используй $figma-wireframe-builder. Обнови экран <screen_id>: <описание правки>. Меняй только затронутые screen/element frames; notes меняй отдельно и обнови figma_build_manifest. +``` diff --git a/wireframe-system/package-manifest.json b/wireframe-system/package-manifest.json new file mode 100644 index 0000000..a955945 --- /dev/null +++ b/wireframe-system/package-manifest.json @@ -0,0 +1,224 @@ +{ + "package_name": "wireframe-system", + "version": "0.2.0", + "exported_at": "2026-05-04T12:29:30.5621641Z", + "source_manifest": "wireframe-system.manifest.json", + "default_artifact_dir": "workspace/artifacts/wireframe-gen", + "system_paths": [ + ".agents", + "scripts", + "docs", + "tests", + "AGENTS.md", + "wireframe-system.manifest.json" + ], + "preserve_paths": [ + "workspace", + "artifacts", + "maintenance", + "dist" + ], + "excluded_paths": [ + "workspace", + "artifacts", + "maintenance", + "dist", + ".git", + ".cache", + "node_modules", + "tmp", + "temp" + ], + "files": [ + { + "path": ".agents/skills/_shared/references/alternative-landscape.md", + "sha256": "eb995f8b586db5dff36572cec6b3d35912d4c87e529f262b233c26c5f2cac868", + "length": 1022 + }, + { + "path": ".agents/skills/_shared/references/artifact-contracts.md", + "sha256": "56807e66fc16fb65b532e42b98b8f0deb52b9f03bc556a1eff9b1d812842c433", + "length": 6164 + }, + { + "path": ".agents/skills/_shared/references/figma-mcp-workflow.md", + "sha256": "dcf4c464567b89895fd2755325e3a1fd96332c5c9f4af1bf7053f27d37359e20", + "length": 2332 + }, + { + "path": ".agents/skills/_shared/references/prompt-guidelines.md", + "sha256": "4a22677a932b0ae92006af79a745f679efcb2564ef433ecee02213417dd296c7", + "length": 1377 + }, + { + "path": ".agents/skills/_shared/references/quality-gates.md", + "sha256": "37825cf7d04a1e73c42288bfe2203f08bfe359ae19b7bf49f682033634280510", + "length": 1176 + }, + { + "path": ".agents/skills/_shared/references/ux-research-policy.md", + "sha256": "8d58a540e0d24ae800a810201928bac8e486da54455291cb6e411542e1f9978b", + "length": 1053 + }, + { + "path": ".agents/skills/_shared/references/ux-research-registry.json", + "sha256": "b74fde8953f18e36d78199fb6bd8063837cf30ea851053ea28a4c5f3199cf23a", + "length": 3401 + }, + { + "path": ".agents/skills/documentation-normalizer/agents/openai.yaml", + "sha256": "1b5284eaff24c476086811434c895d87adafda863217582456fb0ea3475fbb59", + "length": 277 + }, + { + "path": ".agents/skills/documentation-normalizer/references/normalization-examples.md", + "sha256": "03777b24a043a303f3d68cf19c8bf94bcaf700419ae87e492932449c8e04e503", + "length": 579 + }, + { + "path": ".agents/skills/documentation-normalizer/SKILL.md", + "sha256": "859004abfa92882f2374691e83c4d1bff02b6aa952536351c4fe38b73e74e560", + "length": 3130 + }, + { + "path": ".agents/skills/figma-wireframe-builder/agents/openai.yaml", + "sha256": "1fe065b44c1cccf102698fba54320692f9de83d7cd5aa1eb9534b4947b145cb3", + "length": 421 + }, + { + "path": ".agents/skills/figma-wireframe-builder/references/mid-fi-wireframe-primitives.md", + "sha256": "d572939e294b449fb004b44d9c410daf514c24227c57dfe10541334e96433a5b", + "length": 787 + }, + { + "path": ".agents/skills/figma-wireframe-builder/SKILL.md", + "sha256": "aa1739c50c9b41af1729611fbef749138abc168f5e2096ffc2413b1e790c60c7", + "length": 7071 + }, + { + "path": ".agents/skills/ux-constructor/agents/openai.yaml", + "sha256": "cb508b480c571db1f71263e5c901492f27c4607185c404126394ed87427c9c86", + "length": 263 + }, + { + "path": ".agents/skills/ux-constructor/references/ux-spec-patterns.md", + "sha256": "66382f773d26c5d09d69e2beaa2bdcb9ca5889cc7f6a2910506565b02014ffb1", + "length": 900 + }, + { + "path": ".agents/skills/ux-constructor/SKILL.md", + "sha256": "18a59edcd19756c4c66c9445d0cdba43566733824ddb64cdb3e13fe8d21bee75", + "length": 3636 + }, + { + "path": ".agents/skills/wireframe-coordinator/agents/openai.yaml", + "sha256": "7122e598c42ea00783086a31a175318a387b639be373064205aabbc983c18799", + "length": 252 + }, + { + "path": ".agents/skills/wireframe-coordinator/references/routing-table.md", + "sha256": "8ff0ab981e2becdac9d65bd8e4e9e0391e483ec6d563f7c345f6fcf7477c6a9d", + "length": 596 + }, + { + "path": ".agents/skills/wireframe-coordinator/SKILL.md", + "sha256": "33e2165548c38d6440099820e61a282a5f676f295b6da50ea5f0f9e187812045", + "length": 3761 + }, + { + "path": "AGENTS.md", + "sha256": "bd4e580583af04555b80cbb38ded0844defe13380e27ad5edde04af623627313", + "length": 2483 + }, + { + "path": "docs/usage-guide.ru.md", + "sha256": "2b93caeaa8c7beaf6654646e31525c01f7ada119667a5ecb8163c86fac3f47f3", + "length": 7396 + }, + { + "path": "scripts/system/apply-hot-update.ps1", + "sha256": "73a5d46d6ba0984589345bc682e25761c51b96ceb0506a8aeaf140dee2e00426", + "length": 5822 + }, + { + "path": "scripts/system/export-update-package.ps1", + "sha256": "f00146d18ef2b2289b0d317416968856e624e5277ea70ae9905ba1d4f0e6b958", + "length": 4680 + }, + { + "path": "scripts/system/init-workspace.ps1", + "sha256": "78ef3c1f6eabf6e36696cf829f10742f1f884d6de4ec62bfa800c8f58ee10fdd", + "length": 2253 + }, + { + "path": "scripts/system/new-bug-report.ps1", + "sha256": "943f9a22f975c6bc116f435cd74998b54c484c3d026574ac5b8c9279471bafc0", + "length": 4225 + }, + { + "path": "scripts/wireframe/extract-documents.ps1", + "sha256": "7abcdf9e3c7ed73aa80d7efd07edc6334775acdce83e0b8ee262e035e1f0b03b", + "length": 4666 + }, + { + "path": "scripts/wireframe/init-artifacts.ps1", + "sha256": "8c938ccd7727e7d6b781bb04e3d38cb50a0b839b793b1ba385c504b16c4601de", + "length": 1850 + }, + { + "path": "scripts/wireframe/update-research-registry.ps1", + "sha256": "b73440c4332d26a473fd2f1c156ce9677674c86d1523fd3abeced1762d468170", + "length": 1355 + }, + { + "path": "scripts/wireframe/validate-artifacts.ps1", + "sha256": "286d1d4a56a5ff844b24ecb79c82b444fcf2ad79f1b5fd9d3f84e9fa8bc02c05", + "length": 10193 + }, + { + "path": "tests/fixtures/artifacts/figma_build_manifest.json", + "sha256": "8c389059c461b14e53a38ab8e088d01e7d181e95d12cb781098a6f5c256b1a82", + "length": 555 + }, + { + "path": "tests/fixtures/artifacts/normalized_project.json", + "sha256": "c4ebc65692d42cbc4ca008f6fb75ceb0f2fa4c023487642196622039ae9ee6d2", + "length": 2241 + }, + { + "path": "tests/fixtures/artifacts/screen_blueprints.json", + "sha256": "049217303dbe82395685a874becb53eedf706a717695bcad7397aa86e40fc2e3", + "length": 1325 + }, + { + "path": "tests/fixtures/artifacts/source_inventory.json", + "sha256": "ec5d554e41c4acb9352deb110ea30569d8ebb846b2b14e31d6d2f09108d6a4d0", + "length": 405 + }, + { + "path": "tests/fixtures/artifacts/ux_spec.json", + "sha256": "9ef0bd2d36fef0dd81896b0381a8ed9e429c71756f72d7f5370c2cdf764bea4b", + "length": 1582 + }, + { + "path": "tests/fixtures/docs/product.md", + "sha256": "ac6158928d29e73661b88b2065f96f762744d3cee1d1cea56ecc86354dc0e3ed", + "length": 605 + }, + { + "path": "tests/fixtures/docs/requirements.txt", + "sha256": "eedf1eb7457b5ef952c124418ee55388f87bd1fac3109c0903610a52d5de3979", + "length": 281 + }, + { + "path": "tests/Run-WireframeChecks.ps1", + "sha256": "8dea3a4c80e10c5c14d4945c1b906421161d9f9ae6146e39c01d44598e25b466", + "length": 12871 + }, + { + "path": "wireframe-system.manifest.json", + "sha256": "0556b9fa3568b83b727ddda023ca4f5727c43335618e73fce8c2b6819df56417", + "length": 511 + } + ] +} diff --git a/wireframe-system/scripts/system/apply-hot-update.ps1 b/wireframe-system/scripts/system/apply-hot-update.ps1 new file mode 100644 index 0000000..470bc59 --- /dev/null +++ b/wireframe-system/scripts/system/apply-hot-update.ps1 @@ -0,0 +1,178 @@ +param( + [Parameter(Mandatory = $true)] + [string]$PackagePath, + + [Parameter(Mandatory = $true)] + [string]$TargetRoot, + + [Parameter(Mandatory = $false)] + [switch]$DryRun +) + +$ErrorActionPreference = "Stop" + +function Normalize-RelativePath { + param([string]$Path) + $value = $Path -replace "\\", "/" + $value = $value.Trim() + while ($value.StartsWith("./")) { + $value = $value.Substring(2) + } + $value = $value.TrimStart("/") + return $value.TrimEnd("/") +} + +function Test-PathMatchesPrefix { + param( + [string]$RelativePath, + [string]$Prefix + ) + + $pathValue = Normalize-RelativePath -Path $RelativePath + $prefixValue = Normalize-RelativePath -Path $Prefix + if ([string]::IsNullOrWhiteSpace($prefixValue)) { return $false } + return $pathValue -eq $prefixValue -or $pathValue.StartsWith("$prefixValue/") +} + +function Test-IsAllowed { + param( + [string]$RelativePath, + [string[]]$AllowedPaths + ) + + foreach ($allowed in $AllowedPaths) { + if (Test-PathMatchesPrefix -RelativePath $RelativePath -Prefix $allowed) { + return $true + } + } + return $false +} + +function Test-IsBlocked { + param( + [string]$RelativePath, + [string[]]$BlockedPaths + ) + + foreach ($blocked in $BlockedPaths) { + if (Test-PathMatchesPrefix -RelativePath $RelativePath -Prefix $blocked) { + return $true + } + } + return $false +} + +function New-Directory { + param([string]$Path) + if (-not (Test-Path -LiteralPath $Path)) { + New-Item -ItemType Directory -Force -Path $Path | Out-Null + } +} + +function Get-TargetFileHash { + param([string]$Path) + if (-not (Test-Path -LiteralPath $Path)) { return "" } + return (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant() +} + +$resolvedPackage = Resolve-Path -LiteralPath $PackagePath +$packageItem = Get-Item -LiteralPath $resolvedPackage.Path +$packageRoot = if ($packageItem.PSIsContainer) { $packageItem.FullName } else { Split-Path -Parent $packageItem.FullName } +$packageManifestPath = if ($packageItem.PSIsContainer) { Join-Path $packageRoot "package-manifest.json" } else { $packageItem.FullName } + +if (-not (Test-Path -LiteralPath $packageManifestPath)) { + throw "Package manifest not found: $packageManifestPath" +} +if (-not (Test-Path -LiteralPath $TargetRoot)) { + throw "Target root not found: $TargetRoot" +} + +$targetRootPath = (Resolve-Path -LiteralPath $TargetRoot).Path +$packageManifest = Get-Content -LiteralPath $packageManifestPath -Raw -Encoding UTF8 | ConvertFrom-Json +$allowedPaths = @($packageManifest.system_paths | ForEach-Object { [string]$_ }) +$blockedPaths = @($packageManifest.excluded_paths + $packageManifest.preserve_paths | ForEach-Object { [string]$_ }) + +$operations = New-Object System.Collections.Generic.List[object] + +foreach ($file in @($packageManifest.files)) { + $relative = Normalize-RelativePath -Path ([string]$file.path) + if (-not (Test-IsAllowed -RelativePath $relative -AllowedPaths $allowedPaths)) { + throw "Package file is outside system allowlist: $relative" + } + if (Test-IsBlocked -RelativePath $relative -BlockedPaths $blockedPaths) { + throw "Package file targets a preserved/excluded path: $relative" + } + + $source = Join-Path $packageRoot ($relative -replace "/", "\") + if (-not (Test-Path -LiteralPath $source)) { + throw "Package file is missing: $relative" + } + + $sourceHash = (Get-FileHash -LiteralPath $source -Algorithm SHA256).Hash.ToLowerInvariant() + if ($sourceHash -ne ([string]$file.sha256).ToLowerInvariant()) { + throw "Checksum mismatch for package file: $relative" + } + + $target = Join-Path $targetRootPath ($relative -replace "/", "\") + $targetHash = Get-TargetFileHash -Path $target + if ($targetHash -eq $sourceHash) { + continue + } + + $action = if (Test-Path -LiteralPath $target) { "update" } else { "create" } + $operations.Add([ordered]@{ + action = $action + path = $relative + source = $source + target = $target + sha256 = $sourceHash + }) +} + +if ($DryRun) { + Write-Output "Dry run: $($operations.Count) file operation(s) would be applied." + foreach ($operation in $operations) { + Write-Output "$($operation.action): $($operation.path)" + } + return +} + +$timestamp = (Get-Date).ToUniversalTime().ToString("yyyyMMdd-HHmmss-fff") +$hotUpdateDir = Join-Path $targetRootPath "workspace/.hot-update" +$backupRoot = Join-Path $hotUpdateDir "backups/$timestamp" +New-Directory -Path $backupRoot + +$backupEntries = New-Object System.Collections.Generic.List[object] +foreach ($operation in $operations) { + if ($operation.action -eq "update") { + $backupPath = Join-Path $backupRoot ($operation.path -replace "/", "\") + New-Directory -Path (Split-Path -Parent $backupPath) + Copy-Item -LiteralPath $operation.target -Destination $backupPath -Force + $backupEntries.Add([ordered]@{ + path = $operation.path + backup_path = ("workspace/.hot-update/backups/$timestamp/" + $operation.path) + original_sha256 = (Get-FileHash -LiteralPath $backupPath -Algorithm SHA256).Hash.ToLowerInvariant() + }) + } + + New-Directory -Path (Split-Path -Parent $operation.target) + Copy-Item -LiteralPath $operation.source -Destination $operation.target -Force +} + +$applyLog = [ordered]@{ + applied_at = (Get-Date).ToUniversalTime().ToString("o") + package_name = [string]$packageManifest.package_name + version = [string]$packageManifest.version + dry_run = $false + operations = $operations + backups = $backupEntries +} + +$backupManifestPath = Join-Path $backupRoot "backup-manifest.json" +$applyLog | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $backupManifestPath -Encoding UTF8 + +$applyLogPath = Join-Path $hotUpdateDir "last-apply.json" +$applyLog | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $applyLogPath -Encoding UTF8 + +Write-Output "Applied $($operations.Count) file operation(s)." +Write-Output "Backup: $backupRoot" diff --git a/wireframe-system/scripts/system/export-update-package.ps1 b/wireframe-system/scripts/system/export-update-package.ps1 new file mode 100644 index 0000000..c216f89 --- /dev/null +++ b/wireframe-system/scripts/system/export-update-package.ps1 @@ -0,0 +1,146 @@ +param( + [Parameter(Mandatory = $false)] + [string]$OutputDir = "dist/wireframe-system", + + [Parameter(Mandatory = $false)] + [string]$ManifestPath = "wireframe-system.manifest.json" +) + +$ErrorActionPreference = "Stop" + +function Get-RepoRoot { + return (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) +} + +function Normalize-RelativePath { + param([string]$Path) + $value = $Path -replace "\\", "/" + $value = $value.Trim() + while ($value.StartsWith("./")) { + $value = $value.Substring(2) + } + $value = $value.TrimStart("/") + return $value.TrimEnd("/") +} + +function Test-PathMatchesPrefix { + param( + [string]$RelativePath, + [string]$Prefix + ) + + $pathValue = Normalize-RelativePath -Path $RelativePath + $prefixValue = Normalize-RelativePath -Path $Prefix + if ([string]::IsNullOrWhiteSpace($prefixValue)) { return $false } + return $pathValue -eq $prefixValue -or $pathValue.StartsWith("$prefixValue/") +} + +function Test-IsExcluded { + param( + [string]$RelativePath, + [string[]]$ExcludedPaths + ) + + foreach ($excluded in $ExcludedPaths) { + if (Test-PathMatchesPrefix -RelativePath $RelativePath -Prefix $excluded) { + return $true + } + } + return $false +} + +function Get-RelativePath { + param( + [string]$Root, + [string]$Path + ) + + $rootPath = (Resolve-Path -LiteralPath $Root).Path + if (-not $rootPath.EndsWith([System.IO.Path]::DirectorySeparatorChar)) { + $rootPath += [System.IO.Path]::DirectorySeparatorChar + } + + $pathValue = (Resolve-Path -LiteralPath $Path).Path + $rootUri = New-Object System.Uri($rootPath) + $pathUri = New-Object System.Uri($pathValue) + $relativeUri = $rootUri.MakeRelativeUri($pathUri) + return [System.Uri]::UnescapeDataString($relativeUri.ToString()).Replace("/", "\") +} + +function New-Directory { + param([string]$Path) + if (-not (Test-Path -LiteralPath $Path)) { + New-Item -ItemType Directory -Force -Path $Path | Out-Null + } +} + +$root = Get-RepoRoot +$manifestFullPath = Join-Path $root $ManifestPath +if (-not (Test-Path -LiteralPath $manifestFullPath)) { + throw "System manifest not found: $manifestFullPath" +} + +$manifest = Get-Content -LiteralPath $manifestFullPath -Raw -Encoding UTF8 | ConvertFrom-Json +$packageName = [string]$manifest.package_name +$version = [string]$manifest.version +if ([string]::IsNullOrWhiteSpace($packageName) -or [string]::IsNullOrWhiteSpace($version)) { + throw "System manifest must include package_name and version." +} + +$excludedPaths = @($manifest.excluded_paths | ForEach-Object { [string]$_ }) +$outputRoot = if ([System.IO.Path]::IsPathRooted($OutputDir)) { $OutputDir } else { Join-Path $root $OutputDir } +New-Directory -Path $outputRoot + +$timestamp = (Get-Date).ToUniversalTime().ToString("yyyyMMdd-HHmmss-fff") +$packageRoot = Join-Path $outputRoot "$packageName-$version-$timestamp" +New-Directory -Path $packageRoot + +foreach ($relativePath in @($manifest.system_paths)) { + $relative = Normalize-RelativePath -Path ([string]$relativePath) + if (Test-IsExcluded -RelativePath $relative -ExcludedPaths $excludedPaths) { + throw "System path is excluded and cannot be packaged: $relative" + } + + $source = Join-Path $root $relative + if (-not (Test-Path -LiteralPath $source)) { + throw "System path listed in manifest does not exist: $relative" + } + + $destination = Join-Path $packageRoot $relative + $destinationParent = Split-Path -Parent $destination + if (-not [string]::IsNullOrWhiteSpace($destinationParent)) { + New-Directory -Path $destinationParent + } + Copy-Item -LiteralPath $source -Destination $destination -Recurse -Force +} + +$files = New-Object System.Collections.Generic.List[object] +Get-ChildItem -LiteralPath $packageRoot -Recurse -File -Force | + Sort-Object FullName | + ForEach-Object { + $relative = (Get-RelativePath -Root $packageRoot -Path $_.FullName) -replace "\\", "/" + $hash = Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256 + $files.Add([ordered]@{ + path = $relative + sha256 = $hash.Hash.ToLowerInvariant() + length = $_.Length + }) + } + +$packageManifest = [ordered]@{ + package_name = $packageName + version = $version + exported_at = (Get-Date).ToUniversalTime().ToString("o") + source_manifest = $ManifestPath + default_artifact_dir = [string]$manifest.default_artifact_dir + system_paths = @($manifest.system_paths) + preserve_paths = @($manifest.preserve_paths) + excluded_paths = @($manifest.excluded_paths) + files = $files +} + +$packageManifestPath = Join-Path $packageRoot "package-manifest.json" +$packageManifest | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $packageManifestPath -Encoding UTF8 + +Write-Output "Exported $($files.Count) system file(s)." +Write-Output $packageRoot diff --git a/wireframe-system/scripts/system/init-workspace.ps1 b/wireframe-system/scripts/system/init-workspace.ps1 new file mode 100644 index 0000000..f9d3692 --- /dev/null +++ b/wireframe-system/scripts/system/init-workspace.ps1 @@ -0,0 +1,70 @@ +param( + [Parameter(Mandatory = $false)] + [string]$WorkspaceDir = "workspace", + + [Parameter(Mandatory = $false)] + [string]$ArtifactDir = "workspace/artifacts/wireframe-gen", + + [Parameter(Mandatory = $false)] + [string]$LegacyArtifactDir = "artifacts/wireframe-gen" +) + +$ErrorActionPreference = "Stop" + +function Get-RepoRoot { + return (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) +} + +function Resolve-RepoPath { + param( + [string]$Root, + [string]$Path + ) + if ([System.IO.Path]::IsPathRooted($Path)) { return $Path } + return Join-Path $Root $Path +} + +function New-Directory { + param([string]$Path) + if (-not (Test-Path -LiteralPath $Path)) { + New-Item -ItemType Directory -Force -Path $Path | Out-Null + } +} + +function Test-DirectoryHasFiles { + param([string]$Path) + if (-not (Test-Path -LiteralPath $Path)) { return $false } + return $null -ne (Get-ChildItem -LiteralPath $Path -Force -Recurse -File | Select-Object -First 1) +} + +$root = Get-RepoRoot +$workspacePath = Resolve-RepoPath -Root $root -Path $WorkspaceDir +$artifactPath = Resolve-RepoPath -Root $root -Path $ArtifactDir +$legacyArtifactPath = Resolve-RepoPath -Root $root -Path $LegacyArtifactDir + +New-Directory -Path $workspacePath +New-Directory -Path $artifactPath +New-Directory -Path (Join-Path $workspacePath "system-feedback/bug-reports") +New-Directory -Path (Join-Path $workspacePath ".hot-update/backups") + +$targetHadFiles = Test-DirectoryHasFiles -Path $artifactPath +$legacyExists = Test-Path -LiteralPath $legacyArtifactPath +$copiedLegacy = $false + +if ($legacyExists -and -not $targetHadFiles) { + Get-ChildItem -LiteralPath $legacyArtifactPath -Force | ForEach-Object { + Copy-Item -LiteralPath $_.FullName -Destination $artifactPath -Recurse -Force + } + $copiedLegacy = $true +} + +if (-not (Test-DirectoryHasFiles -Path $artifactPath)) { + $initArtifactsScript = Join-Path $root "scripts/wireframe/init-artifacts.ps1" + powershell -NoProfile -ExecutionPolicy Bypass -File $initArtifactsScript -ArtifactDir $artifactPath | Out-Null +} + +Write-Output "Workspace initialized: $workspacePath" +Write-Output "Artifact dir: $artifactPath" +if ($copiedLegacy) { + Write-Output "Copied legacy artifacts from $legacyArtifactPath. Legacy artifacts were not deleted." +} diff --git a/wireframe-system/scripts/system/new-bug-report.ps1 b/wireframe-system/scripts/system/new-bug-report.ps1 new file mode 100644 index 0000000..eedbe20 --- /dev/null +++ b/wireframe-system/scripts/system/new-bug-report.ps1 @@ -0,0 +1,162 @@ +param( + [Parameter(Mandatory = $true)] + [string]$Title, + + [Parameter(Mandatory = $true)] + [string]$Area, + + [Parameter(Mandatory = $true)] + [string]$Expected, + + [Parameter(Mandatory = $true)] + [string]$Actual, + + [Parameter(Mandatory = $false)] + [ValidateSet("low", "medium", "high", "critical")] + [string]$Severity = "medium", + + [Parameter(Mandatory = $false)] + [string]$TriggerContext = "Not specified", + + [Parameter(Mandatory = $false)] + [string[]]$ReproSteps = @(), + + [Parameter(Mandatory = $false)] + [string]$Impact = "", + + [Parameter(Mandatory = $false)] + [string]$Workaround = "", + + [Parameter(Mandatory = $false)] + [string]$DepersonalizationNotes = "No client names, copied client documents, Figma content, credentials, or private identifiers are included.", + + [Parameter(Mandatory = $false)] + [switch]$PrivacyConfirmed, + + [Parameter(Mandatory = $false)] + [string]$WorkspaceDir = "workspace", + + [Parameter(Mandatory = $false)] + [string]$SystemManifestPath = "wireframe-system.manifest.json" +) + +$ErrorActionPreference = "Stop" + +function Get-RepoRoot { + return (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) +} + +function Resolve-RepoPath { + param( + [string]$Root, + [string]$Path + ) + if ([System.IO.Path]::IsPathRooted($Path)) { return $Path } + return Join-Path $Root $Path +} + +function New-Slug { + param([string]$Value) + $slug = $Value.ToLowerInvariant() -replace "[^a-z0-9]+", "-" + $slug = $slug.Trim("-") + if ([string]::IsNullOrWhiteSpace($slug)) { return "bug-report" } + if ($slug.Length -gt 48) { return $slug.Substring(0, 48).Trim("-") } + return $slug +} + +function New-Directory { + param([string]$Path) + if (-not (Test-Path -LiteralPath $Path)) { + New-Item -ItemType Directory -Force -Path $Path | Out-Null + } +} + +$root = Get-RepoRoot +$workspacePath = Resolve-RepoPath -Root $root -Path $WorkspaceDir +$reportRoot = Join-Path $workspacePath "system-feedback/bug-reports" +New-Directory -Path $reportRoot + +$systemVersion = "unknown" +$manifestPath = Resolve-RepoPath -Root $root -Path $SystemManifestPath +if (Test-Path -LiteralPath $manifestPath) { + $manifest = Get-Content -LiteralPath $manifestPath -Raw -Encoding UTF8 | ConvertFrom-Json + if ($manifest.PSObject.Properties.Name -contains "version") { + $systemVersion = [string]$manifest.version + } +} + +$createdAt = (Get-Date).ToUniversalTime() +$id = "BUG-" + $createdAt.ToString("yyyyMMdd-HHmmss-fff") +$reportDir = Join-Path $reportRoot ($id + "-" + (New-Slug -Value $Title)) +New-Directory -Path $reportDir +New-Directory -Path (Join-Path $reportDir "sanitized-snippets") + +$report = [ordered]@{ + id = $id + title = $Title + created_at = $createdAt.ToString("o") + system_version = $systemVersion + area = $Area + severity = $Severity + trigger_context = $TriggerContext + actual_behavior = $Actual + expected_behavior = $Expected + repro_steps = @($ReproSteps) + impact = $Impact + workaround = $Workaround + depersonalization_notes = $DepersonalizationNotes + privacy_confirmed = [bool]$PrivacyConfirmed +} + +$jsonPath = Join-Path $reportDir "report.json" +$report | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $jsonPath -Encoding UTF8 + +$stepsText = if (@($ReproSteps).Count -gt 0) { + (@($ReproSteps) | ForEach-Object { "- $_" }) -join "`n" +} +else { + "- Not provided" +} + +$markdown = @" +# $id - $Title + +- Created at: $($report.created_at) +- System version: $systemVersion +- Area: $Area +- Severity: $Severity +- Privacy confirmed: $([bool]$PrivacyConfirmed) + +## Trigger Context +$TriggerContext + +## Actual Behavior +$Actual + +## Expected Behavior +$Expected + +## Reproduction Steps +$stepsText + +## Impact +$Impact + +## Workaround +$Workaround + +## Depersonalization Notes +$DepersonalizationNotes + +## Sanitized Attachments +Place only manually cleaned snippets in sanitized-snippets/. Do not attach source client documents, copied Figma content, credentials, private names, or identifiers. +"@ + +$markdownPath = Join-Path $reportDir "report.md" +Set-Content -LiteralPath $markdownPath -Value $markdown -Encoding UTF8 + +Set-Content -LiteralPath (Join-Path $reportDir "sanitized-snippets/.gitkeep") -Value "" -Encoding UTF8 + +Write-Output "Bug report created: $reportDir" +Write-Output $jsonPath +Write-Output $markdownPath diff --git a/wireframe-system/scripts/wireframe/extract-documents.ps1 b/wireframe-system/scripts/wireframe/extract-documents.ps1 new file mode 100644 index 0000000..78db8b8 --- /dev/null +++ b/wireframe-system/scripts/wireframe/extract-documents.ps1 @@ -0,0 +1,154 @@ +param( + [Parameter(Mandatory = $false)] + [string]$InputPath = ".", + + [Parameter(Mandatory = $false)] + [string]$OutputDir = "workspace/artifacts/wireframe-gen/extracted" +) + +$ErrorActionPreference = "Stop" + +function New-Slug { + param([string]$Value) + $slug = [System.IO.Path]::GetFileNameWithoutExtension($Value).ToLowerInvariant() + $slug = $slug -replace "[^a-z0-9]+", "-" + $slug = $slug.Trim("-") + if ([string]::IsNullOrWhiteSpace($slug)) { return "source" } + return $slug +} + +function Get-TextFileContent { + param([string]$Path) + return Get-Content -LiteralPath $Path -Raw -Encoding UTF8 +} + +function Get-DocxText { + param([string]$Path) + Add-Type -AssemblyName System.IO.Compression.FileSystem + $zip = [System.IO.Compression.ZipFile]::OpenRead((Resolve-Path -LiteralPath $Path)) + try { + $parts = $zip.Entries | Where-Object { + $_.FullName -eq "word/document.xml" -or + $_.FullName -like "word/header*.xml" -or + $_.FullName -like "word/footer*.xml" + } + $texts = New-Object System.Collections.Generic.List[string] + foreach ($part in $parts) { + $reader = New-Object System.IO.StreamReader($part.Open()) + try { + $xml = $reader.ReadToEnd() + $xml = $xml -replace "</w:p>", "`n" + $matches = [regex]::Matches($xml, "<w:t[^>]*>(.*?)</w:t>") + foreach ($match in $matches) { + $texts.Add([System.Net.WebUtility]::HtmlDecode($match.Groups[1].Value)) + } + $texts.Add("`n") + } + finally { + $reader.Dispose() + } + } + return (($texts -join "") -replace "`r", "" -replace "`n{3,}", "`n`n").Trim() + } + finally { + $zip.Dispose() + } +} + +function Get-PdfText { + param([string]$Path) + $tool = Get-Command pdftotext -ErrorAction SilentlyContinue + if ($null -eq $tool) { + return @{ + Text = "" + Note = "PDF extraction requires pdftotext in PATH. No OCR fallback is used in v1." + Status = "needs_external_extractor" + } + } + + $tempFile = Join-Path ([System.IO.Path]::GetTempPath()) ("wireframe-pdf-" + [guid]::NewGuid().ToString() + ".txt") + & $tool.Source "-layout" $Path $tempFile | Out-Null + $text = Get-Content -LiteralPath $tempFile -Raw -Encoding UTF8 + Remove-Item -LiteralPath $tempFile -Force + return @{ + Text = $text + Note = "Extracted with pdftotext." + Status = "ok" + } +} + +function Get-SourceFiles { + param([string]$Path) + $resolved = Resolve-Path -LiteralPath $Path + $item = Get-Item -LiteralPath $resolved + $extensions = @(".md", ".markdown", ".txt", ".pdf", ".docx") + if ($item.PSIsContainer) { + return Get-ChildItem -LiteralPath $item.FullName -Recurse -File | + Where-Object { $extensions -contains $_.Extension.ToLowerInvariant() } | + Sort-Object FullName + } + if ($extensions -contains $item.Extension.ToLowerInvariant()) { + return @($item) + } + throw "Unsupported input file type: $($item.Extension)" +} + +New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null +$files = Get-SourceFiles -Path $InputPath +$sources = New-Object System.Collections.Generic.List[object] +$index = 1 + +foreach ($file in $files) { + $sourceId = "SRC-{0:D3}" -f $index + $extension = $file.Extension.ToLowerInvariant() + $status = "ok" + $notes = "" + $text = "" + + try { + switch ($extension) { + ".md" { $text = Get-TextFileContent -Path $file.FullName } + ".markdown" { $text = Get-TextFileContent -Path $file.FullName } + ".txt" { $text = Get-TextFileContent -Path $file.FullName } + ".docx" { $text = Get-DocxText -Path $file.FullName } + ".pdf" { + $pdf = Get-PdfText -Path $file.FullName + $text = $pdf.Text + $notes = $pdf.Note + $status = $pdf.Status + } + } + } + catch { + $status = "error" + $notes = $_.Exception.Message + } + + $slug = New-Slug -Value $file.Name + $outName = "$sourceId-$slug.txt" + $outPath = Join-Path $OutputDir $outName + Set-Content -LiteralPath $outPath -Value $text -Encoding UTF8 + + $sources.Add([ordered]@{ + source_id = $sourceId + path = $file.FullName + type = $extension.TrimStart(".") + status = $status + extracted_text_path = $outPath + character_count = $text.Length + notes = $notes + }) + $index += 1 +} + +$inventory = [ordered]@{ + generated_at = (Get-Date).ToUniversalTime().ToString("o") + input_path = (Resolve-Path -LiteralPath $InputPath).Path + output_dir = (Resolve-Path -LiteralPath $OutputDir).Path + sources = $sources +} + +$inventoryPath = Join-Path $OutputDir "source_inventory.json" +$inventory | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $inventoryPath -Encoding UTF8 +Write-Output "Extracted $($sources.Count) source(s) to $OutputDir" +Write-Output $inventoryPath diff --git a/wireframe-system/scripts/wireframe/init-artifacts.ps1 b/wireframe-system/scripts/wireframe/init-artifacts.ps1 new file mode 100644 index 0000000..c39e6a0 --- /dev/null +++ b/wireframe-system/scripts/wireframe/init-artifacts.ps1 @@ -0,0 +1,77 @@ +param( + [Parameter(Mandatory = $false)] + [string]$ArtifactDir = "workspace/artifacts/wireframe-gen" +) + +$ErrorActionPreference = "Stop" +New-Item -ItemType Directory -Force -Path $ArtifactDir | Out-Null + +function Write-IfMissing { + param( + [string]$Path, + [string]$Value + ) + if (-not (Test-Path -LiteralPath $Path)) { + Set-Content -LiteralPath $Path -Value $Value -Encoding UTF8 + } +} + +Write-IfMissing (Join-Path $ArtifactDir "source_inventory.json") @' +{ + "generated_at": "", + "input_path": "", + "output_dir": "", + "sources": [] +} +'@ + +Write-IfMissing (Join-Path $ArtifactDir "normalized_project.json") @' +{ + "project": {}, + "audiences": [], + "goals": [], + "actors": [], + "functional_modules": [], + "entities": [], + "rules": [], + "constraints": [], + "risks": [], + "open_questions": [], + "source_trace": [] +} +'@ + +Write-IfMissing (Join-Path $ArtifactDir "ux_spec.json") @' +{ + "information_architecture": [], + "user_flows": [], + "screen_inventory": [], + "screen_purposes": [], + "ux_decisions": [], + "research_citations": [], + "acceptance_criteria": [] +} +'@ + +Write-IfMissing (Join-Path $ArtifactDir "screen_blueprints.json") "[]" + +Write-IfMissing (Join-Path $ArtifactDir "figma_build_manifest.json") @' +{ + "file_key": "", + "page": "", + "screen_ids": [], + "created_node_ids": [], + "mutated_node_ids": [], + "annotation_node_ids": [], + "screenshots": [], + "validation_notes": [], + "known_issues": [] +} +'@ + +Write-IfMissing (Join-Path $ArtifactDir "normalized_project.summary.md") "# Normalized Project Summary`n" +Write-IfMissing (Join-Path $ArtifactDir "ux_spec.summary.md") "# UX Spec Summary`n" +Write-IfMissing (Join-Path $ArtifactDir "figma_validation.md") "# Figma Validation`n" +Write-IfMissing (Join-Path $ArtifactDir "decision_log.md") "# Decision Log`n" + +Write-Output "Initialized artifacts in $ArtifactDir" diff --git a/wireframe-system/scripts/wireframe/update-research-registry.ps1 b/wireframe-system/scripts/wireframe/update-research-registry.ps1 new file mode 100644 index 0000000..ed0ae57 --- /dev/null +++ b/wireframe-system/scripts/wireframe/update-research-registry.ps1 @@ -0,0 +1,53 @@ +param( + [Parameter(Mandatory = $false)] + [string]$RegistryPath = ".agents/skills/_shared/references/ux-research-registry.json", + + [Parameter(Mandatory = $true)] + [string]$Id, + + [Parameter(Mandatory = $true)] + [string]$Title, + + [Parameter(Mandatory = $true)] + [string]$Url, + + [Parameter(Mandatory = $false)] + [string]$Publisher = "", + + [Parameter(Mandatory = $false)] + [string[]]$Domains = @(), + + [Parameter(Mandatory = $false)] + [string[]]$Claims = @() +) + +$ErrorActionPreference = "Stop" + +if (-not (Test-Path -LiteralPath $RegistryPath)) { + throw "Registry not found: $RegistryPath" +} + +$registry = Get-Content -LiteralPath $RegistryPath -Raw -Encoding UTF8 | ConvertFrom-Json +$sources = @($registry.sources) +$existing = $sources | Where-Object { $_.id -eq $Id } | Select-Object -First 1 + +$entry = [ordered]@{ + id = $Id + title = $Title + url = $Url + publisher = $Publisher + domains = @($Domains) + claims = @($Claims) + last_checked = (Get-Date).ToString("yyyy-MM-dd") +} + +if ($null -ne $existing) { + $sources = $sources | Where-Object { $_.id -ne $Id } +} + +$sources += [pscustomobject]$entry +$registry.last_updated = (Get-Date).ToString("yyyy-MM-dd") +$registry.sources = @($sources | Sort-Object id) + +$registry | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $RegistryPath -Encoding UTF8 +Write-Output "Updated registry entry: $Id" diff --git a/wireframe-system/scripts/wireframe/validate-artifacts.ps1 b/wireframe-system/scripts/wireframe/validate-artifacts.ps1 new file mode 100644 index 0000000..7014eb6 --- /dev/null +++ b/wireframe-system/scripts/wireframe/validate-artifacts.ps1 @@ -0,0 +1,291 @@ +param( + [Parameter(Mandatory = $false)] + [string]$ArtifactDir = "workspace/artifacts/wireframe-gen", + + [Parameter(Mandatory = $false)] + [switch]$Strict, + + [Parameter(Mandatory = $false)] + [ValidateSet("schema", "pre-ux", "pre-figma")] + [string]$Stage = "schema" +) + +$ErrorActionPreference = "Stop" +$errors = New-Object System.Collections.Generic.List[string] + +function Read-Json { + param([string]$Path) + try { + return Get-Content -LiteralPath $Path -Raw -Encoding UTF8 | ConvertFrom-Json + } + catch { + $script:errors.Add("Invalid JSON: $Path - $($_.Exception.Message)") + return $null + } +} + +function Has-Property { + param( + [object]$Object, + [string]$Name + ) + if ($null -eq $Object) { return $false } + return $null -ne ($Object.PSObject.Properties | Where-Object { $_.Name -eq $Name }) +} + +function Test-RequiredFields { + param( + [object]$Object, + [string[]]$Fields, + [string]$Label + ) + foreach ($field in $Fields) { + if (-not (Has-Property -Object $Object -Name $field)) { + $script:errors.Add("$Label missing required field: $field") + } + } +} + +function Test-IsNumeric { + param([object]$Value) + return ( + $Value -is [byte] -or + $Value -is [sbyte] -or + $Value -is [int16] -or + $Value -is [uint16] -or + $Value -is [int32] -or + $Value -is [uint32] -or + $Value -is [int64] -or + $Value -is [uint64] -or + $Value -is [single] -or + $Value -is [double] -or + $Value -is [decimal] + ) +} + +function Test-GridNumber { + param( + [object]$Value, + [string]$Label, + [switch]$Positive + ) + + if (-not (Test-IsNumeric -Value $Value)) { + $script:errors.Add("$Label must be numeric") + return + } + + $number = [double]$Value + if ($Positive -and $number -lt 1) { + $script:errors.Add("$Label must be at least 1") + } + + $rounded = [math]::Round($number) + if ([math]::Abs($number - $rounded) -gt 0.000001) { + $script:errors.Add("$Label must be a whole number") + return + } + + if (([int64]$rounded % 4) -ne 0) { + $script:errors.Add("$Label must be a multiple of 4") + } +} + +function Test-LayoutNumbers { + param( + [object]$Value, + [string]$Path = "screen_blueprints" + ) + + if ($null -eq $Value) { return } + + if ($Value -is [System.Array]) { + for ($i = 0; $i -lt $Value.Count; $i++) { + Test-LayoutNumbers -Value $Value[$i] -Path "$Path[$i]" + } + return + } + + if (Test-IsNumeric -Value $Value) { return } + if ($Value -is [string]) { return } + + $layoutNamePattern = '^(x|y|width|height|minWidth|maxWidth|minHeight|maxHeight|min_width|max_width|min_height|max_height|top|right|bottom|left|padding|paddingTop|paddingRight|paddingBottom|paddingLeft|padding_top|padding_right|padding_bottom|padding_left|margin|marginTop|marginRight|marginBottom|marginLeft|margin_top|margin_right|margin_bottom|margin_left|gap|rowGap|columnGap|row_gap|column_gap|spacing|radius|borderRadius|cornerRadius|border_radius|corner_radius|inset|offset)$' + + foreach ($prop in $Value.PSObject.Properties) { + $propPath = "$Path.$($prop.Name)" + if ((Test-IsNumeric -Value $prop.Value) -and ($prop.Name -match $layoutNamePattern)) { + Test-GridNumber -Value $prop.Value -Label $propPath + } + elseif ($prop.Value -is [System.Array] -or ($null -ne $prop.Value -and -not ($prop.Value -is [string]) -and -not (Test-IsNumeric -Value $prop.Value) -and $prop.Value.PSObject.Properties.Count -gt 0)) { + Test-LayoutNumbers -Value $prop.Value -Path $propPath + } + } +} + +function Get-QuestionStatus { + param([object]$Question) + if (Has-Property -Object $Question -Name "status" -and -not [string]::IsNullOrWhiteSpace([string]$Question.status)) { + return ([string]$Question.status).ToLowerInvariant() + } + return "unresolved" +} + +function Test-QuestionIsResolved { + param([object]$Question) + $status = Get-QuestionStatus -Question $Question + return @("resolved", "answered", "closed") -contains $status +} + +function Get-QuestionBlocks { + param([object]$Question) + if (-not (Has-Property -Object $Question -Name "blocks") -or $null -eq $Question.blocks) { + return @() + } + return @($Question.blocks | ForEach-Object { ([string]$_).ToLowerInvariant() }) +} + +function Test-QuestionBlocksStage { + param( + [object]$Question, + [string]$StageName + ) + + $blocks = Get-QuestionBlocks -Question $Question + if ($blocks.Count -eq 0) { return $false } + + $common = @("all", "pipeline", "project", "workflow") + $preUx = @("ux", "pre-ux", "ux-construction", "ux-constructor", "ux-spec", "screen-blueprints", "screen_blueprints") + $preFigma = @("figma", "pre-figma", "figma-build", "figma-generation", "figma-target", "roles", "role", "screens", "screen", "critical-states", "states", "screen-blueprints", "screen_blueprints") + + foreach ($block in $blocks) { + if ($common -contains $block) { return $true } + if ($StageName -eq "pre-ux" -and $preUx -contains $block) { return $true } + if ($StageName -eq "pre-figma" -and $preFigma -contains $block) { return $true } + } + + return $false +} + +function Test-OpenQuestionGate { + param( + [object]$NormalizedProject, + [string]$StageName + ) + + if ($StageName -eq "schema" -or $null -eq $NormalizedProject -or -not (Has-Property -Object $NormalizedProject -Name "open_questions")) { + return + } + + foreach ($question in @($NormalizedProject.open_questions)) { + if (-not (Test-QuestionBlocksStage -Question $question -StageName $StageName)) { + continue + } + + $id = if (Has-Property -Object $question -Name "id") { [string]$question.id } else { "(missing id)" } + $text = if (Has-Property -Object $question -Name "question") { [string]$question.question } else { "(missing question text)" } + + if (-not (Test-QuestionIsResolved -Question $question)) { + $errors.Add("Open question $id blocks ${StageName}: $text") + continue + } + + if (-not (Has-Property -Object $question -Name "answer") -or [string]::IsNullOrWhiteSpace([string]$question.answer)) { + $errors.Add("Open question $id is resolved for ${StageName} but missing answer") + } + } +} + +$contracts = @{ + "source_inventory.json" = @("generated_at", "input_path", "output_dir", "sources") + "normalized_project.json" = @("project", "audiences", "goals", "actors", "functional_modules", "entities", "rules", "constraints", "risks", "open_questions", "source_trace") + "ux_spec.json" = @("information_architecture", "user_flows", "screen_inventory", "screen_purposes", "ux_decisions", "research_citations", "acceptance_criteria") + "figma_build_manifest.json" = @("file_key", "page", "screen_ids", "created_node_ids", "mutated_node_ids", "screenshots", "validation_notes", "known_issues") +} + +$artifactJsons = @{} +foreach ($fileName in $contracts.Keys) { + $path = Join-Path $ArtifactDir $fileName + if (-not (Test-Path -LiteralPath $path)) { + if ($Strict) { $errors.Add("Missing artifact: $path") } + continue + } + $json = Read-Json -Path $path + $artifactJsons[$fileName] = $json + Test-RequiredFields -Object $json -Fields $contracts[$fileName] -Label $fileName +} + +if ($artifactJsons.ContainsKey("normalized_project.json")) { + Test-OpenQuestionGate -NormalizedProject $artifactJsons["normalized_project.json"] -StageName $Stage +} + +$blueprintsPath = Join-Path $ArtifactDir "screen_blueprints.json" +if (Test-Path -LiteralPath $blueprintsPath) { + $blueprints = Read-Json -Path $blueprintsPath + if ($null -ne $blueprints) { + $items = @($blueprints) + $screenRequired = @("content_type", "screen_id", "viewport", "purpose", "sections", "components", "states", "content_requirements", "interactions", "empty_error_loading_states") + $elementRequired = @("content_type", "element_id", "parent_screen_id", "bounds") + for ($i = 0; $i -lt $items.Count; $i++) { + $item = $items[$i] + $label = "screen_blueprints[$i]" + Test-RequiredFields -Object $item -Fields @("content_type") -Label $label + + if (-not (Has-Property -Object $item -Name "content_type")) { + continue + } + + switch ($item.content_type) { + "screen" { + Test-RequiredFields -Object $item -Fields $screenRequired -Label $label + if (Has-Property -Object $item -Name "viewport") { + Test-RequiredFields -Object $item.viewport -Fields @("width", "height") -Label "$label.viewport" + if (Has-Property -Object $item.viewport -Name "width") { + Test-GridNumber -Value $item.viewport.width -Label "$label.viewport.width" -Positive + if ([double]$item.viewport.width -ne 1440) { + $errors.Add("$label.viewport.width must equal 1440 for content_type screen") + } + } + if (Has-Property -Object $item.viewport -Name "height") { + Test-GridNumber -Value $item.viewport.height -Label "$label.viewport.height" -Positive + if ([double]$item.viewport.height -lt 800) { + $errors.Add("$label.viewport.height must be at least 800 for content_type screen") + } + } + } + } + "element" { + Test-RequiredFields -Object $item -Fields $elementRequired -Label $label + if (Has-Property -Object $item -Name "bounds") { + Test-RequiredFields -Object $item.bounds -Fields @("width", "height") -Label "$label.bounds" + foreach ($field in @("x", "y", "width", "height")) { + if (Has-Property -Object $item.bounds -Name $field) { + $positive = $field -eq "width" -or $field -eq "height" + if ($positive) { + Test-GridNumber -Value $item.bounds.$field -Label "$label.bounds.$field" -Positive + } + else { + Test-GridNumber -Value $item.bounds.$field -Label "$label.bounds.$field" + } + } + } + } + } + default { + $errors.Add("$label.content_type must be either screen or element") + } + } + + Test-LayoutNumbers -Value $item -Path $label + } + } +} +elseif ($Strict) { + $errors.Add("Missing artifact: $blueprintsPath") +} + +if ($errors.Count -gt 0) { + $errors | ForEach-Object { Write-Error $_ } + exit 1 +} + +Write-Output "Artifact validation passed for $ArtifactDir" diff --git a/wireframe-system/tests/Run-WireframeChecks.ps1 b/wireframe-system/tests/Run-WireframeChecks.ps1 new file mode 100644 index 0000000..288a93a --- /dev/null +++ b/wireframe-system/tests/Run-WireframeChecks.ps1 @@ -0,0 +1,288 @@ +$ErrorActionPreference = "Stop" + +$root = Split-Path -Parent $PSScriptRoot +$initArtifactsScript = Join-Path $root "scripts/wireframe/init-artifacts.ps1" +$extractScript = Join-Path $root "scripts/wireframe/extract-documents.ps1" +$validateScript = Join-Path $root "scripts/wireframe/validate-artifacts.ps1" +$initWorkspaceScript = Join-Path $root "scripts/system/init-workspace.ps1" +$exportUpdateScript = Join-Path $root "scripts/system/export-update-package.ps1" +$applyUpdateScript = Join-Path $root "scripts/system/apply-hot-update.ps1" +$newBugReportScript = Join-Path $root "scripts/system/new-bug-report.ps1" +$fixtureDocs = Join-Path $root "tests/fixtures/docs" +$fixtureArtifacts = Join-Path $root "tests/fixtures/artifacts" +$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) ("wireframe-gen-test-" + [guid]::NewGuid().ToString()) + +function Copy-FixtureArtifacts { + param([string]$Destination) + New-Item -ItemType Directory -Force -Path $Destination | Out-Null + Get-ChildItem -LiteralPath $fixtureArtifacts -Force | ForEach-Object { + Copy-Item -LiteralPath $_.FullName -Destination $Destination -Recurse -Force + } +} + +function Write-Blueprints { + param( + [object]$Blueprints, + [string]$ArtifactDir + ) + $path = Join-Path $ArtifactDir "screen_blueprints.json" + $Blueprints | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $path -Encoding UTF8 +} + +function Write-NormalizedProject { + param( + [object]$Project, + [string]$ArtifactDir + ) + $path = Join-Path $ArtifactDir "normalized_project.json" + $Project | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $path -Encoding UTF8 +} + +function Expect-ValidationFailure { + param( + [string]$Name, + [scriptblock]$Mutate, + [string]$Stage = "schema" + ) + $caseDir = Join-Path $tempDir $Name + Copy-FixtureArtifacts -Destination $caseDir + & $Mutate $caseDir + $oldErrorActionPreference = $ErrorActionPreference + $ErrorActionPreference = "Continue" + try { + powershell -NoProfile -ExecutionPolicy Bypass -File $validateScript -ArtifactDir $caseDir -Strict -Stage $Stage *> $null + $exitCode = $LASTEXITCODE + } + finally { + $ErrorActionPreference = $oldErrorActionPreference + } + if ($exitCode -eq 0) { + throw "Expected validation failure for $Name" + } +} + +function Expect-ValidationSuccess { + param( + [string]$Name, + [scriptblock]$Mutate, + [string]$Stage = "schema" + ) + $caseDir = Join-Path $tempDir $Name + Copy-FixtureArtifacts -Destination $caseDir + & $Mutate $caseDir + powershell -NoProfile -ExecutionPolicy Bypass -File $validateScript -ArtifactDir $caseDir -Strict -Stage $Stage | Out-Null +} + +function Assert-PathExists { + param([string]$Path) + if (-not (Test-Path -LiteralPath $Path)) { + throw "Expected path to exist: $Path" + } +} + +function Assert-PathMissing { + param([string]$Path) + if (Test-Path -LiteralPath $Path) { + throw "Expected path to be absent: $Path" + } +} + +function Assert-Equal { + param( + [object]$Actual, + [object]$Expected, + [string]$Message + ) + if ($Actual -ne $Expected) { + throw "$Message Expected '$Expected', got '$Actual'" + } +} + +function Copy-DirectoryContents { + param( + [string]$Source, + [string]$Destination + ) + New-Item -ItemType Directory -Force -Path $Destination | Out-Null + Get-ChildItem -LiteralPath $Source -Force | ForEach-Object { + Copy-Item -LiteralPath $_.FullName -Destination $Destination -Recurse -Force + } +} + +function Assert-PackageHasNoPreservedFiles { + param([object]$PackageManifest) + foreach ($file in @($PackageManifest.files)) { + $path = ([string]$file.path) -replace "\\", "/" + foreach ($blocked in @("workspace", "artifacts", "maintenance", "dist")) { + if ($path -eq $blocked -or $path.StartsWith("$blocked/")) { + throw "Package should not include preserved path: $path" + } + } + } +} + +try { + $defaultCase = Join-Path $tempDir "default-paths" + New-Item -ItemType Directory -Force -Path $defaultCase | Out-Null + Push-Location $defaultCase + try { + powershell -NoProfile -ExecutionPolicy Bypass -File $initArtifactsScript | Out-Null + Assert-PathExists (Join-Path $defaultCase "workspace/artifacts/wireframe-gen/source_inventory.json") + powershell -NoProfile -ExecutionPolicy Bypass -File $validateScript -Strict | Out-Null + } + finally { + Pop-Location + } + + $legacyArtifactDir = Join-Path $tempDir "legacy-artifact-dir" + powershell -NoProfile -ExecutionPolicy Bypass -File $initArtifactsScript -ArtifactDir $legacyArtifactDir | Out-Null + powershell -NoProfile -ExecutionPolicy Bypass -File $validateScript -ArtifactDir $legacyArtifactDir -Strict | Out-Null + + $legacySource = Join-Path $tempDir "legacy-source/artifacts/wireframe-gen" + Copy-FixtureArtifacts -Destination $legacySource + $workspaceRoot = Join-Path $tempDir "client-workspace" + $workspaceArtifacts = Join-Path $workspaceRoot "artifacts/wireframe-gen" + powershell -NoProfile -ExecutionPolicy Bypass -File $initWorkspaceScript -WorkspaceDir $workspaceRoot -ArtifactDir $workspaceArtifacts -LegacyArtifactDir $legacySource | Out-Null + Assert-PathExists (Join-Path $workspaceArtifacts "source_inventory.json") + Assert-PathExists (Join-Path $workspaceRoot "system-feedback/bug-reports") + Assert-PathExists (Join-Path $workspaceRoot ".hot-update/backups") + Assert-PathExists (Join-Path $legacySource "source_inventory.json") + + powershell -NoProfile -ExecutionPolicy Bypass -File $extractScript -InputPath $fixtureDocs -OutputDir $tempDir | Out-Null + $inventoryPath = Join-Path $tempDir "source_inventory.json" + if (-not (Test-Path -LiteralPath $inventoryPath)) { + throw "source_inventory.json was not created" + } + + $inventory = Get-Content -LiteralPath $inventoryPath -Raw -Encoding UTF8 | ConvertFrom-Json + if (@($inventory.sources).Count -lt 2) { + throw "Expected at least 2 extracted fixture sources" + } + + powershell -NoProfile -ExecutionPolicy Bypass -File $validateScript -ArtifactDir $fixtureArtifacts -Strict | Out-Null + + Expect-ValidationFailure "legacy-open-question-blocks-pre-figma" { + param($caseDir) + } -Stage "pre-figma" + + Expect-ValidationSuccess "resolved-open-question-passes-pre-ux" { + param($caseDir) + } -Stage "pre-ux" + + Expect-ValidationFailure "unresolved-open-question-blocks-pre-ux" { + param($caseDir) + $project = Get-Content -LiteralPath (Join-Path $caseDir "normalized_project.json") -Raw -Encoding UTF8 | ConvertFrom-Json + $project.open_questions += [pscustomobject]@{ + id = "Q-003" + question = "Which navigation model should be used?" + status = "unresolved" + blocks = @("ux-construction") + default_assumption = "" + answer = "" + answered_at = "" + source_refs = @() + } + Write-NormalizedProject -Project $project -ArtifactDir $caseDir + } -Stage "pre-ux" + + Expect-ValidationFailure "bad-screen-width" { + param($caseDir) + $blueprints = Get-Content -LiteralPath (Join-Path $caseDir "screen_blueprints.json") -Raw -Encoding UTF8 | ConvertFrom-Json + $blueprints[0].viewport.width = 1436 + Write-Blueprints -Blueprints $blueprints -ArtifactDir $caseDir + } + + Expect-ValidationFailure "bad-screen-height" { + param($caseDir) + $blueprints = Get-Content -LiteralPath (Join-Path $caseDir "screen_blueprints.json") -Raw -Encoding UTF8 | ConvertFrom-Json + $blueprints[0].viewport.height = 796 + Write-Blueprints -Blueprints $blueprints -ArtifactDir $caseDir + } + + Expect-ValidationFailure "missing-element-parent" { + param($caseDir) + $blueprints = Get-Content -LiteralPath (Join-Path $caseDir "screen_blueprints.json") -Raw -Encoding UTF8 | ConvertFrom-Json + $blueprints[1].PSObject.Properties.Remove("parent_screen_id") + Write-Blueprints -Blueprints $blueprints -ArtifactDir $caseDir + } + + Expect-ValidationFailure "fractional-spacing" { + param($caseDir) + $blueprints = Get-Content -LiteralPath (Join-Path $caseDir "screen_blueprints.json") -Raw -Encoding UTF8 | ConvertFrom-Json + $blueprints[0].sections[0].padding = 32.5 + Write-Blueprints -Blueprints $blueprints -ArtifactDir $caseDir + } + + Expect-ValidationFailure "bad-element-size" { + param($caseDir) + $blueprints = Get-Content -LiteralPath (Join-Path $caseDir "screen_blueprints.json") -Raw -Encoding UTF8 | ConvertFrom-Json + $blueprints[1].bounds.width = 13 + Write-Blueprints -Blueprints $blueprints -ArtifactDir $caseDir + } + + $packageOutput = Join-Path $tempDir "packages" + $exportOutput = powershell -NoProfile -ExecutionPolicy Bypass -File $exportUpdateScript -OutputDir $packageOutput + $packagePath = @($exportOutput)[-1] + Assert-PathExists $packagePath + $packageManifestPath = Join-Path $packagePath "package-manifest.json" + Assert-PathExists $packageManifestPath + $packageManifest = Get-Content -LiteralPath $packageManifestPath -Raw -Encoding UTF8 | ConvertFrom-Json + Assert-PackageHasNoPreservedFiles -PackageManifest $packageManifest + + $clientRoot = Join-Path $tempDir "client-copy" + Copy-DirectoryContents -Source $packagePath -Destination $clientRoot + $clientAgents = Join-Path $clientRoot "AGENTS.md" + Set-Content -LiteralPath $clientAgents -Value "old system instructions" -Encoding UTF8 -NoNewline + New-Item -ItemType Directory -Force -Path (Join-Path $clientRoot "workspace/artifacts/wireframe-gen") | Out-Null + Set-Content -LiteralPath (Join-Path $clientRoot "workspace/artifacts/wireframe-gen/client-data.txt") -Value "do not touch" -Encoding UTF8 -NoNewline + New-Item -ItemType Directory -Force -Path (Join-Path $clientRoot "maintenance") | Out-Null + Set-Content -LiteralPath (Join-Path $clientRoot "maintenance/origin-only.txt") -Value "do not touch" -Encoding UTF8 -NoNewline + + powershell -NoProfile -ExecutionPolicy Bypass -File $applyUpdateScript -PackagePath $packagePath -TargetRoot $clientRoot -DryRun | Out-Null + Assert-Equal (Get-Content -LiteralPath $clientAgents -Raw -Encoding UTF8) "old system instructions" "Dry-run must not mutate system files." + Assert-PathMissing (Join-Path $clientRoot "workspace/.hot-update/last-apply.json") + + powershell -NoProfile -ExecutionPolicy Bypass -File $applyUpdateScript -PackagePath $packagePath -TargetRoot $clientRoot | Out-Null + $sourceAgents = Get-Content -LiteralPath (Join-Path $packagePath "AGENTS.md") -Raw -Encoding UTF8 + $updatedAgents = Get-Content -LiteralPath $clientAgents -Raw -Encoding UTF8 + Assert-Equal $updatedAgents $sourceAgents "Hot Update must replace allowlisted system files." + Assert-Equal (Get-Content -LiteralPath (Join-Path $clientRoot "workspace/artifacts/wireframe-gen/client-data.txt") -Raw -Encoding UTF8) "do not touch" "Hot Update must preserve workspace files." + Assert-Equal (Get-Content -LiteralPath (Join-Path $clientRoot "maintenance/origin-only.txt") -Raw -Encoding UTF8) "do not touch" "Hot Update must preserve maintenance files." + Assert-PathExists (Join-Path $clientRoot "workspace/.hot-update/last-apply.json") + $backupAgents = Get-ChildItem -LiteralPath (Join-Path $clientRoot "workspace/.hot-update/backups") -Recurse -File -Filter "AGENTS.md" | Select-Object -First 1 + if ($null -eq $backupAgents) { + throw "Expected Hot Update to create a backup for overwritten AGENTS.md" + } + Assert-Equal (Get-Content -LiteralPath $backupAgents.FullName -Raw -Encoding UTF8) "old system instructions" "Backup should contain the previous file content." + + $bugWorkspace = Join-Path $tempDir "bug-workspace" + powershell -NoProfile -ExecutionPolicy Bypass -File $newBugReportScript ` + -WorkspaceDir $bugWorkspace ` + -Title "Validation does not block bad screen" ` + -Area "validation" ` + -Expected "Validation fails when a screen is underspecified." ` + -Actual "Validation passed." ` + -TriggerContext "During sanitized fixture checks." ` + -ReproSteps "Run validation; observe success" ` + -Impact "Incorrect downstream wireframes" | Out-Null + + $reportJsonPath = Get-ChildItem -LiteralPath (Join-Path $bugWorkspace "system-feedback/bug-reports") -Recurse -File -Filter "report.json" | Select-Object -First 1 + if ($null -eq $reportJsonPath) { + throw "Expected bug report JSON to be created." + } + $report = Get-Content -LiteralPath $reportJsonPath.FullName -Raw -Encoding UTF8 | ConvertFrom-Json + foreach ($field in @("id", "created_at", "system_version", "area", "severity", "trigger_context", "actual_behavior", "expected_behavior", "repro_steps", "impact", "workaround", "depersonalization_notes", "privacy_confirmed")) { + if (-not ($report.PSObject.Properties.Name -contains $field)) { + throw "Bug report missing required field: $field" + } + } + Assert-PathExists (Join-Path $reportJsonPath.Directory.FullName "report.md") + Assert-PathExists (Join-Path $reportJsonPath.Directory.FullName "sanitized-snippets") + + Write-Output "Wireframe checks passed" +} +finally { + if (Test-Path -LiteralPath $tempDir) { + Remove-Item -LiteralPath $tempDir -Recurse -Force + } +} diff --git a/wireframe-system/tests/fixtures/artifacts/figma_build_manifest.json b/wireframe-system/tests/fixtures/artifacts/figma_build_manifest.json new file mode 100644 index 0000000..c24a807 --- /dev/null +++ b/wireframe-system/tests/fixtures/artifacts/figma_build_manifest.json @@ -0,0 +1,23 @@ +{ + "file_key": "test-file-key", + "page": "Wireframes", + "screen_ids": ["screen-project-review"], + "created_node_ids": ["1:2"], + "mutated_node_ids": [], + "annotation_node_ids": ["1:9"], + "screenshots": [ + { + "screen_id": "screen-project-review", + "node_id": "1:2", + "path": "workspace/artifacts/wireframe-gen/screenshots/screen-project-review.png" + } + ], + "validation_notes": [ + { + "screen_id": "screen-project-review", + "status": "pass", + "notes": "Fixture manifest only." + } + ], + "known_issues": [] +} diff --git a/wireframe-system/tests/fixtures/artifacts/normalized_project.json b/wireframe-system/tests/fixtures/artifacts/normalized_project.json new file mode 100644 index 0000000..c623fee --- /dev/null +++ b/wireframe-system/tests/fixtures/artifacts/normalized_project.json @@ -0,0 +1,91 @@ +{ + "project": { + "id": "PRJ-001", + "name": "Wireframe Gen", + "description": "Generate editable Figma wireframes from text documentation." + }, + "audiences": [ + { + "id": "AUD-001", + "name": "Product teams", + "source_refs": ["SRC-001#users"] + } + ], + "goals": [ + { + "id": "GOAL-001", + "statement": "Create traceable wireframes from source documentation.", + "confidence": "high", + "source_refs": ["SRC-001#goals"] + } + ], + "actors": [ + { + "id": "ACT-001", + "name": "Product manager", + "source_refs": ["SRC-001#users"] + } + ], + "functional_modules": [ + { + "id": "MOD-001", + "name": "Documentation intake", + "requirements": ["REQ-001"], + "source_refs": ["SRC-001#core-requirements"] + } + ], + "entities": [ + { + "id": "ENT-001", + "name": "Source document", + "source_refs": ["SRC-001#core-requirements"] + } + ], + "rules": [ + { + "id": "RULE-001", + "statement": "Every requirement must preserve source traceability.", + "confidence": "high", + "source_refs": ["SRC-002#line-1"] + } + ], + "constraints": [ + { + "id": "CON-001", + "statement": "Schema keys stay in English.", + "source_refs": ["SRC-002#line-2"] + } + ], + "risks": [ + { + "id": "RISK-001", + "statement": "Figma target file can be missing.", + "source_refs": ["SRC-002#line-4"] + } + ], + "open_questions": [ + { + "id": "Q-001", + "question": "Which Figma file should receive generated screens?", + "blocks": ["figma-build"], + "source_refs": ["SRC-002#line-4"] + }, + { + "id": "Q-002", + "question": "Which user role should be used for first UX construction?", + "status": "answered", + "blocks": ["ux-construction"], + "default_assumption": "Use the primary product manager role.", + "answer": "Use ACT-001 as the primary role for the first UX pass.", + "answered_at": "2026-05-03T00:00:00Z", + "source_refs": ["SRC-001#users"] + } + ], + "source_trace": [ + { + "source_ref": "SRC-001#goals", + "path": "tests/fixtures/docs/product.md", + "excerpt": "Normalize scattered documentation into one product model." + } + ] +} diff --git a/wireframe-system/tests/fixtures/artifacts/screen_blueprints.json b/wireframe-system/tests/fixtures/artifacts/screen_blueprints.json new file mode 100644 index 0000000..b30d8f4 --- /dev/null +++ b/wireframe-system/tests/fixtures/artifacts/screen_blueprints.json @@ -0,0 +1,41 @@ +[ + { + "content_type": "screen", + "screen_id": "screen-project-review", + "viewport": { + "width": 1440, + "height": 800 + }, + "purpose": "Review normalized requirements and blockers.", + "sections": [ + { + "id": "section-summary", + "name": "Summary", + "requirements": ["RULE-001"], + "padding": 32, + "gap": 24 + } + ], + "components": ["app-shell", "status-list", "open-question-list"], + "states": ["default", "loading", "error", "empty"], + "content_requirements": ["Show source count", "Show unresolved questions"], + "interactions": ["Continue to UX construction"], + "empty_error_loading_states": ["No sources", "Extraction failed", "Validation running"] + }, + { + "content_type": "element", + "element_id": "element-project-review-open-question-card", + "parent_screen_id": "screen-project-review", + "bounds": { + "x": 320, + "y": 240, + "width": 480, + "height": 160 + }, + "purpose": "Show one unresolved question with impact and next action.", + "states": ["default", "resolved"], + "components": ["question-card", "status-badge", "secondary-action"], + "content_requirements": ["Question text", "Blocking area", "Resolution action"], + "interactions": ["Mark resolved", "Open related source"] + } +] diff --git a/wireframe-system/tests/fixtures/artifacts/source_inventory.json b/wireframe-system/tests/fixtures/artifacts/source_inventory.json new file mode 100644 index 0000000..bad8860 --- /dev/null +++ b/wireframe-system/tests/fixtures/artifacts/source_inventory.json @@ -0,0 +1,16 @@ +{ + "generated_at": "2026-05-03T00:00:00Z", + "input_path": "tests/fixtures/docs", + "output_dir": "artifacts/test-extracted", + "sources": [ + { + "source_id": "SRC-001", + "path": "tests/fixtures/docs/product.md", + "type": "md", + "status": "ok", + "extracted_text_path": "artifacts/test-extracted/SRC-001-product.txt", + "character_count": 640, + "notes": "" + } + ] +} diff --git a/wireframe-system/tests/fixtures/artifacts/ux_spec.json b/wireframe-system/tests/fixtures/artifacts/ux_spec.json new file mode 100644 index 0000000..d147fc7 --- /dev/null +++ b/wireframe-system/tests/fixtures/artifacts/ux_spec.json @@ -0,0 +1,56 @@ +{ + "information_architecture": [ + { + "id": "IA-001", + "name": "Project workspace", + "children": ["Documentation", "UX Spec", "Wireframes"] + } + ], + "user_flows": [ + { + "id": "FLOW-001", + "name": "Generate first wireframes", + "actor_id": "ACT-001", + "steps": ["Add docs", "Review normalized project", "Generate UX spec", "Build Figma"], + "success_outcome": "Editable Figma screens are created." + } + ], + "screen_inventory": [ + { + "screen_id": "screen-project-review", + "name": "Project Review", + "requirement_refs": ["RULE-001"] + } + ], + "screen_purposes": [ + { + "screen_id": "screen-project-review", + "purpose": "Let the user review normalized requirements before UX construction." + } + ], + "ux_decisions": [ + { + "id": "UXD-001", + "decision": "Show extraction and validation status before Figma generation.", + "rationale": "Users need feedback about system status.", + "citation_ids": ["nng-heuristics"], + "source_refs": ["RULE-001"] + } + ], + "research_citations": [ + { + "registry_id": "nng-heuristics", + "title": "10 Usability Heuristics for User Interface Design", + "url": "https://www.nngroup.com/articles/ten-usability-heuristics/", + "claim": "Systems should keep users informed about status.", + "used_for": ["UXD-001"] + } + ], + "acceptance_criteria": [ + { + "id": "AC-001", + "screen_id": "screen-project-review", + "criterion": "The screen shows source extraction status and open questions." + } + ] +} diff --git a/wireframe-system/tests/fixtures/docs/product.md b/wireframe-system/tests/fixtures/docs/product.md new file mode 100644 index 0000000..0de2154 --- /dev/null +++ b/wireframe-system/tests/fixtures/docs/product.md @@ -0,0 +1,22 @@ +# Product Brief + +The system helps product teams generate editable Figma wireframes from project documentation. + +## Goals + +- Normalize scattered documentation into one product model. +- Create UX architecture and screen blueprints before visual generation. +- Build mid-fidelity Figma screens through native Figma MCP. + +## Users + +- Product manager +- UX designer +- Solution architect + +## Core Requirements + +- Users can upload or select documentation files. +- Users can review normalized requirements and open questions. +- Users can generate UX screen blueprints. +- Users can create editable Figma wireframes. diff --git a/wireframe-system/tests/fixtures/docs/requirements.txt b/wireframe-system/tests/fixtures/docs/requirements.txt new file mode 100644 index 0000000..aa4d81c --- /dev/null +++ b/wireframe-system/tests/fixtures/docs/requirements.txt @@ -0,0 +1,4 @@ +Requirement: Preserve source traceability from every requirement to the original source document. +Requirement: Keep schema keys in English. +Requirement: Produce Russian human-readable summaries by default. +Risk: Figma target file may be unknown until the user provides a file key. diff --git a/wireframe-system/wireframe-system.manifest.json b/wireframe-system/wireframe-system.manifest.json new file mode 100644 index 0000000..f1e08cf --- /dev/null +++ b/wireframe-system/wireframe-system.manifest.json @@ -0,0 +1,30 @@ +{ + "version": "0.2.0", + "package_name": "wireframe-system", + "default_artifact_dir": "workspace/artifacts/wireframe-gen", + "system_paths": [ + ".agents", + "scripts", + "docs", + "tests", + "AGENTS.md", + "wireframe-system.manifest.json" + ], + "preserve_paths": [ + "workspace", + "artifacts", + "maintenance", + "dist" + ], + "excluded_paths": [ + "workspace", + "artifacts", + "maintenance", + "dist", + ".git", + ".cache", + "node_modules", + "tmp", + "temp" + ] +}