Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions scripts/wow-frontend-plan.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#!/usr/bin/env node
// Read-only agent planning helper. No network, writes, copying, package installation or browser QA.
import fs from "node:fs";
import path from "node:path";
import process from "node:process";
import { fileURLToPath } from "node:url";
import { previewWowSources, readWowRegistry } from "../ui-registry/wow-contract.mjs";
import { validateWowReviewPolicy, wowSourceDisposition } from "../ui-registry/wow-security.mjs";

const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const POLICY_PATH = path.join(ROOT, "ui-registry", "wow-review-policy.json");
const record = (x) => x !== null && typeof x === "object" && !Array.isArray(x);
const normalize = (s) => typeof s === "string" ? s.trim() : "";
const nonEmpty = (s) => normalize(s).length > 0;
const ISO_LANG = /^[a-z]{2,3}(-[A-Z]{2})?$/;

export function validateWowBrief(brief) {
const errors = [];
if (!record(brief)) return ["brief must be an object"];
const fields = ["version","project","repository","audience","primaryConversion","artDirection","narrative","constraints","notes"];
for (const field of Object.keys(brief)) if (!fields.includes(field)) errors.push("unknown brief field: " + field);
if (brief.version !== 1) errors.push("brief.version must be 1");
for (const field of ["project","audience","primaryConversion","artDirection"]) {
if (!nonEmpty(brief[field]) || normalize(brief[field]).length > 500) errors.push(field + " must be concise nonempty text");
}
if (typeof brief.repository !== "string" ||
!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(brief.repository)) {
errors.push("repository must use owner/name");
}
if (!Array.isArray(brief.narrative) || brief.narrative.length < 2 || brief.narrative.length > 10 ||
brief.narrative.some((s) => !nonEmpty(s) || s.length > 400)) {
errors.push("narrative must contain 2-10 concise product story steps");
}
if (!record(brief.constraints)) errors.push("constraints must be an object");
else {
const allowed = ["preserveStack","languages","assets","merchants","truthfulProofOnly","reducedMotion","thirdPartyBranding"];
for (const key of Object.keys(brief.constraints)) if (!allowed.includes(key)) errors.push("unknown constraint: " + key);
for (const flag of ["preserveStack","truthfulProofOnly","reducedMotion"]) {
if (brief.constraints[flag] !== true) errors.push(flag + " must be true");
}
const languages = brief.constraints.languages;
if (!Array.isArray(languages) || !languages.length || new Set(languages).size !== languages.length ||
languages.some((s) => typeof s !== "string" || !ISO_LANG.test(s))) {
errors.push("languages must be distinct language tags");
}
if (!["self-hosted","rights-reviewed"].includes(brief.constraints.assets)) errors.push("assets must be self-hosted or rights-reviewed");
if (brief.constraints.merchants !== undefined &&
brief.constraints.merchants !== "fictional-demo-only-unless-approved") {
errors.push("real merchant branding requires explicit approval");
}
if (brief.constraints.thirdPartyBranding !== undefined &&
brief.constraints.thirdPartyBranding !== "explicit-permission") {
errors.push("thirdPartyBranding must require explicit-permission");
}
}
if (brief.notes !== undefined && (typeof brief.notes !== "string" || brief.notes.length > 3000))
errors.push("notes must be short text");
return errors;
}

const phases = [
{id:"audit", action:"Inspect real consumer routes, package dependencies, existing components and asset ownership",status:"NOT RUN"},
{id:"art-direction",action:"Define typography, hierarchy, composition, focal imagery and alternate directions",status:"NOT RUN"},
{id:"source-selection",action:"Search existing consumer/native components before proposing original code",status:"NOT RUN"},
{id:"compose",action:"Implement original/native responsive layout in the unchanged consumer stack",status:"NOT RUN"},
{id:"motion",action:"Add meaningful transitions with keyboard and reduced-motion alternatives",status:"NOT RUN"},
{id:"render",action:"Capture and inspect real 1440px/390px browser screenshots",status:"NOT RUN"},
{id:"verify",action:"Run typecheck/build, accessibility, keyboard, reduced motion and measured performance checks",status:"NOT RUN"},
{id:"provenance",action:"Verify licenses/notices and record adopted code only under future central approvals",status:"NOT RUN"}
];

