Added to Git

This commit is contained in:
Vadim Flowed
2026-05-10 19:01:57 +03:00
parent d08c2c4d8a
commit 1c7f9161f1
44 changed files with 2902 additions and 0 deletions
Binary file not shown.
@@ -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
}
}
Binary file not shown.
@@ -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": []
}
@@ -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."
}
]
}
@@ -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"]
}
]
@@ -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": ""
}
]
}
+56
View File
@@ -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."
}
]
}
+22
View File
@@ -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.
+4
View File
@@ -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.