export function planWowFrontend({brief,consumer,inventory,policy}) {
const errors = [
...validateWowBrief(brief),
...validateWowReviewPolicy(policy,inventory)
];
const sources = previewWowSources(consumer,inventory);
if (!sources.ok) errors.push(...sources.errors.map((e) => "consumer/source: " + e));
if (errors.length) return {ok:false,mode:"DRY_RUN",errors,steps:[],sources:[],evidence:{}};
if (!consumer.enabled) {
return {ok:true,mode:"DRY_RUN",status:"DISABLED",project:brief.project,
repository:brief.repository,steps:[],sources:[],evidence:{},operationsAllowed:false};
}
const candidates = sources.sources.map((candidate) => ({
...candidate,
disposition:wowSourceDisposition(candidate.id,inventory,policy).status,
legalReuseDecision:"NO CODE COPY / NO INSTALL / NO EXECUTION"
}));
return {
ok:true,
mode:"DRY_RUN",
status:"PLAN_ONLY",
project:brief.project,
repository:brief.repository,
audience:brief.audience,
primaryConversion:brief.primaryConversion,
artDirection:brief.artDirection,
narrative:[...brief.narrative],
unchangedStack:{...consumer.stack},
languages:[...brief.constraints.languages],
sources:candidates,
steps:phases.map((phase) => ({...phase})),
guardrails:{
preserveStack:true,truthfulProofOnly:true,reducedMotionRequired:true,
thirdPartyComponentInstall:false,thirdPartyCodeCopy:false,
noUnapprovedMerchantBranding:true,
neverClaimRenderedQAWithoutBrowser:true
},
evidence:{
repositoryInspection:"NOT RUN (consumer metadata was supplied, not audited)",
browserScreenshots:"NOT RUN",keyboard:"NOT RUN",
reducedMotion:"NOT RUN",performance:"NOT RUN",build:"NOT RUN",deployment:"NOT VERIFIED"
},
operationsAllowed:false,
nextAction:"Verify this brief against the actual repository, select original/native design primitives, implement in a dedicated PR and perform real browser QA."
};
}

export function dryRunWowBriefFiles(briefPath,consumerPath) {
const brief = JSON.parse(fs.readFileSync(path.resolve(briefPath),"utf8"));
const consumer = JSON.parse(fs.readFileSync(path.resolve(consumerPath),"utf8"));
const policy = JSON.parse(fs.readFileSync(POLICY_PATH,"utf8"));
return planWowFrontend({brief,consumer,inventory:readWowRegistry(),policy});
}

if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
const [briefPath,consumerPath] = process.argv.slice(2);
if (!briefPath || !consumerPath) {
process.stderr.write("Usage: node scripts/wow-frontend-plan.mjs <brief.json> <consumer.json>\n");
process.exitCode = 2;
} else {
try {
const output = dryRunWowBriefFiles(briefPath,consumerPath);
process.stdout.write(JSON.stringify(output,null,2) + "\n");
if (!output.ok) process.exitCode = 1;
} catch (error) {
process.stderr.write("Unable to plan WOW Frontend: " + error.message + "\n");
process.exitCode = 1;
}
}
}
40 changes: 40 additions & 0 deletions skills/wow-frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# WOW Frontend agent skill

Portable SKILL.md for agents designing or refactoring Trigenys product landings.

## Location

The authoritative skill is [SKILL.md](SKILL.md), with references for [source selection](references/source-selection.md), [composition](references/composition.md), [motion](references/motion.md) and [visual QA](references/visual-qa.md). It lives in **`Trigenys/appfactory`**, not in the Project Automation Action or the landing template.

It can be **read directly from this repository**, or an agent that supports local `SKILL.md` packages can copy this entire `wow-frontend/` directory to the agent-specific skill location (for example `.agents/skills/wow-frontend/` or `.cursor/skills/wow-frontend/`, subject to that agent's actual discovery rules). This repository change does **not** install the skill into remote consumers or any ChatGPT/Cursor account.

The references resolve other AppFactory contracts relative to the **AppFactory repository root**; they are not vendored automatically into consumer repositories. Agents running in another checkout should explicitly find the AppFactory source (or vendor the referenced contracts under a separately approved integration).

## Read-only local planning

From the AppFactory checkout:

```sh
node scripts/wow-frontend-plan.mjs \
skills/wow-frontend/examples/commerce-factory.brief.json \
ui-registry/examples/wow-react-vite-css.json
```

The output is a **plan**, not a generated landing. It includes source dispositions, stack-compatibility research references, sequencing, denied installation/copy rights and **NOT RUN** browser/build/QA evidence.

The Commerce Factory example was prepared after inspecting its `README.md`, `package.json`, `src/App.tsx` and `src/styles.css` using read-only GitHub access. It preserves React 19, Vite, TypeScript, existing CSS tokens and FR/EN. No Commerce Factory files, deployments or commercial data were changed.

## Quality

```sh
npm run test:ui-registry
```

This existing CI gate includes tests for the WOW source contract (#139), central approval and provenance policy (#140) and this skill's structure/dry-run behavior (#141). Real screenshot, mobile, keyboard and performance automation are still follow-up work, tracked in [Project Automation #99](https://github.com/EagleFox31/appfactory-project-automation/issues/99).

## Versioning and obligations

- Do not introduce a separate UI component registry: existing `ui-registry/registry.json` remains canonical for native code.
- The external `ui-registry/wow-sources.json` is currently reference-only; central `ui-registry/wow-review-policy.json` has no live approvals.
- Skill instructions **do not** grant import/install permissions or override consumer `AGENTS.md` and repository governance.
- Use RAIDER and include accurate evidence in PRs. A green planning test does not imply production screenshot QA.
68 changes: 68 additions & 0 deletions skills/wow-frontend/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
name: wow-frontend
description: Plan and implement visually distinctive marketing/product pages using AppFactory's existing UI registry, vetted source research, intentional motion, real screenshots, accessibility and performance verification. Use for new or brownfield landing pages; never assume third-party code reuse is authorized.
---

# WOW Frontend — Trigenys AppFactory

You are the implementation assistant for a **real product**, not a screenshot generator. The product owns its source code, framework, style system and deployment; AppFactory provides reusable guidance and contracts.

## Read in this order

1. The consumer's `AGENTS.md` / RAIDER policy, package manifest, current routes, styles/tokens, assets, deployed behavior and existing tests. **Inspect the actual consumer; do not assume the AppFactory template is its stack.**
2. This skill and [source selection](references/source-selection.md), [composition](references/composition.md), [motion](references/motion.md) and [visual QA](references/visual-qa.md).
3. Canonical source inventory: `ui-registry/wow-sources.json`; native component registry: `ui-registry/registry.json`; brand profiles: `ui-registry/profiles.json`; central security approvals: `ui-registry/wow-review-policy.json`. Resolve paths relative to the **AppFactory repository root**, not the consumer.
4. Existing contracts: `ui-registry/wow-contract.mjs`, `ui-registry/wow-security.mjs`, `docs/wow-reuse-security.md`.

## Mandatory workflow

### 1. AUDIT — understand the consumer before changing it

Record product/audience, primary conversion action, real routes/behaviors, existing stack and styling, breakpoints, brand/typography, proof permissions, languages, image sources, performance baseline and current tests. State uncertainties explicitly. Do not invent deployment status, customer logos, partnerships, conversion metrics or testimonials.

### 2. ART DIRECTION — choose a defensible visual idea

Define a one-sentence art direction and two alternatives. Specify typography scale, color and imagery treatment, whitespace/grid, dominant focal point and section rhythm. Include a **content-first** journey explaining the product's actual value. Use [composition](references/composition.md), not arbitrary gradients, glowing blobs or “generic SaaS” styling.

### 3. SEARCH BEFORE BUILD — inspect approved metadata, not random packages

First check existing consumer components, then `ui-registry/registry.json`, then `ui-registry/wow-sources.json`. Use `node scripts/wow-frontend-plan.mjs <brief.json> <consumer.json>` for a **read-only dry run**, when the two configs are available.

Every external entry in the current inventory is *reference-only*. The central `wow-review-policy.json` ships with **zero approvals**. Even when a source says MIT in GitHub metadata, **do not copy, install or execute its code**; its status does not become permission through a consumer prompt or `source.allow`. See [source selection](references/source-selection.md). External tools (Figma, MagicPath, Webflow or 21st.dev) are optional ideation tools, never automatic dependencies.

### 4. COMPOSE — build in the consumer's stack

Prefer the smallest original/native primitive that serves the visual story; reuse actual consumer assets and brand tokens. Do not add Tailwind, Next.js, Webflow, GSAP, Three.js or another runtime merely to acquire one effect. Preserve routes, API semantics, internationalization and truthful product behavior. Scope edits to a dedicated branch and keep production credentials out of the repository.

For third-party material, stop at research/concept until the approval/adapter process is completed under #140/#142. Do not infer that a source marked “approved” in a future review automatically grants execution; `assessWowAdoption()` currently never executes.

### 5. ANIMATE WITH PURPOSE — never animate away usability

Only animate a cause/effect, hierarchy change, product state, meaningful transition or CTA focus. Every animation must have a legible, functional `prefers-reduced-motion` path. Avoid scroll-jacking, continuous distraction, layout shift and duplicate animation libraries. See [motion](references/motion.md).

### 6. RENDER BEFORE DONE — mandatory real verification

Run the consumer's typecheck/tests/build, then render the actual page at desktop and mobile viewports. Save/inspect real screenshots; test keyboard, focus states, CTA destination, no overflow, broken media, console errors, FR/EN content if applicable, reduced-motion path, and measured performance. Follow [visual QA](references/visual-qa.md). **A local planning script or source-code review is never evidence of rendered QA.**

A PR may remain open or the task marked **blocked/partial** until screenshot and runtime checks exist; never mark these tests “PASS” unless run. The reusable automated visual-QA workflow is separately tracked in `EagleFox31/appfactory-project-automation#99`.

### 7. PROVENANCE — preserve source and license evidence

If external code is ever copied under an explicitly reviewed future approval, commit the component, license notice and `.appfactory/frontend-provenance.json` in the consumer repository. Use `reconcileWowProvenance()` and `verifyWowProvenanceFiles()` from AppFactory. Current inventory is reference-only: no provenance entry is required for visual inspiration alone, but describe the source as **reference**, not “installed component”.

### 8. HANDOFF — report facts, not expectations

Report: files changed, source and license decisions, actual preview/PR, tests run vs skipped, screenshot results, accessibility/performance results, known defects, deployment state and follow-up. “WOW complete” requires passing [visual QA](references/visual-qa.md), not just a convincing mockup.

## RAIDER gates

Reusable, Agnostic, Idempotent, Durable/non-regressive, Engineering-grade, Retroactive. A prior working project must still work after this skill is used.

## Anti-patterns

- Fake merchant logos, figures, testimonials, “1000+ shops”, fabricated conversion lifts or unapproved partner branding.
- Template-soup landing sections without content hierarchy or a coherent visual story.
- Random motion/background effects with no product intent.
- Unreviewed third-party code, images, fonts, registry installers or `curl | sh`.
- Replacing a brownfield stack for a single component.
- Declaring screenshot, mobile performance, accessibility or production deployment “done” without observed evidence.
26 changes: 26 additions & 0 deletions skills/wow-frontend/examples/commerce-factory.brief.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"version": 1,
"project": "Trigenys Commerce Factory",
"repository": "Trigenys/trigenys-commerce-factory",
"audience": "Camerounian merchants selling products via a catalog and WhatsApp",
"primaryConversion": "Open merchant onboarding /app",
"artDirection": "Distinctive product-first commerce with real store visuals, warm accents and purposeful transitions",
"narrative": [
"Show recognizable product imagery",
"Demonstrate the catalog/storefront presentation",
"Connect a chosen product to a WhatsApp conversation",
"Explain what merchants can do next"
],
"constraints": {
"preserveStack": true,
"languages": [
"fr",
"en"
],
"assets": "self-hosted",
"merchants": "fictional-demo-only-unless-approved",
"truthfulProofOnly": true,
"reducedMotion": true
},
"notes": "Based on a read-only inspection of Commerce Factory README, package.json, src/App.tsx and src/styles.css. No product changes or browser QA performed."
}
Loading
Loading